Repository: GabrielDosReis/ipr Branch: main Commit: 3ada312a950f Files: 50 Total size: 1.1 MB Directory structure: gitextract_hqqsamgp/ ├── .github/ │ └── workflows/ │ ├── analysis.yml │ ├── docs.yaml │ └── main.yml ├── .gitignore ├── 3rdparty/ │ └── doctest/ │ └── doctest.h ├── CMakeLists.txt ├── ChangeLog ├── INSTALL ├── LICENSE ├── README.md ├── STYLE.md ├── cmake/ │ └── UseLATEX.cmake ├── doc/ │ ├── CMakeLists.txt │ ├── ChangeLog │ ├── ipr.bib │ ├── macro.tex │ ├── ref.tex │ └── user-guide.tex ├── include/ │ ├── ChangeLog │ └── ipr/ │ ├── impl │ ├── input │ ├── lexer │ ├── node-category │ ├── std-preamble │ └── utility-impl ├── infra/ │ └── Analysis.ruleset ├── src/ │ ├── ChangeLog │ ├── builtin.def │ ├── cxx-ipr-io.cxx │ ├── cxx-ipr-io.ixx │ ├── cxx-ipr-syntax.ixx │ ├── cxx-ipr-traversal.cxx │ ├── cxx-ipr-traversal.ixx │ ├── cxx-ipr-vocabulary.ixx │ ├── cxx-ipr.cxx │ ├── cxx-ipr.ixx │ ├── impl.cxx │ ├── input.cxx │ └── utility.cxx └── tests/ ├── CMakeLists.txt └── unit-tests/ ├── CMakeLists.txt ├── conversions.cxx ├── lines.cxx ├── main.cxx ├── phased-eval.cxx ├── region-owner.cxx ├── simple.cxx ├── specifiers.cxx ├── warehouse.cxx └── words.cxx ================================================ FILE CONTENTS ================================================ ================================================ FILE: .github/workflows/analysis.yml ================================================ name: Analysis on: push: paths-ignore: - 'doc/**' branches: [ main ] pull_request: paths-ignore: - 'doc/**' branches: [ main ] jobs: build: name: Microsoft C++ Code Analysis runs-on: windows-latest strategy: fail-fast: false matrix: config: [Release] steps: - uses: actions/checkout@v4 - name: Install MSVC Build Tools Preview shell: pwsh run: | $url = "https://aka.ms/vs/18/insiders/vs_buildtools.exe" Invoke-WebRequest -Uri $url -OutFile "$env:TEMP\vs_buildtools.exe" -UseBasicParsing $process = Start-Process -Wait -PassThru -FilePath "$env:TEMP\vs_buildtools.exe" ` -ArgumentList "--quiet --wait --norestart --add Microsoft.VisualStudio.Workload.VCTools --add Microsoft.VisualStudio.Component.VC.Tools.x86.x64 --add Microsoft.VisualStudio.Component.Windows11SDK.26100 --add Microsoft.VisualStudio.Component.VC.CMake.Project --includeRecommended" $exitCode = $process.ExitCode Write-Output "VS installer exit code: $exitCode" if ($exitCode -notin 0,3010,3015) { throw "VS Installer failed: $exitCode" } - name: Set up MSVC environment uses: ilammy/msvc-dev-cmd@v1 with: vsversion: '18' - name: Configure shell: pwsh run: cmake -S . -B build -G Ninja -DCMAKE_BUILD_TYPE=${{ matrix.config }} - name: Build shell: pwsh run: cmake --build build - name: Run Microsoft Visual C++ Analysis id: run-analysis uses: gdr-at-ms/msvc-code-analysis-action@support-cxx-modules with: cmakeBuildDirectory: build buildConfiguration: ${{ matrix.config }} ignoredTargetPaths: ${{ github.workspace }}/tests ruleset: '${{ github.workspace }}/infra/Analysis.ruleset' - name: Upload SARIF as an Artifact uses: actions/upload-artifact@v4 with: name: sarif-file path: ${{ steps.run-analysis.outputs.sarif }} if-no-files-found: error - name: Upload SARIF to GitHub uses: github/codeql-action/upload-sarif@v4 with: sarif_file: ${{ steps.run-analysis.outputs.sarif }} ================================================ FILE: .github/workflows/docs.yaml ================================================ name: Docs on: push: paths: - 'doc/**' - '.github/workflows/docs.yaml' branches: [ main ] pull_request: paths: - 'doc/**' - '.github/workflows/docs.yaml' branches: [ main ] jobs: build: runs-on: ubuntu-latest name: Build Ubuntu steps: - uses: actions/checkout@v2 - name: Install GCC, CMake run: | # https://github.com/actions/runner-images/issues/7192 echo 'APT::Get::Always-Include-Phased-Updates "false";' | sudo tee /etc/apt/apt.conf.d/99-phased-updates sudo apt-get update && sudo apt-get -y upgrade --fix-missing sudo apt-get install build-essential cmake texlive texlive-latex-extra - name: Configure & Build CMake uses: lukka/run-cmake@v3 with: buildDirectory: build cmakeAppendedArgs: -DBUILD_DOC=ON cmakeListsOrSettingsJson: CMakeListsTxtAdvanced buildWithCMakeArgs: --target doc ================================================ FILE: .github/workflows/main.yml ================================================ name: Build & Test on: push: paths-ignore: - 'doc/**' branches: [ main ] pull_request: paths-ignore: - 'doc/**' branches: [ main ] jobs: # For linux runners build-on-linux: name: Linux -x- ${{ matrix.name }} -x- ${{ matrix.config }} runs-on: ubuntu-latest strategy: fail-fast: false matrix: config: [Debug, Release] name: [gcc-15, clang-21-libstdc++, clang-21-libc++] include: - name: gcc-15 compiler: g++-15 install: | sudo add-apt-repository -y ppa:ubuntu-toolchain-r/test sudo apt-get update sudo apt-get install -y g++-15 cmake_extra: "" - name: clang-21-libstdc++ compiler: clang++-21 install: | wget -qO- https://apt.llvm.org/llvm-snapshot.gpg.key | sudo tee /etc/apt/trusted.gpg.d/apt.llvm.org.asc sudo add-apt-repository -y "deb http://apt.llvm.org/$(lsb_release -cs)/ llvm-toolchain-$(lsb_release -cs)-21 main" sudo apt-get update sudo apt-get install -y clang-21 cmake_extra: "" - name: clang-21-libc++ compiler: clang++-21 install: | wget -qO- https://apt.llvm.org/llvm-snapshot.gpg.key | sudo tee /etc/apt/trusted.gpg.d/apt.llvm.org.asc sudo add-apt-repository -y "deb http://apt.llvm.org/$(lsb_release -cs)/ llvm-toolchain-$(lsb_release -cs)-21 main" sudo apt-get update sudo apt-get install -y clang-21 libc++-21-dev libc++abi-21-dev cmake_extra: "-DCMAKE_CXX_FLAGS=-stdlib=libc++" steps: - uses: actions/checkout@v4 - name: Install C++ compilers and tools run: | # https://github.com/actions/runner-images/issues/7192 echo 'APT::Get::Always-Include-Phased-Updates "false";' | sudo tee /etc/apt/apt.conf.d/99-phased-updates sudo apt-get update && sudo apt-get -y upgrade --fix-missing sudo apt-get install -y build-essential cmake ninja-build ${{ matrix.install }} - name: Configure run: > cmake -S . -B build -G Ninja -DCMAKE_CXX_COMPILER=${{ matrix.compiler }} -DCMAKE_BUILD_TYPE=${{ matrix.config }} ${{ matrix.cmake_extra }} - name: Build run: cmake --build build --parallel --config ${{ matrix.config }} - name: Test run: ctest --test-dir build --output-on-failure # For macOS runners build-on-macos: name: macOS -x- Clang (Homebrew) -x- ${{ matrix.config }} runs-on: macos-latest strategy: fail-fast: false matrix: config: [Debug, Release] steps: - uses: actions/checkout@v4 - name: Install LLVM and CMake run: | brew install llvm cmake ninja echo "$(brew --prefix llvm)/bin" >> $GITHUB_PATH - name: Configure run: > cmake -S . -B build -G Ninja -DCMAKE_CXX_COMPILER=$(brew --prefix llvm)/bin/clang++ -DCMAKE_OSX_SYSROOT=$(xcrun --show-sdk-path) -DCMAKE_BUILD_TYPE=${{ matrix.config }} - name: Build run: cmake --build build --parallel --config ${{ matrix.config }} - name: Test run: ctest --test-dir build --output-on-failure # For Windows runners build-on-windows: name: Windows -x- MSVC Preview -x- ${{ matrix.config }} runs-on: windows-latest strategy: fail-fast: false matrix: config: [Debug, Release] steps: - uses: actions/checkout@v4 - name: Install MSVC Build Tools Preview shell: pwsh run: | $url = "https://aka.ms/vs/18/insiders/vs_buildtools.exe" Invoke-WebRequest -Uri $url -OutFile "$env:TEMP\vs_buildtools.exe" -UseBasicParsing $process = Start-Process -Wait -PassThru -FilePath "$env:TEMP\vs_buildtools.exe" ` -ArgumentList "--quiet --wait --norestart --add Microsoft.VisualStudio.Workload.VCTools --add Microsoft.VisualStudio.Component.VC.Tools.x86.x64 --add Microsoft.VisualStudio.Component.Windows11SDK.26100 --add Microsoft.VisualStudio.Component.VC.CMake.Project --includeRecommended" $exitCode = $process.ExitCode Write-Output "VS installer exit code: $exitCode" if ($exitCode -notin 0,3010,3015) { throw "VS Installer failed: $exitCode" } - name: Set up MSVC environment uses: ilammy/msvc-dev-cmd@v1 with: vsversion: '18' - name: Configure shell: pwsh run: > cmake -S . -B build -G Ninja -DCMAKE_BUILD_TYPE=${{ matrix.config }} - name: Build shell: pwsh run: cmake --build build --parallel --config ${{ matrix.config }} - name: Test shell: pwsh run: ctest --test-dir build --output-on-failure ================================================ FILE: .gitignore ================================================ .vscode/ .vs/ build/ ================================================ FILE: 3rdparty/doctest/doctest.h ================================================ // ====================================================================== lgtm [cpp/missing-header-guard] // == DO NOT MODIFY THIS FILE BY HAND - IT IS AUTO GENERATED BY CMAKE! == // ====================================================================== // // doctest.h - the lightest feature-rich C++ single-header testing framework for unit tests and TDD // // Copyright (c) 2016-2021 Viktor Kirilov // // Distributed under the MIT Software License // See accompanying file LICENSE.txt or copy at // https://opensource.org/licenses/MIT // // The documentation can be found at the library's page: // https://github.com/doctest/doctest/blob/master/doc/markdown/readme.md // // ================================================================================================= // ================================================================================================= // ================================================================================================= // // The library is heavily influenced by Catch - https://github.com/catchorg/Catch2 // which uses the Boost Software License - Version 1.0 // see here - https://github.com/catchorg/Catch2/blob/master/LICENSE.txt // // The concept of subcases (sections in Catch) and expression decomposition are from there. // Some parts of the code are taken directly: // - stringification - the detection of "ostream& operator<<(ostream&, const T&)" and StringMaker<> // - the Approx() helper class for floating point comparison // - colors in the console // - breaking into a debugger // - signal / SEH handling // - timer // - XmlWriter class - thanks to Phil Nash for allowing the direct reuse (AKA copy/paste) // // The expression decomposing templates are taken from lest - https://github.com/martinmoene/lest // which uses the Boost Software License - Version 1.0 // see here - https://github.com/martinmoene/lest/blob/master/LICENSE.txt // // ================================================================================================= // ================================================================================================= // ================================================================================================= #ifndef DOCTEST_LIBRARY_INCLUDED #define DOCTEST_LIBRARY_INCLUDED // ================================================================================================= // == VERSION ====================================================================================== // ================================================================================================= #define DOCTEST_VERSION_MAJOR 2 #define DOCTEST_VERSION_MINOR 4 #define DOCTEST_VERSION_PATCH 8 // util we need here #define DOCTEST_TOSTR_IMPL(x) #x #define DOCTEST_TOSTR(x) DOCTEST_TOSTR_IMPL(x) #define DOCTEST_VERSION_STR \ DOCTEST_TOSTR(DOCTEST_VERSION_MAJOR) "." \ DOCTEST_TOSTR(DOCTEST_VERSION_MINOR) "." \ DOCTEST_TOSTR(DOCTEST_VERSION_PATCH) #define DOCTEST_VERSION \ (DOCTEST_VERSION_MAJOR * 10000 + DOCTEST_VERSION_MINOR * 100 + DOCTEST_VERSION_PATCH) // ================================================================================================= // == COMPILER VERSION ============================================================================= // ================================================================================================= // ideas for the version stuff are taken from here: https://github.com/cxxstuff/cxx_detect #define DOCTEST_COMPILER(MAJOR, MINOR, PATCH) ((MAJOR)*10000000 + (MINOR)*100000 + (PATCH)) // GCC/Clang and GCC/MSVC are mutually exclusive, but Clang/MSVC are not because of clang-cl... #if defined(_MSC_VER) && defined(_MSC_FULL_VER) #if _MSC_VER == _MSC_FULL_VER / 10000 #define DOCTEST_MSVC DOCTEST_COMPILER(_MSC_VER / 100, _MSC_VER % 100, _MSC_FULL_VER % 10000) #else // MSVC #define DOCTEST_MSVC \ DOCTEST_COMPILER(_MSC_VER / 100, (_MSC_FULL_VER / 100000) % 100, _MSC_FULL_VER % 100000) #endif // MSVC #endif // MSVC #if defined(__clang__) && defined(__clang_minor__) #define DOCTEST_CLANG DOCTEST_COMPILER(__clang_major__, __clang_minor__, __clang_patchlevel__) #elif defined(__GNUC__) && defined(__GNUC_MINOR__) && defined(__GNUC_PATCHLEVEL__) && \ !defined(__INTEL_COMPILER) #define DOCTEST_GCC DOCTEST_COMPILER(__GNUC__, __GNUC_MINOR__, __GNUC_PATCHLEVEL__) #endif // GCC #ifndef DOCTEST_MSVC #define DOCTEST_MSVC 0 #endif // DOCTEST_MSVC #ifndef DOCTEST_CLANG #define DOCTEST_CLANG 0 #endif // DOCTEST_CLANG #ifndef DOCTEST_GCC #define DOCTEST_GCC 0 #endif // DOCTEST_GCC // ================================================================================================= // == COMPILER WARNINGS HELPERS ==================================================================== // ================================================================================================= #if DOCTEST_CLANG #define DOCTEST_PRAGMA_TO_STR(x) _Pragma(#x) #define DOCTEST_CLANG_SUPPRESS_WARNING_PUSH _Pragma("clang diagnostic push") #define DOCTEST_CLANG_SUPPRESS_WARNING(w) DOCTEST_PRAGMA_TO_STR(clang diagnostic ignored w) #define DOCTEST_CLANG_SUPPRESS_WARNING_POP _Pragma("clang diagnostic pop") #define DOCTEST_CLANG_SUPPRESS_WARNING_WITH_PUSH(w) \ DOCTEST_CLANG_SUPPRESS_WARNING_PUSH DOCTEST_CLANG_SUPPRESS_WARNING(w) #else // DOCTEST_CLANG #define DOCTEST_CLANG_SUPPRESS_WARNING_PUSH #define DOCTEST_CLANG_SUPPRESS_WARNING(w) #define DOCTEST_CLANG_SUPPRESS_WARNING_POP #define DOCTEST_CLANG_SUPPRESS_WARNING_WITH_PUSH(w) #endif // DOCTEST_CLANG #if DOCTEST_GCC #define DOCTEST_PRAGMA_TO_STR(x) _Pragma(#x) #define DOCTEST_GCC_SUPPRESS_WARNING_PUSH _Pragma("GCC diagnostic push") #define DOCTEST_GCC_SUPPRESS_WARNING(w) DOCTEST_PRAGMA_TO_STR(GCC diagnostic ignored w) #define DOCTEST_GCC_SUPPRESS_WARNING_POP _Pragma("GCC diagnostic pop") #define DOCTEST_GCC_SUPPRESS_WARNING_WITH_PUSH(w) \ DOCTEST_GCC_SUPPRESS_WARNING_PUSH DOCTEST_GCC_SUPPRESS_WARNING(w) #else // DOCTEST_GCC #define DOCTEST_GCC_SUPPRESS_WARNING_PUSH #define DOCTEST_GCC_SUPPRESS_WARNING(w) #define DOCTEST_GCC_SUPPRESS_WARNING_POP #define DOCTEST_GCC_SUPPRESS_WARNING_WITH_PUSH(w) #endif // DOCTEST_GCC #if DOCTEST_MSVC #define DOCTEST_MSVC_SUPPRESS_WARNING_PUSH __pragma(warning(push)) #define DOCTEST_MSVC_SUPPRESS_WARNING(w) __pragma(warning(disable : w)) #define DOCTEST_MSVC_SUPPRESS_WARNING_POP __pragma(warning(pop)) #define DOCTEST_MSVC_SUPPRESS_WARNING_WITH_PUSH(w) \ DOCTEST_MSVC_SUPPRESS_WARNING_PUSH DOCTEST_MSVC_SUPPRESS_WARNING(w) #else // DOCTEST_MSVC #define DOCTEST_MSVC_SUPPRESS_WARNING_PUSH #define DOCTEST_MSVC_SUPPRESS_WARNING(w) #define DOCTEST_MSVC_SUPPRESS_WARNING_POP #define DOCTEST_MSVC_SUPPRESS_WARNING_WITH_PUSH(w) #endif // DOCTEST_MSVC // ================================================================================================= // == COMPILER WARNINGS ============================================================================ // ================================================================================================= // both the header and the implementation suppress all of these, // so it only makes sense to aggregrate them like so #define DOCTEST_SUPPRESS_COMMON_WARNINGS_PUSH \ DOCTEST_CLANG_SUPPRESS_WARNING_PUSH \ DOCTEST_CLANG_SUPPRESS_WARNING("-Wunknown-pragmas") \ DOCTEST_CLANG_SUPPRESS_WARNING("-Wweak-vtables") \ DOCTEST_CLANG_SUPPRESS_WARNING("-Wpadded") \ DOCTEST_CLANG_SUPPRESS_WARNING("-Wmissing-prototypes") \ DOCTEST_CLANG_SUPPRESS_WARNING("-Wunused-local-typedef") \ DOCTEST_CLANG_SUPPRESS_WARNING("-Wc++98-compat") \ DOCTEST_CLANG_SUPPRESS_WARNING("-Wc++98-compat-pedantic") \ \ DOCTEST_GCC_SUPPRESS_WARNING_PUSH \ DOCTEST_GCC_SUPPRESS_WARNING("-Wunknown-pragmas") \ DOCTEST_GCC_SUPPRESS_WARNING("-Wpragmas") \ DOCTEST_GCC_SUPPRESS_WARNING("-Weffc++") \ DOCTEST_GCC_SUPPRESS_WARNING("-Wstrict-overflow") \ DOCTEST_GCC_SUPPRESS_WARNING("-Wstrict-aliasing") \ DOCTEST_GCC_SUPPRESS_WARNING("-Wmissing-declarations") \ DOCTEST_GCC_SUPPRESS_WARNING("-Wunused-local-typedefs") \ DOCTEST_GCC_SUPPRESS_WARNING("-Wuseless-cast") \ DOCTEST_GCC_SUPPRESS_WARNING("-Wnoexcept") \ \ DOCTEST_MSVC_SUPPRESS_WARNING_PUSH \ /* these 4 also disabled globally via cmake: */ \ DOCTEST_MSVC_SUPPRESS_WARNING(4514) /* unreferenced inline function has been removed */ \ DOCTEST_MSVC_SUPPRESS_WARNING(4571) /* SEH related */ \ DOCTEST_MSVC_SUPPRESS_WARNING(4710) /* function not inlined */ \ DOCTEST_MSVC_SUPPRESS_WARNING(4711) /* function selected for inline expansion*/ \ /* */ \ DOCTEST_MSVC_SUPPRESS_WARNING(4616) /* invalid compiler warning */ \ DOCTEST_MSVC_SUPPRESS_WARNING(4619) /* invalid compiler warning */ \ DOCTEST_MSVC_SUPPRESS_WARNING(4996) /* The compiler encountered a deprecated declaration */ \ DOCTEST_MSVC_SUPPRESS_WARNING(4706) /* assignment within conditional expression */ \ DOCTEST_MSVC_SUPPRESS_WARNING(4512) /* 'class' : assignment operator could not be generated */ \ DOCTEST_MSVC_SUPPRESS_WARNING(4127) /* conditional expression is constant */ \ DOCTEST_MSVC_SUPPRESS_WARNING(4820) /* padding */ \ DOCTEST_MSVC_SUPPRESS_WARNING(4625) /* copy constructor was implicitly deleted */ \ DOCTEST_MSVC_SUPPRESS_WARNING(4626) /* assignment operator was implicitly deleted */ \ DOCTEST_MSVC_SUPPRESS_WARNING(5027) /* move assignment operator implicitly deleted */ \ DOCTEST_MSVC_SUPPRESS_WARNING(5026) /* move constructor was implicitly deleted */ \ DOCTEST_MSVC_SUPPRESS_WARNING(4640) /* construction of local static object not thread-safe */ \ DOCTEST_MSVC_SUPPRESS_WARNING(5045) /* Spectre mitigation for memory load */ \ /* static analysis */ \ DOCTEST_MSVC_SUPPRESS_WARNING(26439) /* Function may not throw. Declare it 'noexcept' */ \ DOCTEST_MSVC_SUPPRESS_WARNING(26495) /* Always initialize a member variable */ \ DOCTEST_MSVC_SUPPRESS_WARNING(26451) /* Arithmetic overflow ... */ \ DOCTEST_MSVC_SUPPRESS_WARNING(26444) /* Avoid unnamed objects with custom ctor and dtor... */ \ DOCTEST_MSVC_SUPPRESS_WARNING(26812) /* Prefer 'enum class' over 'enum' */ #define DOCTEST_SUPPRESS_COMMON_WARNINGS_POP \ DOCTEST_CLANG_SUPPRESS_WARNING_POP \ DOCTEST_GCC_SUPPRESS_WARNING_POP \ DOCTEST_MSVC_SUPPRESS_WARNING_POP DOCTEST_SUPPRESS_COMMON_WARNINGS_PUSH DOCTEST_CLANG_SUPPRESS_WARNING_PUSH DOCTEST_CLANG_SUPPRESS_WARNING("-Wnon-virtual-dtor") DOCTEST_CLANG_SUPPRESS_WARNING("-Wdeprecated") DOCTEST_GCC_SUPPRESS_WARNING_PUSH DOCTEST_GCC_SUPPRESS_WARNING("-Wctor-dtor-privacy") DOCTEST_GCC_SUPPRESS_WARNING("-Wnon-virtual-dtor") DOCTEST_GCC_SUPPRESS_WARNING("-Wsign-promo") DOCTEST_MSVC_SUPPRESS_WARNING_PUSH DOCTEST_MSVC_SUPPRESS_WARNING(4623) // default constructor was implicitly defined as deleted #define DOCTEST_MAKE_STD_HEADERS_CLEAN_FROM_WARNINGS_ON_WALL_BEGIN \ DOCTEST_MSVC_SUPPRESS_WARNING_PUSH \ DOCTEST_MSVC_SUPPRESS_WARNING(4548) /* before comma no effect; expected side - effect */ \ DOCTEST_MSVC_SUPPRESS_WARNING(4265) /* virtual functions, but destructor is not virtual */ \ DOCTEST_MSVC_SUPPRESS_WARNING(4986) /* exception specification does not match previous */ \ DOCTEST_MSVC_SUPPRESS_WARNING(4350) /* 'member1' called instead of 'member2' */ \ DOCTEST_MSVC_SUPPRESS_WARNING(4668) /* not defined as a preprocessor macro */ \ DOCTEST_MSVC_SUPPRESS_WARNING(4365) /* signed/unsigned mismatch */ \ DOCTEST_MSVC_SUPPRESS_WARNING(4774) /* format string not a string literal */ \ DOCTEST_MSVC_SUPPRESS_WARNING(4820) /* padding */ \ DOCTEST_MSVC_SUPPRESS_WARNING(4625) /* copy constructor was implicitly deleted */ \ DOCTEST_MSVC_SUPPRESS_WARNING(4626) /* assignment operator was implicitly deleted */ \ DOCTEST_MSVC_SUPPRESS_WARNING(5027) /* move assignment operator implicitly deleted */ \ DOCTEST_MSVC_SUPPRESS_WARNING(5026) /* move constructor was implicitly deleted */ \ DOCTEST_MSVC_SUPPRESS_WARNING(4623) /* default constructor was implicitly deleted */ \ DOCTEST_MSVC_SUPPRESS_WARNING(5039) /* pointer to pot. throwing function passed to extern C */ \ DOCTEST_MSVC_SUPPRESS_WARNING(5045) /* Spectre mitigation for memory load */ \ DOCTEST_MSVC_SUPPRESS_WARNING(5105) /* macro producing 'defined' has undefined behavior */ #define DOCTEST_MAKE_STD_HEADERS_CLEAN_FROM_WARNINGS_ON_WALL_END DOCTEST_MSVC_SUPPRESS_WARNING_POP // ================================================================================================= // == FEATURE DETECTION ============================================================================ // ================================================================================================= // general compiler feature support table: https://en.cppreference.com/w/cpp/compiler_support // MSVC C++11 feature support table: https://msdn.microsoft.com/en-us/library/hh567368.aspx // GCC C++11 feature support table: https://gcc.gnu.org/projects/cxx-status.html // MSVC version table: // https://en.wikipedia.org/wiki/Microsoft_Visual_C%2B%2B#Internal_version_numbering // MSVC++ 14.3 (17) _MSC_VER == 1930 (Visual Studio 2022) // MSVC++ 14.2 (16) _MSC_VER == 1920 (Visual Studio 2019) // MSVC++ 14.1 (15) _MSC_VER == 1910 (Visual Studio 2017) // MSVC++ 14.0 _MSC_VER == 1900 (Visual Studio 2015) // MSVC++ 12.0 _MSC_VER == 1800 (Visual Studio 2013) // MSVC++ 11.0 _MSC_VER == 1700 (Visual Studio 2012) // MSVC++ 10.0 _MSC_VER == 1600 (Visual Studio 2010) // MSVC++ 9.0 _MSC_VER == 1500 (Visual Studio 2008) // MSVC++ 8.0 _MSC_VER == 1400 (Visual Studio 2005) // Universal Windows Platform support #if defined(WINAPI_FAMILY) && (WINAPI_FAMILY == WINAPI_FAMILY_APP) #define DOCTEST_CONFIG_NO_WINDOWS_SEH #endif // WINAPI_FAMILY #if DOCTEST_MSVC && !defined(DOCTEST_CONFIG_WINDOWS_SEH) #define DOCTEST_CONFIG_WINDOWS_SEH #endif // MSVC #if defined(DOCTEST_CONFIG_NO_WINDOWS_SEH) && defined(DOCTEST_CONFIG_WINDOWS_SEH) #undef DOCTEST_CONFIG_WINDOWS_SEH #endif // DOCTEST_CONFIG_NO_WINDOWS_SEH #if !defined(_WIN32) && !defined(__QNX__) && !defined(DOCTEST_CONFIG_POSIX_SIGNALS) && \ !defined(__EMSCRIPTEN__) #define DOCTEST_CONFIG_POSIX_SIGNALS #endif // _WIN32 #if defined(DOCTEST_CONFIG_NO_POSIX_SIGNALS) && defined(DOCTEST_CONFIG_POSIX_SIGNALS) #undef DOCTEST_CONFIG_POSIX_SIGNALS #endif // DOCTEST_CONFIG_NO_POSIX_SIGNALS #ifndef DOCTEST_CONFIG_NO_EXCEPTIONS #if !defined(__cpp_exceptions) && !defined(__EXCEPTIONS) && !defined(_CPPUNWIND) #define DOCTEST_CONFIG_NO_EXCEPTIONS #endif // no exceptions #endif // DOCTEST_CONFIG_NO_EXCEPTIONS #ifdef DOCTEST_CONFIG_NO_EXCEPTIONS_BUT_WITH_ALL_ASSERTS #ifndef DOCTEST_CONFIG_NO_EXCEPTIONS #define DOCTEST_CONFIG_NO_EXCEPTIONS #endif // DOCTEST_CONFIG_NO_EXCEPTIONS #endif // DOCTEST_CONFIG_NO_EXCEPTIONS_BUT_WITH_ALL_ASSERTS #if defined(DOCTEST_CONFIG_NO_EXCEPTIONS) && !defined(DOCTEST_CONFIG_NO_TRY_CATCH_IN_ASSERTS) #define DOCTEST_CONFIG_NO_TRY_CATCH_IN_ASSERTS #endif // DOCTEST_CONFIG_NO_EXCEPTIONS && !DOCTEST_CONFIG_NO_TRY_CATCH_IN_ASSERTS #if defined(DOCTEST_CONFIG_IMPLEMENT_WITH_MAIN) && !defined(DOCTEST_CONFIG_IMPLEMENT) #define DOCTEST_CONFIG_IMPLEMENT #endif // DOCTEST_CONFIG_IMPLEMENT_WITH_MAIN #if defined(_WIN32) || defined(__CYGWIN__) #if DOCTEST_MSVC #define DOCTEST_SYMBOL_EXPORT __declspec(dllexport) #define DOCTEST_SYMBOL_IMPORT __declspec(dllimport) #else // MSVC #define DOCTEST_SYMBOL_EXPORT __attribute__((dllexport)) #define DOCTEST_SYMBOL_IMPORT __attribute__((dllimport)) #endif // MSVC #else // _WIN32 #define DOCTEST_SYMBOL_EXPORT __attribute__((visibility("default"))) #define DOCTEST_SYMBOL_IMPORT #endif // _WIN32 #ifdef DOCTEST_CONFIG_IMPLEMENTATION_IN_DLL #ifdef DOCTEST_CONFIG_IMPLEMENT #define DOCTEST_INTERFACE DOCTEST_SYMBOL_EXPORT #else // DOCTEST_CONFIG_IMPLEMENT #define DOCTEST_INTERFACE DOCTEST_SYMBOL_IMPORT #endif // DOCTEST_CONFIG_IMPLEMENT #else // DOCTEST_CONFIG_IMPLEMENTATION_IN_DLL #define DOCTEST_INTERFACE #endif // DOCTEST_CONFIG_IMPLEMENTATION_IN_DLL #define DOCTEST_EMPTY #if DOCTEST_MSVC #define DOCTEST_NOINLINE __declspec(noinline) #define DOCTEST_UNUSED #define DOCTEST_ALIGNMENT(x) #elif DOCTEST_CLANG && DOCTEST_CLANG < DOCTEST_COMPILER(3, 5, 0) #define DOCTEST_NOINLINE #define DOCTEST_UNUSED #define DOCTEST_ALIGNMENT(x) #else #define DOCTEST_NOINLINE __attribute__((noinline)) #define DOCTEST_UNUSED __attribute__((unused)) #define DOCTEST_ALIGNMENT(x) __attribute__((aligned(x))) #endif #ifndef DOCTEST_NORETURN #if DOCTEST_MSVC && (DOCTEST_MSVC < DOCTEST_COMPILER(19, 0, 0)) #define DOCTEST_NORETURN #else // DOCTEST_MSVC #define DOCTEST_NORETURN [[noreturn]] #endif // DOCTEST_MSVC #endif // DOCTEST_NORETURN #ifndef DOCTEST_NOEXCEPT #if DOCTEST_MSVC && (DOCTEST_MSVC < DOCTEST_COMPILER(19, 0, 0)) #define DOCTEST_NOEXCEPT #else // DOCTEST_MSVC #define DOCTEST_NOEXCEPT noexcept #endif // DOCTEST_MSVC #endif // DOCTEST_NOEXCEPT #ifndef DOCTEST_CONSTEXPR #if DOCTEST_MSVC && (DOCTEST_MSVC < DOCTEST_COMPILER(19, 0, 0)) #define DOCTEST_CONSTEXPR const #else // DOCTEST_MSVC #define DOCTEST_CONSTEXPR constexpr #endif // DOCTEST_MSVC #endif // DOCTEST_CONSTEXPR // ================================================================================================= // == FEATURE DETECTION END ======================================================================== // ================================================================================================= // internal macros for string concatenation and anonymous variable name generation #define DOCTEST_CAT_IMPL(s1, s2) s1##s2 #define DOCTEST_CAT(s1, s2) DOCTEST_CAT_IMPL(s1, s2) #ifdef __COUNTER__ // not standard and may be missing for some compilers #define DOCTEST_ANONYMOUS(x) DOCTEST_CAT(x, __COUNTER__) #else // __COUNTER__ #define DOCTEST_ANONYMOUS(x) DOCTEST_CAT(x, __LINE__) #endif // __COUNTER__ #ifndef DOCTEST_CONFIG_ASSERTION_PARAMETERS_BY_VALUE #define DOCTEST_REF_WRAP(x) x& #else // DOCTEST_CONFIG_ASSERTION_PARAMETERS_BY_VALUE #define DOCTEST_REF_WRAP(x) x #endif // DOCTEST_CONFIG_ASSERTION_PARAMETERS_BY_VALUE // not using __APPLE__ because... this is how Catch does it #ifdef __MAC_OS_X_VERSION_MIN_REQUIRED #define DOCTEST_PLATFORM_MAC #elif defined(__IPHONE_OS_VERSION_MIN_REQUIRED) #define DOCTEST_PLATFORM_IPHONE #elif defined(_WIN32) #define DOCTEST_PLATFORM_WINDOWS #else // DOCTEST_PLATFORM #define DOCTEST_PLATFORM_LINUX #endif // DOCTEST_PLATFORM namespace doctest { namespace detail { static DOCTEST_CONSTEXPR int consume(const int*, int) { return 0; } }} #define DOCTEST_GLOBAL_NO_WARNINGS(var, ...) \ DOCTEST_CLANG_SUPPRESS_WARNING_WITH_PUSH("-Wglobal-constructors") \ static const int var = doctest::detail::consume(&var, __VA_ARGS__); \ DOCTEST_CLANG_SUPPRESS_WARNING_POP #ifndef DOCTEST_BREAK_INTO_DEBUGGER // should probably take a look at https://github.com/scottt/debugbreak #ifdef DOCTEST_PLATFORM_LINUX #if defined(__GNUC__) && (defined(__i386) || defined(__x86_64)) // Break at the location of the failing check if possible #define DOCTEST_BREAK_INTO_DEBUGGER() __asm__("int $3\n" : :) // NOLINT (hicpp-no-assembler) #else #include #define DOCTEST_BREAK_INTO_DEBUGGER() raise(SIGTRAP) #endif #elif defined(DOCTEST_PLATFORM_MAC) #if defined(__x86_64) || defined(__x86_64__) || defined(__amd64__) || defined(__i386) #define DOCTEST_BREAK_INTO_DEBUGGER() __asm__("int $3\n" : :) // NOLINT (hicpp-no-assembler) #else #define DOCTEST_BREAK_INTO_DEBUGGER() __asm__("brk #0"); // NOLINT (hicpp-no-assembler) #endif #elif DOCTEST_MSVC #define DOCTEST_BREAK_INTO_DEBUGGER() __debugbreak() #elif defined(__MINGW32__) DOCTEST_GCC_SUPPRESS_WARNING_WITH_PUSH("-Wredundant-decls") extern "C" __declspec(dllimport) void __stdcall DebugBreak(); DOCTEST_GCC_SUPPRESS_WARNING_POP #define DOCTEST_BREAK_INTO_DEBUGGER() ::DebugBreak() #else // linux #define DOCTEST_BREAK_INTO_DEBUGGER() (static_cast(0)) #endif // linux #endif // DOCTEST_BREAK_INTO_DEBUGGER // this is kept here for backwards compatibility since the config option was changed #ifdef DOCTEST_CONFIG_USE_IOSFWD #define DOCTEST_CONFIG_USE_STD_HEADERS #endif // DOCTEST_CONFIG_USE_IOSFWD // for clang - always include ciso646 (which drags some std stuff) because // we want to check if we are using libc++ with the _LIBCPP_VERSION macro in // which case we don't want to forward declare stuff from std - for reference: // https://github.com/doctest/doctest/issues/126 // https://github.com/doctest/doctest/issues/356 #if DOCTEST_CLANG #include #ifdef _LIBCPP_VERSION #define DOCTEST_CONFIG_USE_STD_HEADERS #endif // _LIBCPP_VERSION #endif // clang #ifdef DOCTEST_CONFIG_USE_STD_HEADERS #ifndef DOCTEST_CONFIG_INCLUDE_TYPE_TRAITS #define DOCTEST_CONFIG_INCLUDE_TYPE_TRAITS #endif // DOCTEST_CONFIG_INCLUDE_TYPE_TRAITS #include #include #include #else // DOCTEST_CONFIG_USE_STD_HEADERS // Forward declaring 'X' in namespace std is not permitted by the C++ Standard. DOCTEST_MSVC_SUPPRESS_WARNING_WITH_PUSH(4643) namespace std { // NOLINT (cert-dcl58-cpp) typedef decltype(nullptr) nullptr_t; template struct char_traits; template <> struct char_traits; template class basic_ostream; typedef basic_ostream> ostream; template class basic_istream; typedef basic_istream> istream; template class tuple; #if DOCTEST_MSVC >= DOCTEST_COMPILER(19, 20, 0) // see this issue on why this is needed: https://github.com/doctest/doctest/issues/183 template class allocator; template class basic_string; using string = basic_string, allocator>; #endif // VS 2019 } // namespace std DOCTEST_MSVC_SUPPRESS_WARNING_POP #endif // DOCTEST_CONFIG_USE_STD_HEADERS #ifdef DOCTEST_CONFIG_INCLUDE_TYPE_TRAITS #include #endif // DOCTEST_CONFIG_INCLUDE_TYPE_TRAITS namespace doctest { DOCTEST_INTERFACE extern bool is_running_in_test; // A 24 byte string class (can be as small as 17 for x64 and 13 for x86) that can hold strings with length // of up to 23 chars on the stack before going on the heap - the last byte of the buffer is used for: // - "is small" bit - the highest bit - if "0" then it is small - otherwise its "1" (128) // - if small - capacity left before going on the heap - using the lowest 5 bits // - if small - 2 bits are left unused - the second and third highest ones // - if small - acts as a null terminator if strlen() is 23 (24 including the null terminator) // and the "is small" bit remains "0" ("as well as the capacity left") so its OK // Idea taken from this lecture about the string implementation of facebook/folly - fbstring // https://www.youtube.com/watch?v=kPR8h4-qZdk // TODO: // - optimizations - like not deleting memory unnecessarily in operator= and etc. // - resize/reserve/clear // - substr // - replace // - back/front // - iterator stuff // - find & friends // - push_back/pop_back // - assign/insert/erase // - relational operators as free functions - taking const char* as one of the params class DOCTEST_INTERFACE String { static const unsigned len = 24; //!OCLINT avoid private static members static const unsigned last = len - 1; //!OCLINT avoid private static members struct view // len should be more than sizeof(view) - because of the final byte for flags { char* ptr; unsigned size; unsigned capacity; }; union { char buf[len]; view data; }; char* allocate(unsigned sz); bool isOnStack() const { return (buf[last] & 128) == 0; } void setOnHeap(); void setLast(unsigned in = last); void copy(const String& other); public: String(); ~String(); // cppcheck-suppress noExplicitConstructor String(const char* in); String(const char* in, unsigned in_size); String(std::istream& in, unsigned in_size); String(const String& other); String& operator=(const String& other); String& operator+=(const String& other); String(String&& other); String& operator=(String&& other); char operator[](unsigned i) const; char& operator[](unsigned i); // the only functions I'm willing to leave in the interface - available for inlining const char* c_str() const { return const_cast(this)->c_str(); } // NOLINT char* c_str() { if(isOnStack()) return reinterpret_cast(buf); return data.ptr; } unsigned size() const; unsigned capacity() const; int compare(const char* other, bool no_case = false) const; int compare(const String& other, bool no_case = false) const; }; DOCTEST_INTERFACE String operator+(const String& lhs, const String& rhs); DOCTEST_INTERFACE bool operator==(const String& lhs, const String& rhs); DOCTEST_INTERFACE bool operator!=(const String& lhs, const String& rhs); DOCTEST_INTERFACE bool operator<(const String& lhs, const String& rhs); DOCTEST_INTERFACE bool operator>(const String& lhs, const String& rhs); DOCTEST_INTERFACE bool operator<=(const String& lhs, const String& rhs); DOCTEST_INTERFACE bool operator>=(const String& lhs, const String& rhs); DOCTEST_INTERFACE std::ostream& operator<<(std::ostream& s, const String& in); namespace Color { enum Enum { None = 0, White, Red, Green, Blue, Cyan, Yellow, Grey, Bright = 0x10, BrightRed = Bright | Red, BrightGreen = Bright | Green, LightGrey = Bright | Grey, BrightWhite = Bright | White }; DOCTEST_INTERFACE std::ostream& operator<<(std::ostream& s, Color::Enum code); } // namespace Color namespace assertType { enum Enum { // macro traits is_warn = 1, is_check = 2 * is_warn, is_require = 2 * is_check, is_normal = 2 * is_require, is_throws = 2 * is_normal, is_throws_as = 2 * is_throws, is_throws_with = 2 * is_throws_as, is_nothrow = 2 * is_throws_with, is_false = 2 * is_nothrow, is_unary = 2 * is_false, // not checked anywhere - used just to distinguish the types is_eq = 2 * is_unary, is_ne = 2 * is_eq, is_lt = 2 * is_ne, is_gt = 2 * is_lt, is_ge = 2 * is_gt, is_le = 2 * is_ge, // macro types DT_WARN = is_normal | is_warn, DT_CHECK = is_normal | is_check, DT_REQUIRE = is_normal | is_require, DT_WARN_FALSE = is_normal | is_false | is_warn, DT_CHECK_FALSE = is_normal | is_false | is_check, DT_REQUIRE_FALSE = is_normal | is_false | is_require, DT_WARN_THROWS = is_throws | is_warn, DT_CHECK_THROWS = is_throws | is_check, DT_REQUIRE_THROWS = is_throws | is_require, DT_WARN_THROWS_AS = is_throws_as | is_warn, DT_CHECK_THROWS_AS = is_throws_as | is_check, DT_REQUIRE_THROWS_AS = is_throws_as | is_require, DT_WARN_THROWS_WITH = is_throws_with | is_warn, DT_CHECK_THROWS_WITH = is_throws_with | is_check, DT_REQUIRE_THROWS_WITH = is_throws_with | is_require, DT_WARN_THROWS_WITH_AS = is_throws_with | is_throws_as | is_warn, DT_CHECK_THROWS_WITH_AS = is_throws_with | is_throws_as | is_check, DT_REQUIRE_THROWS_WITH_AS = is_throws_with | is_throws_as | is_require, DT_WARN_NOTHROW = is_nothrow | is_warn, DT_CHECK_NOTHROW = is_nothrow | is_check, DT_REQUIRE_NOTHROW = is_nothrow | is_require, DT_WARN_EQ = is_normal | is_eq | is_warn, DT_CHECK_EQ = is_normal | is_eq | is_check, DT_REQUIRE_EQ = is_normal | is_eq | is_require, DT_WARN_NE = is_normal | is_ne | is_warn, DT_CHECK_NE = is_normal | is_ne | is_check, DT_REQUIRE_NE = is_normal | is_ne | is_require, DT_WARN_GT = is_normal | is_gt | is_warn, DT_CHECK_GT = is_normal | is_gt | is_check, DT_REQUIRE_GT = is_normal | is_gt | is_require, DT_WARN_LT = is_normal | is_lt | is_warn, DT_CHECK_LT = is_normal | is_lt | is_check, DT_REQUIRE_LT = is_normal | is_lt | is_require, DT_WARN_GE = is_normal | is_ge | is_warn, DT_CHECK_GE = is_normal | is_ge | is_check, DT_REQUIRE_GE = is_normal | is_ge | is_require, DT_WARN_LE = is_normal | is_le | is_warn, DT_CHECK_LE = is_normal | is_le | is_check, DT_REQUIRE_LE = is_normal | is_le | is_require, DT_WARN_UNARY = is_normal | is_unary | is_warn, DT_CHECK_UNARY = is_normal | is_unary | is_check, DT_REQUIRE_UNARY = is_normal | is_unary | is_require, DT_WARN_UNARY_FALSE = is_normal | is_false | is_unary | is_warn, DT_CHECK_UNARY_FALSE = is_normal | is_false | is_unary | is_check, DT_REQUIRE_UNARY_FALSE = is_normal | is_false | is_unary | is_require, }; } // namespace assertType DOCTEST_INTERFACE const char* assertString(assertType::Enum at); DOCTEST_INTERFACE const char* failureString(assertType::Enum at); DOCTEST_INTERFACE const char* skipPathFromFilename(const char* file); struct DOCTEST_INTERFACE TestCaseData { String m_file; // the file in which the test was registered (using String - see #350) unsigned m_line; // the line where the test was registered const char* m_name; // name of the test case const char* m_test_suite; // the test suite in which the test was added const char* m_description; bool m_skip; bool m_no_breaks; bool m_no_output; bool m_may_fail; bool m_should_fail; int m_expected_failures; double m_timeout; }; struct DOCTEST_INTERFACE AssertData { // common - for all asserts const TestCaseData* m_test_case; assertType::Enum m_at; const char* m_file; int m_line; const char* m_expr; bool m_failed; // exception-related - for all asserts bool m_threw; String m_exception; // for normal asserts String m_decomp; // for specific exception-related asserts bool m_threw_as; const char* m_exception_type; const char* m_exception_string; }; struct DOCTEST_INTERFACE MessageData { String m_string; const char* m_file; int m_line; assertType::Enum m_severity; }; struct DOCTEST_INTERFACE SubcaseSignature { String m_name; const char* m_file; int m_line; bool operator<(const SubcaseSignature& other) const; }; struct DOCTEST_INTERFACE IContextScope { IContextScope(); virtual ~IContextScope(); virtual void stringify(std::ostream*) const = 0; }; namespace detail { struct DOCTEST_INTERFACE TestCase; } // namespace detail struct ContextOptions //!OCLINT too many fields { std::ostream* cout = nullptr; // stdout stream String binary_name; // the test binary name const detail::TestCase* currentTest = nullptr; // == parameters from the command line String out; // output filename String order_by; // how tests should be ordered unsigned rand_seed; // the seed for rand ordering unsigned first; // the first (matching) test to be executed unsigned last; // the last (matching) test to be executed int abort_after; // stop tests after this many failed assertions int subcase_filter_levels; // apply the subcase filters for the first N levels bool success; // include successful assertions in output bool case_sensitive; // if filtering should be case sensitive bool exit; // if the program should be exited after the tests are ran/whatever bool duration; // print the time duration of each test case bool minimal; // minimal console output (only test failures) bool quiet; // no console output bool no_throw; // to skip exceptions-related assertion macros bool no_exitcode; // if the framework should return 0 as the exitcode bool no_run; // to not run the tests at all (can be done with an "*" exclude) bool no_intro; // to not print the intro of the framework bool no_version; // to not print the version of the framework bool no_colors; // if output to the console should be colorized bool force_colors; // forces the use of colors even when a tty cannot be detected bool no_breaks; // to not break into the debugger bool no_skip; // don't skip test cases which are marked to be skipped bool gnu_file_line; // if line numbers should be surrounded with :x: and not (x): bool no_path_in_filenames; // if the path to files should be removed from the output bool no_line_numbers; // if source code line numbers should be omitted from the output bool no_debug_output; // no output in the debug console when a debugger is attached bool no_skipped_summary; // don't print "skipped" in the summary !!! UNDOCUMENTED !!! bool no_time_in_output; // omit any time/timestamps from output !!! UNDOCUMENTED !!! bool help; // to print the help bool version; // to print the version bool count; // if only the count of matching tests is to be retrieved bool list_test_cases; // to list all tests matching the filters bool list_test_suites; // to list all suites matching the filters bool list_reporters; // lists all registered reporters }; namespace detail { template struct enable_if {}; template struct enable_if { typedef TYPE type; }; // clang-format off template struct remove_reference { typedef T type; }; template struct remove_reference { typedef T type; }; template struct remove_reference { typedef T type; }; template U declval(int); template T declval(long); template auto declval() DOCTEST_NOEXCEPT -> decltype(declval(0)) ; template struct is_lvalue_reference { const static bool value=false; }; template struct is_lvalue_reference { const static bool value=true; }; template struct is_rvalue_reference { const static bool value=false; }; template struct is_rvalue_reference { const static bool value=true; }; template inline T&& forward(typename remove_reference::type& t) DOCTEST_NOEXCEPT { return static_cast(t); } template inline T&& forward(typename remove_reference::type&& t) DOCTEST_NOEXCEPT { static_assert(!is_lvalue_reference::value, "Can not forward an rvalue as an lvalue."); return static_cast(t); } template struct remove_const { typedef T type; }; template struct remove_const { typedef T type; }; #ifdef DOCTEST_CONFIG_INCLUDE_TYPE_TRAITS template struct is_enum : public std::is_enum {}; template struct underlying_type : public std::underlying_type {}; #else // Use compiler intrinsics template struct is_enum { DOCTEST_CONSTEXPR static bool value = __is_enum(T); }; template struct underlying_type { typedef __underlying_type(T) type; }; #endif // clang-format on template struct deferred_false // cppcheck-suppress unusedStructMember { static const bool value = false; }; namespace has_insertion_operator_impl { std::ostream &os(); template DOCTEST_REF_WRAP(T) val(); template struct check { static DOCTEST_CONSTEXPR bool value = false; }; template struct check(), void())> { static DOCTEST_CONSTEXPR bool value = true; }; } // namespace has_insertion_operator_impl template using has_insertion_operator = has_insertion_operator_impl::check; DOCTEST_INTERFACE std::ostream* tlssPush(); DOCTEST_INTERFACE String tlssPop(); template struct StringMakerBase { template static String convert(const DOCTEST_REF_WRAP(T)) { return "{?}"; } }; // Vector and various type other than pointer or array. template struct filldata { static void fill(std::ostream* stream, const T &in) { *stream << in; } }; template struct filldata { static void fill(std::ostream* stream, const T (&in)[N]) { for (unsigned long i = 0; i < N; i++) { *stream << in[i]; } } }; // Specialized since we don't want the terminating null byte! template struct filldata { static void fill(std::ostream* stream, const char(&in)[N]) { *stream << in; } }; template void filloss(std::ostream* stream, const T& in) { filldata::fill(stream, in); } template void filloss(std::ostream* stream, const T (&in)[N]) { // T[N], T(&)[N], T(&&)[N] have same behaviour. // Hence remove reference. filldata::type>::fill(stream, in); } template <> struct StringMakerBase { template static String convert(const DOCTEST_REF_WRAP(T) in) { /* When parameter "in" is a null terminated const char* it works. * When parameter "in" is a T arr[N] without '\0' we can fill the * stringstream with N objects (T=char).If in is char pointer * * without '\0' , it would cause segfault * stepping over unaccessible memory. */ std::ostream* stream = tlssPush(); filloss(stream, in); return tlssPop(); } }; DOCTEST_INTERFACE String rawMemoryToString(const void* object, unsigned size); template String rawMemoryToString(const DOCTEST_REF_WRAP(T) object) { return rawMemoryToString(&object, sizeof(object)); } template const char* type_to_string() { return "<>"; } } // namespace detail template struct StringMaker : public detail::StringMakerBase::value> {}; template struct StringMaker { template static String convert(U* p) { if(p) return detail::rawMemoryToString(p); return "NULL"; } }; template struct StringMaker { static String convert(R C::*p) { if(p) return detail::rawMemoryToString(p); return "NULL"; } }; template ::value, bool>::type = true> String toString(const DOCTEST_REF_WRAP(T) value) { return StringMaker::convert(value); } #ifdef DOCTEST_CONFIG_TREAT_CHAR_STAR_AS_STRING DOCTEST_INTERFACE String toString(char* in); DOCTEST_INTERFACE String toString(const char* in); #endif // DOCTEST_CONFIG_TREAT_CHAR_STAR_AS_STRING DOCTEST_INTERFACE String toString(bool in); DOCTEST_INTERFACE String toString(float in); DOCTEST_INTERFACE String toString(double in); DOCTEST_INTERFACE String toString(double long in); DOCTEST_INTERFACE String toString(char in); DOCTEST_INTERFACE String toString(char signed in); DOCTEST_INTERFACE String toString(char unsigned in); DOCTEST_INTERFACE String toString(int short in); DOCTEST_INTERFACE String toString(int short unsigned in); DOCTEST_INTERFACE String toString(int in); DOCTEST_INTERFACE String toString(int unsigned in); DOCTEST_INTERFACE String toString(int long in); DOCTEST_INTERFACE String toString(int long unsigned in); DOCTEST_INTERFACE String toString(int long long in); DOCTEST_INTERFACE String toString(int long long unsigned in); DOCTEST_INTERFACE String toString(std::nullptr_t in); template ::value, bool>::type = true> String toString(const DOCTEST_REF_WRAP(T) value) { typedef typename detail::underlying_type::type UT; return toString(static_cast(value)); } #if DOCTEST_MSVC >= DOCTEST_COMPILER(19, 20, 0) // see this issue on why this is needed: https://github.com/doctest/doctest/issues/183 DOCTEST_INTERFACE String toString(const std::string& in); #endif // VS 2019 class DOCTEST_INTERFACE Approx { public: explicit Approx(double value); Approx operator()(double value) const; #ifdef DOCTEST_CONFIG_INCLUDE_TYPE_TRAITS template explicit Approx(const T& value, typename detail::enable_if::value>::type* = static_cast(nullptr)) { *this = Approx(static_cast(value)); } #endif // DOCTEST_CONFIG_INCLUDE_TYPE_TRAITS Approx& epsilon(double newEpsilon); #ifdef DOCTEST_CONFIG_INCLUDE_TYPE_TRAITS template typename detail::enable_if::value, Approx&>::type epsilon( const T& newEpsilon) { m_epsilon = static_cast(newEpsilon); return *this; } #endif // DOCTEST_CONFIG_INCLUDE_TYPE_TRAITS Approx& scale(double newScale); #ifdef DOCTEST_CONFIG_INCLUDE_TYPE_TRAITS template typename detail::enable_if::value, Approx&>::type scale( const T& newScale) { m_scale = static_cast(newScale); return *this; } #endif // DOCTEST_CONFIG_INCLUDE_TYPE_TRAITS // clang-format off DOCTEST_INTERFACE friend bool operator==(double lhs, const Approx & rhs); DOCTEST_INTERFACE friend bool operator==(const Approx & lhs, double rhs); DOCTEST_INTERFACE friend bool operator!=(double lhs, const Approx & rhs); DOCTEST_INTERFACE friend bool operator!=(const Approx & lhs, double rhs); DOCTEST_INTERFACE friend bool operator<=(double lhs, const Approx & rhs); DOCTEST_INTERFACE friend bool operator<=(const Approx & lhs, double rhs); DOCTEST_INTERFACE friend bool operator>=(double lhs, const Approx & rhs); DOCTEST_INTERFACE friend bool operator>=(const Approx & lhs, double rhs); DOCTEST_INTERFACE friend bool operator< (double lhs, const Approx & rhs); DOCTEST_INTERFACE friend bool operator< (const Approx & lhs, double rhs); DOCTEST_INTERFACE friend bool operator> (double lhs, const Approx & rhs); DOCTEST_INTERFACE friend bool operator> (const Approx & lhs, double rhs); DOCTEST_INTERFACE friend String toString(const Approx& in); #ifdef DOCTEST_CONFIG_INCLUDE_TYPE_TRAITS #define DOCTEST_APPROX_PREFIX \ template friend typename detail::enable_if::value, bool>::type DOCTEST_APPROX_PREFIX operator==(const T& lhs, const Approx& rhs) { return operator==(double(lhs), rhs); } DOCTEST_APPROX_PREFIX operator==(const Approx& lhs, const T& rhs) { return operator==(rhs, lhs); } DOCTEST_APPROX_PREFIX operator!=(const T& lhs, const Approx& rhs) { return !operator==(lhs, rhs); } DOCTEST_APPROX_PREFIX operator!=(const Approx& lhs, const T& rhs) { return !operator==(rhs, lhs); } DOCTEST_APPROX_PREFIX operator<=(const T& lhs, const Approx& rhs) { return double(lhs) < rhs.m_value || lhs == rhs; } DOCTEST_APPROX_PREFIX operator<=(const Approx& lhs, const T& rhs) { return lhs.m_value < double(rhs) || lhs == rhs; } DOCTEST_APPROX_PREFIX operator>=(const T& lhs, const Approx& rhs) { return double(lhs) > rhs.m_value || lhs == rhs; } DOCTEST_APPROX_PREFIX operator>=(const Approx& lhs, const T& rhs) { return lhs.m_value > double(rhs) || lhs == rhs; } DOCTEST_APPROX_PREFIX operator< (const T& lhs, const Approx& rhs) { return double(lhs) < rhs.m_value && lhs != rhs; } DOCTEST_APPROX_PREFIX operator< (const Approx& lhs, const T& rhs) { return lhs.m_value < double(rhs) && lhs != rhs; } DOCTEST_APPROX_PREFIX operator> (const T& lhs, const Approx& rhs) { return double(lhs) > rhs.m_value && lhs != rhs; } DOCTEST_APPROX_PREFIX operator> (const Approx& lhs, const T& rhs) { return lhs.m_value > double(rhs) && lhs != rhs; } #undef DOCTEST_APPROX_PREFIX #endif // DOCTEST_CONFIG_INCLUDE_TYPE_TRAITS // clang-format on private: double m_epsilon; double m_scale; double m_value; }; DOCTEST_INTERFACE String toString(const Approx& in); DOCTEST_INTERFACE const ContextOptions* getContextOptions(); #if !defined(DOCTEST_CONFIG_DISABLE) namespace detail { // clang-format off #ifdef DOCTEST_CONFIG_TREAT_CHAR_STAR_AS_STRING template struct decay_array { typedef T type; }; template struct decay_array { typedef T* type; }; template struct decay_array { typedef T* type; }; template struct not_char_pointer { enum { value = 1 }; }; template<> struct not_char_pointer { enum { value = 0 }; }; template<> struct not_char_pointer { enum { value = 0 }; }; template struct can_use_op : public not_char_pointer::type> {}; #endif // DOCTEST_CONFIG_TREAT_CHAR_STAR_AS_STRING // clang-format on struct DOCTEST_INTERFACE TestFailureException { }; DOCTEST_INTERFACE bool checkIfShouldThrow(assertType::Enum at); #ifndef DOCTEST_CONFIG_NO_EXCEPTIONS DOCTEST_NORETURN #endif // DOCTEST_CONFIG_NO_EXCEPTIONS DOCTEST_INTERFACE void throwException(); struct DOCTEST_INTERFACE Subcase { SubcaseSignature m_signature; bool m_entered = false; Subcase(const String& name, const char* file, int line); ~Subcase(); operator bool() const; }; template String stringifyBinaryExpr(const DOCTEST_REF_WRAP(L) lhs, const char* op, const DOCTEST_REF_WRAP(R) rhs) { // NOLINTNEXTLINE(clang-analyzer-cplusplus.NewDeleteLeaks) return toString(lhs) + op + toString(rhs); } #if DOCTEST_CLANG && DOCTEST_CLANG < DOCTEST_COMPILER(3, 6, 0) DOCTEST_CLANG_SUPPRESS_WARNING_WITH_PUSH("-Wunused-comparison") #endif // This will check if there is any way it could find a operator like member or friend and uses it. // If not it doesn't find the operator or if the operator at global scope is defined after // this template, the template won't be instantiated due to SFINAE. Once the template is not // instantiated it can look for global operator using normal conversions. #define SFINAE_OP(ret,op) decltype((void)(doctest::detail::declval() op doctest::detail::declval()),ret{}) #define DOCTEST_DO_BINARY_EXPRESSION_COMPARISON(op, op_str, op_macro) \ template \ DOCTEST_NOINLINE SFINAE_OP(Result,op) operator op(const R&& rhs) { \ bool res = op_macro(doctest::detail::forward(lhs), doctest::detail::forward(rhs)); \ if(m_at & assertType::is_false) \ res = !res; \ if(!res || doctest::getContextOptions()->success) \ return Result(res, stringifyBinaryExpr(lhs, op_str, rhs)); \ return Result(res); \ } \ template ::value, void >::type* = nullptr> \ DOCTEST_NOINLINE SFINAE_OP(Result,op) operator op(const R& rhs) { \ bool res = op_macro(doctest::detail::forward(lhs), rhs); \ if(m_at & assertType::is_false) \ res = !res; \ if(!res || doctest::getContextOptions()->success) \ return Result(res, stringifyBinaryExpr(lhs, op_str, rhs)); \ return Result(res); \ } // more checks could be added - like in Catch: // https://github.com/catchorg/Catch2/pull/1480/files // https://github.com/catchorg/Catch2/pull/1481/files #define DOCTEST_FORBIT_EXPRESSION(rt, op) \ template \ rt& operator op(const R&) { \ static_assert(deferred_false::value, \ "Expression Too Complex Please Rewrite As Binary Comparison!"); \ return *this; \ } struct DOCTEST_INTERFACE Result { bool m_passed; String m_decomp; Result() = default; Result(bool passed, const String& decomposition = String()); // forbidding some expressions based on this table: https://en.cppreference.com/w/cpp/language/operator_precedence DOCTEST_FORBIT_EXPRESSION(Result, &) DOCTEST_FORBIT_EXPRESSION(Result, ^) DOCTEST_FORBIT_EXPRESSION(Result, |) DOCTEST_FORBIT_EXPRESSION(Result, &&) DOCTEST_FORBIT_EXPRESSION(Result, ||) DOCTEST_FORBIT_EXPRESSION(Result, ==) DOCTEST_FORBIT_EXPRESSION(Result, !=) DOCTEST_FORBIT_EXPRESSION(Result, <) DOCTEST_FORBIT_EXPRESSION(Result, >) DOCTEST_FORBIT_EXPRESSION(Result, <=) DOCTEST_FORBIT_EXPRESSION(Result, >=) DOCTEST_FORBIT_EXPRESSION(Result, =) DOCTEST_FORBIT_EXPRESSION(Result, +=) DOCTEST_FORBIT_EXPRESSION(Result, -=) DOCTEST_FORBIT_EXPRESSION(Result, *=) DOCTEST_FORBIT_EXPRESSION(Result, /=) DOCTEST_FORBIT_EXPRESSION(Result, %=) DOCTEST_FORBIT_EXPRESSION(Result, <<=) DOCTEST_FORBIT_EXPRESSION(Result, >>=) DOCTEST_FORBIT_EXPRESSION(Result, &=) DOCTEST_FORBIT_EXPRESSION(Result, ^=) DOCTEST_FORBIT_EXPRESSION(Result, |=) }; #ifndef DOCTEST_CONFIG_NO_COMPARISON_WARNING_SUPPRESSION DOCTEST_CLANG_SUPPRESS_WARNING_PUSH DOCTEST_CLANG_SUPPRESS_WARNING("-Wsign-conversion") DOCTEST_CLANG_SUPPRESS_WARNING("-Wsign-compare") //DOCTEST_CLANG_SUPPRESS_WARNING("-Wdouble-promotion") //DOCTEST_CLANG_SUPPRESS_WARNING("-Wconversion") //DOCTEST_CLANG_SUPPRESS_WARNING("-Wfloat-equal") DOCTEST_GCC_SUPPRESS_WARNING_PUSH DOCTEST_GCC_SUPPRESS_WARNING("-Wsign-conversion") DOCTEST_GCC_SUPPRESS_WARNING("-Wsign-compare") //DOCTEST_GCC_SUPPRESS_WARNING("-Wdouble-promotion") //DOCTEST_GCC_SUPPRESS_WARNING("-Wconversion") //DOCTEST_GCC_SUPPRESS_WARNING("-Wfloat-equal") DOCTEST_MSVC_SUPPRESS_WARNING_PUSH // https://stackoverflow.com/questions/39479163 what's the difference between 4018 and 4389 DOCTEST_MSVC_SUPPRESS_WARNING(4388) // signed/unsigned mismatch DOCTEST_MSVC_SUPPRESS_WARNING(4389) // 'operator' : signed/unsigned mismatch DOCTEST_MSVC_SUPPRESS_WARNING(4018) // 'expression' : signed/unsigned mismatch //DOCTEST_MSVC_SUPPRESS_WARNING(4805) // 'operation' : unsafe mix of type 'type' and type 'type' in operation #endif // DOCTEST_CONFIG_NO_COMPARISON_WARNING_SUPPRESSION // clang-format off #ifndef DOCTEST_CONFIG_TREAT_CHAR_STAR_AS_STRING #define DOCTEST_COMPARISON_RETURN_TYPE bool #else // DOCTEST_CONFIG_TREAT_CHAR_STAR_AS_STRING #define DOCTEST_COMPARISON_RETURN_TYPE typename enable_if::value || can_use_op::value, bool>::type // NOLINTNEXTLINE(clang-analyzer-cplusplus.NewDeleteLeaks) inline bool eq(const char* lhs, const char* rhs) { return String(lhs) == String(rhs); } inline bool ne(const char* lhs, const char* rhs) { return String(lhs) != String(rhs); } inline bool lt(const char* lhs, const char* rhs) { return String(lhs) < String(rhs); } inline bool gt(const char* lhs, const char* rhs) { return String(lhs) > String(rhs); } inline bool le(const char* lhs, const char* rhs) { return String(lhs) <= String(rhs); } inline bool ge(const char* lhs, const char* rhs) { return String(lhs) >= String(rhs); } #endif // DOCTEST_CONFIG_TREAT_CHAR_STAR_AS_STRING // clang-format on #define DOCTEST_RELATIONAL_OP(name, op) \ template \ DOCTEST_COMPARISON_RETURN_TYPE name(const DOCTEST_REF_WRAP(L) lhs, \ const DOCTEST_REF_WRAP(R) rhs) { \ return lhs op rhs; \ } DOCTEST_RELATIONAL_OP(eq, ==) DOCTEST_RELATIONAL_OP(ne, !=) DOCTEST_RELATIONAL_OP(lt, <) DOCTEST_RELATIONAL_OP(gt, >) DOCTEST_RELATIONAL_OP(le, <=) DOCTEST_RELATIONAL_OP(ge, >=) #ifndef DOCTEST_CONFIG_TREAT_CHAR_STAR_AS_STRING #define DOCTEST_CMP_EQ(l, r) l == r #define DOCTEST_CMP_NE(l, r) l != r #define DOCTEST_CMP_GT(l, r) l > r #define DOCTEST_CMP_LT(l, r) l < r #define DOCTEST_CMP_GE(l, r) l >= r #define DOCTEST_CMP_LE(l, r) l <= r #else // DOCTEST_CONFIG_TREAT_CHAR_STAR_AS_STRING #define DOCTEST_CMP_EQ(l, r) eq(l, r) #define DOCTEST_CMP_NE(l, r) ne(l, r) #define DOCTEST_CMP_GT(l, r) gt(l, r) #define DOCTEST_CMP_LT(l, r) lt(l, r) #define DOCTEST_CMP_GE(l, r) ge(l, r) #define DOCTEST_CMP_LE(l, r) le(l, r) #endif // DOCTEST_CONFIG_TREAT_CHAR_STAR_AS_STRING template // cppcheck-suppress copyCtorAndEqOperator struct Expression_lhs { L lhs; assertType::Enum m_at; explicit Expression_lhs(L&& in, assertType::Enum at) : lhs(doctest::detail::forward(in)) , m_at(at) {} DOCTEST_NOINLINE operator Result() { // this is needed only for MSVC 2015 DOCTEST_MSVC_SUPPRESS_WARNING_WITH_PUSH(4800) // 'int': forcing value to bool bool res = static_cast(lhs); DOCTEST_MSVC_SUPPRESS_WARNING_POP if(m_at & assertType::is_false) //!OCLINT bitwise operator in conditional res = !res; if(!res || getContextOptions()->success) return Result(res, toString(lhs)); return Result(res); } /* This is required for user-defined conversions from Expression_lhs to L */ operator L() const { return lhs; } // clang-format off DOCTEST_DO_BINARY_EXPRESSION_COMPARISON(==, " == ", DOCTEST_CMP_EQ) //!OCLINT bitwise operator in conditional DOCTEST_DO_BINARY_EXPRESSION_COMPARISON(!=, " != ", DOCTEST_CMP_NE) //!OCLINT bitwise operator in conditional DOCTEST_DO_BINARY_EXPRESSION_COMPARISON(>, " > ", DOCTEST_CMP_GT) //!OCLINT bitwise operator in conditional DOCTEST_DO_BINARY_EXPRESSION_COMPARISON(<, " < ", DOCTEST_CMP_LT) //!OCLINT bitwise operator in conditional DOCTEST_DO_BINARY_EXPRESSION_COMPARISON(>=, " >= ", DOCTEST_CMP_GE) //!OCLINT bitwise operator in conditional DOCTEST_DO_BINARY_EXPRESSION_COMPARISON(<=, " <= ", DOCTEST_CMP_LE) //!OCLINT bitwise operator in conditional // clang-format on // forbidding some expressions based on this table: https://en.cppreference.com/w/cpp/language/operator_precedence DOCTEST_FORBIT_EXPRESSION(Expression_lhs, &) DOCTEST_FORBIT_EXPRESSION(Expression_lhs, ^) DOCTEST_FORBIT_EXPRESSION(Expression_lhs, |) DOCTEST_FORBIT_EXPRESSION(Expression_lhs, &&) DOCTEST_FORBIT_EXPRESSION(Expression_lhs, ||) DOCTEST_FORBIT_EXPRESSION(Expression_lhs, =) DOCTEST_FORBIT_EXPRESSION(Expression_lhs, +=) DOCTEST_FORBIT_EXPRESSION(Expression_lhs, -=) DOCTEST_FORBIT_EXPRESSION(Expression_lhs, *=) DOCTEST_FORBIT_EXPRESSION(Expression_lhs, /=) DOCTEST_FORBIT_EXPRESSION(Expression_lhs, %=) DOCTEST_FORBIT_EXPRESSION(Expression_lhs, <<=) DOCTEST_FORBIT_EXPRESSION(Expression_lhs, >>=) DOCTEST_FORBIT_EXPRESSION(Expression_lhs, &=) DOCTEST_FORBIT_EXPRESSION(Expression_lhs, ^=) DOCTEST_FORBIT_EXPRESSION(Expression_lhs, |=) // these 2 are unfortunate because they should be allowed - they have higher precedence over the comparisons, but the // ExpressionDecomposer class uses the left shift operator to capture the left operand of the binary expression... DOCTEST_FORBIT_EXPRESSION(Expression_lhs, <<) DOCTEST_FORBIT_EXPRESSION(Expression_lhs, >>) }; #ifndef DOCTEST_CONFIG_NO_COMPARISON_WARNING_SUPPRESSION DOCTEST_CLANG_SUPPRESS_WARNING_POP DOCTEST_MSVC_SUPPRESS_WARNING_POP DOCTEST_GCC_SUPPRESS_WARNING_POP #endif // DOCTEST_CONFIG_NO_COMPARISON_WARNING_SUPPRESSION #if DOCTEST_CLANG && DOCTEST_CLANG < DOCTEST_COMPILER(3, 6, 0) DOCTEST_CLANG_SUPPRESS_WARNING_POP #endif struct DOCTEST_INTERFACE ExpressionDecomposer { assertType::Enum m_at; ExpressionDecomposer(assertType::Enum at); // The right operator for capturing expressions is "<=" instead of "<<" (based on the operator precedence table) // but then there will be warnings from GCC about "-Wparentheses" and since "_Pragma()" is problematic this will stay for now... // https://github.com/catchorg/Catch2/issues/870 // https://github.com/catchorg/Catch2/issues/565 template Expression_lhs operator<<(const L &&operand) { return Expression_lhs(doctest::detail::forward(operand), m_at); } template ::value,void >::type* = nullptr> Expression_lhs operator<<(const L &operand) { return Expression_lhs(operand, m_at); } }; struct DOCTEST_INTERFACE TestSuite { const char* m_test_suite = nullptr; const char* m_description = nullptr; bool m_skip = false; bool m_no_breaks = false; bool m_no_output = false; bool m_may_fail = false; bool m_should_fail = false; int m_expected_failures = 0; double m_timeout = 0; TestSuite& operator*(const char* in); template TestSuite& operator*(const T& in) { in.fill(*this); return *this; } }; typedef void (*funcType)(); struct DOCTEST_INTERFACE TestCase : public TestCaseData { funcType m_test; // a function pointer to the test case const char* m_type; // for templated test cases - gets appended to the real name int m_template_id; // an ID used to distinguish between the different versions of a templated test case String m_full_name; // contains the name (only for templated test cases!) + the template type TestCase(funcType test, const char* file, unsigned line, const TestSuite& test_suite, const char* type = "", int template_id = -1); TestCase(const TestCase& other); DOCTEST_MSVC_SUPPRESS_WARNING_WITH_PUSH(26434) // hides a non-virtual function TestCase& operator=(const TestCase& other); DOCTEST_MSVC_SUPPRESS_WARNING_POP TestCase& operator*(const char* in); template TestCase& operator*(const T& in) { in.fill(*this); return *this; } bool operator<(const TestCase& other) const; }; // forward declarations of functions used by the macros DOCTEST_INTERFACE int regTest(const TestCase& tc); DOCTEST_INTERFACE int setTestSuite(const TestSuite& ts); DOCTEST_INTERFACE bool isDebuggerActive(); template int instantiationHelper(const T&) { return 0; } namespace binaryAssertComparison { enum Enum { eq = 0, ne, gt, lt, ge, le }; } // namespace binaryAssertComparison // clang-format off template struct RelationalComparator { bool operator()(const DOCTEST_REF_WRAP(L), const DOCTEST_REF_WRAP(R) ) const { return false; } }; #define DOCTEST_BINARY_RELATIONAL_OP(n, op) \ template struct RelationalComparator { bool operator()(const DOCTEST_REF_WRAP(L) lhs, const DOCTEST_REF_WRAP(R) rhs) const { return op(lhs, rhs); } }; // clang-format on DOCTEST_BINARY_RELATIONAL_OP(0, doctest::detail::eq) DOCTEST_BINARY_RELATIONAL_OP(1, doctest::detail::ne) DOCTEST_BINARY_RELATIONAL_OP(2, doctest::detail::gt) DOCTEST_BINARY_RELATIONAL_OP(3, doctest::detail::lt) DOCTEST_BINARY_RELATIONAL_OP(4, doctest::detail::ge) DOCTEST_BINARY_RELATIONAL_OP(5, doctest::detail::le) struct DOCTEST_INTERFACE ResultBuilder : public AssertData { ResultBuilder(assertType::Enum at, const char* file, int line, const char* expr, const char* exception_type = "", const char* exception_string = ""); void setResult(const Result& res); template DOCTEST_NOINLINE bool binary_assert(const DOCTEST_REF_WRAP(L) lhs, const DOCTEST_REF_WRAP(R) rhs) { m_failed = !RelationalComparator()(lhs, rhs); if(m_failed || getContextOptions()->success) m_decomp = stringifyBinaryExpr(lhs, ", ", rhs); return !m_failed; } template DOCTEST_NOINLINE bool unary_assert(const DOCTEST_REF_WRAP(L) val) { m_failed = !val; if(m_at & assertType::is_false) //!OCLINT bitwise operator in conditional m_failed = !m_failed; if(m_failed || getContextOptions()->success) m_decomp = toString(val); return !m_failed; } void translateException(); bool log(); void react() const; }; namespace assertAction { enum Enum { nothing = 0, dbgbreak = 1, shouldthrow = 2 }; } // namespace assertAction DOCTEST_INTERFACE void failed_out_of_a_testing_context(const AssertData& ad); DOCTEST_INTERFACE bool decomp_assert(assertType::Enum at, const char* file, int line, const char* expr, Result result); #define DOCTEST_ASSERT_OUT_OF_TESTS(decomp) \ do { \ if(!is_running_in_test) { \ if(failed) { \ ResultBuilder rb(at, file, line, expr); \ rb.m_failed = failed; \ rb.m_decomp = decomp; \ failed_out_of_a_testing_context(rb); \ if(isDebuggerActive() && !getContextOptions()->no_breaks) \ DOCTEST_BREAK_INTO_DEBUGGER(); \ if(checkIfShouldThrow(at)) \ throwException(); \ } \ return !failed; \ } \ } while(false) #define DOCTEST_ASSERT_IN_TESTS(decomp) \ ResultBuilder rb(at, file, line, expr); \ rb.m_failed = failed; \ if(rb.m_failed || getContextOptions()->success) \ rb.m_decomp = decomp; \ if(rb.log()) \ DOCTEST_BREAK_INTO_DEBUGGER(); \ if(rb.m_failed && checkIfShouldThrow(at)) \ throwException() template DOCTEST_NOINLINE bool binary_assert(assertType::Enum at, const char* file, int line, const char* expr, const DOCTEST_REF_WRAP(L) lhs, const DOCTEST_REF_WRAP(R) rhs) { bool failed = !RelationalComparator()(lhs, rhs); // ################################################################################### // IF THE DEBUGGER BREAKS HERE - GO 1 LEVEL UP IN THE CALLSTACK FOR THE FAILING ASSERT // THIS IS THE EFFECT OF HAVING 'DOCTEST_CONFIG_SUPER_FAST_ASSERTS' DEFINED // ################################################################################### DOCTEST_ASSERT_OUT_OF_TESTS(stringifyBinaryExpr(lhs, ", ", rhs)); DOCTEST_ASSERT_IN_TESTS(stringifyBinaryExpr(lhs, ", ", rhs)); return !failed; } template DOCTEST_NOINLINE bool unary_assert(assertType::Enum at, const char* file, int line, const char* expr, const DOCTEST_REF_WRAP(L) val) { bool failed = !val; if(at & assertType::is_false) //!OCLINT bitwise operator in conditional failed = !failed; // ################################################################################### // IF THE DEBUGGER BREAKS HERE - GO 1 LEVEL UP IN THE CALLSTACK FOR THE FAILING ASSERT // THIS IS THE EFFECT OF HAVING 'DOCTEST_CONFIG_SUPER_FAST_ASSERTS' DEFINED // ################################################################################### DOCTEST_ASSERT_OUT_OF_TESTS(toString(val)); DOCTEST_ASSERT_IN_TESTS(toString(val)); return !failed; } struct DOCTEST_INTERFACE IExceptionTranslator { IExceptionTranslator(); virtual ~IExceptionTranslator(); virtual bool translate(String&) const = 0; }; template class ExceptionTranslator : public IExceptionTranslator //!OCLINT destructor of virtual class { public: explicit ExceptionTranslator(String (*translateFunction)(T)) : m_translateFunction(translateFunction) {} bool translate(String& res) const override { #ifndef DOCTEST_CONFIG_NO_EXCEPTIONS try { throw; // lgtm [cpp/rethrow-no-exception] // cppcheck-suppress catchExceptionByValue } catch(T ex) { // NOLINT res = m_translateFunction(ex); //!OCLINT parameter reassignment return true; } catch(...) {} //!OCLINT - empty catch statement #endif // DOCTEST_CONFIG_NO_EXCEPTIONS static_cast(res); // to silence -Wunused-parameter return false; } private: String (*m_translateFunction)(T); }; DOCTEST_INTERFACE void registerExceptionTranslatorImpl(const IExceptionTranslator* et); template struct StringStreamBase { template static void convert(std::ostream* s, const T& in) { *s << toString(in); } // always treat char* as a string in this context - no matter // if DOCTEST_CONFIG_TREAT_CHAR_STAR_AS_STRING is defined static void convert(std::ostream* s, const char* in) { *s << String(in); } }; template <> struct StringStreamBase { template static void convert(std::ostream* s, const T& in) { *s << in; } }; template struct StringStream : public StringStreamBase::value> {}; template void toStream(std::ostream* s, const T& value) { StringStream::convert(s, value); } #ifdef DOCTEST_CONFIG_TREAT_CHAR_STAR_AS_STRING DOCTEST_INTERFACE void toStream(std::ostream* s, char* in); DOCTEST_INTERFACE void toStream(std::ostream* s, const char* in); #endif // DOCTEST_CONFIG_TREAT_CHAR_STAR_AS_STRING DOCTEST_INTERFACE void toStream(std::ostream* s, bool in); DOCTEST_INTERFACE void toStream(std::ostream* s, float in); DOCTEST_INTERFACE void toStream(std::ostream* s, double in); DOCTEST_INTERFACE void toStream(std::ostream* s, double long in); DOCTEST_INTERFACE void toStream(std::ostream* s, char in); DOCTEST_INTERFACE void toStream(std::ostream* s, char signed in); DOCTEST_INTERFACE void toStream(std::ostream* s, char unsigned in); DOCTEST_INTERFACE void toStream(std::ostream* s, int short in); DOCTEST_INTERFACE void toStream(std::ostream* s, int short unsigned in); DOCTEST_INTERFACE void toStream(std::ostream* s, int in); DOCTEST_INTERFACE void toStream(std::ostream* s, int unsigned in); DOCTEST_INTERFACE void toStream(std::ostream* s, int long in); DOCTEST_INTERFACE void toStream(std::ostream* s, int long unsigned in); DOCTEST_INTERFACE void toStream(std::ostream* s, int long long in); DOCTEST_INTERFACE void toStream(std::ostream* s, int long long unsigned in); // ContextScope base class used to allow implementing methods of ContextScope // that don't depend on the template parameter in doctest.cpp. class DOCTEST_INTERFACE ContextScopeBase : public IContextScope { protected: ContextScopeBase(); ContextScopeBase(ContextScopeBase&& other); void destroy(); bool need_to_destroy{true}; }; template class ContextScope : public ContextScopeBase { const L lambda_; public: explicit ContextScope(const L &lambda) : lambda_(lambda) {} ContextScope(ContextScope &&other) : ContextScopeBase(static_cast(other)), lambda_(other.lambda_) {} void stringify(std::ostream* s) const override { lambda_(s); } ~ContextScope() override { if (need_to_destroy) { destroy(); } } }; struct DOCTEST_INTERFACE MessageBuilder : public MessageData { std::ostream* m_stream; bool logged = false; MessageBuilder(const char* file, int line, assertType::Enum severity); MessageBuilder() = delete; ~MessageBuilder(); // the preferred way of chaining parameters for stringification template MessageBuilder& operator,(const T& in) { toStream(m_stream, in); return *this; } // kept here just for backwards-compatibility - the comma operator should be preferred now template MessageBuilder& operator<<(const T& in) { return this->operator,(in); } // the `,` operator has the lowest operator precedence - if `<<` is used by the user then // the `,` operator will be called last which is not what we want and thus the `*` operator // is used first (has higher operator precedence compared to `<<`) so that we guarantee that // an operator of the MessageBuilder class is called first before the rest of the parameters template MessageBuilder& operator*(const T& in) { return this->operator,(in); } bool log(); void react(); }; template ContextScope MakeContextScope(const L &lambda) { return ContextScope(lambda); } } // namespace detail #define DOCTEST_DEFINE_DECORATOR(name, type, def) \ struct name \ { \ type data; \ name(type in = def) \ : data(in) {} \ void fill(detail::TestCase& state) const { state.DOCTEST_CAT(m_, name) = data; } \ void fill(detail::TestSuite& state) const { state.DOCTEST_CAT(m_, name) = data; } \ } DOCTEST_DEFINE_DECORATOR(test_suite, const char*, ""); DOCTEST_DEFINE_DECORATOR(description, const char*, ""); DOCTEST_DEFINE_DECORATOR(skip, bool, true); DOCTEST_DEFINE_DECORATOR(no_breaks, bool, true); DOCTEST_DEFINE_DECORATOR(no_output, bool, true); DOCTEST_DEFINE_DECORATOR(timeout, double, 0); DOCTEST_DEFINE_DECORATOR(may_fail, bool, true); DOCTEST_DEFINE_DECORATOR(should_fail, bool, true); DOCTEST_DEFINE_DECORATOR(expected_failures, int, 0); template int registerExceptionTranslator(String (*translateFunction)(T)) { DOCTEST_CLANG_SUPPRESS_WARNING_WITH_PUSH("-Wexit-time-destructors") static detail::ExceptionTranslator exceptionTranslator(translateFunction); DOCTEST_CLANG_SUPPRESS_WARNING_POP detail::registerExceptionTranslatorImpl(&exceptionTranslator); return 0; } } // namespace doctest // in a separate namespace outside of doctest because the DOCTEST_TEST_SUITE macro // introduces an anonymous namespace in which getCurrentTestSuite gets overridden namespace doctest_detail_test_suite_ns { DOCTEST_INTERFACE doctest::detail::TestSuite& getCurrentTestSuite(); } // namespace doctest_detail_test_suite_ns namespace doctest { #else // DOCTEST_CONFIG_DISABLE template int registerExceptionTranslator(String (*)(T)) { return 0; } #endif // DOCTEST_CONFIG_DISABLE namespace detail { typedef void (*assert_handler)(const AssertData&); struct ContextState; } // namespace detail class DOCTEST_INTERFACE Context { detail::ContextState* p; void parseArgs(int argc, const char* const* argv, bool withDefaults = false); public: explicit Context(int argc = 0, const char* const* argv = nullptr); ~Context(); void applyCommandLine(int argc, const char* const* argv); void addFilter(const char* filter, const char* value); void clearFilters(); void setOption(const char* option, bool value); void setOption(const char* option, int value); void setOption(const char* option, const char* value); bool shouldExit(); void setAsDefaultForAssertsOutOfTestCases(); void setAssertHandler(detail::assert_handler ah); void setCout(std::ostream* out); int run(); }; namespace TestCaseFailureReason { enum Enum { None = 0, AssertFailure = 1, // an assertion has failed in the test case Exception = 2, // test case threw an exception Crash = 4, // a crash... TooManyFailedAsserts = 8, // the abort-after option Timeout = 16, // see the timeout decorator ShouldHaveFailedButDidnt = 32, // see the should_fail decorator ShouldHaveFailedAndDid = 64, // see the should_fail decorator DidntFailExactlyNumTimes = 128, // see the expected_failures decorator FailedExactlyNumTimes = 256, // see the expected_failures decorator CouldHaveFailedAndDid = 512 // see the may_fail decorator }; } // namespace TestCaseFailureReason struct DOCTEST_INTERFACE CurrentTestCaseStats { int numAssertsCurrentTest; int numAssertsFailedCurrentTest; double seconds; int failure_flags; // use TestCaseFailureReason::Enum bool testCaseSuccess; }; struct DOCTEST_INTERFACE TestCaseException { String error_string; bool is_crash; }; struct DOCTEST_INTERFACE TestRunStats { unsigned numTestCases; unsigned numTestCasesPassingFilters; unsigned numTestSuitesPassingFilters; unsigned numTestCasesFailed; int numAsserts; int numAssertsFailed; }; struct QueryData { const TestRunStats* run_stats = nullptr; const TestCaseData** data = nullptr; unsigned num_data = 0; }; struct DOCTEST_INTERFACE IReporter { // The constructor has to accept "const ContextOptions&" as a single argument // which has most of the options for the run + a pointer to the stdout stream // Reporter(const ContextOptions& in) // called when a query should be reported (listing test cases, printing the version, etc.) virtual void report_query(const QueryData&) = 0; // called when the whole test run starts virtual void test_run_start() = 0; // called when the whole test run ends (caching a pointer to the input doesn't make sense here) virtual void test_run_end(const TestRunStats&) = 0; // called when a test case is started (safe to cache a pointer to the input) virtual void test_case_start(const TestCaseData&) = 0; // called when a test case is reentered because of unfinished subcases (safe to cache a pointer to the input) virtual void test_case_reenter(const TestCaseData&) = 0; // called when a test case has ended virtual void test_case_end(const CurrentTestCaseStats&) = 0; // called when an exception is thrown from the test case (or it crashes) virtual void test_case_exception(const TestCaseException&) = 0; // called whenever a subcase is entered (don't cache pointers to the input) virtual void subcase_start(const SubcaseSignature&) = 0; // called whenever a subcase is exited (don't cache pointers to the input) virtual void subcase_end() = 0; // called for each assert (don't cache pointers to the input) virtual void log_assert(const AssertData&) = 0; // called for each message (don't cache pointers to the input) virtual void log_message(const MessageData&) = 0; // called when a test case is skipped either because it doesn't pass the filters, has a skip decorator // or isn't in the execution range (between first and last) (safe to cache a pointer to the input) virtual void test_case_skipped(const TestCaseData&) = 0; // doctest will not be managing the lifetimes of reporters given to it but this would still be nice to have virtual ~IReporter(); // can obtain all currently active contexts and stringify them if one wishes to do so static int get_num_active_contexts(); static const IContextScope* const* get_active_contexts(); // can iterate through contexts which have been stringified automatically in their destructors when an exception has been thrown static int get_num_stringified_contexts(); static const String* get_stringified_contexts(); }; namespace detail { typedef IReporter* (*reporterCreatorFunc)(const ContextOptions&); DOCTEST_INTERFACE void registerReporterImpl(const char* name, int prio, reporterCreatorFunc c, bool isReporter); template IReporter* reporterCreator(const ContextOptions& o) { return new Reporter(o); } } // namespace detail template int registerReporter(const char* name, int priority, bool isReporter) { detail::registerReporterImpl(name, priority, detail::reporterCreator, isReporter); return 0; } } // namespace doctest // if registering is not disabled #if !defined(DOCTEST_CONFIG_DISABLE) // common code in asserts - for convenience #define DOCTEST_ASSERT_LOG_REACT_RETURN(b) \ if(b.log()) \ DOCTEST_BREAK_INTO_DEBUGGER(); \ b.react(); \ return !b.m_failed #ifdef DOCTEST_CONFIG_NO_TRY_CATCH_IN_ASSERTS #define DOCTEST_WRAP_IN_TRY(x) x; #else // DOCTEST_CONFIG_NO_TRY_CATCH_IN_ASSERTS #define DOCTEST_WRAP_IN_TRY(x) \ try { \ x; \ } catch(...) { DOCTEST_RB.translateException(); } #endif // DOCTEST_CONFIG_NO_TRY_CATCH_IN_ASSERTS #ifdef DOCTEST_CONFIG_VOID_CAST_EXPRESSIONS #define DOCTEST_CAST_TO_VOID(...) \ DOCTEST_GCC_SUPPRESS_WARNING_WITH_PUSH("-Wuseless-cast") \ static_cast(__VA_ARGS__); \ DOCTEST_GCC_SUPPRESS_WARNING_POP #else // DOCTEST_CONFIG_VOID_CAST_EXPRESSIONS #define DOCTEST_CAST_TO_VOID(...) __VA_ARGS__; #endif // DOCTEST_CONFIG_VOID_CAST_EXPRESSIONS // registers the test by initializing a dummy var with a function #define DOCTEST_REGISTER_FUNCTION(global_prefix, f, decorators) \ global_prefix DOCTEST_GLOBAL_NO_WARNINGS(DOCTEST_ANONYMOUS(DOCTEST_ANON_VAR_), \ doctest::detail::regTest( \ doctest::detail::TestCase( \ f, __FILE__, __LINE__, \ doctest_detail_test_suite_ns::getCurrentTestSuite()) * \ decorators)) #define DOCTEST_IMPLEMENT_FIXTURE(der, base, func, decorators) \ namespace { \ struct der : public base \ { \ void f(); \ }; \ static void func() { \ der v; \ v.f(); \ } \ DOCTEST_REGISTER_FUNCTION(DOCTEST_EMPTY, func, decorators) \ } \ inline DOCTEST_NOINLINE void der::f() #define DOCTEST_CREATE_AND_REGISTER_FUNCTION(f, decorators) \ static void f(); \ DOCTEST_REGISTER_FUNCTION(DOCTEST_EMPTY, f, decorators) \ static void f() #define DOCTEST_CREATE_AND_REGISTER_FUNCTION_IN_CLASS(f, proxy, decorators) \ static doctest::detail::funcType proxy() { return f; } \ DOCTEST_REGISTER_FUNCTION(inline, proxy(), decorators) \ static void f() // for registering tests #define DOCTEST_TEST_CASE(decorators) \ DOCTEST_CREATE_AND_REGISTER_FUNCTION(DOCTEST_ANONYMOUS(DOCTEST_ANON_FUNC_), decorators) // for registering tests in classes - requires C++17 for inline variables! #if __cplusplus >= 201703L || (DOCTEST_MSVC >= DOCTEST_COMPILER(19, 12, 0) && _MSVC_LANG >= 201703L) #define DOCTEST_TEST_CASE_CLASS(decorators) \ DOCTEST_CREATE_AND_REGISTER_FUNCTION_IN_CLASS(DOCTEST_ANONYMOUS(DOCTEST_ANON_FUNC_), \ DOCTEST_ANONYMOUS(DOCTEST_ANON_PROXY_), \ decorators) #else // DOCTEST_TEST_CASE_CLASS #define DOCTEST_TEST_CASE_CLASS(...) \ TEST_CASES_CAN_BE_REGISTERED_IN_CLASSES_ONLY_IN_CPP17_MODE_OR_WITH_VS_2017_OR_NEWER #endif // DOCTEST_TEST_CASE_CLASS // for registering tests with a fixture #define DOCTEST_TEST_CASE_FIXTURE(c, decorators) \ DOCTEST_IMPLEMENT_FIXTURE(DOCTEST_ANONYMOUS(DOCTEST_ANON_CLASS_), c, \ DOCTEST_ANONYMOUS(DOCTEST_ANON_FUNC_), decorators) // for converting types to strings without the header and demangling #define DOCTEST_TYPE_TO_STRING_IMPL(...) \ template <> \ inline const char* type_to_string<__VA_ARGS__>() { \ return "<" #__VA_ARGS__ ">"; \ } #define DOCTEST_TYPE_TO_STRING(...) \ namespace doctest { namespace detail { \ DOCTEST_TYPE_TO_STRING_IMPL(__VA_ARGS__) \ } \ } \ static_assert(true, "") #define DOCTEST_TEST_CASE_TEMPLATE_DEFINE_IMPL(dec, T, iter, func) \ template \ static void func(); \ namespace { \ template \ struct iter; \ template \ struct iter> \ { \ iter(const char* file, unsigned line, int index) { \ doctest::detail::regTest(doctest::detail::TestCase(func, file, line, \ doctest_detail_test_suite_ns::getCurrentTestSuite(), \ doctest::detail::type_to_string(), \ int(line) * 1000 + index) \ * dec); \ iter>(file, line, index + 1); \ } \ }; \ template <> \ struct iter> \ { \ iter(const char*, unsigned, int) {} \ }; \ } \ template \ static void func() #define DOCTEST_TEST_CASE_TEMPLATE_DEFINE(dec, T, id) \ DOCTEST_TEST_CASE_TEMPLATE_DEFINE_IMPL(dec, T, DOCTEST_CAT(id, ITERATOR), \ DOCTEST_ANONYMOUS(DOCTEST_ANON_TMP_)) #define DOCTEST_TEST_CASE_TEMPLATE_INSTANTIATE_IMPL(id, anon, ...) \ DOCTEST_GLOBAL_NO_WARNINGS(DOCTEST_CAT(anon, DUMMY), \ doctest::detail::instantiationHelper( \ DOCTEST_CAT(id, ITERATOR)<__VA_ARGS__>(__FILE__, __LINE__, 0))) #define DOCTEST_TEST_CASE_TEMPLATE_INVOKE(id, ...) \ DOCTEST_TEST_CASE_TEMPLATE_INSTANTIATE_IMPL(id, DOCTEST_ANONYMOUS(DOCTEST_ANON_TMP_), std::tuple<__VA_ARGS__>) \ static_assert(true, "") #define DOCTEST_TEST_CASE_TEMPLATE_APPLY(id, ...) \ DOCTEST_TEST_CASE_TEMPLATE_INSTANTIATE_IMPL(id, DOCTEST_ANONYMOUS(DOCTEST_ANON_TMP_), __VA_ARGS__) \ static_assert(true, "") #define DOCTEST_TEST_CASE_TEMPLATE_IMPL(dec, T, anon, ...) \ DOCTEST_TEST_CASE_TEMPLATE_DEFINE_IMPL(dec, T, DOCTEST_CAT(anon, ITERATOR), anon); \ DOCTEST_TEST_CASE_TEMPLATE_INSTANTIATE_IMPL(anon, anon, std::tuple<__VA_ARGS__>) \ template \ static void anon() #define DOCTEST_TEST_CASE_TEMPLATE(dec, T, ...) \ DOCTEST_TEST_CASE_TEMPLATE_IMPL(dec, T, DOCTEST_ANONYMOUS(DOCTEST_ANON_TMP_), __VA_ARGS__) // for subcases #define DOCTEST_SUBCASE(name) \ if(const doctest::detail::Subcase & DOCTEST_ANONYMOUS(DOCTEST_ANON_SUBCASE_) DOCTEST_UNUSED = \ doctest::detail::Subcase(name, __FILE__, __LINE__)) // for grouping tests in test suites by using code blocks #define DOCTEST_TEST_SUITE_IMPL(decorators, ns_name) \ namespace ns_name { namespace doctest_detail_test_suite_ns { \ static DOCTEST_NOINLINE doctest::detail::TestSuite& getCurrentTestSuite() { \ DOCTEST_MSVC_SUPPRESS_WARNING_WITH_PUSH(4640) \ DOCTEST_CLANG_SUPPRESS_WARNING_WITH_PUSH("-Wexit-time-destructors") \ DOCTEST_GCC_SUPPRESS_WARNING_WITH_PUSH("-Wmissing-field-initializers") \ static doctest::detail::TestSuite data{}; \ static bool inited = false; \ DOCTEST_MSVC_SUPPRESS_WARNING_POP \ DOCTEST_CLANG_SUPPRESS_WARNING_POP \ DOCTEST_GCC_SUPPRESS_WARNING_POP \ if(!inited) { \ data* decorators; \ inited = true; \ } \ return data; \ } \ } \ } \ namespace ns_name #define DOCTEST_TEST_SUITE(decorators) \ DOCTEST_TEST_SUITE_IMPL(decorators, DOCTEST_ANONYMOUS(DOCTEST_ANON_SUITE_)) // for starting a testsuite block #define DOCTEST_TEST_SUITE_BEGIN(decorators) \ DOCTEST_GLOBAL_NO_WARNINGS(DOCTEST_ANONYMOUS(DOCTEST_ANON_VAR_), \ doctest::detail::setTestSuite(doctest::detail::TestSuite() * decorators)) \ static_assert(true, "") // for ending a testsuite block #define DOCTEST_TEST_SUITE_END \ DOCTEST_GLOBAL_NO_WARNINGS(DOCTEST_ANONYMOUS(DOCTEST_ANON_VAR_), \ doctest::detail::setTestSuite(doctest::detail::TestSuite() * "")) \ typedef int DOCTEST_ANONYMOUS(DOCTEST_ANON_FOR_SEMICOLON_) // for registering exception translators #define DOCTEST_REGISTER_EXCEPTION_TRANSLATOR_IMPL(translatorName, signature) \ inline doctest::String translatorName(signature); \ DOCTEST_GLOBAL_NO_WARNINGS(DOCTEST_ANONYMOUS(DOCTEST_ANON_TRANSLATOR_), \ doctest::registerExceptionTranslator(translatorName)) \ doctest::String translatorName(signature) #define DOCTEST_REGISTER_EXCEPTION_TRANSLATOR(signature) \ DOCTEST_REGISTER_EXCEPTION_TRANSLATOR_IMPL(DOCTEST_ANONYMOUS(DOCTEST_ANON_TRANSLATOR_), \ signature) // for registering reporters #define DOCTEST_REGISTER_REPORTER(name, priority, reporter) \ DOCTEST_GLOBAL_NO_WARNINGS(DOCTEST_ANONYMOUS(DOCTEST_ANON_REPORTER_), \ doctest::registerReporter(name, priority, true)) \ static_assert(true, "") // for registering listeners #define DOCTEST_REGISTER_LISTENER(name, priority, reporter) \ DOCTEST_GLOBAL_NO_WARNINGS(DOCTEST_ANONYMOUS(DOCTEST_ANON_REPORTER_), \ doctest::registerReporter(name, priority, false)) \ static_assert(true, "") // clang-format off // for logging - disabling formatting because it's important to have these on 2 separate lines - see PR #557 #define DOCTEST_INFO(...) \ DOCTEST_INFO_IMPL(DOCTEST_ANONYMOUS(DOCTEST_CAPTURE_), \ DOCTEST_ANONYMOUS(DOCTEST_CAPTURE_OTHER_), \ __VA_ARGS__) // clang-format on #define DOCTEST_INFO_IMPL(mb_name, s_name, ...) \ auto DOCTEST_ANONYMOUS(DOCTEST_CAPTURE_) = doctest::detail::MakeContextScope( \ [&](std::ostream* s_name) { \ doctest::detail::MessageBuilder mb_name(__FILE__, __LINE__, doctest::assertType::is_warn); \ mb_name.m_stream = s_name; \ mb_name * __VA_ARGS__; \ }) #define DOCTEST_CAPTURE(x) DOCTEST_INFO(#x " := ", x) #define DOCTEST_ADD_AT_IMPL(type, file, line, mb, ...) \ [&] { \ doctest::detail::MessageBuilder mb(file, line, doctest::assertType::type); \ mb * __VA_ARGS__; \ if(mb.log()) \ DOCTEST_BREAK_INTO_DEBUGGER(); \ mb.react(); \ }() // clang-format off #define DOCTEST_ADD_MESSAGE_AT(file, line, ...) DOCTEST_ADD_AT_IMPL(is_warn, file, line, DOCTEST_ANONYMOUS(DOCTEST_MESSAGE_), __VA_ARGS__) #define DOCTEST_ADD_FAIL_CHECK_AT(file, line, ...) DOCTEST_ADD_AT_IMPL(is_check, file, line, DOCTEST_ANONYMOUS(DOCTEST_MESSAGE_), __VA_ARGS__) #define DOCTEST_ADD_FAIL_AT(file, line, ...) DOCTEST_ADD_AT_IMPL(is_require, file, line, DOCTEST_ANONYMOUS(DOCTEST_MESSAGE_), __VA_ARGS__) // clang-format on #define DOCTEST_MESSAGE(...) DOCTEST_ADD_MESSAGE_AT(__FILE__, __LINE__, __VA_ARGS__) #define DOCTEST_FAIL_CHECK(...) DOCTEST_ADD_FAIL_CHECK_AT(__FILE__, __LINE__, __VA_ARGS__) #define DOCTEST_FAIL(...) DOCTEST_ADD_FAIL_AT(__FILE__, __LINE__, __VA_ARGS__) #define DOCTEST_TO_LVALUE(...) __VA_ARGS__ // Not removed to keep backwards compatibility. #ifndef DOCTEST_CONFIG_SUPER_FAST_ASSERTS #define DOCTEST_ASSERT_IMPLEMENT_2(assert_type, ...) \ DOCTEST_CLANG_SUPPRESS_WARNING_WITH_PUSH("-Woverloaded-shift-op-parentheses") \ doctest::detail::ResultBuilder DOCTEST_RB(doctest::assertType::assert_type, __FILE__, \ __LINE__, #__VA_ARGS__); \ DOCTEST_WRAP_IN_TRY(DOCTEST_RB.setResult( \ doctest::detail::ExpressionDecomposer(doctest::assertType::assert_type) \ << __VA_ARGS__)) \ DOCTEST_ASSERT_LOG_REACT_RETURN(DOCTEST_RB) \ DOCTEST_CLANG_SUPPRESS_WARNING_POP #define DOCTEST_ASSERT_IMPLEMENT_1(assert_type, ...) \ [&] { \ DOCTEST_ASSERT_IMPLEMENT_2(assert_type, __VA_ARGS__); \ }() #else // DOCTEST_CONFIG_SUPER_FAST_ASSERTS // necessary for _MESSAGE #define DOCTEST_ASSERT_IMPLEMENT_2 DOCTEST_ASSERT_IMPLEMENT_1 #define DOCTEST_ASSERT_IMPLEMENT_1(assert_type, ...) \ DOCTEST_CLANG_SUPPRESS_WARNING_WITH_PUSH("-Woverloaded-shift-op-parentheses") \ doctest::detail::decomp_assert( \ doctest::assertType::assert_type, __FILE__, __LINE__, #__VA_ARGS__, \ doctest::detail::ExpressionDecomposer(doctest::assertType::assert_type) \ << __VA_ARGS__) DOCTEST_CLANG_SUPPRESS_WARNING_POP #endif // DOCTEST_CONFIG_SUPER_FAST_ASSERTS #define DOCTEST_WARN(...) DOCTEST_ASSERT_IMPLEMENT_1(DT_WARN, __VA_ARGS__) #define DOCTEST_CHECK(...) DOCTEST_ASSERT_IMPLEMENT_1(DT_CHECK, __VA_ARGS__) #define DOCTEST_REQUIRE(...) DOCTEST_ASSERT_IMPLEMENT_1(DT_REQUIRE, __VA_ARGS__) #define DOCTEST_WARN_FALSE(...) DOCTEST_ASSERT_IMPLEMENT_1(DT_WARN_FALSE, __VA_ARGS__) #define DOCTEST_CHECK_FALSE(...) DOCTEST_ASSERT_IMPLEMENT_1(DT_CHECK_FALSE, __VA_ARGS__) #define DOCTEST_REQUIRE_FALSE(...) DOCTEST_ASSERT_IMPLEMENT_1(DT_REQUIRE_FALSE, __VA_ARGS__) // clang-format off #define DOCTEST_WARN_MESSAGE(cond, ...) [&] {DOCTEST_INFO(__VA_ARGS__); DOCTEST_ASSERT_IMPLEMENT_2(DT_WARN, cond); }() #define DOCTEST_CHECK_MESSAGE(cond, ...) [&] {DOCTEST_INFO(__VA_ARGS__); DOCTEST_ASSERT_IMPLEMENT_2(DT_CHECK, cond); }() #define DOCTEST_REQUIRE_MESSAGE(cond, ...) [&] {DOCTEST_INFO(__VA_ARGS__); DOCTEST_ASSERT_IMPLEMENT_2(DT_REQUIRE, cond); }() #define DOCTEST_WARN_FALSE_MESSAGE(cond, ...) [&] {DOCTEST_INFO(__VA_ARGS__); DOCTEST_ASSERT_IMPLEMENT_2(DT_WARN_FALSE, cond); }() #define DOCTEST_CHECK_FALSE_MESSAGE(cond, ...) [&] {DOCTEST_INFO(__VA_ARGS__); DOCTEST_ASSERT_IMPLEMENT_2(DT_CHECK_FALSE, cond); }() #define DOCTEST_REQUIRE_FALSE_MESSAGE(cond, ...) [&] {DOCTEST_INFO(__VA_ARGS__); DOCTEST_ASSERT_IMPLEMENT_2(DT_REQUIRE_FALSE, cond); }() // clang-format on #define DOCTEST_ASSERT_THROWS_AS(expr, assert_type, message, ...) \ [&] { \ if(!doctest::getContextOptions()->no_throw) { \ doctest::detail::ResultBuilder DOCTEST_RB(doctest::assertType::assert_type, __FILE__, \ __LINE__, #expr, #__VA_ARGS__, message); \ try { \ DOCTEST_CAST_TO_VOID(expr) \ } catch(const typename doctest::detail::remove_const< \ typename doctest::detail::remove_reference<__VA_ARGS__>::type>::type&) { \ DOCTEST_RB.translateException(); \ DOCTEST_RB.m_threw_as = true; \ } catch(...) { DOCTEST_RB.translateException(); } \ DOCTEST_ASSERT_LOG_REACT_RETURN(DOCTEST_RB); \ } else { \ return false; \ } \ }() #define DOCTEST_ASSERT_THROWS_WITH(expr, expr_str, assert_type, ...) \ [&] { \ if(!doctest::getContextOptions()->no_throw) { \ doctest::detail::ResultBuilder DOCTEST_RB(doctest::assertType::assert_type, __FILE__, \ __LINE__, expr_str, "", __VA_ARGS__); \ try { \ DOCTEST_CAST_TO_VOID(expr) \ } catch(...) { DOCTEST_RB.translateException(); } \ DOCTEST_ASSERT_LOG_REACT_RETURN(DOCTEST_RB); \ } else { \ return false; \ } \ }() #define DOCTEST_ASSERT_NOTHROW(assert_type, ...) \ [&] { \ doctest::detail::ResultBuilder DOCTEST_RB(doctest::assertType::assert_type, __FILE__, \ __LINE__, #__VA_ARGS__); \ try { \ DOCTEST_CAST_TO_VOID(__VA_ARGS__) \ } catch(...) { DOCTEST_RB.translateException(); } \ DOCTEST_ASSERT_LOG_REACT_RETURN(DOCTEST_RB); \ }() // clang-format off #define DOCTEST_WARN_THROWS(...) DOCTEST_ASSERT_THROWS_WITH((__VA_ARGS__), #__VA_ARGS__, DT_WARN_THROWS, "") #define DOCTEST_CHECK_THROWS(...) DOCTEST_ASSERT_THROWS_WITH((__VA_ARGS__), #__VA_ARGS__, DT_CHECK_THROWS, "") #define DOCTEST_REQUIRE_THROWS(...) DOCTEST_ASSERT_THROWS_WITH((__VA_ARGS__), #__VA_ARGS__, DT_REQUIRE_THROWS, "") #define DOCTEST_WARN_THROWS_AS(expr, ...) DOCTEST_ASSERT_THROWS_AS(expr, DT_WARN_THROWS_AS, "", __VA_ARGS__) #define DOCTEST_CHECK_THROWS_AS(expr, ...) DOCTEST_ASSERT_THROWS_AS(expr, DT_CHECK_THROWS_AS, "", __VA_ARGS__) #define DOCTEST_REQUIRE_THROWS_AS(expr, ...) DOCTEST_ASSERT_THROWS_AS(expr, DT_REQUIRE_THROWS_AS, "", __VA_ARGS__) #define DOCTEST_WARN_THROWS_WITH(expr, ...) DOCTEST_ASSERT_THROWS_WITH(expr, #expr, DT_WARN_THROWS_WITH, __VA_ARGS__) #define DOCTEST_CHECK_THROWS_WITH(expr, ...) DOCTEST_ASSERT_THROWS_WITH(expr, #expr, DT_CHECK_THROWS_WITH, __VA_ARGS__) #define DOCTEST_REQUIRE_THROWS_WITH(expr, ...) DOCTEST_ASSERT_THROWS_WITH(expr, #expr, DT_REQUIRE_THROWS_WITH, __VA_ARGS__) #define DOCTEST_WARN_THROWS_WITH_AS(expr, message, ...) DOCTEST_ASSERT_THROWS_AS(expr, DT_WARN_THROWS_WITH_AS, message, __VA_ARGS__) #define DOCTEST_CHECK_THROWS_WITH_AS(expr, message, ...) DOCTEST_ASSERT_THROWS_AS(expr, DT_CHECK_THROWS_WITH_AS, message, __VA_ARGS__) #define DOCTEST_REQUIRE_THROWS_WITH_AS(expr, message, ...) DOCTEST_ASSERT_THROWS_AS(expr, DT_REQUIRE_THROWS_WITH_AS, message, __VA_ARGS__) #define DOCTEST_WARN_NOTHROW(...) DOCTEST_ASSERT_NOTHROW(DT_WARN_NOTHROW, __VA_ARGS__) #define DOCTEST_CHECK_NOTHROW(...) DOCTEST_ASSERT_NOTHROW(DT_CHECK_NOTHROW, __VA_ARGS__) #define DOCTEST_REQUIRE_NOTHROW(...) DOCTEST_ASSERT_NOTHROW(DT_REQUIRE_NOTHROW, __VA_ARGS__) #define DOCTEST_WARN_THROWS_MESSAGE(expr, ...) [&] {DOCTEST_INFO(__VA_ARGS__); DOCTEST_WARN_THROWS(expr); }() #define DOCTEST_CHECK_THROWS_MESSAGE(expr, ...) [&] {DOCTEST_INFO(__VA_ARGS__); DOCTEST_CHECK_THROWS(expr); }() #define DOCTEST_REQUIRE_THROWS_MESSAGE(expr, ...) [&] {DOCTEST_INFO(__VA_ARGS__); DOCTEST_REQUIRE_THROWS(expr); }() #define DOCTEST_WARN_THROWS_AS_MESSAGE(expr, ex, ...) [&] {DOCTEST_INFO(__VA_ARGS__); DOCTEST_WARN_THROWS_AS(expr, ex); }() #define DOCTEST_CHECK_THROWS_AS_MESSAGE(expr, ex, ...) [&] {DOCTEST_INFO(__VA_ARGS__); DOCTEST_CHECK_THROWS_AS(expr, ex); }() #define DOCTEST_REQUIRE_THROWS_AS_MESSAGE(expr, ex, ...) [&] {DOCTEST_INFO(__VA_ARGS__); DOCTEST_REQUIRE_THROWS_AS(expr, ex); }() #define DOCTEST_WARN_THROWS_WITH_MESSAGE(expr, with, ...) [&] {DOCTEST_INFO(__VA_ARGS__); DOCTEST_WARN_THROWS_WITH(expr, with); }() #define DOCTEST_CHECK_THROWS_WITH_MESSAGE(expr, with, ...) [&] {DOCTEST_INFO(__VA_ARGS__); DOCTEST_CHECK_THROWS_WITH(expr, with); }() #define DOCTEST_REQUIRE_THROWS_WITH_MESSAGE(expr, with, ...) [&] {DOCTEST_INFO(__VA_ARGS__); DOCTEST_REQUIRE_THROWS_WITH(expr, with); }() #define DOCTEST_WARN_THROWS_WITH_AS_MESSAGE(expr, with, ex, ...) [&] {DOCTEST_INFO(__VA_ARGS__); DOCTEST_WARN_THROWS_WITH_AS(expr, with, ex); }() #define DOCTEST_CHECK_THROWS_WITH_AS_MESSAGE(expr, with, ex, ...) [&] {DOCTEST_INFO(__VA_ARGS__); DOCTEST_CHECK_THROWS_WITH_AS(expr, with, ex); }() #define DOCTEST_REQUIRE_THROWS_WITH_AS_MESSAGE(expr, with, ex, ...) [&] {DOCTEST_INFO(__VA_ARGS__); DOCTEST_REQUIRE_THROWS_WITH_AS(expr, with, ex); }() #define DOCTEST_WARN_NOTHROW_MESSAGE(expr, ...) [&] {DOCTEST_INFO(__VA_ARGS__); DOCTEST_WARN_NOTHROW(expr); }() #define DOCTEST_CHECK_NOTHROW_MESSAGE(expr, ...) [&] {DOCTEST_INFO(__VA_ARGS__); DOCTEST_CHECK_NOTHROW(expr); }() #define DOCTEST_REQUIRE_NOTHROW_MESSAGE(expr, ...) [&] {DOCTEST_INFO(__VA_ARGS__); DOCTEST_REQUIRE_NOTHROW(expr); }() // clang-format on #ifndef DOCTEST_CONFIG_SUPER_FAST_ASSERTS #define DOCTEST_BINARY_ASSERT(assert_type, comp, ...) \ [&] { \ doctest::detail::ResultBuilder DOCTEST_RB(doctest::assertType::assert_type, __FILE__, \ __LINE__, #__VA_ARGS__); \ DOCTEST_WRAP_IN_TRY( \ DOCTEST_RB.binary_assert( \ __VA_ARGS__)) \ DOCTEST_ASSERT_LOG_REACT_RETURN(DOCTEST_RB); \ }() #define DOCTEST_UNARY_ASSERT(assert_type, ...) \ [&] { \ doctest::detail::ResultBuilder DOCTEST_RB(doctest::assertType::assert_type, __FILE__, \ __LINE__, #__VA_ARGS__); \ DOCTEST_WRAP_IN_TRY(DOCTEST_RB.unary_assert(__VA_ARGS__)) \ DOCTEST_ASSERT_LOG_REACT_RETURN(DOCTEST_RB); \ }() #else // DOCTEST_CONFIG_SUPER_FAST_ASSERTS #define DOCTEST_BINARY_ASSERT(assert_type, comparison, ...) \ doctest::detail::binary_assert( \ doctest::assertType::assert_type, __FILE__, __LINE__, #__VA_ARGS__, __VA_ARGS__) #define DOCTEST_UNARY_ASSERT(assert_type, ...) \ doctest::detail::unary_assert(doctest::assertType::assert_type, __FILE__, __LINE__, \ #__VA_ARGS__, __VA_ARGS__) #endif // DOCTEST_CONFIG_SUPER_FAST_ASSERTS #define DOCTEST_WARN_EQ(...) DOCTEST_BINARY_ASSERT(DT_WARN_EQ, eq, __VA_ARGS__) #define DOCTEST_CHECK_EQ(...) DOCTEST_BINARY_ASSERT(DT_CHECK_EQ, eq, __VA_ARGS__) #define DOCTEST_REQUIRE_EQ(...) DOCTEST_BINARY_ASSERT(DT_REQUIRE_EQ, eq, __VA_ARGS__) #define DOCTEST_WARN_NE(...) DOCTEST_BINARY_ASSERT(DT_WARN_NE, ne, __VA_ARGS__) #define DOCTEST_CHECK_NE(...) DOCTEST_BINARY_ASSERT(DT_CHECK_NE, ne, __VA_ARGS__) #define DOCTEST_REQUIRE_NE(...) DOCTEST_BINARY_ASSERT(DT_REQUIRE_NE, ne, __VA_ARGS__) #define DOCTEST_WARN_GT(...) DOCTEST_BINARY_ASSERT(DT_WARN_GT, gt, __VA_ARGS__) #define DOCTEST_CHECK_GT(...) DOCTEST_BINARY_ASSERT(DT_CHECK_GT, gt, __VA_ARGS__) #define DOCTEST_REQUIRE_GT(...) DOCTEST_BINARY_ASSERT(DT_REQUIRE_GT, gt, __VA_ARGS__) #define DOCTEST_WARN_LT(...) DOCTEST_BINARY_ASSERT(DT_WARN_LT, lt, __VA_ARGS__) #define DOCTEST_CHECK_LT(...) DOCTEST_BINARY_ASSERT(DT_CHECK_LT, lt, __VA_ARGS__) #define DOCTEST_REQUIRE_LT(...) DOCTEST_BINARY_ASSERT(DT_REQUIRE_LT, lt, __VA_ARGS__) #define DOCTEST_WARN_GE(...) DOCTEST_BINARY_ASSERT(DT_WARN_GE, ge, __VA_ARGS__) #define DOCTEST_CHECK_GE(...) DOCTEST_BINARY_ASSERT(DT_CHECK_GE, ge, __VA_ARGS__) #define DOCTEST_REQUIRE_GE(...) DOCTEST_BINARY_ASSERT(DT_REQUIRE_GE, ge, __VA_ARGS__) #define DOCTEST_WARN_LE(...) DOCTEST_BINARY_ASSERT(DT_WARN_LE, le, __VA_ARGS__) #define DOCTEST_CHECK_LE(...) DOCTEST_BINARY_ASSERT(DT_CHECK_LE, le, __VA_ARGS__) #define DOCTEST_REQUIRE_LE(...) DOCTEST_BINARY_ASSERT(DT_REQUIRE_LE, le, __VA_ARGS__) #define DOCTEST_WARN_UNARY(...) DOCTEST_UNARY_ASSERT(DT_WARN_UNARY, __VA_ARGS__) #define DOCTEST_CHECK_UNARY(...) DOCTEST_UNARY_ASSERT(DT_CHECK_UNARY, __VA_ARGS__) #define DOCTEST_REQUIRE_UNARY(...) DOCTEST_UNARY_ASSERT(DT_REQUIRE_UNARY, __VA_ARGS__) #define DOCTEST_WARN_UNARY_FALSE(...) DOCTEST_UNARY_ASSERT(DT_WARN_UNARY_FALSE, __VA_ARGS__) #define DOCTEST_CHECK_UNARY_FALSE(...) DOCTEST_UNARY_ASSERT(DT_CHECK_UNARY_FALSE, __VA_ARGS__) #define DOCTEST_REQUIRE_UNARY_FALSE(...) DOCTEST_UNARY_ASSERT(DT_REQUIRE_UNARY_FALSE, __VA_ARGS__) #ifdef DOCTEST_CONFIG_NO_EXCEPTIONS #undef DOCTEST_WARN_THROWS #undef DOCTEST_CHECK_THROWS #undef DOCTEST_REQUIRE_THROWS #undef DOCTEST_WARN_THROWS_AS #undef DOCTEST_CHECK_THROWS_AS #undef DOCTEST_REQUIRE_THROWS_AS #undef DOCTEST_WARN_THROWS_WITH #undef DOCTEST_CHECK_THROWS_WITH #undef DOCTEST_REQUIRE_THROWS_WITH #undef DOCTEST_WARN_THROWS_WITH_AS #undef DOCTEST_CHECK_THROWS_WITH_AS #undef DOCTEST_REQUIRE_THROWS_WITH_AS #undef DOCTEST_WARN_NOTHROW #undef DOCTEST_CHECK_NOTHROW #undef DOCTEST_REQUIRE_NOTHROW #undef DOCTEST_WARN_THROWS_MESSAGE #undef DOCTEST_CHECK_THROWS_MESSAGE #undef DOCTEST_REQUIRE_THROWS_MESSAGE #undef DOCTEST_WARN_THROWS_AS_MESSAGE #undef DOCTEST_CHECK_THROWS_AS_MESSAGE #undef DOCTEST_REQUIRE_THROWS_AS_MESSAGE #undef DOCTEST_WARN_THROWS_WITH_MESSAGE #undef DOCTEST_CHECK_THROWS_WITH_MESSAGE #undef DOCTEST_REQUIRE_THROWS_WITH_MESSAGE #undef DOCTEST_WARN_THROWS_WITH_AS_MESSAGE #undef DOCTEST_CHECK_THROWS_WITH_AS_MESSAGE #undef DOCTEST_REQUIRE_THROWS_WITH_AS_MESSAGE #undef DOCTEST_WARN_NOTHROW_MESSAGE #undef DOCTEST_CHECK_NOTHROW_MESSAGE #undef DOCTEST_REQUIRE_NOTHROW_MESSAGE #ifdef DOCTEST_CONFIG_NO_EXCEPTIONS_BUT_WITH_ALL_ASSERTS #define DOCTEST_WARN_THROWS(...) ([] { return false; }) #define DOCTEST_CHECK_THROWS(...) ([] { return false; }) #define DOCTEST_REQUIRE_THROWS(...) ([] { return false; }) #define DOCTEST_WARN_THROWS_AS(expr, ...) ([] { return false; }) #define DOCTEST_CHECK_THROWS_AS(expr, ...) ([] { return false; }) #define DOCTEST_REQUIRE_THROWS_AS(expr, ...) ([] { return false; }) #define DOCTEST_WARN_THROWS_WITH(expr, ...) ([] { return false; }) #define DOCTEST_CHECK_THROWS_WITH(expr, ...) ([] { return false; }) #define DOCTEST_REQUIRE_THROWS_WITH(expr, ...) ([] { return false; }) #define DOCTEST_WARN_THROWS_WITH_AS(expr, with, ...) ([] { return false; }) #define DOCTEST_CHECK_THROWS_WITH_AS(expr, with, ...) ([] { return false; }) #define DOCTEST_REQUIRE_THROWS_WITH_AS(expr, with, ...) ([] { return false; }) #define DOCTEST_WARN_NOTHROW(...) ([] { return false; }) #define DOCTEST_CHECK_NOTHROW(...) ([] { return false; }) #define DOCTEST_REQUIRE_NOTHROW(...) ([] { return false; }) #define DOCTEST_WARN_THROWS_MESSAGE(expr, ...) ([] { return false; }) #define DOCTEST_CHECK_THROWS_MESSAGE(expr, ...) ([] { return false; }) #define DOCTEST_REQUIRE_THROWS_MESSAGE(expr, ...) ([] { return false; }) #define DOCTEST_WARN_THROWS_AS_MESSAGE(expr, ex, ...) ([] { return false; }) #define DOCTEST_CHECK_THROWS_AS_MESSAGE(expr, ex, ...) ([] { return false; }) #define DOCTEST_REQUIRE_THROWS_AS_MESSAGE(expr, ex, ...) ([] { return false; }) #define DOCTEST_WARN_THROWS_WITH_MESSAGE(expr, with, ...) ([] { return false; }) #define DOCTEST_CHECK_THROWS_WITH_MESSAGE(expr, with, ...) ([] { return false; }) #define DOCTEST_REQUIRE_THROWS_WITH_MESSAGE(expr, with, ...) ([] { return false; }) #define DOCTEST_WARN_THROWS_WITH_AS_MESSAGE(expr, with, ex, ...) ([] { return false; }) #define DOCTEST_CHECK_THROWS_WITH_AS_MESSAGE(expr, with, ex, ...) ([] { return false; }) #define DOCTEST_REQUIRE_THROWS_WITH_AS_MESSAGE(expr, with, ex, ...) ([] { return false; }) #define DOCTEST_WARN_NOTHROW_MESSAGE(expr, ...) ([] { return false; }) #define DOCTEST_CHECK_NOTHROW_MESSAGE(expr, ...) ([] { return false; }) #define DOCTEST_REQUIRE_NOTHROW_MESSAGE(expr, ...) ([] { return false; }) #else // DOCTEST_CONFIG_NO_EXCEPTIONS_BUT_WITH_ALL_ASSERTS #undef DOCTEST_REQUIRE #undef DOCTEST_REQUIRE_FALSE #undef DOCTEST_REQUIRE_MESSAGE #undef DOCTEST_REQUIRE_FALSE_MESSAGE #undef DOCTEST_REQUIRE_EQ #undef DOCTEST_REQUIRE_NE #undef DOCTEST_REQUIRE_GT #undef DOCTEST_REQUIRE_LT #undef DOCTEST_REQUIRE_GE #undef DOCTEST_REQUIRE_LE #undef DOCTEST_REQUIRE_UNARY #undef DOCTEST_REQUIRE_UNARY_FALSE #endif // DOCTEST_CONFIG_NO_EXCEPTIONS_BUT_WITH_ALL_ASSERTS #endif // DOCTEST_CONFIG_NO_EXCEPTIONS // ================================================================================================= // == WHAT FOLLOWS IS VERSIONS OF THE MACROS THAT DO NOT DO ANY REGISTERING! == // == THIS CAN BE ENABLED BY DEFINING DOCTEST_CONFIG_DISABLE GLOBALLY! == // ================================================================================================= #else // DOCTEST_CONFIG_DISABLE #define DOCTEST_IMPLEMENT_FIXTURE(der, base, func, name) \ namespace { \ template \ struct der : public base \ { void f(); }; \ } \ template \ inline void der::f() #define DOCTEST_CREATE_AND_REGISTER_FUNCTION(f, name) \ template \ static inline void f() // for registering tests #define DOCTEST_TEST_CASE(name) \ DOCTEST_CREATE_AND_REGISTER_FUNCTION(DOCTEST_ANONYMOUS(DOCTEST_ANON_FUNC_), name) // for registering tests in classes #define DOCTEST_TEST_CASE_CLASS(name) \ DOCTEST_CREATE_AND_REGISTER_FUNCTION(DOCTEST_ANONYMOUS(DOCTEST_ANON_FUNC_), name) // for registering tests with a fixture #define DOCTEST_TEST_CASE_FIXTURE(x, name) \ DOCTEST_IMPLEMENT_FIXTURE(DOCTEST_ANONYMOUS(DOCTEST_ANON_CLASS_), x, \ DOCTEST_ANONYMOUS(DOCTEST_ANON_FUNC_), name) // for converting types to strings without the header and demangling #define DOCTEST_TYPE_TO_STRING(...) static_assert(true, "") #define DOCTEST_TYPE_TO_STRING_IMPL(...) // for typed tests #define DOCTEST_TEST_CASE_TEMPLATE(name, type, ...) \ template \ inline void DOCTEST_ANONYMOUS(DOCTEST_ANON_TMP_)() #define DOCTEST_TEST_CASE_TEMPLATE_DEFINE(name, type, id) \ template \ inline void DOCTEST_ANONYMOUS(DOCTEST_ANON_TMP_)() #define DOCTEST_TEST_CASE_TEMPLATE_INVOKE(id, ...) static_assert(true, "") #define DOCTEST_TEST_CASE_TEMPLATE_APPLY(id, ...) static_assert(true, "") // for subcases #define DOCTEST_SUBCASE(name) // for a testsuite block #define DOCTEST_TEST_SUITE(name) namespace // for starting a testsuite block #define DOCTEST_TEST_SUITE_BEGIN(name) static_assert(true, "") // for ending a testsuite block #define DOCTEST_TEST_SUITE_END typedef int DOCTEST_ANONYMOUS(DOCTEST_ANON_FOR_SEMICOLON_) #define DOCTEST_REGISTER_EXCEPTION_TRANSLATOR(signature) \ template \ static inline doctest::String DOCTEST_ANONYMOUS(DOCTEST_ANON_TRANSLATOR_)(signature) #define DOCTEST_REGISTER_REPORTER(name, priority, reporter) #define DOCTEST_REGISTER_LISTENER(name, priority, reporter) #define DOCTEST_INFO(...) (static_cast(0)) #define DOCTEST_CAPTURE(x) (static_cast(0)) #define DOCTEST_ADD_MESSAGE_AT(file, line, ...) (static_cast(0)) #define DOCTEST_ADD_FAIL_CHECK_AT(file, line, ...) (static_cast(0)) #define DOCTEST_ADD_FAIL_AT(file, line, ...) (static_cast(0)) #define DOCTEST_MESSAGE(...) (static_cast(0)) #define DOCTEST_FAIL_CHECK(...) (static_cast(0)) #define DOCTEST_FAIL(...) (static_cast(0)) #ifdef DOCTEST_CONFIG_EVALUATE_ASSERTS_EVEN_WHEN_DISABLED #define DOCTEST_WARN(...) [&] { return __VA_ARGS__; }() #define DOCTEST_CHECK(...) [&] { return __VA_ARGS__; }() #define DOCTEST_REQUIRE(...) [&] { return __VA_ARGS__; }() #define DOCTEST_WARN_FALSE(...) [&] { return !(__VA_ARGS__); }() #define DOCTEST_CHECK_FALSE(...) [&] { return !(__VA_ARGS__); }() #define DOCTEST_REQUIRE_FALSE(...) [&] { return !(__VA_ARGS__); }() #define DOCTEST_WARN_MESSAGE(cond, ...) [&] { return cond; }() #define DOCTEST_CHECK_MESSAGE(cond, ...) [&] { return cond; }() #define DOCTEST_REQUIRE_MESSAGE(cond, ...) [&] { return cond; }() #define DOCTEST_WARN_FALSE_MESSAGE(cond, ...) [&] { return !(cond); }() #define DOCTEST_CHECK_FALSE_MESSAGE(cond, ...) [&] { return !(cond); }() #define DOCTEST_REQUIRE_FALSE_MESSAGE(cond, ...) [&] { return !(cond); }() namespace doctest { namespace detail { #define DOCTEST_RELATIONAL_OP(name, op) \ template \ bool name(const DOCTEST_REF_WRAP(L) lhs, const DOCTEST_REF_WRAP(R) rhs) { return lhs op rhs; } DOCTEST_RELATIONAL_OP(eq, ==) DOCTEST_RELATIONAL_OP(ne, !=) DOCTEST_RELATIONAL_OP(lt, <) DOCTEST_RELATIONAL_OP(gt, >) DOCTEST_RELATIONAL_OP(le, <=) DOCTEST_RELATIONAL_OP(ge, >=) } // namespace detail } // namespace doctest #define DOCTEST_WARN_EQ(...) [&] { return doctest::detail::eq(__VA_ARGS__); }() #define DOCTEST_CHECK_EQ(...) [&] { return doctest::detail::eq(__VA_ARGS__); }() #define DOCTEST_REQUIRE_EQ(...) [&] { return doctest::detail::eq(__VA_ARGS__); }() #define DOCTEST_WARN_NE(...) [&] { return doctest::detail::ne(__VA_ARGS__); }() #define DOCTEST_CHECK_NE(...) [&] { return doctest::detail::ne(__VA_ARGS__); }() #define DOCTEST_REQUIRE_NE(...) [&] { return doctest::detail::ne(__VA_ARGS__); }() #define DOCTEST_WARN_LT(...) [&] { return doctest::detail::lt(__VA_ARGS__); }() #define DOCTEST_CHECK_LT(...) [&] { return doctest::detail::lt(__VA_ARGS__); }() #define DOCTEST_REQUIRE_LT(...) [&] { return doctest::detail::lt(__VA_ARGS__); }() #define DOCTEST_WARN_GT(...) [&] { return doctest::detail::gt(__VA_ARGS__); }() #define DOCTEST_CHECK_GT(...) [&] { return doctest::detail::gt(__VA_ARGS__); }() #define DOCTEST_REQUIRE_GT(...) [&] { return doctest::detail::gt(__VA_ARGS__); }() #define DOCTEST_WARN_LE(...) [&] { return doctest::detail::le(__VA_ARGS__); }() #define DOCTEST_CHECK_LE(...) [&] { return doctest::detail::le(__VA_ARGS__); }() #define DOCTEST_REQUIRE_LE(...) [&] { return doctest::detail::le(__VA_ARGS__); }() #define DOCTEST_WARN_GE(...) [&] { return doctest::detail::ge(__VA_ARGS__); }() #define DOCTEST_CHECK_GE(...) [&] { return doctest::detail::ge(__VA_ARGS__); }() #define DOCTEST_REQUIRE_GE(...) [&] { return doctest::detail::ge(__VA_ARGS__); }() #define DOCTEST_WARN_UNARY(...) [&] { return __VA_ARGS__; }() #define DOCTEST_CHECK_UNARY(...) [&] { return __VA_ARGS__; }() #define DOCTEST_REQUIRE_UNARY(...) [&] { return __VA_ARGS__; }() #define DOCTEST_WARN_UNARY_FALSE(...) [&] { return !(__VA_ARGS__); }() #define DOCTEST_CHECK_UNARY_FALSE(...) [&] { return !(__VA_ARGS__); }() #define DOCTEST_REQUIRE_UNARY_FALSE(...) [&] { return !(__VA_ARGS__); }() #else // DOCTEST_CONFIG_EVALUATE_ASSERTS_EVEN_WHEN_DISABLED #define DOCTEST_WARN(...) ([] { return false; }) #define DOCTEST_CHECK(...) ([] { return false; }) #define DOCTEST_REQUIRE(...) ([] { return false; }) #define DOCTEST_WARN_FALSE(...) ([] { return false; }) #define DOCTEST_CHECK_FALSE(...) ([] { return false; }) #define DOCTEST_REQUIRE_FALSE(...) ([] { return false; }) #define DOCTEST_WARN_MESSAGE(cond, ...) ([] { return false; }) #define DOCTEST_CHECK_MESSAGE(cond, ...) ([] { return false; }) #define DOCTEST_REQUIRE_MESSAGE(cond, ...) ([] { return false; }) #define DOCTEST_WARN_FALSE_MESSAGE(cond, ...) ([] { return false; }) #define DOCTEST_CHECK_FALSE_MESSAGE(cond, ...) ([] { return false; }) #define DOCTEST_REQUIRE_FALSE_MESSAGE(cond, ...) ([] { return false; }) #define DOCTEST_WARN_EQ(...) ([] { return false; }) #define DOCTEST_CHECK_EQ(...) ([] { return false; }) #define DOCTEST_REQUIRE_EQ(...) ([] { return false; }) #define DOCTEST_WARN_NE(...) ([] { return false; }) #define DOCTEST_CHECK_NE(...) ([] { return false; }) #define DOCTEST_REQUIRE_NE(...) ([] { return false; }) #define DOCTEST_WARN_GT(...) ([] { return false; }) #define DOCTEST_CHECK_GT(...) ([] { return false; }) #define DOCTEST_REQUIRE_GT(...) ([] { return false; }) #define DOCTEST_WARN_LT(...) ([] { return false; }) #define DOCTEST_CHECK_LT(...) ([] { return false; }) #define DOCTEST_REQUIRE_LT(...) ([] { return false; }) #define DOCTEST_WARN_GE(...) ([] { return false; }) #define DOCTEST_CHECK_GE(...) ([] { return false; }) #define DOCTEST_REQUIRE_GE(...) ([] { return false; }) #define DOCTEST_WARN_LE(...) ([] { return false; }) #define DOCTEST_CHECK_LE(...) ([] { return false; }) #define DOCTEST_REQUIRE_LE(...) ([] { return false; }) #define DOCTEST_WARN_UNARY(...) ([] { return false; }) #define DOCTEST_CHECK_UNARY(...) ([] { return false; }) #define DOCTEST_REQUIRE_UNARY(...) ([] { return false; }) #define DOCTEST_WARN_UNARY_FALSE(...) ([] { return false; }) #define DOCTEST_CHECK_UNARY_FALSE(...) ([] { return false; }) #define DOCTEST_REQUIRE_UNARY_FALSE(...) ([] { return false; }) #endif // DOCTEST_CONFIG_EVALUATE_ASSERTS_EVEN_WHEN_DISABLED // TODO: think about if these also need to work properly even when doctest is disabled #define DOCTEST_WARN_THROWS(...) ([] { return false; }) #define DOCTEST_CHECK_THROWS(...) ([] { return false; }) #define DOCTEST_REQUIRE_THROWS(...) ([] { return false; }) #define DOCTEST_WARN_THROWS_AS(expr, ...) ([] { return false; }) #define DOCTEST_CHECK_THROWS_AS(expr, ...) ([] { return false; }) #define DOCTEST_REQUIRE_THROWS_AS(expr, ...) ([] { return false; }) #define DOCTEST_WARN_THROWS_WITH(expr, ...) ([] { return false; }) #define DOCTEST_CHECK_THROWS_WITH(expr, ...) ([] { return false; }) #define DOCTEST_REQUIRE_THROWS_WITH(expr, ...) ([] { return false; }) #define DOCTEST_WARN_THROWS_WITH_AS(expr, with, ...) ([] { return false; }) #define DOCTEST_CHECK_THROWS_WITH_AS(expr, with, ...) ([] { return false; }) #define DOCTEST_REQUIRE_THROWS_WITH_AS(expr, with, ...) ([] { return false; }) #define DOCTEST_WARN_NOTHROW(...) ([] { return false; }) #define DOCTEST_CHECK_NOTHROW(...) ([] { return false; }) #define DOCTEST_REQUIRE_NOTHROW(...) ([] { return false; }) #define DOCTEST_WARN_THROWS_MESSAGE(expr, ...) ([] { return false; }) #define DOCTEST_CHECK_THROWS_MESSAGE(expr, ...) ([] { return false; }) #define DOCTEST_REQUIRE_THROWS_MESSAGE(expr, ...) ([] { return false; }) #define DOCTEST_WARN_THROWS_AS_MESSAGE(expr, ex, ...) ([] { return false; }) #define DOCTEST_CHECK_THROWS_AS_MESSAGE(expr, ex, ...) ([] { return false; }) #define DOCTEST_REQUIRE_THROWS_AS_MESSAGE(expr, ex, ...) ([] { return false; }) #define DOCTEST_WARN_THROWS_WITH_MESSAGE(expr, with, ...) ([] { return false; }) #define DOCTEST_CHECK_THROWS_WITH_MESSAGE(expr, with, ...) ([] { return false; }) #define DOCTEST_REQUIRE_THROWS_WITH_MESSAGE(expr, with, ...) ([] { return false; }) #define DOCTEST_WARN_THROWS_WITH_AS_MESSAGE(expr, with, ex, ...) ([] { return false; }) #define DOCTEST_CHECK_THROWS_WITH_AS_MESSAGE(expr, with, ex, ...) ([] { return false; }) #define DOCTEST_REQUIRE_THROWS_WITH_AS_MESSAGE(expr, with, ex, ...) ([] { return false; }) #define DOCTEST_WARN_NOTHROW_MESSAGE(expr, ...) ([] { return false; }) #define DOCTEST_CHECK_NOTHROW_MESSAGE(expr, ...) ([] { return false; }) #define DOCTEST_REQUIRE_NOTHROW_MESSAGE(expr, ...) ([] { return false; }) #endif // DOCTEST_CONFIG_DISABLE // clang-format off // KEPT FOR BACKWARDS COMPATIBILITY - FORWARDING TO THE RIGHT MACROS #define DOCTEST_FAST_WARN_EQ DOCTEST_WARN_EQ #define DOCTEST_FAST_CHECK_EQ DOCTEST_CHECK_EQ #define DOCTEST_FAST_REQUIRE_EQ DOCTEST_REQUIRE_EQ #define DOCTEST_FAST_WARN_NE DOCTEST_WARN_NE #define DOCTEST_FAST_CHECK_NE DOCTEST_CHECK_NE #define DOCTEST_FAST_REQUIRE_NE DOCTEST_REQUIRE_NE #define DOCTEST_FAST_WARN_GT DOCTEST_WARN_GT #define DOCTEST_FAST_CHECK_GT DOCTEST_CHECK_GT #define DOCTEST_FAST_REQUIRE_GT DOCTEST_REQUIRE_GT #define DOCTEST_FAST_WARN_LT DOCTEST_WARN_LT #define DOCTEST_FAST_CHECK_LT DOCTEST_CHECK_LT #define DOCTEST_FAST_REQUIRE_LT DOCTEST_REQUIRE_LT #define DOCTEST_FAST_WARN_GE DOCTEST_WARN_GE #define DOCTEST_FAST_CHECK_GE DOCTEST_CHECK_GE #define DOCTEST_FAST_REQUIRE_GE DOCTEST_REQUIRE_GE #define DOCTEST_FAST_WARN_LE DOCTEST_WARN_LE #define DOCTEST_FAST_CHECK_LE DOCTEST_CHECK_LE #define DOCTEST_FAST_REQUIRE_LE DOCTEST_REQUIRE_LE #define DOCTEST_FAST_WARN_UNARY DOCTEST_WARN_UNARY #define DOCTEST_FAST_CHECK_UNARY DOCTEST_CHECK_UNARY #define DOCTEST_FAST_REQUIRE_UNARY DOCTEST_REQUIRE_UNARY #define DOCTEST_FAST_WARN_UNARY_FALSE DOCTEST_WARN_UNARY_FALSE #define DOCTEST_FAST_CHECK_UNARY_FALSE DOCTEST_CHECK_UNARY_FALSE #define DOCTEST_FAST_REQUIRE_UNARY_FALSE DOCTEST_REQUIRE_UNARY_FALSE #define DOCTEST_TEST_CASE_TEMPLATE_INSTANTIATE(id, ...) DOCTEST_TEST_CASE_TEMPLATE_INVOKE(id,__VA_ARGS__) // clang-format on // BDD style macros // clang-format off #define DOCTEST_SCENARIO(name) DOCTEST_TEST_CASE(" Scenario: " name) #define DOCTEST_SCENARIO_CLASS(name) DOCTEST_TEST_CASE_CLASS(" Scenario: " name) #define DOCTEST_SCENARIO_TEMPLATE(name, T, ...) DOCTEST_TEST_CASE_TEMPLATE(" Scenario: " name, T, __VA_ARGS__) #define DOCTEST_SCENARIO_TEMPLATE_DEFINE(name, T, id) DOCTEST_TEST_CASE_TEMPLATE_DEFINE(" Scenario: " name, T, id) #define DOCTEST_GIVEN(name) DOCTEST_SUBCASE(" Given: " name) #define DOCTEST_WHEN(name) DOCTEST_SUBCASE(" When: " name) #define DOCTEST_AND_WHEN(name) DOCTEST_SUBCASE("And when: " name) #define DOCTEST_THEN(name) DOCTEST_SUBCASE(" Then: " name) #define DOCTEST_AND_THEN(name) DOCTEST_SUBCASE(" And: " name) // clang-format on // == SHORT VERSIONS OF THE MACROS #if !defined(DOCTEST_CONFIG_NO_SHORT_MACRO_NAMES) #define TEST_CASE(name) DOCTEST_TEST_CASE(name) #define TEST_CASE_CLASS(name) DOCTEST_TEST_CASE_CLASS(name) #define TEST_CASE_FIXTURE(x, name) DOCTEST_TEST_CASE_FIXTURE(x, name) #define TYPE_TO_STRING(...) DOCTEST_TYPE_TO_STRING(__VA_ARGS__) #define TEST_CASE_TEMPLATE(name, T, ...) DOCTEST_TEST_CASE_TEMPLATE(name, T, __VA_ARGS__) #define TEST_CASE_TEMPLATE_DEFINE(name, T, id) DOCTEST_TEST_CASE_TEMPLATE_DEFINE(name, T, id) #define TEST_CASE_TEMPLATE_INVOKE(id, ...) DOCTEST_TEST_CASE_TEMPLATE_INVOKE(id, __VA_ARGS__) #define TEST_CASE_TEMPLATE_APPLY(id, ...) DOCTEST_TEST_CASE_TEMPLATE_APPLY(id, __VA_ARGS__) #define SUBCASE(name) DOCTEST_SUBCASE(name) #define TEST_SUITE(decorators) DOCTEST_TEST_SUITE(decorators) #define TEST_SUITE_BEGIN(name) DOCTEST_TEST_SUITE_BEGIN(name) #define TEST_SUITE_END DOCTEST_TEST_SUITE_END #define REGISTER_EXCEPTION_TRANSLATOR(signature) DOCTEST_REGISTER_EXCEPTION_TRANSLATOR(signature) #define REGISTER_REPORTER(name, priority, reporter) DOCTEST_REGISTER_REPORTER(name, priority, reporter) #define REGISTER_LISTENER(name, priority, reporter) DOCTEST_REGISTER_LISTENER(name, priority, reporter) #define INFO(...) DOCTEST_INFO(__VA_ARGS__) #define CAPTURE(x) DOCTEST_CAPTURE(x) #define ADD_MESSAGE_AT(file, line, ...) DOCTEST_ADD_MESSAGE_AT(file, line, __VA_ARGS__) #define ADD_FAIL_CHECK_AT(file, line, ...) DOCTEST_ADD_FAIL_CHECK_AT(file, line, __VA_ARGS__) #define ADD_FAIL_AT(file, line, ...) DOCTEST_ADD_FAIL_AT(file, line, __VA_ARGS__) #define MESSAGE(...) DOCTEST_MESSAGE(__VA_ARGS__) #define FAIL_CHECK(...) DOCTEST_FAIL_CHECK(__VA_ARGS__) #define FAIL(...) DOCTEST_FAIL(__VA_ARGS__) #define TO_LVALUE(...) DOCTEST_TO_LVALUE(__VA_ARGS__) #define WARN(...) DOCTEST_WARN(__VA_ARGS__) #define WARN_FALSE(...) DOCTEST_WARN_FALSE(__VA_ARGS__) #define WARN_THROWS(...) DOCTEST_WARN_THROWS(__VA_ARGS__) #define WARN_THROWS_AS(expr, ...) DOCTEST_WARN_THROWS_AS(expr, __VA_ARGS__) #define WARN_THROWS_WITH(expr, ...) DOCTEST_WARN_THROWS_WITH(expr, __VA_ARGS__) #define WARN_THROWS_WITH_AS(expr, with, ...) DOCTEST_WARN_THROWS_WITH_AS(expr, with, __VA_ARGS__) #define WARN_NOTHROW(...) DOCTEST_WARN_NOTHROW(__VA_ARGS__) #define CHECK(...) DOCTEST_CHECK(__VA_ARGS__) #define CHECK_FALSE(...) DOCTEST_CHECK_FALSE(__VA_ARGS__) #define CHECK_THROWS(...) DOCTEST_CHECK_THROWS(__VA_ARGS__) #define CHECK_THROWS_AS(expr, ...) DOCTEST_CHECK_THROWS_AS(expr, __VA_ARGS__) #define CHECK_THROWS_WITH(expr, ...) DOCTEST_CHECK_THROWS_WITH(expr, __VA_ARGS__) #define CHECK_THROWS_WITH_AS(expr, with, ...) DOCTEST_CHECK_THROWS_WITH_AS(expr, with, __VA_ARGS__) #define CHECK_NOTHROW(...) DOCTEST_CHECK_NOTHROW(__VA_ARGS__) #define REQUIRE(...) DOCTEST_REQUIRE(__VA_ARGS__) #define REQUIRE_FALSE(...) DOCTEST_REQUIRE_FALSE(__VA_ARGS__) #define REQUIRE_THROWS(...) DOCTEST_REQUIRE_THROWS(__VA_ARGS__) #define REQUIRE_THROWS_AS(expr, ...) DOCTEST_REQUIRE_THROWS_AS(expr, __VA_ARGS__) #define REQUIRE_THROWS_WITH(expr, ...) DOCTEST_REQUIRE_THROWS_WITH(expr, __VA_ARGS__) #define REQUIRE_THROWS_WITH_AS(expr, with, ...) DOCTEST_REQUIRE_THROWS_WITH_AS(expr, with, __VA_ARGS__) #define REQUIRE_NOTHROW(...) DOCTEST_REQUIRE_NOTHROW(__VA_ARGS__) #define WARN_MESSAGE(cond, ...) DOCTEST_WARN_MESSAGE(cond, __VA_ARGS__) #define WARN_FALSE_MESSAGE(cond, ...) DOCTEST_WARN_FALSE_MESSAGE(cond, __VA_ARGS__) #define WARN_THROWS_MESSAGE(expr, ...) DOCTEST_WARN_THROWS_MESSAGE(expr, __VA_ARGS__) #define WARN_THROWS_AS_MESSAGE(expr, ex, ...) DOCTEST_WARN_THROWS_AS_MESSAGE(expr, ex, __VA_ARGS__) #define WARN_THROWS_WITH_MESSAGE(expr, with, ...) DOCTEST_WARN_THROWS_WITH_MESSAGE(expr, with, __VA_ARGS__) #define WARN_THROWS_WITH_AS_MESSAGE(expr, with, ex, ...) DOCTEST_WARN_THROWS_WITH_AS_MESSAGE(expr, with, ex, __VA_ARGS__) #define WARN_NOTHROW_MESSAGE(expr, ...) DOCTEST_WARN_NOTHROW_MESSAGE(expr, __VA_ARGS__) #define CHECK_MESSAGE(cond, ...) DOCTEST_CHECK_MESSAGE(cond, __VA_ARGS__) #define CHECK_FALSE_MESSAGE(cond, ...) DOCTEST_CHECK_FALSE_MESSAGE(cond, __VA_ARGS__) #define CHECK_THROWS_MESSAGE(expr, ...) DOCTEST_CHECK_THROWS_MESSAGE(expr, __VA_ARGS__) #define CHECK_THROWS_AS_MESSAGE(expr, ex, ...) DOCTEST_CHECK_THROWS_AS_MESSAGE(expr, ex, __VA_ARGS__) #define CHECK_THROWS_WITH_MESSAGE(expr, with, ...) DOCTEST_CHECK_THROWS_WITH_MESSAGE(expr, with, __VA_ARGS__) #define CHECK_THROWS_WITH_AS_MESSAGE(expr, with, ex, ...) DOCTEST_CHECK_THROWS_WITH_AS_MESSAGE(expr, with, ex, __VA_ARGS__) #define CHECK_NOTHROW_MESSAGE(expr, ...) DOCTEST_CHECK_NOTHROW_MESSAGE(expr, __VA_ARGS__) #define REQUIRE_MESSAGE(cond, ...) DOCTEST_REQUIRE_MESSAGE(cond, __VA_ARGS__) #define REQUIRE_FALSE_MESSAGE(cond, ...) DOCTEST_REQUIRE_FALSE_MESSAGE(cond, __VA_ARGS__) #define REQUIRE_THROWS_MESSAGE(expr, ...) DOCTEST_REQUIRE_THROWS_MESSAGE(expr, __VA_ARGS__) #define REQUIRE_THROWS_AS_MESSAGE(expr, ex, ...) DOCTEST_REQUIRE_THROWS_AS_MESSAGE(expr, ex, __VA_ARGS__) #define REQUIRE_THROWS_WITH_MESSAGE(expr, with, ...) DOCTEST_REQUIRE_THROWS_WITH_MESSAGE(expr, with, __VA_ARGS__) #define REQUIRE_THROWS_WITH_AS_MESSAGE(expr, with, ex, ...) DOCTEST_REQUIRE_THROWS_WITH_AS_MESSAGE(expr, with, ex, __VA_ARGS__) #define REQUIRE_NOTHROW_MESSAGE(expr, ...) DOCTEST_REQUIRE_NOTHROW_MESSAGE(expr, __VA_ARGS__) #define SCENARIO(name) DOCTEST_SCENARIO(name) #define SCENARIO_CLASS(name) DOCTEST_SCENARIO_CLASS(name) #define SCENARIO_TEMPLATE(name, T, ...) DOCTEST_SCENARIO_TEMPLATE(name, T, __VA_ARGS__) #define SCENARIO_TEMPLATE_DEFINE(name, T, id) DOCTEST_SCENARIO_TEMPLATE_DEFINE(name, T, id) #define GIVEN(name) DOCTEST_GIVEN(name) #define WHEN(name) DOCTEST_WHEN(name) #define AND_WHEN(name) DOCTEST_AND_WHEN(name) #define THEN(name) DOCTEST_THEN(name) #define AND_THEN(name) DOCTEST_AND_THEN(name) #define WARN_EQ(...) DOCTEST_WARN_EQ(__VA_ARGS__) #define CHECK_EQ(...) DOCTEST_CHECK_EQ(__VA_ARGS__) #define REQUIRE_EQ(...) DOCTEST_REQUIRE_EQ(__VA_ARGS__) #define WARN_NE(...) DOCTEST_WARN_NE(__VA_ARGS__) #define CHECK_NE(...) DOCTEST_CHECK_NE(__VA_ARGS__) #define REQUIRE_NE(...) DOCTEST_REQUIRE_NE(__VA_ARGS__) #define WARN_GT(...) DOCTEST_WARN_GT(__VA_ARGS__) #define CHECK_GT(...) DOCTEST_CHECK_GT(__VA_ARGS__) #define REQUIRE_GT(...) DOCTEST_REQUIRE_GT(__VA_ARGS__) #define WARN_LT(...) DOCTEST_WARN_LT(__VA_ARGS__) #define CHECK_LT(...) DOCTEST_CHECK_LT(__VA_ARGS__) #define REQUIRE_LT(...) DOCTEST_REQUIRE_LT(__VA_ARGS__) #define WARN_GE(...) DOCTEST_WARN_GE(__VA_ARGS__) #define CHECK_GE(...) DOCTEST_CHECK_GE(__VA_ARGS__) #define REQUIRE_GE(...) DOCTEST_REQUIRE_GE(__VA_ARGS__) #define WARN_LE(...) DOCTEST_WARN_LE(__VA_ARGS__) #define CHECK_LE(...) DOCTEST_CHECK_LE(__VA_ARGS__) #define REQUIRE_LE(...) DOCTEST_REQUIRE_LE(__VA_ARGS__) #define WARN_UNARY(...) DOCTEST_WARN_UNARY(__VA_ARGS__) #define CHECK_UNARY(...) DOCTEST_CHECK_UNARY(__VA_ARGS__) #define REQUIRE_UNARY(...) DOCTEST_REQUIRE_UNARY(__VA_ARGS__) #define WARN_UNARY_FALSE(...) DOCTEST_WARN_UNARY_FALSE(__VA_ARGS__) #define CHECK_UNARY_FALSE(...) DOCTEST_CHECK_UNARY_FALSE(__VA_ARGS__) #define REQUIRE_UNARY_FALSE(...) DOCTEST_REQUIRE_UNARY_FALSE(__VA_ARGS__) // KEPT FOR BACKWARDS COMPATIBILITY #define FAST_WARN_EQ(...) DOCTEST_FAST_WARN_EQ(__VA_ARGS__) #define FAST_CHECK_EQ(...) DOCTEST_FAST_CHECK_EQ(__VA_ARGS__) #define FAST_REQUIRE_EQ(...) DOCTEST_FAST_REQUIRE_EQ(__VA_ARGS__) #define FAST_WARN_NE(...) DOCTEST_FAST_WARN_NE(__VA_ARGS__) #define FAST_CHECK_NE(...) DOCTEST_FAST_CHECK_NE(__VA_ARGS__) #define FAST_REQUIRE_NE(...) DOCTEST_FAST_REQUIRE_NE(__VA_ARGS__) #define FAST_WARN_GT(...) DOCTEST_FAST_WARN_GT(__VA_ARGS__) #define FAST_CHECK_GT(...) DOCTEST_FAST_CHECK_GT(__VA_ARGS__) #define FAST_REQUIRE_GT(...) DOCTEST_FAST_REQUIRE_GT(__VA_ARGS__) #define FAST_WARN_LT(...) DOCTEST_FAST_WARN_LT(__VA_ARGS__) #define FAST_CHECK_LT(...) DOCTEST_FAST_CHECK_LT(__VA_ARGS__) #define FAST_REQUIRE_LT(...) DOCTEST_FAST_REQUIRE_LT(__VA_ARGS__) #define FAST_WARN_GE(...) DOCTEST_FAST_WARN_GE(__VA_ARGS__) #define FAST_CHECK_GE(...) DOCTEST_FAST_CHECK_GE(__VA_ARGS__) #define FAST_REQUIRE_GE(...) DOCTEST_FAST_REQUIRE_GE(__VA_ARGS__) #define FAST_WARN_LE(...) DOCTEST_FAST_WARN_LE(__VA_ARGS__) #define FAST_CHECK_LE(...) DOCTEST_FAST_CHECK_LE(__VA_ARGS__) #define FAST_REQUIRE_LE(...) DOCTEST_FAST_REQUIRE_LE(__VA_ARGS__) #define FAST_WARN_UNARY(...) DOCTEST_FAST_WARN_UNARY(__VA_ARGS__) #define FAST_CHECK_UNARY(...) DOCTEST_FAST_CHECK_UNARY(__VA_ARGS__) #define FAST_REQUIRE_UNARY(...) DOCTEST_FAST_REQUIRE_UNARY(__VA_ARGS__) #define FAST_WARN_UNARY_FALSE(...) DOCTEST_FAST_WARN_UNARY_FALSE(__VA_ARGS__) #define FAST_CHECK_UNARY_FALSE(...) DOCTEST_FAST_CHECK_UNARY_FALSE(__VA_ARGS__) #define FAST_REQUIRE_UNARY_FALSE(...) DOCTEST_FAST_REQUIRE_UNARY_FALSE(__VA_ARGS__) #define TEST_CASE_TEMPLATE_INSTANTIATE(id, ...) DOCTEST_TEST_CASE_TEMPLATE_INSTANTIATE(id, __VA_ARGS__) #endif // DOCTEST_CONFIG_NO_SHORT_MACRO_NAMES #if !defined(DOCTEST_CONFIG_DISABLE) // this is here to clear the 'current test suite' for the current translation unit - at the top DOCTEST_TEST_SUITE_END(); // add stringification for primitive/fundamental types namespace doctest { namespace detail { DOCTEST_TYPE_TO_STRING_IMPL(bool) DOCTEST_TYPE_TO_STRING_IMPL(float) DOCTEST_TYPE_TO_STRING_IMPL(double) DOCTEST_TYPE_TO_STRING_IMPL(long double) DOCTEST_TYPE_TO_STRING_IMPL(char) DOCTEST_TYPE_TO_STRING_IMPL(signed char) DOCTEST_TYPE_TO_STRING_IMPL(unsigned char) #if !DOCTEST_MSVC || defined(_NATIVE_WCHAR_T_DEFINED) DOCTEST_TYPE_TO_STRING_IMPL(wchar_t) #endif // not MSVC or wchar_t support enabled DOCTEST_TYPE_TO_STRING_IMPL(short int) DOCTEST_TYPE_TO_STRING_IMPL(unsigned short int) DOCTEST_TYPE_TO_STRING_IMPL(int) DOCTEST_TYPE_TO_STRING_IMPL(unsigned int) DOCTEST_TYPE_TO_STRING_IMPL(long int) DOCTEST_TYPE_TO_STRING_IMPL(unsigned long int) DOCTEST_TYPE_TO_STRING_IMPL(long long int) DOCTEST_TYPE_TO_STRING_IMPL(unsigned long long int) }} // namespace doctest::detail #endif // DOCTEST_CONFIG_DISABLE DOCTEST_CLANG_SUPPRESS_WARNING_POP DOCTEST_MSVC_SUPPRESS_WARNING_POP DOCTEST_GCC_SUPPRESS_WARNING_POP DOCTEST_SUPPRESS_COMMON_WARNINGS_POP #endif // DOCTEST_LIBRARY_INCLUDED #ifndef DOCTEST_SINGLE_HEADER #define DOCTEST_SINGLE_HEADER #endif // DOCTEST_SINGLE_HEADER #if defined(DOCTEST_CONFIG_IMPLEMENT) || !defined(DOCTEST_SINGLE_HEADER) #ifndef DOCTEST_SINGLE_HEADER #include "doctest_fwd.h" #endif // DOCTEST_SINGLE_HEADER DOCTEST_CLANG_SUPPRESS_WARNING_WITH_PUSH("-Wunused-macros") #ifndef DOCTEST_LIBRARY_IMPLEMENTATION #define DOCTEST_LIBRARY_IMPLEMENTATION DOCTEST_CLANG_SUPPRESS_WARNING_POP DOCTEST_SUPPRESS_COMMON_WARNINGS_PUSH DOCTEST_CLANG_SUPPRESS_WARNING_PUSH DOCTEST_CLANG_SUPPRESS_WARNING("-Wglobal-constructors") DOCTEST_CLANG_SUPPRESS_WARNING("-Wexit-time-destructors") DOCTEST_CLANG_SUPPRESS_WARNING("-Wsign-conversion") DOCTEST_CLANG_SUPPRESS_WARNING("-Wshorten-64-to-32") DOCTEST_CLANG_SUPPRESS_WARNING("-Wmissing-variable-declarations") DOCTEST_CLANG_SUPPRESS_WARNING("-Wswitch") DOCTEST_CLANG_SUPPRESS_WARNING("-Wswitch-enum") DOCTEST_CLANG_SUPPRESS_WARNING("-Wcovered-switch-default") DOCTEST_CLANG_SUPPRESS_WARNING("-Wmissing-noreturn") DOCTEST_CLANG_SUPPRESS_WARNING("-Wdisabled-macro-expansion") DOCTEST_CLANG_SUPPRESS_WARNING("-Wmissing-braces") DOCTEST_CLANG_SUPPRESS_WARNING("-Wmissing-field-initializers") DOCTEST_CLANG_SUPPRESS_WARNING("-Wunused-member-function") DOCTEST_CLANG_SUPPRESS_WARNING("-Wnonportable-system-include-path") DOCTEST_GCC_SUPPRESS_WARNING_PUSH DOCTEST_GCC_SUPPRESS_WARNING("-Wconversion") DOCTEST_GCC_SUPPRESS_WARNING("-Wsign-conversion") DOCTEST_GCC_SUPPRESS_WARNING("-Wmissing-field-initializers") DOCTEST_GCC_SUPPRESS_WARNING("-Wmissing-braces") DOCTEST_GCC_SUPPRESS_WARNING("-Wswitch") DOCTEST_GCC_SUPPRESS_WARNING("-Wswitch-enum") DOCTEST_GCC_SUPPRESS_WARNING("-Wswitch-default") DOCTEST_GCC_SUPPRESS_WARNING("-Wunsafe-loop-optimizations") DOCTEST_GCC_SUPPRESS_WARNING("-Wold-style-cast") DOCTEST_GCC_SUPPRESS_WARNING("-Wunused-function") DOCTEST_GCC_SUPPRESS_WARNING("-Wmultiple-inheritance") DOCTEST_GCC_SUPPRESS_WARNING("-Wsuggest-attribute") DOCTEST_MSVC_SUPPRESS_WARNING_PUSH DOCTEST_MSVC_SUPPRESS_WARNING(4267) // 'var' : conversion from 'x' to 'y', possible loss of data DOCTEST_MSVC_SUPPRESS_WARNING(4530) // C++ exception handler used, but unwind semantics not enabled DOCTEST_MSVC_SUPPRESS_WARNING(4577) // 'noexcept' used with no exception handling mode specified DOCTEST_MSVC_SUPPRESS_WARNING(4774) // format string expected in argument is not a string literal DOCTEST_MSVC_SUPPRESS_WARNING(4365) // conversion from 'int' to 'unsigned', signed/unsigned mismatch DOCTEST_MSVC_SUPPRESS_WARNING(5039) // pointer to potentially throwing function passed to extern C DOCTEST_MSVC_SUPPRESS_WARNING(4800) // forcing value to bool 'true' or 'false' (performance warning) DOCTEST_MSVC_SUPPRESS_WARNING(5245) // unreferenced function with internal linkage has been removed DOCTEST_MAKE_STD_HEADERS_CLEAN_FROM_WARNINGS_ON_WALL_BEGIN // required includes - will go only in one translation unit! #include #include #include // borland (Embarcadero) compiler requires math.h and not cmath - https://github.com/doctest/doctest/pull/37 #ifdef __BORLANDC__ #include #endif // __BORLANDC__ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #ifdef DOCTEST_PLATFORM_MAC #include #include #include #endif // DOCTEST_PLATFORM_MAC #ifdef DOCTEST_PLATFORM_WINDOWS // defines for a leaner windows.h #ifndef WIN32_LEAN_AND_MEAN #define WIN32_LEAN_AND_MEAN #endif // WIN32_LEAN_AND_MEAN #ifndef NOMINMAX #define NOMINMAX #endif // NOMINMAX // not sure what AfxWin.h is for - here I do what Catch does #ifdef __AFXDLL #include #else #include #endif #include #else // DOCTEST_PLATFORM_WINDOWS #include #include #endif // DOCTEST_PLATFORM_WINDOWS // this is a fix for https://github.com/doctest/doctest/issues/348 // https://mail.gnome.org/archives/xml/2012-January/msg00000.html #if !defined(HAVE_UNISTD_H) && !defined(STDOUT_FILENO) #define STDOUT_FILENO fileno(stdout) #endif // HAVE_UNISTD_H DOCTEST_MAKE_STD_HEADERS_CLEAN_FROM_WARNINGS_ON_WALL_END // counts the number of elements in a C array #define DOCTEST_COUNTOF(x) (sizeof(x) / sizeof(x[0])) #ifdef DOCTEST_CONFIG_DISABLE #define DOCTEST_BRANCH_ON_DISABLED(if_disabled, if_not_disabled) if_disabled #else // DOCTEST_CONFIG_DISABLE #define DOCTEST_BRANCH_ON_DISABLED(if_disabled, if_not_disabled) if_not_disabled #endif // DOCTEST_CONFIG_DISABLE #ifndef DOCTEST_CONFIG_OPTIONS_PREFIX #define DOCTEST_CONFIG_OPTIONS_PREFIX "dt-" #endif #ifndef DOCTEST_THREAD_LOCAL #if DOCTEST_MSVC && (DOCTEST_MSVC < DOCTEST_COMPILER(19, 0, 0)) #define DOCTEST_THREAD_LOCAL #else // DOCTEST_MSVC #define DOCTEST_THREAD_LOCAL thread_local #endif // DOCTEST_MSVC #endif // DOCTEST_THREAD_LOCAL #ifndef DOCTEST_MULTI_LANE_ATOMICS_THREAD_LANES #define DOCTEST_MULTI_LANE_ATOMICS_THREAD_LANES 32 #endif #ifndef DOCTEST_MULTI_LANE_ATOMICS_CACHE_LINE_SIZE #define DOCTEST_MULTI_LANE_ATOMICS_CACHE_LINE_SIZE 64 #endif #ifdef DOCTEST_CONFIG_NO_UNPREFIXED_OPTIONS #define DOCTEST_OPTIONS_PREFIX_DISPLAY DOCTEST_CONFIG_OPTIONS_PREFIX #else #define DOCTEST_OPTIONS_PREFIX_DISPLAY "" #endif #if defined(WINAPI_FAMILY) && (WINAPI_FAMILY == WINAPI_FAMILY_APP) #define DOCTEST_CONFIG_NO_MULTI_LANE_ATOMICS #endif #ifndef DOCTEST_CDECL #define DOCTEST_CDECL __cdecl #endif namespace doctest { bool is_running_in_test = false; namespace { using namespace detail; template DOCTEST_NORETURN void throw_exception(Ex const& e) { #ifndef DOCTEST_CONFIG_NO_EXCEPTIONS throw e; #else // DOCTEST_CONFIG_NO_EXCEPTIONS std::cerr << "doctest will terminate because it needed to throw an exception.\n" << "The message was: " << e.what() << '\n'; std::terminate(); #endif // DOCTEST_CONFIG_NO_EXCEPTIONS } #ifndef DOCTEST_INTERNAL_ERROR #define DOCTEST_INTERNAL_ERROR(msg) \ throw_exception(std::logic_error( \ __FILE__ ":" DOCTEST_TOSTR(__LINE__) ": Internal doctest error: " msg)) #endif // DOCTEST_INTERNAL_ERROR // case insensitive strcmp int stricmp(const char* a, const char* b) { for(;; a++, b++) { const int d = tolower(*a) - tolower(*b); if(d != 0 || !*a) return d; } } template String fpToString(T value, int precision) { std::ostringstream oss; oss << std::setprecision(precision) << std::fixed << value; std::string d = oss.str(); size_t i = d.find_last_not_of('0'); if(i != std::string::npos && i != d.size() - 1) { if(d[i] == '.') i++; d = d.substr(0, i + 1); } return d.c_str(); } struct Endianness { enum Arch { Big, Little }; static Arch which() { int x = 1; // casting any data pointer to char* is allowed auto ptr = reinterpret_cast(&x); if(*ptr) return Little; return Big; } }; } // namespace namespace detail { String rawMemoryToString(const void* object, unsigned size) { // Reverse order for little endian architectures int i = 0, end = static_cast(size), inc = 1; if(Endianness::which() == Endianness::Little) { i = end - 1; end = inc = -1; } unsigned const char* bytes = static_cast(object); std::ostream* oss = tlssPush(); *oss << "0x" << std::setfill('0') << std::hex; for(; i != end; i += inc) *oss << std::setw(2) << static_cast(bytes[i]); return tlssPop(); } DOCTEST_THREAD_LOCAL class { std::vector stack; std::stringstream ss; public: std::ostream* push() { stack.push_back(ss.tellp()); return &ss; } String pop() { if (stack.empty()) DOCTEST_INTERNAL_ERROR("TLSS was empty when trying to pop!"); std::streampos pos = stack.back(); stack.pop_back(); unsigned sz = static_cast(ss.tellp() - pos); ss.rdbuf()->pubseekpos(pos, std::ios::in | std::ios::out); return String(ss, sz); } } g_oss; std::ostream* tlssPush() { return g_oss.push(); } String tlssPop() { return g_oss.pop(); } #ifndef DOCTEST_CONFIG_DISABLE namespace timer_large_integer { #if defined(DOCTEST_PLATFORM_WINDOWS) typedef ULONGLONG type; #else // DOCTEST_PLATFORM_WINDOWS typedef std::uint64_t type; #endif // DOCTEST_PLATFORM_WINDOWS } typedef timer_large_integer::type ticks_t; #ifdef DOCTEST_CONFIG_GETCURRENTTICKS ticks_t getCurrentTicks() { return DOCTEST_CONFIG_GETCURRENTTICKS(); } #elif defined(DOCTEST_PLATFORM_WINDOWS) ticks_t getCurrentTicks() { static LARGE_INTEGER hz = {0}, hzo = {0}; if(!hz.QuadPart) { QueryPerformanceFrequency(&hz); QueryPerformanceCounter(&hzo); } LARGE_INTEGER t; QueryPerformanceCounter(&t); return ((t.QuadPart - hzo.QuadPart) * LONGLONG(1000000)) / hz.QuadPart; } #else // DOCTEST_PLATFORM_WINDOWS ticks_t getCurrentTicks() { timeval t; gettimeofday(&t, nullptr); return static_cast(t.tv_sec) * 1000000 + static_cast(t.tv_usec); } #endif // DOCTEST_PLATFORM_WINDOWS struct Timer { void start() { m_ticks = getCurrentTicks(); } unsigned int getElapsedMicroseconds() const { return static_cast(getCurrentTicks() - m_ticks); } //unsigned int getElapsedMilliseconds() const { // return static_cast(getElapsedMicroseconds() / 1000); //} double getElapsedSeconds() const { return static_cast(getCurrentTicks() - m_ticks) / 1000000.0; } private: ticks_t m_ticks = 0; }; #ifdef DOCTEST_CONFIG_NO_MULTI_LANE_ATOMICS template using AtomicOrMultiLaneAtomic = std::atomic; #else // DOCTEST_CONFIG_NO_MULTI_LANE_ATOMICS // Provides a multilane implementation of an atomic variable that supports add, sub, load, // store. Instead of using a single atomic variable, this splits up into multiple ones, // each sitting on a separate cache line. The goal is to provide a speedup when most // operations are modifying. It achieves this with two properties: // // * Multiple atomics are used, so chance of congestion from the same atomic is reduced. // * Each atomic sits on a separate cache line, so false sharing is reduced. // // The disadvantage is that there is a small overhead due to the use of TLS, and load/store // is slower because all atomics have to be accessed. template class MultiLaneAtomic { struct CacheLineAlignedAtomic { std::atomic atomic{}; char padding[DOCTEST_MULTI_LANE_ATOMICS_CACHE_LINE_SIZE - sizeof(std::atomic)]; }; CacheLineAlignedAtomic m_atomics[DOCTEST_MULTI_LANE_ATOMICS_THREAD_LANES]; static_assert(sizeof(CacheLineAlignedAtomic) == DOCTEST_MULTI_LANE_ATOMICS_CACHE_LINE_SIZE, "guarantee one atomic takes exactly one cache line"); public: T operator++() DOCTEST_NOEXCEPT { return fetch_add(1) + 1; } T operator++(int) DOCTEST_NOEXCEPT { return fetch_add(1); } T fetch_add(T arg, std::memory_order order = std::memory_order_seq_cst) DOCTEST_NOEXCEPT { return myAtomic().fetch_add(arg, order); } T fetch_sub(T arg, std::memory_order order = std::memory_order_seq_cst) DOCTEST_NOEXCEPT { return myAtomic().fetch_sub(arg, order); } operator T() const DOCTEST_NOEXCEPT { return load(); } T load(std::memory_order order = std::memory_order_seq_cst) const DOCTEST_NOEXCEPT { auto result = T(); for(auto const& c : m_atomics) { result += c.atomic.load(order); } return result; } T operator=(T desired) DOCTEST_NOEXCEPT { // lgtm [cpp/assignment-does-not-return-this] store(desired); return desired; } void store(T desired, std::memory_order order = std::memory_order_seq_cst) DOCTEST_NOEXCEPT { // first value becomes desired", all others become 0. for(auto& c : m_atomics) { c.atomic.store(desired, order); desired = {}; } } private: // Each thread has a different atomic that it operates on. If more than NumLanes threads // use this, some will use the same atomic. So performance will degrade a bit, but still // everything will work. // // The logic here is a bit tricky. The call should be as fast as possible, so that there // is minimal to no overhead in determining the correct atomic for the current thread. // // 1. A global static counter laneCounter counts continuously up. // 2. Each successive thread will use modulo operation of that counter so it gets an atomic // assigned in a round-robin fashion. // 3. This tlsLaneIdx is stored in the thread local data, so it is directly available with // little overhead. std::atomic& myAtomic() DOCTEST_NOEXCEPT { static std::atomic laneCounter; DOCTEST_THREAD_LOCAL size_t tlsLaneIdx = laneCounter++ % DOCTEST_MULTI_LANE_ATOMICS_THREAD_LANES; return m_atomics[tlsLaneIdx].atomic; } }; template using AtomicOrMultiLaneAtomic = MultiLaneAtomic; #endif // DOCTEST_CONFIG_NO_MULTI_LANE_ATOMICS // this holds both parameters from the command line and runtime data for tests struct ContextState : ContextOptions, TestRunStats, CurrentTestCaseStats { AtomicOrMultiLaneAtomic numAssertsCurrentTest_atomic; AtomicOrMultiLaneAtomic numAssertsFailedCurrentTest_atomic; std::vector> filters = decltype(filters)(9); // 9 different filters std::vector reporters_currently_used; assert_handler ah = nullptr; Timer timer; std::vector stringifiedContexts; // logging from INFO() due to an exception // stuff for subcases std::vector subcasesStack; std::set subcasesPassed; int subcasesCurrentMaxLevel; bool should_reenter; std::atomic shouldLogCurrentException; void resetRunData() { numTestCases = 0; numTestCasesPassingFilters = 0; numTestSuitesPassingFilters = 0; numTestCasesFailed = 0; numAsserts = 0; numAssertsFailed = 0; numAssertsCurrentTest = 0; numAssertsFailedCurrentTest = 0; } void finalizeTestCaseData() { seconds = timer.getElapsedSeconds(); // update the non-atomic counters numAsserts += numAssertsCurrentTest_atomic; numAssertsFailed += numAssertsFailedCurrentTest_atomic; numAssertsCurrentTest = numAssertsCurrentTest_atomic; numAssertsFailedCurrentTest = numAssertsFailedCurrentTest_atomic; if(numAssertsFailedCurrentTest) failure_flags |= TestCaseFailureReason::AssertFailure; if(Approx(currentTest->m_timeout).epsilon(DBL_EPSILON) != 0 && Approx(seconds).epsilon(DBL_EPSILON) > currentTest->m_timeout) failure_flags |= TestCaseFailureReason::Timeout; if(currentTest->m_should_fail) { if(failure_flags) { failure_flags |= TestCaseFailureReason::ShouldHaveFailedAndDid; } else { failure_flags |= TestCaseFailureReason::ShouldHaveFailedButDidnt; } } else if(failure_flags && currentTest->m_may_fail) { failure_flags |= TestCaseFailureReason::CouldHaveFailedAndDid; } else if(currentTest->m_expected_failures > 0) { if(numAssertsFailedCurrentTest == currentTest->m_expected_failures) { failure_flags |= TestCaseFailureReason::FailedExactlyNumTimes; } else { failure_flags |= TestCaseFailureReason::DidntFailExactlyNumTimes; } } bool ok_to_fail = (TestCaseFailureReason::ShouldHaveFailedAndDid & failure_flags) || (TestCaseFailureReason::CouldHaveFailedAndDid & failure_flags) || (TestCaseFailureReason::FailedExactlyNumTimes & failure_flags); // if any subcase has failed - the whole test case has failed testCaseSuccess = !(failure_flags && !ok_to_fail); if(!testCaseSuccess) numTestCasesFailed++; } }; ContextState* g_cs = nullptr; // used to avoid locks for the debug output // TODO: figure out if this is indeed necessary/correct - seems like either there still // could be a race or that there wouldn't be a race even if using the context directly DOCTEST_THREAD_LOCAL bool g_no_colors; #endif // DOCTEST_CONFIG_DISABLE } // namespace detail char* String::allocate(unsigned sz) { if (sz <= last) { buf[sz] = '\0'; setLast(last - sz); return buf; } else { setOnHeap(); data.size = sz; data.capacity = data.size + 1; data.ptr = new char[data.capacity]; data.ptr[sz] = '\0'; return data.ptr; } } void String::setOnHeap() { *reinterpret_cast(&buf[last]) = 128; } void String::setLast(unsigned in) { buf[last] = char(in); } void String::copy(const String& other) { if(other.isOnStack()) { memcpy(buf, other.buf, len); } else { memcpy(allocate(other.data.size), other.data.ptr, other.data.size); } } String::String() { buf[0] = '\0'; setLast(); } String::~String() { if(!isOnStack()) delete[] data.ptr; // NOLINTNEXTLINE(clang-analyzer-cplusplus.NewDeleteLeaks) } String::String(const char* in) : String(in, strlen(in)) {} String::String(const char* in, unsigned in_size) { memcpy(allocate(in_size), in, in_size); } String::String(std::istream& in, unsigned in_size) { in.read(allocate(in_size), in_size); } String::String(const String& other) { copy(other); } String& String::operator=(const String& other) { if(this != &other) { if(!isOnStack()) delete[] data.ptr; copy(other); } return *this; } String& String::operator+=(const String& other) { const unsigned my_old_size = size(); const unsigned other_size = other.size(); const unsigned total_size = my_old_size + other_size; if(isOnStack()) { if(total_size < len) { // append to the current stack space memcpy(buf + my_old_size, other.c_str(), other_size + 1); // NOLINTNEXTLINE(clang-analyzer-cplusplus.NewDeleteLeaks) setLast(last - total_size); } else { // alloc new chunk char* temp = new char[total_size + 1]; // copy current data to new location before writing in the union memcpy(temp, buf, my_old_size); // skip the +1 ('\0') for speed // update data in union setOnHeap(); data.size = total_size; data.capacity = data.size + 1; data.ptr = temp; // transfer the rest of the data memcpy(data.ptr + my_old_size, other.c_str(), other_size + 1); } } else { if(data.capacity > total_size) { // append to the current heap block data.size = total_size; memcpy(data.ptr + my_old_size, other.c_str(), other_size + 1); } else { // resize data.capacity *= 2; if(data.capacity <= total_size) data.capacity = total_size + 1; // alloc new chunk char* temp = new char[data.capacity]; // copy current data to new location before releasing it memcpy(temp, data.ptr, my_old_size); // skip the +1 ('\0') for speed // release old chunk delete[] data.ptr; // update the rest of the union members data.size = total_size; data.ptr = temp; // transfer the rest of the data memcpy(data.ptr + my_old_size, other.c_str(), other_size + 1); } } return *this; } String::String(String&& other) { memcpy(buf, other.buf, len); other.buf[0] = '\0'; other.setLast(); } String& String::operator=(String&& other) { if(this != &other) { if(!isOnStack()) delete[] data.ptr; memcpy(buf, other.buf, len); other.buf[0] = '\0'; other.setLast(); } return *this; } char String::operator[](unsigned i) const { return const_cast(this)->operator[](i); // NOLINT } char& String::operator[](unsigned i) { if(isOnStack()) return reinterpret_cast(buf)[i]; return data.ptr[i]; } DOCTEST_GCC_SUPPRESS_WARNING_WITH_PUSH("-Wmaybe-uninitialized") unsigned String::size() const { if(isOnStack()) return last - (unsigned(buf[last]) & 31); // using "last" would work only if "len" is 32 return data.size; } DOCTEST_GCC_SUPPRESS_WARNING_POP unsigned String::capacity() const { if(isOnStack()) return len; return data.capacity; } int String::compare(const char* other, bool no_case) const { if(no_case) return doctest::stricmp(c_str(), other); return std::strcmp(c_str(), other); } int String::compare(const String& other, bool no_case) const { return compare(other.c_str(), no_case); } // NOLINTNEXTLINE(clang-analyzer-cplusplus.NewDeleteLeaks) String operator+(const String& lhs, const String& rhs) { return String(lhs) += rhs; } // clang-format off bool operator==(const String& lhs, const String& rhs) { return lhs.compare(rhs) == 0; } bool operator!=(const String& lhs, const String& rhs) { return lhs.compare(rhs) != 0; } bool operator< (const String& lhs, const String& rhs) { return lhs.compare(rhs) < 0; } bool operator> (const String& lhs, const String& rhs) { return lhs.compare(rhs) > 0; } bool operator<=(const String& lhs, const String& rhs) { return (lhs != rhs) ? lhs.compare(rhs) < 0 : true; } bool operator>=(const String& lhs, const String& rhs) { return (lhs != rhs) ? lhs.compare(rhs) > 0 : true; } // clang-format on std::ostream& operator<<(std::ostream& s, const String& in) { return s << in.c_str(); } namespace { void color_to_stream(std::ostream&, Color::Enum) DOCTEST_BRANCH_ON_DISABLED({}, ;) } // namespace namespace Color { std::ostream& operator<<(std::ostream& s, Color::Enum code) { color_to_stream(s, code); return s; } } // namespace Color // clang-format off const char* assertString(assertType::Enum at) { DOCTEST_MSVC_SUPPRESS_WARNING_WITH_PUSH(4062) // enum 'x' in switch of enum 'y' is not handled switch(at) { //!OCLINT missing default in switch statements case assertType::DT_WARN : return "WARN"; case assertType::DT_CHECK : return "CHECK"; case assertType::DT_REQUIRE : return "REQUIRE"; case assertType::DT_WARN_FALSE : return "WARN_FALSE"; case assertType::DT_CHECK_FALSE : return "CHECK_FALSE"; case assertType::DT_REQUIRE_FALSE : return "REQUIRE_FALSE"; case assertType::DT_WARN_THROWS : return "WARN_THROWS"; case assertType::DT_CHECK_THROWS : return "CHECK_THROWS"; case assertType::DT_REQUIRE_THROWS : return "REQUIRE_THROWS"; case assertType::DT_WARN_THROWS_AS : return "WARN_THROWS_AS"; case assertType::DT_CHECK_THROWS_AS : return "CHECK_THROWS_AS"; case assertType::DT_REQUIRE_THROWS_AS : return "REQUIRE_THROWS_AS"; case assertType::DT_WARN_THROWS_WITH : return "WARN_THROWS_WITH"; case assertType::DT_CHECK_THROWS_WITH : return "CHECK_THROWS_WITH"; case assertType::DT_REQUIRE_THROWS_WITH : return "REQUIRE_THROWS_WITH"; case assertType::DT_WARN_THROWS_WITH_AS : return "WARN_THROWS_WITH_AS"; case assertType::DT_CHECK_THROWS_WITH_AS : return "CHECK_THROWS_WITH_AS"; case assertType::DT_REQUIRE_THROWS_WITH_AS : return "REQUIRE_THROWS_WITH_AS"; case assertType::DT_WARN_NOTHROW : return "WARN_NOTHROW"; case assertType::DT_CHECK_NOTHROW : return "CHECK_NOTHROW"; case assertType::DT_REQUIRE_NOTHROW : return "REQUIRE_NOTHROW"; case assertType::DT_WARN_EQ : return "WARN_EQ"; case assertType::DT_CHECK_EQ : return "CHECK_EQ"; case assertType::DT_REQUIRE_EQ : return "REQUIRE_EQ"; case assertType::DT_WARN_NE : return "WARN_NE"; case assertType::DT_CHECK_NE : return "CHECK_NE"; case assertType::DT_REQUIRE_NE : return "REQUIRE_NE"; case assertType::DT_WARN_GT : return "WARN_GT"; case assertType::DT_CHECK_GT : return "CHECK_GT"; case assertType::DT_REQUIRE_GT : return "REQUIRE_GT"; case assertType::DT_WARN_LT : return "WARN_LT"; case assertType::DT_CHECK_LT : return "CHECK_LT"; case assertType::DT_REQUIRE_LT : return "REQUIRE_LT"; case assertType::DT_WARN_GE : return "WARN_GE"; case assertType::DT_CHECK_GE : return "CHECK_GE"; case assertType::DT_REQUIRE_GE : return "REQUIRE_GE"; case assertType::DT_WARN_LE : return "WARN_LE"; case assertType::DT_CHECK_LE : return "CHECK_LE"; case assertType::DT_REQUIRE_LE : return "REQUIRE_LE"; case assertType::DT_WARN_UNARY : return "WARN_UNARY"; case assertType::DT_CHECK_UNARY : return "CHECK_UNARY"; case assertType::DT_REQUIRE_UNARY : return "REQUIRE_UNARY"; case assertType::DT_WARN_UNARY_FALSE : return "WARN_UNARY_FALSE"; case assertType::DT_CHECK_UNARY_FALSE : return "CHECK_UNARY_FALSE"; case assertType::DT_REQUIRE_UNARY_FALSE : return "REQUIRE_UNARY_FALSE"; } DOCTEST_MSVC_SUPPRESS_WARNING_POP return ""; } // clang-format on const char* failureString(assertType::Enum at) { if(at & assertType::is_warn) //!OCLINT bitwise operator in conditional return "WARNING"; if(at & assertType::is_check) //!OCLINT bitwise operator in conditional return "ERROR"; if(at & assertType::is_require) //!OCLINT bitwise operator in conditional return "FATAL ERROR"; return ""; } DOCTEST_CLANG_SUPPRESS_WARNING_WITH_PUSH("-Wnull-dereference") DOCTEST_GCC_SUPPRESS_WARNING_WITH_PUSH("-Wnull-dereference") // depending on the current options this will remove the path of filenames const char* skipPathFromFilename(const char* file) { #ifndef DOCTEST_CONFIG_DISABLE if(getContextOptions()->no_path_in_filenames) { auto back = std::strrchr(file, '\\'); auto forward = std::strrchr(file, '/'); if(back || forward) { if(back > forward) forward = back; return forward + 1; } } #endif // DOCTEST_CONFIG_DISABLE return file; } DOCTEST_CLANG_SUPPRESS_WARNING_POP DOCTEST_GCC_SUPPRESS_WARNING_POP bool SubcaseSignature::operator<(const SubcaseSignature& other) const { if(m_line != other.m_line) return m_line < other.m_line; if(std::strcmp(m_file, other.m_file) != 0) return std::strcmp(m_file, other.m_file) < 0; return m_name.compare(other.m_name) < 0; } IContextScope::IContextScope() = default; IContextScope::~IContextScope() = default; #ifdef DOCTEST_CONFIG_TREAT_CHAR_STAR_AS_STRING String toString(char* in) { return toString(static_cast(in)); } // NOLINTNEXTLINE(clang-analyzer-cplusplus.NewDeleteLeaks) String toString(const char* in) { return String("\"") + (in ? in : "{null string}") + "\""; } #endif // DOCTEST_CONFIG_TREAT_CHAR_STAR_AS_STRING String toString(bool in) { return in ? "true" : "false"; } String toString(float in) { return fpToString(in, 5) + "f"; } String toString(double in) { return fpToString(in, 10); } String toString(double long in) { return fpToString(in, 15); } #define DOCTEST_TO_STRING_OVERLOAD(type, fmt) \ String toString(type in) { \ char buf[64]; \ std::sprintf(buf, fmt, in); \ return buf; \ } DOCTEST_TO_STRING_OVERLOAD(char, "%d") DOCTEST_TO_STRING_OVERLOAD(char signed, "%d") DOCTEST_TO_STRING_OVERLOAD(char unsigned, "%u") DOCTEST_TO_STRING_OVERLOAD(int short, "%d") DOCTEST_TO_STRING_OVERLOAD(int short unsigned, "%u") DOCTEST_TO_STRING_OVERLOAD(int, "%d") DOCTEST_TO_STRING_OVERLOAD(unsigned, "%u") DOCTEST_TO_STRING_OVERLOAD(int long, "%ld") DOCTEST_TO_STRING_OVERLOAD(int long unsigned, "%lu") DOCTEST_TO_STRING_OVERLOAD(int long long, "%lld") DOCTEST_TO_STRING_OVERLOAD(int long long unsigned, "%llu") String toString(std::nullptr_t) { return "NULL"; } #if DOCTEST_MSVC >= DOCTEST_COMPILER(19, 20, 0) // see this issue on why this is needed: https://github.com/doctest/doctest/issues/183 String toString(const std::string& in) { return in.c_str(); } #endif // VS 2019 Approx::Approx(double value) : m_epsilon(static_cast(std::numeric_limits::epsilon()) * 100) , m_scale(1.0) , m_value(value) {} Approx Approx::operator()(double value) const { Approx approx(value); approx.epsilon(m_epsilon); approx.scale(m_scale); return approx; } Approx& Approx::epsilon(double newEpsilon) { m_epsilon = newEpsilon; return *this; } Approx& Approx::scale(double newScale) { m_scale = newScale; return *this; } bool operator==(double lhs, const Approx& rhs) { // Thanks to Richard Harris for his help refining this formula return std::fabs(lhs - rhs.m_value) < rhs.m_epsilon * (rhs.m_scale + std::max(std::fabs(lhs), std::fabs(rhs.m_value))); } bool operator==(const Approx& lhs, double rhs) { return operator==(rhs, lhs); } bool operator!=(double lhs, const Approx& rhs) { return !operator==(lhs, rhs); } bool operator!=(const Approx& lhs, double rhs) { return !operator==(rhs, lhs); } bool operator<=(double lhs, const Approx& rhs) { return lhs < rhs.m_value || lhs == rhs; } bool operator<=(const Approx& lhs, double rhs) { return lhs.m_value < rhs || lhs == rhs; } bool operator>=(double lhs, const Approx& rhs) { return lhs > rhs.m_value || lhs == rhs; } bool operator>=(const Approx& lhs, double rhs) { return lhs.m_value > rhs || lhs == rhs; } bool operator<(double lhs, const Approx& rhs) { return lhs < rhs.m_value && lhs != rhs; } bool operator<(const Approx& lhs, double rhs) { return lhs.m_value < rhs && lhs != rhs; } bool operator>(double lhs, const Approx& rhs) { return lhs > rhs.m_value && lhs != rhs; } bool operator>(const Approx& lhs, double rhs) { return lhs.m_value > rhs && lhs != rhs; } String toString(const Approx& in) { // NOLINTNEXTLINE(clang-analyzer-cplusplus.NewDeleteLeaks) return "Approx( " + doctest::toString(in.m_value) + " )"; } const ContextOptions* getContextOptions() { return DOCTEST_BRANCH_ON_DISABLED(nullptr, g_cs); } } // namespace doctest #ifdef DOCTEST_CONFIG_DISABLE namespace doctest { Context::Context(int, const char* const*) {} Context::~Context() = default; void Context::applyCommandLine(int, const char* const*) {} void Context::addFilter(const char*, const char*) {} void Context::clearFilters() {} void Context::setOption(const char*, bool) {} void Context::setOption(const char*, int) {} void Context::setOption(const char*, const char*) {} bool Context::shouldExit() { return false; } void Context::setAsDefaultForAssertsOutOfTestCases() {} void Context::setAssertHandler(detail::assert_handler) {} void Context::setCout(std::ostream* out) {} int Context::run() { return 0; } IReporter::~IReporter() = default; int IReporter::get_num_active_contexts() { return 0; } const IContextScope* const* IReporter::get_active_contexts() { return nullptr; } int IReporter::get_num_stringified_contexts() { return 0; } const String* IReporter::get_stringified_contexts() { return nullptr; } int registerReporter(const char*, int, IReporter*) { return 0; } } // namespace doctest #else // DOCTEST_CONFIG_DISABLE #if !defined(DOCTEST_CONFIG_COLORS_NONE) #if !defined(DOCTEST_CONFIG_COLORS_WINDOWS) && !defined(DOCTEST_CONFIG_COLORS_ANSI) #ifdef DOCTEST_PLATFORM_WINDOWS #define DOCTEST_CONFIG_COLORS_WINDOWS #else // linux #define DOCTEST_CONFIG_COLORS_ANSI #endif // platform #endif // DOCTEST_CONFIG_COLORS_WINDOWS && DOCTEST_CONFIG_COLORS_ANSI #endif // DOCTEST_CONFIG_COLORS_NONE namespace doctest_detail_test_suite_ns { // holds the current test suite doctest::detail::TestSuite& getCurrentTestSuite() { static doctest::detail::TestSuite data{}; return data; } } // namespace doctest_detail_test_suite_ns namespace doctest { namespace { // the int (priority) is part of the key for automatic sorting - sadly one can register a // reporter with a duplicate name and a different priority but hopefully that won't happen often :| typedef std::map, reporterCreatorFunc> reporterMap; reporterMap& getReporters() { static reporterMap data; return data; } reporterMap& getListeners() { static reporterMap data; return data; } } // namespace namespace detail { #define DOCTEST_ITERATE_THROUGH_REPORTERS(function, ...) \ for(auto& curr_rep : g_cs->reporters_currently_used) \ curr_rep->function(__VA_ARGS__) bool checkIfShouldThrow(assertType::Enum at) { if(at & assertType::is_require) //!OCLINT bitwise operator in conditional return true; if((at & assertType::is_check) //!OCLINT bitwise operator in conditional && getContextOptions()->abort_after > 0 && (g_cs->numAssertsFailed + g_cs->numAssertsFailedCurrentTest_atomic) >= getContextOptions()->abort_after) return true; return false; } #ifndef DOCTEST_CONFIG_NO_EXCEPTIONS DOCTEST_NORETURN void throwException() { g_cs->shouldLogCurrentException = false; throw TestFailureException(); } // NOLINT(cert-err60-cpp) #else // DOCTEST_CONFIG_NO_EXCEPTIONS void throwException() {} #endif // DOCTEST_CONFIG_NO_EXCEPTIONS } // namespace detail namespace { using namespace detail; // matching of a string against a wildcard mask (case sensitivity configurable) taken from // https://www.codeproject.com/Articles/1088/Wildcard-string-compare-globbing int wildcmp(const char* str, const char* wild, bool caseSensitive) { const char* cp = str; const char* mp = wild; while((*str) && (*wild != '*')) { if((caseSensitive ? (*wild != *str) : (tolower(*wild) != tolower(*str))) && (*wild != '?')) { return 0; } wild++; str++; } while(*str) { if(*wild == '*') { if(!*++wild) { return 1; } mp = wild; cp = str + 1; } else if((caseSensitive ? (*wild == *str) : (tolower(*wild) == tolower(*str))) || (*wild == '?')) { wild++; str++; } else { wild = mp; //!OCLINT parameter reassignment str = cp++; //!OCLINT parameter reassignment } } while(*wild == '*') { wild++; } return !*wild; } //// C string hash function (djb2) - taken from http://www.cse.yorku.ca/~oz/hash.html //unsigned hashStr(unsigned const char* str) { // unsigned long hash = 5381; // char c; // while((c = *str++)) // hash = ((hash << 5) + hash) + c; // hash * 33 + c // return hash; //} // checks if the name matches any of the filters (and can be configured what to do when empty) bool matchesAny(const char* name, const std::vector& filters, bool matchEmpty, bool caseSensitive) { if(filters.empty() && matchEmpty) return true; for(auto& curr : filters) if(wildcmp(name, curr.c_str(), caseSensitive)) return true; return false; } } // namespace namespace detail { Subcase::Subcase(const String& name, const char* file, int line) : m_signature({name, file, line}) { auto* s = g_cs; // check subcase filters if(s->subcasesStack.size() < size_t(s->subcase_filter_levels)) { if(!matchesAny(m_signature.m_name.c_str(), s->filters[6], true, s->case_sensitive)) return; if(matchesAny(m_signature.m_name.c_str(), s->filters[7], false, s->case_sensitive)) return; } // if a Subcase on the same level has already been entered if(s->subcasesStack.size() < size_t(s->subcasesCurrentMaxLevel)) { s->should_reenter = true; return; } // push the current signature to the stack so we can check if the // current stack + the current new subcase have been traversed s->subcasesStack.push_back(m_signature); if(s->subcasesPassed.count(s->subcasesStack) != 0) { // pop - revert to previous stack since we've already passed this s->subcasesStack.pop_back(); return; } s->subcasesCurrentMaxLevel = s->subcasesStack.size(); m_entered = true; DOCTEST_ITERATE_THROUGH_REPORTERS(subcase_start, m_signature); } DOCTEST_MSVC_SUPPRESS_WARNING_WITH_PUSH(4996) // std::uncaught_exception is deprecated in C++17 DOCTEST_GCC_SUPPRESS_WARNING_WITH_PUSH("-Wdeprecated-declarations") DOCTEST_CLANG_SUPPRESS_WARNING_WITH_PUSH("-Wdeprecated-declarations") Subcase::~Subcase() { if(m_entered) { // only mark the subcase stack as passed if no subcases have been skipped if(g_cs->should_reenter == false) g_cs->subcasesPassed.insert(g_cs->subcasesStack); g_cs->subcasesStack.pop_back(); #if defined(__cpp_lib_uncaught_exceptions) && __cpp_lib_uncaught_exceptions >= 201411L && (!defined(__MAC_OS_X_VERSION_MIN_REQUIRED) || __MAC_OS_X_VERSION_MIN_REQUIRED >= 101200) if(std::uncaught_exceptions() > 0 #else if(std::uncaught_exception() #endif && g_cs->shouldLogCurrentException) { DOCTEST_ITERATE_THROUGH_REPORTERS( test_case_exception, {"exception thrown in subcase - will translate later " "when the whole test case has been exited (cannot " "translate while there is an active exception)", false}); g_cs->shouldLogCurrentException = false; } DOCTEST_ITERATE_THROUGH_REPORTERS(subcase_end, DOCTEST_EMPTY); } } DOCTEST_CLANG_SUPPRESS_WARNING_POP DOCTEST_GCC_SUPPRESS_WARNING_POP DOCTEST_MSVC_SUPPRESS_WARNING_POP Subcase::operator bool() const { return m_entered; } Result::Result(bool passed, const String& decomposition) : m_passed(passed) , m_decomp(decomposition) {} ExpressionDecomposer::ExpressionDecomposer(assertType::Enum at) : m_at(at) {} TestSuite& TestSuite::operator*(const char* in) { m_test_suite = in; return *this; } TestCase::TestCase(funcType test, const char* file, unsigned line, const TestSuite& test_suite, const char* type, int template_id) { m_file = file; m_line = line; m_name = nullptr; // will be later overridden in operator* m_test_suite = test_suite.m_test_suite; m_description = test_suite.m_description; m_skip = test_suite.m_skip; m_no_breaks = test_suite.m_no_breaks; m_no_output = test_suite.m_no_output; m_may_fail = test_suite.m_may_fail; m_should_fail = test_suite.m_should_fail; m_expected_failures = test_suite.m_expected_failures; m_timeout = test_suite.m_timeout; m_test = test; m_type = type; m_template_id = template_id; } TestCase::TestCase(const TestCase& other) : TestCaseData() { *this = other; } DOCTEST_MSVC_SUPPRESS_WARNING_WITH_PUSH(26434) // hides a non-virtual function DOCTEST_MSVC_SUPPRESS_WARNING(26437) // Do not slice TestCase& TestCase::operator=(const TestCase& other) { static_cast(*this) = static_cast(other); m_test = other.m_test; m_type = other.m_type; m_template_id = other.m_template_id; m_full_name = other.m_full_name; if(m_template_id != -1) m_name = m_full_name.c_str(); return *this; } DOCTEST_MSVC_SUPPRESS_WARNING_POP TestCase& TestCase::operator*(const char* in) { m_name = in; // make a new name with an appended type for templated test case if(m_template_id != -1) { m_full_name = String(m_name) + m_type; // redirect the name to point to the newly constructed full name m_name = m_full_name.c_str(); } return *this; } bool TestCase::operator<(const TestCase& other) const { // this will be used only to differentiate between test cases - not relevant for sorting if(m_line != other.m_line) return m_line < other.m_line; const int name_cmp = strcmp(m_name, other.m_name); if(name_cmp != 0) return name_cmp < 0; const int file_cmp = m_file.compare(other.m_file); if(file_cmp != 0) return file_cmp < 0; return m_template_id < other.m_template_id; } // all the registered tests std::set& getRegisteredTests() { static std::set data; return data; } } // namespace detail namespace { using namespace detail; // for sorting tests by file/line bool fileOrderComparator(const TestCase* lhs, const TestCase* rhs) { // this is needed because MSVC gives different case for drive letters // for __FILE__ when evaluated in a header and a source file const int res = lhs->m_file.compare(rhs->m_file, bool(DOCTEST_MSVC)); if(res != 0) return res < 0; if(lhs->m_line != rhs->m_line) return lhs->m_line < rhs->m_line; return lhs->m_template_id < rhs->m_template_id; } // for sorting tests by suite/file/line bool suiteOrderComparator(const TestCase* lhs, const TestCase* rhs) { const int res = std::strcmp(lhs->m_test_suite, rhs->m_test_suite); if(res != 0) return res < 0; return fileOrderComparator(lhs, rhs); } // for sorting tests by name/suite/file/line bool nameOrderComparator(const TestCase* lhs, const TestCase* rhs) { const int res = std::strcmp(lhs->m_name, rhs->m_name); if(res != 0) return res < 0; return suiteOrderComparator(lhs, rhs); } DOCTEST_CLANG_SUPPRESS_WARNING_WITH_PUSH("-Wdeprecated-declarations") void color_to_stream(std::ostream& s, Color::Enum code) { static_cast(s); // for DOCTEST_CONFIG_COLORS_NONE or DOCTEST_CONFIG_COLORS_WINDOWS static_cast(code); // for DOCTEST_CONFIG_COLORS_NONE #ifdef DOCTEST_CONFIG_COLORS_ANSI if(g_no_colors || (isatty(STDOUT_FILENO) == false && getContextOptions()->force_colors == false)) return; auto col = ""; // clang-format off switch(code) { //!OCLINT missing break in switch statement / unnecessary default statement in covered switch statement case Color::Red: col = "[0;31m"; break; case Color::Green: col = "[0;32m"; break; case Color::Blue: col = "[0;34m"; break; case Color::Cyan: col = "[0;36m"; break; case Color::Yellow: col = "[0;33m"; break; case Color::Grey: col = "[1;30m"; break; case Color::LightGrey: col = "[0;37m"; break; case Color::BrightRed: col = "[1;31m"; break; case Color::BrightGreen: col = "[1;32m"; break; case Color::BrightWhite: col = "[1;37m"; break; case Color::Bright: // invalid case Color::None: case Color::White: default: col = "[0m"; } // clang-format on s << "\033" << col; #endif // DOCTEST_CONFIG_COLORS_ANSI #ifdef DOCTEST_CONFIG_COLORS_WINDOWS if(g_no_colors || (_isatty(_fileno(stdout)) == false && getContextOptions()->force_colors == false)) return; static struct ConsoleHelper { HANDLE stdoutHandle; WORD origFgAttrs; WORD origBgAttrs; ConsoleHelper() { stdoutHandle = GetStdHandle(STD_OUTPUT_HANDLE); CONSOLE_SCREEN_BUFFER_INFO csbiInfo; GetConsoleScreenBufferInfo(stdoutHandle, &csbiInfo); origFgAttrs = csbiInfo.wAttributes & ~(BACKGROUND_GREEN | BACKGROUND_RED | BACKGROUND_BLUE | BACKGROUND_INTENSITY); origBgAttrs = csbiInfo.wAttributes & ~(FOREGROUND_GREEN | FOREGROUND_RED | FOREGROUND_BLUE | FOREGROUND_INTENSITY); } } ch; #define DOCTEST_SET_ATTR(x) SetConsoleTextAttribute(ch.stdoutHandle, x | ch.origBgAttrs) // clang-format off switch (code) { case Color::White: DOCTEST_SET_ATTR(FOREGROUND_GREEN | FOREGROUND_RED | FOREGROUND_BLUE); break; case Color::Red: DOCTEST_SET_ATTR(FOREGROUND_RED); break; case Color::Green: DOCTEST_SET_ATTR(FOREGROUND_GREEN); break; case Color::Blue: DOCTEST_SET_ATTR(FOREGROUND_BLUE); break; case Color::Cyan: DOCTEST_SET_ATTR(FOREGROUND_BLUE | FOREGROUND_GREEN); break; case Color::Yellow: DOCTEST_SET_ATTR(FOREGROUND_RED | FOREGROUND_GREEN); break; case Color::Grey: DOCTEST_SET_ATTR(0); break; case Color::LightGrey: DOCTEST_SET_ATTR(FOREGROUND_INTENSITY); break; case Color::BrightRed: DOCTEST_SET_ATTR(FOREGROUND_INTENSITY | FOREGROUND_RED); break; case Color::BrightGreen: DOCTEST_SET_ATTR(FOREGROUND_INTENSITY | FOREGROUND_GREEN); break; case Color::BrightWhite: DOCTEST_SET_ATTR(FOREGROUND_INTENSITY | FOREGROUND_GREEN | FOREGROUND_RED | FOREGROUND_BLUE); break; case Color::None: case Color::Bright: // invalid default: DOCTEST_SET_ATTR(ch.origFgAttrs); } // clang-format on #endif // DOCTEST_CONFIG_COLORS_WINDOWS } DOCTEST_CLANG_SUPPRESS_WARNING_POP std::vector& getExceptionTranslators() { static std::vector data; return data; } String translateActiveException() { #ifndef DOCTEST_CONFIG_NO_EXCEPTIONS String res; auto& translators = getExceptionTranslators(); for(auto& curr : translators) if(curr->translate(res)) return res; // clang-format off DOCTEST_GCC_SUPPRESS_WARNING_WITH_PUSH("-Wcatch-value") try { throw; } catch(std::exception& ex) { return ex.what(); } catch(std::string& msg) { return msg.c_str(); } catch(const char* msg) { return msg; } catch(...) { return "unknown exception"; } DOCTEST_GCC_SUPPRESS_WARNING_POP // clang-format on #else // DOCTEST_CONFIG_NO_EXCEPTIONS return ""; #endif // DOCTEST_CONFIG_NO_EXCEPTIONS } } // namespace namespace detail { // used by the macros for registering tests int regTest(const TestCase& tc) { getRegisteredTests().insert(tc); return 0; } // sets the current test suite int setTestSuite(const TestSuite& ts) { doctest_detail_test_suite_ns::getCurrentTestSuite() = ts; return 0; } #ifdef DOCTEST_IS_DEBUGGER_ACTIVE bool isDebuggerActive() { return DOCTEST_IS_DEBUGGER_ACTIVE(); } #else // DOCTEST_IS_DEBUGGER_ACTIVE #ifdef DOCTEST_PLATFORM_LINUX class ErrnoGuard { public: ErrnoGuard() : m_oldErrno(errno) {} ~ErrnoGuard() { errno = m_oldErrno; } private: int m_oldErrno; }; // See the comments in Catch2 for the reasoning behind this implementation: // https://github.com/catchorg/Catch2/blob/v2.13.1/include/internal/catch_debugger.cpp#L79-L102 bool isDebuggerActive() { ErrnoGuard guard; std::ifstream in("/proc/self/status"); for(std::string line; std::getline(in, line);) { static const int PREFIX_LEN = 11; if(line.compare(0, PREFIX_LEN, "TracerPid:\t") == 0) { return line.length() > PREFIX_LEN && line[PREFIX_LEN] != '0'; } } return false; } #elif defined(DOCTEST_PLATFORM_MAC) // The following function is taken directly from the following technical note: // https://developer.apple.com/library/archive/qa/qa1361/_index.html // Returns true if the current process is being debugged (either // running under the debugger or has a debugger attached post facto). bool isDebuggerActive() { int mib[4]; kinfo_proc info; size_t size; // Initialize the flags so that, if sysctl fails for some bizarre // reason, we get a predictable result. info.kp_proc.p_flag = 0; // Initialize mib, which tells sysctl the info we want, in this case // we're looking for information about a specific process ID. mib[0] = CTL_KERN; mib[1] = KERN_PROC; mib[2] = KERN_PROC_PID; mib[3] = getpid(); // Call sysctl. size = sizeof(info); if(sysctl(mib, DOCTEST_COUNTOF(mib), &info, &size, 0, 0) != 0) { std::cerr << "\nCall to sysctl failed - unable to determine if debugger is active **\n"; return false; } // We're being debugged if the P_TRACED flag is set. return ((info.kp_proc.p_flag & P_TRACED) != 0); } #elif DOCTEST_MSVC || defined(__MINGW32__) || defined(__MINGW64__) bool isDebuggerActive() { return ::IsDebuggerPresent() != 0; } #else bool isDebuggerActive() { return false; } #endif // Platform #endif // DOCTEST_IS_DEBUGGER_ACTIVE void registerExceptionTranslatorImpl(const IExceptionTranslator* et) { if(std::find(getExceptionTranslators().begin(), getExceptionTranslators().end(), et) == getExceptionTranslators().end()) getExceptionTranslators().push_back(et); } #ifdef DOCTEST_CONFIG_TREAT_CHAR_STAR_AS_STRING void toStream(std::ostream* s, char* in) { *s << in; } void toStream(std::ostream* s, const char* in) { *s << in; } #endif // DOCTEST_CONFIG_TREAT_CHAR_STAR_AS_STRING void toStream(std::ostream* s, bool in) { *s << std::boolalpha << in << std::noboolalpha; } void toStream(std::ostream* s, float in) { *s << in; } void toStream(std::ostream* s, double in) { *s << in; } void toStream(std::ostream* s, double long in) { *s << in; } void toStream(std::ostream* s, char in) { *s << in; } void toStream(std::ostream* s, char signed in) { *s << in; } void toStream(std::ostream* s, char unsigned in) { *s << in; } void toStream(std::ostream* s, int short in) { *s << in; } void toStream(std::ostream* s, int short unsigned in) { *s << in; } void toStream(std::ostream* s, int in) { *s << in; } void toStream(std::ostream* s, int unsigned in) { *s << in; } void toStream(std::ostream* s, int long in) { *s << in; } void toStream(std::ostream* s, int long unsigned in) { *s << in; } void toStream(std::ostream* s, int long long in) { *s << in; } void toStream(std::ostream* s, int long long unsigned in) { *s << in; } DOCTEST_THREAD_LOCAL std::vector g_infoContexts; // for logging with INFO() ContextScopeBase::ContextScopeBase() { g_infoContexts.push_back(this); } ContextScopeBase::ContextScopeBase(ContextScopeBase&& other) { if (other.need_to_destroy) { other.destroy(); } other.need_to_destroy = false; g_infoContexts.push_back(this); } DOCTEST_MSVC_SUPPRESS_WARNING_WITH_PUSH(4996) // std::uncaught_exception is deprecated in C++17 DOCTEST_GCC_SUPPRESS_WARNING_WITH_PUSH("-Wdeprecated-declarations") DOCTEST_CLANG_SUPPRESS_WARNING_WITH_PUSH("-Wdeprecated-declarations") // destroy cannot be inlined into the destructor because that would mean calling stringify after // ContextScope has been destroyed (base class destructors run after derived class destructors). // Instead, ContextScope calls this method directly from its destructor. void ContextScopeBase::destroy() { #if defined(__cpp_lib_uncaught_exceptions) && __cpp_lib_uncaught_exceptions >= 201411L && (!defined(__MAC_OS_X_VERSION_MIN_REQUIRED) || __MAC_OS_X_VERSION_MIN_REQUIRED >= 101200) if(std::uncaught_exceptions() > 0) { #else if(std::uncaught_exception()) { #endif std::ostringstream s; this->stringify(&s); g_cs->stringifiedContexts.push_back(s.str().c_str()); } g_infoContexts.pop_back(); } DOCTEST_CLANG_SUPPRESS_WARNING_POP DOCTEST_GCC_SUPPRESS_WARNING_POP DOCTEST_MSVC_SUPPRESS_WARNING_POP } // namespace detail namespace { using namespace detail; #if !defined(DOCTEST_CONFIG_POSIX_SIGNALS) && !defined(DOCTEST_CONFIG_WINDOWS_SEH) struct FatalConditionHandler { static void reset() {} static void allocateAltStackMem() {} static void freeAltStackMem() {} }; #else // DOCTEST_CONFIG_POSIX_SIGNALS || DOCTEST_CONFIG_WINDOWS_SEH void reportFatal(const std::string&); #ifdef DOCTEST_PLATFORM_WINDOWS struct SignalDefs { DWORD id; const char* name; }; // There is no 1-1 mapping between signals and windows exceptions. // Windows can easily distinguish between SO and SigSegV, // but SigInt, SigTerm, etc are handled differently. SignalDefs signalDefs[] = { {static_cast(EXCEPTION_ILLEGAL_INSTRUCTION), "SIGILL - Illegal instruction signal"}, {static_cast(EXCEPTION_STACK_OVERFLOW), "SIGSEGV - Stack overflow"}, {static_cast(EXCEPTION_ACCESS_VIOLATION), "SIGSEGV - Segmentation violation signal"}, {static_cast(EXCEPTION_INT_DIVIDE_BY_ZERO), "Divide by zero error"}, }; struct FatalConditionHandler { static LONG CALLBACK handleException(PEXCEPTION_POINTERS ExceptionInfo) { // Multiple threads may enter this filter/handler at once. We want the error message to be printed on the // console just once no matter how many threads have crashed. static std::mutex mutex; static bool execute = true; { std::lock_guard lock(mutex); if(execute) { bool reported = false; for(size_t i = 0; i < DOCTEST_COUNTOF(signalDefs); ++i) { if(ExceptionInfo->ExceptionRecord->ExceptionCode == signalDefs[i].id) { reportFatal(signalDefs[i].name); reported = true; break; } } if(reported == false) reportFatal("Unhandled SEH exception caught"); if(isDebuggerActive() && !g_cs->no_breaks) DOCTEST_BREAK_INTO_DEBUGGER(); } execute = false; } std::exit(EXIT_FAILURE); } static void allocateAltStackMem() {} static void freeAltStackMem() {} FatalConditionHandler() { isSet = true; // 32k seems enough for doctest to handle stack overflow, // but the value was found experimentally, so there is no strong guarantee guaranteeSize = 32 * 1024; // Register an unhandled exception filter previousTop = SetUnhandledExceptionFilter(handleException); // Pass in guarantee size to be filled SetThreadStackGuarantee(&guaranteeSize); // On Windows uncaught exceptions from another thread, exceptions from // destructors, or calls to std::terminate are not a SEH exception // The terminal handler gets called when: // - std::terminate is called FROM THE TEST RUNNER THREAD // - an exception is thrown from a destructor FROM THE TEST RUNNER THREAD original_terminate_handler = std::get_terminate(); std::set_terminate([]() DOCTEST_NOEXCEPT { reportFatal("Terminate handler called"); if(isDebuggerActive() && !g_cs->no_breaks) DOCTEST_BREAK_INTO_DEBUGGER(); std::exit(EXIT_FAILURE); // explicitly exit - otherwise the SIGABRT handler may be called as well }); // SIGABRT is raised when: // - std::terminate is called FROM A DIFFERENT THREAD // - an exception is thrown from a destructor FROM A DIFFERENT THREAD // - an uncaught exception is thrown FROM A DIFFERENT THREAD prev_sigabrt_handler = std::signal(SIGABRT, [](int signal) DOCTEST_NOEXCEPT { if(signal == SIGABRT) { reportFatal("SIGABRT - Abort (abnormal termination) signal"); if(isDebuggerActive() && !g_cs->no_breaks) DOCTEST_BREAK_INTO_DEBUGGER(); std::exit(EXIT_FAILURE); } }); // The following settings are taken from google test, and more // specifically from UnitTest::Run() inside of gtest.cc // the user does not want to see pop-up dialogs about crashes prev_error_mode_1 = SetErrorMode(SEM_FAILCRITICALERRORS | SEM_NOALIGNMENTFAULTEXCEPT | SEM_NOGPFAULTERRORBOX | SEM_NOOPENFILEERRORBOX); // This forces the abort message to go to stderr in all circumstances. prev_error_mode_2 = _set_error_mode(_OUT_TO_STDERR); // In the debug version, Visual Studio pops up a separate dialog // offering a choice to debug the aborted program - we want to disable that. prev_abort_behavior = _set_abort_behavior(0x0, _WRITE_ABORT_MSG | _CALL_REPORTFAULT); // In debug mode, the Windows CRT can crash with an assertion over invalid // input (e.g. passing an invalid file descriptor). The default handling // for these assertions is to pop up a dialog and wait for user input. // Instead ask the CRT to dump such assertions to stderr non-interactively. prev_report_mode = _CrtSetReportMode(_CRT_ASSERT, _CRTDBG_MODE_FILE | _CRTDBG_MODE_DEBUG); prev_report_file = _CrtSetReportFile(_CRT_ASSERT, _CRTDBG_FILE_STDERR); } static void reset() { if(isSet) { // Unregister handler and restore the old guarantee SetUnhandledExceptionFilter(previousTop); SetThreadStackGuarantee(&guaranteeSize); std::set_terminate(original_terminate_handler); std::signal(SIGABRT, prev_sigabrt_handler); SetErrorMode(prev_error_mode_1); _set_error_mode(prev_error_mode_2); _set_abort_behavior(prev_abort_behavior, _WRITE_ABORT_MSG | _CALL_REPORTFAULT); static_cast(_CrtSetReportMode(_CRT_ASSERT, prev_report_mode)); static_cast(_CrtSetReportFile(_CRT_ASSERT, prev_report_file)); isSet = false; } } ~FatalConditionHandler() { reset(); } private: static UINT prev_error_mode_1; static int prev_error_mode_2; static unsigned int prev_abort_behavior; static int prev_report_mode; static _HFILE prev_report_file; static void (DOCTEST_CDECL *prev_sigabrt_handler)(int); static std::terminate_handler original_terminate_handler; static bool isSet; static ULONG guaranteeSize; static LPTOP_LEVEL_EXCEPTION_FILTER previousTop; }; UINT FatalConditionHandler::prev_error_mode_1; int FatalConditionHandler::prev_error_mode_2; unsigned int FatalConditionHandler::prev_abort_behavior; int FatalConditionHandler::prev_report_mode; _HFILE FatalConditionHandler::prev_report_file; void (DOCTEST_CDECL *FatalConditionHandler::prev_sigabrt_handler)(int); std::terminate_handler FatalConditionHandler::original_terminate_handler; bool FatalConditionHandler::isSet = false; ULONG FatalConditionHandler::guaranteeSize = 0; LPTOP_LEVEL_EXCEPTION_FILTER FatalConditionHandler::previousTop = nullptr; #else // DOCTEST_PLATFORM_WINDOWS struct SignalDefs { int id; const char* name; }; SignalDefs signalDefs[] = {{SIGINT, "SIGINT - Terminal interrupt signal"}, {SIGILL, "SIGILL - Illegal instruction signal"}, {SIGFPE, "SIGFPE - Floating point error signal"}, {SIGSEGV, "SIGSEGV - Segmentation violation signal"}, {SIGTERM, "SIGTERM - Termination request signal"}, {SIGABRT, "SIGABRT - Abort (abnormal termination) signal"}}; struct FatalConditionHandler { static bool isSet; static struct sigaction oldSigActions[DOCTEST_COUNTOF(signalDefs)]; static stack_t oldSigStack; static size_t altStackSize; static char* altStackMem; static void handleSignal(int sig) { const char* name = ""; for(std::size_t i = 0; i < DOCTEST_COUNTOF(signalDefs); ++i) { SignalDefs& def = signalDefs[i]; if(sig == def.id) { name = def.name; break; } } reset(); reportFatal(name); raise(sig); } static void allocateAltStackMem() { altStackMem = new char[altStackSize]; } static void freeAltStackMem() { delete[] altStackMem; } FatalConditionHandler() { isSet = true; stack_t sigStack; sigStack.ss_sp = altStackMem; sigStack.ss_size = altStackSize; sigStack.ss_flags = 0; sigaltstack(&sigStack, &oldSigStack); struct sigaction sa = {}; sa.sa_handler = handleSignal; // NOLINT sa.sa_flags = SA_ONSTACK; for(std::size_t i = 0; i < DOCTEST_COUNTOF(signalDefs); ++i) { sigaction(signalDefs[i].id, &sa, &oldSigActions[i]); } } ~FatalConditionHandler() { reset(); } static void reset() { if(isSet) { // Set signals back to previous values -- hopefully nobody overwrote them in the meantime for(std::size_t i = 0; i < DOCTEST_COUNTOF(signalDefs); ++i) { sigaction(signalDefs[i].id, &oldSigActions[i], nullptr); } // Return the old stack sigaltstack(&oldSigStack, nullptr); isSet = false; } } }; bool FatalConditionHandler::isSet = false; struct sigaction FatalConditionHandler::oldSigActions[DOCTEST_COUNTOF(signalDefs)] = {}; stack_t FatalConditionHandler::oldSigStack = {}; size_t FatalConditionHandler::altStackSize = 4 * SIGSTKSZ; char* FatalConditionHandler::altStackMem = nullptr; #endif // DOCTEST_PLATFORM_WINDOWS #endif // DOCTEST_CONFIG_POSIX_SIGNALS || DOCTEST_CONFIG_WINDOWS_SEH } // namespace namespace { using namespace detail; #ifdef DOCTEST_PLATFORM_WINDOWS #define DOCTEST_OUTPUT_DEBUG_STRING(text) ::OutputDebugStringA(text) #else // TODO: integration with XCode and other IDEs #define DOCTEST_OUTPUT_DEBUG_STRING(text) // NOLINT(clang-diagnostic-unused-macros) #endif // Platform void addAssert(assertType::Enum at) { if((at & assertType::is_warn) == 0) //!OCLINT bitwise operator in conditional g_cs->numAssertsCurrentTest_atomic++; } void addFailedAssert(assertType::Enum at) { if((at & assertType::is_warn) == 0) //!OCLINT bitwise operator in conditional g_cs->numAssertsFailedCurrentTest_atomic++; } #if defined(DOCTEST_CONFIG_POSIX_SIGNALS) || defined(DOCTEST_CONFIG_WINDOWS_SEH) void reportFatal(const std::string& message) { g_cs->failure_flags |= TestCaseFailureReason::Crash; DOCTEST_ITERATE_THROUGH_REPORTERS(test_case_exception, {message.c_str(), true}); while(g_cs->subcasesStack.size()) { g_cs->subcasesStack.pop_back(); DOCTEST_ITERATE_THROUGH_REPORTERS(subcase_end, DOCTEST_EMPTY); } g_cs->finalizeTestCaseData(); DOCTEST_ITERATE_THROUGH_REPORTERS(test_case_end, *g_cs); DOCTEST_ITERATE_THROUGH_REPORTERS(test_run_end, *g_cs); } #endif // DOCTEST_CONFIG_POSIX_SIGNALS || DOCTEST_CONFIG_WINDOWS_SEH } // namespace namespace detail { ResultBuilder::ResultBuilder(assertType::Enum at, const char* file, int line, const char* expr, const char* exception_type, const char* exception_string) { m_test_case = g_cs->currentTest; m_at = at; m_file = file; m_line = line; m_expr = expr; m_failed = true; m_threw = false; m_threw_as = false; m_exception_type = exception_type; m_exception_string = exception_string; #if DOCTEST_MSVC if(m_expr[0] == ' ') // this happens when variadic macros are disabled under MSVC ++m_expr; #endif // MSVC } void ResultBuilder::setResult(const Result& res) { m_decomp = res.m_decomp; m_failed = !res.m_passed; } void ResultBuilder::translateException() { m_threw = true; m_exception = translateActiveException(); } bool ResultBuilder::log() { if(m_at & assertType::is_throws) { //!OCLINT bitwise operator in conditional m_failed = !m_threw; } else if((m_at & assertType::is_throws_as) && (m_at & assertType::is_throws_with)) { //!OCLINT m_failed = !m_threw_as || (m_exception != m_exception_string); } else if(m_at & assertType::is_throws_as) { //!OCLINT bitwise operator in conditional m_failed = !m_threw_as; } else if(m_at & assertType::is_throws_with) { //!OCLINT bitwise operator in conditional m_failed = m_exception != m_exception_string; } else if(m_at & assertType::is_nothrow) { //!OCLINT bitwise operator in conditional m_failed = m_threw; } if(m_exception.size()) m_exception = "\"" + m_exception + "\""; if(is_running_in_test) { addAssert(m_at); DOCTEST_ITERATE_THROUGH_REPORTERS(log_assert, *this); if(m_failed) addFailedAssert(m_at); } else if(m_failed) { failed_out_of_a_testing_context(*this); } return m_failed && isDebuggerActive() && !getContextOptions()->no_breaks && (g_cs->currentTest == nullptr || !g_cs->currentTest->m_no_breaks); // break into debugger } void ResultBuilder::react() const { if(m_failed && checkIfShouldThrow(m_at)) throwException(); } void failed_out_of_a_testing_context(const AssertData& ad) { if(g_cs->ah) g_cs->ah(ad); else std::abort(); } bool decomp_assert(assertType::Enum at, const char* file, int line, const char* expr, Result result) { bool failed = !result.m_passed; // ################################################################################### // IF THE DEBUGGER BREAKS HERE - GO 1 LEVEL UP IN THE CALLSTACK FOR THE FAILING ASSERT // THIS IS THE EFFECT OF HAVING 'DOCTEST_CONFIG_SUPER_FAST_ASSERTS' DEFINED // ################################################################################### DOCTEST_ASSERT_OUT_OF_TESTS(result.m_decomp); DOCTEST_ASSERT_IN_TESTS(result.m_decomp); // NOLINTNEXTLINE(clang-analyzer-cplusplus.NewDeleteLeaks) return !failed; } MessageBuilder::MessageBuilder(const char* file, int line, assertType::Enum severity) { m_stream = tlssPush(); m_file = file; m_line = line; m_severity = severity; } MessageBuilder::~MessageBuilder() { if (!logged) tlssPop(); } IExceptionTranslator::IExceptionTranslator() = default; IExceptionTranslator::~IExceptionTranslator() = default; bool MessageBuilder::log() { if (!logged) { m_string = tlssPop(); logged = true; } DOCTEST_ITERATE_THROUGH_REPORTERS(log_message, *this); const bool isWarn = m_severity & assertType::is_warn; // warn is just a message in this context so we don't treat it as an assert if(!isWarn) { addAssert(m_severity); addFailedAssert(m_severity); } return isDebuggerActive() && !getContextOptions()->no_breaks && !isWarn && (g_cs->currentTest == nullptr || !g_cs->currentTest->m_no_breaks); // break into debugger } void MessageBuilder::react() { if(m_severity & assertType::is_require) //!OCLINT bitwise operator in conditional throwException(); } } // namespace detail namespace { using namespace detail; // clang-format off // ================================================================================================= // The following code has been taken verbatim from Catch2/include/internal/catch_xmlwriter.h/cpp // This is done so cherry-picking bug fixes is trivial - even the style/formatting is untouched. // ================================================================================================= class XmlEncode { public: enum ForWhat { ForTextNodes, ForAttributes }; XmlEncode( std::string const& str, ForWhat forWhat = ForTextNodes ); void encodeTo( std::ostream& os ) const; friend std::ostream& operator << ( std::ostream& os, XmlEncode const& xmlEncode ); private: std::string m_str; ForWhat m_forWhat; }; class XmlWriter { public: class ScopedElement { public: ScopedElement( XmlWriter* writer ); ScopedElement( ScopedElement&& other ) DOCTEST_NOEXCEPT; ScopedElement& operator=( ScopedElement&& other ) DOCTEST_NOEXCEPT; ~ScopedElement(); ScopedElement& writeText( std::string const& text, bool indent = true ); template ScopedElement& writeAttribute( std::string const& name, T const& attribute ) { m_writer->writeAttribute( name, attribute ); return *this; } private: mutable XmlWriter* m_writer = nullptr; }; XmlWriter( std::ostream& os = std::cout ); ~XmlWriter(); XmlWriter( XmlWriter const& ) = delete; XmlWriter& operator=( XmlWriter const& ) = delete; XmlWriter& startElement( std::string const& name ); ScopedElement scopedElement( std::string const& name ); XmlWriter& endElement(); XmlWriter& writeAttribute( std::string const& name, std::string const& attribute ); XmlWriter& writeAttribute( std::string const& name, const char* attribute ); XmlWriter& writeAttribute( std::string const& name, bool attribute ); template XmlWriter& writeAttribute( std::string const& name, T const& attribute ) { std::stringstream rss; rss << attribute; return writeAttribute( name, rss.str() ); } XmlWriter& writeText( std::string const& text, bool indent = true ); //XmlWriter& writeComment( std::string const& text ); //void writeStylesheetRef( std::string const& url ); //XmlWriter& writeBlankLine(); void ensureTagClosed(); private: void writeDeclaration(); void newlineIfNecessary(); bool m_tagIsOpen = false; bool m_needsNewline = false; std::vector m_tags; std::string m_indent; std::ostream& m_os; }; // ================================================================================================= // The following code has been taken verbatim from Catch2/include/internal/catch_xmlwriter.h/cpp // This is done so cherry-picking bug fixes is trivial - even the style/formatting is untouched. // ================================================================================================= using uchar = unsigned char; namespace { size_t trailingBytes(unsigned char c) { if ((c & 0xE0) == 0xC0) { return 2; } if ((c & 0xF0) == 0xE0) { return 3; } if ((c & 0xF8) == 0xF0) { return 4; } DOCTEST_INTERNAL_ERROR("Invalid multibyte utf-8 start byte encountered"); } uint32_t headerValue(unsigned char c) { if ((c & 0xE0) == 0xC0) { return c & 0x1F; } if ((c & 0xF0) == 0xE0) { return c & 0x0F; } if ((c & 0xF8) == 0xF0) { return c & 0x07; } DOCTEST_INTERNAL_ERROR("Invalid multibyte utf-8 start byte encountered"); } void hexEscapeChar(std::ostream& os, unsigned char c) { std::ios_base::fmtflags f(os.flags()); os << "\\x" << std::uppercase << std::hex << std::setfill('0') << std::setw(2) << static_cast(c); os.flags(f); } } // anonymous namespace XmlEncode::XmlEncode( std::string const& str, ForWhat forWhat ) : m_str( str ), m_forWhat( forWhat ) {} void XmlEncode::encodeTo( std::ostream& os ) const { // Apostrophe escaping not necessary if we always use " to write attributes // (see: https://www.w3.org/TR/xml/#syntax) for( std::size_t idx = 0; idx < m_str.size(); ++ idx ) { uchar c = m_str[idx]; switch (c) { case '<': os << "<"; break; case '&': os << "&"; break; case '>': // See: https://www.w3.org/TR/xml/#syntax if (idx > 2 && m_str[idx - 1] == ']' && m_str[idx - 2] == ']') os << ">"; else os << c; break; case '\"': if (m_forWhat == ForAttributes) os << """; else os << c; break; default: // Check for control characters and invalid utf-8 // Escape control characters in standard ascii // see https://stackoverflow.com/questions/404107/why-are-control-characters-illegal-in-xml-1-0 if (c < 0x09 || (c > 0x0D && c < 0x20) || c == 0x7F) { hexEscapeChar(os, c); break; } // Plain ASCII: Write it to stream if (c < 0x7F) { os << c; break; } // UTF-8 territory // Check if the encoding is valid and if it is not, hex escape bytes. // Important: We do not check the exact decoded values for validity, only the encoding format // First check that this bytes is a valid lead byte: // This means that it is not encoded as 1111 1XXX // Or as 10XX XXXX if (c < 0xC0 || c >= 0xF8) { hexEscapeChar(os, c); break; } auto encBytes = trailingBytes(c); // Are there enough bytes left to avoid accessing out-of-bounds memory? if (idx + encBytes - 1 >= m_str.size()) { hexEscapeChar(os, c); break; } // The header is valid, check data // The next encBytes bytes must together be a valid utf-8 // This means: bitpattern 10XX XXXX and the extracted value is sane (ish) bool valid = true; uint32_t value = headerValue(c); for (std::size_t n = 1; n < encBytes; ++n) { uchar nc = m_str[idx + n]; valid &= ((nc & 0xC0) == 0x80); value = (value << 6) | (nc & 0x3F); } if ( // Wrong bit pattern of following bytes (!valid) || // Overlong encodings (value < 0x80) || ( value < 0x800 && encBytes > 2) || // removed "0x80 <= value &&" because redundant (0x800 < value && value < 0x10000 && encBytes > 3) || // Encoded value out of range (value >= 0x110000) ) { hexEscapeChar(os, c); break; } // If we got here, this is in fact a valid(ish) utf-8 sequence for (std::size_t n = 0; n < encBytes; ++n) { os << m_str[idx + n]; } idx += encBytes - 1; break; } } } std::ostream& operator << ( std::ostream& os, XmlEncode const& xmlEncode ) { xmlEncode.encodeTo( os ); return os; } XmlWriter::ScopedElement::ScopedElement( XmlWriter* writer ) : m_writer( writer ) {} XmlWriter::ScopedElement::ScopedElement( ScopedElement&& other ) DOCTEST_NOEXCEPT : m_writer( other.m_writer ){ other.m_writer = nullptr; } XmlWriter::ScopedElement& XmlWriter::ScopedElement::operator=( ScopedElement&& other ) DOCTEST_NOEXCEPT { if ( m_writer ) { m_writer->endElement(); } m_writer = other.m_writer; other.m_writer = nullptr; return *this; } XmlWriter::ScopedElement::~ScopedElement() { if( m_writer ) m_writer->endElement(); } XmlWriter::ScopedElement& XmlWriter::ScopedElement::writeText( std::string const& text, bool indent ) { m_writer->writeText( text, indent ); return *this; } XmlWriter::XmlWriter( std::ostream& os ) : m_os( os ) { writeDeclaration(); } XmlWriter::~XmlWriter() { while( !m_tags.empty() ) endElement(); } XmlWriter& XmlWriter::startElement( std::string const& name ) { ensureTagClosed(); newlineIfNecessary(); m_os << m_indent << '<' << name; m_tags.push_back( name ); m_indent += " "; m_tagIsOpen = true; return *this; } XmlWriter::ScopedElement XmlWriter::scopedElement( std::string const& name ) { ScopedElement scoped( this ); startElement( name ); return scoped; } XmlWriter& XmlWriter::endElement() { newlineIfNecessary(); m_indent = m_indent.substr( 0, m_indent.size()-2 ); if( m_tagIsOpen ) { m_os << "/>"; m_tagIsOpen = false; } else { m_os << m_indent << ""; } m_os << std::endl; m_tags.pop_back(); return *this; } XmlWriter& XmlWriter::writeAttribute( std::string const& name, std::string const& attribute ) { if( !name.empty() && !attribute.empty() ) m_os << ' ' << name << "=\"" << XmlEncode( attribute, XmlEncode::ForAttributes ) << '"'; return *this; } XmlWriter& XmlWriter::writeAttribute( std::string const& name, const char* attribute ) { if( !name.empty() && attribute && attribute[0] != '\0' ) m_os << ' ' << name << "=\"" << XmlEncode( attribute, XmlEncode::ForAttributes ) << '"'; return *this; } XmlWriter& XmlWriter::writeAttribute( std::string const& name, bool attribute ) { m_os << ' ' << name << "=\"" << ( attribute ? "true" : "false" ) << '"'; return *this; } XmlWriter& XmlWriter::writeText( std::string const& text, bool indent ) { if( !text.empty() ){ bool tagWasOpen = m_tagIsOpen; ensureTagClosed(); if( tagWasOpen && indent ) m_os << m_indent; m_os << XmlEncode( text ); m_needsNewline = true; } return *this; } //XmlWriter& XmlWriter::writeComment( std::string const& text ) { // ensureTagClosed(); // m_os << m_indent << ""; // m_needsNewline = true; // return *this; //} //void XmlWriter::writeStylesheetRef( std::string const& url ) { // m_os << "\n"; //} //XmlWriter& XmlWriter::writeBlankLine() { // ensureTagClosed(); // m_os << '\n'; // return *this; //} void XmlWriter::ensureTagClosed() { if( m_tagIsOpen ) { m_os << ">" << std::endl; m_tagIsOpen = false; } } void XmlWriter::writeDeclaration() { m_os << "\n"; } void XmlWriter::newlineIfNecessary() { if( m_needsNewline ) { m_os << std::endl; m_needsNewline = false; } } // ================================================================================================= // End of copy-pasted code from Catch // ================================================================================================= // clang-format on struct XmlReporter : public IReporter { XmlWriter xml; std::mutex mutex; // caching pointers/references to objects of these types - safe to do const ContextOptions& opt; const TestCaseData* tc = nullptr; XmlReporter(const ContextOptions& co) : xml(*co.cout) , opt(co) {} void log_contexts() { int num_contexts = get_num_active_contexts(); if(num_contexts) { auto contexts = get_active_contexts(); std::stringstream ss; for(int i = 0; i < num_contexts; ++i) { contexts[i]->stringify(&ss); xml.scopedElement("Info").writeText(ss.str()); ss.str(""); } } } unsigned line(unsigned l) const { return opt.no_line_numbers ? 0 : l; } void test_case_start_impl(const TestCaseData& in) { bool open_ts_tag = false; if(tc != nullptr) { // we have already opened a test suite if(std::strcmp(tc->m_test_suite, in.m_test_suite) != 0) { xml.endElement(); open_ts_tag = true; } } else { open_ts_tag = true; // first test case ==> first test suite } if(open_ts_tag) { xml.startElement("TestSuite"); xml.writeAttribute("name", in.m_test_suite); } tc = ∈ xml.startElement("TestCase") .writeAttribute("name", in.m_name) .writeAttribute("filename", skipPathFromFilename(in.m_file.c_str())) .writeAttribute("line", line(in.m_line)) .writeAttribute("description", in.m_description); if(Approx(in.m_timeout) != 0) xml.writeAttribute("timeout", in.m_timeout); if(in.m_may_fail) xml.writeAttribute("may_fail", true); if(in.m_should_fail) xml.writeAttribute("should_fail", true); } // ========================================================================================= // WHAT FOLLOWS ARE OVERRIDES OF THE VIRTUAL METHODS OF THE REPORTER INTERFACE // ========================================================================================= void report_query(const QueryData& in) override { test_run_start(); if(opt.list_reporters) { for(auto& curr : getListeners()) xml.scopedElement("Listener") .writeAttribute("priority", curr.first.first) .writeAttribute("name", curr.first.second); for(auto& curr : getReporters()) xml.scopedElement("Reporter") .writeAttribute("priority", curr.first.first) .writeAttribute("name", curr.first.second); } else if(opt.count || opt.list_test_cases) { for(unsigned i = 0; i < in.num_data; ++i) { xml.scopedElement("TestCase").writeAttribute("name", in.data[i]->m_name) .writeAttribute("testsuite", in.data[i]->m_test_suite) .writeAttribute("filename", skipPathFromFilename(in.data[i]->m_file.c_str())) .writeAttribute("line", line(in.data[i]->m_line)) .writeAttribute("skipped", in.data[i]->m_skip); } xml.scopedElement("OverallResultsTestCases") .writeAttribute("unskipped", in.run_stats->numTestCasesPassingFilters); } else if(opt.list_test_suites) { for(unsigned i = 0; i < in.num_data; ++i) xml.scopedElement("TestSuite").writeAttribute("name", in.data[i]->m_test_suite); xml.scopedElement("OverallResultsTestCases") .writeAttribute("unskipped", in.run_stats->numTestCasesPassingFilters); xml.scopedElement("OverallResultsTestSuites") .writeAttribute("unskipped", in.run_stats->numTestSuitesPassingFilters); } xml.endElement(); } void test_run_start() override { // remove .exe extension - mainly to have the same output on UNIX and Windows std::string binary_name = skipPathFromFilename(opt.binary_name.c_str()); #ifdef DOCTEST_PLATFORM_WINDOWS if(binary_name.rfind(".exe") != std::string::npos) binary_name = binary_name.substr(0, binary_name.length() - 4); #endif // DOCTEST_PLATFORM_WINDOWS xml.startElement("doctest").writeAttribute("binary", binary_name); if(opt.no_version == false) xml.writeAttribute("version", DOCTEST_VERSION_STR); // only the consequential ones (TODO: filters) xml.scopedElement("Options") .writeAttribute("order_by", opt.order_by.c_str()) .writeAttribute("rand_seed", opt.rand_seed) .writeAttribute("first", opt.first) .writeAttribute("last", opt.last) .writeAttribute("abort_after", opt.abort_after) .writeAttribute("subcase_filter_levels", opt.subcase_filter_levels) .writeAttribute("case_sensitive", opt.case_sensitive) .writeAttribute("no_throw", opt.no_throw) .writeAttribute("no_skip", opt.no_skip); } void test_run_end(const TestRunStats& p) override { if(tc) // the TestSuite tag - only if there has been at least 1 test case xml.endElement(); xml.scopedElement("OverallResultsAsserts") .writeAttribute("successes", p.numAsserts - p.numAssertsFailed) .writeAttribute("failures", p.numAssertsFailed); xml.startElement("OverallResultsTestCases") .writeAttribute("successes", p.numTestCasesPassingFilters - p.numTestCasesFailed) .writeAttribute("failures", p.numTestCasesFailed); if(opt.no_skipped_summary == false) xml.writeAttribute("skipped", p.numTestCases - p.numTestCasesPassingFilters); xml.endElement(); xml.endElement(); } void test_case_start(const TestCaseData& in) override { test_case_start_impl(in); xml.ensureTagClosed(); } void test_case_reenter(const TestCaseData&) override {} void test_case_end(const CurrentTestCaseStats& st) override { xml.startElement("OverallResultsAsserts") .writeAttribute("successes", st.numAssertsCurrentTest - st.numAssertsFailedCurrentTest) .writeAttribute("failures", st.numAssertsFailedCurrentTest) .writeAttribute("test_case_success", st.testCaseSuccess); if(opt.duration) xml.writeAttribute("duration", st.seconds); if(tc->m_expected_failures) xml.writeAttribute("expected_failures", tc->m_expected_failures); xml.endElement(); xml.endElement(); } void test_case_exception(const TestCaseException& e) override { std::lock_guard lock(mutex); xml.scopedElement("Exception") .writeAttribute("crash", e.is_crash) .writeText(e.error_string.c_str()); } void subcase_start(const SubcaseSignature& in) override { xml.startElement("SubCase") .writeAttribute("name", in.m_name) .writeAttribute("filename", skipPathFromFilename(in.m_file)) .writeAttribute("line", line(in.m_line)); xml.ensureTagClosed(); } void subcase_end() override { xml.endElement(); } void log_assert(const AssertData& rb) override { if(!rb.m_failed && !opt.success) return; std::lock_guard lock(mutex); xml.startElement("Expression") .writeAttribute("success", !rb.m_failed) .writeAttribute("type", assertString(rb.m_at)) .writeAttribute("filename", skipPathFromFilename(rb.m_file)) .writeAttribute("line", line(rb.m_line)); xml.scopedElement("Original").writeText(rb.m_expr); if(rb.m_threw) xml.scopedElement("Exception").writeText(rb.m_exception.c_str()); if(rb.m_at & assertType::is_throws_as) xml.scopedElement("ExpectedException").writeText(rb.m_exception_type); if(rb.m_at & assertType::is_throws_with) xml.scopedElement("ExpectedExceptionString").writeText(rb.m_exception_string); if((rb.m_at & assertType::is_normal) && !rb.m_threw) xml.scopedElement("Expanded").writeText(rb.m_decomp.c_str()); log_contexts(); xml.endElement(); } void log_message(const MessageData& mb) override { std::lock_guard lock(mutex); xml.startElement("Message") .writeAttribute("type", failureString(mb.m_severity)) .writeAttribute("filename", skipPathFromFilename(mb.m_file)) .writeAttribute("line", line(mb.m_line)); xml.scopedElement("Text").writeText(mb.m_string.c_str()); log_contexts(); xml.endElement(); } void test_case_skipped(const TestCaseData& in) override { if(opt.no_skipped_summary == false) { test_case_start_impl(in); xml.writeAttribute("skipped", "true"); xml.endElement(); } } }; DOCTEST_REGISTER_REPORTER("xml", 0, XmlReporter); void fulltext_log_assert_to_stream(std::ostream& s, const AssertData& rb) { if((rb.m_at & (assertType::is_throws_as | assertType::is_throws_with)) == 0) //!OCLINT bitwise operator in conditional s << Color::Cyan << assertString(rb.m_at) << "( " << rb.m_expr << " ) " << Color::None; if(rb.m_at & assertType::is_throws) { //!OCLINT bitwise operator in conditional s << (rb.m_threw ? "threw as expected!" : "did NOT throw at all!") << "\n"; } else if((rb.m_at & assertType::is_throws_as) && (rb.m_at & assertType::is_throws_with)) { //!OCLINT s << Color::Cyan << assertString(rb.m_at) << "( " << rb.m_expr << ", \"" << rb.m_exception_string << "\", " << rb.m_exception_type << " ) " << Color::None; if(rb.m_threw) { if(!rb.m_failed) { s << "threw as expected!\n"; } else { s << "threw a DIFFERENT exception! (contents: " << rb.m_exception << ")\n"; } } else { s << "did NOT throw at all!\n"; } } else if(rb.m_at & assertType::is_throws_as) { //!OCLINT bitwise operator in conditional s << Color::Cyan << assertString(rb.m_at) << "( " << rb.m_expr << ", " << rb.m_exception_type << " ) " << Color::None << (rb.m_threw ? (rb.m_threw_as ? "threw as expected!" : "threw a DIFFERENT exception: ") : "did NOT throw at all!") << Color::Cyan << rb.m_exception << "\n"; } else if(rb.m_at & assertType::is_throws_with) { //!OCLINT bitwise operator in conditional s << Color::Cyan << assertString(rb.m_at) << "( " << rb.m_expr << ", \"" << rb.m_exception_string << "\" ) " << Color::None << (rb.m_threw ? (!rb.m_failed ? "threw as expected!" : "threw a DIFFERENT exception: ") : "did NOT throw at all!") << Color::Cyan << rb.m_exception << "\n"; } else if(rb.m_at & assertType::is_nothrow) { //!OCLINT bitwise operator in conditional s << (rb.m_threw ? "THREW exception: " : "didn't throw!") << Color::Cyan << rb.m_exception << "\n"; } else { s << (rb.m_threw ? "THREW exception: " : (!rb.m_failed ? "is correct!\n" : "is NOT correct!\n")); if(rb.m_threw) s << rb.m_exception << "\n"; else s << " values: " << assertString(rb.m_at) << "( " << rb.m_decomp << " )\n"; } } // TODO: // - log_message() // - respond to queries // - honor remaining options // - more attributes in tags struct JUnitReporter : public IReporter { XmlWriter xml; std::mutex mutex; Timer timer; std::vector deepestSubcaseStackNames; struct JUnitTestCaseData { static std::string getCurrentTimestamp() { // Beware, this is not reentrant because of backward compatibility issues // Also, UTC only, again because of backward compatibility (%z is C++11) time_t rawtime; std::time(&rawtime); auto const timeStampSize = sizeof("2017-01-16T17:06:45Z"); std::tm timeInfo; #ifdef DOCTEST_PLATFORM_WINDOWS gmtime_s(&timeInfo, &rawtime); #else // DOCTEST_PLATFORM_WINDOWS gmtime_r(&rawtime, &timeInfo); #endif // DOCTEST_PLATFORM_WINDOWS char timeStamp[timeStampSize]; const char* const fmt = "%Y-%m-%dT%H:%M:%SZ"; std::strftime(timeStamp, timeStampSize, fmt, &timeInfo); return std::string(timeStamp); } struct JUnitTestMessage { JUnitTestMessage(const std::string& _message, const std::string& _type, const std::string& _details) : message(_message), type(_type), details(_details) {} JUnitTestMessage(const std::string& _message, const std::string& _details) : message(_message), type(), details(_details) {} std::string message, type, details; }; struct JUnitTestCase { JUnitTestCase(const std::string& _classname, const std::string& _name) : classname(_classname), name(_name), time(0), failures() {} std::string classname, name; double time; std::vector failures, errors; }; void add(const std::string& classname, const std::string& name) { testcases.emplace_back(classname, name); } void appendSubcaseNamesToLastTestcase(std::vector nameStack) { for(auto& curr: nameStack) if(curr.size()) testcases.back().name += std::string("/") + curr.c_str(); } void addTime(double time) { if(time < 1e-4) time = 0; testcases.back().time = time; totalSeconds += time; } void addFailure(const std::string& message, const std::string& type, const std::string& details) { testcases.back().failures.emplace_back(message, type, details); ++totalFailures; } void addError(const std::string& message, const std::string& details) { testcases.back().errors.emplace_back(message, details); ++totalErrors; } std::vector testcases; double totalSeconds = 0; int totalErrors = 0, totalFailures = 0; }; JUnitTestCaseData testCaseData; // caching pointers/references to objects of these types - safe to do const ContextOptions& opt; const TestCaseData* tc = nullptr; JUnitReporter(const ContextOptions& co) : xml(*co.cout) , opt(co) {} unsigned line(unsigned l) const { return opt.no_line_numbers ? 0 : l; } // ========================================================================================= // WHAT FOLLOWS ARE OVERRIDES OF THE VIRTUAL METHODS OF THE REPORTER INTERFACE // ========================================================================================= void report_query(const QueryData&) override {} void test_run_start() override {} void test_run_end(const TestRunStats& p) override { // remove .exe extension - mainly to have the same output on UNIX and Windows std::string binary_name = skipPathFromFilename(opt.binary_name.c_str()); #ifdef DOCTEST_PLATFORM_WINDOWS if(binary_name.rfind(".exe") != std::string::npos) binary_name = binary_name.substr(0, binary_name.length() - 4); #endif // DOCTEST_PLATFORM_WINDOWS xml.startElement("testsuites"); xml.startElement("testsuite").writeAttribute("name", binary_name) .writeAttribute("errors", testCaseData.totalErrors) .writeAttribute("failures", testCaseData.totalFailures) .writeAttribute("tests", p.numAsserts); if(opt.no_time_in_output == false) { xml.writeAttribute("time", testCaseData.totalSeconds); xml.writeAttribute("timestamp", JUnitTestCaseData::getCurrentTimestamp()); } if(opt.no_version == false) xml.writeAttribute("doctest_version", DOCTEST_VERSION_STR); for(const auto& testCase : testCaseData.testcases) { xml.startElement("testcase") .writeAttribute("classname", testCase.classname) .writeAttribute("name", testCase.name); if(opt.no_time_in_output == false) xml.writeAttribute("time", testCase.time); // This is not ideal, but it should be enough to mimic gtest's junit output. xml.writeAttribute("status", "run"); for(const auto& failure : testCase.failures) { xml.scopedElement("failure") .writeAttribute("message", failure.message) .writeAttribute("type", failure.type) .writeText(failure.details, false); } for(const auto& error : testCase.errors) { xml.scopedElement("error") .writeAttribute("message", error.message) .writeText(error.details); } xml.endElement(); } xml.endElement(); xml.endElement(); } void test_case_start(const TestCaseData& in) override { testCaseData.add(skipPathFromFilename(in.m_file.c_str()), in.m_name); timer.start(); } void test_case_reenter(const TestCaseData& in) override { testCaseData.addTime(timer.getElapsedSeconds()); testCaseData.appendSubcaseNamesToLastTestcase(deepestSubcaseStackNames); deepestSubcaseStackNames.clear(); timer.start(); testCaseData.add(skipPathFromFilename(in.m_file.c_str()), in.m_name); } void test_case_end(const CurrentTestCaseStats&) override { testCaseData.addTime(timer.getElapsedSeconds()); testCaseData.appendSubcaseNamesToLastTestcase(deepestSubcaseStackNames); deepestSubcaseStackNames.clear(); } void test_case_exception(const TestCaseException& e) override { std::lock_guard lock(mutex); testCaseData.addError("exception", e.error_string.c_str()); } void subcase_start(const SubcaseSignature& in) override { deepestSubcaseStackNames.push_back(in.m_name); } void subcase_end() override {} void log_assert(const AssertData& rb) override { if(!rb.m_failed) // report only failures & ignore the `success` option return; std::lock_guard lock(mutex); std::ostringstream os; os << skipPathFromFilename(rb.m_file) << (opt.gnu_file_line ? ":" : "(") << line(rb.m_line) << (opt.gnu_file_line ? ":" : "):") << std::endl; fulltext_log_assert_to_stream(os, rb); log_contexts(os); testCaseData.addFailure(rb.m_decomp.c_str(), assertString(rb.m_at), os.str()); } void log_message(const MessageData&) override {} void test_case_skipped(const TestCaseData&) override {} void log_contexts(std::ostringstream& s) { int num_contexts = get_num_active_contexts(); if(num_contexts) { auto contexts = get_active_contexts(); s << " logged: "; for(int i = 0; i < num_contexts; ++i) { s << (i == 0 ? "" : " "); contexts[i]->stringify(&s); s << std::endl; } } } }; DOCTEST_REGISTER_REPORTER("junit", 0, JUnitReporter); struct Whitespace { int nrSpaces; explicit Whitespace(int nr) : nrSpaces(nr) {} }; std::ostream& operator<<(std::ostream& out, const Whitespace& ws) { if(ws.nrSpaces != 0) out << std::setw(ws.nrSpaces) << ' '; return out; } struct ConsoleReporter : public IReporter { std::ostream& s; bool hasLoggedCurrentTestStart; std::vector subcasesStack; size_t currentSubcaseLevel; std::mutex mutex; // caching pointers/references to objects of these types - safe to do const ContextOptions& opt; const TestCaseData* tc; ConsoleReporter(const ContextOptions& co) : s(*co.cout) , opt(co) {} ConsoleReporter(const ContextOptions& co, std::ostream& ostr) : s(ostr) , opt(co) {} // ========================================================================================= // WHAT FOLLOWS ARE HELPERS USED BY THE OVERRIDES OF THE VIRTUAL METHODS OF THE INTERFACE // ========================================================================================= void separator_to_stream() { s << Color::Yellow << "===============================================================================" "\n"; } const char* getSuccessOrFailString(bool success, assertType::Enum at, const char* success_str) { if(success) return success_str; return failureString(at); } Color::Enum getSuccessOrFailColor(bool success, assertType::Enum at) { return success ? Color::BrightGreen : (at & assertType::is_warn) ? Color::Yellow : Color::Red; } void successOrFailColoredStringToStream(bool success, assertType::Enum at, const char* success_str = "SUCCESS") { s << getSuccessOrFailColor(success, at) << getSuccessOrFailString(success, at, success_str) << ": "; } void log_contexts() { int num_contexts = get_num_active_contexts(); if(num_contexts) { auto contexts = get_active_contexts(); s << Color::None << " logged: "; for(int i = 0; i < num_contexts; ++i) { s << (i == 0 ? "" : " "); contexts[i]->stringify(&s); s << "\n"; } } s << "\n"; } // this was requested to be made virtual so users could override it virtual void file_line_to_stream(const char* file, int line, const char* tail = "") { s << Color::LightGrey << skipPathFromFilename(file) << (opt.gnu_file_line ? ":" : "(") << (opt.no_line_numbers ? 0 : line) // 0 or the real num depending on the option << (opt.gnu_file_line ? ":" : "):") << tail; } void logTestStart() { if(hasLoggedCurrentTestStart) return; separator_to_stream(); file_line_to_stream(tc->m_file.c_str(), tc->m_line, "\n"); if(tc->m_description) s << Color::Yellow << "DESCRIPTION: " << Color::None << tc->m_description << "\n"; if(tc->m_test_suite && tc->m_test_suite[0] != '\0') s << Color::Yellow << "TEST SUITE: " << Color::None << tc->m_test_suite << "\n"; if(strncmp(tc->m_name, " Scenario:", 11) != 0) s << Color::Yellow << "TEST CASE: "; s << Color::None << tc->m_name << "\n"; for(size_t i = 0; i < currentSubcaseLevel; ++i) { if(subcasesStack[i].m_name[0] != '\0') s << " " << subcasesStack[i].m_name << "\n"; } if(currentSubcaseLevel != subcasesStack.size()) { s << Color::Yellow << "\nDEEPEST SUBCASE STACK REACHED (DIFFERENT FROM THE CURRENT ONE):\n" << Color::None; for(size_t i = 0; i < subcasesStack.size(); ++i) { if(subcasesStack[i].m_name[0] != '\0') s << " " << subcasesStack[i].m_name << "\n"; } } s << "\n"; hasLoggedCurrentTestStart = true; } void printVersion() { if(opt.no_version == false) s << Color::Cyan << "[doctest] " << Color::None << "doctest version is \"" << DOCTEST_VERSION_STR << "\"\n"; } void printIntro() { if(opt.no_intro == false) { printVersion(); s << Color::Cyan << "[doctest] " << Color::None << "run with \"--" DOCTEST_OPTIONS_PREFIX_DISPLAY "help\" for options\n"; } } void printHelp() { int sizePrefixDisplay = static_cast(strlen(DOCTEST_OPTIONS_PREFIX_DISPLAY)); printVersion(); // clang-format off s << Color::Cyan << "[doctest]\n" << Color::None; s << Color::Cyan << "[doctest] " << Color::None; s << "boolean values: \"1/on/yes/true\" or \"0/off/no/false\"\n"; s << Color::Cyan << "[doctest] " << Color::None; s << "filter values: \"str1,str2,str3\" (comma separated strings)\n"; s << Color::Cyan << "[doctest]\n" << Color::None; s << Color::Cyan << "[doctest] " << Color::None; s << "filters use wildcards for matching strings\n"; s << Color::Cyan << "[doctest] " << Color::None; s << "something passes a filter if any of the strings in a filter matches\n"; #ifndef DOCTEST_CONFIG_NO_UNPREFIXED_OPTIONS s << Color::Cyan << "[doctest]\n" << Color::None; s << Color::Cyan << "[doctest] " << Color::None; s << "ALL FLAGS, OPTIONS AND FILTERS ALSO AVAILABLE WITH A \"" DOCTEST_CONFIG_OPTIONS_PREFIX "\" PREFIX!!!\n"; #endif s << Color::Cyan << "[doctest]\n" << Color::None; s << Color::Cyan << "[doctest] " << Color::None; s << "Query flags - the program quits after them. Available:\n\n"; s << " -" DOCTEST_OPTIONS_PREFIX_DISPLAY "?, --" DOCTEST_OPTIONS_PREFIX_DISPLAY "help, -" DOCTEST_OPTIONS_PREFIX_DISPLAY "h " << Whitespace(sizePrefixDisplay*0) << "prints this message\n"; s << " -" DOCTEST_OPTIONS_PREFIX_DISPLAY "v, --" DOCTEST_OPTIONS_PREFIX_DISPLAY "version " << Whitespace(sizePrefixDisplay*1) << "prints the version\n"; s << " -" DOCTEST_OPTIONS_PREFIX_DISPLAY "c, --" DOCTEST_OPTIONS_PREFIX_DISPLAY "count " << Whitespace(sizePrefixDisplay*1) << "prints the number of matching tests\n"; s << " -" DOCTEST_OPTIONS_PREFIX_DISPLAY "ltc, --" DOCTEST_OPTIONS_PREFIX_DISPLAY "list-test-cases " << Whitespace(sizePrefixDisplay*1) << "lists all matching tests by name\n"; s << " -" DOCTEST_OPTIONS_PREFIX_DISPLAY "lts, --" DOCTEST_OPTIONS_PREFIX_DISPLAY "list-test-suites " << Whitespace(sizePrefixDisplay*1) << "lists all matching test suites\n"; s << " -" DOCTEST_OPTIONS_PREFIX_DISPLAY "lr, --" DOCTEST_OPTIONS_PREFIX_DISPLAY "list-reporters " << Whitespace(sizePrefixDisplay*1) << "lists all registered reporters\n\n"; // ================================================================================== << 79 s << Color::Cyan << "[doctest] " << Color::None; s << "The available / options/filters are:\n\n"; s << " -" DOCTEST_OPTIONS_PREFIX_DISPLAY "tc, --" DOCTEST_OPTIONS_PREFIX_DISPLAY "test-case= " << Whitespace(sizePrefixDisplay*1) << "filters tests by their name\n"; s << " -" DOCTEST_OPTIONS_PREFIX_DISPLAY "tce, --" DOCTEST_OPTIONS_PREFIX_DISPLAY "test-case-exclude= " << Whitespace(sizePrefixDisplay*1) << "filters OUT tests by their name\n"; s << " -" DOCTEST_OPTIONS_PREFIX_DISPLAY "sf, --" DOCTEST_OPTIONS_PREFIX_DISPLAY "source-file= " << Whitespace(sizePrefixDisplay*1) << "filters tests by their file\n"; s << " -" DOCTEST_OPTIONS_PREFIX_DISPLAY "sfe, --" DOCTEST_OPTIONS_PREFIX_DISPLAY "source-file-exclude= " << Whitespace(sizePrefixDisplay*1) << "filters OUT tests by their file\n"; s << " -" DOCTEST_OPTIONS_PREFIX_DISPLAY "ts, --" DOCTEST_OPTIONS_PREFIX_DISPLAY "test-suite= " << Whitespace(sizePrefixDisplay*1) << "filters tests by their test suite\n"; s << " -" DOCTEST_OPTIONS_PREFIX_DISPLAY "tse, --" DOCTEST_OPTIONS_PREFIX_DISPLAY "test-suite-exclude= " << Whitespace(sizePrefixDisplay*1) << "filters OUT tests by their test suite\n"; s << " -" DOCTEST_OPTIONS_PREFIX_DISPLAY "sc, --" DOCTEST_OPTIONS_PREFIX_DISPLAY "subcase= " << Whitespace(sizePrefixDisplay*1) << "filters subcases by their name\n"; s << " -" DOCTEST_OPTIONS_PREFIX_DISPLAY "sce, --" DOCTEST_OPTIONS_PREFIX_DISPLAY "subcase-exclude= " << Whitespace(sizePrefixDisplay*1) << "filters OUT subcases by their name\n"; s << " -" DOCTEST_OPTIONS_PREFIX_DISPLAY "r, --" DOCTEST_OPTIONS_PREFIX_DISPLAY "reporters= " << Whitespace(sizePrefixDisplay*1) << "reporters to use (console is default)\n"; s << " -" DOCTEST_OPTIONS_PREFIX_DISPLAY "o, --" DOCTEST_OPTIONS_PREFIX_DISPLAY "out= " << Whitespace(sizePrefixDisplay*1) << "output filename\n"; s << " -" DOCTEST_OPTIONS_PREFIX_DISPLAY "ob, --" DOCTEST_OPTIONS_PREFIX_DISPLAY "order-by= " << Whitespace(sizePrefixDisplay*1) << "how the tests should be ordered\n"; s << Whitespace(sizePrefixDisplay*3) << " - [file/suite/name/rand/none]\n"; s << " -" DOCTEST_OPTIONS_PREFIX_DISPLAY "rs, --" DOCTEST_OPTIONS_PREFIX_DISPLAY "rand-seed= " << Whitespace(sizePrefixDisplay*1) << "seed for random ordering\n"; s << " -" DOCTEST_OPTIONS_PREFIX_DISPLAY "f, --" DOCTEST_OPTIONS_PREFIX_DISPLAY "first= " << Whitespace(sizePrefixDisplay*1) << "the first test passing the filters to\n"; s << Whitespace(sizePrefixDisplay*3) << " execute - for range-based execution\n"; s << " -" DOCTEST_OPTIONS_PREFIX_DISPLAY "l, --" DOCTEST_OPTIONS_PREFIX_DISPLAY "last= " << Whitespace(sizePrefixDisplay*1) << "the last test passing the filters to\n"; s << Whitespace(sizePrefixDisplay*3) << " execute - for range-based execution\n"; s << " -" DOCTEST_OPTIONS_PREFIX_DISPLAY "aa, --" DOCTEST_OPTIONS_PREFIX_DISPLAY "abort-after= " << Whitespace(sizePrefixDisplay*1) << "stop after failed assertions\n"; s << " -" DOCTEST_OPTIONS_PREFIX_DISPLAY "scfl,--" DOCTEST_OPTIONS_PREFIX_DISPLAY "subcase-filter-levels= " << Whitespace(sizePrefixDisplay*1) << "apply filters for the first levels\n"; s << Color::Cyan << "\n[doctest] " << Color::None; s << "Bool options - can be used like flags and true is assumed. Available:\n\n"; s << " -" DOCTEST_OPTIONS_PREFIX_DISPLAY "s, --" DOCTEST_OPTIONS_PREFIX_DISPLAY "success= " << Whitespace(sizePrefixDisplay*1) << "include successful assertions in output\n"; s << " -" DOCTEST_OPTIONS_PREFIX_DISPLAY "cs, --" DOCTEST_OPTIONS_PREFIX_DISPLAY "case-sensitive= " << Whitespace(sizePrefixDisplay*1) << "filters being treated as case sensitive\n"; s << " -" DOCTEST_OPTIONS_PREFIX_DISPLAY "e, --" DOCTEST_OPTIONS_PREFIX_DISPLAY "exit= " << Whitespace(sizePrefixDisplay*1) << "exits after the tests finish\n"; s << " -" DOCTEST_OPTIONS_PREFIX_DISPLAY "d, --" DOCTEST_OPTIONS_PREFIX_DISPLAY "duration= " << Whitespace(sizePrefixDisplay*1) << "prints the time duration of each test\n"; s << " -" DOCTEST_OPTIONS_PREFIX_DISPLAY "m, --" DOCTEST_OPTIONS_PREFIX_DISPLAY "minimal= " << Whitespace(sizePrefixDisplay*1) << "minimal console output (only failures)\n"; s << " -" DOCTEST_OPTIONS_PREFIX_DISPLAY "q, --" DOCTEST_OPTIONS_PREFIX_DISPLAY "quiet= " << Whitespace(sizePrefixDisplay*1) << "no console output\n"; s << " -" DOCTEST_OPTIONS_PREFIX_DISPLAY "nt, --" DOCTEST_OPTIONS_PREFIX_DISPLAY "no-throw= " << Whitespace(sizePrefixDisplay*1) << "skips exceptions-related assert checks\n"; s << " -" DOCTEST_OPTIONS_PREFIX_DISPLAY "ne, --" DOCTEST_OPTIONS_PREFIX_DISPLAY "no-exitcode= " << Whitespace(sizePrefixDisplay*1) << "returns (or exits) always with success\n"; s << " -" DOCTEST_OPTIONS_PREFIX_DISPLAY "nr, --" DOCTEST_OPTIONS_PREFIX_DISPLAY "no-run= " << Whitespace(sizePrefixDisplay*1) << "skips all runtime doctest operations\n"; s << " -" DOCTEST_OPTIONS_PREFIX_DISPLAY "ni, --" DOCTEST_OPTIONS_PREFIX_DISPLAY "no-intro= " << Whitespace(sizePrefixDisplay*1) << "omit the framework intro in the output\n"; s << " -" DOCTEST_OPTIONS_PREFIX_DISPLAY "nv, --" DOCTEST_OPTIONS_PREFIX_DISPLAY "no-version= " << Whitespace(sizePrefixDisplay*1) << "omit the framework version in the output\n"; s << " -" DOCTEST_OPTIONS_PREFIX_DISPLAY "nc, --" DOCTEST_OPTIONS_PREFIX_DISPLAY "no-colors= " << Whitespace(sizePrefixDisplay*1) << "disables colors in output\n"; s << " -" DOCTEST_OPTIONS_PREFIX_DISPLAY "fc, --" DOCTEST_OPTIONS_PREFIX_DISPLAY "force-colors= " << Whitespace(sizePrefixDisplay*1) << "use colors even when not in a tty\n"; s << " -" DOCTEST_OPTIONS_PREFIX_DISPLAY "nb, --" DOCTEST_OPTIONS_PREFIX_DISPLAY "no-breaks= " << Whitespace(sizePrefixDisplay*1) << "disables breakpoints in debuggers\n"; s << " -" DOCTEST_OPTIONS_PREFIX_DISPLAY "ns, --" DOCTEST_OPTIONS_PREFIX_DISPLAY "no-skip= " << Whitespace(sizePrefixDisplay*1) << "don't skip test cases marked as skip\n"; s << " -" DOCTEST_OPTIONS_PREFIX_DISPLAY "gfl, --" DOCTEST_OPTIONS_PREFIX_DISPLAY "gnu-file-line= " << Whitespace(sizePrefixDisplay*1) << ":n: vs (n): for line numbers in output\n"; s << " -" DOCTEST_OPTIONS_PREFIX_DISPLAY "npf, --" DOCTEST_OPTIONS_PREFIX_DISPLAY "no-path-filenames= " << Whitespace(sizePrefixDisplay*1) << "only filenames and no paths in output\n"; s << " -" DOCTEST_OPTIONS_PREFIX_DISPLAY "nln, --" DOCTEST_OPTIONS_PREFIX_DISPLAY "no-line-numbers= " << Whitespace(sizePrefixDisplay*1) << "0 instead of real line numbers in output\n"; // ================================================================================== << 79 // clang-format on s << Color::Cyan << "\n[doctest] " << Color::None; s << "for more information visit the project documentation\n\n"; } void printRegisteredReporters() { printVersion(); auto printReporters = [this] (const reporterMap& reporters, const char* type) { if(reporters.size()) { s << Color::Cyan << "[doctest] " << Color::None << "listing all registered " << type << "\n"; for(auto& curr : reporters) s << "priority: " << std::setw(5) << curr.first.first << " name: " << curr.first.second << "\n"; } }; printReporters(getListeners(), "listeners"); printReporters(getReporters(), "reporters"); } // ========================================================================================= // WHAT FOLLOWS ARE OVERRIDES OF THE VIRTUAL METHODS OF THE REPORTER INTERFACE // ========================================================================================= void report_query(const QueryData& in) override { if(opt.version) { printVersion(); } else if(opt.help) { printHelp(); } else if(opt.list_reporters) { printRegisteredReporters(); } else if(opt.count || opt.list_test_cases) { if(opt.list_test_cases) { s << Color::Cyan << "[doctest] " << Color::None << "listing all test case names\n"; separator_to_stream(); } for(unsigned i = 0; i < in.num_data; ++i) s << Color::None << in.data[i]->m_name << "\n"; separator_to_stream(); s << Color::Cyan << "[doctest] " << Color::None << "unskipped test cases passing the current filters: " << g_cs->numTestCasesPassingFilters << "\n"; } else if(opt.list_test_suites) { s << Color::Cyan << "[doctest] " << Color::None << "listing all test suites\n"; separator_to_stream(); for(unsigned i = 0; i < in.num_data; ++i) s << Color::None << in.data[i]->m_test_suite << "\n"; separator_to_stream(); s << Color::Cyan << "[doctest] " << Color::None << "unskipped test cases passing the current filters: " << g_cs->numTestCasesPassingFilters << "\n"; s << Color::Cyan << "[doctest] " << Color::None << "test suites with unskipped test cases passing the current filters: " << g_cs->numTestSuitesPassingFilters << "\n"; } } void test_run_start() override { if(!opt.minimal) printIntro(); } void test_run_end(const TestRunStats& p) override { if(opt.minimal && p.numTestCasesFailed == 0) return; separator_to_stream(); s << std::dec; auto totwidth = int(std::ceil(log10((std::max(p.numTestCasesPassingFilters, static_cast(p.numAsserts))) + 1))); auto passwidth = int(std::ceil(log10((std::max(p.numTestCasesPassingFilters - p.numTestCasesFailed, static_cast(p.numAsserts - p.numAssertsFailed))) + 1))); auto failwidth = int(std::ceil(log10((std::max(p.numTestCasesFailed, static_cast(p.numAssertsFailed))) + 1))); const bool anythingFailed = p.numTestCasesFailed > 0 || p.numAssertsFailed > 0; s << Color::Cyan << "[doctest] " << Color::None << "test cases: " << std::setw(totwidth) << p.numTestCasesPassingFilters << " | " << ((p.numTestCasesPassingFilters == 0 || anythingFailed) ? Color::None : Color::Green) << std::setw(passwidth) << p.numTestCasesPassingFilters - p.numTestCasesFailed << " passed" << Color::None << " | " << (p.numTestCasesFailed > 0 ? Color::Red : Color::None) << std::setw(failwidth) << p.numTestCasesFailed << " failed" << Color::None << " |"; if(opt.no_skipped_summary == false) { const int numSkipped = p.numTestCases - p.numTestCasesPassingFilters; s << " " << (numSkipped == 0 ? Color::None : Color::Yellow) << numSkipped << " skipped" << Color::None; } s << "\n"; s << Color::Cyan << "[doctest] " << Color::None << "assertions: " << std::setw(totwidth) << p.numAsserts << " | " << ((p.numAsserts == 0 || anythingFailed) ? Color::None : Color::Green) << std::setw(passwidth) << (p.numAsserts - p.numAssertsFailed) << " passed" << Color::None << " | " << (p.numAssertsFailed > 0 ? Color::Red : Color::None) << std::setw(failwidth) << p.numAssertsFailed << " failed" << Color::None << " |\n"; s << Color::Cyan << "[doctest] " << Color::None << "Status: " << (p.numTestCasesFailed > 0 ? Color::Red : Color::Green) << ((p.numTestCasesFailed > 0) ? "FAILURE!" : "SUCCESS!") << Color::None << std::endl; } void test_case_start(const TestCaseData& in) override { hasLoggedCurrentTestStart = false; tc = ∈ subcasesStack.clear(); currentSubcaseLevel = 0; } void test_case_reenter(const TestCaseData&) override { subcasesStack.clear(); } void test_case_end(const CurrentTestCaseStats& st) override { if(tc->m_no_output) return; // log the preamble of the test case only if there is something // else to print - something other than that an assert has failed if(opt.duration || (st.failure_flags && st.failure_flags != TestCaseFailureReason::AssertFailure)) logTestStart(); if(opt.duration) s << Color::None << std::setprecision(6) << std::fixed << st.seconds << " s: " << tc->m_name << "\n"; if(st.failure_flags & TestCaseFailureReason::Timeout) s << Color::Red << "Test case exceeded time limit of " << std::setprecision(6) << std::fixed << tc->m_timeout << "!\n"; if(st.failure_flags & TestCaseFailureReason::ShouldHaveFailedButDidnt) { s << Color::Red << "Should have failed but didn't! Marking it as failed!\n"; } else if(st.failure_flags & TestCaseFailureReason::ShouldHaveFailedAndDid) { s << Color::Yellow << "Failed as expected so marking it as not failed\n"; } else if(st.failure_flags & TestCaseFailureReason::CouldHaveFailedAndDid) { s << Color::Yellow << "Allowed to fail so marking it as not failed\n"; } else if(st.failure_flags & TestCaseFailureReason::DidntFailExactlyNumTimes) { s << Color::Red << "Didn't fail exactly " << tc->m_expected_failures << " times so marking it as failed!\n"; } else if(st.failure_flags & TestCaseFailureReason::FailedExactlyNumTimes) { s << Color::Yellow << "Failed exactly " << tc->m_expected_failures << " times as expected so marking it as not failed!\n"; } if(st.failure_flags & TestCaseFailureReason::TooManyFailedAsserts) { s << Color::Red << "Aborting - too many failed asserts!\n"; } s << Color::None; // lgtm [cpp/useless-expression] } void test_case_exception(const TestCaseException& e) override { std::lock_guard lock(mutex); if(tc->m_no_output) return; logTestStart(); file_line_to_stream(tc->m_file.c_str(), tc->m_line, " "); successOrFailColoredStringToStream(false, e.is_crash ? assertType::is_require : assertType::is_check); s << Color::Red << (e.is_crash ? "test case CRASHED: " : "test case THREW exception: ") << Color::Cyan << e.error_string << "\n"; int num_stringified_contexts = get_num_stringified_contexts(); if(num_stringified_contexts) { auto stringified_contexts = get_stringified_contexts(); s << Color::None << " logged: "; for(int i = num_stringified_contexts; i > 0; --i) { s << (i == num_stringified_contexts ? "" : " ") << stringified_contexts[i - 1] << "\n"; } } s << "\n" << Color::None; } void subcase_start(const SubcaseSignature& subc) override { subcasesStack.push_back(subc); ++currentSubcaseLevel; hasLoggedCurrentTestStart = false; } void subcase_end() override { --currentSubcaseLevel; hasLoggedCurrentTestStart = false; } void log_assert(const AssertData& rb) override { if((!rb.m_failed && !opt.success) || tc->m_no_output) return; std::lock_guard lock(mutex); logTestStart(); file_line_to_stream(rb.m_file, rb.m_line, " "); successOrFailColoredStringToStream(!rb.m_failed, rb.m_at); fulltext_log_assert_to_stream(s, rb); log_contexts(); } void log_message(const MessageData& mb) override { if(tc->m_no_output) return; std::lock_guard lock(mutex); logTestStart(); file_line_to_stream(mb.m_file, mb.m_line, " "); s << getSuccessOrFailColor(false, mb.m_severity) << getSuccessOrFailString(mb.m_severity & assertType::is_warn, mb.m_severity, "MESSAGE") << ": "; s << Color::None << mb.m_string << "\n"; log_contexts(); } void test_case_skipped(const TestCaseData&) override {} }; DOCTEST_REGISTER_REPORTER("console", 0, ConsoleReporter); #ifdef DOCTEST_PLATFORM_WINDOWS struct DebugOutputWindowReporter : public ConsoleReporter { DOCTEST_THREAD_LOCAL static std::ostringstream oss; DebugOutputWindowReporter(const ContextOptions& co) : ConsoleReporter(co, oss) {} #define DOCTEST_DEBUG_OUTPUT_REPORTER_OVERRIDE(func, type, arg) \ void func(type arg) override { \ bool with_col = g_no_colors; \ g_no_colors = false; \ ConsoleReporter::func(arg); \ if(oss.tellp() != std::streampos{}) { \ DOCTEST_OUTPUT_DEBUG_STRING(oss.str().c_str()); \ oss.str(""); \ } \ g_no_colors = with_col; \ } DOCTEST_DEBUG_OUTPUT_REPORTER_OVERRIDE(test_run_start, DOCTEST_EMPTY, DOCTEST_EMPTY) DOCTEST_DEBUG_OUTPUT_REPORTER_OVERRIDE(test_run_end, const TestRunStats&, in) DOCTEST_DEBUG_OUTPUT_REPORTER_OVERRIDE(test_case_start, const TestCaseData&, in) DOCTEST_DEBUG_OUTPUT_REPORTER_OVERRIDE(test_case_reenter, const TestCaseData&, in) DOCTEST_DEBUG_OUTPUT_REPORTER_OVERRIDE(test_case_end, const CurrentTestCaseStats&, in) DOCTEST_DEBUG_OUTPUT_REPORTER_OVERRIDE(test_case_exception, const TestCaseException&, in) DOCTEST_DEBUG_OUTPUT_REPORTER_OVERRIDE(subcase_start, const SubcaseSignature&, in) DOCTEST_DEBUG_OUTPUT_REPORTER_OVERRIDE(subcase_end, DOCTEST_EMPTY, DOCTEST_EMPTY) DOCTEST_DEBUG_OUTPUT_REPORTER_OVERRIDE(log_assert, const AssertData&, in) DOCTEST_DEBUG_OUTPUT_REPORTER_OVERRIDE(log_message, const MessageData&, in) DOCTEST_DEBUG_OUTPUT_REPORTER_OVERRIDE(test_case_skipped, const TestCaseData&, in) }; DOCTEST_THREAD_LOCAL std::ostringstream DebugOutputWindowReporter::oss; #endif // DOCTEST_PLATFORM_WINDOWS // the implementation of parseOption() bool parseOptionImpl(int argc, const char* const* argv, const char* pattern, String* value) { // going from the end to the beginning and stopping on the first occurrence from the end for(int i = argc; i > 0; --i) { auto index = i - 1; auto temp = std::strstr(argv[index], pattern); if(temp && (value || strlen(temp) == strlen(pattern))) { //!OCLINT prefer early exits and continue // eliminate matches in which the chars before the option are not '-' bool noBadCharsFound = true; auto curr = argv[index]; while(curr != temp) { if(*curr++ != '-') { noBadCharsFound = false; break; } } if(noBadCharsFound && argv[index][0] == '-') { if(value) { // parsing the value of an option temp += strlen(pattern); const unsigned len = strlen(temp); if(len) { *value = temp; return true; } } else { // just a flag - no value return true; } } } } return false; } // parses an option and returns the string after the '=' character bool parseOption(int argc, const char* const* argv, const char* pattern, String* value = nullptr, const String& defaultVal = String()) { if(value) *value = defaultVal; #ifndef DOCTEST_CONFIG_NO_UNPREFIXED_OPTIONS // offset (normally 3 for "dt-") to skip prefix if(parseOptionImpl(argc, argv, pattern + strlen(DOCTEST_CONFIG_OPTIONS_PREFIX), value)) return true; #endif // DOCTEST_CONFIG_NO_UNPREFIXED_OPTIONS return parseOptionImpl(argc, argv, pattern, value); } // locates a flag on the command line bool parseFlag(int argc, const char* const* argv, const char* pattern) { return parseOption(argc, argv, pattern); } // parses a comma separated list of words after a pattern in one of the arguments in argv bool parseCommaSepArgs(int argc, const char* const* argv, const char* pattern, std::vector& res) { String filtersString; if(parseOption(argc, argv, pattern, &filtersString)) { // tokenize with "," as a separator, unless escaped with backslash std::ostringstream s; auto flush = [&s, &res]() { auto string = s.str(); if(string.size() > 0) { res.push_back(string.c_str()); } s.str(""); }; bool seenBackslash = false; const char* current = filtersString.c_str(); const char* end = current + strlen(current); while(current != end) { char character = *current++; if(seenBackslash) { seenBackslash = false; if(character == ',') { s.put(','); continue; } s.put('\\'); } if(character == '\\') { seenBackslash = true; } else if(character == ',') { flush(); } else { s.put(character); } } if(seenBackslash) { s.put('\\'); } flush(); return true; } return false; } enum optionType { option_bool, option_int }; // parses an int/bool option from the command line bool parseIntOption(int argc, const char* const* argv, const char* pattern, optionType type, int& res) { String parsedValue; if(!parseOption(argc, argv, pattern, &parsedValue)) return false; if(type == 0) { // boolean const char positive[][5] = {"1", "true", "on", "yes"}; // 5 - strlen("true") + 1 const char negative[][6] = {"0", "false", "off", "no"}; // 6 - strlen("false") + 1 // if the value matches any of the positive/negative possibilities for(unsigned i = 0; i < 4; i++) { if(parsedValue.compare(positive[i], true) == 0) { res = 1; //!OCLINT parameter reassignment return true; } if(parsedValue.compare(negative[i], true) == 0) { res = 0; //!OCLINT parameter reassignment return true; } } } else { // integer // TODO: change this to use std::stoi or something else! currently it uses undefined behavior - assumes '0' on failed parse... int theInt = std::atoi(parsedValue.c_str()); // NOLINT if(theInt != 0) { res = theInt; //!OCLINT parameter reassignment return true; } } return false; } } // namespace Context::Context(int argc, const char* const* argv) : p(new detail::ContextState) { parseArgs(argc, argv, true); if(argc) p->binary_name = argv[0]; } Context::~Context() { if(g_cs == p) g_cs = nullptr; delete p; } void Context::applyCommandLine(int argc, const char* const* argv) { parseArgs(argc, argv); if(argc) p->binary_name = argv[0]; } // parses args void Context::parseArgs(int argc, const char* const* argv, bool withDefaults) { using namespace detail; // clang-format off parseCommaSepArgs(argc, argv, DOCTEST_CONFIG_OPTIONS_PREFIX "source-file=", p->filters[0]); parseCommaSepArgs(argc, argv, DOCTEST_CONFIG_OPTIONS_PREFIX "sf=", p->filters[0]); parseCommaSepArgs(argc, argv, DOCTEST_CONFIG_OPTIONS_PREFIX "source-file-exclude=",p->filters[1]); parseCommaSepArgs(argc, argv, DOCTEST_CONFIG_OPTIONS_PREFIX "sfe=", p->filters[1]); parseCommaSepArgs(argc, argv, DOCTEST_CONFIG_OPTIONS_PREFIX "test-suite=", p->filters[2]); parseCommaSepArgs(argc, argv, DOCTEST_CONFIG_OPTIONS_PREFIX "ts=", p->filters[2]); parseCommaSepArgs(argc, argv, DOCTEST_CONFIG_OPTIONS_PREFIX "test-suite-exclude=", p->filters[3]); parseCommaSepArgs(argc, argv, DOCTEST_CONFIG_OPTIONS_PREFIX "tse=", p->filters[3]); parseCommaSepArgs(argc, argv, DOCTEST_CONFIG_OPTIONS_PREFIX "test-case=", p->filters[4]); parseCommaSepArgs(argc, argv, DOCTEST_CONFIG_OPTIONS_PREFIX "tc=", p->filters[4]); parseCommaSepArgs(argc, argv, DOCTEST_CONFIG_OPTIONS_PREFIX "test-case-exclude=", p->filters[5]); parseCommaSepArgs(argc, argv, DOCTEST_CONFIG_OPTIONS_PREFIX "tce=", p->filters[5]); parseCommaSepArgs(argc, argv, DOCTEST_CONFIG_OPTIONS_PREFIX "subcase=", p->filters[6]); parseCommaSepArgs(argc, argv, DOCTEST_CONFIG_OPTIONS_PREFIX "sc=", p->filters[6]); parseCommaSepArgs(argc, argv, DOCTEST_CONFIG_OPTIONS_PREFIX "subcase-exclude=", p->filters[7]); parseCommaSepArgs(argc, argv, DOCTEST_CONFIG_OPTIONS_PREFIX "sce=", p->filters[7]); parseCommaSepArgs(argc, argv, DOCTEST_CONFIG_OPTIONS_PREFIX "reporters=", p->filters[8]); parseCommaSepArgs(argc, argv, DOCTEST_CONFIG_OPTIONS_PREFIX "r=", p->filters[8]); // clang-format on int intRes = 0; String strRes; #define DOCTEST_PARSE_AS_BOOL_OR_FLAG(name, sname, var, default) \ if(parseIntOption(argc, argv, DOCTEST_CONFIG_OPTIONS_PREFIX name "=", option_bool, intRes) || \ parseIntOption(argc, argv, DOCTEST_CONFIG_OPTIONS_PREFIX sname "=", option_bool, intRes)) \ p->var = static_cast(intRes); \ else if(parseFlag(argc, argv, DOCTEST_CONFIG_OPTIONS_PREFIX name) || \ parseFlag(argc, argv, DOCTEST_CONFIG_OPTIONS_PREFIX sname)) \ p->var = true; \ else if(withDefaults) \ p->var = default #define DOCTEST_PARSE_INT_OPTION(name, sname, var, default) \ if(parseIntOption(argc, argv, DOCTEST_CONFIG_OPTIONS_PREFIX name "=", option_int, intRes) || \ parseIntOption(argc, argv, DOCTEST_CONFIG_OPTIONS_PREFIX sname "=", option_int, intRes)) \ p->var = intRes; \ else if(withDefaults) \ p->var = default #define DOCTEST_PARSE_STR_OPTION(name, sname, var, default) \ if(parseOption(argc, argv, DOCTEST_CONFIG_OPTIONS_PREFIX name "=", &strRes, default) || \ parseOption(argc, argv, DOCTEST_CONFIG_OPTIONS_PREFIX sname "=", &strRes, default) || \ withDefaults) \ p->var = strRes // clang-format off DOCTEST_PARSE_STR_OPTION("out", "o", out, ""); DOCTEST_PARSE_STR_OPTION("order-by", "ob", order_by, "file"); DOCTEST_PARSE_INT_OPTION("rand-seed", "rs", rand_seed, 0); DOCTEST_PARSE_INT_OPTION("first", "f", first, 0); DOCTEST_PARSE_INT_OPTION("last", "l", last, UINT_MAX); DOCTEST_PARSE_INT_OPTION("abort-after", "aa", abort_after, 0); DOCTEST_PARSE_INT_OPTION("subcase-filter-levels", "scfl", subcase_filter_levels, INT_MAX); DOCTEST_PARSE_AS_BOOL_OR_FLAG("success", "s", success, false); DOCTEST_PARSE_AS_BOOL_OR_FLAG("case-sensitive", "cs", case_sensitive, false); DOCTEST_PARSE_AS_BOOL_OR_FLAG("exit", "e", exit, false); DOCTEST_PARSE_AS_BOOL_OR_FLAG("duration", "d", duration, false); DOCTEST_PARSE_AS_BOOL_OR_FLAG("minimal", "m", minimal, false); DOCTEST_PARSE_AS_BOOL_OR_FLAG("quiet", "q", quiet, false); DOCTEST_PARSE_AS_BOOL_OR_FLAG("no-throw", "nt", no_throw, false); DOCTEST_PARSE_AS_BOOL_OR_FLAG("no-exitcode", "ne", no_exitcode, false); DOCTEST_PARSE_AS_BOOL_OR_FLAG("no-run", "nr", no_run, false); DOCTEST_PARSE_AS_BOOL_OR_FLAG("no-intro", "ni", no_intro, false); DOCTEST_PARSE_AS_BOOL_OR_FLAG("no-version", "nv", no_version, false); DOCTEST_PARSE_AS_BOOL_OR_FLAG("no-colors", "nc", no_colors, false); DOCTEST_PARSE_AS_BOOL_OR_FLAG("force-colors", "fc", force_colors, false); DOCTEST_PARSE_AS_BOOL_OR_FLAG("no-breaks", "nb", no_breaks, false); DOCTEST_PARSE_AS_BOOL_OR_FLAG("no-skip", "ns", no_skip, false); DOCTEST_PARSE_AS_BOOL_OR_FLAG("gnu-file-line", "gfl", gnu_file_line, !bool(DOCTEST_MSVC)); DOCTEST_PARSE_AS_BOOL_OR_FLAG("no-path-filenames", "npf", no_path_in_filenames, false); DOCTEST_PARSE_AS_BOOL_OR_FLAG("no-line-numbers", "nln", no_line_numbers, false); DOCTEST_PARSE_AS_BOOL_OR_FLAG("no-debug-output", "ndo", no_debug_output, false); DOCTEST_PARSE_AS_BOOL_OR_FLAG("no-skipped-summary", "nss", no_skipped_summary, false); DOCTEST_PARSE_AS_BOOL_OR_FLAG("no-time-in-output", "ntio", no_time_in_output, false); // clang-format on if(withDefaults) { p->help = false; p->version = false; p->count = false; p->list_test_cases = false; p->list_test_suites = false; p->list_reporters = false; } if(parseFlag(argc, argv, DOCTEST_CONFIG_OPTIONS_PREFIX "help") || parseFlag(argc, argv, DOCTEST_CONFIG_OPTIONS_PREFIX "h") || parseFlag(argc, argv, DOCTEST_CONFIG_OPTIONS_PREFIX "?")) { p->help = true; p->exit = true; } if(parseFlag(argc, argv, DOCTEST_CONFIG_OPTIONS_PREFIX "version") || parseFlag(argc, argv, DOCTEST_CONFIG_OPTIONS_PREFIX "v")) { p->version = true; p->exit = true; } if(parseFlag(argc, argv, DOCTEST_CONFIG_OPTIONS_PREFIX "count") || parseFlag(argc, argv, DOCTEST_CONFIG_OPTIONS_PREFIX "c")) { p->count = true; p->exit = true; } if(parseFlag(argc, argv, DOCTEST_CONFIG_OPTIONS_PREFIX "list-test-cases") || parseFlag(argc, argv, DOCTEST_CONFIG_OPTIONS_PREFIX "ltc")) { p->list_test_cases = true; p->exit = true; } if(parseFlag(argc, argv, DOCTEST_CONFIG_OPTIONS_PREFIX "list-test-suites") || parseFlag(argc, argv, DOCTEST_CONFIG_OPTIONS_PREFIX "lts")) { p->list_test_suites = true; p->exit = true; } if(parseFlag(argc, argv, DOCTEST_CONFIG_OPTIONS_PREFIX "list-reporters") || parseFlag(argc, argv, DOCTEST_CONFIG_OPTIONS_PREFIX "lr")) { p->list_reporters = true; p->exit = true; } } // allows the user to add procedurally to the filters from the command line void Context::addFilter(const char* filter, const char* value) { setOption(filter, value); } // allows the user to clear all filters from the command line void Context::clearFilters() { for(auto& curr : p->filters) curr.clear(); } // allows the user to override procedurally the bool options from the command line void Context::setOption(const char* option, bool value) { setOption(option, value ? "true" : "false"); } // allows the user to override procedurally the int options from the command line void Context::setOption(const char* option, int value) { setOption(option, toString(value).c_str()); // NOLINTNEXTLINE(clang-analyzer-cplusplus.NewDeleteLeaks) } // allows the user to override procedurally the string options from the command line void Context::setOption(const char* option, const char* value) { auto argv = String("-") + option + "=" + value; auto lvalue = argv.c_str(); parseArgs(1, &lvalue); } // users should query this in their main() and exit the program if true bool Context::shouldExit() { return p->exit; } void Context::setAsDefaultForAssertsOutOfTestCases() { g_cs = p; } void Context::setAssertHandler(detail::assert_handler ah) { p->ah = ah; } void Context::setCout(std::ostream* out) { p->cout = out; } static class DiscardOStream : public std::ostream { private: class : public std::streambuf { private: // allowing some buffering decreases the amount of calls to overflow char buf[1024]; protected: std::streamsize xsputn(const char_type*, std::streamsize count) override { return count; } int_type overflow(int_type ch) override { setp(std::begin(buf), std::end(buf)); return traits_type::not_eof(ch); } } discardBuf; public: DiscardOStream() : std::ostream(&discardBuf) {} } discardOut; // the main function that does all the filtering and test running int Context::run() { using namespace detail; // save the old context state in case such was setup - for using asserts out of a testing context auto old_cs = g_cs; // this is the current contest g_cs = p; is_running_in_test = true; g_no_colors = p->no_colors; p->resetRunData(); std::fstream fstr; if(p->cout == nullptr) { if(p->quiet) { p->cout = &discardOut; } else if(p->out.size()) { // to a file if specified fstr.open(p->out.c_str(), std::fstream::out); p->cout = &fstr; } else { // stdout by default p->cout = &std::cout; } } FatalConditionHandler::allocateAltStackMem(); auto cleanup_and_return = [&]() { FatalConditionHandler::freeAltStackMem(); if(fstr.is_open()) fstr.close(); // restore context g_cs = old_cs; is_running_in_test = false; // we have to free the reporters which were allocated when the run started for(auto& curr : p->reporters_currently_used) delete curr; p->reporters_currently_used.clear(); if(p->numTestCasesFailed && !p->no_exitcode) return EXIT_FAILURE; return EXIT_SUCCESS; }; // setup default reporter if none is given through the command line if(p->filters[8].empty()) p->filters[8].push_back("console"); // check to see if any of the registered reporters has been selected for(auto& curr : getReporters()) { if(matchesAny(curr.first.second.c_str(), p->filters[8], false, p->case_sensitive)) p->reporters_currently_used.push_back(curr.second(*g_cs)); } // TODO: check if there is nothing in reporters_currently_used // prepend all listeners for(auto& curr : getListeners()) p->reporters_currently_used.insert(p->reporters_currently_used.begin(), curr.second(*g_cs)); #ifdef DOCTEST_PLATFORM_WINDOWS if(isDebuggerActive() && p->no_debug_output == false) p->reporters_currently_used.push_back(new DebugOutputWindowReporter(*g_cs)); #endif // DOCTEST_PLATFORM_WINDOWS // handle version, help and no_run if(p->no_run || p->version || p->help || p->list_reporters) { DOCTEST_ITERATE_THROUGH_REPORTERS(report_query, QueryData()); return cleanup_and_return(); } std::vector testArray; for(auto& curr : getRegisteredTests()) testArray.push_back(&curr); p->numTestCases = testArray.size(); // sort the collected records if(!testArray.empty()) { if(p->order_by.compare("file", true) == 0) { std::sort(testArray.begin(), testArray.end(), fileOrderComparator); } else if(p->order_by.compare("suite", true) == 0) { std::sort(testArray.begin(), testArray.end(), suiteOrderComparator); } else if(p->order_by.compare("name", true) == 0) { std::sort(testArray.begin(), testArray.end(), nameOrderComparator); } else if(p->order_by.compare("rand", true) == 0) { std::srand(p->rand_seed); // random_shuffle implementation const auto first = &testArray[0]; for(size_t i = testArray.size() - 1; i > 0; --i) { int idxToSwap = std::rand() % (i + 1); // NOLINT const auto temp = first[i]; first[i] = first[idxToSwap]; first[idxToSwap] = temp; } } else if(p->order_by.compare("none", true) == 0) { // means no sorting - beneficial for death tests which call into the executable // with a specific test case in mind - we don't want to slow down the startup times } } std::set testSuitesPassingFilt; bool query_mode = p->count || p->list_test_cases || p->list_test_suites; std::vector queryResults; if(!query_mode) DOCTEST_ITERATE_THROUGH_REPORTERS(test_run_start, DOCTEST_EMPTY); // invoke the registered functions if they match the filter criteria (or just count them) for(auto& curr : testArray) { const auto& tc = *curr; bool skip_me = false; if(tc.m_skip && !p->no_skip) skip_me = true; if(!matchesAny(tc.m_file.c_str(), p->filters[0], true, p->case_sensitive)) skip_me = true; if(matchesAny(tc.m_file.c_str(), p->filters[1], false, p->case_sensitive)) skip_me = true; if(!matchesAny(tc.m_test_suite, p->filters[2], true, p->case_sensitive)) skip_me = true; if(matchesAny(tc.m_test_suite, p->filters[3], false, p->case_sensitive)) skip_me = true; if(!matchesAny(tc.m_name, p->filters[4], true, p->case_sensitive)) skip_me = true; if(matchesAny(tc.m_name, p->filters[5], false, p->case_sensitive)) skip_me = true; if(!skip_me) p->numTestCasesPassingFilters++; // skip the test if it is not in the execution range if((p->last < p->numTestCasesPassingFilters && p->first <= p->last) || (p->first > p->numTestCasesPassingFilters)) skip_me = true; if(skip_me) { if(!query_mode) DOCTEST_ITERATE_THROUGH_REPORTERS(test_case_skipped, tc); continue; } // do not execute the test if we are to only count the number of filter passing tests if(p->count) continue; // print the name of the test and don't execute it if(p->list_test_cases) { queryResults.push_back(&tc); continue; } // print the name of the test suite if not done already and don't execute it if(p->list_test_suites) { if((testSuitesPassingFilt.count(tc.m_test_suite) == 0) && tc.m_test_suite[0] != '\0') { queryResults.push_back(&tc); testSuitesPassingFilt.insert(tc.m_test_suite); p->numTestSuitesPassingFilters++; } continue; } // execute the test if it passes all the filtering { p->currentTest = &tc; p->failure_flags = TestCaseFailureReason::None; p->seconds = 0; // reset atomic counters p->numAssertsFailedCurrentTest_atomic = 0; p->numAssertsCurrentTest_atomic = 0; p->subcasesPassed.clear(); DOCTEST_ITERATE_THROUGH_REPORTERS(test_case_start, tc); p->timer.start(); bool run_test = true; do { // reset some of the fields for subcases (except for the set of fully passed ones) p->should_reenter = false; p->subcasesCurrentMaxLevel = 0; p->subcasesStack.clear(); p->shouldLogCurrentException = true; // reset stuff for logging with INFO() p->stringifiedContexts.clear(); #ifndef DOCTEST_CONFIG_NO_EXCEPTIONS try { #endif // DOCTEST_CONFIG_NO_EXCEPTIONS // MSVC 2015 diagnoses fatalConditionHandler as unused (because reset() is a static method) DOCTEST_MSVC_SUPPRESS_WARNING_WITH_PUSH(4101) // unreferenced local variable FatalConditionHandler fatalConditionHandler; // Handle signals // execute the test tc.m_test(); fatalConditionHandler.reset(); DOCTEST_MSVC_SUPPRESS_WARNING_POP #ifndef DOCTEST_CONFIG_NO_EXCEPTIONS } catch(const TestFailureException&) { p->failure_flags |= TestCaseFailureReason::AssertFailure; } catch(...) { DOCTEST_ITERATE_THROUGH_REPORTERS(test_case_exception, {translateActiveException(), false}); p->failure_flags |= TestCaseFailureReason::Exception; } #endif // DOCTEST_CONFIG_NO_EXCEPTIONS // exit this loop if enough assertions have failed - even if there are more subcases if(p->abort_after > 0 && p->numAssertsFailed + p->numAssertsFailedCurrentTest_atomic >= p->abort_after) { run_test = false; p->failure_flags |= TestCaseFailureReason::TooManyFailedAsserts; } if(p->should_reenter && run_test) DOCTEST_ITERATE_THROUGH_REPORTERS(test_case_reenter, tc); if(!p->should_reenter) run_test = false; } while(run_test); p->finalizeTestCaseData(); DOCTEST_ITERATE_THROUGH_REPORTERS(test_case_end, *g_cs); p->currentTest = nullptr; // stop executing tests if enough assertions have failed if(p->abort_after > 0 && p->numAssertsFailed >= p->abort_after) break; } } if(!query_mode) { DOCTEST_ITERATE_THROUGH_REPORTERS(test_run_end, *g_cs); } else { QueryData qdata; qdata.run_stats = g_cs; qdata.data = queryResults.data(); qdata.num_data = unsigned(queryResults.size()); DOCTEST_ITERATE_THROUGH_REPORTERS(report_query, qdata); } return cleanup_and_return(); } IReporter::~IReporter() = default; int IReporter::get_num_active_contexts() { return detail::g_infoContexts.size(); } const IContextScope* const* IReporter::get_active_contexts() { return get_num_active_contexts() ? &detail::g_infoContexts[0] : nullptr; } int IReporter::get_num_stringified_contexts() { return detail::g_cs->stringifiedContexts.size(); } const String* IReporter::get_stringified_contexts() { return get_num_stringified_contexts() ? &detail::g_cs->stringifiedContexts[0] : nullptr; } namespace detail { void registerReporterImpl(const char* name, int priority, reporterCreatorFunc c, bool isReporter) { if(isReporter) getReporters().insert(reporterMap::value_type(reporterMap::key_type(priority, name), c)); else getListeners().insert(reporterMap::value_type(reporterMap::key_type(priority, name), c)); } } // namespace detail } // namespace doctest #endif // DOCTEST_CONFIG_DISABLE #ifdef DOCTEST_CONFIG_IMPLEMENT_WITH_MAIN DOCTEST_MSVC_SUPPRESS_WARNING_WITH_PUSH(4007) // 'function' : must be 'attribute' - see issue #182 int main(int argc, char** argv) { return doctest::Context(argc, argv).run(); } DOCTEST_MSVC_SUPPRESS_WARNING_POP #endif // DOCTEST_CONFIG_IMPLEMENT_WITH_MAIN DOCTEST_CLANG_SUPPRESS_WARNING_POP DOCTEST_MSVC_SUPPRESS_WARNING_POP DOCTEST_GCC_SUPPRESS_WARNING_POP DOCTEST_SUPPRESS_COMMON_WARNINGS_POP #endif // DOCTEST_LIBRARY_IMPLEMENTATION #endif // DOCTEST_CONFIG_IMPLEMENT ================================================ FILE: CMakeLists.txt ================================================ cmake_minimum_required( VERSION 3.31 ) include(CheckIPOSupported) option(BUILD_DOC "Build LaTeX documentation" OFF ) project(ipr VERSION 0.50 LANGUAGES CXX ) set(CMAKE_CXX_STANDARD 23) set(CMAKE_CXX_STANDARD_REQUIRED True) set(CMAKE_CXX_EXTENSIONS False) # Enable link-time optimization or close approximation check_ipo_supported(RESULT ipo_supported OUTPUT ipo_disabled_msg) if (ipo_supported) set(CMAKE_INTERPROCEDURAL_OPTIMIZATION TRUE) else() message(WARNING "LTO disabled: ${ipo_disabled_msg}") endif() add_library(${PROJECT_NAME} src/impl.cxx src/cxx-ipr-io.cxx src/cxx-ipr-traversal.cxx src/input.cxx src/utility.cxx src/cxx-ipr.cxx ) target_sources(${PROJECT_NAME} PUBLIC FILE_SET CXX_MODULES FILES src/cxx-ipr-vocabulary.ixx src/cxx-ipr-syntax.ixx src/cxx-ipr.ixx src/cxx-ipr-traversal.ixx src/cxx-ipr-io.ixx ) target_include_directories(${PROJECT_NAME} PUBLIC ${PROJECT_SOURCE_DIR}/include ) target_sources(${PROJECT_NAME} PRIVATE ${PROJECT_SOURCE_DIR}/include/ipr/impl ${PROJECT_SOURCE_DIR}/include/ipr/lexer ${PROJECT_SOURCE_DIR}/include/ipr/node-category ${PROJECT_SOURCE_DIR}/include/ipr/utility-impl ${PROJECT_SOURCE_DIR}/include/ipr/input ${PROJECT_SOURCE_DIR}/3rdparty/doctest/doctest.h ) set_target_properties(${PROJECT_NAME} PROPERTIES CXX_STANDARD 23 CXX_STANDARD_REQUIRED ON CXX_EXTENSIONS OFF ) target_compile_options(${PROJECT_NAME} PUBLIC $<$: /permissive- # Turn on strict language conformance /EHsc # Turn on exception handling semantics > $<$,$>: -Wno-overloaded-virtual # Avoid bogus warnings against foundational techniques. > PRIVATE $<$: /W2 # Usual warnings > $<$,$>: -Wall # Turn on all useful warnings -pedantic # Turn on strict language conformance > $<$: -Wno-delete-non-abstract-non-virtual-dtor # System headers plagued > ) target_compile_definitions(${PROJECT_NAME} PRIVATE $<$: _FILE_OFFSET_BITS=64 # We want the ability to process large files. > ) set_property(SOURCE ${PROJECT_SOURCE_DIR}/src/input.cxx APPEND PROPERTY COMPILE_DEFINITIONS NDEBUG) install( TARGETS ipr LIBRARY DESTINATION lib ARCHIVE DESTINATION lib FILE_SET CXX_MODULES DESTINATION lib/cmake/ipr ) install( DIRECTORY include/ipr DESTINATION include ) if(BUILD_DOC) include(cmake/UseLATEX.cmake) add_subdirectory(doc) endif() # Testing enable_testing() set(DOCTEST_INCLUDE_DIR "${PROJECT_SOURCE_DIR}/3rdparty") add_subdirectory(tests) ================================================ FILE: ChangeLog ================================================ 2019-06-26 Gabriel Dos Reis * configure.ac: Check for pdflatex. Regenate build description. * CMakeLists.txt: Fix typo. 2010-11-13 Gabriel Dos Reis * configure.ac: Bump version to 0.47. 2010-07-23 Gabriel Dos Reis * configure.ac: Bump version to 0.46. 2010-03-29 Gabriel Dos Reis * configure.ac: Bump to 0.45. 2009-09-22 Gabriel Dos Reis * configure.ac: Bump to 0.44. 2008-05-23 Gabriel Dos Reis * configure.ac: Bump to 0.40. 2005-04-04 Gabriel Dos Reis * configure.ac (ipr_top_builddir): Remove. (ipr_srcdir): Likewise. Require Autoconf 2.52 or higher. Bump version to 0.36. Include "foreign" Automake's options. 2005-03-23 Gabriel Dos Reis * configure.ac: Bump version to 0.35. (ipr_top_builddir): New. (ipr_srcdir): Remove. 2005-03-02 Gabriel Dos Reis * Mark for 0.34. * configure.ac: Bump version. 2005-02-06 Gabriel Dos Reis * Mark for 0.33 * configure.ac: Bump version. * configure: Regenrate. 2005-02-03 Gabriel Dos Reis * configure.ac: Bump version. * configure: Regenerate. 2005-02-01 Gabriel Dos Reis * Mark for 0.31. * configure.ac: Bump version. * configure: Regenerate. 2005-01-31 Gabriel Dos Reis * Mark for 0.30. * configure: Regenrate. * configure.ac: Bump version. 2005-01-27 Gabriel Dos Reis * Mark for 0.29 * configure.ac: Bump version. 2005-01-10 Gabriel Dos Reis * configure.ac: AC_OUTPUT doc/Makefile too. 2005-01-10 Gabriel Dos Reis * Mark for 0.28 2005-01-10 Gabriel Dos Reis * configure.ac: AC_OUTPUT include/Makefile * configure: Regenerate. * Makefile.am (SUBDIRS): Add include. * Makefile.in: Regenerate. * libtool: New. 2005-01-09 Gabriel Dos Reis Enable autoconf/automake support. * aclocal.m4: New. * config.guess: Likewise. * config.sub: Likewise. * configure.ac: Likewise. * depcomp: Likewise. * install-sh: Likewise. * missing: Likewise. * Makefile.am: Likewise. * Makefile.in: Likewise. 2005-01-09 Gabriel Dos Reis * Mark for 0.27. 2004-10-28 Gabriel Dos Reis * Mark for 0.26. 2004-09-23 Gabriel Dos Reis * Mark for 0.25. 2004-09-20 Gabriel Dos Reis * Mark for 0.24. 2004-08-30 Gabriel Dos Reis * Mark for 0.23. 2004-08-25 Gabriel Dos Reis * Mark for 0.22. 2004-08-15 Gabriel Dos Reis * Mark for 0.21. 2004-08-10 Gabriel Dos Reis * Mark for 0.20. 2004-08-03 Gabriel Dos Reis * Mark for 0.19. 2004-07-16 Gabriel Dos Reis * Mark for 0.18 2004-07-15 Gabriel Dos Reis * Mark for 0.17 2004-07-08 Gabriel Dos Reis * Mark for 0.16 2004-07-08 Gabriel Dos Reis * grammar/nxpr.y: Update to last version from BS. 2004-07-05 Gabriel Dos Reis * Mark for 0.15. 2004-07-04 Gabriel Dos Reis * Mark for 0.14. 2004-07-02 Gabriel Dos Reis * Mark for 0.13. 2004-07-01 Gabriel Dos Reis * Mark for 0.12. 2004-07-01 Gabriel Dos Reis * grammar/nxpr.y: Update to changes from BS. 2004-06-29 Gabriel Dos Reis * Mark for 0.11. 2004-06-12 Gabriel Dos Reis * Mark for 0.10. 2004-06-12 Gabriel Dos Reis * grammar/nxpr.y: Incorporate update from BS. 2004-05-17 Gabriel Dos Reis * Mark for 0.08. 2004-05-05 Gabriel Dos Reis * Mark for 0.06. 2004-05-01 Gabriel Dos Reis * Mark for 0.05. 2004-04-21 Gabriel Dos Reis * Mark for 0.04. ================================================ FILE: INSTALL ================================================ IPR is using CMake as its build system. The default configuration will build the library and the tests. The tests can be invoked using the test target: > mkdir build && cd build > cmake .. > make > make test Or alternatively, tests can be invoked using CTest: > ctest The Latex documentation is also optional. The BUILD_DOC CMake option enables building the documentation. It required pdflatex to be available in the path. The documentation can be built using the doc target. > mkdir build && cd build > cmake .. -DBUILD_DOC=ON > make doc ================================================ FILE: LICENSE ================================================ Copyright (C) 2004-2013, Texas A&M University. Copyright (C) 2014-2015, Gabriel Dos Reis and Bjarne Stroustrup. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ================================================ FILE: README.md ================================================ ![Build & Test](https://github.com/GabrielDosReis/ipr/actions/workflows/main.yml/badge.svg) ![Analysis](https://github.com/GabrielDosReis/ipr/actions/workflows/analysis.yml/badge.svg) ![Docs](https://github.com/GabrielDosReis/ipr/actions/workflows/docs.yaml/badge.svg) The IPR, short for *Internal Program Representation*, is an open source project originally developed as the core data structures of a framework for semantics-based analysis and transformation of C++ programs. The foundations of the IPR were laid down and implemented between 2004 and 2005, as part of The Pivot research project. An overview, the general philosophy, and the design principles behind the IPR are presented in the paper ["A Principled, Complete, and Efficient Representation of C++"](http://www.stroustrup.com/macis09.pdf) . That paper is a useful source of general information about the IPR and non-obvious challenges in representing C++ programs in their most general forms. The IPR library purposefully separates the interface (a collection of abstract classes found in ``) from the implementation (found in `) for various reasons. An interface class (say `ipr::Fundecl`) can admit several implementations: a class for non-defining function declarations, and another class for function definitions. Furthermore, compilers and tools (in general) can provide their own specific optimized implementations of the interface without impacting users of the IPR as long as those users restrict themselves to the public interface. Such a separation of concerns shields users of the library from implementation vagaries. The implementation in `` is provided for exposition and reference. For more information, bug reports, and suggestions, please visit https://github.com/GabrielDosReis/ipr Gabriel Dos Reis, Bjarne Stroustrup. ================================================ FILE: STYLE.md ================================================ # Coding Style Use good judgment, and taste. Appreciate simplicity. In absence of the above, consider the [C++ Core Guidelines](https://github.com/isocpp/CppCoreGuidelines). ## Logical connectives Use: - `not` for logical negation - `and` for logical conjunction - `or` for logical disjunction ## Factory function names Use: - `get_xxx` for factory function names that return the same output for same inputs - `make_xxx` for factory function names that always return fresh structures ## Source file extensions Use - `.cxx` for implementation source files ================================================ FILE: cmake/UseLATEX.cmake ================================================ # File: UseLATEX.cmake # CMAKE commands to actually use the LaTeX compiler # Version: 2.7.0 # Author: Kenneth Moreland # # Copyright 2004, 2015 Sandia Corporation. # Under the terms of Contract DE-AC04-94AL85000, there is a non-exclusive # license for use of this work by or on behalf of the U.S. Government. # # This software is released under the BSD 3-Clause License. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions are # met: # # 1. Redistributions of source code must retain the above copyright notice, # this list of conditions and the following disclaimer. # # 2. Redistributions in binary form must reproduce the above copyright # notice, this list of conditions and the following disclaimer in the # documentation and/or other materials provided with the distribution. # # 3. Neither the name of the copyright holder nor the names of its # contributors may be used to endorse or promote products derived from this # software without specific prior written permission. # # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS # IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, # THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR # PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR # CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, # EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, # PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR # PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF # LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING # NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS # SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. # # # The following function is defined: # # add_latex_document( # [BIBFILES ] # [INPUTS ] # [IMAGE_DIRS ] # [IMAGES ] # [CONFIGURE ] # [DEPENDS ] # [MULTIBIB_NEWCITES ] # [USE_BIBLATEX] # [USE_INDEX] # [INDEX_NAMES ] # [USE_GLOSSARY] [USE_NOMENCL] # [FORCE_PDF] [FORCE_DVI] [FORCE_HTML] # [TARGET_NAME ] # [INCLUDE_DIRECTORIES ] # [EXCLUDE_FROM_ALL] # [EXCLUDE_FROM_DEFAULTS]) # Adds targets that compile . The latex output is placed # in LATEX_OUTPUT_PATH or CMAKE_CURRENT_BINARY_DIR if the former is # not set. The latex program is picky about where files are located, # so all input files are copied from the source directory to the # output directory. This includes the target tex file, any tex file # listed with the INPUTS option, the bibliography files listed with # the BIBFILES option, and any .cls, .bst, .clo, .sty, .ist, and .fd # files found in the current source directory. Images found in the # IMAGE_DIRS directories or listed by IMAGES are also copied to the # output directory and converted to an appropriate format if necessary. # Any tex files also listed with the CONFIGURE option are also processed # with the CMake CONFIGURE_FILE command (with the @ONLY flag). Any file # listed in CONFIGURE but not the target tex file or listed with INPUTS # has no effect. DEPENDS can be used to specify generated files that are # needed to compile the latex target. # # The following targets are made. The name prefix is based off of the # base name of the tex file unless TARGET_NAME is specified. If # TARGET_NAME is specified, then that name is used for the targets. # # name_dvi: Makes .dvi # name_pdf: Makes .pdf using pdflatex. # name_safepdf: Makes .pdf using ps2pdf. If using the # default program arguments, this will ensure all fonts # are embedded and no lossy compression has been # performed on images. # name_ps: Makes .ps # name_html: Makes .html # name_auxclean: Deletes .aux and other auxiliary files. # This is sometimes necessary if a LaTeX error occurs # and writes a bad aux file. Unlike the regular clean # target, it does not delete other input files, such as # converted images, to save time on the rebuild. # # Unless the EXCLUDE_FROM_ALL option is given, one of these targets # is added to the ALL target and built by default. Which target is # determined by the LATEX_DEFAULT_BUILD CMake variable. See the # documentation of that variable for more details. # # Unless the EXCLUDE_FROM_DEFAULTS option is given, all these targets # are added as dependencies to targets named dvi, pdf, safepdf, ps, # html, and auxclean, respectively. # # USE_BIBLATEX enables the use of biblatex/biber as an alternative to # bibtex. Bibtex remains the default if USE_BIBLATEX is not # specified. # # If the argument USE_INDEX is given, then commands to build an index # are made. If the argument INDEX_NAMES is given, an index file is # generated for each name in this list. See the LaTeX package multind # for more information about how to generate multiple indices. # # If the argument USE_GLOSSARY is given, then commands to # build a glossary are made. If the argument MULTIBIB_NEWCITES is # given, then additional bibtex calls are added to the build to # support the extra auxiliary files created with the \newcite command # in the multibib package. # # INCLUDE_DIRECTORIES provides a list of directories in which LaTeX # should look for input files. It accepts both files relative to the # binary directory and absolute paths. # # History: # # 2.7.0 Add INCLUDE_DIRECTORIES parameters. (Thanks to Eric Dönges.) # # 2.6.1 Fix issue with detecting long undefined reference warnings that # LaTeX "helpfully" split across lines (and which fowled up our # regex). # # 2.6.0 Skip image conversion targets that are not used when a force option # is given. This helps prevent errors for missing conversion programs # that are not needed. (Thanks to Martin Wetzel.) # # 2.5.0 Parse biber output for warnings. # # For regular bibtex, you get warnings about undefined references # when you run latex. However, when using biber, biber itself prints # out the said warning and latex sees nothing. Thus, when using biber # the regular output is now suppressed and the log file is scanned # for potential issues. # # 2.4.9 Use biblatex.cfg file if it exists and the USE_BIBLATEX option is ON. # # 2.4.8 Fix synctex issue with absolute paths not being converted. # # 2.4.7 Fix some issues with spaces in the path of the working directory where # LaTeX is executed. # # 2.4.6 Fix parse issue with older versions of CMake. # # 2.4.5 Fix issues with files and paths containing spaces. # # 2.4.4 Improve error reporting message when LaTeX fails. # # When LaTeX fails, delete the output file, which is invalid. # # Add warnings for "missing characters." These usually mean that a # non-ASCII character is in the document and will not be printed # correctly. # # 2.4.3 Check for warnings from the natbib package. When using natbib, # warnings for missing bibliography references look different. So # far, natbib seems to be quiet unless something is important, so # look for all natbib warnings. (We can change this later if # necessary.) # # 2.4.2 Fix an issue where new versions of ImageMagick expect the order of # options in command line execution of magick/convert. (See, for # example, http://www.imagemagick.org/Usage/basics/#why.) # # 2.4.1 Add ability to dump LaTeX log file when using batch mode. Batch # mode suppresses most output, often including error messages. To # make sure critical error messages get displayed, show the full log # on failures. # # 2.4.0 Remove "-r 600" from the default PDFTOPS_CONVERTER_FLAGS. The -r flag # is available from the Poppler version of pdftops, but not the Xpdf # version. # # Fix an issue with the flags for the different programs not being # properly separated. # # Fix an issue on windows where the = character is not allowed for # ps2pdf arguments. # # Change default arguments for latex and pdflatex commands. Makes the # output more quiet and prints out the file/line where errors occur. # (Thanks to Nikos Koukis.) # # After a LaTeX build, check the log file for warnings that are # indicative of problems with the build. # # Remove support for latex2html. Instead, use the htlatex program. # This is now part of TeX Live and most other distributions. It also # behaves much more like the other LaTeX programs. Also fixed some # nasty issues with the htlatex arguments. # # 2.3.2 Declare LaTeX input files as sources for targets so that they show # up in IDEs like QtCreator. # # Fix issue where main tex files in subdirectories were creating # invalid targets for building HTML. Just disable the HTML targets in # this case. # # 2.3.1 Support use of magick command instead of convert command for # ImageMagick 7. # # 2.3.0 Add USE_BIBLATEX option to support the biblatex package, which # requires using the program biber as a replacement for bibtex # (thanks to David Tracey). # # 2.2.1 Add STRINGS property to LATEX_DEFAULT_BUILD to make it easier to # select the default build in the CMake GUI. # # 2.2.0 Add TARGET_NAME option. # # 2.1.1 Support for finding bmp, ppm, and other image files. # # 2.1.0 Fix an error where the pdf target and others were defined multiple # times if UseLATEX.cmake was included multiple times. # # Added INDEX_NAMES option to support multiple indexes in a single # document from the multind package (thanks to Dan Lipsa). # # 2.0.0 First major revision of UseLATEX.cmake updates to more recent features # of CMake and some non-backward compatible changes. # # Changed all function and macro names to lower case. CMake's identifiers # are case insensitive, but the convention moved from all upper case to # all lower case somewhere around the release of CMake 2. (The original # version of UseLATEX.cmake predates that.) # # Remove condition matching in if statements. They are no longer necessary # and are even discouraged (because else clauses get confusing). # # Use "new" features available in CMake such as list and argument parsing. # # Remove some code that has been deprecated for a while. # # Mark variables for compiler and converter executables as advanced to # match the more conventional CMake behavior. # # Changed how default builds are specified and add the ability to force # a particular build. # # Made the base targets (pdf, dvi, etc.) global. add_latex_document # always mangles its target names and these base targets depend on # the targets with mangled names. # # 1.10.5 Fix for Window's convert check (thanks to Martin Baute). # # 1.10.4 Copy font files to binary directory for packages that come with # their own fonts. # # 1.10.3 Check for Windows version of convert being used instead of # ImageMagick's version (thanks to Martin Baute). # # 1.10.2 Use htlatex as a fallback when latex2html is not available (thanks # to Tomasz Grzegurzko). # # 1.10.1 Make convert program mandatory only if actually used (thanks to # Julien Schueller). # # 1.10.0 Added NO_DEFAULT and DEFAULT_PS options. # Fixed issue with cleaning files for LaTeX documents originating in # a subdirectory. # # 1.9.6 Fixed problem with LATEX_SMALL_IMAGES. # Strengthened check to make sure the output directory does not contain # the source files. # # 1.9.5 Add support for image types not directly supported by either latex # or pdflatex. (Thanks to Jorge Gerardo Pena Pastor for SVG support.) # # 1.9.4 Fix issues with filenames containing multiple periods. # # 1.9.3 Hide some variables that are now cached but should not show up in # the ccmake list of variables. # # 1.9.2 Changed MACRO declarations to FUNCTION declarations. The better # FUNCTION scoping will hopefully avoid some common but subtle bugs. # This implicitly increases the minimum CMake version to 4.6 (although # I honestly only test it with the latest 4.8 version). # # Since we are updating the minimum CMake version, I'm going to start # using the builtin LIST commands that are now available. # # Favor using pdftops from the Poppler package to convert from pdf to # eps. It does a much better job than ImageMagick or ghostscript. # # 1.9.1 Fixed typo that caused the LATEX_SMALL_IMAGES option to fail to # activate. # # 1.9.0 Add support for the multibib package (thanks to Antonio LaTorre). # # 1.8.2 Fix corner case when an argument name was also a variable containing # the text of an argument. In this case, the CMake IF was matching # the argument text with the contents of the variable with the same # argument name. # # 1.8.1 Fix problem where ps2pdf was not getting the appropriate arguments. # # 1.8.0 Add support for synctex. # # 1.7.7 Support calling xindy when making glossaries. # # Improved make clean support. # # 1.7.6 Add support for the nomencl package (thanks to Myles English). # # 1.7.5 Fix issue with bibfiles being copied two different ways, which causes # Problems with dependencies (thanks to Edwin van Leeuwen). # # 1.7.4 Added the DEFAULT_SAFEPDF option (thanks to Raymond Wan). # # Added warnings when image directories are not found (and were # probably not given relative to the source directory). # # 1.7.3 Fix some issues with interactions between makeglossaries and bibtex # (thanks to Mark de Wever). # # 1.7.2 Use ps2pdf to convert eps to pdf to get around the problem with # ImageMagick dropping the bounding box (thanks to Lukasz Lis). # # 1.7.1 Fixed some dependency issues. # # 1.7.0 Added DEPENDS options (thanks to Theodore Papadopoulo). # # 1.6.1 Ported the makeglossaries command to CMake and embedded the port # into UseLATEX.cmake. # # 1.6.0 Allow the use of the makeglossaries command. Thanks to Oystein # S. Haaland for the patch. # # 1.5.0 Allow any type of file in the INPUTS lists, not just tex file # (suggested by Eric Noulard). As a consequence, the ability to # specify tex files without the .tex extension is removed. The removed # function is of dubious value anyway. # # When copying input files, skip over any file that exists in the # binary directory but does not exist in the source directory with the # assumption that these files were added by some other mechanism. I # find this useful when creating large documents with multiple # chapters that I want to build separately (for speed) as I work on # them. I use the same boilerplate as the starting point for all # and just copy it with different configurations. This was what the # separate ADD_LATEX_DOCUMENT method was supposed to originally be for. # Since its external use is pretty much deprecated, I removed that # documentation. # # 1.4.1 Copy .sty files along with the other class and package files. # # 1.4.0 Added a MANGLE_TARGET_NAMES option that will mangle the target names. # # Fixed problem with copying bib files that became apparent with # CMake 2.4. # # 1.3.0 Added a LATEX_OUTPUT_PATH variable that allows you or the user to # specify where the built latex documents to go. This is especially # handy if you want to do in-source builds. # # Removed the ADD_LATEX_IMAGES macro and absorbed the functionality # into ADD_LATEX_DOCUMENT. The old interface was always kind of # clunky anyway since you had to specify the image directory in both # places. It also made supporting LATEX_OUTPUT_PATH problematic. # # Added support for jpeg files. # # 1.2.0 Changed the configuration options yet again. Removed the NO_CONFIGURE # Replaced it with a CONFIGURE option that lists input files for which # configure should be run. # # The pdf target no longer depends on the dvi target. This allows you # to build latex documents that require pdflatex. Also added an option # to make the pdf target the default one. # # 1.1.1 Added the NO_CONFIGURE option. The @ character can be used when # specifying table column separators. If two or more are used, then # will incorrectly substitute them. # # 1.1.0 Added ability include multiple bib files. Added ability to do copy # sub-tex files for multipart tex files. # # 1.0.0 If both ps and pdf type images exist, just copy the one that # matches the current render mode. Replaced a bunch of STRING # commands with GET_FILENAME_COMPONENT commands that were made to do # the desired function. # # 0.4.0 First version posted to CMake Wiki. # if(__USE_LATEX_INCLUDED) return() endif() set(__USE_LATEX_INCLUDED TRUE) ############################################################################# # Find the location of myself while originally executing. If you do this # inside of a macro, it will recode where the macro was invoked. ############################################################################# set(LATEX_USE_LATEX_LOCATION ${CMAKE_CURRENT_LIST_FILE} CACHE INTERNAL "Location of UseLATEX.cmake file." FORCE ) ############################################################################# # Generic helper functions ############################################################################# include(CMakeParseArguments) function(latex_list_contains var value) set(input_list ${ARGN}) list(FIND input_list "${value}" index) if(index GREATER -1) set(${var} TRUE PARENT_SCOPE) else() set(${var} PARENT_SCOPE) endif() endfunction(latex_list_contains) # Match the contents of a file to a regular expression. function(latex_file_match variable filename regexp default) # The FILE STRINGS command would be a bit better, but I'm not totally sure # the match will always be to a whole line, and I don't want to break things. file(READ ${filename} file_contents) string(REGEX MATCHALL "${regexp}" match_result ${file_contents} ) if(match_result) set(${variable} "${match_result}" PARENT_SCOPE) else() set(${variable} "${default}" PARENT_SCOPE) endif() endfunction(latex_file_match) # A version of GET_FILENAME_COMPONENT that treats extensions after the last # period rather than the first. To the best of my knowledge, all filenames # typically used by LaTeX, including image files, have small extensions # after the last dot. function(latex_get_filename_component varname filename type) set(result) if("${type}" STREQUAL "NAME_WE") get_filename_component(name ${filename} NAME) string(REGEX REPLACE "\\.[^.]*\$" "" result "${name}") elseif("${type}" STREQUAL "EXT") get_filename_component(name ${filename} NAME) string(REGEX MATCH "\\.[^.]*\$" result "${name}") else() get_filename_component(result ${filename} ${type}) endif() set(${varname} "${result}" PARENT_SCOPE) endfunction(latex_get_filename_component) ############################################################################# # Functions that perform processing during a LaTeX build. ############################################################################# function(latex_execute_latex) if(NOT LATEX_WORKING_DIRECTORY) message(SEND_ERROR "Need to define LATEX_WORKING_DIRECTORY") endif() if(NOT LATEX_FULL_COMMAND) message(SEND_ERROR "Need to define LATEX_FULL_COMMAND") endif() if(NOT LATEX_OUTPUT_FILE) message(SEND_ERROR "Need to define LATEX_OUTPUT_FILE") endif() if(NOT LATEX_LOG_FILE) message(SEND_ERROR "Need to define LATEX_LOG_FILE") endif() set(full_command_original "${LATEX_FULL_COMMAND}") # Chose the native method for parsing command arguments. Newer versions of # CMake allow you to just use NATIVE_COMMAND. if (CMAKE_VERSION VERSION_GREATER 3.8) set(separate_arguments_mode NATIVE_COMMAND) else() if (WIN32) set(separate_arguments_mode WINDOWS_COMMAND) else() set(separate_arguments_mode UNIX_COMMAND) endif() endif() # Preps variables for use in execute_process. # Even though we expect LATEX_WORKING_DIRECTORY to have a single "argument," # we also want to make sure that we strip out any escape characters that can # foul up the WORKING_DIRECTORY argument. separate_arguments(LATEX_FULL_COMMAND UNIX_COMMAND "${LATEX_FULL_COMMAND}") separate_arguments(LATEX_WORKING_DIRECTORY_SEP UNIX_COMMAND "${LATEX_WORKING_DIRECTORY}") execute_process( COMMAND ${LATEX_FULL_COMMAND} WORKING_DIRECTORY "${LATEX_WORKING_DIRECTORY_SEP}" RESULT_VARIABLE execute_result OUTPUT_VARIABLE ignore ERROR_VARIABLE ignore ) if(NOT ${execute_result} EQUAL 0) # LaTeX tends to write a file when a failure happens. Delete that file so # that LaTeX will run again. file(REMOVE "${LATEX_WORKING_DIRECTORY}/${LATEX_OUTPUT_FILE}") message("\n\nLaTeX command failed") message("${full_command_original}") message("Log output:") file(READ "${LATEX_WORKING_DIRECTORY}/${LATEX_LOG_FILE}" log_output) message("${log_output}") message(FATAL_ERROR "Executed LaTeX, but LaTeX returned an error.") endif() endfunction(latex_execute_latex) function(latex_makeglossaries) # This is really a bare bones port of the makeglossaries perl script into # CMake scripting. message("**************************** In makeglossaries") if(NOT LATEX_TARGET) message(SEND_ERROR "Need to define LATEX_TARGET") endif() set(aux_file ${LATEX_TARGET}.aux) if(NOT EXISTS ${aux_file}) message(SEND_ERROR "${aux_file} does not exist. Run latex on your target file.") endif() latex_file_match(newglossary_lines ${aux_file} "@newglossary[ \t]*{([^}]*)}{([^}]*)}{([^}]*)}{([^}]*)}" "@newglossary{main}{glg}{gls}{glo}" ) latex_file_match(istfile_line ${aux_file} "@istfilename[ \t]*{([^}]*)}" "@istfilename{${LATEX_TARGET}.ist}" ) string(REGEX REPLACE "@istfilename[ \t]*{([^}]*)}" "\\1" istfile ${istfile_line} ) string(REGEX MATCH ".*\\.xdy" use_xindy "${istfile}") if(use_xindy) message("*************** Using xindy") if(NOT XINDY_COMPILER) message(SEND_ERROR "Need to define XINDY_COMPILER") endif() else() message("*************** Using makeindex") if(NOT MAKEINDEX_COMPILER) message(SEND_ERROR "Need to define MAKEINDEX_COMPILER") endif() endif() foreach(newglossary ${newglossary_lines}) string(REGEX REPLACE "@newglossary[ \t]*{([^}]*)}{([^}]*)}{([^}]*)}{([^}]*)}" "\\1" glossary_name ${newglossary} ) string(REGEX REPLACE "@newglossary[ \t]*{([^}]*)}{([^}]*)}{([^}]*)}{([^}]*)}" "${LATEX_TARGET}.\\2" glossary_log ${newglossary} ) string(REGEX REPLACE "@newglossary[ \t]*{([^}]*)}{([^}]*)}{([^}]*)}{([^}]*)}" "${LATEX_TARGET}.\\3" glossary_out ${newglossary} ) string(REGEX REPLACE "@newglossary[ \t]*{([^}]*)}{([^}]*)}{([^}]*)}{([^}]*)}" "${LATEX_TARGET}.\\4" glossary_in ${newglossary} ) if(use_xindy) latex_file_match(xdylanguage_line ${aux_file} "@xdylanguage[ \t]*{${glossary_name}}{([^}]*)}" "@xdylanguage{${glossary_name}}{english}" ) string(REGEX REPLACE "@xdylanguage[ \t]*{${glossary_name}}{([^}]*)}" "\\1" language ${xdylanguage_line} ) # What crazy person makes a LaTeX index generator that uses different # identifiers for language than babel (or at least does not support # the old ones)? if(${language} STREQUAL "frenchb") set(language "french") elseif(${language} MATCHES "^n?germanb?$") set(language "german") elseif(${language} STREQUAL "magyar") set(language "hungarian") elseif(${language} STREQUAL "lsorbian") set(language "lower-sorbian") elseif(${language} STREQUAL "norsk") set(language "norwegian") elseif(${language} STREQUAL "portuges") set(language "portuguese") elseif(${language} STREQUAL "russianb") set(language "russian") elseif(${language} STREQUAL "slovene") set(language "slovenian") elseif(${language} STREQUAL "ukraineb") set(language "ukrainian") elseif(${language} STREQUAL "usorbian") set(language "upper-sorbian") endif() if(language) set(language_flags "-L ${language}") else() set(language_flags "") endif() latex_file_match(codepage_line ${aux_file} "@gls@codepage[ \t]*{${glossary_name}}{([^}]*)}" "@gls@codepage{${glossary_name}}{utf}" ) string(REGEX REPLACE "@gls@codepage[ \t]*{${glossary_name}}{([^}]*)}" "\\1" codepage ${codepage_line} ) if(codepage) set(codepage_flags "-C ${codepage}") else() # Ideally, we would check that the language is compatible with the # default codepage, but I'm hoping that distributions will be smart # enough to specify their own codepage. I know, it's asking a lot. set(codepage_flags "") endif() message("${XINDY_COMPILER} ${MAKEGLOSSARIES_COMPILER_ARGS} ${language_flags} ${codepage_flags} -I xindy -M ${glossary_name} -t ${glossary_log} -o ${glossary_out} ${glossary_in}" ) exec_program(${XINDY_COMPILER} ARGS ${MAKEGLOSSARIES_COMPILER_ARGS} ${language_flags} ${codepage_flags} -I xindy -M ${glossary_name} -t ${glossary_log} -o ${glossary_out} ${glossary_in} OUTPUT_VARIABLE xindy_output ) message("${xindy_output}") # So, it is possible (perhaps common?) for aux files to specify a # language and codepage that are incompatible with each other. Check # for that condition, and if it happens run again with the default # codepage. if("${xindy_output}" MATCHES "^Cannot locate xindy module for language (.+) in codepage (.+)\\.$") message("*************** Retrying xindy with default codepage.") exec_program(${XINDY_COMPILER} ARGS ${MAKEGLOSSARIES_COMPILER_ARGS} ${language_flags} -I xindy -M ${glossary_name} -t ${glossary_log} -o ${glossary_out} ${glossary_in} ) endif() else() message("${MAKEINDEX_COMPILER} ${MAKEGLOSSARIES_COMPILER_ARGS} -s ${istfile} -t ${glossary_log} -o ${glossary_out} ${glossary_in}") exec_program(${MAKEINDEX_COMPILER} ARGS ${MAKEGLOSSARIES_COMPILER_ARGS} -s ${istfile} -t ${glossary_log} -o ${glossary_out} ${glossary_in} ) endif() endforeach(newglossary) endfunction(latex_makeglossaries) function(latex_makenomenclature) message("**************************** In makenomenclature") if(NOT LATEX_TARGET) message(SEND_ERROR "Need to define LATEX_TARGET") endif() if(NOT MAKEINDEX_COMPILER) message(SEND_ERROR "Need to define MAKEINDEX_COMPILER") endif() set(nomencl_out ${LATEX_TARGET}.nls) set(nomencl_in ${LATEX_TARGET}.nlo) exec_program(${MAKEINDEX_COMPILER} ARGS ${MAKENOMENCLATURE_COMPILER_ARGS} ${nomencl_in} -s "nomencl.ist" -o ${nomencl_out} ) endfunction(latex_makenomenclature) function(latex_correct_synctex) message("**************************** In correct SyncTeX") if(NOT LATEX_TARGET) message(SEND_ERROR "Need to define LATEX_TARGET") endif() if(NOT GZIP) message(SEND_ERROR "Need to define GZIP") endif() if(NOT LATEX_SOURCE_DIRECTORY) message(SEND_ERROR "Need to define LATEX_SOURCE_DIRECTORY") endif() if(NOT LATEX_BINARY_DIRECTORY) message(SEND_ERROR "Need to define LATEX_BINARY_DIRECTORY") endif() message("${LATEX_BINARY_DIRECTORY}") message("${LATEX_SOURCE_DIRECTORY}") set(synctex_file ${LATEX_BINARY_DIRECTORY}/${LATEX_TARGET}.synctex) set(synctex_file_gz ${synctex_file}.gz) if(EXISTS ${synctex_file_gz}) message("Making backup of synctex file.") configure_file(${synctex_file_gz} ${synctex_file}.bak.gz COPYONLY) message("Uncompressing synctex file.") exec_program(${GZIP} ARGS --decompress ${synctex_file_gz} ) message("Reading synctex file.") file(READ ${synctex_file} synctex_data) message("Replacing output paths with input paths.") foreach(extension tex cls bst clo sty ist fd) # Relative paths string(REGEX REPLACE "(Input:[0-9]+:)([^/\n][^\n]\\.${extension}*)" "\\1${LATEX_SOURCE_DIRECTORY}/\\2" synctex_data "${synctex_data}" ) # Absolute paths string(REGEX REPLACE "(Input:[0-9]+:)${LATEX_BINARY_DIRECTORY}([^\n]*\\.${extension})" "\\1${LATEX_SOURCE_DIRECTORY}\\2" synctex_data "${synctex_data}" ) endforeach(extension) message("Writing synctex file.") file(WRITE ${synctex_file} "${synctex_data}") message("Compressing synctex file.") exec_program(${GZIP} ARGS ${synctex_file} ) else() message(SEND_ERROR "File ${synctex_file_gz} not found. Perhaps synctex is not supported by your LaTeX compiler.") endif() endfunction(latex_correct_synctex) function(latex_check_important_warnings) # Check for biber warnings/errors if that was run set(bib_log_file ${LATEX_TARGET}.blg) if(EXISTS ${bib_log_file}) file(READ ${bib_log_file} bib_log) if(bib_log MATCHES "INFO - This is Biber") message("\nChecking ${bib_log_file} for Biber warnings/errors.") string(REGEX MATCHALL "[A-Z]+ - [^\n]*" biber_messages "${bib_log}") set(found_error) foreach(message ${biber_messages}) if(NOT message MATCHES "^INFO - ") set(found_error TRUE) message("${message}") endif() endforeach(message) if(found_error) latex_get_filename_component(log_file_path ${bib_log_file} ABSOLUTE) message("\nConsult ${log_file_path} for more information on Biber output.") else() message("No known important Biber output found.") endif(found_error) else() # Biber output not in log file message("Skipping biber checks (biber not used)") endif() else() # No bib log file message("Skipping bibliography checks (not run)") endif() set(log_file ${LATEX_TARGET}.log) message("\nChecking ${log_file} for important warnings.") if(NOT LATEX_TARGET) message(SEND_ERROR "Need to define LATEX_TARGET") endif() if(NOT EXISTS ${log_file}) message("Could not find log file: ${log_file}") return() endif() set(found_error) file(READ ${log_file} log) # Check for declared LaTeX warnings string(REGEX MATCHALL "\nLaTeX Warning:[^\n]*" latex_warnings "${log}") if(latex_warnings) set(found_error TRUE) message("\nFound declared LaTeX warnings.") foreach(warning ${latex_warnings}) string(STRIP "${warning}" warning_no_newline) message("${warning_no_newline}") endforeach(warning) endif() # Check for natbib warnings string(REGEX MATCHALL "\nPackage natbib Warning:[^\n]*" natbib_warnings "${log}") if(natbib_warnings) set(found_error TRUE) message("\nFound natbib package warnings.") foreach(warning ${natbib_warnings}) string(STRIP "${warning}" warning_no_newline) message("${warning_no_newline}") endforeach(warning) endif() # Check for overfull string(REGEX MATCHALL "\nOverfull[^\n]*" overfull_warnings "${log}") if(overfull_warnings) set(found_error TRUE) message("\nFound overfull warnings. These are indicative of layout errors.") foreach(warning ${overfull_warnings}) string(STRIP "${warning}" warning_no_newline) message("${warning_no_newline}") endforeach(warning) endif() # Check for invalid characters string(REGEX MATCHALL "\nMissing character:[^\n]*" invalid_character_warnings "${log}") if(invalid_character_warnings) set(found_error TRUE) message("\nFound invalid character warnings. These characters are likely not printed correctly.") foreach(warning ${invalid_character_warnings}) string(STRIP "${warning}" warning_no_newline) message("${warning_no_newline}") endforeach(warning) endif() if(found_error) latex_get_filename_component(log_file_path ${log_file} ABSOLUTE) message("\nConsult ${log_file_path} for more information on LaTeX build.") else() message("No known important warnings found.") endif(found_error) endfunction(latex_check_important_warnings) ############################################################################# # Helper functions for establishing LaTeX build. ############################################################################# function(latex_needit VAR NAME) if(NOT ${VAR}) message(SEND_ERROR "I need the ${NAME} command.") endif() endfunction(latex_needit) function(latex_wantit VAR NAME) if(NOT ${VAR}) message(STATUS "I could not find the ${NAME} command.") endif() endfunction(latex_wantit) function(latex_setup_variables) set(LATEX_OUTPUT_PATH "${LATEX_OUTPUT_PATH}" CACHE PATH "If non empty, specifies the location to place LaTeX output." ) find_package(LATEX) find_program(XINDY_COMPILER NAME xindy PATHS ${MIKTEX_BINARY_PATH} /usr/bin ) find_package(UnixCommands) find_program(PDFTOPS_CONVERTER NAMES pdftops DOC "The pdf to ps converter program from the Poppler package." ) find_program(HTLATEX_COMPILER NAMES htlatex PATHS ${MIKTEX_BINARY_PATH} /usr/bin ) mark_as_advanced( LATEX_COMPILER PDFLATEX_COMPILER BIBTEX_COMPILER BIBER_COMPILER MAKEINDEX_COMPILER XINDY_COMPILER DVIPS_CONVERTER PS2PDF_CONVERTER PDFTOPS_CONVERTER LATEX2HTML_CONVERTER HTLATEX_COMPILER ) latex_needit(LATEX_COMPILER latex) latex_wantit(PDFLATEX_COMPILER pdflatex) latex_wantit(HTLATEX_COMPILER htlatex) latex_needit(BIBTEX_COMPILER bibtex) latex_wantit(BIBER_COMPILER biber) latex_needit(MAKEINDEX_COMPILER makeindex) latex_wantit(DVIPS_CONVERTER dvips) latex_wantit(PS2PDF_CONVERTER ps2pdf) latex_wantit(PDFTOPS_CONVERTER pdftops) set(LATEX_COMPILER_FLAGS "-interaction=batchmode -file-line-error" CACHE STRING "Flags passed to latex.") set(PDFLATEX_COMPILER_FLAGS ${LATEX_COMPILER_FLAGS} CACHE STRING "Flags passed to pdflatex.") set(HTLATEX_COMPILER_TEX4HT_FLAGS "html" CACHE STRING "Options for the tex4ht.sty and *.4ht style files.") set(HTLATEX_COMPILER_TEX4HT_POSTPROCESSOR_FLAGS "" CACHE STRING "Options for the text4ht postprocessor.") set(HTLATEX_COMPILER_T4HT_POSTPROCESSOR_FLAGS "" CACHE STRING "Options for the t4ht postprocessor.") set(HTLATEX_COMPILER_LATEX_FLAGS ${LATEX_COMPILER_FLAGS} CACHE STRING "Flags passed from htlatex to the LaTeX compiler.") set(LATEX_SYNCTEX_FLAGS "-synctex=1" CACHE STRING "latex/pdflatex flags used to create synctex file.") set(BIBTEX_COMPILER_FLAGS "" CACHE STRING "Flags passed to bibtex.") set(BIBER_COMPILER_FLAGS "" CACHE STRING "Flags passed to biber.") set(MAKEINDEX_COMPILER_FLAGS "" CACHE STRING "Flags passed to makeindex.") set(MAKEGLOSSARIES_COMPILER_FLAGS "" CACHE STRING "Flags passed to makeglossaries.") set(MAKENOMENCLATURE_COMPILER_FLAGS "" CACHE STRING "Flags passed to makenomenclature.") set(DVIPS_CONVERTER_FLAGS "-Ppdf -G0 -t letter" CACHE STRING "Flags passed to dvips.") if(NOT WIN32) set(PS2PDF_CONVERTER_FLAGS "-dMaxSubsetPct=100 -dCompatibilityLevel=1.3 -dSubsetFonts=true -dEmbedAllFonts=true -dAutoFilterColorImages=false -dAutoFilterGrayImages=false -dColorImageFilter=/FlateEncode -dGrayImageFilter=/FlateEncode -dMonoImageFilter=/FlateEncode" CACHE STRING "Flags passed to ps2pdf.") else() # Most windows ports of ghostscript utilities use .bat files for ps2pdf # commands. bat scripts interpret "=" as a special character and separate # those arguments. To get around this, the ghostscript utilities also # support using "#" in place of "=". set(PS2PDF_CONVERTER_FLAGS "-dMaxSubsetPct#100 -dCompatibilityLevel#1.3 -dSubsetFonts#true -dEmbedAllFonts#true -dAutoFilterColorImages#false -dAutoFilterGrayImages#false -dColorImageFilter#/FlateEncode -dGrayImageFilter#/FlateEncode -dMonoImageFilter#/FlateEncode" CACHE STRING "Flags passed to ps2pdf.") endif() set(PDFTOPS_CONVERTER_FLAGS "" CACHE STRING "Flags passed to pdftops.") mark_as_advanced( LATEX_COMPILER_FLAGS PDFLATEX_COMPILER_FLAGS HTLATEX_COMPILER_TEX4HT_FLAGS HTLATEX_COMPILER_TEX4HT_POSTPROCESSOR_FLAGS HTLATEX_COMPILER_T4HT_POSTPROCESSOR_FLAGS HTLATEX_COMPILER_LATEX_FLAGS LATEX_SYNCTEX_FLAGS BIBTEX_COMPILER_FLAGS BIBER_COMPILER_FLAGS MAKEINDEX_COMPILER_FLAGS MAKEGLOSSARIES_COMPILER_FLAGS MAKENOMENCLATURE_COMPILER_FLAGS DVIPS_CONVERTER_FLAGS PS2PDF_CONVERTER_FLAGS PDFTOPS_CONVERTER_FLAGS ) # Because it is easier to type, the flags variables are entered as # space-separated strings much like you would in a shell. However, when # using a CMake command to execute a program, it works better to hold the # arguments in semicolon-separated lists (otherwise the whole string will # be interpreted as a single argument). Use the separate_arguments to # convert the space-separated strings to semicolon-separated lists. separate_arguments(LATEX_COMPILER_FLAGS) separate_arguments(PDFLATEX_COMPILER_FLAGS) separate_arguments(HTLATEX_COMPILER_LATEX_FLAGS) separate_arguments(LATEX_SYNCTEX_FLAGS) separate_arguments(BIBTEX_COMPILER_FLAGS) separate_arguments(BIBER_COMPILER_FLAGS) separate_arguments(MAKEINDEX_COMPILER_FLAGS) separate_arguments(MAKEGLOSSARIES_COMPILER_FLAGS) separate_arguments(MAKENOMENCLATURE_COMPILER_FLAGS) separate_arguments(DVIPS_CONVERTER_FLAGS) separate_arguments(PS2PDF_CONVERTER_FLAGS) separate_arguments(PDFTOPS_CONVERTER_FLAGS) # Not quite done. When you call separate_arguments on a cache variable, # the result is written to a local variable. That local variable goes # away when this function returns (which is before any of them are used). # So, copy these variables with local scope to cache variables with # global scope. set(LATEX_COMPILER_ARGS "${LATEX_COMPILER_FLAGS}" CACHE INTERNAL "") set(PDFLATEX_COMPILER_ARGS "${PDFLATEX_COMPILER_FLAGS}" CACHE INTERNAL "") set(HTLATEX_COMPILER_ARGS "${HTLATEX_COMPILER_LATEX_FLAGS}" CACHE INTERNAL "") set(LATEX_SYNCTEX_ARGS "${LATEX_SYNCTEX_FLAGS}" CACHE INTERNAL "") set(BIBTEX_COMPILER_ARGS "${BIBTEX_COMPILER_FLAGS}" CACHE INTERNAL "") set(BIBER_COMPILER_ARGS "${BIBER_COMPILER_FLAGS}" CACHE INTERNAL "") set(MAKEINDEX_COMPILER_ARGS "${MAKEINDEX_COMPILER_FLAGS}" CACHE INTERNAL "") set(MAKEGLOSSARIES_COMPILER_ARGS "${MAKEGLOSSARIES_COMPILER_FLAGS}" CACHE INTERNAL "") set(MAKENOMENCLATURE_COMPILER_ARGS "${MAKENOMENCLATURE_COMPILER_FLAGS}" CACHE INTERNAL "") set(DVIPS_CONVERTER_ARGS "${DVIPS_CONVERTER_FLAGS}" CACHE INTERNAL "") set(PS2PDF_CONVERTER_ARGS "${PS2PDF_CONVERTER_FLAGS}" CACHE INTERNAL "") set(PDFTOPS_CONVERTER_ARGS "${PDFTOPS_CONVERTER_FLAGS}" CACHE INTERNAL "") find_program(IMAGEMAGICK_CONVERT NAMES magick convert DOC "The convert program that comes with ImageMagick (available at http://www.imagemagick.org)." ) mark_as_advanced(IMAGEMAGICK_CONVERT) if(DEFINED ENV{LATEX_DEFAULT_BUILD}) set(default_build $ENV{LATEX_DEFAULT_BUILD}) else() set(default_build pdf) endif() set(LATEX_DEFAULT_BUILD "${default_build}" CACHE STRING "Choose the default type of LaTeX build. Valid options are pdf, dvi, ps, safepdf, html" ) set_property(CACHE LATEX_DEFAULT_BUILD PROPERTY STRINGS pdf dvi ps safepdf html ) option(LATEX_USE_SYNCTEX "If on, have LaTeX generate a synctex file, which WYSIWYG editors can use to correlate output files like dvi and pdf with the lines of LaTeX source that generates them. In addition to adding the LATEX_SYNCTEX_FLAGS to the command line, this option also adds build commands that \"corrects\" the resulting synctex file to point to the original LaTeX files rather than those generated by UseLATEX.cmake." OFF ) option(LATEX_SMALL_IMAGES "If on, the raster images will be converted to 1/6 the original size. This is because papers usually require 600 dpi images whereas most monitors only require at most 96 dpi. Thus, smaller images make smaller files for web distribution and can make it faster to read dvi files." OFF) if(LATEX_SMALL_IMAGES) set(LATEX_RASTER_SCALE 16 PARENT_SCOPE) set(LATEX_OPPOSITE_RASTER_SCALE 100 PARENT_SCOPE) else() set(LATEX_RASTER_SCALE 100 PARENT_SCOPE) set(LATEX_OPPOSITE_RASTER_SCALE 16 PARENT_SCOPE) endif() # Just holds extensions for known image types. They should all be lower case. # For historical reasons, these are all declared in the global scope. set(LATEX_DVI_VECTOR_IMAGE_EXTENSIONS .eps CACHE INTERNAL "") set(LATEX_DVI_RASTER_IMAGE_EXTENSIONS CACHE INTERNAL "") set(LATEX_DVI_IMAGE_EXTENSIONS ${LATEX_DVI_VECTOR_IMAGE_EXTENSIONS} ${LATEX_DVI_RASTER_IMAGE_EXTENSIONS} CACHE INTERNAL "" ) set(LATEX_PDF_VECTOR_IMAGE_EXTENSIONS .pdf CACHE INTERNAL "") set(LATEX_PDF_RASTER_IMAGE_EXTENSIONS .jpeg .jpg .png CACHE INTERNAL "") set(LATEX_PDF_IMAGE_EXTENSIONS ${LATEX_PDF_VECTOR_IMAGE_EXTENSIONS} ${LATEX_PDF_RASTER_IMAGE_EXTENSIONS} CACHE INTERNAL "" ) set(LATEX_OTHER_VECTOR_IMAGE_EXTENSIONS .ai .dot .svg CACHE INTERNAL "") set(LATEX_OTHER_RASTER_IMAGE_EXTENSIONS .bmp .bmp2 .bmp3 .dcm .dcx .ico .gif .pict .ppm .tif .tiff CACHE INTERNAL "") set(LATEX_OTHER_IMAGE_EXTENSIONS ${LATEX_OTHER_VECTOR_IMAGE_EXTENSIONS} ${LATEX_OTHER_RASTER_IMAGE_EXTENSIONS} CACHE INTERNAL "" ) set(LATEX_VECTOR_IMAGE_EXTENSIONS ${LATEX_DVI_VECTOR_IMAGE_EXTENSIONS} ${LATEX_PDF_VECTOR_IMAGE_EXTENSIONS} ${LATEX_OTHER_VECTOR_IMAGE_EXTENSIONS} CACHE INTERNAL "" ) set(LATEX_RASTER_IMAGE_EXTENSIONS ${LATEX_DVI_RASTER_IMAGE_EXTENSIONS} ${LATEX_PDF_RASTER_IMAGE_EXTENSIONS} ${LATEX_OTHER_RASTER_IMAGE_EXTENSIONS} CACHE INTERNAL "" ) set(LATEX_IMAGE_EXTENSIONS ${LATEX_DVI_IMAGE_EXTENSIONS} ${LATEX_PDF_IMAGE_EXTENSIONS} ${LATEX_OTHER_IMAGE_EXTENSIONS} CACHE INTERNAL "" ) endfunction(latex_setup_variables) function(latex_setup_targets) if(NOT TARGET pdf) add_custom_target(pdf) endif() if(NOT TARGET dvi) add_custom_target(dvi) endif() if(NOT TARGET ps) add_custom_target(ps) endif() if(NOT TARGET safepdf) add_custom_target(safepdf) endif() if(NOT TARGET html) add_custom_target(html) endif() if(NOT TARGET auxclean) add_custom_target(auxclean) endif() endfunction(latex_setup_targets) function(latex_get_output_path var) set(latex_output_path) if(LATEX_OUTPUT_PATH) get_filename_component( LATEX_OUTPUT_PATH_FULL "${LATEX_OUTPUT_PATH}" ABSOLUTE ) if("${LATEX_OUTPUT_PATH_FULL}" STREQUAL "${CMAKE_CURRENT_SOURCE_DIR}") message(SEND_ERROR "You cannot set LATEX_OUTPUT_PATH to the same directory that contains LaTeX input files.") else() set(latex_output_path "${LATEX_OUTPUT_PATH_FULL}") endif() else() if("${CMAKE_CURRENT_BINARY_DIR}" STREQUAL "${CMAKE_CURRENT_SOURCE_DIR}") message(SEND_ERROR "LaTeX files must be built out of source or you must set LATEX_OUTPUT_PATH.") else() set(latex_output_path "${CMAKE_CURRENT_BINARY_DIR}") endif() endif() set(${var} ${latex_output_path} PARENT_SCOPE) endfunction(latex_get_output_path) function(latex_add_convert_command output_path input_path output_extension input_extension flags ) set(require_imagemagick_convert TRUE) set(convert_flags "") if(${input_extension} STREQUAL ".eps" AND ${output_extension} STREQUAL ".pdf") # ImageMagick has broken eps to pdf conversion # use ps2pdf instead if(PS2PDF_CONVERTER) set(require_imagemagick_convert FALSE) set(converter ${PS2PDF_CONVERTER}) set(convert_flags -dEPSCrop ${PS2PDF_CONVERTER_ARGS}) else() message(SEND_ERROR "Using postscript files with pdflatex requires ps2pdf for conversion.") endif() elseif(${input_extension} STREQUAL ".pdf" AND ${output_extension} STREQUAL ".eps") # ImageMagick can also be sketchy on pdf to eps conversion. Not good with # color spaces and tends to unnecessarily rasterize. # use pdftops instead if(PDFTOPS_CONVERTER) set(require_imagemagick_convert FALSE) set(converter ${PDFTOPS_CONVERTER}) set(convert_flags -eps ${PDFTOPS_CONVERTER_ARGS}) else() message(STATUS "Consider getting pdftops from Poppler to convert PDF images to EPS images.") set(convert_flags ${flags}) endif() else() set(convert_flags ${flags}) endif() if(require_imagemagick_convert) if(IMAGEMAGICK_CONVERT) string(TOLOWER ${IMAGEMAGICK_CONVERT} IMAGEMAGICK_CONVERT_LOWERCASE) if(${IMAGEMAGICK_CONVERT_LOWERCASE} MATCHES "system32[/\\\\]convert\\.exe") message(SEND_ERROR "IMAGEMAGICK_CONVERT set to Window's convert.exe for changing file systems rather than ImageMagick's convert for changing image formats. Please make sure ImageMagick is installed (available at http://www.imagemagick.org). If you have a recent version of ImageMagick (7.0 or higher), use the magick program instead of convert for IMAGEMAGICK_CONVERT.") else() set(converter ${IMAGEMAGICK_CONVERT}) # ImageMagick requires a special order of arguments where resize and # arguments of that nature must be placed after the input image path. add_custom_command(OUTPUT ${output_path} COMMAND ${converter} ARGS ${input_path} ${convert_flags} ${output_path} DEPENDS ${input_path} ) endif() else() message(SEND_ERROR "Could not find convert program. Please download ImageMagick from http://www.imagemagick.org and install.") endif() else() # Not ImageMagick convert add_custom_command(OUTPUT ${output_path} COMMAND ${converter} ARGS ${convert_flags} ${input_path} ${output_path} DEPENDS ${input_path} ) endif() endfunction(latex_add_convert_command) # Makes custom commands to convert a file to a particular type. function(latex_convert_image output_files_var input_file output_extension convert_flags output_extensions other_files ) set(output_file_list) set(input_dir ${CMAKE_CURRENT_SOURCE_DIR}) latex_get_output_path(output_dir) latex_get_filename_component(extension "${input_file}" EXT) # Check input filename for potential problems with LaTeX. latex_get_filename_component(name "${input_file}" NAME_WE) set(suggested_name "${name}") if(suggested_name MATCHES ".*\\..*") string(REPLACE "." "-" suggested_name "${suggested_name}") endif() if(suggested_name MATCHES ".* .*") string(REPLACE " " "-" suggested_name "${suggested_name}") endif() if(NOT suggested_name STREQUAL name) message(WARNING "Some LaTeX distributions have problems with image file names with multiple extensions or spaces. Consider changing ${name}${extension} to something like ${suggested_name}${extension}.") endif() string(REGEX REPLACE "\\.[^.]*\$" ${output_extension} output_file "${input_file}") latex_list_contains(is_type ${extension} ${output_extensions}) if(is_type) if(convert_flags) latex_add_convert_command(${output_dir}/${output_file} ${input_dir}/${input_file} ${output_extension} ${extension} "${convert_flags}") set(output_file_list ${output_dir}/${output_file}) else() # As a shortcut, we can just copy the file. add_custom_command(OUTPUT ${output_dir}/${input_file} COMMAND ${CMAKE_COMMAND} ARGS -E copy ${input_dir}/${input_file} ${output_dir}/${input_file} DEPENDS ${input_dir}/${input_file} ) set(output_file_list ${output_dir}/${input_file}) endif() else() set(do_convert TRUE) # Check to see if there is another input file of the appropriate type. foreach(valid_extension ${output_extensions}) string(REGEX REPLACE "\\.[^.]*\$" ${output_extension} try_file "${input_file}") latex_list_contains(has_native_file "${try_file}" ${other_files}) if(has_native_file) set(do_convert FALSE) endif() endforeach(valid_extension) # If we still need to convert, do it. if(do_convert) latex_add_convert_command(${output_dir}/${output_file} ${input_dir}/${input_file} ${output_extension} ${extension} "${convert_flags}") set(output_file_list ${output_dir}/${output_file}) endif() endif() set(${output_files_var} ${output_file_list} PARENT_SCOPE) endfunction(latex_convert_image) # Adds custom commands to process the given files for dvi and pdf builds. # Adds the output files to the given variables (does not replace). function(latex_process_images dvi_outputs_var pdf_outputs_var) latex_get_output_path(output_dir) set(dvi_outputs) set(pdf_outputs) foreach(file ${ARGN}) if(EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/${file}") latex_get_filename_component(extension "${file}" EXT) set(convert_flags) # Check to see if we need to downsample the image. latex_list_contains(is_raster "${extension}" ${LATEX_RASTER_IMAGE_EXTENSIONS}) if(LATEX_SMALL_IMAGES) if(is_raster) set(convert_flags -resize ${LATEX_RASTER_SCALE}%) endif() endif() # Make sure the output directory exists. latex_get_filename_component(path "${output_dir}/${file}" PATH) make_directory("${path}") # Do conversions for dvi. if(NOT LATEX_FORCE_PDF) latex_convert_image(output_files "${file}" .eps "${convert_flags}" "${LATEX_DVI_IMAGE_EXTENSIONS}" "${ARGN}") list(APPEND dvi_outputs ${output_files}) endif () # Do conversions for pdf. if(NOT LATEX_FORCE_DVI AND NOT LATEX_FORCE_HTML) if(is_raster) latex_convert_image(output_files "${file}" .png "${convert_flags}" "${LATEX_PDF_IMAGE_EXTENSIONS}" "${ARGN}") list(APPEND pdf_outputs ${output_files}) else() latex_convert_image(output_files "${file}" .pdf "${convert_flags}" "${LATEX_PDF_IMAGE_EXTENSIONS}" "${ARGN}") list(APPEND pdf_outputs ${output_files}) endif() endif() else() message(WARNING "Could not find file ${CMAKE_CURRENT_SOURCE_DIR}/${file}. Are you sure you gave relative paths to IMAGES?") endif() endforeach(file) set(${dvi_outputs_var} ${dvi_outputs} PARENT_SCOPE) set(${pdf_outputs_var} ${pdf_outputs} PARENT_SCOPE) endfunction(latex_process_images) function(latex_copy_globbed_files pattern dest) file(GLOB file_list ${pattern}) foreach(in_file ${file_list}) latex_get_filename_component(out_file ${in_file} NAME) configure_file(${in_file} ${dest}/${out_file} COPYONLY) endforeach(in_file) endfunction(latex_copy_globbed_files) function(latex_copy_input_file file) latex_get_output_path(output_dir) if(EXISTS ${CMAKE_CURRENT_SOURCE_DIR}/${file}) latex_get_filename_component(path ${file} PATH) file(MAKE_DIRECTORY ${output_dir}/${path}) latex_list_contains(use_config ${file} ${LATEX_CONFIGURE}) if(use_config) configure_file(${CMAKE_CURRENT_SOURCE_DIR}/${file} ${output_dir}/${file} @ONLY ) add_custom_command(OUTPUT ${output_dir}/${file} COMMAND ${CMAKE_COMMAND} ARGS ${CMAKE_BINARY_DIR} DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/${file} ) else() add_custom_command(OUTPUT ${output_dir}/${file} COMMAND ${CMAKE_COMMAND} ARGS -E copy ${CMAKE_CURRENT_SOURCE_DIR}/${file} ${output_dir}/${file} DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/${file} ) endif() else() if(EXISTS ${output_dir}/${file}) # Special case: output exists but input does not. Assume that it was # created elsewhere and skip the input file copy. else() message("Could not find input file ${CMAKE_CURRENT_SOURCE_DIR}/${file}") endif() endif() endfunction(latex_copy_input_file) ############################################################################# # Commands provided by the UseLATEX.cmake "package" ############################################################################# function(latex_usage command message) message(SEND_ERROR "${message}\n Usage: ${command}(\n [BIBFILES ...]\n [INPUTS ...]\n [IMAGE_DIRS ...]\n [IMAGES \n [CONFIGURE ...]\n [DEPENDS ...]\n [MULTIBIB_NEWCITES] \n [USE_BIBLATEX] [USE_INDEX] [USE_GLOSSARY] [USE_NOMENCL]\n [FORCE_PDF] [FORCE_DVI] [FORCE_HTML]\n [TARGET_NAME] \n [EXCLUDE_FROM_ALL]\n [EXCLUDE_FROM_DEFAULTS])" ) endfunction(latex_usage command message) # Parses arguments to add_latex_document and ADD_LATEX_TARGETS and sets the # variables LATEX_TARGET, LATEX_IMAGE_DIR, LATEX_BIBFILES, LATEX_DEPENDS, and # LATEX_INPUTS. function(parse_add_latex_arguments command latex_main_input) set(options USE_BIBLATEX USE_INDEX USE_GLOSSARY USE_NOMENCL FORCE_PDF FORCE_DVI FORCE_HTML EXCLUDE_FROM_ALL EXCLUDE_FROM_DEFAULTS # Deprecated options USE_GLOSSARIES DEFAULT_PDF DEFAULT_SAFEPDF DEFAULT_PS NO_DEFAULT MANGLE_TARGET_NAMES ) set(oneValueArgs TARGET_NAME ) set(multiValueArgs BIBFILES MULTIBIB_NEWCITES INPUTS IMAGE_DIRS IMAGES CONFIGURE DEPENDS INDEX_NAMES INCLUDE_DIRECTORIES ) cmake_parse_arguments( LATEX "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN}) # Handle invalid and deprecated arguments if(LATEX_UNPARSED_ARGUMENTS) latex_usage(${command} "Invalid or deprecated arguments: ${LATEX_UNPARSED_ARGUMENTS}") endif() if(LATEX_USE_GLOSSARIES) latex_usage(${command} "USE_GLOSSARIES option removed in version 1.6.1. Use USE_GLOSSARY instead.") endif() if(LATEX_DEFAULT_PDF) latex_usage(${command} "DEFAULT_PDF option removed in version 2.0. Use FORCE_PDF option or LATEX_DEFAULT_BUILD CMake variable instead.") endif() if(LATEX_DEFAULT_SAFEPDF) latex_usage(${command} "DEFAULT_SAFEPDF option removed in version 2.0. Use LATEX_DEFAULT_BUILD CMake variable instead.") endif() if(LATEX_DEFAULT_DVI) latex_usage(${command} "DEFAULT_DVI option removed in version 2.0. Use FORCE_DVI option or LATEX_DEFAULT_BUILD CMake variable instead.") endif() if(LATEX_NO_DEFAULT) latex_usage(${command} "NO_DEFAULT option removed in version 2.0. Use EXCLUDE_FROM_ALL instead.") endif() if(LATEX_MANGLE_TARGET_NAMES) latex_usage(${command} "MANGLE_TARGET_NAMES option removed in version 2.0. All LaTeX targets use mangled names now.") endif() # Capture the first argument, which is the main LaTeX input. latex_get_filename_component(latex_target ${latex_main_input} NAME_WE) set(LATEX_MAIN_INPUT ${latex_main_input} PARENT_SCOPE) set(LATEX_TARGET ${latex_target} PARENT_SCOPE) # Propagate the result variables to the caller foreach(arg_name ${options} ${oneValueArgs} ${multiValueArgs}) set(var_name LATEX_${arg_name}) set(${var_name} ${${var_name}} PARENT_SCOPE) endforeach(arg_name) endfunction(parse_add_latex_arguments) function(add_latex_targets_internal) latex_get_output_path(output_dir) if(LATEX_USE_SYNCTEX) set(synctex_flags ${LATEX_SYNCTEX_ARGS}) else() set(synctex_flags) endif() # The commands to run LaTeX. They are repeated multiple times. set(latex_build_command ${LATEX_COMPILER} ${LATEX_COMPILER_ARGS} ${synctex_flags} ${LATEX_MAIN_INPUT} ) if(LATEX_COMPILER_ARGS MATCHES ".*batchmode.*") # Wrap command in script that dumps the log file on error. This makes sure # errors can be seen. set(latex_build_command ${CMAKE_COMMAND} -D LATEX_BUILD_COMMAND=execute_latex -D LATEX_WORKING_DIRECTORY="${output_dir}" -D LATEX_FULL_COMMAND="${latex_build_command}" -D LATEX_OUTPUT_FILE="${LATEX_TARGET}.dvi" -D LATEX_LOG_FILE="${LATEX_TARGET}.log" -P "${LATEX_USE_LATEX_LOCATION}" ) endif() set(pdflatex_build_command ${PDFLATEX_COMPILER} ${PDFLATEX_COMPILER_ARGS} ${synctex_flags} ${LATEX_MAIN_INPUT} ) if(PDFLATEX_COMPILER_ARGS MATCHES ".*batchmode.*") # Wrap command in script that dumps the log file on error. This makes sure # errors can be seen. set(pdflatex_build_command ${CMAKE_COMMAND} -D LATEX_BUILD_COMMAND=execute_latex -D LATEX_WORKING_DIRECTORY="${output_dir}" -D LATEX_FULL_COMMAND="${pdflatex_build_command}" -D LATEX_OUTPUT_FILE="${LATEX_TARGET}.pdf" -D LATEX_LOG_FILE="${LATEX_TARGET}.log" -P "${LATEX_USE_LATEX_LOCATION}" ) endif() if(LATEX_INCLUDE_DIRECTORIES) # The include directories needs to start with the build directory so # that the copied files can be found. It also needs to end with an # empty directory so that the standard system directories are included # after any specified. set(LATEX_INCLUDE_DIRECTORIES . ${LATEX_INCLUDE_DIRECTORIES} "") # CMake separates items in a list with a semicolon. Lists of # directories on most systems are separated by colons, so we can do a # simple text replace. On Windows, directories are separated by # semicolons, but we replace them with the $ generator # expression to make sure CMake treats it as a single string. if(CMAKE_HOST_WIN32) string(REPLACE ";" "$" TEXINPUTS "${LATEX_INCLUDE_DIRECTORIES}") else() string(REPLACE ";" ":" TEXINPUTS "${LATEX_INCLUDE_DIRECTORIES}") endif() # Set the TEXINPUTS environment variable set(latex_build_command ${CMAKE_COMMAND} -E env TEXINPUTS=${TEXINPUTS} ${latex_build_command}) set(pdflatex_build_command ${CMAKE_COMMAND} -E env TEXINPUTS=${TEXINPUTS} ${pdflatex_build_command}) endif() if(NOT LATEX_TARGET_NAME) # Use the main filename (minus the .tex) as the target name. Remove any # spaces since CMake cannot have spaces in its target names. string(REPLACE " " "_" LATEX_TARGET_NAME ${LATEX_TARGET}) endif() # Some LaTeX commands may need to be modified (or may not work) if the main # tex file is in a subdirectory. Make a flag for that. get_filename_component(LATEX_MAIN_INPUT_SUBDIR ${LATEX_MAIN_INPUT} DIRECTORY) # Set up target names. set(dvi_target ${LATEX_TARGET_NAME}_dvi) set(pdf_target ${LATEX_TARGET_NAME}_pdf) set(ps_target ${LATEX_TARGET_NAME}_ps) set(safepdf_target ${LATEX_TARGET_NAME}_safepdf) set(html_target ${LATEX_TARGET_NAME}_html) set(auxclean_target ${LATEX_TARGET_NAME}_auxclean) # Probably not all of these will be generated, but they could be. # Note that the aux file is added later. set(auxiliary_clean_files ${output_dir}/${LATEX_TARGET}.aux ${output_dir}/${LATEX_TARGET}.bbl ${output_dir}/${LATEX_TARGET}.blg ${output_dir}/${LATEX_TARGET}-blx.bib ${output_dir}/${LATEX_TARGET}.glg ${output_dir}/${LATEX_TARGET}.glo ${output_dir}/${LATEX_TARGET}.gls ${output_dir}/${LATEX_TARGET}.idx ${output_dir}/${LATEX_TARGET}.ilg ${output_dir}/${LATEX_TARGET}.ind ${output_dir}/${LATEX_TARGET}.ist ${output_dir}/${LATEX_TARGET}.log ${output_dir}/${LATEX_TARGET}.out ${output_dir}/${LATEX_TARGET}.toc ${output_dir}/${LATEX_TARGET}.lof ${output_dir}/${LATEX_TARGET}.xdy ${output_dir}/${LATEX_TARGET}.synctex.gz ${output_dir}/${LATEX_TARGET}.synctex.bak.gz ${output_dir}/${LATEX_TARGET}.dvi ${output_dir}/${LATEX_TARGET}.ps ${output_dir}/${LATEX_TARGET}.pdf ) set(image_list ${LATEX_IMAGES}) # For each directory in LATEX_IMAGE_DIRS, glob all the image files and # place them in LATEX_IMAGES. foreach(dir ${LATEX_IMAGE_DIRS}) if(NOT EXISTS ${CMAKE_CURRENT_SOURCE_DIR}/${dir}) message(WARNING "Image directory ${CMAKE_CURRENT_SOURCE_DIR}/${dir} does not exist. Are you sure you gave relative directories to IMAGE_DIRS?") endif() foreach(extension ${LATEX_IMAGE_EXTENSIONS}) file(GLOB files ${CMAKE_CURRENT_SOURCE_DIR}/${dir}/*${extension}) foreach(file ${files}) latex_get_filename_component(filename ${file} NAME) list(APPEND image_list ${dir}/${filename}) endforeach(file) endforeach(extension) endforeach(dir) latex_process_images(dvi_images pdf_images ${image_list}) set(make_dvi_command ${CMAKE_COMMAND} -E chdir ${output_dir} ${latex_build_command}) set(make_pdf_command ${CMAKE_COMMAND} -E chdir ${output_dir} ${pdflatex_build_command} ) set(make_dvi_depends ${LATEX_DEPENDS} ${dvi_images}) set(make_pdf_depends ${LATEX_DEPENDS} ${pdf_images}) foreach(input ${LATEX_MAIN_INPUT} ${LATEX_INPUTS}) list(APPEND make_dvi_depends ${output_dir}/${input}) list(APPEND make_pdf_depends ${output_dir}/${input}) if(${input} MATCHES "\\.tex$") # Dependent .tex files might have their own .aux files created. Make # sure these get cleaned as well. This might replicate the cleaning # of the main .aux file, which is OK. string(REGEX REPLACE "\\.tex$" "" input_we ${input}) list(APPEND auxiliary_clean_files ${output_dir}/${input_we}.aux ${output_dir}/${input}.aux ) endif() endforeach(input) set(all_latex_sources ${LATEX_MAIN_INPUT} ${LATEX_INPUTS} ${image_list}) if(LATEX_USE_GLOSSARY) foreach(dummy 0 1) # Repeat these commands twice. set(make_dvi_command ${make_dvi_command} COMMAND ${CMAKE_COMMAND} -E chdir ${output_dir} ${CMAKE_COMMAND} -D LATEX_BUILD_COMMAND=makeglossaries -D LATEX_TARGET=${LATEX_TARGET} -D MAKEINDEX_COMPILER=${MAKEINDEX_COMPILER} -D XINDY_COMPILER=${XINDY_COMPILER} -D MAKEGLOSSARIES_COMPILER_ARGS=${MAKEGLOSSARIES_COMPILER_ARGS} -P ${LATEX_USE_LATEX_LOCATION} COMMAND ${CMAKE_COMMAND} -E chdir ${output_dir} ${latex_build_command} ) set(make_pdf_command ${make_pdf_command} COMMAND ${CMAKE_COMMAND} -E chdir ${output_dir} ${CMAKE_COMMAND} -D LATEX_BUILD_COMMAND=makeglossaries -D LATEX_TARGET=${LATEX_TARGET} -D MAKEINDEX_COMPILER=${MAKEINDEX_COMPILER} -D XINDY_COMPILER=${XINDY_COMPILER} -D MAKEGLOSSARIES_COMPILER_ARGS=${MAKEGLOSSARIES_COMPILER_ARGS} -P ${LATEX_USE_LATEX_LOCATION} COMMAND ${CMAKE_COMMAND} -E chdir ${output_dir} ${pdflatex_build_command} ) endforeach(dummy) endif() if(LATEX_USE_NOMENCL) foreach(dummy 0 1) # Repeat these commands twice. set(make_dvi_command ${make_dvi_command} COMMAND ${CMAKE_COMMAND} -E chdir ${output_dir} ${CMAKE_COMMAND} -D LATEX_BUILD_COMMAND=makenomenclature -D LATEX_TARGET=${LATEX_TARGET} -D MAKEINDEX_COMPILER=${MAKEINDEX_COMPILER} -D MAKENOMENCLATURE_COMPILER_ARGS=${MAKENOMENCLATURE_COMPILER_ARGS} -P ${LATEX_USE_LATEX_LOCATION} COMMAND ${CMAKE_COMMAND} -E chdir ${output_dir} ${latex_build_command} ) set(make_pdf_command ${make_pdf_command} COMMAND ${CMAKE_COMMAND} -E chdir ${output_dir} ${CMAKE_COMMAND} -D LATEX_BUILD_COMMAND=makenomenclature -D LATEX_TARGET=${LATEX_TARGET} -D MAKEINDEX_COMPILER=${MAKEINDEX_COMPILER} -D MAKENOMENCLATURE_COMPILER_ARGS=${MAKENOMENCLATURE_COMPILER_ARGS} -P ${LATEX_USE_LATEX_LOCATION} COMMAND ${CMAKE_COMMAND} -E chdir ${output_dir} ${pdflatex_build_command} ) endforeach(dummy) endif() if(LATEX_BIBFILES) set(suppress_bib_output) if(LATEX_USE_BIBLATEX) if(NOT BIBER_COMPILER) message(SEND_ERROR "I need the biber command.") endif() set(bib_compiler ${BIBER_COMPILER}) set(bib_compiler_flags ${BIBER_COMPILER_ARGS}) if(NOT BIBER_COMPILER_ARGS MATCHES ".*-q.*") # Only suppress bib output if the quiet option is not specified. set(suppress_bib_output TRUE) endif() if(LATEX_USE_BIBLATEX_CONFIG) list(APPEND auxiliary_clean_files ${output_dir}/biblatex.cfg) list(APPEND make_dvi_depends ${output_dir}/biblatex.cfg) list(APPEND make_pdf_depends ${output_dir}/biblatex.cfg) endif() else() set(bib_compiler ${BIBTEX_COMPILER}) set(bib_compiler_flags ${BIBTEX_COMPILER_ARGS}) endif() if(LATEX_MULTIBIB_NEWCITES) # Suppressed bib output currently not supported for multibib foreach (multibib_auxfile ${LATEX_MULTIBIB_NEWCITES}) latex_get_filename_component(multibib_target ${multibib_auxfile} NAME_WE) set(make_dvi_command ${make_dvi_command} COMMAND ${CMAKE_COMMAND} -E chdir ${output_dir} ${bib_compiler} ${bib_compiler_flags} ${multibib_target}) set(make_pdf_command ${make_pdf_command} COMMAND ${CMAKE_COMMAND} -E chdir ${output_dir} ${bib_compiler} ${bib_compiler_flags} ${multibib_target}) set(auxiliary_clean_files ${auxiliary_clean_files} ${output_dir}/${multibib_target}.aux) endforeach (multibib_auxfile ${LATEX_MULTIBIB_NEWCITES}) else() set(full_bib_command ${bib_compiler} ${bib_compiler_flags} ${LATEX_TARGET}) if(suppress_bib_output) set(full_bib_command ${CMAKE_COMMAND} -D LATEX_BUILD_COMMAND=execute_latex -D LATEX_WORKING_DIRECTORY="${output_dir}" -D LATEX_FULL_COMMAND="${full_bib_command}" -D LATEX_OUTPUT_FILE="${LATEX_TARGET}.bbl" -D LATEX_LOG_FILE="${LATEX_TARGET}.blg" -P "${LATEX_USE_LATEX_LOCATION}" ) endif() set(make_dvi_command ${make_dvi_command} COMMAND ${CMAKE_COMMAND} -E chdir ${output_dir} ${full_bib_command}) set(make_pdf_command ${make_pdf_command} COMMAND ${CMAKE_COMMAND} -E chdir ${output_dir} ${full_bib_command}) endif() foreach (bibfile ${LATEX_BIBFILES}) list(APPEND make_dvi_depends ${output_dir}/${bibfile}) list(APPEND make_pdf_depends ${output_dir}/${bibfile}) endforeach (bibfile ${LATEX_BIBFILES}) else() if(LATEX_MULTIBIB_NEWCITES) message(WARNING "MULTIBIB_NEWCITES has no effect without BIBFILES option.") endif() endif() if(LATEX_USE_INDEX) if(LATEX_INDEX_NAMES) set(INDEX_NAMES ${LATEX_INDEX_NAMES}) else() set(INDEX_NAMES ${LATEX_TARGET}) endif() foreach(idx_name ${INDEX_NAMES}) set(make_dvi_command ${make_dvi_command} COMMAND ${CMAKE_COMMAND} -E chdir ${output_dir} ${latex_build_command} COMMAND ${CMAKE_COMMAND} -E chdir ${output_dir} ${MAKEINDEX_COMPILER} ${MAKEINDEX_COMPILER_ARGS} ${idx_name}.idx) set(make_pdf_command ${make_pdf_command} COMMAND ${CMAKE_COMMAND} -E chdir ${output_dir} ${pdflatex_build_command} COMMAND ${CMAKE_COMMAND} -E chdir ${output_dir} ${MAKEINDEX_COMPILER} ${MAKEINDEX_COMPILER_ARGS} ${idx_name}.idx) set(auxiliary_clean_files ${auxiliary_clean_files} ${output_dir}/${idx_name}.idx ${output_dir}/${idx_name}.ilg ${output_dir}/${idx_name}.ind) endforeach() else() if(LATEX_INDEX_NAMES) message(WARNING "INDEX_NAMES has no effect without USE_INDEX option.") endif() endif() set(make_dvi_command ${make_dvi_command} COMMAND ${CMAKE_COMMAND} -E chdir ${output_dir} ${latex_build_command} COMMAND ${CMAKE_COMMAND} -E chdir ${output_dir} ${latex_build_command}) set(make_pdf_command ${make_pdf_command} COMMAND ${CMAKE_COMMAND} -E chdir ${output_dir} ${pdflatex_build_command} COMMAND ${CMAKE_COMMAND} -E chdir ${output_dir} ${pdflatex_build_command}) # Need to run one more time to remove biblatex' warning # about page breaks that have changed. if(LATEX_USE_BIBLATEX) set(make_dvi_command ${make_dvi_command} COMMAND ${CMAKE_COMMAND} -E chdir ${output_dir} ${latex_build_command}) set(make_pdf_command ${make_pdf_command} COMMAND ${CMAKE_COMMAND} -E chdir ${output_dir} ${pdflatex_build_command}) endif() if(LATEX_USE_SYNCTEX) if(NOT GZIP) message(SEND_ERROR "UseLATEX.cmake: USE_SYNTEX option requires gzip program. Set GZIP variable.") endif() set(make_dvi_command ${make_dvi_command} COMMAND ${CMAKE_COMMAND} -D LATEX_BUILD_COMMAND=correct_synctex -D LATEX_TARGET=${LATEX_TARGET} -D GZIP=${GZIP} -D "LATEX_SOURCE_DIRECTORY=${CMAKE_CURRENT_SOURCE_DIR}" -D "LATEX_BINARY_DIRECTORY=${output_dir}" -P ${LATEX_USE_LATEX_LOCATION} ) set(make_pdf_command ${make_pdf_command} COMMAND ${CMAKE_COMMAND} -D LATEX_BUILD_COMMAND=correct_synctex -D LATEX_TARGET=${LATEX_TARGET} -D GZIP=${GZIP} -D "LATEX_SOURCE_DIRECTORY=${CMAKE_CURRENT_SOURCE_DIR}" -D "LATEX_BINARY_DIRECTORY=${output_dir}" -P ${LATEX_USE_LATEX_LOCATION} ) endif() # Check LaTeX output for important warnings at end of build set(make_dvi_command ${make_dvi_command} COMMAND ${CMAKE_COMMAND} -E chdir ${output_dir} ${CMAKE_COMMAND} -D LATEX_BUILD_COMMAND=check_important_warnings -D LATEX_TARGET=${LATEX_TARGET} -P ${LATEX_USE_LATEX_LOCATION} ) set(make_pdf_command ${make_pdf_command} COMMAND ${CMAKE_COMMAND} -E chdir ${output_dir} ${CMAKE_COMMAND} -D LATEX_BUILD_COMMAND=check_important_warnings -D LATEX_TARGET=${LATEX_TARGET} -P ${LATEX_USE_LATEX_LOCATION} ) # Capture the default build. string(TOLOWER "${LATEX_DEFAULT_BUILD}" default_build) if((NOT LATEX_FORCE_PDF) AND (NOT LATEX_FORCE_DVI) AND (NOT LATEX_FORCE_HTML)) set(no_force TRUE) endif() # Add commands and targets for building pdf outputs (with pdflatex). if(LATEX_FORCE_PDF OR no_force) if(LATEX_FORCE_PDF) set(default_build pdf) endif() if(PDFLATEX_COMPILER) add_custom_command(OUTPUT ${output_dir}/${LATEX_TARGET}.pdf COMMAND ${make_pdf_command} DEPENDS ${make_pdf_depends} ) add_custom_target(${pdf_target} DEPENDS ${output_dir}/${LATEX_TARGET}.pdf SOURCES ${all_latex_sources} ) if(NOT LATEX_EXCLUDE_FROM_DEFAULTS) add_dependencies(pdf ${pdf_target}) endif() endif() endif() # Add commands and targets for building dvi outputs. if(LATEX_FORCE_DVI OR LATEX_FORCE_HTML OR no_force) if(LATEX_FORCE_DVI) if((NOT default_build STREQUAL dvi) AND (NOT default_build STREQUAL ps) AND (NOT default_build STREQUAL safepdf)) set(default_build dvi) endif() endif() add_custom_command(OUTPUT ${output_dir}/${LATEX_TARGET}.dvi COMMAND ${make_dvi_command} DEPENDS ${make_dvi_depends} ) add_custom_target(${dvi_target} DEPENDS ${output_dir}/${LATEX_TARGET}.dvi SOURCES ${all_latex_sources} ) if(NOT LATEX_EXCLUDE_FROM_DEFAULTS) add_dependencies(dvi ${dvi_target}) endif() if(DVIPS_CONVERTER) add_custom_command(OUTPUT ${output_dir}/${LATEX_TARGET}.ps COMMAND ${CMAKE_COMMAND} -E chdir ${output_dir} ${DVIPS_CONVERTER} ${DVIPS_CONVERTER_ARGS} -o ${LATEX_TARGET}.ps ${LATEX_TARGET}.dvi DEPENDS ${output_dir}/${LATEX_TARGET}.dvi) add_custom_target(${ps_target} DEPENDS ${output_dir}/${LATEX_TARGET}.ps SOURCES ${all_latex_sources} ) if(NOT LATEX_EXCLUDE_FROM_DEFAULTS) add_dependencies(ps ${ps_target}) endif() if(PS2PDF_CONVERTER) # Since both the pdf and safepdf targets have the same output, we # cannot properly do the dependencies for both. When selecting safepdf, # simply force a recompile every time. add_custom_target(${safepdf_target} ${CMAKE_COMMAND} -E chdir ${output_dir} ${PS2PDF_CONVERTER} ${PS2PDF_CONVERTER_ARGS} ${LATEX_TARGET}.ps ${LATEX_TARGET}.pdf DEPENDS ${ps_target} ) if(NOT LATEX_EXCLUDE_FROM_DEFAULTS) add_dependencies(safepdf ${safepdf_target}) endif() endif() endif() endif() if(LATEX_FORCE_HTML OR no_force) if (LATEX_FORCE_HTML) set(default_build html) endif() if(HTLATEX_COMPILER AND LATEX_MAIN_INPUT_SUBDIR) message(STATUS "Disabling HTML build for ${LATEX_TARGET_NAME}.tex because the main file is in subdirectory ${LATEX_MAIN_INPUT_SUBDIR}" ) # The code below to run HTML assumes that LATEX_TARGET.tex is in the # current directory. I have tried to specify that LATEX_TARGET.tex is # in a subdirectory. That makes the build targets correct, but the # HTML build still fails (at least for htlatex) because files are not # generated where expected. I am getting around the problem by simply # disabling HTML in this case. If someone really cares, they can fix # this, but make sure it runs on many platforms and build programs. elseif(HTLATEX_COMPILER) # htlatex places the output in a different location set(HTML_OUTPUT "${output_dir}/${LATEX_TARGET}.html") add_custom_command(OUTPUT ${HTML_OUTPUT} COMMAND ${CMAKE_COMMAND} -E chdir ${output_dir} ${HTLATEX_COMPILER} ${LATEX_MAIN_INPUT} "${HTLATEX_COMPILER_TEX4HT_FLAGS}" "${HTLATEX_COMPILER_TEX4HT_POSTPROCESSOR_FLAGS}" "${HTLATEX_COMPILER_T4HT_POSTPROCESSOR_FLAGS}" ${HTLATEX_COMPILER_ARGS} DEPENDS ${output_dir}/${LATEX_TARGET}.tex ${output_dir}/${LATEX_TARGET}.dvi VERBATIM ) add_custom_target(${html_target} DEPENDS ${HTML_OUTPUT} ${dvi_target} SOURCES ${all_latex_sources} ) if(NOT LATEX_EXCLUDE_FROM_DEFAULTS) add_dependencies(html ${html_target}) endif() endif() endif() # Set default targets. if("${default_build}" STREQUAL "pdf") add_custom_target(${LATEX_TARGET_NAME} DEPENDS ${pdf_target}) elseif("${default_build}" STREQUAL "dvi") add_custom_target(${LATEX_TARGET_NAME} DEPENDS ${dvi_target}) elseif("${default_build}" STREQUAL "ps") add_custom_target(${LATEX_TARGET_NAME} DEPENDS ${ps_target}) elseif("${default_build}" STREQUAL "safepdf") add_custom_target(${LATEX_TARGET_NAME} DEPENDS ${safepdf_target}) elseif("${default_build}" STREQUAL "html") add_custom_target(${LATEX_TARGET_NAME} DEPENDS ${html_target}) else() message(SEND_ERROR "LATEX_DEFAULT_BUILD set to an invalid value. See the documentation for that variable.") endif() if(NOT LATEX_EXCLUDE_FROM_ALL) add_custom_target(_${LATEX_TARGET_NAME} ALL DEPENDS ${LATEX_TARGET_NAME}) endif() set_directory_properties(. ADDITIONAL_MAKE_CLEAN_FILES "${auxiliary_clean_files}" ) add_custom_target(${auxclean_target} COMMENT "Cleaning auxiliary LaTeX files." COMMAND ${CMAKE_COMMAND} -E remove ${auxiliary_clean_files} ) add_dependencies(auxclean ${auxclean_target}) endfunction(add_latex_targets_internal) function(add_latex_targets latex_main_input) latex_get_output_path(output_dir) parse_add_latex_arguments(ADD_LATEX_TARGETS ${latex_main_input} ${ARGN}) add_latex_targets_internal() endfunction(add_latex_targets) function(add_latex_document latex_main_input) latex_get_output_path(output_dir) if(output_dir) parse_add_latex_arguments(add_latex_document ${latex_main_input} ${ARGN}) latex_copy_input_file(${LATEX_MAIN_INPUT}) foreach (bib_file ${LATEX_BIBFILES}) latex_copy_input_file(${bib_file}) endforeach (bib_file) if (LATEX_USE_BIBLATEX AND EXISTS ${CMAKE_CURRENT_SOURCE_DIR}/biblatex.cfg) latex_copy_input_file(biblatex.cfg) set(LATEX_USE_BIBLATEX_CONFIG TRUE) endif() foreach (input ${LATEX_INPUTS}) latex_copy_input_file(${input}) endforeach(input) latex_copy_globbed_files(${CMAKE_CURRENT_SOURCE_DIR}/*.cls ${output_dir}) latex_copy_globbed_files(${CMAKE_CURRENT_SOURCE_DIR}/*.bst ${output_dir}) latex_copy_globbed_files(${CMAKE_CURRENT_SOURCE_DIR}/*.clo ${output_dir}) latex_copy_globbed_files(${CMAKE_CURRENT_SOURCE_DIR}/*.sty ${output_dir}) latex_copy_globbed_files(${CMAKE_CURRENT_SOURCE_DIR}/*.ist ${output_dir}) latex_copy_globbed_files(${CMAKE_CURRENT_SOURCE_DIR}/*.fd ${output_dir}) add_latex_targets_internal() endif() endfunction(add_latex_document) ############################################################################# # Actually do stuff ############################################################################# if(LATEX_BUILD_COMMAND) set(command_handled) if("${LATEX_BUILD_COMMAND}" STREQUAL execute_latex) latex_execute_latex() set(command_handled TRUE) endif() if("${LATEX_BUILD_COMMAND}" STREQUAL makeglossaries) latex_makeglossaries() set(command_handled TRUE) endif() if("${LATEX_BUILD_COMMAND}" STREQUAL makenomenclature) latex_makenomenclature() set(command_handled TRUE) endif() if("${LATEX_BUILD_COMMAND}" STREQUAL correct_synctex) latex_correct_synctex() set(command_handled TRUE) endif() if("${LATEX_BUILD_COMMAND}" STREQUAL check_important_warnings) latex_check_important_warnings() set(command_handled TRUE) endif() if(NOT command_handled) message(SEND_ERROR "Unknown command: ${LATEX_BUILD_COMMAND}") endif() else() # Must be part of the actual configure (included from CMakeLists.txt). latex_setup_variables() latex_setup_targets() endif() ================================================ FILE: doc/CMakeLists.txt ================================================ add_latex_document(user-guide.tex INPUTS ref.tex macro.tex BIBFILES ipr.bib TARGET_NAME doc) ================================================ FILE: doc/ChangeLog ================================================ 2019-06-26 Gabriel Dos Reis * user-guide.tex: Tidy. * ref.tex: Likewise. 2019-06-26 Gabriel Dos Reis * Makefile.am: Generate PDF. 2010-03-29 Gabriel Dos Reis * user-guide.tex: Fix various discrepancies. 2005-03-02 Gabriel Dos Reis * Mark for 0.34 2005-02-18 Gabriel Dos Reis * user-guide.tex (subsection{Pointer to member type}): New. (subsection{Non-static data members}): Move from user-define type section to variable section. (subsection{Selection statement}): Give examples. (section{Parameterized declarations}): Add more explanations. 2005-02-17 Gabriel Dos Reis * user-guide.tex (subsection{Expression as type}): Propagate renaming of Expr_as_type to As_type. (subsubsection{Members of user-defined types}): Propagate renaming of heterogeneous_region to Region. (section{Function declarations}): Rework. 2005-02-14 Gabriel Dos Reis * user-guide.tex (subsection{Primary named mappings}): Document construction of primary template declaration. 2005-02-12 Gabriel Dos Reis * user-guide.tex (subsection{Compound statement}): Fix thinko. 2005-02-10 Gabriel Dos Reis * user-guide.tex (subsection{User-defined types}): Document construction of user-defined types. 2005-02-06 Gabriel Dos Reis * Mark for 0.33 * user-guide.tex: Add more documentations. (\section{Generalities}): New. (\section{Statements}): Rework. (\section{Function declaration}): Rework. (\section{Declarations}): Remove. * ref.tex: Remove bogus descriptions. 2005-02-03 Gabriel Dos Reis * ref.tex: Rewrite introductory section. Document region and such. Remove needless math obfuscation. * macro.tex (\cxxOpt): New command. * ipr.bib: Update referneces. 2005-02-01 Gabriel Dos Reis * Mark for 0.31. 2005-01-31 Gabriel Dos Reis * Mark for 0.30. 2005-01-27 Gabriel Dos Reis * Mark for 0.29. 2005-01-10 Gabriel Dos Reis * Makefile.am: New. * Makefile.in: Likewise. 2005-01-10 Gabriel Dos Reis * Mark for 0.28. 2005-01-09 Gabriel Dos Reis * Mark for 0.27. 2004-10-28 Gabriel Dos Reis * Mark for 0.26. 2004-09-23 Gabriel Dos Reis * Mark for 0.25 * user-guide.tex: Fix typos. Document new nodes Ctor_name and Dtor_name. 2004-09-20 Gabriel Dos Reis * Mark for 0.24. 2004-09-20 Gabriel Dos Reis * user-guide.tex: Add more documentation on declaration construction. 2004-09-13 Gabriel Dos Reis * ref.tex (subsection{Annotations}): Update diagram. Add more description. (subsubsection{\texttt{Identifier}}): Update description. Add diagram. (subsubsection{\texttt{Operator}}): Likewise. * macro.tex: Add new macro for proper operation in Xy-pic mode. 2004-09-02 Gabriel Dos Reis * user-guide.tex (section{Literals}): Clarify creation of literals. 2004-08-31 Gabriel Dos Reis * Mark for 0.23. 2004-08-30 Gabriel Dos Reis * user-guide.tex: Document more type nodes. 2004-08-27 Gabriel Dos Reis * user-guide.tex (section{Names}): Add documentations for the various name nodes. 2004-08-25 Gabriel Dos Reis * Mark for 0.22. 2004-08-15 Gabriel Dos Reis * Mark for 0.21. 2004-08-15 Gabriel Dos Reis * ipr.bib: New file. * user-guide.tex: Use it. Document more nodes. * ref.tex: Use ipr.bib. Update out-dated class definitions. 2004-08-14 Gabriel Dos Reis * macro.tex: New file. * user-guide.tex: Use it. * ref.tex: Likewise. 2004-08-10 Gabriel Dos Reis * Mark for 0.20. 2004-08-10 Gabriel Dos Reis * user-guide.tex: Document statements. 2004-08-03 Gabriel Dos Reis * Mark for 0.19. 2004-08-03 Gabriel Dos Reis * user-guide.tex: New file. 2004-07-16 Gabriel Dos Reis * Mark for 0.18 2004-07-15 Gabriel Dos Reis * Mark for 0.17 2004-07-15 Gabriel Dos Reis * user-guide.tex: New file. 2004-07-08 Gabriel Dos Reis * Mark for 0.16. 2004-07-05 Gabriel Dos Reis * Mark for 0.15. 2004-07-04 Gabriel Dos Reis * Mark for 0.14. 2004-07-02 Gabriel Dos Reis * Mark for 0.13. 2004-07-01 Gabriel Dos Reis * Mark for 0.12. 2004-06-29 Gabriel Dos Reis * Mark for 0.11. 2004-06-12 Gabriel Dos Reis * Mark for 0.10. 2004-05-21 Gabriel Dos Reis * ref.tex: Expand on declarations, scopes documentation. Document the notion of type. 2004-05-17 Gabriel Dos Reis * Mark for 0.08. 2004-05-13 Gabriel Dos Reis * Mark for 0.07. 2004-05-13 Gabriel Dos Reis * ref.tex: Document more interface nodes. 2004-05-05 Gabriel Dos Reis * Mark for 0.06. 2004-05-01 Gabriel Dos Reis * Mark for 0.05. ================================================ FILE: doc/ipr.bib ================================================ %% Books @Book{mac-lane01:categories, author = {Mac~Lane, S.}, title = {{Categories} {for} {the} {Working} {Mathematicians}}, publisher = {Springer}, year = 2001, edition = {2nd} } @Book{gamma, author = {Gamma, E. and al.}, title = {{Design Patterns}}, publisher = {Addison-Wesley}, year = 1994 } %% Manuals @Manual{dosreis04:ipr-user-guide, title = {{IPR} {User} {Guide}}, author = {Dos~Reis, G.}, year = 2004 } @Manual{dosreis04:ipr-ref, title = {{Internal Program Representation for \ThePivot{}}}, author = {Dos~Reis, G. and Stroustrup, B.}, year = 2004 } @Manual{gdr-bs05:cxx-rep, title = {{Representing C++ Directly, Efficiently and Completely}}, author = {Dos~Reis, G. and Stroustrup, B.}, year = 2005 } @Manual{stroustrup02:xti, title = {{XTI: A simple, complete C++ Extended Type Information Library}}, author = {Stroustrup, B.}, year = 2002 } @Manual{iso03, title = {{International Standard ISO/IEC 14882. Programming Languages --- C++}}, organization = {ISO}, edition = {2nd}, year = 2003 } @Manual{iso99, title = {{International Standard ISO/IEC 9899. Programming Languages --- C}}, organization = {ISO}, edition = {2nd}, year = 1999 } @Manual{iso98, title = {{International Standard ISO/IEC 14882. Programming Languages --- C++}}, organization = {ISO}, year = 1998 } ================================================ FILE: doc/macro.tex ================================================ \newcommand{\of}[1]{\left(#1\right)} \newcommand{\ThePivot}{\textit{The~Pivot}} \newcommand{\cxxRule}[1]{{\rmfamily\itshape #1}} \newcommand{\cxxOpt}[1]{{#1}$_{\mathit{opt}}$} \newcommand{\cxxOptRule}[1]{\cxxRule{#1}$_{\mathit{opt}}$} \newcommand{\NodeClass}[1]{\texttt{#1}} \newcommand{\bslash}{$\backslash$} \newcommand{\ie}{\emph{i.e.}} \newcommand{\eg}{e.g.} \newcommand{\secref}[1]{$\S$\ref{#1}} \newcommand{\const}{\texttt{const}} \newcommand{\cat}[1]{\ensuremath{\mathfrak{#1}}} \newcommand{\code}[1]{{\ttfamily #1}} \newcommand{\rel}[1]{{\fontfamily{pzc}\selectfont #1}} \newcommand{\xyrel}[1]{\hbox{\rel{#1}}} \newcommand{\xylabel}[1]{\hbox{{\scriptsize\textit{#1}}}} \newcommand{\xyiprop}[1]{\hbox{\iprop{#1}}} \newcommand{\xycst}[1]{\hbox{{\scriptsize\cst{#1}}}} \newcommand{\xycode}[1]{\hbox{\scriptsize\code{#1}}} \newcommand{\iprop}[1]{\textit{#1}} \newcommand{\expr}[1]{{\ttfamily\slshape #1}} \newcommand{\ptr}[1]{#1\textasteriskcentered} \newcommand{\cst}[1]{\textsf{#1}} \newcommand{\Class}[1]{\texttt{#1}} \newenvironment{Program}{\begin{alltt}\footnotesize% }{\end{alltt}} \newenvironment{Grammar}{\begin{alltt}\footnotesize% }{\end{alltt}} \newenvironment{Output}{\begin{alltt}\footnotesize% }{\end{alltt}} \newenvironment{note}{\small\itshape [Note: }{---end note]} %%% Local Variables: %%% mode: latex %%% End: ================================================ FILE: doc/ref.tex ================================================ \documentclass[letter,11pt]{article} \usepackage[latin1]{inputenc} \usepackage[T1]{fontenc} \usepackage{mathrsfs} %\usepackage{lucidabr} %\usepackage{yhmath} %\usepackage{palatino} \usepackage{amsmath,amssymb} %\usepackage{graphics, psfig} \usepackage[all]{xy} \usepackage{alltt} \input{macro} \title{Internal Program Representation for \ThePivot} \author{G.~Dos~Reis \and B.~Stroustrup} \date{} \begin{document} \maketitle \section{Introduction} \label{sec:intro} \ThePivot{} framework is a general infrastructure for transformation and static analysis of programs written in C++ programming language. This document serves as a refenrece manual for the internal program represenation use in the \ThePivot{} framework. A companion document~\cite{dosreis04:ipr-user-guide} provides a user point of view. %% \begin{alltt}\small %% \begin{note} %% Include examples on the max function. %% \end{note} %% \end{alltt} This document is structured as follows. The next subsections introduce general notions of the language used to represent C++ entities. %% The second part %% \secref{sec:intro:organization} explains how the IPR library is organized. Then \secref{sec:interface-classes} go on describing the interface of the various categories of nodes used in IPR. After that, \secref{sec:impl} discusses the implementations of those interfaces. Finally, \secref{sec:xpr-grammar} gives a formal definition of XPR, the concrete syntax for the external representation of programs in \ThePivot{}. \subsection{The problem, solutions space and design choices} \label{sec:design} See \cite{stroustrup02:xti, gdr-bs05:cxx-rep}. \subsection{General notions} \label{sec:intro:general-notion} A program is internally represented as a graph. The nodes represents various notions, such as types, expresssions, declarations, scopes, names... A (well-formed) program written in C++ is composed of instantiation units. Instantiation units are obtained from translation units after template instantiation requests satisfaction. Consequently, most IPR notions are driven by declarations. \subsubsection{Declaration} \label{sec:intro:general-notion:declaration} A \emph{declaration} is a specification of a \emph{type}, a set of \emph{attributes}, and an optional initialier for a name. The type dictates the major mode of interpretation of the name. The attributes or \emph{declaration specifiers} supply additional interpretations or modifiers the major mode. The initializer is the expression the value of which the name evaluates to, until eventual assignment to another value (if permitted by the type). This notion of declaration covers a wide range of situations. Example: \begin{minipage}[t]{12cm} \begin{minipage}[t]{5cm} \centering{\textit{C++}} \begin{Program} int counter = 0; struct point \{ int x; int y; \}; int next(int x) \{ return x + 1; \} template struct buffer \{ char data[N]; \}; \end{Program} \end{minipage}\hfil \vrule \hfil \begin{minipage}[t]{5cm} \centering{\textit{XPR}} \begin{Program} counter : int = 0; point : class = \{ x : int; y : int; \}; next : (x : int) int = \{ return x + 1; \} buffer : class = \{ data : [N] char; \}; \end{Program} \end{minipage} \end{minipage} In this example, the same program is expressed in both standard C++ and XPR notations. All the declarations follow the same pattern (which is probably more stricking in the XPR notation): a name, a type, and an initializer. In particular, the class body and the function body are the initializers for the class \texttt{point} and \texttt{next}, repsectively. The name \texttt{point} is declared to have type \texttt{class}. In some regions, a declaration can be repeated many times. The first declaration for a name (with a given type) in a given region is called the \emph{master declaration} of that name in that region. \subsubsection{Type} \label{sec:intro:general-notion:type} In IPR, a \emph{type} is conceived of as a collection of values or objects with operations or functions. This is not the most abstract definition of type in circulation, but it is sufficiently general and close to the general aims of the \ThePivot{} framework. A type in IPR is a notion similar to that of category~\cite{mac-lane01:categories}, it is a notion used to describe a collection of objects with given properties. In similar ways, the notion of category can be used to talk about collection of categories, types in IPR are expressions that have types. For example, the class \texttt{ios} of the previous section has type \texttt{class}, just like the enumerator \texttt{ios::dec} has type \texttt{ios::flags} and the enumeration \texttt{ios::flags} has type \texttt{enum}. \subsubsection{Overloading} \label{sec:intro:general-notion:overloading} Most regions can contain different declarations with a given name. Such declarations are either redeclarations or declarations that use different types. A name is said to be \emph{overloaded} in a given region if that region contains at least two declarations specifying different types for that name. This concept of overloading is uniformly applied in IPR. In particular, any declaration can be overloaded, not just function declaration. So the following fragment (in XPR notation) is valid. \begin{Program} pi : double = 3.141592653589793; pi : float = 3.141592654f; \end{Program} That program frgament cannot be written directly in C++, as only functions can be overloaded in Standard C++. However it could be emulated, in some specific cases, as a function object: \begin{Program} struct pi \{ operator double() const \{ return 3.141592653589793; \} operator float() const \{ return 3.141592654f; \} \}; \end{Program} Given a name in some scope, the collection of all declarations for that name is called its \emph{overload set}. \subsubsection{Scope} \label{sec:intro:general-notion:scope} A \emph{scope} is a sequence of declarations. This notion is much more general than that of Standard C++. Obviously, the set of all declarations in a given region is a scope. So is the union of the results of looking a name in a collection of regions. That notion sets an elegant way to account for the base classes in a class definition or the enumerators in an enumeration definition. Consider the following declarations: \begin{center} \begin{minipage}[t]{6cm} \centering{C++} \begin{Program} struct ios \{ enum flags \{ boolapha, dec, fixed, // ... \}; // ... \}; struct istream : virtual ios \{ // ... \}; struct ostream : virtual ios \{ // ... \}; struct stream : istream, ostream \{ // ... \}; \end{Program} \end{minipage}\hfil \vrule \hfil \begin{minipage}[t]{6cm} \centering{XPR} \begin{Program} ios :class \{ flags :enum \{ boolapha, dec, fixed, // ... \}; // ... \}; istream :class (ios public virtual) \{ // ... \}; ostream :class (ios public virtual) \{ // ... \}; stream :class (istream public, ostream public) \{ // ... \}; \end{Program} \end{minipage} \end{center} The sequence of declarations for \texttt{ios}, \texttt{istream}, \texttt{ostream} and \texttt{stream}, appearing at toplevel, forms a scope. So is the sequence of enumerators \texttt{ios::booalpha}, \texttt{ios::dec}, \texttt{ios::fixed}, ... since they are for \emph{emplicitly declared} named constants. Similarly, the sequence of the base classes \texttt{istream} and \texttt{ostream} of class \texttt{stream} is a scope. It is the sequence of \emph{implicitly declared} subjects with the \texttt{public} declaration specifier. Finally, the declaration for classes \texttt{istream} and \texttt{ostream} says that their base scopes consist in a single (implicit) declaration with specifiers \texttt{public} and \texttt{virtual}. %% A scope also supports the operation of lookup by name. The result of that %% operation usually is a set of declarations that can be partitionned into %% subsets of declarations that specify the same type for the looked-up name. %% Those subsets are called \emph{decl-sets}. The collection of the decl-sets is %% known as \emph{overload-set}, discussed in %% \secref{sec:general-notion:overload-set}. %% \subsubsection{Overload set} %% \label{sec:general-notion:overload-set} %% In general, the result of looking up a name in a given scope %% (\secref{sec:intro:general-notion:scope}) is a set of %% declarations, called the \emph{overload set} of that name in that scope. %% %(Note: an overload set is something that grows up as the scope is being filled %% %with declarations). %% Members of an overload set are distinguished by their types %% (\secref{sec:intro:general-notion:type}). So, an overload set supports lookup %% by types, in the same way a scope supports lookup by name. The result %% os subscripting an overload-set by a type is a declaration. %% %is the master declaration. %% \begin{note} Mention master declaration, secondary declaration and %% relationship between them. \end{note} %% \subsubsection{Sequence} %% \label{sec:intro:general-notion:sequence} %% A \emph{sequence} is a map $I\longrightarrow T$ where the domain $I$ is a %% subset of set of integers $\mathds{N}$. Sequences considered in IPR have %% bounded domains. Therefore, when we speak of a sequence it is implicitly %% understood to have a bounded domain. The cardinal of the domain of a sequence %% is its \emph{size}. A sequence $I\stackrel{\Sigma}{\longrightarrow} T$ of %% size $n$ can be, canonically, decomposed into a chain of two maps %% $I\stackrel{\nu}{\longrightarrow} [0,n)\longrightarrow T$ where the map $\nu$, %% a numbering of the domain, is an increasing function. %% \subsubsection{Iterator} %% \label{sec:intro:general-notion:iterator} %% An \emph{iterator} is a pair of a sequence and an integer called %% \emph{position}. An iterator $(s, i)$ is \emph{valid for indirection} if its %% position $i$ is in the range of the numbering domain for $s$, \ie{} if $i$ is %% nonnegative and less than the size of the sequence $s$. The distinguished %% iterators $(s, 0)$ and $(s, \mathit{size}(n))$ are called \emph{first} and %% \emph{one-past-the-end} iterators, respectively. %% \subsection{Unified type nodes} %% \label{sec:into.type.unified} %% IPR represents C++ programs as set of graphs and offers an abstract interfaces %% and non-mutating operations on the nodes (see \secref{sec:interface-classes}). %% Eevry node is constructed out of essential expressions, necessary to set up %% its invariants. For example, an \NodeClass{Identifier} node is constructed %% out of the string that denotes the spelling of the identifier. That string is %% necessary and sufficient to determine whether two identifiers are the same or %% not. %% Consequently, IPR nodes %% \subsubsection{Pros} %% \subsubsection{Cons} \subsection{Organization} \label{sec:intro:organization} The IPR library is organized into three header files: \begin{enumerate} \item The interface header file \texttt{}. This header defines the abstract classes that serve as interface to the implementation classes. \item The implementation header file \texttt{}. Implementations for abstract class in the interface header are found there. Their names are derived from the interface my appending the suffix \texttt{\_impl}. \item The input and ouput header file \texttt{}. This header provides functions for reading in memory IPR nodes externally represented in XPR syntax, and writing out IPR nodes in the XPR syntax. \end{enumerate} \section{Interface Classes} \label{sec:interface-classes} All classes describing the interface of IPR are \emph{immutable} in the sense that they support only \texttt{const} operations. There is no way programs using only the interface classes could alter the nodes. A class of IPR nodes is said \emph{unified} if two nodes of that class have equal values if and only if they have the same identity (as nodes). Some nodes naturally lead themselves to unification in the sense that they are completely determined by their types and the argument list used to create them. Examples of such nodes are those that represent pointer-types, reference-types or identifiers. Other nodes, such as sequences, are first created then ``filled up''. Unification for such nodes involves some degree of structural equality. As stated in the introductory section of this paper, most of the IPR notions are driven by the representation of declarations, with full type information. Declarations involve types and expressions (as initializers). C++ expressions share similarity with C++ types in the sense that most of them are obtained as the result of sub-components compositions through unary, binary or ternary operators. That observation is at the basis of the decision of viewing most C++ entities as \emph{generalized expressions}, or simply expressions when the context is clear. In particular, types, statements and declarations are generalized expressions. \subsection{Structural interfaces} \label{sec:interface:structure} As observed above, there is a high degree of common structures between expressions, types and statements. Those commonality are captured in form of unary, binary or ternary expressions, and rendered as the following interfaceo templates: \begin{Program} // -- Unary<> -- template struct Unary : Cat \{ typedef Operand Arg_type; virtual const Operand& operand() const = 0; \}; // -- Binary<> -- template struct Binary : Cat \{ typedef First Arg1_type; typedef Second Arg2_type; virtual const First& first() const = 0; virtual const Second& second() const = 0; \}; // -- Ternary<> -- template struct Ternary : Cat \{ typedef First Arg1_type; typedef Second Arg2_type; typedef Third Arg3_type; virtual const First& first() const = 0; virtual const Second& second() const = 0; virtual const Third& third() const = 0; \}; \end{Program} The first template parameter \code{Cat} indicates the kind of major categories (e.g. plain nodes, expressions, statements or declarations) the node is a member of. The remaning template parameters indicate the type if sub-components. The actual node interfaces will define forwarding functions (with more meaningful names) to the generic selectors. \subsection{Universal base class for nodes} \label{sec:interface:node} \begin{figure}[htbp] \leavevmode \centering \begin{xy} \xymatrix @W=4pc @H=1pc @R=0pc @*[F-]{% *=<4pc,1pc>[F-]{\xyrel{Node}} & *{} & *{} \\ *=<4pc,1pc>[F-]{\xycst{node\_id}}\\ *=<4pc,1pc>[F-]{\xylabel{accept}}\\ } \end{xy} \caption{The \cat{Node} interface} \label{fig:node.struct} \end{figure} C++ programs are represented in IPR as graphs, the nodes of which denote (generalizations of) C++ constructs. Any notion directly represented in IPR is so as an object of class derived (either directly or indirectly) from class \texttt{Node}. An informal definition of a \texttt{Node} is anything of potential interest while traversing graphs of program fragments (through the Visitor Design Pattern~\cite{gamma}). This class is the root of the IPR class hierarchies. \begin{Program} struct Node \{ const int node_id; Node(); virtual void accept(Visitor&) const = 0; \}; \end{Program} Every node in the IPR representation has a unique identifier (an integer value) assigned to it at its creation. That identifier value is given by the member \code{Node::node\_id}. An alternative would have been to use the node's address as its unique identifier, but using an identifier independent of the address space provides good support for persistency. It also means that we can store nodes in associative containers and use their identifiers as keys, at a relatively efficient and portable fashion. \subsection{Comments in programs} \label{sec:interface:comment} \begin{figure}[htbp] \leavevmode \centering \begin{xy} \xymatrix @W=4pc @H=1pc @R=0pc @*[F-]{% *=<4pc,1pc>[F-]{\xyrel{Comment}} & *{} & *{} \\ *=<4pc,1pc>[F-]{\bullet} \save * {} \ar ^{\xylabel{text}} [rr] \restore & *{} & *=<7pc,1pc>{\txt{\cat{std::string}}} } \end{xy} \caption{The \cat{Comment} interface} \label{fig:comment.struct} \end{figure} A comment in C++ programs (be they written in C- or BCPL-style) are represented as unary node, the operand of which is the character stream that makes the comment: \begin{Program} struct Comment : Unary \{ const std::string& text() const \{ return operand(); \} \}; \end{Program} \subsection{Annotations} \label{sec:interface:annotations} \begin{figure}[htbp] \leavevmode \centering \begin{xy} \xymatrix @W=5pc @H=1pc @R=0pc @*[F-] {% *=<5pc,1pc>[F-]{\xyrel{Annotation}} & *{} & *{} \\ *=<5pc,1pc>[F-]{\bullet} \save * {} \ar ^{\xylabel{name}} [rr] \restore & *{} & *=<7pc,1pc>{\txt{\cat{std::string}}} \\ *=<5pc,1pc>[F-]{\bullet} \save * {} \ar ^{\xylabel{value}} [rr] \restore & *{} & *=<5pc,1pc>{\txt{\cat{Literal}}} } \end{xy} \caption{\cat{Annotation} interface} \label{fig:annotation.struct} \end{figure} The external and internal representations of C++ programs allow for annotations on statements. Annotations mostly represent informations not understood by IPR. They are pairs of identifier and literal. \begin{Program} struct Annotation : Binary \{ const std::string& name() const \{ return first(); \} const Literal& value() const \{ return second(); \} \}; \end{Program} The exact interpretation of an annotation is dependent on the tool that uses the IPR library. Only statements (and therefore declarations) can be directly annotated. \subsection{Regions of program text} \label{sec:interface:region} Declarations appear inside regions of program texts. Regions contribute to determine validity of name use, objects lifetime, and lexical characterizations of program fragments. Regions nest. Consequently, every region but the global has a parent region. \begin{Program} struct Region : Node \{ typedef std::pair Location_span; virtual const Location_span& span() const = 0; virtual const Region& enclosing() const = 0; virtual const Scope& bindings() const = 0; virtual const Expr& owner() const = 0; \}; \end{Program} Regions can be delimitated so that they correspond to lexical extent of function or class definitions. A region has an owner, \ie{} C++ entities they are attached to. Finally, all declarations appearing in a given region form a scope, available through \xyiprop{Region::bindings}. \begin{figure}[htbp] \leavevmode \centering \begin{xy} \xymatrix @W=4pc @H=1.5pc @R=0pc @*[F-] {% *=<4pc,1.5pc>[F-]{\xyrel{Region}} & *{} & *{} \\ *=<4pc,1pc>[F-]{\bullet} \save * {} \ar ^{\xylabel{span}} [rr] \restore & *{} & *=<7pc,1pc>{\txt{\cat{Location\_span}}} \\ *=<4pc,1pc>[F-]{\bullet} \save * {} \ar ^{\xylabel{enclosing}} [rr] \restore & *{} & *=<4pc,1pc>{\txt{\cat{Region}}}\\ *=<4pc,1pc>[F-]{\bullet} \save * {} \ar ^{\xylabel{bindings}} [rr] \restore & *{} & *=<4pc,1pc>{\txt{\cat{Scope}}}\\ *=<4pc,1pc>[F-]{\bullet} \save * {} \ar ^{\xylabel{owner}} [rr] \restore & *{} & *=<4pc,1pc>{\txt{\cat{Expr}}} } \end{xy} \caption{\cat{Region} interface} \label{fig:region.struct} \end{figure} \subsection{Program unit} \label{sec:interface:unit} A unit is the sequence of toplevel declarations in a transltaion or instantiation unit. \begin{Program} struct Unit : Node \{ virtual const Namespace& global_namespace() const = 0; virtual const Type& Void() const = 0; virtual const Type& Bool() const = 0; virtual const Type& Char() const = 0; virtual const Type& Signed_char() const = 0; virtual const Type& Unsigned_char() const = 0; virtual const Type& Wchar_t() const = 0; virtual const Type& Short() const = 0; virtual const Type& Unsigned_short() const = 0; virtual const Type& Int() const = 0; virtual const Type& Unsigned_int() const = 0; virtual const Type& Long() const = 0; virtual const Type& Unsigned_long() const = 0; virtual const Type& Long_long() const = 0; virtual const Type& Unsigned_long_long() const = 0; virtual const Type& Float() const = 0; virtual const Type& Double() const = 0; virtual const Type& Long_double() const = 0; virtual const Expr& null_expr() const = 0; \}; \end{Program} It provides access to C++ fundamental types. \subsection{Generalized expressions} \label{sec:interface:expr} As explained earlier, most C++ notions are represented in IPR as generalized expressions. All IPR expressions are typed. For instance, a declatarion \begin{Program} struct Expr : Node \{ virtual const Type& type() const = 0; \}; \end{Program} obviously has a type (which is the type specified in the declaration). In particular, a template declaration has a type (as opposed to Standard C++). A type in turn has a type, as explained previously. \subsection{Overload sets} \label{sec:interface:overload} The result of looking up the totallity of declarations for a name in a given scope is an overload set. An overload set is further partitioned into subsets of declarations or redeclarations with same type. \begin{Program} struct Overload : Sequence, Expr \{ using Sequence::operator[]; virtual const Sequence& operator[](const Type&) const = 0; \}; \end{Program} An overload set is an expression, therefore has a type. The type of an overload set is a Product type, that accounts for the types of all master declarations. Selecting a particular member of an overload set, based on its type is supported through the subscription-by-type operation. The result of that operation is the master declaration with that type. \subsection{Interface for scopes} \label{sec:interface:scope} There is only one interface class, \texttt{Scope}, that represents the variety of scopes available in a C++ programs (function scope, function prototype scope, local scope, class scope, namespace scope). The following are general operations supported by all scope nodes. \begin{description} \item[\texttt{const Type\& owner(const Scope scope\&)}] Returns a \const{} reference to the \texttt{Type} that defines \texttt{scope}. Any scope is associated with a given type for which it represents the sequence of associated members or values. \item[\texttt{Scope::operator[](const Name\& name) const}] Returns a \const{} reference to an \texttt{Overload} that contains all the master declarations of \texttt{name} (in the calling \texttt{Scope} object). This set has null size if no declaration is found. The name look up considered here is the \emph{lexical} name lookup. \end{description} \texttt{Scope}s are visitable, they have an \texttt{accept()} member function that takes a reference to any vistor whose type derives from \subsection{Naming entities} \subsubsection{\texttt{Identifier}} \label{sec:interface:identifier} An \emph{identifier} in IPR is anything that Standard C++ defines as identifier, although the IPR library does not make any special enforcement. %% \begin{displaymath} %% \xyrel{Identifier}: \cat{Alphanumeric}\longrightarrow\cat{Identifier}. %% \end{displaymath} The string that serves to construct an \cat{Identifier} is given by the operation \iprop{string()}: %% \begin{displaymath} %% \forall s \in\cat{Alphanumeric}\qquad %% \iprop{string}\of{\xyrel{Identifier}\of{s}} = s. %% \end{displaymath} Identifiers are expressions, and do belong to a given category. That category is the value of the operation \iprop{type()}. Since interpretations of identifiers depend on the context of use, the type of an identifier is a \cat{Decltype} whose operand is the identifier in question. \begin{figure}[htbp] \leavevmode \centering \begin{xy} \xymatrix @W=5pc @H=1pc @R=0pc @*[F-] {% {\xyrel{Identifier}} & *{} & {\xyrel{Delctype}} \\ *+<5pc,2pc>[F-]{\bullet} \save * {} \ar ^{\xylabel{type}} [rr] \restore & *{} & *+<5pc,2pc>[F-]{\bullet} \save * {} \ar`r[u] +/r4pc/^{\xylabel{expr}} `[u] +/u2pc/ `[ll][llu] \restore \\ *+<5pc,2pc>[F-]{\bullet} \save * {} \ar ^{\xylabel{string}} [rr] \restore & *{} & *+=<5pc,2pc>[F-:<4pt>]{\txt{\cat{std::string}}} } \end{xy} \caption{\cat{Identifier} interface} \label{fig:identifier.struct} \end{figure} The IPR interface to the category \cat{Identifier} of identifiers is implemented by the class \code{Identifier}: \begin{Program} struct Identifier : Unary \{ const std::string& string() const \{ return operand(); \} \}; \end{Program} \subsubsection{\texttt{Operator}} \label{sec:interface:operator} An IPR \emph{operator name} is any Standard C++ operator name of the form \code{operator @}, where \code{@} is a member of \cat{OperatorName} (see table ???). %% \begin{displaymath} %% \xyrel{Operator}:\cat{OperatorName}\longrightarrow\cat{Operator}. %% \end{displaymath} Similar to an \cat{Identifier}, the string that served to construct an \cat{Operator} is given by the operation \iprop{opname}: %% \begin{displaymath} %% \forall s \in\cat{OperatorName}\qquad %% \iprop{opname}\of{\xyrel{Operator}\of{s}} = s. %% \end{displaymath} Also, similar to an \cat{Identifier}, an \cat{Operator} supports the \iprop{type} operation. \begin{figure}[htbp] \leavevmode \centering \begin{xy} \xymatrix @W=5pc @H=1pc @R=0pc @*[F-] {% {\xyrel{Operator}} & *{} & {\xyrel{Delctype}} \\ *+<5pc,2pc>[F-]{\bullet} \save * {} \ar ^{\xylabel{type}} [rr] \restore & *{} & *+<5pc,2pc>[F-]{\bullet} \save * {} \ar`r[u] +/r4pc/^{\xylabel{expr}} `[u] +/u2pc/ `[ll][llu] \restore \\ *+<5pc,2pc>[F-]{\bullet} \save * {} \ar ^{\xylabel{opname}} [rr] \restore & *{} & *+=<5pc,2pc>[F-:<4pt>]{\txt{\cat{std::string}}} } \end{xy} \caption{\cat{Operator} interface} \label{fig:operator.struct} \end{figure} The value of that operation is a \cat{Decltype} whose operand is the \cat{Operator}. The IPR interface to the operator name category \cat{Operator} is implemented by the class \code{Operator}: \begin{Program} struct Operator : Unary \{ const std::string& opname() const \{ return operand(); \} \}; \end{Program} \subsubsection{\texttt{Conversion}} \label{sec:interface:conversion} A \emph{conversion} name is any name made out of \texttt{operator} and a type. Given %% \begin{displaymath} %% \rel{Conversion}:\cat{Type}\longrightarrow\cat{Name} %% \end{displaymath} the following holds: %% \begin{displaymath} %% \forall t\in\cat{Type}\qquad %% \mathit{target}\of{\rel{Conversion}\of{t}} = t. %% \end{displaymath} The IPR interface to \cat{Conversion} is \begin{Program} struct Conversion : Unary \{ const Type& target() const \{ return operand(); \} \}; \end{Program} \begin{description} \item[\texttt{Conversion::target() const}] The type this conversion function converts to. \end{description} \subsection{Expressions} Nearly everyting is an expression in IPR. An \emph{expression} is understood as the specification of an operator and arguments for a computation. The operator need not be alsways explicitly spelled out. The various categories of expressions are described in detail in subsequent sections. The interface to this notion is given by \begin{Program} struct Expr : Node \{ virtual const Type& type() const = 0; \}; \end{Program} Thus, every expression node supports the \texttt{type()} and \texttt{annotation()} operations: \begin{description} \item[\texttt{Expr::type() const}] The IPR node that represent the type of this expression. \item[\texttt{Expr::annotation() const}] The sequence of annotations made to this expression. \end{description} \subsection{Literals} %% \begin{displaymath} %% \rel{Literal} : \cat{String} \times \cat{Type} \longrightarrow %% \cat{Expr} %% \end{displaymath} %% \begin{align*} %% \forall s\in\cat{String},\, \forall t\in\cat{Type}\qquad %% &\mathit{string}\of{\rel{Literal}\of{s,\, t}} = s\\ %% &\mathit{type}\of{\rel{Literal}\of{s,\, t}} = t %% \end{align*} \begin{Program} struct Literal : Binary \{ const Type& type() const \{ return first(); \} const std::string& string() const \{ return second(); \} \}; \end{Program} \begin{description} \item[\texttt{Literal::string()}] \end{description} \subsection{Types in IPR} \label{sec:interface:types} The variety of C++ types are represented in IPR. The abstract class \texttt{Type} serves as a base class of that hierarchy. A type in IPR is a generalized expression, and therefore derives from the abstract class \texttt{Expr}. \subsubsection{Reference type} %% \begin{gather*} %% \rel{Reference} : \cat{Type} \longrightarrow \cat{Type}\\ %% \forall t\in\cat{Type}\qquad %% \mathit{refers\_to}\of{\rel{Reference}\of{t}} = t %% \end{gather*} \begin{Program} struct Reference : Unary \{ const Type& refers_to() const \{ return operand(); \} \}; \end{Program} \subsubsection{Function type} %% \begin{displaymath} %% \rel{Function} : \cat{Signature} \times \cat{Type} \times %% \cat{Sequence_{Type}} \longrightarrow \cat{Type} %% \end{displaymath} %% \begin{align*} %% \forall s\in \cat{Signature},\, \forall t\in\cat{Type},\, %% \forall e\in\cat{Sequence_{Type}}\qquad %% &\mathit{signature}\of{\rel{Function}\of{s,\, t,\, e}} = s\\ %% &\mathit{target}\of{\rel{Function}\of{s,\, t,\, e}} = t\\ %% &\mathit{throws}\of{\rel{Function}\of{s,\, t,\, e}} = e\\ %% \end{align*} \begin{Program} struct Function : Ternary \{ const Product& signature() const \{ return first(); \} const Type& target() const \{ return second(); \} const Type& throws() const \{ return third(); \} \}; \end{Program} \begin{description} \item[\texttt{Function::signature() const}] \item[\texttt{Function::target() const}] \item[\texttt{Function::throws() const}] \end{description} \subsubsection{Pointer type} %% \begin{gather*} %% \rel{Pointer} : \cat{Type} \longrightarrow \cat{Type}\\ %% \forall t\in \cat{Type}\qquad %% \mathit{points\_to}\of{\rel{Pointer}\of{t}} = t %% \end{gather*} \begin{Program} struct Pointer : Unary \{ const Type& points_to() const \{ return operand(); \} \}; \end{Program} \begin{description} \item[\texttt{Pointer::points\_to() const}] \end{description} \subsubsection{Array type} %% \begin{gather*} %% \rel{Array} : \cat{Object\_type}\times\cat{Expr} %% \longrightarrow \cat{Object\_type}\\ %% \forall t\in\cat{Object\_type},\,\forall b\in\cat{Expr}\\ %% \mathit{element\_type}\of{\rel{Array}\of{t,\, b}} = t\\ %% \mathit{bound}\of{\rel{Array}\of{t,\, b}} = b\\ %% \end{gather*} \begin{Program} struct Array : Binary \{ const Type& element_type() const \{ return first(); \} const Expr& bound() const \{ return second(); \} \}; \end{Program} \begin{description} \item[\texttt{Array::element\_type() const}] \item[\texttt{Array::bound() const}] \end{description} \subsubsection{User-defined types} %% \begin{gather*} %% \rel{Udt} : \cat{String}\times \cat{Scope} \times \cat{Scope} %% \longrightarrow \cat{Type}\\ %% \rel{Class} = %% \rel{Udt}\of{\rel{Class},\, \bullet,\,\bullet}\\ %% \rel{Union} = %% \rel{Udt}\of{\rel{Union},\, \bullet,\,\bullet}\\ %% \rel{Enum} = %% \rel{Udt}\of{\rel{Enum},\, \bullet,\,\bullet}\\ %% \rel{Namespace} = %% \rel{Udt}\of{\rel{Namespace},\, \bullet,\,\bullet}\\ %% \end{gather*} %% \begin{align*} %% \forall b\in\cat{Scope},\,\forall m\in\cat{Scope}\qquad %% &\mathit{bases}\of{\rel{User\_defined\_type}\of{\bullet,\, b, m}} = b\\ %% &\mathit{members}\of{\rel{User\_defined\_type}\of{\bullet,\, b, m}} = m\\ %% \end{align*} \begin{Program} struct Udt : Type \{ virtual const Scope& scope() const = 0; \}; struct Namespace : Udt \{ const Sequence& members() const \{ return scope().members(); \} \}; struct Class : Udt \{ const Sequence& members() const \{ return scope().members(); \} virtual const Sequence& bases() const = 0; virtual const Scope& base_scope() const = 0; \}; struct Union : Udt \{ const Sequence& members() const \{ return scope().members(); \} \}; struct Enum : Udt \{ virtual const Sequence& members() const = 0; \}; \end{Program} \subsection{Unary expressions} \label{sec:interface:unary-expr} \subsubsection{Incrementation} %% \begin{gather*} %% \rel{Pre\_increment}:\cat{Expr}\longrightarrow\cat{Expr}\\ %% \rel{Post\_increment}:\cat{Expr}\longrightarrow\cat{Expr} %% \end{gather*} \subsubsection{Decrementation} %% \begin{gather*} %% \rel{Pre\_decrement}:\cat{Expr}\longrightarrow\cat{Expr}\\ %% \rel{Post\_decrement}:\cat{Expr}\longrightarrow\cat{Expr} %% \end{gather*} \subsubsection{Conversions} %% \begin{gather*} %% \rel{Dynamic\_cast}:\cat{Expr}\times\cat{Type}\longrightarrow\cat{Expr}\\ %% \rel{Static\_cast}:\cat{Expr}\times\cat{Type}\longrightarrow\cat{Expr}\\ %% \rel{Reinterpret\_cast}:\cat{Expr}\times\cat{Type} %% \longrightarrow\cat{Expr}\\ %% \rel{Const\_cast}:\cat{Expr}\times\cat{Type}\longrightarrow\cat{Expr}\\ %% \rel{Cast}:\cat{Expr}\times\cat{Type}\longrightarrow\cat{Expr} %% \end{gather*} \subsubsection{Type identification} A type identification comes in two flavors %% \begin{gather*} %% \rel{Expr\_typeid} : \cat{Expr} \longrightarrow \cat{Expr}\\ %% \rel{Type\_typeid} : \cat{Type} \longrightarrow \cat{Expr} %% \end{gather*} \subsubsection{Size of object representation} %% \begin{gather*} %% \rel{Expr\_sizeof} : \cat{Expr} \longrightarrow \cat{Expr}\\ %% \rel{Type\_sizeof} : \cat{Type} \longrightarrow \cat{Expr} %% \end{gather*} \subsubsection{Dereferencing} %% \begin{displaymath} %% \rel{Deref}: \cat{Expr} \longrightarrow \cat{Expr} %% \end{displaymath} \subsubsection{Taking the address of an object} %% \begin{displaymath} %% \rel{Address} : \cat{Expr} \longrightarrow \cat{Expr} %% \end{displaymath} \subsubsection{Unary plus} %% \begin{displaymath} %% \rel{Unary\_plus} : \cat{Expr} \longrightarrow \cat{Expr} %% \end{displaymath} \subsubsection{Negation} %% \begin{gather*} %% \rel{Negate} : \cat{Expr} \longrightarrow \cat{Expr}\\ %% \rel{Not} : \cat{Expr} \longrightarrow \cat{Expr} %% \end{gather*} \subsubsection{Complement} %% \begin{displaymath} %% \rel{Complement} : \cat{Expr} \longrightarrow \cat{Expr} %% \end{displaymath} \subsubsection{Deletion} %% \begin{gather*} %% \rel{Delete} : \cat{Expr} \longrightarrow \cat{Expr}\\ %% \rel{Array\_delete} : \cat{Expr} \longrightarrow \cat{Expr} %% \end{gather*} \subsection{Binary expressions} \label{sec:interface:binary-expr} \subsubsection{Multiplicative expressions} %% \begin{gather*} %% \rel{Mul} : \cat{Expr} \times \cat{Expr} \longrightarrow \cat{Expr}\\ %% \rel{Div} : \cat{Expr} \times \cat{Expr} \longrightarrow \cat{Expr}\\ %% \rel{Modulo} : \cat{Expr} \times \cat{Expr} \longrightarrow \cat{Expr}\\ %% \end{gather*} \subsubsection{Additive expressions} %% \begin{gather*} %% \rel{Add} : \cat{Expr} \times \cat{Expr} \longrightarrow \cat{Expr}\\ %% \rel{Sub} : \cat{Expr} \times \cat{Expr} \longrightarrow \cat{Expr}\\ %% \end{gather*} \subsubsection{Logical expressions} %% \begin{gather*} %% \rel{Less} : \cat{Expr} \times \cat{Expr} %% \longrightarrow \cat{Expr}\\ %% \rel{Greater} : \cat{Expr} \times \cat{Expr} %% \longrightarrow \cat{Expr}\\ %% \rel{Equal} : \cat{Expr} \times \cat{Expr} %% \longrightarrow \cat{Expr}\\ %% \rel{Less\_equal} : \cat{Expr} \times \cat{Expr} %% \longrightarrow \cat{Expr}\\ %% \rel{Greater\_equal} : \cat{Expr} \times \cat{Expr} %% \longrightarrow \cat{Expr}\\ %% \rel{Not\_equal} : \cat{Expr} \times \cat{Expr} %% \longrightarrow \cat{Expr}\\ %% \rel{And} : \cat{Expr} \times \cat{Expr} %% \longrightarrow \cat{Expr}\\ %% \rel{Or} : \cat{Expr} \times \cat{Expr} %% \longrightarrow \cat{Expr}\\ %% \end{gather*} \subsubsection{Bits arithmetic} %% \begin{gather*} %% \rel{Bit\_and} : \cat{Expr} \times \cat{Expr} %% \longrightarrow \cat{Expr}\\ %% \rel{Bit\_xor} : \cat{Expr} \times \cat{Expr} %% \longrightarrow \cat{Expr}\\ %% \rel{Bit\_or} : \cat{Expr} \times \cat{Expr} %% \longrightarrow \cat{Expr}\\ %% \rel{Shift\_left} : \cat{Expr} \times \cat{Expr} %% \longrightarrow \cat{Expr}\\ %% \rel{Shift\_right} : \cat{Expr} \times \cat{Expr} %% \longrightarrow \cat{Expr} %% \end{gather*} \subsubsection{Member selection} %% \begin{gather*} %% \rel{Dot\_select} : \cat{Expr} \times \cat{Expr} %% \longrightarrow \cat{Expr}\\ %% \rel{Dot\_star} : \cat{Expr} \times \cat{Expr} %% \longrightarrow \cat{Expr}\\ %% \rel{Arrow\_select} : \cat{Expr} \times \cat{Expr} %% \longrightarrow \cat{Expr}\\ %% \rel{Arrow\_star} : \cat{Expr} \times \cat{Expr} %% \longrightarrow \cat{Expr}\\ %% \rel{Scope\_select} : \cat{Expr} \times \cat{Expr} %% \longrightarrow \cat{Expr}\\ %% \end{gather*} \subsubsection{Expression sequencing} %% \begin{displaymath} %% \rel{Comma} : \cat{Expr} \times \cat{Expr} %% \longrightarrow \cat{Expr} %% \end{displaymath} \subsubsection{Assignment} %% \begin{displaymath} %% \rel{Assign} : \cat{Expr} \times \cat{Expr} %% \longrightarrow \cat{Expr} %% \end{displaymath} \subsection{Expressions with variable number of arguments} \label{sec:interface:var-args-expr} \subsubsection{Function call} %% \begin{displaymath} %% \rel{Call} : \cat{Expr} \times \cat{Sequence} %% \longrightarrow \cat{Expr} %% \end{displaymath} \subsubsection{Data construction} %% \begin{displaymath} %% \rel{Object\_construction} : \cat{Type} \times \cat{Sequence} %% \longrightarrow \cat{Expr} %% \end{displaymath} \subsection{Statements} \subsubsection{Labeled statements} %% \begin{displaymath} %% \rel{Labeled\_stmt} : \cat{Expr}\times\cat{Expr} \longrightarrow \cat{Expr} %% \end{displaymath} \subsubsection{Expression statements} \begin{Program} struct Expr_stmt : Stmt \{ virtual const Expr& expr() const = 0; \}; \end{Program} \begin{Program} struct Labeled_stmt : Stmt \{ virtual const Expr& label() const = 0; virtual const Expr& stmt() const = 0; \}; \end{Program} \subsubsection{Blocks} %% \begin{displaymath} %% \rel{Block} : \cat{Expr} \longrightarrow \cat{Expr} %% \end{displaymath} \begin{Program} struct Block : Stmt \{ virtual const Scope& members() const = 0; virtual const Sequence& stmts() const = 0; virtual const Sequence& handlers() const = 0; \}; \end{Program} \subsubsection{Ctor bodies} %% \begin{displaymath} %% \rel{Function\_body} : \cat{Sequence_{Expr}} \times %% \cat{Block} \longrightarrow \cat{Expr} %% \end{displaymath} \begin{Program} struct Ctor_body : Stmt \{ virtual const Expr_list& inits() const = 0; virtual const Block& block() const = 0; \}; \end{Program} \subsubsection{Selection statements} %% \begin{gather*} %% \rel{If\_then} : \cat{Expr} \times \cat{Expr} \longrightarrow \cat{Expr}\\ %% \rel{If\_then\_else} : \cat{Expr} \times \cat{Expr} \times \cat{Expr} %% \longrightarrow \cat{Expr} %% \end{gather*} \begin{Program} struct If_then : Stmt \{ virtual const Expr& condition() const = 0; virtual const Expr& then_stmt() const = 0; \}; struct If_then_else : Stmt \{ virtual const Expr& condition() const = 0; virtual const Expr& then_stmt() const = 0; virtual const Expr& else_stmt() const = 0; \}; \end{Program} %% \begin{displaymath} %% \rel{Switch} : \cat{Expr} \times \cat{Expr} \longrightarrow \cat{Expr} %% \end{displaymath} \begin{Program} struct Switch : Stmt { virtual const Expr& condition() const = 0; virtual const Expr& body() const = 0; }; \end{Program} \subsubsection{Iteration iterations} %% \begin{gather*} %% \rel{While} : \cat{Expr} \times \cat{Expr} \longrightarrow \cat{Expr}\\ %% \rel{Do} : \cat{Expr} \times \cat{Expr} \longrightarrow \cat{Expr}\\ %% \rel{For} : \cat{Expr} \times \cat{Expr} \times \cat{Expr} \times \cat{Expr} %% \longrightarrow \cat{Expr} %% \end{gather*} \begin{Program} struct While : Stmt \{ virtual const Expr& condition() const = 0; virtual const Expr& body() const = 0; \}; struct Do : Stmt \{ virtual const Expr& condition() const = 0; virtual const Expr& body() const = 0; \}; struct For : Stmt \{ virtual const Expr& initializer() const = 0; virtual const Expr& condition() const = 0; virtual const Expr& increment() const = 0; virtual const Expr& body() const = 0; \}; \end{Program} \subsubsection{Jump statements} %% \begin{gather*} %% \rel{Break} : \cat{Expr} \longrightarrow \cat{Expr}\\ %% \rel{Continue} : \cat{Expr} \longrightarrow \cat{Expr}\\ %% \rel{Goto} : \cat{Expr} \longrightarrow \cat{Expr}\\ %% \rel{Return} : \cat{Expr} \longrightarrow \cat{Expr} %% \end{gather*} \begin{Program} struct Break : Stmt \{ virtual const Expr& from() const = 0; \}; struct Continue : Stmt \{ virtual const Expr& iteration() const = 0; \}; struct Goto: Stmt \{ virtual const Expr& target() const = 0; \}; struct Return : Stmt \{ virtual const Expr& value() const = 0; const Type& type() const { return value().type(); } \}; \end{Program} \subsubsection{Exception handlers} \begin{Program} struct Handler : Stmt \{ virtual const Decl& exception() const = 0; virtual const Block& body() const = 0; \}; \end{Program} \subsection{Declarations} \begin{Program} struct Decl : Stmt \{ enum Specifier \{ None = 0, Auto = 1 << 0, Register = 1 << 1, Static = 1 << 2, Extern = 1 << 3, Mutable = 1 << 4, StorageClass = Auto | Register | Static | Extern | Mutable, Inline = 1 << 5, Virtual = 1 << 6, Explicit = 1 << 7, Pure = 1 << 8, FunctionSpecifier = Inline | Virtual | Explicit | Pure, Friend = 1 << 9, Typedef = 1 << 10, Public = 1 << 11, Protected = 1 << 12, Private = 1 << 13, AccessProtection = Public | Protected | Private \}; virtual Specifier specifiers() const = 0; virtual const Name& name() const = 0; virtual const Scope& scope() const = 0; virtual bool has_initializer() const = 0; virtual const Expr& initializer() const = 0; const Sequence& decl_set() const \{ return scope()[name()][type()]; \} const Decl& master() const \{ return *decl_set().begin(); \} \}; \end{Program} A declaration is a statement. It usually introduces a name in a given scope and attributes for its interpretation: (a) a type; (b) a set of specifiers for access or storage; and (b) an optional initializer. \subsubsection{Declaration specifiers} \paragraph{Storage class specifiers} \paragraph{Function specifiers} \paragraph{Access control and friendship} \subsubsection{Variable declarations} \begin{Program} struct Var : Decl \{ \}; \end{Program} \subsubsection{Data member declarations} \begin{Program} struct Field : Decl \{ \}; struct Bit_field : Decl \{ virtual const Expr& size() const = 0; \}; \end{Program} \subsubsection{Parameter declarations} \begin{Program} struct Parameter : Decl \{ virtual int position() const = 0; const Expr& default_value() const { return initializer(); } \}; \end{Program} \subsubsection{Function declarations} \begin{Program} struct Fun_decl : Decl \{ virtual const Type& target() const = 0; virtual const Parameter_list& parameters() const = 0; virtual const Type& throws() const = 0; \}; \end{Program} \paragraph{Constructors} \begin{Program} struct Constructor : Fun_decl \{ const Name& name() const \{ return membership().name(); \} const Type& target() const \{ return membership(); \} virtual const Type& membership() const = 0; \}; \end{Program} \paragraph{Destructors} \begin{Program} struct Destructor : Fun_decl \{ virtual const Type& membership() const = 0; \}; \end{Program} \subsubsection{Alias declarations} \begin{Program} struct Alias : Decl \{ \}; \end{Program} \subsubsection{Base-type declarations} \begin{Program} struct Base_type : Decl \{ const Name& name() const \{ return type().name(); \} \}; \end{Program} \subsubsection{Enumerator declarations} \begin{Program} struct Enumerator : Decl \{ const Type& type() const \{ return membership(); \} virtual const Enum& membership() const = 0; \}; \end{Program} \subsubsection{Type declarations} \begin{Program} struct Type_decl : Decl \{ \}; \end{Program} \subsection{Traversing graphs: Visitors} \begin{Program} struct Visitor \{ virtual void visit(const Node&) = 0; virtual void visit(const Annotation&); virtual void visit(const Expr&) = 0; virtual void visit(const Name&) = 0; virtual void visit(const Identifier&); virtual void visit(const Operator&); virtual void visit(const Conversion&); virtual void visit(const Type_id&); virtual void visit(const Type&) = 0; virtual void visit(const Array&); virtual void visit(const Class&); virtual void visit(const Enum&); virtual void visit(const Expr_as_type&); virtual void visit(const Function&); virtual void visit(const Namespace&); virtual void visit(const Pointer&); virtual void visit(const Product&); virtual void visit(const Reference&); virtual void visit(const Template&); virtual void visit(const Union&); virtual void visit(const Udt&); virtual void visit(const Expr_list&); virtual void visit(const Overload&); virtual void visit(const Scope&); virtual void visit(const Address&); virtual void visit(const Array_delete&); virtual void visit(const Complement&); virtual void visit(const Delete&); virtual void visit(const Deref&); virtual void visit(const Expr_sizeof&); virtual void visit(const Expr_stmt&); virtual void visit(const Expr_typeid&); virtual void visit(const Label&); virtual void visit(const Negate&); virtual void visit(const Not&); virtual void visit(const Post_decrement&); virtual void visit(const Post_increment&); virtual void visit(const Pre_decrement&); virtual void visit(const Pre_increment&); virtual void visit(const Throw&); virtual void visit(const Type_sizeof&); virtual void visit(const Type_typeid&); virtual void visit(const Unary_plus&); virtual void visit(const Add&); virtual void visit(const Add_assign&); virtual void visit(const And&); virtual void visit(const Array_ref&); virtual void visit(const Arrow_select&); virtual void visit(const Arrow_star&); virtual void visit(const Assign&); virtual void visit(const Bitand&); virtual void visit(const Bitand_assign&); virtual void visit(const Bitor&); virtual void visit(const Bitor_assign&); virtual void visit(const Bitxor&); virtual void visit(const Bitxor_assign&); virtual void visit(const C_cast&); virtual void visit(const Call&); virtual void visit(const Comma&); virtual void visit(const Const_cast&); virtual void visit(const Div&); virtual void visit(const Div_assign&); virtual void visit(const Dot_select&); virtual void visit(const Dot_star&); virtual void visit(const Dynamic_cast&); virtual void visit(const Equal&); virtual void visit(const Greater&); virtual void visit(const Greater_equal&); virtual void visit(const Less&); virtual void visit(const Less_equal&); virtual void visit(const Literal&); virtual void visit(const Member_init&); virtual void visit(const Modulo&); virtual void visit(const Modulo_assign&); virtual void visit(const Mul&); virtual void visit(const Mul_assign&); virtual void visit(const Not_equal&); virtual void visit(const Object_construction&); virtual void visit(const Or&); virtual void visit(const Reinterpret_cast&); virtual void visit(const Scope_ref&); virtual void visit(const Shift_left&); virtual void visit(const Shift_left_assign&); virtual void visit(const Shift_right&); virtual void visit(const Shift_right_assign&); virtual void visit(const Specialization&); virtual void visit(const Static_cast&); virtual void visit(const Sub&); virtual void visit(const Sub_assign&); virtual void visit(const Conditional&); virtual void visit(const New&); virtual void visit(const Stmt&) = 0; virtual void visit(const Labeled_stmt&); virtual void visit(const Block&); virtual void visit(const Ctor_body&); virtual void visit(const If_then&); virtual void visit(const If_then_else&); virtual void visit(const Switch&); virtual void visit(const While&); virtual void visit(const Do&); virtual void visit(const For&); virtual void visit(const Break&); virtual void visit(const Continue&); virtual void visit(const Goto&); virtual void visit(const Return&); virtual void visit(const Handler&); virtual void visit(const Decl&) = 0; virtual void visit(const Alias&); virtual void visit(const Base_type&); virtual void visit(const Parameter&); virtual void visit(const Parameter_list&); virtual void visit(const Var&); virtual void visit(const Field&); virtual void visit(const Bit_field&); virtual void visit(const Fun_decl&); virtual void visit(const Constructor&); virtual void visit(const Destructor&); virtual void visit(const Type_decl&); \}; \end{Program} \section{Implementation Classes} \label{sec:impl} Every abstract class in the interface class hierarchy has a corresponding implementation class, named after that interface and with the suffix \texttt{Impl}. Those implementations classes often supports non-\texttt{const} operations too. Every implementation class provide an overriding implementation for the visitor hook \texttt{Node::accept()} that transfer control to the appropriate overriding member function of \texttt{Visitor::visit()} There is only one instance of \texttt{std::string} for any given text string that might be hold in any IPR node (e.g. by names). \subsection{Implementation classes for names} The four kinds of names \textit{identifier}, \textit{operator name}, \textit{conversion-function name} and \textit{template instantiation name} are respectively implemented by \texttt{impl::Identifer}, \texttt{impl::Operator}, \texttt{impl::Conversion} and \texttt{impl::Specialization}. They all derive from instantiations of the parameterized implementation class \texttt{impl::Name<>}. The later is parameterized by the interface \subsubsection{\texttt{impl::Identifier}} \subsubsection{\texttt{impl::Operator}} \subsubsection{\texttt{impl::Conversion}} \section{XPR grammars} \label{sec:xpr-grammar} XPR essentially retains C++'s syntax for expressions. The differences appear mainly in the declaration syntax where XPR aims for a linear notation. \subsection{Lexical conventions} \label{sec:xpr-grammar:lexical-convention} \subsubsection{Literals} XPR retains C~\cite{iso99} and C++~\cite{iso03}'s spelling for literals \begin{Program} \end{Program} \bibliographystyle{amsplain} \bibliography{ipr} \end{document} %%% Local Variables: %%% mode: latex %%% TeX-master: t %%% End: ================================================ FILE: doc/user-guide.tex ================================================ \documentclass[11pt]{article} \usepackage[latin1]{inputenc} \usepackage[T1]{fontenc} \usepackage{mathrsfs} \usepackage{palatino} \usepackage{amsmath,amssymb} \usepackage{graphics} \usepackage[all]{xy} \usepackage{alltt} \input{macro} \renewcommand{\ttdefault}{txtt} \title{IPR User Guide\\{\normalsize (for version 0.45)}} \author{Gabriel Dos~Reis} \begin{document} \maketitle \section{Introduction} \label{sec:intro} This document is a companion to the IPR Reference Manual~\cite{dosreis04:ipr-ref}. It is intended to illustrate uses of the IPR library. The library currently consists of three components: \begin{itemize} \item \emph{the interface}, available through \code{}. This is a collection of interfaces (\ie{} abstract classes) that provide views and non-mutating operations to manipulate IPR nodes; \item \emph{the I/O component}, available through \code{}. This header file declares functions necessary to render IPR nodes in their external, persistent form according to the XPR syntax; \item \emph{the implementation classes}, available through \texttt{}. This is a collection of types that provide implementations for the interface component. They also support mutating operations necessary to build complete IPR nodes. \end{itemize} Programs that use the IPR library usually include \code{} when their only interests are non-mutating manipulations of IPR nodes. They need to include \code{} if they intend to print out IPR nodes in XPR syntax. Finally, they include \code{} if they do create IPR nodes, as opposed to inspecting them. The interface classes reside in the namespace \texttt{ipr}. The implementation classes are found in the sub-namespace \code{ipr::impl}. In general, an interface \code{ipr::Xyz} has a corresponding implementation named \code{ipr::impl::Xyz}. \section{Installation} \label{sec:install} \section{Generalities} \label{sec:generalities} The IPR library provides data structures for representing programs written in ISO Standard C++. Programs are represented as graphs, with distinguished roots called \emph{units}. An IPR unit roughly corresponds to a translation unit in C++. In fact, IPR represents a superset of Standard C++; so it can handle programs using full C++ as well as incorrect or incomplete programs. The notion of unit is directly represented by the interface class \NodeClass{Unit}. An object of that type contains the sequence of top-level declarations in a given translation unit. It also provides access to nodes that represent C++ fundamental types. The general approach is that every C++ entity has a type. In particular C++ types, being expressions in IPR, do have types. For example, the following program prints out some IPR expression nodes and their types. \begin{Program} #include #include #include template void inspect(const E& e) // Print a expression and its type. \{ ipr::Printer pp(std::cout); // Create an XPR pretty printer // tied to std::cout. pp << ipr::xpr_expr(e); // Output e in XPR notation. pp << " has type "; pp << ipr::xpr_expr(e.type()); // Output type of e in XPR notation. std::cout << std::endl; \} int main() \{ using namespace ipr; impl::Unit unit; // Current translation unit // Inspect the representations of some literals. inspect(*unit.make_literal(unit.get_char(), u8"'c'")); inspect(*unit.make_literal(unit.get_int(), u8"1024")); inspect(*unit.make_literal(unit.get_double(), u8"3.141592653589793")); // Inspect the representations of some C++ fundamental types. inspect(unit.get_char()); inspect(unit.get_int()); inspect(unit.get_double()); \} \end{Program} When compiled and linked against the IPR library, it produces the output \begin{Output} 'c' has type char 1024 has type int 3.141592653589793 has type double char has type typename int has type typename double has type typename \end{Output} %% Notions like \code{class}, \code{enum}, \code{union} and \code{namespace} are %% types in IPR: they characterize type expressions such as class-expressions, %% enum-expressions, union-expression and namespace-expressions. A node of type \NodeClass{Unit} also serves to represent the notion of \emph{instantiation unit}\footnote{See clause 2 of \cite{iso98, iso03}}. Recall that typical C++ translation units contain requests for template instantiations. An instantiation unit is a translation unit where all template instantiation requests have been resolved. The next sections will focus on the part of the library typically used for building IPR nodes. It is meant to provide support for tools that create IPR nodes, \eg{} IPR generators from compilers or XPR parsers. All IPR node construction functions return pointers; usually their names start with the prefix \code{make\_}. Type nodes, expression nodes, and statements nodes are created with \NodeClass{impl::Unit} objects. Declarations are created with their enclosing regions. They are generally represented as a triple of name, type and optional initializer. \section{Literals} \label{sec:literals} Lexical units (\eg{} names, literals) of Standard C++ are directly represented in IPR. This section deals with literals and the next with names. ISO Standard C++ defines five sub-categories of literals: \begin{Grammar} \cxxRule{literal:} \cxxRule{integer-literal} \cxxRule{character-literal} \cxxRule{floating-literal} \cxxRule{string-literal} \cxxRule{boolean-literal} \end{Grammar} They are all uniformly represented in IPR as pairs of type and string. There is no restriction on the set of types a literal can be conceived for. In other words, any type node in IPR can serve to create a literal. The semantics of the resulting node is entirely up to the environment that creates and, or interprets such nodes. %% \begin{Program} %% #include %% #include %% #include %% int main() %% \{ %% using namespace ipr; %% impl::Unit unit; %% const Literal* four = unit.make_literal(unit.Int(), u8"4"); %% const Literal* c = unit.make_literal(unit.Char(), u8"'c'"); %% Printer pp(std::cout); %% pp << xpr_expr(*four) %% << '\bslash{n}' %% << xpr_expr(*c); %% std::cout << std::endl; %% \} %% \end{Program} As the example from \secref{sec:generalities} shows, literals are made with the member function \texttt{make\_literal()} of the implementation class \NodeClass{impl::Unit}. It takes two arguments: the type of the literal and the spelling string. There is no mutating operations on literals. The sequence of characters should include back-quotes or double-quotes for character literals or string literals. \section{Names} \label{sec:name} Names are useful for designating data, types or any computational entity of interest. IPR covers the variety of names found in Standard C++. All names are unified in the sense that, two calls to the same name-constructor function, with the same argument list, will yield the same physical node. Consequently, all name nodes are immutable. There are eight sub-categories of names \begin{Grammar} \cxxRule{name:} \cxxRule{identifier} \cxxRule{operator-function-id} \cxxRule{conversion-function-id} \cxxRule{qualified-id} \cxxRule{template-id} \cxxRule{type-id} \cxxRule{constructor-name} \cxxRule{destructor-name} \end{Grammar} accounting for the variety of names in Standard C++. \subsection{Plain identifiers} \label{sec:name.identifier} \begin{Grammar} \cxxRule{identifier:} \cxxRule{nondigit} \cxxRule{identifier} \cxxRule{nondigit} \cxxRule{identifier} \cxxRule{digit} \end{Grammar} An \NodeClass{Identifier} node can be created out of a string that contains any character string: \begin{Program} \expr{unit}.get_identifier(\expr{string}) \end{Program} where \expr{unit} is an instance of \code{impl::Unit} and \expr{string} is the UTF-8 encoded string to make an identifier out of. The returned value of the function \code{make\_identifier} has type \code{const Identifier*}. \subsection{Operator function names} \label{sec:name.operator} \begin{Grammar} \cxxRule{operator-function-id:} operator \cxxRule{operator} \end{Grammar} Any operator-name is represented by IPR node constructed out of a string containing the spelling of that operator: \begin{Program} \expr{unit}.make_operator(\expr{string}); \end{Program} where \expr{unit} is an expression of type \code{impl::Unit} and \expr{string} is a string for the operator-name. The whole expression has type \code{const Operator*}. For usual operators like \texttt{operator+} or \texttt{operator==}, the string is \texttt{"+"} or \texttt{"=="}. For the array forms of the allocating and deallocating functions, the string is \texttt{"new[]"} or \texttt{"delete[]"}. \subsection{Conversion function names} \label{sec:name.conversion} \begin{Grammar} \cxxRule{conversion-function-id:} operator \cxxRule{type-id} \end{Grammar} IPR represents a conversion function name as a node that takes the destination type as argument. Such node is constructed as: \begin{Program} \expr{unit}.make_conversion(\expr{type}) \end{Program} where \expr{unit} is an expression of type \code{impl::Unit} and \expr{type} is an expression of type derived from \code{Type}. The returned value is of type \code{const Conversion*}. \subsection{Qualified names} \label{sec:name.qualified} \begin{Grammar} \cxxRule{qualified-id:} \cxxOpt{::} \cxxRule{nested-name-specifier} \cxxRule{unqualified-name} {::} \cxxRule{name} \end{Grammar} IPR represents qualified names as a \NodeClass{Scope\_ref} that takes two arguments: (a) an expression for the qualifying scope; and (b) the designated name. \begin{Program} \expr{unit}.make_scope_ref(\expr{scope}, \expr{name}) \end{Program} where \expr{unit} is of type \code{impl::Unit}, \expr{scope} --- designating the qualifying part --- is any node of type derived from \code{Expr}, and \expr{name} --- designating the name being qualified --- is also any node of type derived from \code{Expr}. The return value is of type \code{const Scope\_ref*}. \subsection{Template specialization names} \label{sec:name.specialization} \begin{Grammar} \cxxRule{template-id:} \cxxRule{unqualified-name} <\cxxOptRule{template-argument-list}> \end{Grammar} Template specialization names, \NodeClass{Template\_id} in IPR, are made out of two expressions, as: \begin{Program} \expr{unit}.make_template_id(\expr{template}, \expr{arglist}); \end{Program} where the first argument designates the template to specialize and the second argument is an \code{Expr\_list} that denotes the template-argument list. The return value has type \code{const Specialization*}. \subsection{Type names} \label{sec:name.type-id} All types have names. Type-names may be as simple as \texttt{int} or \texttt{double}; but they can also be more complicated expression, composed not just a single identifier, e.g. \texttt{int (*[256])(int)}. Names for such constructed types are represented in IPR by \NodeClass{Type\_id} nodes. They are built as \begin{Program} \expr{unit}.make_type_id(\expr{type}) \end{Program} that takes the to-be-named type as its sole argument. Usually, IPR users should not need to have to create a \NodeClass{Type\_id} nodes by themselves as these get created automatically for compound types that need them. \subsection{Constructor name} \label{sec:name.ctor-name} Standard C++ says that constructors don't have names. However, it also says that some qualified names actually designate constructors. Which is quite confusing. For example, in the program fragment \begin{Program} struct S \{ // ... \}; S x = S(); \end{Program} the declaration of the variable \code{x}, the symbol \code{S} refers to two distinct entity: The first is the class-type and the second is the default constructor (invoked through \code{S()}). Similarly, a class with a template constructor needs ways to refer to partial specializations and explicit instantiations. IPR defines a category of name called \code{Ctor\_name} used to represent constructor names. A node for such type can be built as \begin{Program} \expr{unit}.make_ctor_name(\expr{type}) \end{Program} where \expr{type} is the type whose constructor one wants to build a name for. The resulting expression has type \code{const Ctor\_name*}. \subsection{Destructor name} \label{sec:name.dtor-name} The node class \code{Dtor\_name} represents a destructor name. Say \begin{Program} \expr{unit}.make_dtor_name(\expr{type}) \end{Program} to make a destructor name for \expr{type}. The resulting expression has type \code{const Dtor\_name*}. \section{Types} \label{sec:types} IPR has a unified type system. By that we mean that any two calls to the same type node construction function with the same argument list will yield the same physical node. \subsection{CV-qualified type} \label{sec:type.cv-qualified} Any type (in IPR sense) can be cv-qualified. The set of cv-qualifiers currently supported consists of \texttt{const}, \texttt{volatile} and \texttt{restrict} (C99). They are denoted by the enumerators \texttt{Type::Const}, \texttt{Type::Volatile} and \texttt{Type::Restrict}. They can be bitor-ed. A cv-qualified type is built with the \texttt{impl::Unit} member function \begin{Program} const Type* make_qualified(const Type&, Type::Qualifier); \end{Program} where the first parameter denotes the type to be cv-qualified and the second parameter denotes the set of cv-qualifier to apply. \subsection{Pointer type} \label{sec:type.pointer} A \NodeClass{Pointer} type node can be constructed out of any IPR type node with the \NodeClass{impl::Unit} member function \begin{Program} const Pointer* make_pointer(const Type&); \end{Program} \subsection{Reference type} \label{sec:type.reference} Similar to pointer type nodes, a \NodeClass{Reference} type node can be constructed out of any IPR type node with the \NodeClass{impl::Unit} member function \begin{Program} const Reference* make_reference(const Type&); \end{Program} \subsection{Array type} \label{sec:type.array} An array is made with the \NodeClass{impl::Unit} member function \begin{Program} const Array* make_array(const Type&, const Expr&); \end{Program} where the first argument is the array element type and the second argument specifies the bound. For example, ISO Standard C++ says that the type of the string literal \code{"Hello World!"} is \code{const char[13]}, so its type will be represented in IPR with a node constructed as \begin{Program} // Build a node for the integer literal value `13' const Expr& sz = *unit.make_literal(unit.get_int(),unit.get_string(u8"13")); // create the actual node for `const int[13]' unit.make_array(*unit.make_cv_qualified(unit.get_char()), sz); \end{Program} As a limiting case, an array type with no specified bound, \eg{} \texttt{int[]}, may be constructed along the lines of \begin{Program} unit.make_array(unit.get_int(), unit.null_expr()); \end{Program} where \texttt{unit.null\_expr()} denotes here the absence of bound specification. \subsection{Declared type} \label{sec:type.decltype} IPR has provision to represent the mechanism to query generalized expression types (see the \texttt{auto} and \texttt{decltype} proposals). Such an explicit request for inferred type is represented by a \NodeClass{Decltype} node, constructed with \begin{Program} const Decltype* make_decltype(const Expr&); \end{Program} \subsection{Expression as type} \label{sec:type.as-type} In various contexts it is useful to view an expression node as actually denoting a type. For instance, the C++ reserved identifier \code{bool} designates the built-in boolean type. The way that is accomplished in IPR is to build an \NodeClass{As\_type} node out of the \NodeClass{Identifier} node created for \code{bool}: \begin{Program} unit.make_as_type(unit.get_identifier(u8"bool")) \end{Program} All built-in C++ simple types are created that way. Another instance of situations where an \NodeClass{As\_type} is helpful is when dealing with dependent types. Assume that you have created a \NodeClass{Parameter} node for a template-parameter \texttt{C} and you want to represent the nested-type \texttt{typename C::value\_type}. The natural answer here is to build a \NodeClass{Scope\_ref} and use the resulting node as a type (reflecting the assumption introduced by the C++ keyword \texttt{typename}): \begin{Program} unit.make_as_type(*unit.make_scope_ref (\expr{C}, unit.get_identifier(u8"value_type"))) \end{Program} \subsection{Function type} \label{sec:type.function} A function type consists of three logical parts: (a) the parameter type list; (b) the return type; and (c) the list of exceptions a function of that type may throw. IPR represents a function type as a \NodeClass{Function} node. Such nodes can be constructed with the \NodeClass{impl::Unit} member functions \begin{Program} const Function* make_function(const Product&, const Type&); const Function* make_function(const Product&, const Type&, const Sum&); \end{Program} The first constructor function is a convenient surrogate for the second, as it implies that the function may throw any type (noted \code{...}). \subsection{Template type} \label{sec:type.template} A template type is very similar to a function type, except that it is represented by a \NodeClass{Template} node. The corresponding constructor function is the member function of \NodeClass{impl::Unit} \begin{Program} const Template* make_template(const Product&, const Type&); \end{Program} The first argument is the type of the parameter list, and the second argument is the type of the expression being parameterized. \subsection{Pointer to member type} \label{sec:type.ptro-to-member} A pointer to member type is represented as a pair: The class-type and the type of the member. The corresponding construction function is \begin{Program} const Ptr_to_member* make_ptr_to_member(const ipr::Type&, const ipr::Type&); \end{Program} of the class \NodeClass{impl::Unit}. At the implementation level, most compilers distinguish pointer to data member types from pointer to member function types. However, the internal representation is immaterial to the actual semantics constraints. One issue with our desire to have uniform syntax and representation for non-static member and ordinary functions, however, is to make sure that address of members are given the right type: this is easily solved as at anytime we do know when a function declaration is a non-static member function. See the discussion in \ref{sec:fun-decl}. \subsection{User-defined types} \label{sec:type.udt} All standard C++ user-defined types (\eg{} class or struct, union, enum) as well as namespace are considered user-defined types in IPR. They can be constructed with one of the expressions \begin{Program} \expr{unit}.make_class(\expr{parent_region}); \expr{unit}.make_enum(\expr{parent_region}); \expr{unit}.make_namespace(\expr{parent_region}); \expr{unit}.make_union(\expr{parent_region}); \end{Program} The resultinf type is \code{impl::Class*}, \code{impl::Enum*}, \code{impl::Namespace*} and \code{impl::Union*}, respectively. The definitions of all user-defined types delimit a region, which is the declarative region of their members. All, except enums, have a heterogeneous region, implemented by the class \code{impl::Region}. Enums' bodies are homogeneous region (\code{impl::homogeneous\_region}) because their members are all enumerators of the same type. For instance, an enumeration's body is a \code{impl::homogeneous\_region} Similarly the sequence of declarations in a heterogeneous region is a heterogeneous scope (\code{impl::Scope}), whereas in a homogeneous region, it is a \code{impl::homogeneous\_scope}, where \code{T} is the interface type of the declarations. \section{Classic expressions} \label{sec:expression} The abstract language modeled by IPR nodes is expression-based. This is to say that nearly any computational entity of interest is thought of as an expression. Thus compared to Standard C++ notions, most IPR nodes may be considered as representations of \emph{generalized expressions}. Consequently, the term \emph{classic expressions} is used to designate genuine Standard C++ expressions. Classic expressions are generally divided into categories: \begin{itemize} \item \emph{unary expressions}, e.g. \texttt{sizeof (int)}; \item \emph{binary expressions}, e.g. \texttt{a + b}; \item \emph{ternary expressions}, e.g. \texttt{x < y ? x : y}. \end{itemize} Each category contains a comprehensive list of IPR nodes specifically designed to cover all kinds of expressions that fall in that category. Every expression is made by a \texttt{impl::Unit} object. For example, the following program fragment shows how to create an IPR node for \texttt{sizeof (int)}: \begin{Program} unit.make_type_sizeof(unit.get_int()); \end{Program} When an expression involves an identifier that refers to a declared entity, we need to distinguish the \emph{use} of that identifier from the declaration itself. This is important for preserving the semantics of the input source program. For example the fragments \begin{Program} if (bool b = get_answer()) // ... \end{Program} and \begin{Program} bool b = get_answer(); if (b) // ... \end{Program} are in general not equivalent. The distinction between a use of identifier that denotes a declared entity from the introducing declaration is captured by the node \code{Id\_expr}. In general, one says \begin{Program} \expr{unit}.make_id_expr(\expr{decl}); \end{Program} to build an \code{Id\_expr} out of a \code{Decl} node. For the occasional situation where --- in a template definition --- an identifier cannot be resolved to the introducing declarations, one uses a \code{Name} node instead of a \code{Decl} node as argument to \code{make\_id\_expr}. Here is how one can construct nodes for the expression \texttt{a + b} \begin{Program} const Var& a = ... const Var& b = ... Expr* sum = unit.make_plus(*unit.make_id_expr(a), *unit.make_id_expr(b)); \end{Program} The conditional expression \texttt{x < y ? x : y} can be constructed as follows \begin{Program} const Expr& x = ... const Expr& y = ... const Expr& min = *unit.make_conditional(*unit.make_less(x, y), x, y); \end{Program} Cast-expressions are represented as binary expressions: The first operand is the target type and the second operand is the source expression. \subsection{User-defined operators} \label{sec:expression.user-defined-operator} In Standard C++, most operators can be overloaded and given a user-defined meaning. For generality, IPR takes the approach that just about any operator can be given a user-defined meaning. Consequently, every IPR implementation class for an operator has a field \code{op\_impl} to record the function declaration that implements that operator. For instance, consider the expression \code{cout~<<~2} for inserting the integer value tow in the output standard stream. Assume further that the insertion function operator declaration is represented by \code{ins}, then the IPR representation for the whole expression would be constructed as \begin{Program} impl::Lshift* shift = unit.make_lshist(\expr{cout}, \expr{two}); shift->op_impl = ins; // remember the insertion function \end{Program} If the member \code{op\_impl} is not set, then it is assumed to be a built-in operator. \subsection{Names and resolutions} \label{sec:expression.name-lookup} \section{Named data} \label{sec:data.named} This section describes the representation of named data, \ie{} variables, data member. Notice that in either case, references are treated no specially from variables or data members -- even though the C++ standard says that references are not objects. Named data are declared at user-defined types (\ie{} class-types, enumerations or namespaces) level. Consequently, their IPR node representations are manufactured with a member function, prefixed by \code{declare\_}, of the enclosing user-defined type. The next subsections discuss the mapping of the variety C++ named data to IPR representation. \subsection{Variable declaration} \label{sec:decl.variable} A variable is an association of a name and a storage for data, that does not have any enclosing object. The notion of variable is implemented by \code{impl::Var}. Manufacturing a node of that type requires --- as do most declaration nodes --- three things: (a) the name of the variable; (b) the type of the data to store; and (c) an optional initializer. A node for a variable is created with the member function \texttt{declare\_var()} of the implementation class for the enclosing user-defined type (\code{impl::Class}, \code{imp::Union} or \code{impl::Namespace}). Once a \texttt{impl::Var} is created, it must be associated with its initializer (if any). The following program illustrates how to build a variable declaration \begin{Program} static const int bufsz = 1024; \end{Program} at the global scope and write the resulting node in XPR notation. \begin{Program} #include #include #include int main() \{ using namespace ipr; impl::Unit unit; // current translation unit // Build the variable's name and type const Name& name = unit.get_identifier(u8"bufsz"); const Type* type = unit.make_cv_qualified(unit.get_int(), Type::Const); // and the actual impl::Var node at global scope and initialize it impl::Var* var = unit.global_ns->declare_var(name, *type); var->decl_data.spec = ipr::Decl::Static; var->init = unit.make_literal(unit.get_int(), u8"1024"); // Print out the whole translation unit Printer pp(std::cout); pp << unit; std::cout << '\bslash{n}'; \} \end{Program} The output of the above program is \begin{Output} bufsz : static const int = 1024; \end{Output} The effort of constructing the representation of that declaration basically is one line per significant token. \subsection{Static data members} \label{sec:named.data.static-member} A static data member is a global variable in disguise. They key difference between a static data member and an ordinary variable that the former is subject to access control. Therefore, a static data member is also represented by a \code{Var} node. From the IPR representation point of view, the steps for manufacturing a node for a static data member is pretty much the same as for ordinary variable, except that one uses the enclosing class-type as the manufacturer. For instance, consider the program fragment \begin{Program} class nifty_counter \{ // ... private: static int count; \}; \end{Program} where we are only interested in the portion relating to the representation of the static data member \code{nifty\_counter::count}. Here is a program that accomplishes that job and prints the IPR representation: \begin{Program} #include #include #include int main() \{ using namespace ipr; impl::Unit unit; // current translation unit // First, build the node for the nifty_counter class impl::Class* nifty_type = unit.make_class(*unit.global_region()); nifty_type->id = &unit.get_identifier(u8"nifty_counter"); unit.global_ns->declare_type(*nifty_type->id, unit.Class())->init = nifty_type; // Then build the static data member impl::Var* count = nifty_type->declare_var(unit.get_identifier(u8"count"), unit.get_int()); // "cout" is private count->decl_data.spec = Decl::Static | Decl::Private; // We do not set the initializer, since there is none. // Print out the whole translation unit Printer pp(std::cout); pp << unit; std::cout << '\bslash{n}'; \} \end{Program} A \code{Var} node represents a static data member if and only if its membership is a class-type. Notice that the operation \code{declare\_var()} is supported by implementation classes for all user-defined types, except enumerations. \subsection{Non-static data members} \label{sec:named.data.nonstatic-member} A non-static data member has semantics different from that of ordinary variable and static data-members. Consequently, it is represented by a distinct node (\code{Field}). However, the steps for manufacturing such node is very similar to that of a variable, except that the construction function \code{declare\_field()} is used --- instead of \code{declare\_var()}. Consider the following C++ program fragment \begin{Program} struct point \{ int x; int y; \}; \end{Program} that declares a class named \code{point} at the global scope. That declaration can be constructed as follows \begin{Program} #include #include #include int main() \{ using namespace ipr; impl::Unit unit; // Make the class node, and bind it to the name "point". impl::Class* point = unit.make_class(*unit.global_region()); point->id = &unit.get_identifier(u8"point"); unit.global_ns->declare_type(*point->id, unit.Class())->init = point; // declare the public data member "x". point->declare_field(unit.get_identifier(u8"x"), unit.get_int()) ->decl_data.spec = Decl::Public; // Ditto for "y". point->declare_field(unit.get_identifier(u8"y"), unit.get_int()) ->decl_data.spec = Decl::Public; // Print the current unit. Printer pp(std::cout); pp << unit; std::cout << std::endl; \} \end{Program} The output is: \begin{Program} point : class \{ x : public int; y : public int; \}; \end{Program} In a well formed C++ programs, a \code{Field} does not have an initializer. However, the IPR library does not enforce that rule --- an erroneous program may initialize a non-static data member and we do not gain any uniformity in enforcing that rule in the IPR data structures. \section{Function declarations} \label{sec:fun-decl} This sections discusses the basics for building nodes for function declarations. A function declaration is an association of a name with a mapping. For example, the C++ declaration \begin{Program} int identity(int); \end{Program} defines a mapping from \code{int} to \code{int} called \code{identity}. More generally, that declaration is represented as the association of the name \code{identity} with a unary parameterized expression, where the argument is expected to be of type \code{int}, the result of type \code{int}, and the computation of the result may be abnormally ended with an exception of any type (implied by the absence of exception specification). In XPR notation, it reads: \begin{Program} identity : (:int) int throw (...); \end{Program} Similar to the situation of named data (\secref{sec:data.named}), C++ distinguishes member functions from non-member functions. Unlike the named data case, both member functions and non-member function declarations are uniformly represented by \NodeClass{Fundecl} nodes. Like in the \code{Var} case, the membership of a \code{Fundecl} distinguishes a non-member function (membership is a namespace) from a member function (membership is a class-type). Member functions are further subdivided into two categories: static member functions and non-static member functions. The former are disguised non-member functions with privileged access to their enclosing class-type's members (kind of friend non-member functions), and the latter have an implicit or implied parameter. We do not have different kinds of function types for the types of static member functions and non-static member functions. They all have types uniformly represented by a \code{Function} node. A \code{Fundecl} for a static member function differs from a \code{Fundecl} for a non-static member function in that the former has the \code{Decl::Static} set and the latter not. Furthermore, the correspondence between a non-static member function in C++ and its ``regularized'' version in IPR makes and adjustment: the keyword \code{this} is made an explicit parameter. For instance, the following member function declarations \begin{Program} struct A \{ // ... int& f(); const int& f() const; static double g(A*); virtual void h(int) = 0; \}; \end{Program} will be interpreted as (XPR notation) \begin{Program} f: public (this: *A) &int throw(...); f: public (this: *const A) &const int throw(...); g: public static (: *A) double throw(...); h: public virtual pure (this: A*, : int) void throw(...); \end{Program} As a general rule, every non-static member function is adjusted to take \code{this} as first parameter, of type \code{*}\expr{cv T} where \expr{T} is the class and \expr{cv} is the cv-qualification of the non-static member function. \subsection{Mapping} \label{sec:fun-decl.mapping} A mapping is a parameterized expression. The notion of mapping is general enough to account for both function declaration bodies and template declaration bodies. To illustrate the generality here, consider the following program fragments in both Standard C++ and XPR notations: \begin{center} \begin{minipage}[t]{13cm} \begin{minipage}[t]{4cm} \centering{\textit{C++}} \begin{Program} int identity(int x) \{ return x; \} template struct buffer \{ char data[N]; \}; \end{Program} \end{minipage}\hfil \vrule \hfil \begin{minipage}[t]{7cm} \centering{\textit{XPR}} \begin{Program} identity : (x : int) int throw(...) = \{ return x; \} buffer : class = \{ data : public [N] char; \}; \end{Program} \end{minipage} \end{minipage} \end{center} A feature of the XPR notation here is that it makes a clear separation between the names being declared and what they are being bound to. The function \code{identity} maps an \code{int} to an \code{int}, and the class template \code{buffer} maps an \code{int} to a class-type. Furthermore, how the integer result of \code{identity} is computed is given by \code{int \{~return x;~\}}; similarly how the class result of \code{buffer} is computed is given by \code{struct \{~char data[N];~\}}. The implementation of either map constitutes its body. A mapping consists of a parameter list and a body. Its IPR implementation node is described by the class\NodeClass{impl::Mapping}, built with the member function \begin{Program} impl::Mapping* make_mapping(const ipr::Region&); \end{Program} of the \NodeClass{impl::Unit} class. For example, let's assume the previous declaration for \code{identity} appears at the global scope. Then, its associated mapping node would be made as follows: \begin{Program} impl::Mapping* mapping = unit.make_mapping(*unit.global_region()); \end{Program} Parameters are specified with the \code{param()} member function of \NodeClass{impl::Mapping}. \begin{Program} impl::Parameter* x = mapping->param(unit.get_identifier(u8"x"), unit.get_int()); \end{Program} The data member \code{impl::Mapping::parameters} holds the list of specified parameters. Its type is a product type, and represented by a \NodeClass{Product} node. It describes the domain type of the mapping. A mapping, being an expression, as a type. That type is represented by a \NodeClass{Function} node when it is associated with a function declaration, and a \NodeClass{Template} node when it is associated with a parameterized declaration. So, for the case of the \code{identity} function declaration, one would write the following \begin{Program} const Function* ftype = unit.make_function (mapping->parameters.type(), unit.get_int()); mapping->constraint = ftype; \end{Program} We will discuss the case of parameterized declarations in \secref{sec:decls:parameterized}. \subsection{Naming a mapping} \label{sec:fun-decl:naming} Building a \NodeClass{impl::Fundecl} is very similar to the process of building a node for a variable: one needs a name, a type and optional initializer. As explained above, the initializer for a function declaration is a mapping. \begin{Program} impl::Fundecl* f = unit.global_ns->declare_fun (unit.get_identifier(u8"identity"), *ftype); f->init = mapping; // the named mapping \end{Program} The node for a function declaration that is not a definition is initialized with an incomplete mapping. An incomplete mapping is a mapping whose body is not specified. \subsection{Constructors and destructors} A constructor or destructor is represented as a non-static member function, suitably adjusted to take \code{this} as a first parameter. Constructors and destructors to not return values, consequently their return type is \code{void}. \section{Statements} \label{sec:statements} This section gives the translation of ISO Standard C++ statements to IPR nodes. \subsection{Compound statement} \label{sec:stmt.compound} Named mappings are initialized with blocks in function definitions. An IPR block is a statement and consists of a sequence of statements and optional sequence of handlers. Standard C++ defines a compound statement as any brace-enclosed sequence of statements \begin{Grammar} \cxxRule{compound-statement:} \{ \cxxOptRule{statement-seq} \} \cxxRule{statement-seq:} \cxxRule{statement} \cxxRule{statement-seq} \cxxRule{statement} \end{Grammar} The corresponding concrete IPR representation is \NodeClass{impl::Block}. Such a node is built with the member function \begin{Program} impl::Block* make_block(const ipr::Region&); \end{Program} of the class \NodeClass{impl::Unit}. Suppose that we have to create nodes for the definition \begin{Program} int identity(int x) \{ return x; \} \end{Program} Then one would first create a block node for the body of the mapping associated with \code{identity}, and then fill in that block with sequence of statements as explained in sub-sections to follow. \begin{Program} impl::Block* body = unit.make_block(mapping->parameters, unit.get_int()); mapping->body = body; // fill in the body with add_stmt() as shown below \end{Program} \subsection{Expression statement} \label{sec:stmt.expr} Most statements are actually expressions statements, which Standard C++ defines as \begin{Grammar} \cxxRule{expression-statement:} \cxxOptRule{expression} ; \end{Grammar} They are concretely represented with \NodeClass{impl::Expr\_stmt}: \begin{Program} unit.make_expr_stmt(\cxxRule{expr}) \end{Program} The case of \emph{null statement}, \ie{} an expression statement with missing expression, is handled by calling the (member) function \verb=null_expr()= for the \NodeClass{Unit} object. An instance of null statement is the following fragment \begin{Program} while (*dst++ = *src++) ; \end{Program} While statements are discussed in \secref{sec:stmt.iteration.while}. Here, we just illustrate the representation an ``empty'' body: \begin{Program} Stmt* stmt = unit.make_expr_stmt(unit.null_expr()); \end{Program} \subsection{Selection statement} \label{sec:stmt.selection} A selection statement is any of the three kind of statements as defined by \begin{Grammar} \cxxRule{selection-statement:} if ( \cxxRule{condition} ) \cxxRule{statement} if ( \cxxRule{condition} ) \cxxRule{statement} else \cxxRule{statement} switch ( \cxxRule{condition} ) \cxxRule{statement} \end{Grammar} They are concretely represented in IPR with \NodeClass{If\_then}, \NodeClass{If\_then\_else} and \NodeClass{Switch} nodes, respectively. Both \NodeClass{impl::If\_then} and \NodeClass{impl::Switch} nodes are constructed in similar ways. They all require two arguments: the first being the condition and the second being the selected statement. Use \texttt{make\_if\_then()} to build a \NodeClass{impl::If\_then} node, and \texttt{make\_switch()} for a \NodeClass{impl::Switch} node. For instance, the fragment \begin{Program} if (lhs < rhs) return false; \end{Program} may be translated as: \begin{Program} Expr* return_value = unit.make_literal(unit.Bool(), u8"bool"); unit.make_if_then(*unit.make_less(\expr{lhs}, \expr{rhs}), *unit.make_return(*return_value)); \end{Program} An \NodeClass{impl::If\_then\_else} node requires three arguments: the condition, the then-branch statement and the else-branch statement. It is constructed through the (member) function \texttt{make\_if\_then\_else()} of class \NodeClass{impl::Unit}. \subsection{Iteration statement} \label{sec:stmt.iteration} Standard C++ defines an iteration statement to be \begin{Grammar} \cxxRule{iteration-statement:} while ( \cxxRule{condition} ) \cxxRule{statement} do \cxxRule{statement} while ( \cxxRule{condition} ) for ( \cxxOptRule{for-init-statement} \cxxOptRule{condition} ; \cxxOptRule{expression} ) \cxxRule{statement} \end{Grammar} \subsubsection{While statement} \label{sec:stmt.iteration.while} Constructing a \NodeClass{impl::While} node requires the condition node and the iterated statement node. For example, the following fragment \begin{Program} while (n != 0) n = process_line(n); \end{Program} may be constructed with \begin{Program} impl::Var* n = ... impl::Fundecl* processline = ... // ... Expr* cond = unit.make_not_equal(n, *unit.make_literal(unit.get_int(), u8"0")); impl::Expr_list* args = unit.make_expr_list(); // hold the arg-list. args->push_back(n); Expr* call = unit.make_call(processline, *args); Stmt* stmt = unit.make_expr_stmt(*uni.make_assign(n, call)); Stmt* while_stmt = unit.make_while(cond, stmt); \end{Program} \subsubsection{Do statement} A do statement is constructed similar to a while statement. The (member) function to call is \texttt{make\_do()} with the iterated statement and the condition expression as arguments, in that order. \subsubsection{For statement} A for statement is a curious and interesting statement. All its components are optional. A missing part is equivalent to either a null expression or a null statement. A \NodeClass{For} node is created through the (member) function \texttt{make\_for()} which takes four arguments, one for each components. Let's first look at \begin{Program} for (int i = 0; i < N; ++i) \cxxRule{stmt} \end{Program} In this case, the \cxxRule{for-init-statement} is a declaration. Therefore, we create a sub-region (of the active region) that will contain the declaration and we use the scope of that sub-region as the first argument for \texttt{make\_for()}. \begin{Program} // the IPR node representing the for statement impl::For* for_stmt = unit.make_for(); // Build the declaration for "i". impl::Region* init_region = \expr{active_region}->make_subregion(); impl::Var* i = init_region->declare_var(unit.get_identifier(u8"i"), unit.get_int()); i->init = unit.make_literal(unit.get_int(), u8"0"); // set the for-init for_stmt->init = &init_region.scope; // Build the condition. for_stmt->cond = unit.make_less(*i, \expr{N}); // the incrementation for_stmt->inc = unit.make_pre_increment(*i); // the body of the for-statement for_stmt->stmt = \expr{stmt}; \end{Program} %% Notice that, in this case, the scope for the init-declaration becomes the %% active scope till \cxxRule{stmt}. If the declaration for the variable \texttt{i} was not limited to the for statement, \ie{} if we had \begin{Program} int i; for (i = 0; i < N; ++i) \cxxRule{stmt} \end{Program} then we would not need to build a sub-scope for that variable. Rather, we would just build the declaration in the current scope: \begin{Program} // Build a declaration for "i", Var* i = \textit{active_region}->declare_var(unit.get_identifier(u8"i"), unit.get_int()); impl::For* for_stmt = unit.make_for(); for_stmt->init = unit.make_assign(*i, *unit.make_literal(unit.get_int(), u8"0")); // the condition, for_stmt->cond = unit.make_less(*i, \textit{N}); // the incrementation, for_stmt->inc = unit.make_pre_increment(*i); \end{Program} Another interesting case is when the \cxxRule{condition} in the for statement is actually a declaration. In that case, we build a sub-region (of the active region) and use it as the second argument to \texttt{make\_for()}. Therefore the following fragment \begin{Program} for (int i = 0; int j = N - i; ++i) \cxxRule{stmt} \end{Program} may be translated by \begin{Program} impl::For* for_stmt = unit.make_for(); // Build the for-initialization part impl::Region* init_region = \expr{active_region}->make_subregion(); for_stmt->init = &init_region->scope; impl::Var* i = init_region->declare_var(unit.get_identifier(u8"i"), unit.get_int()); i->init = unit.make_literal(unit.get_int(), u8"0"); // The for-condition part impl::Region* cond_region = init_region->make_subregion(); for_stmt->cond = &cond_region->scope; impl::Var* j = cond_region->declare_var(unit.get_identifier(u8"j"), unit.get_int()); j->init = unit.make_sub(\expr{N}, *i); // the incrementation part, for_stmt->inc = unit.make_pre_increment(*i); // and the body of the for-statement. for_stmt->stmt = \cxxRule{stmt}; \end{Program} Notice that the region containing \code{j} is a sub-region of the scope containing \code{i} and is the active scope till \cxxRule{stmt}. \subsection{Labeled statement} \label{sec:stmt.labeled} Standard C++ defines a labeled statement according to the grammar: \begin{Grammar} \cxxRule{labeled-statement:} \cxxRule{identifier} : \cxxRule{statement} case \cxxRule{constant-expression} : \cxxRule{statement} default : \cxxRule{statement} \end{Grammar} All these three variants of labeled statements are uniformly represented in IPR through the node class \NodeClass{Labeled\_stmt}. The label can be any IPR expression. Since a name is an expression a statement like \begin{Program} id: token = cursor - 1; // ... \end{Program} may be represented in IPR as: \begin{Program} impl::Var cursor = ...; // ... impl::Minus* rhs = unit.make_minus(cursor, *unit.make_literal(unit.get_int(), u8"1"))); impl::Assign* expr = unit.make_assign(*cursor, *rhs); impl::Expr_stmt* expr_stmt = unit.make_expr_stmt(*expr); auto& lbl = unit.get_identifier(u8"id"); impl::Labeled_stmt* labeled_stmt = unit.make_labeled_stmt(lbl, *expr_stmt); \end{Program} Here a node created for the name \texttt{id} is used as the expression that labels the whole statement. For a \texttt{case} label, the associated constant expression is used as the labeling expression. For example, for the program fragment \begin{Program} int line_count = 0; // ... switch (*cur) \{ case '{\bslash}n': ++line_count; // ... \} \end{Program} one might construct \begin{Program} impl::Var* linecount = ... // ... // literal used to label the case-statement impl::Literal* nl = unit.make_literal(unit.get_char(), u8"{\bslash\bslash}n"); impl::Labeled_stmt* stmt = unit.make_labeled_stmt (*nl, *unit.make_expr_stmt(*unit.make_pre_increment(*linecount))); \end{Program} The \texttt{default} label is represented no different from ordinary labels. That is, one uses \verb=unit.get_identifier(u8"default")= as the labeling expression. \subsection{Jump statement} \label{sec:stmt.jump} A jump statement is any of \begin{Grammar} \cxxRule{jump-statement:} break ; continue ; return \cxxOptRule{expression} ; goto \cxxRule{identifier} ; \end{Grammar} Return-statements are built with the member function \code{make\_return()} of \code{imp::Unit}. So, continuing with the \code{identity} function \begin{Program} body->add_stmt(unit.make_return(*x)); \end{Program} A break-statement is built with \code{make\_break()}, a continue-statement is built with \code{make\_continue()}, and a goto-statement is built with \code{make\_goto()} taking the destination as argument. At the exception of the return statement, IPR nodes for jump statements have room to record the statements primarily affected by the control transfer. Consider the program fragment \begin{Program} char c; int line_count = 0; // ... switch (c) \{ case '{\bslash}n': ++line_count; break; // ... \} \end{Program} Here is a corresponding IPR nodes construction: \begin{Program} // Build declaration for "c", impl::Var* c = \expr{active_region}->declare_var(unit.get_identifier(u8"c"), unit.get_char()); // do the same for "line_count", impl::Var* line_count = \expr{active_region}->declare_var (unit.get_identifier(u8"line_count"), unit.get_int()); line_count->init = unit.make_literal(unit.get_int(), u8"0"); // ... // Build the Block for the switch statement. impl::Block* block = unit.make_block(\textit{active_region}); // Build the switch-statement node. Switch* switch_stmt = unit.make_switch(*c, *block); // Fill in the switch body, Stmt* stmt = unit.make_expr_stmt(*unit.make_pre_increment(line_count)); Expr* lbl = unit.make_literal(unit.get_char(), u8"{\bslash}n"); block->add_stmt(unit.make_labeled_stmt(lbl, stmt)); // Build the break statement impl::Break* break_stmt = unit.make_break(); // record the statement we're breaking from break_stmt->stmt = switch_stmt; // put it in the body. block->add_stmt(break_stmt); // ... \end{Program} \subsection{Declaration statement} \label{sec:stmt.declaration} A declaration is a statement. As such, a declaration that appears at block scope shall be added to the sequence of statements that constitute the body of that block. \subsection{Try Block} \label{sec:stmt.try-block} Try blocks in Standard C++ come into various syntactic flavors. \begin{Grammar} \cxxRule{try-block:} try \cxxRule{compound-statement} \cxxRule{handler-seq} \cxxRule{function-try-block:} try \cxxOptRule{ctor-initializer} \cxxRule{function-body} \cxxRule{handler-seq} \cxxRule{handler-seq:} \cxxRule{handler} \cxxOptRule{handler-seq} \cxxRule{handler:} catch ( \cxxRule{exception-declaration} ) \cxxRule{compound-statement} \end{Grammar} In IPR, we do not have a separate node for try-block statement. Rather, we take the general approach that any block can potentially throw an exception; therefore any \NodeClass{Block} has an associated sequence of handlers. If that sequence is empty then it does not come from a try-block. \section{Parameterized declarations} \label{sec:decls:parameterized} In IPR, any expression can be parameterized. Parameterized expressions, and are uniformly represented with \NodeClass{impl::Mapping} nodes (see discussion in \secref{sec:fun-decl.mapping}). Parameterized declarations, or template declarations in Standard C++ terminology, are declaration generators. For instance, consider the following generalization of the function \code{identity} from previous section: \begin{Program} template T identity(T x) { return x; } \end{Program} equivalently written in XPR as \begin{Program} identity: (x: T) T throws(...) = \{ return x; \} \end{Program} It is clear that it is a named mapping which, when given a type argument T, produces a function declaration --- named \code{identity} -- taking a \code{T} and returning a value of the same type. In a sense, it is a mapping of a mapping: the result of the first mapping is compile-time, whereas the second is runtime; however the abstract representations are similar. A named mapping is a declaration (\code{Named\_map}). It has type represented by a \NodeClass{Template} node. Its initializer is a mapping of type \code{Template}. \subsection{Primary declaration generators} \label{sec:named-mapping:primary} C++ template declarations can be divided into two categories: (a) primary templates; and (b) secondary templates. A primary template is the most general form of a declaration generator. It indicates the type of the declaration it generates, and the number and sorts of arguments it accepts. A primary declaration generator participates in overload resolution (secondary declaration generators don't). The notion of primary template declaration should not be confused with that of master declaration. A master declaration is the first declaration of an entity in a given scope. Primary declarations, on the other hand, may be repeated where permitted (for instance, at namespace scope). \begin{Program} template struct Array \{ // \#1 // ... \}; template T sum(const Array&); // \#2 template struct Array; // \#3 \end{Program} \code{\#1} is the first declaration of the template \code{Array<>}; so it is a master declaration. It is also the most general form of \code{Array<>} instance declaration generator; therefore it is also a primary template. In summary, \code{\#1} is a master primary template declaration. On the other hand, \code{\#3} is a re-declaration, therefore it is a non-master primary template declaration. A node for a primary template declaration is built with the member function \begin{Program} impl::Named_map* declare_primary_map(const ipr::Name&, const ipr::Template&); \end{Program} of the enclosing user-defined type. A primary map does bookkeeping for various ``administrative'' information. The program fragment below builds the nodes for the representation of \code{\#1} \begin{Program} #include #include #include int main() \{ using namespace ipr; impl::Unit unit; Printer pp(std::cout); // make the node that contains the body of the Array template impl::Mapping* mapping = unit.make_mapping(*unit.global_region()); // declare the template type-parameter "T". impl::Parameter* T = mapping->param(unit.get_identifier(u8"T"), unit.Class()); // set the type of the mapping. const ipr::Template* tt = unit.make_template(mapping->parameters.type(), unit.Class()); mapping->constraint = tt; // build the decl for "Array", with same type. impl::Named_map* array = unit.global_ns->declare_primary_map (unit.get_identifier(u8"Array"), *tt); array->init = mapping; // "Array" uses the argument list "". array->args.push_back(T); // and the body of the mapping is a class-expression impl::Class* body = unit.make_class(mapping->parameters); mapping->body = body; // set its name. body->id = unit.make_template_id(*array, array->args); pp << unit; std::cout << std::endl; \} \end{Program} Every instance of \code{Named\_map} keeps track of the (template) argument list it uses. That argument list is needed for the purpose of determining the most specialized version of a named mapping use. \subsection{Secondary named mappings} \label{sec:named-mapping:secondary} A secondary template declaration provides a specialized implementation for given sub-family of the primary template. Therefore, it may introduce more or fewer parameters than the primary template does. However, it must supply an argument list that meets the requirements (in number and kinds) stated by the primary template declaration. Secondary templates do not participate in overload resolution. Consider for example th previous \code{Array<>} declaration, continued as follows \begin{Program} template<> struct Array \{ // #4 // ... \}; template struct Array : Array \{ // #5 // ... \}; \end{Program} The declaration \code{\#4} is an explicit specialization of \code{Array<>}, but it is \emph{not} a template. Therefore, it must be represented as an ordinary class. On the other hand, \code{\#5} is a secondary template declaration. It specializes \code{\#1}, and uses the argument list \code{}. Secondary named mappings are manufactured with the member function \begin{Program} impl::Named_map* declare_secondar_map(const ipr::Name&, const ipr::Template&); \end{Program} of the enclosing user-defined type. \section{External representation} \label{sec:xpr} The persistent form of IPR nodes is expressed through the XPR syntax --- for \emph{eXternal Program Representation}. The XPR syntax is defined in the Reference Manual. The header file \texttt{} provides access to XPR output facilities. That header file defines: \begin{itemize} \item a basic XPR printer \texttt{ipr::Printer} ; \item facilities to print \emph{expressions}, \emph{types}, \emph{statements} and \emph{declarations}. \end{itemize} The basic XPR printer takes a reference to an \emph{std::ostream} --- which is the stream into which it will insert the XPR syntax of whatever nodes it is asked to print. A typical construction is \begin{Program} \#include \#include \#include int main() \{ ipr::impl::Unit unit; // construct IPR nodes ... // Print them out, starting from the global scope ipr::Printer printer(std::cout); printer << unit; std::cout << std::endl; // ... \} \end{Program} \bibliographystyle{amsalpha} \bibliography{ipr} \end{document} %%% Local Variables: %%% mode: latex %%% TeX-master: t %%% End: ================================================ FILE: include/ChangeLog ================================================ 2015-12-20 Gabriel Dos Reis * ipr/interface: Introduce Module_name, Module_unit, Interface_unit, Module. Rename Unit to Translation_unit. Translation_unit is now a class hierarchy root, outside Node. * ipr/impl: Implement. 2015-12-19 Gabriel Dos Reis * ipr/interface: Introduce ipr::Lexicon, split from ipr::Unit. * ipr/impl: Implement. 2015-12-17 Gabriel Dos Reis * ipr/interface: Add Optional. Use it to capture commonality of optional node properties. * ipr/impl: Propagate changes. 2015-12-16 Gabriel Dos Reis * ipr/interface: Replace Type_sizeof and Expr_sizeof with Sizeof. Similarly, replace Type_typeid and Expr_typeid with Typeid. * ipr/impl: Propagate changes. Implement. 2015-12-15 Gabriel Dos Reis * ipr/interface: Add Expansion for pack expansions. * ipr/impl: Implement. 2015-12-14 Gabriel Dos Reis * ipr/interface: Sequence::begin and Sequence::end no longer virtual -- they shouldn't have been ever since get() was customization point. 2015-12-14 Gabriel Dos Reis * ipr/interface: Move Decl::Specifier to toplevel. Rename to DeclSpecifiers. Now an enum class with fixed underlying type. 2015-12-14 Gabriel Dos Reis * ipr/interface: Add ipr::Auto type node. 2015-12-14 Gabriel Dos Reis * ipr/interface: 'auto' is no longer a storage-class specifier in C++11 and up. 2015-12-14 Gabriel Dos Reis * ipr/interface: Move Type::Qualifier to toplevel. Rename to Type_qualifier. Adjust users. 2015-12-14 Gabriel Dos Reis * ipr/interface: Add TokenCategory, TokenValue, Token, Attribute, BasicAttribute, ScopedAttribute, LabeledAttribute, CalledAttribute, and Attribute::Visitor. * ipr/impl: Implement. 2015-11-29 Gabriel Dos Reis * ipr/interfce: Modify ipr::Enum to account for scoped enumerations. * ipr/impl: Implement modifications. 2015-11-22 Gabriel Dos Reis * Move all source header files into dedicated ipr/ directory. Simplifies build and installation setup. 2015-11-19 Gabriel Dos Reis * impl.hxx (impl::stable_farm<>): New. Use in lieu of homegrown util::slist. (util::slist<>): Remove. * traversal.hxx (ipr): Remove workarounds for ancient VC++. 2010-11-13 Gabriel Dos Reis * impl.H (expr_factory::make_phantom): Add overload taking a type argument. 2010-07-23 Gabriel Dos Reis * impl.H (impl::expr_factory::make_id_expr): Add new overload for Decl. 2010-05-28 Gabriel Dos Reis * interface.H (Initializer_list): New. * impl.H: Provide implementation for Initializer_list. 2010-04-26 Gabriel Dos Reis * interface.H (Unit::get_typename): Declare. * impl.H (impl::Unit::get_typename): Likewise. 2010-03-29 Gabriel Dos Reis * impl.H (expr_factory::make_identifier): Add overloads. (expr_factory::make_operator): Likewise. 2010-03-29 Gabriel Dos Reis * interface.H (C_linkage): Remove. (Cxx_linkage): Likewise. (Visitor::visit(const C_linkage&)): Likewise. (Visitor::visit(const Cxx_linkage&)): Likewise. * impl.H (Unit::get_linkage): Move to... (expr_factory::get_linkage): ... here. (type_factory::make_as_type): Add unary overload. (Unit::get_cxx_linkage): Simplify return type. (Unit::get_c_linkage): Likewise. (Unit::cxx_linkage): Remove. (Unit::c_linkage): Likewise. (Unit::linkages): Move to ... (expr_factory::linkages): ...here. 2010-03-29 Gabriel Dos Reis * impl.H (expr_factory::make_literal): Add overloads for 'const char*' and 'const std::string&'. 2010-03-29 Gabriel Dos Reis * impl.H (Unit::get_string): Move to... (expr_factory::get_string): ... here. 2009-09-22 Gabriel Dos Reis * interface.H (Rvalue_reference): New IPR node type for rvalue-reference types. (Visitor::visit): Overload for Rvalue_reference. * node-category.def: Add rvalue_reference_cat. * impl.H (impl::Rvalue_reference): New type alias for ipr::Rvalue_reference implementation. (type_factory::make_rvalue_reference): New. 2009-06-23 Gabriel Dos Reis Version 0.43 * interface.H (For_in): New IPR node type. * impl.H (impl::For_in): New. 2008-11-24 Gabriel Dos Reis * interface.H (Mapping::has_result): Remove. (Decl::Constexpr): New specifier. 2008-11-23 Gabriel Dos Reis * impl.H (impl::Builtin): Simplify. 2008-11-22 Gabriel Dos Reis * impl.H (impl::Builtin): Set this->id. 2008-11-19 Gabriel Dos Reis * interface.H (Stmt::source_location): New. * impl.H (impl::Stmt::source_location): Implement. 2008-09-20 Gabriel Dos Reis * interface.H (Datum): Now member of Classic. The implementation was already in Classic, so this just fix a thinko. 2008-09-15 Gabriel Dos Reis * interface.H (Classic): Now non-template. Propogate change throughout. 2008-06-20 Gabriel Dos Reis * interface.H (Var::membership): Remove. * impl.H (impr::Var::membership): Likewise. (impl::Var::member_of): Likeise. (Udt<>::impl::declare_var): Don't set member_of. 2008-05-29 Gabriel Dos Reis * interface.H (Decl::lexical_region): Declare. (Decl::home_region): Likewise. * impl.H (impl::Fundecl::lexical_region): Declare. (impl::Typedecl::lexical_region): Likewise. (impl::Bitfield::lexical_region): Likewise. (impl::Bitfield::home_region): Likewise. (impl::Field::lexical_region): Likewise. (impl::Field::home_region): Likewise. (impl::Var::lexical_region): Likewise. (impl::Alias::lexical_region): Likewise. (impl::Named_map::lexical_region): Likewise. 2005-11-10 Gabriel Dos Reis * lexer.H: New. 2005-09-26 Gabriel Dos Reis * interface.H (Unit::Ellipsis): Declare. (Unit::Class): Likewise. (Unit::Union): Likewise. (Unit::Enum): Likewise. (Unit::Namespace): Likewise. 2005-09-03 Gabriel Dos Reis * impl.H (impl::unary_node): New. (impl::unary_expr): Use it as base class. Refactor. (impl::unar_expr::rep): Remove, now inherited from impl::unary_node. (impl::unary_expr::operand): Remove, now implemened by impl::unar_node. (impl::Linkage): New; implement ipr::Linkage. * node-category.def (linkage_cat): New. * interface.H (Linkage): New. (Visitor::visit(const Linkage&)): New. 2005-05-20 Gabriel Dos Reis * impl.H (impl::Mapping::nesting_level): New. (impl::Mapping::depth): Override ipr::Mapping::depth. (impl::Mapping::Mapping): Adjust. (impl::expr_factory::make_mapping): Take an additional "level" parameter. (impl::unique_decl<>::scope_pos): Remove. (impl::unique_decl<>::position): Remove. (impl::Parameter::constraint): Remove. (impl::Parameter::abstract_designator): New. (impl::Parameter::position): New. (impl::Parameter::Parameter): Adjust signature. (impl::Base_type::scope_pos): New, declare. (impl::Base_type::Base_type): Adjust signature. (impl::Base_type::position): Override ipr::Decl::position. (impl::Enumerator::scope_pos): New, declare. (impl::Enumerator::Enumerator): Adjust signature. (impl::Enumerator::position): Override ipr::Decl::position. (impl::Parameter_list::add_member): Adjust signature. (impl::expr_factory::rname_for_next_param): Rename from impl::expr_factory::make_rname. (impl::Unit::make_parameter): New. * interface.H (Mapping::depth): New. * io.H (Printer::operator<<(int)): Define. * impl.H (impl::Rname): New. (impl::expr_factory::make_rname): Likewise. (impl::expr_factory::rnames): Likewise. * interface.H (Visitor::visit(const Rname&)): New. * node-category.def (rname_cat): New. * interface.H (Rname): New. 2005-04-07 Gabriel Dos Reis * io.H (xpr_decl::needs_newline): New. (xpr_decl::xpr_decl): Take an additional parameter. 2005-04-04 Gabriel Dos Reis * Makefile.am: Tidy. 2005-04-03 Gabriel Dos Reis * impl.H (impl::unary_expr::unary_expr): Initialize constraint. (impl::binary_expr::binary_expr): Likewise. (impl::ternary_expr::ternary_expr): Likewise. 2005-03-31 Gabriel Dos Reis * Makefile.am (stamp-ipr): Avoid symbolic links with relative paths. It does not work. 2005-03-23 Gabriel Dos Reis * interface.H (stats:all_nodes_count): Declare. (stats::node_count): Likewise. * Makefile.am (ipr_srchdr): Add node-category.def (stamp-ipr): Set link for node-category.def too. (install-data-local): Install node-category too. * Makefile.in: Regenerate. * impl.H: Revert previous changes relating to constructor definition. 2005-03-22 Gabriel Dos Reis * interface.H (of_same_category): New function. (Node::Node): Make protected. (Node::kind): Remove. (Comment::Comment): Define. (Annotation::Annotation): Likewise. (Region::Region): Likewise. (Expr::Expr): Likewise. (Name::Name): Likewise. (Identifier::Identifier): Likewise. (Operator::Operator): Likewise. (Conversion::Conversion): Likewise. (Scope_ref::Scope_ref): Likewise. (Template_id::Template_id): Likewise. (Ctor_name::Ctor_name): Likewise. (Dtor_name::Dtor_name): Likewise. (Overload::Overload): Likewise. (Scope::Scope): Likewise. (Type::Type): Likewise. (Type_id::Type_id): Likewise. (Product::Product): Likewise. (Sum::Sum): Likewise. (Decltype::Decltype): Likewise. (Pointer::Pointer): Likewise. (Ptr_to_member::Ptr_to_member): Likewise. (Reference::Reference): Likewise. (Array::Array): Likewise. (As_type::As_type): Likewise. (Udt::Udt): Likewise. (Namespace::Namespace): Likewise. (Class::Class): Likewise. (Union::Union): Likewise. (Enum::Enum): Likewise. (Template::Template): Likewise. (Classic): Take additional template parameter. (Classic::Classic): Define. (Address, Array_delete, Complement, Delete, Deref, Expr_sizeof, Expr_typeid, Unary_minus, Not, Post_decrement, Post_increment, Pre_decrement, Pre_increment): Have Classic the node category code as argument. (Paren_expr::Paren_expr): Define. (Expr_list::Expr_list): Likewise. (Label::Label): Likewise. 2005-03-21 Gabriel Dos Reis * impl.H (impl::Overload, impl::single_overload, impl::empty_overload): Have Node as base class. (impl::Overload::accept, impl::single_overload::accept, impl::empty_overload::accept): Remove. (impl::unary_expr<>::accept): Remove. (impl::Type<>::accept): Remove. (impl::type_sequence<>): Have Node as base type. (impl::basic_expr<>::accept): Remove. (impl::conversion_expr<>::accept): Remove. (impl::ternary_expr<>::accept): Remove. (impl::Decl<>::accept): Remove. (impl::unique_decl<>::accept): Remove. (impl::homogeneous_scope<>): Have Node as base class. (impl::homogeneous_scope<>::accept): Remove. (impl::homogeneous_region<>::accept): Remove. (impl::Comment): Have Node as base class. (impl::Comment::accept): Remove. (impl::Expr_list): Have Node as base class. (impl::Expr_list::accept): Remove. (impl::Identifier): Have Node as base class. (impl::Identifier::accept): Remove. (impl::Id_expr): Have Node as base class. (impl::Id_expr::accept): Remove. (impl::Operator): Have Node as base class. (impl::Operator::accept): Remove. (impl::Paren_expr): Have Node as base class. (impl::Paren_expr::accept): Remove. (impl::Region) Have Node as base class. (impl::Region::accept): Remove. (impl::Scope): Have Node as base class. (impl::Scope::accept): Remove. (impl::Enum): Have impl::Node, ipr::enum_node> as base class. (impl::Break): Have impl::Node as base class. (impl::Break::accept): Remove. (impl::Continue): Have impl::Node as base class. (impl::Continue::accept): Remove. (impl::Block): impl::Node as base class. (impl::Block::accept): Remove. (impl::For): Have impl::Node as base class. (impl::For:accept): Remove. (impl::Type_id::accept): Remove. (impl::Literal::accept): Remove. (impl::Mapping::accept): Remove. * interface.H (Node_kind): New enumeration. (Basic_node<>): New. (Node::kind): New. (Comment): Make Basic_node a base class. (Annotation): Make Basic_node a base class. (Region): Make Basic_node a base class. (Identifier): Make Basic_node a base class. (Operator): Have Basic_node as base class. (Conversion) Have Basic_node as base class. (Scope_ref): Have Basic_node as base class. (Template_id): Have Basic_node as base class. (Ctor_name): Have Basic_node as base class. (Ctor_name): Have Basic_node as base class. * node-kind.def: New file. Define Node_code enumerators. * impl.H (impl::Id_expr): New. Implement corresponding interface. (expr_factory::id_exprs): New. (expr_factory::make_id_expr): Declare. * interface.H (Id_expr): New. (Visitor::visit(const Id_expr&)): Declare. * impl.H (impl::Mapping::value_type): New. (impl::Mapping::result_type): Declare. * interface.H (Mapping::result_type): New. 2005-03-17 Gabriel Dos Reis * io.H (xpr_type): New. (operator<<(Printer&, xpr_type)): Likewise. 2005-03-10 Peter Pirkelbauer * impl.H (Classic<>): Change type of op_impl/impl_decl() from const ipr::Fundecl* to const ipr::Decl*. * interface.H (Classic<>): Change type of impl_decl() from const ipr::Fundecl* to const ipr::Decl* 2005-03-02 Gabriel Dos Reis * interface.H (Classic<>): New. * Mark for 0.34 2005-02-28 Gabriel Dos Reis * impl.H (impl::Unit::global_ns): Make public. 2005-02-18 Gabriel Dos Reis * interface.H (Var::membership): New. (Typedecl::membership): New. * impl.H (impl::Class::add_base): Rename to declare_base. (impl::Var::member_of): New. (impl::Var::membership): Declare. (impl::Typedecl::member_of): New. (impl::Typedecl::membership): New. 2005-02-17 Gabriel Dos Reis * interface.H: Rename Expr_as_type to As_type * impl.H: Likewise. Rename make_expr_as_type to make_as_type. Rename heterogeneous_scope so Scope. Rename heterogeneous_region to Region. (impl::Region::declare_bitfield): Define. * impl.H (impl::Datum): Fix thinko. 2005-02-16 Gabriel Dos Reis * utility.H (impl::util::rb_tree::container<>::find_slot): Fix thinko. (impl::util::rb_tree::container<>::find): Rework. (impl::util::rb_tree::container<>::insert): Likewise. (impl::util::rb_tree::container<>::find_slot): Remove. 2005-02-14 Gabriel Dos Reis * impl.H (impl::basic_map): Remove. (impl::master_map_data): Likewise. (impl::Impl): Rename to impl::decl_rep. (impl::primary_map_data): Remove. (impl::basic_map_data): Likewise. (impl::decl_factory::decls): Make public. (impl::decl_factory::master_info): Likewise. (impl::Fundecl::init): Make non-const. (impl::heterogeneous_scope::make_secondary_map): Adjust type. (impl::heterogeneous_scope::master_primary_map_info): Remove. (impl::heterogeneous_scope::master_secondary_map_info): Likewise. (impl::heterogeneous_scope::maps): Likewise. (impl::heterogeneous_region::declare_primary_map): New. (impl::heterogeneous_region::declare_secondary_map): Likewise. 2005-02-12 Gabriel Dos Reis * io.H (Printer::operator<<): Define overloads for characters. (needs_newline): Remove. (newline): Likewise. * impl.H (impl::Fundecl::init): Make non-const. 2005-02-10 Gabriel Dos Reis * io.H (Token): Remove. (operator<<(Printer&, Printer& (*)(Printer&))): Likewise. (Printer::operator<<): Remove template declaration. Add individual declaration. * impl.H (impl::heterogeneous_region::declare_alias): New. (impl::heterogeneous_region::declare_var): Likewise. (impl::heterogeneous_region::declare_field): Likewise (impl::heterogeneous_region::declare_typedecl): Likewise. (impl::heterogeneous_region::declare_fundecl): Likewise. (impl::Udt<>::decl): Remove. (impl::Udt<>::id): New. (impl::Udt<>::name): Adjust. (impl::Udt<>::Udt): Likewise. (impl::Enum::decl): Remove. (impl::Enum::id): New. 2005-02-08 Gabriel Dos Reis * impl.H (impl::Class::add_base): New. 2005-02-06 Gabriel Dos Reis * Mark for 0.33 * io.H (xpr_decl): Rename from xpr_declaration. (xpr_stmt): Rename from xpr_statement. (xpr_expr): Rename from xpr_expression. * interface.H (Unit::global_members): Remove. * impl.H (impl::ref_sequence<>): Tidy. (impl::Decl<>): Document. (impl::unique_decl<>): Likewise. (impl::homogeneous_sequence<>): Tidy. (impl::homogeneous_sequence::push_back): New. (impl::homogeneous_scope<>): Tidy. (impl::homogeneous_scope::push_back): New. (impl::homogeneous_region::size): New. (impl::homogeneous_region::get): New. (impl::homogeneous_region::type): New. (impl::Expr_list): Make a class. Support push_back operation. (impl::heterogeneous_scope): Tidy. Document. (impl::Udt<>): Liekwise. (impl::type_factory): Likewise. (impl::Break): Document. (impl::Conitnue): Likewise. (impl::stmt_factory): Tidy. (impl::Fundecl::init): Rename from impl::Fundecl::expr. 2005-02-03 Gabriel Dos Reis * impl.H (impl::ref_sequence<>): Import members "begin", "end". (impl::Unit::make_expr_list): Take an impl::ref_sequence. (impl::Unit::make_product): Likewise. (impl::Unit::make_sum): Likewise. (impl::Unit::expr_seqs): New. (impl::Unit::type_seqs): Likewise. 2005-02-02 Gabriel Dos Reis * interface.H (Unary<>::Category): Remove. (Binary<>::Category): Likewise. (Ternary<>::Category): Likewise. 2005-02-01 Gabriel Dos Reis * Mark for 0.31. * utility.H (util::rb_tree::core<>::rotate_right): Fix thinko. (util::rb_tree::container<>::insert): Likewise. (util::slist<>::push_back): New overload. * impl.H (impl::Type<>::constraint): Remove. (impl::Type<>::Type): Likewise. (impl::Type<>::type): Likewise. (impl::elaborated_type<>::id): Change type from impl::Type_id to ipr::Name*. (impl::typed_sequence<>::constraint): New. (impl::typed_sequence<>::typed_sequence): Likewise. (impl::homogeneous_scope<>::homogeneous_scope): Likewise. (impl::homogeneous_region<>::homogeneous_region): Take additional parameter. (impl::Parameter_list::Parameter_list): Likewise. (impl::Type_id): Make a class. (impl::Mapping::Mapping): Take additional parameter. (impl::Scope_ref): Make a typedef. (impl::expr_factory::makde_ctor_name): New. (impl::expr_factory::make_dtor_name): New. (impl::expr_factory::make_type_id): New. (impl::expr_factory::make_mapping): Take additional parameter. (impl::basic_map_data::basic_map_data): Lose the last parameter. (impl::heterogeneous_scope::heterogeneous_scope): Take additional parameter. (impl::heterogeneous_region): Move member definitions out of class. (impl::Udt<>::constraint): New. (impl::Udt<>::Udt): Take additional parameter. (impl::Enum::constraint): New. (impl::Enum::Enum): Take additional parameter. (impl::Class::Class): Likewise. (impl::Block::Block): Likewise. (impl::stmt_factory::make_block): Likewise. (impl::Unit::global_ns): Change type to pointer. 2005-01-31 Gabriel Dos Reis * Mark for 0.30. * impl.H: Add more docuementation. (impl::Overload): Tidy. (impl::singleton_overload): New. (impl::unique_decl<>): New. (impl::Parameter): Derive from impl::unique_decl<>. (impl::Base_type): Likewise. (impl::Enumerator): Likewise. (impl::empty_overload): New. (impl::homogeneous_scope): Use it. (impl::heterogeneous_scope): Likewise. * utility.H (util::slist<>): Don't derive from std::allocator<>. (util::slist<>::allocate): New. (util::slist<>::deallocate): Likewise. 2005-01-27 Gabriel Dos Reis * Mark for 0.29 * utility.H (util::rb_tree): Rework. * interface.H (Node::node_id): New. (Type): Tidy. (Fundecl::throws): Remove. (Fundecl::target): Likewise. * impl.H (impl::decl_sequence::get): Make public. (impl::overload_datum::type): Make a reference. (impl::master_decl_data::master_decl_data): Take a type as parameter. (impl::Overload::lookup): New. (impl::singleton_overload::type): Declare. (impl::basic_type::accept): Define. (impl::typed_sequence): New. (impl::conversion_expr<>::Rep): New. (impl::conversion_expr<>::conversion_expr): Use it. (impl::Parameter::overload): Rename from impl::Parameter::declseq. (impl::Base_type::overload): Rename from impl::Base_type::declseq. (impl::Enumerator::overload): Rename from impl::Enumerator::declseq. (impl::homogeneous_sequence<>): Tidy. (impl::homogeneous_scope<>): Likewise. (impl::Parameter_list::type): New. (impl::redecl<>): Tidy. (impl::first_decl<>): Likewise. (impl::type_factory): Likewise. (impl::Identifier::Less): New. (impl::Literal): Tidy. (impl::Mapping): Likewise. (impl::expr_factory): Likewise. (impl::Var): Robustify. (impl::Bitfield): Likewise. (impl::Typedecl): Likewise. (impl::Fundecl): Likewise. (impl::heterogeneous_scope): Tidy. (impl::decl_factory): Robustify. (impl::Enum::accept): Remove. (impl::type_factory): New. (impl::Block): Tidy. (impl::Break): Likewise. (impl::Continue): Likewise. (impl::stmt_factory): Likewise. (impl::Unit): Likewise. 2005-01-24 Gabriel Dos Reis * utility.H: Move red-black tree implementations to namespace ipr::util::rb_tree. Rename data types. (util::rb_tree::link::as): Remove. (util::rb_tree::core::root): Tweak. * interface.H (Expr_in_parens): Rename to ... (Paren_expr): ... this. (Add): Rename to ... (Plus): ... this. (Add_assign): Rename to... (Plus_assign): ... this. (C_cast): Rename to ... (Cast): ... this. (Value): Rename to ... (Datum): ... this. (Scheme): Rename to ... (Mapping): ... this. (Sub): Rename to ... (Minus): ... this. (Sub_assign): Rename to ... (Minus_assign): ... this. (Pattern): Rename to ... (Named_map): ... this. (Overload::region): Comment out. (Namespace::Member): New. (Enum::Member): Likewise. (Class::Member): Likewise. (Union::Member): Likewise. (Negate): Rename to ... (Unary_minus): ... this. * impl.H: Rewrite from scratch. Move implementation classes to ipr::impl. 2005-01-10 Gabriel Dos Reis * traversal.H (physically_same): New. 2005-01-10 Gabriel Dos Reis * Mark for 0.28. 2005-01-10 Gabriel Dos Reis * Makefile.am: New. * Makefile.in: Likewise. * impl.H ("ipr/interface.H"): Change to ("ipr/utility.H"): Change to . * io.H ("ipr/interface.H"): Change to * traversal.H ("ipr/interface.H"): Change to . 2005-01-09 Gabriel Dos Reis * Move files from $ROOT/ipr to here. 2005-01-09 Gabriel Dos Reis * Mark for 0.27. 2005-01-09 Gabriel Dos Reis * utility.H (util::rb_tree<>::destroy_node): Tidy. (util::slist<>::~slist): Likewise. * io.H (operator<<(Printer&, const Unit&)): Declare. * interface.H (Type_expr): Rename to ... (Expr_ast_type): ... this. (Arrow_select): Rename to ... (Arrow): ... this. (Dot_select): Rename to ... (Dot): ... this. (Scheme): New data type. Implemenet paramerized expressions. (Bit_field): Rename to ... (Bitfied): ... this. (Fun_decl): Rename to ... (Fundecl): ... this. (Type_decl): Rename to ... (Typedecl): ... this. (Template_decl): Rename to ... (Pattern): ... this. (Region::decls): Rename to ... (Region::bindings): ... this. (Class::base_scope): Remove. (Parameter_list): Make simple a Node that is also a Sequence. (Visitor): Adjust member functions to reflect renaming. (Visitor::visit(const Scheme&)): New. * impl.H (Sequence_impl<>): Take an additional template argument, in case we're implementing a derived interface of Sequence<>. (Value_sequence<>): Implement Sequence<>, but stores actual values, not pointers. (Expr_impl<>::type_impl): Remove. (Expr_impl<>::constraint): Make public. (Scope_impl<>::register_decl): Remove. (Region_impl<>::scope): Remove. (Region_impl<>::decls_impl): Likewise. (Region_impl<>::span_impl): Likewise. (Region_impl<>::owner_impl): Likewise. (Region_impl<>::owned_by): Make public. (Region_impl<>::locs): Rename to ... (Region_impl<>::extent): ... this. (Region_impl<>::decl_seq): Rename to ... (Region_impl<>::decls): ... this. (Udt_impl<>::decl_impl): Remove. (Udt_imp<>::region_impl): Likewise. (Udt_impl<>::scope_impl): Likewise. (Udt_impl<>::typedecl): Make public. (Udt_impl<>::body): Likewise. (Stmt_impl<>::loc): Rename to ... (Stmt_impl<>::unit_locus): ... this. Make public. (Stmt_impl<>::attrs): Rename to ... (Stmt_impl<>::notes): ... this. (Stmt_impl<>::unit_location_impl): Remove. (Stmt_impl<>::annotation_impl): Likewise. (Decl_impl<>::spec): Make public. (Decl_impl<>::init): Likewise. (Decl_impl<>::specifiers_impl): Remove. (Decl_impl<>::scope_impl): Likewise. (Decl_impl<>::initializer_impl): Likewise. (Decl_impl<>::register_to): Likewise. (Decl_with_name<>::id): Make public. (Decl_with_name<>::pat): Likewise. (Decl_with_name<>::tmpl_args): Likewise. (Enumerator_impl::membership): Move defintion out of line. (Enumerator_impl::Enumerator_impl): Likewise. (Enum_impl::add_member): New member function. (combined_sequence::enums): Remove. (combined_sequence::classes): Likewise. (combined_sequence::namespaces): Likewise. (combined_sequence::unions): Likewise. (Parameter_list_impl): Derive from Node_impl >. (Scheme_impl): New. Implement Scheme. (Class_impl::base_scope): Remove. (Class_impl::bases_impl): Likewise. (Class_impl::bases): Move definition out of line. (Class_impl::Class_impl): Likewise. (Class_impl::baseobjs): Rename to .. (Class_impl::base_subobjects): ... this. (Block_impl::Block_impl): Move definition out of line. (Block_impl::members): Likewise. (Block_impl::body): Likewise. (Block_impl::handlers): Likewise. (Block_impl::members_impl): Remove. (Block_impl::stmts_impl): Likewise. (Block_impl::handlers_impl): Likewise. (Block_impl::zone): Rename to ... (Block_impl::region): ... this. (Block_impl::stmt_seq): Make public. (Block_impl::handler_seq): Likewise. (For_impl::For_impl): Move definition out of line. (For_impl::initializer): Likewise. (For_impl::condition): Likewise. (For_impl::increment): Likewise. (For_impl::body): Likewise. (Break_impl::Break_impl): Likewise. (Break_impl::from): Likewise. (Continue_impl::Continue_impl): Likewise. (Continue_impl::iteration): Likewise. (Fun_decl_impl): Rename to ... (Fundecl_impl): ... this. Move all in-class definitions out of line. (Fundecl_impl::params): Remove. (Var_impl::Var_impl): Move definition our of line. (Var_impl::definition): Likewise. (Alias_impl::Alias_impl): Likewise. (Field_impl::Field_impl): Likewise. (Field_impl::membership): Likewise. (Bit_field_impl): Rename to ... (Bitfield_impl): ... this. (Bitfield_impl::Bitfield_impl): Move definition out of line. (Bitfield_impl::membership): Likewise. (Bitfield_impl::size): Rename to ... (Bitfield_impl::precision): ... this. Move definition out of line. (Type_decl_impl): Rename to ... (Typedecl_impl): ... this. (Typedecl_impl::Typedecl_impl): Move definition out of line. (Typedecl_impl::definition): Likewise. (Template_decl_impl): Split into primary_pattern and specialized_pattern. Remove. (basic_pattern): New. (primary_pattern): New. Implement primary templates. (specialized_pattern): New. Implement partial and explicit specializations of templates. (Address_impl, Array_delete_impl, Complement_impl, Conversion_impl, Ctor_name_impl, Dtor_name_impl, Delete_impl, Deref_impl, Expr_in_parens_impl, Expr_list_impl, Expr_sizeof_impl, Expr_typeid_impl, Identifier_impl, Negate_impl, Not_impl, Operator_impl, Pre_increment_impl, Pre_decrement_impl, Post_increment_impl, Post_decrement_impl, Throw_impl, Type_id_impl, Type_sizeof_impl, Type_typeid_impl, Unary_plus_impl, Add_impl, Add_assign_impl, And_impl, Annotation_Impl, Array_ref_impl, Arrow_impl, Arrow_star_impl, Assign_impl, Bitand_impl, Bitand_assign_impl, Bitor_impl, Bitor_assign_impl, Bitxor_impl, Bitxor_assign_impl, C_cast_impl, Call_impl, Comma_impl, Const_cast_impl, Div_impl, Div_assign_impl, Dot_impl, Dot_star_impl, Dynamic_cast_impl, Equal_impl, Greater_impl, Greater_equal_impl, Less_impl, Less_equal_impl, Literal_impl, Lshift_impl, Lshift_assign_impl, Member_init_impl, Modulo_impl, Modulo_assign_impl, Mul_impl, Mul_assign_impl, Not_equal_impl, Or_impl, Reinterpret_cast_impl, Rshift_impl, Rshift_assign_impl, Scope_ref_impl, Static_cast_impl, Sub_impl, Sub_assign_impl, Template_id_impl, Value_impl, New_impl, Conditional_impl): Introduce as typedefs for interface implementations. (Expr_builder): Adjust signatures of all member functions. (Unit_impl::global_region): Tidy. (Unit_impl::global_scope): Likewise. (Unit_impl::make_alias): Remove. (Unit_impl::make_enumerator): Likewise. (Unit_impl::make_base_type): Likewise. (Unit_impl::make_fun_decl): Likewise. (Unit_impl::make_var): Likewise. (Unit_impl::make_field): Likewise. (Unit_impl::make_bit_field): Likewise. 2005-01-07 Gabriel Dos Reis * interface.H (Sequence<>::Position): Remove. (Sequence<>::get): Adjust signature. (Sequence<>::operator[]): Likewise. (Sequence<>::Iterator::Position): Remove. (Sequence<>::Iterator::index): Adjust type. (Parameter_list): Derive from Udt, instead of scope. * impl.H: Propagate changes in interface.H to implementation classes. (Empty_sequence::Position): Remove. (combined_sequence::decls): New data member. (combined_sequence::size): Define. (combined_sequence::get): Define. * impl.H (Enum_impl::make_enumerator): New member function. (Unit_impl::make_enumerator): Remove. (Unit_impl::make_base_type): Remove. (Unit_impl::make_alias): Move to ... (combined_sequence::make_alias): ... here. 2004-12-30 Gabriel Dos Reis * impl.H (Expr_impl<>::Expr_impl()): Initialize constraint. (combined_sequence<>): New. (Scope_impl<>): Derive from it. 2004-12-21 Gabriel Dos Reis * utility.H (util::less): New function objects. 2004-12-14 Gabriel Dos Reis * utility.H (util::check): New function. * impl.H (Checked_ptr): Remove. (Unit_impl::String): Remove. 2004-12-02 Gabriel Dos Reis * traversal.H (Missing_overrider): New function object. (missing_overrider): Turn it into the above function object. 2004-11-24 Gabriel Dos Reis * traversal.H (as): Move from interface.H. (missing_overrider): Likewise. * interface.H (Lshift): Rename from Shift_left. (Lshift_assign): Rename from Shift_left_assign. (Rshift): Rename from Shift_right. (Rshift_assign): Rename from Shift_right_assign. * impl.H: Make corresponding changes. 2004-11-23 Gabriel Dos Reis * impl.H (Multi_comment_impl): Remove. * interface.H (Multi_comment): Remove. (Visitor::visit(const Multi_comment&)): Likewise. 2004-11-21 Gabriel Dos Reis * impl.H (Comment_impl): typedef to instance of Unary_impl<>. (Multi_comment_impl): Likewise. (Type_builder): Inherit from table for Ptr_to_member. (Unit_impl): Inherit from Node_impl, instead of Unit. (Unit_impl::make_ptr_to_member): Declare. * interface.H (Commment): New datatype. (Multi_comment): Likewise. (Ptr_to_member): Likewise. (Visitor::visit(const Comment&)): Declare. (Visitor::visit(const Multi_comment&)): Likewise. (Visitor::visit(const Ptr_to_member&)): Likewise. (Unit): Derive from Node. 2004-11-09 Gabriel Dos Reis * interface.H (Type_expr): Rename from Expr_as_type. (Template_id): Rename from Specialization. (Basic_location): New datatype. (Source_location): Likewise. Derive from Basic_location. (Unit_location): Likewise. * impl.H: Likewise. 2004-11-08 Gabriel Dos Reis * interface.H (Decl::template_params): New. (Decl::template_args): Likewise. 2004-11-06 Gabriel Dos Reis * interface.H (Asm): New Decl node. (Visitor::visit(const Asm&)): New. 2004-11-01 Gabriel Dos Reis * impl.H (Var_impl::definition): Override. (Var_impl::definition_impl): New. * interface.H (Decl::definition): New. 2004-10-28 Gabriel Dos Reis * Mark for 0.26. 2004-10-28 Gabriel Dos Reis * impl.H (Expr_stmt_impl): Make a typedef to Unary_impl >. (Labeled_stmt_impl): Make a typedef to Binary_impl >. (Ctor_body_impl): Make a typedef to Binary_impl. (If_then_impl): Make a typedef to Binary_impl >. (If_then_else_impl): Make a typedef to Ternary_impl >. (Switch_impl): Make a typedef to Binary_impl > (While_impl): Make a typedef to Binary_impl >. (Do_impl): Make a typedef to Binary_impl >. (Goto_impl): Make a typedef to Unary_impl >. (Handler_impl): Make a typedef to Binary_impl >. (Union_impl): Make a typedef to Udt_impl. (Namespace_impl): Make a typedef to Udt_impl. * interface.H (Expr_stmt): Derive from Unary. (Expr_stmt::expr): Forward to Unary<>::operand. (Labeled_stmt): Derive from Binary. (Labeled_stmt::label): Forward to Binary<>::first. (Labeled_stmt::stmt): Forward to Binary<>::second. (Ctor_body): Derive from Binary. (Ctor_body::inits): Forward to Binary<>::first. (Ctor_body::block): Forward to Binary<>::second. (If_then): Derive from Binary. (If_then::condition): Forward to Binary<>::first. (If_then::then_stmt): Forward to Binary<>::second. (If_then_else): Derive from Ternary. (If_then_else::condition): Forward to Binary<>::first. (If_then_else::then_stmt): Forward to Binary<>::second. (If_then_else::else_stmt): Forward to Binary<>::third. (Switch): Derive from Binary. (Switch::condition): Forward to Binary<>::first. (Switch::body): Forward to Binary<>::second. (While): Derive from Binary. (While::condition): Forward to Binary<>::first. (While::body): Forward to Binary<>::second. (Do): Derive from Binary. (Do::condition): Forward to Binary<>::first. (Do::body): Forward to Binary<>::second. (Goto): Derive from Unary. (Goto::target): Forward to Unary<>::operand. (Handler): Derive from Binary. (Hanlder::expcetion): Forward to Binary<>::first. (Handler::body): Forward to Binary<>::second. 2004-10-27 Gabriel Dos Reis * impl.H (Unary_Factor<>::Impl): Move to enclosing scope... (Unary_impl<>): As this. 2004-10-26 Gabriel Dos Reis * interface.H (Type_id::type): Define. This is the type of the type. * impl.H (Expr_builder): Move name nodes factories to Unit_impl. (Unit_impl): Constify return types for unified node factories. (Type_builder): Expunge almost of member functions. (Stmt_builder): Move member function deifnitions to src/ipr.C (Expr_basic_impl<>): Remove. 2004-10-21 Gabriel Dos Reis * impl.H (Unit_impl::make_specialization): Declare. 2004-10-20 Gabriel Dos Reis * impl.H (Unary_factor<>::Impl::accept()): Remove. (Binary_factor<>::Impl::accept()): Likewise. (Ternary_factor<>::Impl::accept()): Likewise. (Unary_factory): Now take Cat_impl as a template template-parameter. (Unary_factory<>::Impl): Inherit from Cat_impl. (Binary_factory): Now take Cat_impl as a template template-parameter. (Binary_factory<>::Impl): Inherit from Cat_impl. (Ternary_factory): Now take Cat_impl as a template template-parameter. (Ternary_factory<>::Impl): Inherit from Cat_impl. (Node_impl<>): New. (Expr_basic_impl<>): Remove. (Expr_builder): Tidy. (Type_builder): Likewise. 2004-10-19 Gabriel Dos Reis * impl.H (Template_decl_impl): New. 2004-10-14 Gabriel Dos Reis * interface.H (Template_decl): New. (Visitor::visit(const Template_decl&)): Likewise. * impl.H (Scope_impl<>::Overload_impl::register_decl): Take the opportunity to set constraint. 2004-10-01 Gabriel Dos Reis * impl.H (Expr_builder): Add table for Expr_in_parens. (Expr_in_parens::make_expr_in_parens): New. * interface.H (Expr_in_parens): New. (Visitor::visit(const Expr_in_parens&)): Declare. (Template::parameterized): Rename from parametree. (Decl::Export): New enumerator. 2004-09-23 Gabriel Dos Reis * Mark for 0.25. * impl.H (Constructor_impl): Remove. (Destructor): Likewise. (Unit_impl::make_constructor): Likewise. (Unit_impl::make_destructor): Likewise. (Expr_builder): Inherit also from Unary_fatctory and Unary_fatctory. (Expr_builder::make_ctor_name): New. (Expr_builder::make_dtor_name): Likewise. (Unit_impl::make_ctor_name): Likewise. (Unit_impl::make_dtor_name): Likewise. * interface.H (Constructor): Remove. (Destructor): Likewise. (Visitor::visit(const Constructor&)): Likewise. (Visitor::visit(const Destructor&)): Likewise. (Dtor_name): New node class. (Ctor_name): Likewise. For consistency with destructor. (Visitor::visit(const Dtor_name&)): Declare. (Visitor::visit(const Ctor_name&)): Likewise. 2004-09-20 Gabriel Dos Reis * traversal.H: New file. * interface.H (Visitor::visit(const Name&)): Don't make pure. 2004-09-20 Gabriel Dos Reis * Mark for 0.24 2004-09-20 Gabriel Dos Reis * impl.H (Decl_impl<>::register_to): New. 2004-09-15 Gabriel Dos Reis * impl.H (Unit_impl::make_fun_decl): Update signature. (Unit_impl::make_constructor): Likewise. (Unit_impl::make_destructor): New. 2004-09-13 Gabriel Dos Reis * impl.H (Type_builder): Add table for Sum too. (Type_builder::make_sum): New. (Type_builder::make_function): Update. (Unit_impl::make_sum): New. (Unit_impl::make_function): Update. * interface.H (Sum): New type node. (Function): Use it for exception specification list. (Visitor::visit(const Sum&)): Declare. 2004-08-31 Gabriel Dos Reis * Mark for 0.23. 2004-08-30 Gabriel Dos Reis * interface.H (Visitor::visit(const Exnumerator&)): Add. 2004-08-27 Gabriel Dos Reis * interface.H (Specialization): Derive from Name. 2004-08-26 Gabriel Dos Reis * impl.H (Type_builder): Inherirt from factory for Decltype too. (Type_builder::make_decltype): New. (Unit_impl::make_decltype): New. (Expr_builder::make_identifier): Return Expr_impl*. (Expr_builder::make_operator): Return Expr_impl*. (Expr_builder::make_conversion): Return Expr_impl*. (Expr_builder::make_scope_ref): Return Expr_impl*. (Unit_impl::make_identifier): New. (Unit_impl::make_operator): Likewise. (Unit_impl::make_conversion): Likewise. (Unit_impl::make_scope_ref): Likewise. * interface.H (Decltype): New node class. (Visitor::visit(const Decltype&)): New. 2004-08-25 Gabriel Dos Reis * Mark for 0.22. 2004-08-25 Gabriel Dos Reis * interface.H (Alias): Add lazy evaluation of type(). It is always that of the initializer. 2004-08-24 Gabriel Dos Reis * impl.H (Expr_builder::make_scope_ref): Adjust return-type to Scope_ref*. * interface.H (Scope_ref): Derive from Name. 2004-08-15 Gabriel Dos Reis * Mark for 0.21. 2004-08-14 Gabriel Dos Reis * interface.H (Block::stmts): Revert from previosu change. Return a Sequence instead of Sequence. (Labeled_stmt::stmt): Revert from previous change. Return an Expr, not Stmt. (If_then::then_stmt): Likewise. (If_then_else::then_stmt, If_then_else::else_stmt): Likewise. (While::body): Likewise. (Do_while::body): Likewise. (For::body): Likewise. (Break::from, Continue::iteration): Likewise. * impl.H (Labeled_stmt_impl): Reflect changes in ipr/initerface.H. (Block_impl): Likewise. (If_then_impl): Likewise. (If_then_else_impl): Likewise. (Switch_impl): Likewise. (While_impl): Likewise. (Do_while_impl): Likewise. (For_impl): Likewise. (Break_impl): Likewise. (Continue_impl): Likewise. (Stmt_builder::make_do_while): Likewise. (Stmt_builder::make_if_then): Likewise. (Stmt_builder::make_switch): Likewise. (Stmt_builder::make_labeled_stmt): Likewise. (Stmt_builder::make_switch): Likewise. (Stmt_builder::make_if_then_else): Likewise. (Stmt_builder::make_for): Likewise. (Stmt_builder::make_do): Rename from make_do_while. * interface.H: Simplify Do_while to Do. * impl.H: Likewise. 2004-08-10 Gabriel Dos Reis * Mark for 0.20. 2004-08-10 Gabriel Dos Reis * interface.H (Visitor::visit(const Udt&)): New. * impl.H (Block_impl::decls): Make it an integral part of the block. (Block_impl::Block_impl): Also take a Unit_impl parameter. (Unit_impl::make_block): New. Forward to Stmt_builder::make_block. 2004-08-07 Gabriel Dos Reis * impl.H (Ctor_body_impl): Rename from Function_body_impl. Implement Ctor_body interface. (Quaternary_factory): Remove. (Expr_stmt_impl): New. Implement Expr_stmt interface. (Labeled_stmt_impl): New. Implement Labeled_stmt interface. (Block_impl): New. Implement Block interface. (If_then_impl): New. Implement If_then interface. (If_then_else_impl): New. Implement If_then_else interface. (Switch_impl): New. Implement Switch interface. (While_impl): New. Implement While interface. (Do_while_impl): New. Implement Do_while interface. (For_impl): New. Implement For interface. (Break_impl): New. Implement Break interface. (Continue_impl): New. Implement Continue interface. (Goto_impl): New. Implement Goto interface. (Return_impl): New. Implement Return interface. (Handler_impl): New. Implement Handler interface. (Stmt_builder): Rework. (Unit_impl::make_handler_seq): Remove. (Unit_impl::hander_seqs): Likewise. * interface.H (Ctor_body, Expr_stmt): New interfaces. (Function_body): Remove. (Labeled_stmt, Block, If_then, If_then, If_then_else, Switch, While, Do_while, For, Break, Continue, Goto, Return, Handler): Derive directly from Stmt. (Visitor::visit(const Function_body&)): Remove. (Visitor::visit(const Ctor_body&)): New. (Visitor::visit(const Expr_stmt&)): Likewise. 2004-08-03 Gabriel Dos Reis * Mark for 0.19 2004-07-16 Gabriel Dos Reis * Mark for 0.18 2004-07-16 Gabriel Dos Reis * interface.H (Member_init): Add convenient functions. * impl.H (Expr_builder<>): Add factory for Member_init. 2004-07-15 Gabriel Dos Reis * Mark for 0.17 2004-07-15 Gabriel Dos Reis * interface.H (Type_id): New datatype. Interface for type-ids. (Expr_list): Now a Unary<> expression. Add subscripting operation. (Product): Add subscripting operation. (Name_as_expr): Remove. (Visitor::visit(const Name_as_expr&)): Remove. (Visitor::visit(const Type_id&)): New. * impl.H (Type_impl<>): Tidy. (Scope_impl<>): Temporarily remove protected copy-constructor (Expr_list_impl<>): Remove. (Decl_with_name<>): New datatype. (Parameter_impl): Derive from it. (Enumerator_impl): Likewise. (Fun_decl_impl): Likewise. (Alias_impl): Likewise. (Var_impl): Likewise. (Type_impl): All types now have names. (Type_decl_impl): Likewise. (Field_impl): Likewise. (Bit_field_impl): Likewise. (Type_builder): Add factory for Product. (Expr_builder): Adjust to make name as expressions. Add factory for Expr_list. (Unit_impl): Tidy. 2004-07-08 Gabriel Dos Reis * Mark for 0.16 2004-07-08 Gabriel Dos Reis * impl.H (Unary_fatctory<>::make): Overload with a version that takes the type of the expression. (Binary_factor<>::make): Likewise. (Ternary_factor<>::make): Likewise. (Stmt_impl<>::annotation): Move from Expr_impl<>. (Stmt_impl<>::annotation_impl): Likewise. (Unit_impl::make_pointer): New. (Unit_impl::make_reference): Likewise. (Unit_impl::make_expr_as_type): Likewise. (Unit_impl::make_array): Likewise. (Unit_impl::make_template): Likewise. (Unit_impl::make_function): Likwise. (Unit_impl::built_in_type): Likewise. (Unit_impl::Builtin<>): Remove. (Unit_impl::CV_fiber): Tidy to work-around bugs in GCC-3.5.x (Unit_impl::Typename): New. 2004-07-07 Gabriel Dos Reis * interface.H (Bit_field): New node. (Field): Likewise. (Visitor::visit(const Field&)): New. (Visitor::visit(const Bit_field&)): Likewise. * impl.H (Udt_impl<>::decl_impl): Fix const-correctness thinko. (Field_impl): New. Implement Field interface. (Bit_field): New. Implement Bit_fiekd interface. (Unit_impl::make_field): New. (Unit_impl::make_bit_field): Likewise. 2004-07-05 Gabriel Dos Reis * impl.H: Fix thinko in last change. (Unit_impl::make_function): Overload to take only parameter-types and return type. 2004-07-05 Gabriel Dos Reis * Mark for 0.15. 2004-07-05 Gabriel Dos Reis * interface.H (operator|=(Type::Qualifier&, Type::Qualifier)): New. (operator^=(Type::Qualifier&, Type::Qualifier)): Likewise. (operator&=(Type::Qualifier&, Type::Qualifier)): Likewise. (operator|=(Decl::Specifier&, Decl::Specifier)): Likewise. (operator^=(Decl::Specifier&, Decl::Specifier)): Likewise. (operator&=(Decl::Specifier&, Decl::Specifier)): Likewise. * impl.H (Unit_impl::make_subscope): Move from Scope_impl<>. (Unit_impl::make_parameter_list): Likewise. (Scope_impl<>::enclosing_impl): Remove. (Scope_impl<>::unit_impl): Remove. 2004-07-04 Gabriel Dos Reis * Mark 0.14 2004-07-04 Gabriel Dos Reis * interface.H (as): Move from ipr.C. (Template): Use Parameter_list. (operator|(Decl::Specifier, Decl::Specifier)): New. (operator^(Decl::Specifier, Decl::Specifier)): New. (operator&(Decl::Specifier, Decl::Specifier)): New. (Unit::global_namespace): Rename from global_scope. (Unit::global_members): New. * impl.H (Type_builder::make_template): Take Parameter_list as first argument. (Unit_impl::global_namespace): Rename from global_scope. 2004-07-02 Gabriel Dos Reis * Mark for 0.13. 2004-07-02 Gabriel Dos Reis * io.H: Reimplement from scratch. * interface.H (Conditional): New node. (Type_decl): Likewise. (Visitor::visit(const Conditional&)): New. (Visitor::visit(const Type_decl&)): Likewise. * impl.H (Name_builder): Merge with Expr_builder. Remove. (Expr_builder): Unify strings before making a Literal. (Unit_impl): Adjust. (Unit_impl::make_type_decl): New. (Type_decl_impl): New. Implement Type_decl interface. 2004-07-01 Gabriel Dos Reis * Mark for 0.12. 2004-07-01 Gabriel Dos Reis * interface.H (Add_assign): New. (Bitand_assign): Likewise. (Bitor_assign): Likewise. (Bitxor_assign): Likewise. (Div_assign): Likewise. (Member_init): Likewise. (Modulo_assign): Likewise. (Mul_assign): Likewise. (Shift_left_assign): Likewise. (Shift_right_assign): Likewise. (Sub_assign): Likewise. (Udt<>): Simplify. (Visitor::visit(const Add_assign&)): New. (Visitor::visit(const Bitand_assign&)): Likewise. (Visitor::visit(const Bitor_assign&)): Likewise. (Visitor::visit(const Bitxor_assign&)): Likewise. (Visitor::visit(const Div_assign&)): Likewise. (Visitor::visit(const Member_init&)): Likewise. (Visitor::visit(const Modulo_assign&)): Likewise. (Visitor::visit(const Mul_assign&)): Likewise. (Visitor::visit(const Shift_left_assign&)): Likewise. (Visitor::visit(const Shift_right_assign&)): Likewise. (Visitor::visit(const Sub_assign&)): Likewise. * impl.H (Udt_impl<>): Simplify. (Class_impl): Tidy. (Union_impl): Likewise. (Namespace_Impl): Likewise. (Enum_impl): Likewise. (Destructor_impl): New. Implement Destructor interface. (Alias_impl): New. Implement Alias interface. (Name_builder::make_conversion): Fix typo. (Expr_builder): Make computed assignment nodes. (Unit_impl::GlobalNamespace::bases): Remove. * io.H (printer::Base): Rework. Move common stubs from derived classes here. Start infrastructure for better pretty-printing. 2004-06-29 Gabriel Dos Reis * Mark for 0.11 2004-06-29 Gabriel Dos Reis * interface.H (Specialization): Rename from Instantiation. Derive from Expr. (Scope_ref): Rename from Qualified. Derive from Expr. (Object_type): Remove. (Pointer): Adjust. (Array): Likewise. (Expr_as_type): New interface. (Udt): Simplify. (Namespace): Adjust. (Class): Likewise. (Enum): Likewise. (Union): Likewise. (Scope_select): Remove. (Visitor::visit(const Instantiation&)): Remove. (Visitor::visit(const Qualified&)): Likewise. (Visitor::visit(const Scope_select&)): Likewise. (Visitor::visit(const Expr_as_type&)): New. (Unit::): Tidy. * impl.H (Object_type_impl): Remove. (Udt_impl): Tidy. (Enum_impl): Likewise. (Namespace_impl): Likewise. (Var_impl): New. Implement the Var interface. (Name_builder): Rework. (Type_builder): Build Expr_as_type. Tidy. (Expr_builder): Build Specialization and Scope_ref. (Unit_impl): Tidy. * io.H: Reflect changes in interface.H. 2004-06-29 Gabriel Dos Reis * interface.H: #include . (Product): New interface. (Expr_list): Likewise. (Stmt): Rename from Statement. (Scope): Derive from Expr. (Udt): Rename from User_defined_type. (Class): Adjust. (Union): Likewise. (Enum): Likewise. (Namespace): Likewise. (Function): Use Product for parameter-type-list. (Template): Likewise. (Call): Use Expr_list for argument-list. (Object_construction): Likewise. (New): Use Expr_list for placement and initializer list. (Function_body): Make an Expr. (Enumerator::value): Remove. (Alias::has_initializer): Remove. (Alias::aliasee): Likewise. (Base_type::has_initializer): Remove. (Parameter::has_default): Likewise. (Template_param): Likewise. (Constructor): New interface. (Copy_assignment): Likewise. (Visitor::visit(const Scope&)): Make non pure. (Visitor::visit(const Product&)): New. (Visitor::visit(const Expr_list&)): Likewise. * impl.H: #include . (Expr_impl<>): Tidy. (Parameter_data): Remove. (Scope_impl<>): Simplify. (Product_impl): New datatype. Implement Product interface. (Udt_imp<>): Rename from User_defined_type_impl<>. (Class_impl): Simplify. (Union_impl): Likewise. (Enum_impl): Likewise. (Namespace_impl): Likewise. (Expr_list_impl): New data type. Implement Expr_list interface. (Stmt_impl): Rename from Statement_impl. (Decl_impl<>): Simplify. (Fun_decl_impl): Likewise. (Enumerator_impl): New. Implement Enumerator interface. (Constructor_impl): New. Implement Constructor interface. (Parameter_list_impl): New. Implement Parameter_list interface. (Name_builder): New. (Type_builder); Likewise. (Expr_builder): Likewise. (Stmt_builder): Likewise. (Unit_impl): Reimplement. * io.H (printer::xpr::Declaration::visit(const Parameter_list&)): New. (printer::xpr::Declaration::visit(const Enumerator&)): New. (printer::xpr::Declaration::visit(const Base_type&)): New. (printer::xpr::Declaration::visit(const Scope&)): Remove. (printer::xpr::Expression::visit(const Product&)): New. (printer::xpr::Expression::visit(const Expr_list&)): New. 2004-06-19 Gabriel Dos Reis * interface.H: Re-architecture. (Enumerator): New Decl node. (Base_type): Likewise. (Sequence::size): Return int. (Unary<>): New class template. (Binary<>): Likewise. (Ternary<>): Likewise. (Quaternary<>): Likewise. (Annotation): Derive from Binary. (Identifier): Derive from Unary. (Operator): Likewise. (Conversion): Derive from Unary. (Instantiation): Derive from Binary >. (Qualified): Derive from Binary. (Pointer): Derive from Unary. (Reference): Derive from Unary. (Array): Derive from Binary. (Function): Derive from Ternary, Type, Sequence >. (Template): Derive from Binary, Expr>. (Literal): Derive from Binary. (Unary_expr<>): Remove. (Binary_expr<>): Likewise. (Name_expr<>): Derive from Unary. (Pre_increment): Derive from Unary. (Post_increment): Likewise. (Pre_decrement): Likewise. (Post_decrement): Likewise. (Expr_typeid): Likewise. (Expr_sizeof): Likewise. (Type_typeid): Derive from Unary. (Type_sizeof): Likewise. (Deref): Derive from Unary. (Address): Likewise. (Unary_plus): Likewise. (Negate): Likewise. (Not): Likewise. (Complement): Likewise. (Delete): Likewise. (Array_delete): Likewise. (Throw): Likewise. (Literal); Derive from Binary. (Cast_expr): Derive from Binary. Make non-template. (Mul): Derive from Binary. (Div): Likewise. (Modulo): Likewise. (Add): Likewise. (Sub): Likewise. (Shift_left): Likewise. (Shift_right): Likewise. (Less): Likewise. (Greater): Likewise. (Less_equal): Likewise. (Greater_equal): Likewise. (Equal): Likewise. (Not_equal): Likewise. (Bit_and): Likewise. (Bit_or): Likewise. (Bit_xor): Likewise. (And): Likewise. (Or): Likewise. (Comma): Likewise. (Assign): Likewise. (Call): Likewise. (Object_construction): Likewise. (Member_select): Likewise. Make non-template. (New): Derive from Ternary, Object_type, Sequence >. (Labeled_stmt): Derive from Binary. (Block): Derive from Ternary, Sequence >. (Function_body): Derive from Binary, Block>. (If_then): Derive from Binary. (If_then_else): Derive from Ternary. (Switch): Derive from Binary. (While): Likewise. (Do_while): Likewise. (For): Derivee from Quaternary. (Break): Derive from Unary. (Continue): Likewise. (Goto): Likewise. (Return): Likewise. (Handler): Derive from Binary. (Visitor::visit(const Base_type&)): New member function. Remove unnecessary codes. * impl.H (Sequence_impl<>::size): Return int. (Identifier_impl): Remove again. (Operator_impl): Likewise. (Conversion_impl): Likewise. (Instantiation_impl): Likewise. (Qualified_impl): Likewise. (Scope_impl<>::operator[](const Name&)): Suspend definition. (Parameter_data): New datatype. (Scope_impl<>::make_block()): Remove. (Scope_impl<>::make_namespace()): Likewise. (Scope_Impl<>::make_parameter): Likewise. (Scope_impl<>::make_fun_decl(const Name&)): Likewise. (Scope_impl<>::Overload_impl): Move to Unit_impl. (Scope_impl<>::Binding): Remove. (Scope_Impl<>::bindings): Likewise. (Type_impl<>::Type_impl(const Type_impl&)): Implement. Make protected. Add FIXME-comments. (Type_impl<>::operator=(const Type_impl&)): Add FIXME-comments. (Unary_expr_impl<>): Remove. (Binary_expr_impl<>): Likewise. (New_impl): Likewise. (Block_impl): Likewise. (Unary_factor<>): New datatype. (Binary_factor<>): Likewise. (Ternary_factor<>): Likewise. (Quaternary_factor<>): Likewise. (Fun_decl_impl): Comment out. (Unit_impl::make_identifier): Reimplement. (Unit_impl::make_operator): Likewise. (Unit_impl::make_conversion): Likewise. (Unit_impl::make_instantiation): Likewise. (Unit_impl::make_qualified): Likewise. (Unit_impl::make_pointer): Likewise. (Unit_impl::make_reference): Likewise. (Unit_impl::make_array): Likewise. (Unit_impl::make_function): Likewise. (Unit_impl::make_if_then_else): Remove. (Unit_impl::make_for): Remove. (Unit_impl::make_handler): Likewise. (Unit_impl::make_literal): Reimplement. (Unit_impl::make_annotation): Likewise. (Unit_impl::make_unary_expr): Likewise. (Unit_impl::make_binary_expr): Likewise. (Unit_impl::make_ternary_expr): New. (Unit_impl::make_unary_stmt): Likewise. (Unit_impl::make_binary_stmt): New. (Unit_impl::make_ternary_stmt): New. (Unit_impl::make_quaternary_stmt): New. (Unit_impl::Overload_impl): Move from Scope_impl<>::. (Unit_impl::TypeFiber): Simplify. Rename to CV_fiber. (Unit_impl::ids): Change type to Unary_factory. (Unit_impl::ops): Change type to Unary_factory. (Unit_impl::convs): Change type to Unary_factory. (Unit_impl::insts): Change type to Binary_factory >. (Unit_impl::quals): Change type to Binary_factory. (Unit_impl::pointers): New data member. (Unit_impl::references): Likewise. (Unit_impl::arrays): Likewise. (Unit_impl::functions): Likewise. (Unit_impl::UnaryExprs): New datatype. (Unit_impl::unary_exprs: New data member. (Unit_impl::BinaryExprs): New datatype. (Unit_impl::binary_exprs: New data member. (Unit_impl::TernaryExprs): New datatype. (Unit_impl::ternary_exprs: New data member. (Unit_impl::QuaternaryExprs): New datatype. (Unit_impl::quaternary_exprs: New data member. * io.H (Expression::visit(const Name_expr&)): Correct spelling. (Expression::visit(const Throw&)): New member function. 2004-06-12 Gabriel Dos Reis * Mark for 0.10. 2004-06-12 Gabriel Dos Reis * impl.H (Scope_impl): Turn into a template class parameterized by the node declaration kind, with default to Decl. Inherit from ... (Scope_impl_base<>): ... this. New class template. (Name_binding): Remove. (Name_binding::Overload_impl): Move to Scope_impl<>. (Name_traits): Remove. (Name_rep): Likewise. (Name_impl): Likewise. (Identifier_impl): Resurect. (Operator_impl): Likewise. (Conversion_impl): Likewise. (Instantiation_impl): Likewise. (Qualified_impl): Likewise. (Expr_type<>): Take a second template parameter, the type of the expression with default to Type. (Type_type<>): Likewise. (Signature_impl<>): Remove. (Statement_impl<>): Likewise. (Decl_impl<>): Likewise. (Ternay_expr<>): New class template. (Unit_impl): Tidy. (make_decl): Remove. * interface.H (Sequence<>): Provide an STL-style looking. (Name::value): Remove. (Name::scope): Likewise. (Instantiation::scope): Likewise. (Qualified::scope): Likewise. (Scope::operator[](const Name&)): Return an Overload. (Overload::operator[](const Type&)): Return a Sequence. (Signature): Remove. (Function::signature): Return a Sequence. (Member): Remove. (Template::signature): Return a Sequence. (Name_expr): Derived from Unary_expr<>. (New): Derive from Ternary_expr. (If_then_else): Likewise. (Return::has_value): Remove. (Decl::master): Return a Sequence. (Member_decl): Remove. (Enumerator): New node class. (Alias::aliasee): Return an Expr, the initializer. (Fun_decl::signature): Return a Sequence. (Visitor::visit(const Signature&)): Remove. (Visitor::visit(const Member&)): Likewise. (Visitor::visit(const Member_decl&)): Likewise. (Unit::null_expr): New member function. * io.H (printer::Base::operator<<): New function. Forward to standard format operations. 2004-06-05 Gabriel Dos Reis * interface.H (Signature): Remove. (Function::signature): Adjust return type. Return Sequence. (Template::signature): Likewise. (Fun_decl::signature): Likewise. (Fun_decl::parameters): Make pure virtual. (Visitor::visit(const Signature&)): Remove. * impl.H (Scope_impl::make_fun_decl): Tidy. (Signature_impl): Likewise. (Parameter_impl): Tidy. (Fun_decl_impl::ParameterList): Lose base class Signature. (Fun_decl_impl::DeclType::signature): Adjust return type. (Fun_decl_impl::signature): Likewise. (Unit_impl::make_signature): Remove. (Unit_impl::make_function): Tidy. (Unit_impl::sigs): Lose. 2004-05-23 Gabriel Dos Reis * interface.H (Unary_expr<>): Take a third parameter as the category of the expression. Inherit from it. (Binary_expr<>): Take Category as fourth parameter. Inherit from it. (Labeled_stmt): Inherit from Binary_expr<>. (Block): Likewise. (Function_body): Likewise. (If_then): Likewise. (Swicth): Likewise. (While): Likewise. (Do_while): Likewise. (Break): Inherit from Unary_expr<>. (Continue): Likewise. (Goto): Likewise. (Return): Likewise. (Handler): Inherit from Binary_expr<>. * io.H (ipr::printer): New namespace. (Printer_base): Move there. Rename to Base. (ipr::printer::xpr): New namespace. (Declaration_printer): Move there. Rename to Declaration. (Statement_printer): Move there. Rename to Statement. (Expression_printer): Move there. Rename to Expression. * interface.H: Rename Progam to Unit, throughout. (Name::scope): New member function. (Instantiation): Override Name::scope. (Qualified): Likewise. (Name_expr): Document. (Binary_expr<>): Take two more template-arguments. (Call): Derive from Binary_expr<>. (Object_construction): Likewise. (Template::parameters): Remove. (Template::signature): New member function. (Visitor::visit(const Assign&)): Declare. (Template): Derived from Type instead of Expr. (Template::result): New member function. (Class_template): Remove. (Fun_decl_template): Remove. (Return::type): Override Expr::type. * impl.H: Likewise. (Call_impl): Remove. (Object_construction_impl): Likewise. 2004-05-17 Gabriel Dos Reis * Mark for 0.08. 2004-05-17 Gabriel Dos Reis * interface.H (Type::has_name): Remove. (Type::name): Return a Name, not an Identifier. (operator|): Overload for Type::Qualifier. (operator&): Likewise. (operator^): Likewise. (Cast_expr<>): New class template. Derive from Binary_expr<> (Dynamic_cast): Derived from Cast_expr<>, not Unary_expr<>. (Static_cast): Likewise. (Const_cast): Likewise (Reinterpret_cast): Likewise. (Cast): Likewise. (Labeled_stmt::label): Return an Expr. (Function_body): Make only a statement, not a sequence of Statements. (Block): Make a sequence of Exprs too. (Parameter::has_default): Alias for Decl::has_initializer. (Parameter::default_value): Alias for Decl::initializer. * impl.H (Checked_ptr<>): Tidy. (Sequence_impl<>): Likewise. (Name_binding): New class. (Name_traits<>): Likewise. (Name_rep<>): Likewise. Use the above. (Name_impl<>): Derive from both Name_binding and Name_rep<>. Tidy. (Expr_impl<>): Tidy. (Overload_impl): Likewise. (Type_impl<>): Likewise. (Object_type_impl<>): Likewise. (User_defined_type_impl<>): Likewise. (Namespace_impl): Likewise. (Unary_plus_impl<>): Likewise. (Member_selection_impl<>): Remove. (Binary_expr_impl<>): Tidy. (New_impl): Likewise. (Statement_impl<>): Likewise. (Block_impl): Likewise. (Handler_impl): Likewise. (Decl_impl<>): Likewise. (Parameter_impl): Likewise. (Program_impl): Likewise. 2004-05-14 Gabriel Dos Reis * interface.H (Type::All): New enumerator. Collect all type-qualifier values. * impl.H (Program_impl::TypeFiber): New class. (Program_impl::typebundle): New data member. Cache already computed "fiber" values for types. (Program_impl::make_cv_qualified): New member function. 2004-05-13 Gabriel Dos Reis * Mark for 0.07. 2004-05-13 Gabriel Dos Reis * io.H: Rework pretty-printers. * interface.H (Sequence<>::Position): New nested class. (Sequence<>::operator[]): Take a Position, instead of int. (Sequence<>::Iterator::index): Change to type Sequence<>::Position. (Scope): Derive also from Node. (Scope::accept): Remove, since inherited from Node. (Expr::attributes): Rename to Expr::annotation. (Member_selection<>): Derive from Binary_expr<>. (Labeled_stmt::body): Rename to ...::stmt. (Function_body): Derive from both Statement and Sequence. (Function_body::members): Remove. (For::has_initializer): Likewise. (For::has_condition): Likewise. (For::has_increment): Likewise. (Continue::iter_stmt): Rename to ...::iteration. (Goto::target): Rename an Expr. (Decl::has_name): Remove. (Fun_decl): Rework. * impl.H (Pointer_impl): Remove. (Reference_impl): Likewise. (Array_impl): Likewise. (Var_impl): Likewise. (Identifier_impl): Likewise. (Conversion_impl): Likewise. (Qualified_impl): Likewise. (Literal_impl): Likewise. (Name_expr_impl): Likewise. (Pre_increment_impl): Likewise. (Post_increment_impl): Likewise. (Pre_decrement_impl): Likewise. (Post_decrement_impl): Likewise. (Dynamic_cast_impl): Likewise. (Static_cast_impl): Likewise. (Reinterpret_cast_impl): Likewise. (Const_cast_impl): Likewise. (Cast_impl): Likewise. (Expr_typeid_impl): Likewise. (Type_typeid_impl): Likewise. (Expr_sizeof_impl): Likewise. (Type_sizeof_impl): Likewise. (Deref_impl): Likewise. (Address_impl): Likewise. (Unary_plus_impl): Likewise. (Negate_impl): Likewise. (Not_impl): Likewise. (Complement_impl): Likewise. (Delete_impl): Likewise. (Array_delete_impl): Likewise. (Unary_expr_impl<>::Unary_expr_impl): Tidy signature. (Dinary_expr_impl<>::Unary_expr_impl): Likewise. (Dot_star_impl): Remove. (Arrow_star_impl): Remove. (Mul_impl): Remove. (Div_impl): Remove. (Add_impl): Remove. (Sub_impl): Remove. (Shift_left_impl): Remove. (Shift_right_impl): Remove. (Less_impl): Remove. (Greater_impl): Remove. (Less_equal_impl): Remove. (Greater_equal_impl): Remove. (Equal_impl): Remove. (Not_equal_impl): Remove. (Bit_and_impl): Remove. (Bit_xor_impl): Remove. (Bit_or_impl): Remove. (And_impl): Remove. (Or_impl): Remove. (Comma_impl): Remove. (Assign_impl): Remove. (Array_select_impl): Remove. (Dot_select_impl): Remove. (Arrow_select_impl): Remove. (Scope_select_impl): Remove. (Member_select_impl): Tidy. (Checked_ptr<>): New class. (Sequence_impl<>::get): Adjust signature. (Name_impl<>::value_impl): Returns a Checked_ptr<>. (Name_Impl<>::error_if_bound): Remove. (Name_impl<>::binding): Declare as Checked_ptr. (Expr_impl<>::attributes): Rename to Expr_impl<>::annotation. (Expr_impl<>::constaint): Declare as Checked_ptr. (Expr_impl<>::type_impl): Returns a Checked_ptr&. (Scope_impl::enclosing_scope): Returns a Checked_ptr. (Scope_impl::lookup_identifier): Returns a Name_impl*. (Scope_impl::lookup_operator): Returns a Name_impl*. (Scope_impl::lookup_conversion): Returns a Name_impl*. (Scope_impl::make_identifier): Returns a Name_impl*. (Scope_impl::make_operator): Returns a Name_impl*. (Scope_impl::make_conversion): Returns a Name_impl*. (Scope_impl::make_instantiation): Returns a Name_impl*. (Scope_impl::make_qualified): Returns a Name_impl*. (Scope_impl::make_block): New more overloads. (Scope_impl::make_var): New overloads. (Scope_impl::make_fun_decl): Likewise. (Scope_impl::get): Adjust type. (Signature_impl): New class. (Function_impl): Rework. (Labeled_stmt_impl): Remove. (Block_impl): Tidy. (Function_body_impl): Rework. (If_then_impl): Remove. (If_then_else_impl): Remove. (Switch_impl): Remove. (While_impl): Remove. (Do_while_impl): Remove. (For_impl): Remove. (Break_impl): Remove. (Continue_impl): Remove. (Goto_impl): Remove. (Return_impl): Remove. (Decl_impl<>::has_name): Remove. (Decl_impl<>::init): Change type to Checked_ptr (Var_impl): Remove. (Parameter_impl): Tidy. (Fun_decl_impl): Rework. (Program_impl::make_function): New. (Program_impl::make_labeled_stmt): Likewise. (Program_impl::make_if_then): Likewise. (Program_impl::make_if_then_else): Likewise. (Program_impl::make_switch): Likewise. (Program_impl::make_while): Likewise. (Program_impl::make_do_while): Likewise. (Program_impl::make_for): Likewise. (Program_impl::make_break): Likewise. (Program_impl::make_continue): Likewise. (Program_impl::make_goto): Likewise. (Program_impl::make_return): Likewise. (Program_impl::make_handler): Likewise. (Program_impl::make_expr_typeid): Likewise. (Program_impl::make_type_typeid): Likewise. (Program_impl::make_expr_sizeof): Likewise. (Program_impl::make_type_sizeof): Likewise. (Program_impl::make_name_expr): Likewise. (Program_impl::make_unary_expr): Likewise. (Program_impl::make_binary_expr): Likewise. (Program_impl::make_member_selection): Likewise. (Program_impl::make): Remove. (Program_impl::IdMap): Likewise. (Program_impl::Namemap): Likewise. (Name_impl<>::value): Tidy. (Name_impl<>::with): Tidy. (Expr_impl<>::type): Tidy. (make_decl): Tidy. 2004-05-05 Gabriel Dos Reis * interface.H (Fun_decl::params): New member function. (Function::domain): Rename to Function::source. (Signature): New class. (Visitor::visit(const Signature&)): New. * impl.H (Fun_decl_impl::params): Likewise. 2004-05-05 Gabriel Dos Reis * Mark 0.06. 2004-05-05 Gabriel Dos Reis * impl.H (Program_impl::Class): New member function. (Program_impl::Union): Likewise. (Program_impl::Enum): Likewise. (Program_impl::Namespace): Likewise. (Program_impl::classtype): New data member. (Program_impl::uniontype): Likewise. (Program_impl::enumtype): Likewise. (Program_impl::namespacetype): Likewise. 2004-05-03 Gabriel Dos Reis * impl.H (Name_impl<>::with): New member. Forward to... (make_decl): ...this function. New. (missing_overrider): Declare. (Identifier_impl<>::with): Remove. (Operator_impl<>::with): Likewise. (Conversion_impl<>::with): Likewise. (Instantiation_impl<>::with): Likewise. (Qualified_impl<>::with): Likewise. (Decl_impl<>::Decl_impl): Revert previous change. Take declname by reference. (Program_impl::make_decl<>): New member. (Type_impl<>::has_name): Override Type::has_name. Default to false. (Program_impl::ellipsis_impl): Rename tp Program_impl::Ellipsis. 2004-05-01 Gabriel Dos Reis * Mark for 0.05. 2004-05-01 Gabriel Dos Reis * interface.H (Visitor::visit(const Node&)): Make pure. (Visitor::visit(const Name&)): Likewise. (Visitor::visit(const Type&)): Likewise. (Visitor::visit(const Expr&)): Likewise. (Visitor::visit(const Statement&)): Likewise. (Visitor::visit(const Decl&)): Likewise. (Type::is_named): Rename to Type::has_name. (Decl::has_name): New member. * impl.H (User_defined_type_impl<>::is_named): Rename to ...::has_name. (Program_impl::Builtin<>::is_named): Likewise. (Decl_impl<>::Decl_impl): Take decl-name by pointer, not reference. 2004-04-25 Gabriel Dos Reis * io.H (Sequence_printer): New. (Expression_printer): Override more visit() member functions for the purpose of pretty-printing more kinds of expression. 2004-04-08 Gabriel Dos Reis * interface.H (Type): Add members is_named(), name() to support named types. * impl.H (User_defined_type_impl<>): Implement. (Program_impl::Builtin<>): New. Implement built-in types. 2004-03-22 Gabriel Dos Reis * impl.H (Unary_expr_impl): Have an explicit constructor that takes an expression as sole argument. (Pre_increment_impl): Likewise. (Post_increment_impl): Likewise. (Pre_decrement_impl): Likewise. (Post_decrement_impl): Likewise. (Expr_typeid_impl): Likewise. (Type_typeid_impl): Likewise. (Expr_sizeof_impl): Likewise. (Type_sizeof_impl): Likewise. (Deref_impl): Likewise. (Address_impl): Likewise. (Unary_plus_impl): Likewise. (Negate_impl): Likewise. (Not_impl): Likewise. (Complement_impl): Likewise. (Delete_impl): Likewise. (Array_delete_impl): Likewise. (Binary_expr_impl): Declare constructor taking two Exprs. (Dot_star_impl): Likewise. (Arrow_star_impl): Likewise. (Mul_impl): Likewise. (Div_impl): Likewise. (Modulo_impl): Likewise. (Add_impl): Likewise. (Sub_impl): Likewise. (Shift_left_impl): Likewise. (Shift_right_impl): Likewise. (Less_impl): Likewise. (Greater_impl): Likewise. (Less_equal_impl): Likewise. (Greater_equal_impl): Likewise. (Equal_impl): Likewise. (Not_equal_impl): Likewise. (Bit_and_impl): Likewise. (Bit_xor_impl): Likewise. (Bit_or_impl): Likewise. (And_impl): Likewise. (Or_impl): Likewise. (Comma_impl): Likewise. (Assign_impl): Likewise. (Member_selection_impl): Likewise. (Array_select_impl): Likewise. (Dot_select_impl): Likewise. (Arrow_select_impl): Likewise. (Scope_select_impl): Likewise. ================================================ FILE: include/ipr/impl ================================================ // -*- C++ -*- // // This file is part of The Pivot framework. // Written by Gabriel Dos Reis. // See LICENSE for copyright and license notices. // #ifndef IPR_IMPL_INCLUDED #define IPR_IMPL_INCLUDED // Consumers of this header must: // 1. #include all needed standard headers // 2. import cxx.ipr; // 3. #include #include // ----------------- // -- Methodology -- // ----------------- // This file provides implementations for the interface classes // defined in . The basic rule is that every class // found in that header file has at least one implementation in // this file. That rule applies to all abstract interfaces such as // "ipr::Expr", as well as more concrete ones like "ipr::Address". // Implementations for the abstract interfaces usually consists in // providing implementations for common operations on some set of // interfaces. For instance, "impl::Expr provides implementation for // the ipr::Expr::type() operation. // For that to work properly, without using virtual inheritance, // we parameterize the implementations of the abstract interfaces with // (set of) concrete interfaces. That ensures, for example, that // impl::Unary overrides ipr::Node::accept() and forward to the right // ipr::Visitor::visit() hook. namespace ipr::util { // Interface nodes are neither copyable nor moveable, by virtue of being abstract classes. // Similarly, implementation of those interfaces are not intended to be copyable nor moveable. // This utility class captures the intent of disabling the automatic generation of the usual // copy/move operations. The class definition is more verbose than the intent. struct immotile { constexpr immotile() = default; immotile(immotile&&) = delete; immotile(const immotile&) = delete; immotile& operator=(immotile&&) = delete; immotile& operator=(const immotile&) = delete; }; } namespace ipr::impl { // -- impl::Node -- template struct Node : T { using Interface = T; void accept(ipr::Visitor& v) const final { v.visit(*this); } }; // -- impl::immotile_node template struct immotile_node : impl::Node, util::immotile { }; // -- Generic implementation of ipr::Basic_unary template struct Basic_unary : Interface { using typename Interface::Arg_type; Arg_type rep; explicit constexpr Basic_unary(Arg_type a) : rep{a} { } constexpr Arg_type operand() const final { return rep; } }; // -- impl::Unary_node: Shorthand for the implementation of generic unary nodes. template using Unary_node = impl::Basic_unary>; // -- Generic implementation of ipr::Basic_binary -- template struct Basic_binary : Interface { using typename Interface::Arg1_type; using typename Interface::Arg2_type; struct Rep { Arg1_type first; Arg2_type second; }; Rep rep; constexpr Basic_binary(const Rep& r) : rep{r} { } constexpr Basic_binary(Arg1_type f, Arg2_type s) : rep{f, s} { } constexpr Arg1_type first() const final { return rep.first; } constexpr Arg2_type second() const final { return rep.second; } }; // -- impl::Binary_node: Short hand for implementation of generic binary nodes. template using Binary_node = impl::Basic_binary>; // -- impl::Ternary -- template struct Ternary : Interface { using typename Interface::Arg1_type; using typename Interface::Arg2_type; using typename Interface::Arg3_type; struct Rep { Arg1_type first; Arg2_type second; Arg3_type third; }; Rep rep; Ternary(const Rep& r) : rep(r) { } Ternary(Arg1_type f, Arg2_type s, Arg3_type t) : rep{ f, s, t } { } Arg1_type first() const final { return rep.first; } Arg2_type second() const final { return rep.second; } Arg3_type third() const final { return rep.third; } }; // -- impl::Quaternary -- template struct Quaternary : Interface { using typename Interface::Arg1_type; using typename Interface::Arg2_type; using typename Interface::Arg3_type; using typename Interface::Arg4_type; struct Rep { Arg1_type first; Arg2_type second; Arg3_type third; Arg4_type fourth; }; Rep rep; Quaternary(const Rep& r) : rep(r) { } Quaternary(Arg1_type f, Arg2_type s, Arg3_type t, Arg4_type u) : rep{ f, s, t, u } { } Arg1_type first() const final { return rep.first; } Arg2_type second() const final { return rep.second; } Arg3_type third() const final { return rep.third; } Arg4_type fourth() const final { return rep.fourth; } }; using Logogram = Basic_unary; using Comment = Unary_node; using Identifier = Unary_node; using Suffix = Unary_node; using Operator = Unary_node; using Conversion = Unary_node; using Ctor_name = Unary_node; using Dtor_name = Unary_node; using Guide_name = Unary_node; using Type_id = Unary_node; // -- Generic implementarion of ipr::Transfer using Transfer = impl::Basic_binary; // -- Specialized implementation of ipr::Transfer // An object of this class pairs a specified language linkage with the natural // C++ calling convention. struct Transfer_from_linkage : ipr::Transfer { constexpr explicit Transfer_from_linkage(const ipr::Language_linkage& l) : link{l} { } const ipr::Language_linkage& first() const final { return link; } const ipr::Calling_convention& second() const final; private: const ipr::Language_linkage& link; }; // -- Specialized implementation of ipr::Transfer // An object of this class pairs the C++ language linkage with a specified calling convention. struct Transfer_from_cc : ipr::Transfer { constexpr explicit Transfer_from_cc(const ipr::Calling_convention& cc) : conv{cc} { } const ipr::Language_linkage& first() const final; const ipr::Calling_convention& second() const final { return conv; } private: const ipr::Calling_convention& conv; }; } namespace ipr::util { // Type for representing the hash of data (mostly character strings) used internally. enum class hash_code : std::size_t { }; } namespace ipr::impl { // -- impl::String -- struct String : immotile_node { constexpr String(const char8_t* s) : txt{ s } { } constexpr String(util::word_view w) : txt{ w } { } constexpr util::word_view characters() const final { return txt; } private: const util::word_view txt; }; } namespace ipr::util { // String pool. Used to intern words used for external designation of entities. struct string_pool : private std::map> { const ipr::String& intern(word_view); private: util::string::arena strings; }; } namespace ipr::impl { // -------------------------------------- // -- Implementations of ipr::Sequence -- // -------------------------------------- // The Sequence<> interface admits various implementations. // Here is the list of the implementations currently in use: // (a) ref_sequence // (b) obj_sequence // (c) obj_list // (d) empty_sequence // Variants exist in form of // (i) decl_sequence // (ii) singleton_ref // (iii) singleton_obj // -- When a class implements an interface, return that interface; // -- otherwise, the type itself. template concept Has_interface = requires { typename T::Interface; }; template struct abstraction { using type = T; }; // Remove any wrapper or implementation class. template struct abstraction { using type = typename T::Interface; }; template class C> struct abstraction> : abstraction { }; template using projection = typename abstraction::type; // -- impl::ref_sequence -- // The class ref_sequence implements Sequence by storing // references (e.g. pointers) to data of type T, allocated // somewhere else. That for example if useful when keeping track // of redeclarations in decl-sets. // In general, it can be used to implement the notion of sub-sequence. template struct ref_sequence : ipr::Sequence, private std::vector { using Seq = Sequence; using Rep = std::vector; using pointer = const T*; using Iterator = typename Seq::Iterator; using Index = typename Seq::Index; explicit ref_sequence(std::size_t n = 0) : Rep(n) { } Index size() const final { return Rep::size(); } using Seq::begin; using Seq::end; using Rep::resize; using Rep::push_back; const T& get(Index p) const final { return *pointer(this->at(p)); } }; // -- impl::Warehouse -- // The class Warehouse is intended to be used by clients of the // impl::Lexicon to temporarily accumulate a sequence of references to // data of type T to assist in construction of ipr nodes such as product or sum. template struct Warehouse : private ref_sequence { using Rep = ref_sequence; explicit Warehouse(std::size_t n = 0) : Rep(n) { } Rep& rep() { return *this; } const Rep& rep() const { return *this; } using Rep::Seq::begin; using Rep::Seq::end; using Rep::size; void push_back(const T& item) { Rep::push_back(&item); } }; // -- stable_farm -- // Backing store for stable reference-producing factories. template struct stable_farm : std::forward_list { using std::forward_list::forward_list; template T* make(Args&&... args) { this->emplace_front(std::forward(args)...); return &this->front(); } }; // -- impl::obj_sequence -- // The class obj_sequence implements the interface Sequence by storing the // actual values as random access sequence of objects, instead of references to // values (as is the case of ref_sequence). This Sequence implementation // is useful for potentially large sequence of items (e.g. enumerator lists) // and for which iteration is frequent. template struct obj_sequence : ipr::Sequence>, private std::deque { using Seq = ipr::Sequence>; using Impl = std::deque; using Iterator = typename Seq::Iterator; using Index = typename Seq::Index; using Seq::begin; using Seq::end; Index size() const final { return Impl::size(); } const projection& get(Index p) const final { if (p < 0 or p >= size()) throw std::domain_error("obj_sequence::get"); return backing_store()[p]; } Impl& backing_store() { return *this; } const Impl& backing_store() const { return *this; } template T* push_back(Args&&... args) { return &Impl::emplace_back(std::forward(args)...); } }; // -- impl::obj_list -- // The class obj_list implements Sequence by storing the actual // values in a singly-linked list, instead of references to values (as is // the case of ref_sequence). This Sequence implementation can be used // for typically small sequence of items (e.g. parameter lists, or direct // base class lists) template struct obj_list : ipr::Sequence>, private stable_farm { using Seq = ipr::Sequence>; using Impl = stable_farm; using Iterator = typename Seq::Iterator; using Index = typename Seq::Index; using Seq::begin; using Seq::end; obj_list() : mark{this->before_begin()} { } Index size() const final { return std::distance(Impl::begin(), Impl::end()); } Impl& backing_store() { return *this; } const Impl& backing_store() const { return *this; } const projection& get(Index p) const final { if (p < 0 or p >= size()) throw std::domain_error("obj_list::get"); auto b = Impl::begin(); std::advance(b, p); return *b; } template T* push_back(Args&&... args) { mark = Impl::emplace_after(mark, std::forward(args)...); return &*mark; } private: typename Impl::iterator mark; }; // -- impl::empty_sequence -- // There are various situations where the general notion of // sequence leads to consider empty sequence in implementations. // We could just use the generic ref_sequence<> or obj_list<>; // however, this specialization will help control data size // inflation, as the general sequences need at least two words // to keep track of their elements (even when they are empty). template struct empty_sequence : ipr::Sequence { using Index = typename Sequence::Index; Index size() const final { return 0; } const T& get(Index) const final { throw std::domain_error("empty_sequence::get"); } }; // --impl::singleton // A singleton container (in Standard C++ sense), providing the backing store for // the implementation of a singleton sequence. No allocator or other complications. // Usual minimal requirements: begin(), end() // Optionally: front(). // FIXME: It is a pity it takes this amount of code to provide that simple // interface, in C++20. template struct singleton { using iterator = T*; using const_iterator = const T*; template explicit singleton(Args&&... args) : item{std::forward(args)...} { } iterator begin() { return &item; } const_iterator begin() const { return &item; } iterator end() { return begin() + 1; } const_iterator end() const { return begin() + 1; } T& front() { return item; } const T& front() const { return item; } private: T item; }; // A sequence implementation for the case of a collection consisting of exactly one element. template struct singleton_obj : ipr::Sequence> { using Index = typename Sequence>::Index; template singleton_obj(Args&&... args) : seq{std::forward(args)...} { } Index size() const final { return 1; } const projection& get(Index i) const final { if (i == 0) return seq.front(); throw std::domain_error("singleton::get"); } const T& element() const { return seq.front(); } singleton& backing_store() { return seq; } const singleton& backing_store() const { return seq; } private: impl::singleton seq; }; } namespace ipr::impl { // The natural C++ data and control transfer. const ipr::Transfer& cxx_transfer(); // The two language linkages required of all C++ implementations. const ipr::Language_linkage& c_linkage(); inline const ipr::Language_linkage& cxx_linkage() { return cxx_transfer().language_linkage(); } // The type of types, including itselt. // Yes, we have a ``Type: Type'' problem. const ipr::Type& typename_type(); // -- impl::Classic template struct Classic : Operation { Optional op_impl { }; using Operation::Operation; Optional implementation() const final { return op_impl; } }; // -- impl::Expr template struct Expr : immotile_node { Optional typing; constexpr Expr(Optional t = { }) : typing{ t } { } const ipr::Type& type() const final { return typing.get(); } }; // -- impl::Unary_expr: Short hand for the implementation of generic unary expression nodes. template using Unary_expr = Basic_unary>; // -- impl::Classic_unary_expr template using Classic_unary_expr = Classic>; // -- impl::Binary_expr template using Binary_expr = impl::Basic_binary>; // -- impl::Classic_binary_expr template using Classic_binary_expr = Classic>; // ----------------------- // -- Generalized types -- // ----------------------- // impl::Type implements the common operations supported // by all type node objects. Almost all C++ types have "C++" language linkage. // The exceptions are handled elsewhere. template struct Type : impl::Node { const ipr::Transfer& transfer() const final { return impl::cxx_transfer(); } }; // A composite type is one built from existing types and built-in type constructors. // Their names are exactly the type-expressions referencing the types themselves. template struct Composite : impl::Type { constexpr Composite() : id{*this} { } const ipr::Type& type() const final { return impl::typename_type(); } const ipr::Name& name() const final { return id; } private: impl::Type_id id; }; // -- impl::Unary_type template using Unary_type = impl::Basic_unary>; // -- impl::Binary_type template using Binary_type = impl::Basic_binary>; // -- impl::Ternary_type template using Ternary_type = Ternary>; } namespace ipr::impl{ // ---------------------------------------------------- // -- Scopes, sequence of declarations and overloads -- // ---------------------------------------------------- // // A scope is defined to be a sequence of declarations. // There may be multiple instances of a declaration in // a scope. All declarations sharing the same name // form an overload set. Consequently a scope is partitioned // into overload sets, each of which is uniquely identified // by its name. // An overload set, in turn, is partitioned in sub-sets of // (multiple) declarations with same type. So, each decl-set // within its overload set, is uniquely determined by its // type. Each decl-set has a "standard" representative, // called the "master declaration". A master declaration is // the first seen declaration of a decl-set. // // There are few special cases of the above general description. // Parameters, base-classes and enumerators cannot be multiply // declared. Therefore within a parameter-lits, a base-class // list or an enumeration definition, each decl-set is // singleton whose sole element is the master declaration. // // A scope chains declaration together. A declaration in a // Scope has a "position", that uniquely identifies it as a member // of a sequence. To provide a relatively fast "subscription by position" // operation on a scope, the chain of declaration is // organized as a red-back tree where the position is used as key. // And alternative is a hash table, when we get there. // The chain of declarations in a scope. struct scope_datum : util::rb_tree::link { // The specifiers for this declaration. S ipr::Specifiers spec = { }; // Back-pointer to this declaration. It shall be set at // the declaration creation. const ipr::Decl* decl = { }; }; // -- impl::decl_sequence -- struct decl_sequence : ref_sequence { }; // -- impl::singleton_ref -- // A singleton_ref is a specialization of ref_sequence that contains // exactly a single reference. It is mostly used to support the // general interface of singly-item as inherited in parameter-list or // enumerator definitions or EH regions. template struct singleton_ref : ipr::Sequence { using Index = typename Sequence::Index; const T& datum; explicit singleton_ref(const T& t) : datum(t) { } Index size() const final { return 1; } const T& get(Index i) const final { if (i == 0) return datum; throw std::domain_error("singleton_ref::get"); } }; // Parameters, base-subobjects and enumerations cannot be // multiply declared in a given region. Therefore such a // declaration is the sole member in its own decl-set. // Furthermore, there cannot be other declaration with // different type but same name. Therefore the name for // such a declaration defines an overload set with a single // member. This class implements such a special overload set. template struct singleton_overload : immotile_node { T decl; // Really the declset, but it is a singleton. template explicit singleton_overload(const Args&... args) : decl{args...} { } const ipr::Name& name() const { return decl.name(); } const ipr::Type& type() const final { return decl.type(); } Optional operator[](const ipr::Type& t) const final { if (physically_same(t, decl.type())) return decl; return { }; } // Where needed, implicitly unwrap the singleton to reveal its content. operator const T&() const { return decl; } }; } namespace ipr::impl { // Scopes, as expressions, have Product types. Such types could // be implemented directly as a separate sequence of types. // However, it would require coordination from the various // scope updaters to keep the types in sync. An alternative, // implemented by typed_sequence, is to wrap sequences // in "type envelop" and return the nth type as being the // type of the nth declaration in the sequence. template struct typed_sequence : impl::Composite, ipr::Sequence { using Index = typename Sequence::Index; Seq seq; typed_sequence() { } explicit typed_sequence(const Seq& s) : seq(s) { } template explicit typed_sequence(Args&&... args) : seq(std::forward(args)...) { } const ipr::Sequence& operand() const final { return *this; } Index size() const final { return seq.size(); } const ipr::Type& get(Index i) const final { return seq.get(i).type(); } }; } namespace ipr::impl { // -- homogeneous_scope // A sequence of homogenous node can be represented directly as a container // of the concrete implementations classes instead of pointers to the // interface nodes. This is the case, in particular, for enumerators of // an enumeration or parameters in a parameter list. template class Seq = obj_list> struct homogeneous_scope : impl::Node, ipr::Sequence, ipr::Sequence { using Index = typename ipr::Sequence::Index; using Members = Seq>; typed_sequence decls; template homogeneous_scope(Args&&... args) : decls{std::forward(args)...} { } Index size() const final { return decls.size(); } const projection& get(Index i) const final { return decls.seq.get(i); } const ipr::Product& type() const final { return decls; } const ipr::Sequence& elements() const final { return *this; } Optional operator[](const Name&) const final; template Member* push_back(const Args&... args) { return &decls.seq.push_back(args...)->decl; } }; // FIXME: Remove this linear search. template class Seq> Optional homogeneous_scope::operator[](const ipr::Name& n) const { for (auto& decl : decls.seq.backing_store()) { if (physically_same(decl.name(), n)) return { decl }; } return { }; } // -- impl::homogeneous_region: template class Seq = obj_list> struct homogeneous_region : impl::Node { using location_span = ipr::Region::Location_span; const ipr::Region& parent; location_span extent; Optional owned_by { }; homogeneous_scope scope; explicit homogeneous_region(const ipr::Region& p) : parent(p) { } template explicit homogeneous_region(const ipr::Region& r, Args&&... args) : parent{r}, scope{r, std::forward(args)...} { } const ipr::Region& enclosing() const final { return parent; } const ipr::Sequence& body() const final { return scope; } const ipr::Scope& bindings() const final { return scope; } const location_span& span() const final { return extent; } Optional owner() const final { return owned_by; } bool global() const final { return false; } }; } namespace ipr::impl { // -- impl::Directive template struct Directive : impl::Expr { Phases phases() const final { return f; } }; template struct Stmt : S { ipr::Unit_location unit_locus; ipr::Source_location src_locus; ref_sequence notes; ref_sequence attrs; const ipr::Unit_location& unit_location() const final { return unit_locus; } const ipr::Source_location& source_location() const final { return src_locus; } const ipr::Sequence& annotation() const final { return notes; } const ipr::Sequence& attributes() const final { return attrs; } }; template using immotile_stmt = Stmt>; } namespace ipr::impl { // -- unique_decl: // Some declarations (e.g. parameters, base-classes, enumerators) // cannot be redeclared in their declarative regions. Consequently // their decl-sets and overload sets are singleton, containing // only the mater declarations. Consequently, it seems // heavyweight space wasteful to deploy the general representation // machinery for them. The class unique_decl<> implements the // specialization of Decl<> in those cases. template struct unique_decl : immotile_stmt { unique_decl() : seq{*this} { } ipr::Specifiers specifiers() const override { return { }; } const ipr::Decl& master() const final { return *this; } const ipr::Language_linkage& language_linkage() const final { return impl::cxx_linkage(); } const ipr::Sequence& decl_set() const final { return seq; } private: singleton_ref seq; }; } namespace ipr::impl { struct Parameter_list; // -- impl::Parameter -- struct Parameter final : unique_decl { Optional init; Parameter(const ipr::Name&, const ipr::Type&, Decl_position); const ipr::Name& name() const final { return id; } const ipr::Type& type() const final { return typing; } const ipr::Region& home_region() const final { return where.get().region(); } Mapping_level level() const final { return where.get().level(); } Decl_position position() const final { return pos; } Optional initializer() const final { return init; } private: const ipr::Name& id; const ipr::Type& typing; util::ref where; const Decl_position pos; friend struct Parameter_list; }; // -- impl::Parameter_list -- struct Parameter_list : impl::Node { Parameter_list(const ipr::Region&, Mapping_level); const ipr::Product& type() const final; const ipr::Region& region() const final; Mapping_level level() const final { return nesting; } const ipr::Sequence& elements() const final; impl::Parameter* add_member(const ipr::Name&, const ipr::Type&); // Need access by Mapping, Lambda and whomever creates Callable_species homogeneous_region parms; private: const Mapping_level nesting; }; // -- impl::Parameterization template struct Parameterization : Base { impl::Parameter_list inputs; util::ref body; Parameterization(const ipr::Region& r, Mapping_level l) : inputs{r, l} { } const ipr::Parameter_list& parameters() const final { return inputs; } const T& result() const final { return body.get(); } }; } namespace ipr::cxx_form::impl { // -- implementation of ipr::cxx_form::Constraint template struct Constraint : T, ipr::util::immotile { using Interface = T; void accept(Constraint_visitor& v) const final { v.visit(static_cast(*this)); } }; // -- implementation of ipr::cxx_form::Constraint::Monadic struct Monadic_constraint : impl::Constraint { explicit Monadic_constraint(const ipr::Identifier& n) : id{n} { } Monadic_constraint(const ipr::Expr& s, const ipr::Identifier& n) : outer{s}, id{n} { } Optional scope() const final { return outer; } const ipr::Identifier& concept_name() const final { return id; } private: Optional outer { }; const ipr::Identifier& id; }; // -- implementation of ipr::cxx_form::Constraint::Polyadic struct Polyadic_constraint : impl::Constraint { ipr::impl::ref_sequence args; explicit Polyadic_constraint(const ipr::Identifier& n) : id{n} { } Polyadic_constraint(const ipr::Expr& s, const ipr::Identifier& n) : outer{s}, id{n} { } Optional scope() const final { return outer; } const ipr::Identifier& concept_name() const final { return id; } const ipr::Sequence& trailing_arguments() const final { return args; } private: Optional outer { }; const ipr::Identifier& id; }; // -- implementation of ipr::cxx_form::Requirement template struct Requirement : T, ipr::util::immotile { using Interface = T; void accept(Requirement_visitor& v) const final { v.visit(static_cast(*this)); } }; // -- implementation of ipr::cxx_form::Requirement::Simple struct Simple_requirement : impl::Requirement { explicit Simple_requirement(const ipr::Expr& x) : pattern{x} { } const ipr::Expr& expr() const final { return pattern; } private: const ipr::Expr& pattern; }; // -- implementation of ipr::cxx_form::Requirement::Type struct Type_requirement : impl::Requirement { explicit Type_requirement(const ipr::Name& n) : name{n} { } Type_requirement(const ipr::Expr& s, const ipr::Name& n) : outer{s}, name{n} { } Optional scope() const final { return outer; } const ipr::Name& type_name() const final { return name; } private: Optional outer { }; const ipr::Name& name; }; // -- implementation of ipr::cxx_form::Requirement::Compound struct Compound_requirement : impl::Requirement { Optional type; bool has_noexcept = false; explicit Compound_requirement(const ipr::Expr& x) : pattern{x} { } const ipr::Expr& expr() const final { return pattern; } Optional constraint() const final { return type; } bool nothrow() const final { return has_noexcept; } private: const ipr::Expr& pattern; }; // -- implementation of ipr::cxx_form::Requirement::Compound struct Nested_requirement : impl::Requirement { explicit Nested_requirement(const ipr::Expr& x) : cond{x} { } const ipr::Expr& condition() const final { return cond; } private: const ipr::Expr& cond; }; // -- implementation of ipr::cxx_form::Indirector template struct Indirector : T { using Interface = T; ipr::impl::ref_sequence attr_seq; const ipr::Sequence& attributes() const final { return attr_seq; } void accept(Indirector_visitor& v) const final { v.visit(static_cast(*this)); } }; // -- implementation of ipr::cxx_form::Indirector::Pointer struct Pointer_indirector : impl::Indirector { explicit Pointer_indirector(ipr::Qualifiers q) : quals{q} { } ipr::Qualifiers qualifiers() const final { return quals; } private: ipr::Qualifiers quals; }; // -- implementation of ipr::cxx_form::Indirector::Reference struct Reference_indirector : impl::Indirector { explicit Reference_indirector(Reference_flavor f) : how{f} { } Reference_flavor flavor() const final { return how; } private: Reference_flavor how { }; }; // -- implementation of ipr::cxx_form::Indirector::Member struct Member_indirector : impl::Indirector { Member_indirector(const ipr::Expr& s, ipr::Qualifiers q) : whole{s}, quals{q} { } const ipr::Expr& scope() const final { return whole; } ipr::Qualifiers qualifiers() const final { return quals; } private: const ipr::Expr& whole; ipr::Qualifiers quals; }; // -- implementation of ipr::cxx_form::Morphism template struct Morphism : T { using Interface = T; ipr::impl::ref_sequence attr_seq; const ipr::Sequence& attributes() const final { return attr_seq; } void accept(Morphism_visitor& v) const final { v.visit(static_cast(*this)); } }; // -- implementation of ipr::cxx_form::Morphism::Function struct Function_morphism : impl::Morphism { ipr::impl::Parameter_list inputs; Optional eh_spec; ipr::Qualifiers quals { }; Binding_mode ref_qual { }; Function_morphism(const ipr::Region&, Mapping_level); const ipr::Parameter_list& parameters() const final { return inputs; } Optional throws() const final { return eh_spec; } ipr::Qualifiers qualifiers() const final { return quals; } Binding_mode binding_mode() const final { return ref_qual; } }; // -- implementation of ipr::cxx_form::Morphism::Array struct Array_morphism : impl::Morphism { Optional array_bound; Optional bound() const final { return array_bound; } }; // -- implementation of ipr::cxx_form::Species_declarator template struct Species : T { using Interface = T; ipr::impl::ref_sequence morphisms; const ipr::Sequence& suffix() const final { return morphisms; } void accept(Species_visitor& v) const final { v.visit(static_cast(*this)); } }; // Provide implementation for common operations for the various classes implementing // classes for instances of declarator-id. template struct Id_species : impl::Species { ipr::impl::ref_sequence attr_seq; const ipr::Sequence& attributes() const final { return attr_seq; } }; // Provide implementation for common operations for the various classes implementing // unqualified-id instances of declarator-id. template struct Name_species : impl::Id_species { Name_species() = default; explicit Name_species(const S& s) : id{s} { } Optional name() const final { return id; } private: Optional id{ }; }; // -- implementation of ipr::cxx_form::Species_declarator::Unqualified_id using Unqualified_id_species = Name_species; // -- implementation of ipr::cxx_form::Species_declarator::Pack using Pack_species = Name_species; // -- implementation of ipr::cxx_form::Species_declarator::Qualified_id struct Qualified_id_species : impl::Id_species { Qualified_id_species(const ipr::Expr& s, const ipr::Name& n) : outer{s}, id{n} { } const ipr::Expr& scope() const final { return outer; } const ipr::Name& member() const final { return id; } private: const ipr::Expr& outer; const ipr::Name& id; }; // -- implementation of ipr::cxx_form::Species_declarator::Parenthesized struct Parenthesized_species : impl::Species { util::ref declarator; const Declarator::Term& term() const final { return declarator.get(); } }; // -- implementation of ipr::cxx_form::Declarator. // Only the `accept()` operation is implemented here. The other common operation, `species()`, // is individually implemented by each of the derived classes. template struct Declarator : T { using Interface = T; void accept(cxx_form::Declarator_visitor& v) const final { v.visit(static_cast(*this)); } }; // -- implementation of ipr::cxx_form::Declarator::Term struct Term_declarator : impl::Declarator { ipr::impl::ref_sequence prefix; util::ref tail; const ipr::Sequence& indirectors() const final { return prefix; } const Species_declarator& species() const final { return tail.get(); } }; // -- implementation of ipr::cxx_form::Declarator::Targeted struct Targeted_declarator : impl::Declarator { Targeted_declarator(const cxx_form::Species_declarator& s, const ipr::Type& t) : domain{s}, range{t} { } const cxx_form::Species_declarator& species() const final { return domain; } const ipr::Type& target() const final { return range; } private: const cxx_form::Species_declarator& domain; const ipr::Type& range; }; // -- implementation of ipr::cxx_form::Initialization_provision template struct Initialization_provision : T { void accept(cxx_form::Provision_visitor& v) const final { v.visit(static_cast(*this)); } }; // -- implementation of ipr::cxx_form::Elemental_initializer template struct Elemental_initializer : T { using Interface = T; void accept(cxx_form::Initializer_visitor& v) const final { v.visit(static_cast(*this)); } }; // -- implementation of ipr::cxx_form::Subobject_designator template struct Subobject_designator : T { using Interface = T; void accept(cxx_form::Designator_visitor& v) const final { v.visit(static_cast(*this)); } }; // -- implementation of ipr::cxx_form::Field_designator struct Field_designator : impl::Subobject_designator { explicit Field_designator(const ipr::Identifier& x) : id{x} { } const ipr::Identifier& name() const final { return id; } private: const ipr::Identifier& id; }; // -- implementation of ipr::cxx_form::Slot_designator struct Slot_designator : impl::Subobject_designator { explicit Slot_designator(const ipr::Expr& x) : idx{x} { } const ipr::Expr& index() const final { return idx; } private: const ipr::Expr& idx; }; // -- implementation of ipr::cxx_form::Earmarked_initializer struct Earmarked_initializer : cxx_form::Earmarked_initializer { using Interface = cxx_form::Earmarked_initializer; Earmarked_initializer(const cxx_form::Subobject_designator& f, const cxx_form::Initialization_provision& x) : sub{f}, init{x} { } const cxx_form::Subobject_designator& subobject() const final { return sub; } const cxx_form::Initialization_provision& initializer() const final { return init; } private: const cxx_form::Subobject_designator& sub; const cxx_form::Initialization_provision& init; }; // -- implementation of ipr::cxx_form::Classic_provision struct Classic_provision : impl::Initialization_provision { explicit Classic_provision(const ipr::cxx_form::Elemental_initializer& i) : init{i} { } const ipr::cxx_form::Elemental_initializer& initializer() const final { return init; } private: const ipr::cxx_form::Elemental_initializer& init; }; // -- implementation of ipr::cxx_form::Parenthesized_provision struct Parenthesized_provision : impl::Initialization_provision { explicit Parenthesized_provision(const ipr::Expr& x) : expr{x} { } const ipr::Expr& initializer() const final { return expr; } private: const ipr::Expr& expr; }; // -- implementation of ipr::cxx_form::Braced_provision struct Braced_provision : impl::Initialization_provision> { ipr::impl::ref_sequence seq; const ipr::Sequence& elements() const final { return seq; } }; // -- implementation of ipr::cxx_form::Designated_list_provision struct Designated_list_provision : impl::Initialization_provision> { ipr::impl::obj_sequence seq; const ipr::Sequence& elements() const final { return seq; } }; // -- Factory of C++ declarator forms. struct form_factory { Monadic_constraint* make_monadic_constraint(const ipr::Identifier&); Monadic_constraint* make_monadic_constraint(const ipr::Expr&, const ipr::Identifier&); Polyadic_constraint* make_polyadic_constraint(const ipr::Identifier&); Polyadic_constraint* make_polyadic_constraint(const ipr::Expr&, const ipr::Identifier&); Simple_requirement* make_simple_requirement(const ipr::Expr&); Type_requirement* make_type_requirement(const ipr::Name&); Type_requirement* make_type_requirement(const ipr::Expr&, const ipr::Name&); Compound_requirement* make_compound_requirement(const ipr::Expr&); Nested_requirement* make_nested_requirement(const ipr::Expr&); Pointer_indirector* make_pointer_indirector(ipr::Qualifiers); Reference_indirector* make_reference_indirector(Reference_flavor); Member_indirector* make_member_indirector(const ipr::Expr&, ipr::Qualifiers); Unqualified_id_species* make_unqualified_id_species(); Unqualified_id_species* make_unqualified_id_species(const ipr::Name&); Pack_species* make_pack_species(); Pack_species* make_pack_species(const ipr::Identifier&); Qualified_id_species* make_qualified_id_species(const ipr::Expr&, const ipr::Name&); Parenthesized_species* make_parenthesized_species(); Function_morphism* make_function_morphism(const ipr::Region& parent, Mapping_level level); Array_morphism* make_array_morphism(); Term_declarator* make_term_declarator(); Targeted_declarator* make_targeted_declarator(const cxx_form::Species_declarator&, const ipr::Type&); Classic_provision* make_classic_provision(const cxx_form::Elemental_initializer&); Parenthesized_provision* make_parenthesized_provision(const ipr::Expr&); Braced_provision* make_braced_provision(); Designated_list_provision* make_designated_provision(); Field_designator* make_field_designator(const ipr::Identifier&); Slot_designator* make_slot_designator(const ipr::Expr&); private: ipr::impl::stable_farm monadic_constraints; ipr::impl::stable_farm polyadic_constraints; ipr::impl::stable_farm simple_reqs; ipr::impl::stable_farm type_reqs; ipr::impl::stable_farm compound_reqs; ipr::impl::stable_farm nested_reqs; ipr::impl::stable_farm pointer_indirectors; ipr::impl::stable_farm reference_indirectors; ipr::impl::stable_farm member_indirectors; ipr::impl::stable_farm unqualified_id_species; ipr::impl::stable_farm pack_species; ipr::impl::stable_farm qualified_id_species; ipr::impl::stable_farm paren_species; ipr::impl::stable_farm function_morphisms; ipr::impl::stable_farm array_morphisms; ipr::impl::stable_farm term_declarators; ipr::impl::stable_farm targeted_declarators; ipr::impl::stable_farm classic_provisions; ipr::impl::stable_farm paren_provisions; ipr::impl::stable_farm braced_provisions; ipr::impl::stable_farm designated_provisions; ipr::impl::stable_farm field_designators; ipr::impl::stable_farm slot_designators; }; } namespace ipr::impl { struct Lexicon; template struct node_ref { const T& node; explicit node_ref(const T& t) : node(t) { } }; // -- impl::Token -- struct Token : ipr::Lexeme, ipr::Token { using Interface = ipr::Token; Token(const ipr::String&, const Source_location&, TokenValue, TokenCategory); const ipr::String& spelling() const final { return text; } const Source_location& locus() const final { return location; } const ipr::Lexeme& lexeme() const final { return *this; } TokenValue value() const final { return token_value; } TokenCategory category() const final { return token_category; } private: const ipr::String& text; Source_location location; TokenValue token_value; TokenCategory token_category; }; // -- Parameterized implementation of ipr::Attribute. // -- The type parameter is used here as a device to implement an overrider // -- for ipr::Attribute::visit(), for all T implementation classes of attribute. template struct Attribute : T { void accept(typename T::Visitor& v) const final { v.visit(*this); } }; template using Unary_attribute = impl::Basic_unary>; template using Binary_attribute = impl::Basic_binary>; // -- implementation of ipr::BasicAttribute using BasicAttribute = Unary_attribute; // -- implementation of ipr::ScopedAttribute using ScopedAttribute = Binary_attribute; // -- implementation of ipr::LabeledAttribute using LabeledAttribute = Binary_attribute; // -- implementation of ipr::CalledAttribute using CalledAttribute = Binary_attribute; // -- implementation of ipr::ExpandedAttribute using ExpandedAttribute = Binary_attribute; // -- implementation of ipr::FactoredAttribute using FactoredAttribute = Binary_attribute; // -- implementation of ipr::ElaboratedAttribute using ElaboratedAttribute = Unary_attribute; // -- Attributes factory -- struct attr_factory { const ipr::BasicAttribute& make_basic_attribute(const ipr::Token&); const ipr::ScopedAttribute& make_scoped_attribute(const ipr::Token&, const ipr::Token&); const ipr::LabeledAttribute& make_labeled_attribute(const ipr::Token&, const ipr::Attribute&); const ipr::CalledAttribute& make_called_attribute(const ipr::Attribute&, const ipr::Sequence&); const ipr::ExpandedAttribute& make_expanded_attribute(const ipr::Token&, const ipr::Attribute&); const ipr::FactoredAttribute& make_factored_attribute(const ipr::Token&, const ipr::Sequence&); const ipr::ElaboratedAttribute& make_elaborated_attribute(const ipr::Expr&); private: stable_farm basics; stable_farm scopeds; stable_farm labeleds; stable_farm calleds; stable_farm expandeds; stable_farm factoreds; stable_farm elaborateds; }; // -- implementation for ipr::Capture struct Capture : ipr::Capture { using Interface = ipr::Capture; Capture(const ipr::Decl& d, ipr::Binding_mode m) : decl{d}, md{m} { } ipr::Binding_mode mode() const final { return md; } const ipr::Decl& entity() const final { return decl; } private: const ipr::Decl& decl; const ipr::Binding_mode md; }; // -- Generic implementation of ipr::Capture_specification template struct Capture_specification : T { void accept(typename T::Visitor& v) const final { v.visit(*this); } }; // -- implementation of ipr::Capture_specification::Default struct Default_capture_specification : Capture_specification { explicit Default_capture_specification(Binding_mode m) : md{m} { } Binding_mode mode() const final { return md; } private: const Binding_mode md; }; // -- implementation of ipr::Capture_specification::Implicit_object struct Implicit_object_capture_specification : Capture_specification { explicit Implicit_object_capture_specification(Binding_mode m) : md{m} { } Binding_mode how() const final { return md; } private: const Binding_mode md; }; // -- implementation of ipr::Capture_specification::Enclosing_local struct Enclosing_local_capture_specification : Capture_specification { Enclosing_local_capture_specification(const ipr::Decl& x, Binding_mode m) : decl{x}, md{m} { } Binding_mode mode() const final { return md; } const ipr::Identifier& name() const final; const ipr::Decl& declaration() const final { return decl; } private: const ipr::Decl& decl; const Binding_mode md; }; // -- implementation of ipr::Capture_capture::Binding struct Binding_capture_specification : Capture_specification { Binding_capture_specification(const ipr::Identifier& n, const ipr::Expr& x, Binding_mode m) : id{n}, init{x}, md{m} { } Binding_mode mode() const final { return md; } const ipr::Identifier& name() const final { return id; } const ipr::Expr& initializer() const final { return init; } private: const ipr::Identifier& id; const ipr::Expr& init; const Binding_mode md; }; // -- implementation of ipr::Capture_specification::Expansion. struct Expansion_capture_specification : Capture_specification { explicit Expansion_capture_specification(const Named& x) : cap{x} { } const Named& what() const final { return cap; } private: const Named& cap; }; // -- capture specification factory struct capture_spec_factory { const ipr::Capture_specification::Default& default_capture(Binding_mode); const ipr::Capture_specification::Implicit_object& implicit_object_capture(Binding_mode); const ipr::Capture_specification::Enclosing_local& enclosing_local_capture(const ipr::Decl&, Binding_mode); const ipr::Capture_specification::Binding& binding_capture(const ipr::Identifier&, const ipr::Expr&, Binding_mode); const ipr::Capture_specification::Expansion& expansion_capture(const ipr::Capture_specification::Named&); private: stable_farm defaults; stable_farm implicits; stable_farm enclosings; stable_farm bindings; stable_farm expansions; }; // -- impl::Module_name -- struct Module_name : ipr::Module_name { ref_sequence components; const ipr::Sequence& stems() const final; }; template, int> = 0> constexpr int compare(T lhs, T rhs) { constexpr std::less<> lt { }; return lt(lhs, rhs) ? -1 : (lt(rhs, lhs) ? 1 : 0); } constexpr int compare(const ipr::Node& lhs, const ipr::Node& rhs) { return compare(&lhs, &rhs); } constexpr int compare(const ipr::String& lhs, const ipr::String& rhs) { return lhs.characters().compare(rhs.characters()); } inline int compare(const ipr::Logogram& lhs, const ipr::Logogram& rhs) { return compare(lhs.what(), rhs.what()); } inline int compare(const ipr::Language_linkage& lhs, const ipr::Language_linkage& rhs) { return compare(lhs.language(), rhs.language()); } // -- Conversion_expr -- template struct Conversion_expr : impl::Classic> { using Base = impl::Classic>; using Base::Base; const ipr::Type& type() const final { return this->rep.first; } }; // All declarations (except parameters, base-classes, enumerations) // maintain data about their position and master declaration. Since // all declarations in a given decl-set have the same type, only // the master declaration has the "type" information. // Similarly, only the master declaration maintains the home // region information. // // In a given decl-set, only one of the declarations is a definition. // The master declaration keeps track of that definition, so all // other redeclarations can refer to it through the master // declaration data. template struct master_decl_data; struct Overload; // An entry in an overload set. Part of the data that a // master declaration manages. It is determined, within // an overload set, by its type. All redeclarations must // register themselves before the master declaration, at // the creation time. struct overload_entry : util::rb_tree::link { const ipr::Type& type; ref_sequence declset; explicit overload_entry(const ipr::Type& t) : type(t) { } }; template struct basic_decl_data : scope_datum { // Information about the master declaration. This pointer // shall be set at actual declaration creation time. master_decl_data* master_data; explicit basic_decl_data(master_decl_data* mdd) : master_data(mdd) { } }; // A master declaration is the first seen introduction of // a name with a given type. Further redeclarations // are represented by basic_decl_data<> and are linked to the // master declaration. That forms the declaration-set of // that declaration. template struct master_decl_data : basic_decl_data, overload_entry { // The declaration that is considered to be the definition. Optional def { }; util::ref lang_linkage { }; // The overload set that contains this master declaration. It // shall be set at the time the node for the master declaration // is created. impl::Overload* overload; const ipr::Region* home; master_decl_data(impl::Overload* ovl, const ipr::Type& t) : basic_decl_data{this}, overload_entry{t}, overload{ovl}, home{nullptr} { } }; template<> struct master_decl_data : basic_decl_data, overload_entry { using Base = basic_decl_data; // The declaration that is considered to be the definition. Optional def { }; util::ref lang_linkage { }; const ipr::Template* primary; const ipr::Region* home; // The overload set that contains this master declaration. It // shall be set at the time the node for the master declaration // is created. impl::Overload* overload; // Sequence of specializations decl_sequence specs; master_decl_data(impl::Overload*, const ipr::Type&); }; struct Overload : impl::Expr { // The name of this overload set. const ipr::Name& name; // All entries in this overload set, chained together in a // red-back tree to permit fast retrieval based on type (as key). // They are all master declarations. util::rb_tree::chain entries; // A sequence of master declarations. They are added as they // appear in their enclosing scope. std::vector masters; explicit Overload(const ipr::Name&); Optional operator[](const ipr::Type&) const final; overload_entry* lookup(const ipr::Type&) const; template void push_back(master_decl_data*); }; struct node_compare { int operator()(const ipr::Node& lhs, const ipr::Node& rhs) const { return compare(lhs, rhs); } int operator()(const overload_entry& e, const ipr::Type& t) const { return (*this)(e.type, t); } int operator()(const overload_entry& l, const overload_entry& r) const { return (*this)(l.type, r.type); } }; // Capture the commonality of controlled statements such as `do-statement', // `while-statement', and `switch-statement'. template struct Controlled_stmt : immotile_stmt { using typename Base::Arg1_type; using typename Base::Arg2_type; util::ref> control; util::ref> stmt; Arg1_type first() const final { return control.get(); } Arg2_type second() const final { return stmt.get(); } const ipr::Type& type() const final { return second().type(); } }; // The class template Decl<> implements common operations // for most declarations nodes. The exception cases are // handled by the class template unique_decl<>. template struct Decl : immotile_stmt { basic_decl_data decl_data; Decl() : decl_data{ nullptr } { } const ipr::Language_linkage& language_linkage() const final { return util::check(decl_data.master_data)->lang_linkage.get(); } const ipr::Region& home_region() const final { return *util::check(util::check(decl_data.master_data)->home); } // Set declaration specifiers for this decl. using D::specifiers; void specifiers(ipr::Specifiers s) { decl_data.spec = s; } }; struct Base_type final : unique_decl { const ipr::Type& base; const ipr::Region& where; const Decl_position scope_pos; ipr::Specifiers spec; Base_type(const ipr::Type&, const ipr::Region&, Decl_position); const ipr::Type& type() const final { return base; } // FIXME: for a base-class subobject, the home region and lexical // region are slightly different. The home region should be that // of the class this is a base class, whereas the lexical region // should be the actual lexical region where the base type was specified. const ipr::Region& lexical_region() const final { return where; } const ipr::Region& home_region() const final { return where; } Decl_position position() const final { return scope_pos; } Optional initializer() const final; Specifiers specifiers() const final { return spec; } }; struct Enumerator final : unique_decl { const ipr::Name& id; const ipr::Enum& typing; const Decl_position scope_pos; util::ref where; Optional init; Enumerator(const ipr::Name&, const ipr::Enum&, Decl_position); const ipr::Name& name() const final { return id; } const ipr::Type& type() const final { return typing; } const ipr::Region& lexical_region() const final { return where.get(); } const ipr::Region& home_region() const final { return where.get(); } Decl_position position() const final { return scope_pos; } Optional initializer() const final { return init; } }; template struct decl_rep : T { using Interface = typename T::Interface; decl_rep(master_decl_data* mdd) { this->decl_data.master_data = mdd; this->decl_data.decl = this; mdd->declset.push_back(this); } const ipr::Name& name() const final { return this->decl_data.master_data->overload->name; } ipr::Specifiers specifiers() const final { return this->decl_data.spec; } const ipr::Type& type() const final { return this->decl_data.master_data->type; } const ipr::Scope& scope() const { return this->decl_data.master_data->overload->where; } Decl_position position() const { return this->decl_data.scope_pos; } const ipr::Decl& master() const final { return *util::check(this->decl_data.master_data->decl); } const ipr::Sequence& decl_set() const final { return this->decl_data.master_data->declset; } Optional definition() const { return this->decl_data.master_data->def; } }; // -- impl::symbolic_type // Representation of generalized built-in type. // All built-in types have type "typename" and, of course, have C++ linkage. // The type-parameter to this template is the type of identifier naming the type. template T> struct symbolic_type : impl::Type { constexpr explicit symbolic_type(const T& t) : id{t} { } const ipr::Name& name() const final { return id; } const ipr::Type& type() const final { return impl::typename_type(); } const ipr::Expr& operand() const final { return *this; } private: const T& id; }; using extended_type = symbolic_type; using Array = Binary_type; using Decltype = Unary_type; using As_type = Unary_type; using Tor = Binary_type; using Function = Ternary_type; using Pointer = Unary_type; using Product = Unary_type; using Ptr_to_member = Binary_type; using Qualified = Binary_type; using Reference = Unary_type; using Rvalue_reference = Unary_type; using Sum = Unary_type; using Forall = Binary_type; // -- Specialized implementation of ipr::As_type. // A given core expression can be proclaimed to name a type with a specified data // or control transfer. That is useful for interoperating with code fragments // written in other languages. struct As_type_with_transfer : immotile_node { struct Rep { const ipr::Expr& expr; const ipr::Transfer& xfer; }; explicit As_type_with_transfer(const Rep& r) : id{*this}, rep{r} { } const ipr::Name& name() const final { return id; } const ipr::Type& type() const final { return impl::typename_type(); } const ipr::Expr& operand() const final { return rep.expr; } const ipr::Transfer& transfer() const final { return rep.xfer; } private: Type_id id; Rep rep; }; // -- Specialized implementation of ipr::Function. // A function type with a specified data or control transfer (other than C++) protocol. struct Function_with_transfer : immotile_node { struct Rep { const ipr::Product& source; const ipr::Type& target; const ipr::Expr& throws; const ipr::Transfer& xfer; }; explicit Function_with_transfer(const Rep& r) : id{*this}, rep{r} { } const ipr::Name& name() const final { return id; } const ipr::Type& type() const final { return impl::typename_type(); } const ipr::Transfer& transfer() const final { return rep.xfer; } const ipr::Product& first() const final { return rep.source; } const ipr::Type& second() const final { return rep.target; } const ipr::Expr& third() const final { return rep.throws; } private: Type_id id; Rep rep; }; using Phantom = Expr; using Eclipsis = Expr; struct Symbol final : Unary_expr { explicit constexpr Symbol(const ipr::Name& n) : Unary_expr{ n } { } constexpr Symbol(const ipr::Name& n, const ipr::Type& t) : Unary_expr{ n } { typing = t; } }; struct Lambda : impl::Parameterization> { util::ref typing; Optional value_type; Optional decl_constraint; impl::ref_sequence attrs; impl::ref_sequence env_spec; Optional eh; Lambda_specifiers lam_spec; Lambda(const ipr::Region&, Mapping_level); const ipr::Closure& type() const final { return typing.get(); } Optional target() const final { return value_type; } Optional requirement() const final { return decl_constraint; } const ipr::Sequence& attributes() const final { return attrs; } Optional eh_specification() const final { return eh; } Lambda_specifiers specifiers() const final { return lam_spec; } const ipr::Sequence& captures() const final { return env_spec; } }; struct Requires : immotile_node { impl::Parameter_list formals; impl::ref_sequence requirements; Requires(const ipr::Region& r, Mapping_level l) : formals{r, l} { } const ipr::Type& type() const final; const ipr::Parameter_list& parameters() const final { return formals; } const ipr::Sequence& body() const final { return requirements; } }; using Address = Classic_unary_expr; using Array_delete = Classic_unary_expr; using Complement = Classic_unary_expr; using Delete = Classic_unary_expr; using Demotion = Unary_expr; using Deref = Classic_unary_expr; struct Asm : impl::Unary_node { explicit Asm(const ipr::String&); const ipr::Type& type() const final; }; struct Expr_list : immotile_node { typed_sequence> seq; Expr_list(); explicit Expr_list(const ref_sequence&); const ipr::Product& type() const final; const ipr::Sequence& operand() const final; void push_back(const ipr::Expr* e) { seq.seq.push_back(e); } }; using Alignof = Unary_expr; using Sizeof = Unary_expr; using Args_cardinality = Unary_expr; using Typeid = Unary_expr; using Label = Unary_expr; struct Restriction : Unary_node { explicit Restriction(const ipr::Expr& x) : Unary_node{x} { } const ipr::Type& type() const final; }; struct Id_expr : Unary_expr { Optional decls { }; explicit Id_expr(const ipr::Name&); Optional resolution() const final; }; using Materialization = Unary_expr; using Not = Classic_unary_expr; struct Enclosure : impl::Unary_expr { Enclosure(ipr::Delimiter, const ipr::Expr&); Delimiter delimiters() const final { return delim; } private: Delimiter delim; }; using Pre_decrement = Classic_unary_expr; using Pre_increment = Classic_unary_expr; using Post_decrement = Classic_unary_expr; using Post_increment = Classic_unary_expr; using Promotion = Unary_expr; using Read = Unary_expr; using Throw = Classic_unary_expr; using Unary_minus = Classic_unary_expr; using Unary_plus = Classic_unary_expr; using Expansion = Classic_unary_expr; using Construction = Classic_unary_expr; using Noexcept = Unary_expr; using Rewrite = Binary_node; using And = Classic_binary_expr; using Annotation = Binary_node; using Array_ref = Classic_binary_expr; using Arrow = Classic_binary_expr; using Arrow_star = Classic_binary_expr; using Assign = Classic_binary_expr; using Bitand = Classic_binary_expr; using Bitand_assign = Classic_binary_expr; using Bitor = Classic_binary_expr; using Bitor_assign = Classic_binary_expr; using Bitxor = Classic_binary_expr; using Bitxor_assign = Classic_binary_expr; using Call = Classic_binary_expr; using Cast = Conversion_expr; using Coercion = Classic_binary_expr; using Comma = Classic_binary_expr; using Const_cast = Conversion_expr; using Div = Classic_binary_expr; using Div_assign = Classic_binary_expr; using Dot = Classic_binary_expr; using Dot_star = Classic_binary_expr; using Dynamic_cast = Conversion_expr; using Equal = Classic_binary_expr; using Greater = Classic_binary_expr; using Greater_equal = Classic_binary_expr; using Less = Classic_binary_expr; using Less_equal = Classic_binary_expr; using Literal = Conversion_expr; using Lshift = Classic_binary_expr; using Lshift_assign = Classic_binary_expr; struct Mapping : impl::Parameterization> { Mapping(const ipr::Region&, Mapping_level); impl::Parameter* param(const ipr::Name& n, const ipr::Type& t) { return inputs.add_member(n, t); } }; using Member_init = Binary_expr; using Minus = Classic_binary_expr; using Minus_assign = Classic_binary_expr; using Modulo = Classic_binary_expr; using Modulo_assign = Classic_binary_expr; using Mul = Classic_binary_expr; using Mul_assign = Classic_binary_expr; using Narrow = Binary_expr; using Not_equal = Classic_binary_expr; using Or = Classic_binary_expr; using Plus = Classic_binary_expr; using Plus_assign = Classic_binary_expr; using Pretend = Binary_expr; using Qualification = Binary_expr; using Reinterpret_cast = Conversion_expr; using Rshift = Classic_binary_expr; using Rshift_assign = Classic_binary_expr; using Scope_ref = Classic_binary_expr; using Static_cast = Conversion_expr; using Template_id = Binary_node; using Widen = Binary_expr; // A Where node where the attendant() is not a scope. using Where_no_decl = Binary_node; struct Instantiation : immotile_node { Optional result; Instantiation(const ipr::Expr& e, const ipr::Substitution& s) : expr{e}, subst{s} { } const ipr::Expr& pattern() const final { return expr; } const ipr::Substitution& substitution() const final { return subst; } Optional instance() const final { return result; } private: const ipr::Expr& expr; const ipr::Substitution& subst; }; struct Binary_fold : Classic_binary_expr { Category_code fold_op; Binary_fold(Category_code, const ipr::Expr&, const ipr::Expr&); Category_code operation() const final; }; struct New : impl::Classic_binary_expr { bool global = false; New(Optional, const ipr::Construction&); bool global_requested() const final; }; using Conditional = Ternary>>; // An elementary substitution is a subsitution the domain of which is a singleton. struct Elementary_substitution : ipr::Substitution { Elementary_substitution(const ipr::Parameter& p, const ipr::Expr& v) : parm{p}, value{v} { } const ipr::Expr& operator[](const ipr::Parameter& p) const final { return physically_same(p, parm) ? parm : value; } private: const ipr::Parameter& parm; const ipr::Expr& value; }; // A general substitution is a substitution the domain of which has cardinality greater than one. struct General_substitution : ipr::Substitution { const ipr::Expr& operator[](const ipr::Parameter&) const final; General_substitution& subst(const ipr::Parameter&, const ipr::Expr&); private: std::map mapping; }; struct Template : impl::Decl { util::ref init; util::ref lexreg; Template(); const ipr::Template& primary_template() const final; const ipr::Sequence& specializations() const final; const ipr::Mapping& mapping() const final { return init.get(); } // FIXME: The initializer should actually be the mapping, since a Template is a // *named* mapping. In Classic IPR, and in the current incarnation, the initializer // is the initializer of the current instantiation. Optional initializer() const final { return { mapping().result() }; } const ipr::Region& lexical_region() const final { return lexreg.get(); } }; template struct decl_factory { using Interface = typename T::Interface; stable_farm> decls; stable_farm> master_info; // We have gotten an overload-set for a name, and we are about // to enter the first declaration for that name with the type T. decl_rep* declare(Overload* ovl, const ipr::Type& t) { // Grab bookkeeping memory for this master declaration. master_decl_data* data = master_info.make(ovl, t); // The actual representation for the declaration points back // to the master declaration bookkeeping store. decl_rep* master = decls.make(data); // Inform the overload-set that we have a new master declaration. ovl->push_back(data); return master; } decl_rep* redeclare(overload_entry* decl) { return decls.make (static_cast*>(decl)); } }; struct Alias : impl::Decl { util::ref aliasee; Alias(); Optional initializer() const final { return aliasee.get(); } }; struct Var : impl::Decl { Optional init; util::ref lexreg; Var(); Optional initializer() const final { return init; } const ipr::Region& lexical_region() const final { return lexreg.get(); } }; // FIXME: Field should use unique_decl, not impl::Decl. struct Field : impl::Decl { Optional init; Field(); Optional initializer() const final { return init; } }; // FIXME: Bitfield should use unique_decl, not impl::Decl struct Bitfield : impl::Decl { util::ref length; Optional init; Bitfield(); const ipr::Expr& precision() const final { return length.get(); } Optional initializer() const final { return init; } }; struct Typedecl : impl::Decl { Optional init; util::ref lexreg; Typedecl(); Optional initializer() const final { return init; } const ipr::Region& lexical_region() const final { return lexreg.get(); } }; struct Concept : impl::Decl { impl::Parameter_list inputs; util::ref init; explicit Concept(const ipr::Region& r) : inputs{r, { }} { } const ipr::Parameter_list& parameters() const final { return inputs; } const ipr::Expr& result() const final { return init.get(); } Optional initializer() const final { return { *this }; } }; // For a non-defining function declaration, the parameters - if any is named // or has a default argument - are stored with that particular declaration. // A function definition is one that specifies the initializer (Mapping) which // already has room for the named parameters or the default arguments. struct fundecl_data : std::variant { ipr::FunctionTraits traits { }; impl::Parameter_list* parameters() const { return std::get<0>(*this); } impl::Mapping* mapping() const { return std::get<1>(*this); } }; struct Fundecl : impl::Decl { fundecl_data data; util::ref lexreg; Fundecl(); ipr::FunctionTraits traits() const final { return data.traits; } const ipr::Parameter_list& parameters() const final; Optional mapping() const final; Optional initializer() const final; const ipr::Region& lexical_region() const final { return lexreg.get(); } }; // A heterogeneous scope is a sequence of declarations of // almost of kinds. The omitted kinds being parameters, // base-class subobjects and enumerators. Those form // a homogeneous scope, implemented by homogeneous_scope. struct Scope : immotile_node { Scope(); const ipr::Type& type() const final { return decls; } const ipr::Sequence& elements() const final { return decls.seq; } Optional operator[](const ipr::Name&) const final; impl::Alias* make_alias(const ipr::Name&, const ipr::Expr&); impl::Var* make_var(const ipr::Name&, const ipr::Type&); impl::Field* make_field(const ipr::Name&, const ipr::Type&); impl::Bitfield* make_bitfield(const ipr::Name&, const ipr::Type&); impl::Typedecl* make_typedecl(const ipr::Name&, const ipr::Type&); impl::Fundecl* make_fundecl(const ipr::Name&, const ipr::Function&); impl::Template* make_primary_template(const ipr::Name&, const ipr::Forall&); impl::Template* make_secondary_template(const ipr::Name&, const ipr::Forall&); private: util::rb_tree::container overloads; typed_sequence decls; decl_factory aliases; decl_factory vars; decl_factory fields; decl_factory bitfields; decl_factory fundecls; decl_factory typedecls; decl_factory primary_maps; decl_factory secondary_maps; template inline void add_member(T*); }; // A heterogeneous region is a region of program text that // contains heterogeneous scope (as defined above). struct Region : immotile_node, cxx_form::impl::form_factory { using location_span = ipr::Region::Location_span; Optional parent; location_span extent; Optional owned_by { }; impl::Scope scope; impl::ref_sequence expr_seq; // all the expressions making up the body. const ipr::Region& enclosing() const final { return parent.get(); } const ipr::Sequence& body() const final { return expr_seq; } const ipr::Scope& bindings() const final { return scope; } const location_span& span() const final { return extent; } Optional owner() const final { return owned_by; } bool global() const final { return not parent.is_valid(); } impl::Region* make_subregion(); // Convenient functions, forwarding to those of SCOPE. impl::Alias* declare_alias(const ipr::Name& n, const ipr::Type& t) { return scope.make_alias(n, t); } impl::Var* declare_var(const ipr::Name& n, const ipr::Type& t) { return scope.make_var(n, t); } impl::Field* declare_field(const ipr::Name& n, const ipr::Type& t) { return scope.make_field(n, t); } impl::Bitfield* declare_bitfield(const ipr::Name& n, const ipr::Type& t) { return scope.make_bitfield(n, t); } Typedecl* declare_type(const ipr::Name& n, const ipr::Type& t) { return scope.make_typedecl(n, t); } Fundecl* declare_fun(const ipr::Name& n, const ipr::Function& t) { return scope.make_fundecl(n, t); } Template* declare_primary_template(const ipr::Name& n, const ipr::Forall& t) { return scope.make_primary_template(n, t); } Template* declare_secondary_template(const ipr::Name& n, const ipr::Forall& t) { return scope.make_secondary_template(n, t); } explicit Region(Optional); private: stable_farm subregions; }; // Implement common operations for user-defined types. The case // of enums is handled separately because its body is a // homogeneous region. template struct Udt : impl::Type { Region body; Optional id; explicit Udt(const ipr::Region* pr) : body(pr) { body.owned_by = this; } const ipr::Name& name() const final { return id.get(); } const ipr::Region& region() const final { return body; } impl::Alias* declare_alias(const ipr::Name& n, const ipr::Type& t) { impl::Alias* alias = body.declare_alias(n, t); return alias; } impl::Field* declare_field(const ipr::Name& n, const ipr::Type& t) { impl::Field* field = body.declare_field(n, t); return field; } impl::Bitfield* declare_bitfield(const ipr::Name& n, const ipr::Type& t) { impl::Bitfield* field = body.declare_bitfield(n, t); return field; } impl::Var* declare_var(const ipr::Name& n, const ipr::Type& t) { impl::Var* var = body.declare_var(n, t); return var; } impl::Typedecl* declare_type(const ipr::Name& n, const ipr::Type& t) { impl::Typedecl* typedecl = body.declare_type(n, t); return typedecl; } impl::Fundecl* declare_fun(const ipr::Name& n, const ipr::Function& t) { impl::Fundecl* fundecl = body.declare_fun(n, t); return fundecl; } impl::Template* declare_primary_template(const ipr::Name& n, const ipr::Forall& t) { impl::Template* map = body.declare_primary_template(n, t); return map; } impl::Template* declare_secondary_template(const ipr::Name& n, const ipr::Forall& t) { impl::Template* map = body.declare_secondary_template(n, t); return map; } }; struct Enum : impl::Type { Optional underlying; homogeneous_region body; Optional id; const Kind enum_kind; Enum(const ipr::Region&, Kind); const ipr::Name& name() const final { return id.get(); } const ipr::Type& type() const final; const ipr::Region& region() const final; const Sequence& members() const final; Kind kind() const final; Optional base() const final { return underlying; } impl::Enumerator* add_member(const ipr::Name&); }; struct Union : Udt { explicit Union(const ipr::Region&); const ipr::Type& type() const final; }; struct Namespace : Udt { explicit Namespace(const ipr::Region*); const ipr::Type& type() const final; }; struct Class : impl::Udt { homogeneous_region base_subobjects; explicit Class(const ipr::Region&); const ipr::Type& type() const final; const ipr::Sequence& bases() const final; impl::Base_type* declare_base(const ipr::Type&); }; struct Auto : impl::Composite { }; struct Closure : impl::Udt { impl::obj_list captures; explicit Closure(const ipr::Region&); const ipr::Type& type() const final; const ipr::Sequence& members() const final { return captures; } }; // This class is responsible for creating nodes that // represent types. It is responsible for the storage // management that is implied. Notice that the type nodes // created by this class may need additional processing such // as setting their types (as expressions) and their names. struct type_factory { const ipr::Transfer& get_transfer_from_linkage(const ipr::Language_linkage&); const ipr::Transfer& get_transfer_from_convention(const ipr::Calling_convention&); const ipr::Transfer& get_transfer(const ipr::Language_linkage&, const ipr::Calling_convention&); // Build an IPR node for an expression that denotes a type. // The transfer protocol, if not specified, is assumed to be C++. const ipr::As_type& get_as_type(const ipr::Identifier&); const ipr::As_type& get_as_type(const ipr::Expr&); const ipr::As_type& get_as_type(const ipr::Expr&, const ipr::Transfer&); const ipr::Array& get_array(const ipr::Type&, const ipr::Expr&); const ipr::Qualified& get_qualified(ipr::Qualifiers, const ipr::Type&); const ipr::Decltype& get_decltype(const ipr::Expr&); const ipr::Tor& get_tor(const ipr::Product&, const ipr::Sum&); const ipr::Function& get_function(const ipr::Product&, const ipr::Type&); const ipr::Function& get_function(const ipr::Product&, const ipr::Type&, const ipr::Transfer&); const ipr::Function& get_function(const ipr::Product&, const ipr::Type&, const ipr::Expr&); const ipr::Function& get_function(const ipr::Product&, const ipr::Type&, const ipr::Expr&, const ipr::Transfer&); const ipr::Pointer& get_pointer(const ipr::Type&); const ipr::Product& get_product(const ipr::Sequence&); const ipr::Product& get_product(const Warehouse&); const ipr::Ptr_to_member& get_ptr_to_member(const ipr::Type&, const ipr::Type&); const ipr::Reference& get_reference(const ipr::Type&); const ipr::Rvalue_reference& get_rvalue_reference(const ipr::Type&); const ipr::Sum& get_sum(const ipr::Sequence&); const ipr::Sum& get_sum(const Warehouse&); const ipr::Forall& get_forall(const ipr::Product&, const ipr::Type&); const ipr::Auto& get_auto(); impl::Enum* make_enum(const ipr::Region&, Enum::Kind); impl::Class* make_class(const ipr::Region&); impl::Union* make_union(const ipr::Region&); impl::Namespace* make_namespace(const ipr::Region&); impl::Closure* make_closure(const ipr::Region&); private: util::rb_tree::container xfer_links; util::rb_tree::container xfer_ccs; util::rb_tree::container xfers; util::rb_tree::container extendeds; util::rb_tree::container arrays; util::rb_tree::container type_refs; util::rb_tree::container type_xfers; util::rb_tree::container tors; util::rb_tree::container functions; util::rb_tree::container fun_xfers; util::rb_tree::container pointers; util::rb_tree::container products; util::rb_tree::container member_ptrs; util::rb_tree::container qualifieds; util::rb_tree::container references; util::rb_tree::container refrefs; util::rb_tree::container sums; util::rb_tree::container foralls; util::rb_tree::container> type_seqs; stable_farm decltypes; stable_farm enums; stable_farm classes; stable_farm unions; stable_farm namespaces; stable_farm closures; stable_farm autos; }; // -- Implementation of directives -- struct Specifiers_spread : impl::Directive { impl::ref_sequence proc_seq; ipr::Specifiers specs { }; const ipr::Sequence& targets() const final { return proc_seq; } ipr::Specifiers specifiers() const final { return specs; } }; struct Structured_binding : impl::Directive { impl::ref_sequence ids; // names in this structured binding util::ref init; // initializer of this structured binding impl::ref_sequence decl_seq; // declarations resulting from this structured binding ipr::Specifiers specs { }; // the non-type part of decl-specifier-seq in // this structured binding. Note: `type()` contains the // the type part of the decl-specifier-seq ipr::Binding_mode binding_mode { }; // the binding mode of this structured binding ipr::Specifiers specifiers() const final { return specs; } ipr::Binding_mode mode() const final { return binding_mode; } const ipr::Sequence& names() const final { return ids; } const ipr::Expr& initializer() const final { return init.get(); } const ipr::Sequence& bindings() const final { return decl_seq; } }; // Implementation of ipr::Using_declaration in case where using-declarator-list is a singleton. struct single_using_declaration : impl::Directive { single_using_declaration(const ipr::Scope_ref&, Designator::Mode); const ipr::Sequence& designators() const final { return what; } private: singleton_obj what; }; struct Using_declaration : impl::Directive { impl::obj_list seq; const ipr::Sequence& designators() const final { return seq; } }; struct Using_directive : impl::Directive { explicit Using_directive(const ipr::Scope&); const ipr::Scope& nominated_scope() const final { return scope; } private: const ipr::Scope& scope; }; // -- impl::Phased_evaluation struct Phased_evaluation : immotile_node { Phased_evaluation(const ipr::Expr& x, Phases f) : expr{x}, ph{f} { } Phases phases() const final { return ph; } const ipr::Expr& expression() const final { return expr; } private: const ipr::Expr& expr; Phases ph; }; struct Pragma : impl::Directive { obj_list tokens; const ipr::Sequence& operand() const final { return tokens; } }; // ---------------------------------- // -- Implementation of statements -- // ---------------------------------- using Ctor_body = impl::Basic_binary>>; using Do = Controlled_stmt; using Expr_stmt = impl::Unary_node>; using Goto = impl::Unary_node>; using If = Ternary>>; using Labeled_stmt = impl::Binary_node>; using Return = impl::Basic_unary>>; using Switch = Controlled_stmt; using While = Controlled_stmt; // -- impl::EH_parameter struct EH_parameter : unique_decl { EH_parameter(const ipr::Region&, const ipr::Name&, const ipr::Type&); const ipr::Name& name() const final { return id; } const ipr::Type& type() const final { return typing; } const ipr::Region& home_region() const final { return home; } const ipr::Region& lexical_region() const final { return home; } private: const ipr::Name& id; const ipr::Type& typing; const ipr::Region& home; }; // -- impl::handler_block // The block-statement body of a handler. It has no associated EH handlers of its own. struct handler_block : impl::Stmt> { impl::Region lexical_region; handler_block(const ipr::Region&); const ipr::Region& region() const final { return lexical_region; } const ipr::Sequence& handlers() const final { return none; } // The scope of declarations in this block impl::Scope* scope() { return &lexical_region.scope; } void add_stmt(const ipr::Expr& s) { lexical_region.expr_seq.push_back(&s); } private: impl::empty_sequence none; }; // -- impl::eh_region // The region containing the declaration of the exception parameter of a Handler. // Like for function parameter region, this region encloses the region of the // ipr::Block (body) associated with the Handler. // Note: the enclosing() region of this eh_region is the same as the enclosing // region() of the Block with which the Handler is associated. struct eh_region : homogeneous_region { using base = homogeneous_region; using base::base; const ipr::EH_parameter& parameter() const { return scope.decls.seq.element(); } }; // -- impl::Handler struct Handler : immotile_stmt { Handler(const ipr::Region&, const ipr::Name&, const ipr::Type&); const ipr::Type& type() const final { return block.type(); } const ipr::EH_parameter& exception() const final { return eh.parameter(); } const ipr::Block& body() const final { return block; } impl::handler_block& body() { return block; } private: eh_region eh; impl::handler_block block; }; // A Block holds a heterogeneous region, suitable for // recording the set of declarations appearing in that // block. It also holds a sequence of handlers, when the // block actually represents a C++ try-block. struct Block : impl::Stmt> { impl::Region lexical_region; explicit Block(const ipr::Region&); const ipr::Region& region() const final { return lexical_region; } const ipr::Sequence& handlers() const final { return handler_seq; } // The scope of declarations in this block impl::Scope* scope() { return &lexical_region.scope; } impl::Handler* new_handler(const ipr::Name&, const ipr::Type&); void add_stmt(const ipr::Expr& s) { lexical_region.expr_seq.push_back(&s); } private: impl::obj_list handler_seq; }; // A for-statement node in its most general form is a quaternry // expresion; for flexibility, it is made in a way that // supports settings of its components after construction. struct For : immotile_stmt { util::ref init; util::ref cond; util::ref inc; util::ref stmt; For(); const ipr::Type& type() const final { return body().type(); } const ipr::Expr& initializer() const final { return init.get(); } const ipr::Expr& condition() const final { return cond.get(); } const ipr::Expr& increment() const final { return inc.get(); } const ipr::Stmt& body() const final { return stmt.get(); } }; struct For_in : immotile_stmt { util::ref var; util::ref seq; util::ref stmt; For_in(); const ipr::Type& type() const final { return body().type(); } const ipr::Var& variable() const final { return var.get(); } const ipr::Expr& sequence() const final { return seq.get(); } const ipr::Stmt& body() const final { return stmt.get(); } }; // A Break node can record the selction- of iteration-statement it // transfers control out of. struct Break : immotile_stmt { util::ref stmt; Break(); const ipr::Type& type() const final; const ipr::Stmt& from() const final { return stmt.get(); } }; // Like a Break, a Continue node can refer back to the // iteration-statement containing it. struct Continue : immotile_stmt { util::ref stmt; Continue(); const ipr::Type& type() const final; const ipr::Stmt& iteration() const final { return stmt.get(); } }; // Type of `Where`-nodes introducing declarations in their `attendant()` expressions. // Note: See impl::Where_no_decls for the degenerated case where the `attendant()` // is just a classic expression, with no declaration introduced. struct Where : immotile_node { impl::Region region; // hosts the declarations in the `attendant()`. util::ref result; // the `main()` expression. explicit Where(const ipr::Region&); const ipr::Expr& first() const final { return result.get(); } const ipr::Scope& second() const final { return region.bindings(); } }; // -- impl::Static_assert struct Static_assert : impl::Binary_node { Static_assert(const ipr::Expr&, Optional); const ipr::Type& type() const final; }; struct name_factory { const ipr::String& get_string(util::word_view); const ipr::Identifier& get_identifier(const ipr::String&); const ipr::Identifier& get_identifier(util::word_view); const ipr::Suffix& get_suffix(const ipr::Identifier&); const ipr::Operator& get_operator(const ipr::String&); const ipr::Operator& get_operator(util::word_view); const ipr::Conversion& get_conversion(const ipr::Type&); const ipr::Ctor_name& get_ctor_name(const ipr::Type&); const ipr::Dtor_name& get_dtor_name(const ipr::Type&); const ipr::Guide_name& get_guide_name(const ipr::Template&); const ipr::Logogram& get_logogram(const ipr::String&); private: util::string_pool strings; util::rb_tree::container logos; util::rb_tree::container ids; util::rb_tree::container suffixes; util::rb_tree::container convs; util::rb_tree::container ctors; util::rb_tree::container dtors; util::rb_tree::container ops; util::rb_tree::container guide_ids; }; struct expr_factory : name_factory { // Returns an IPR node a language linkage. const ipr::Language_linkage& get_linkage(util::word_view); const ipr::Language_linkage& get_linkage(const ipr::String&); // Rerturn an IPR for a calling convention. const ipr::Calling_convention& get_calling_convention(util::word_view); // Return a symbol with a given name and type. const ipr::Symbol& get_symbol(const ipr::Name&, const ipr::Type&); const ipr::Symbol& get_label(const ipr::Identifier&); // Return a symbolic value for `this` with a given type. const ipr::Symbol& get_this(const ipr::Type&); Annotation* make_annotation(const ipr::String&, const ipr::Literal&); // Build a node for a missing expression of an unspecified type. Phantom* make_phantom(); // Build an unspecified expression node of a given type. const ipr::Phantom* make_phantom(const ipr::Type&); Eclipsis* make_eclipsis(const ipr::Type&); // Returns an IPR node for a typed literal expression. Literal* make_literal(const ipr::Type&, const ipr::String&); Literal* make_literal(const ipr::Type&, util::word_view); Address* make_address(const ipr::Expr&, Optional = {}); Array_delete* make_array_delete(const ipr::Expr&); Complement* make_complement(const ipr::Expr&, Optional = {}); Delete* make_delete(const ipr::Expr&); Demotion* make_demotion(const ipr::Expr&, const ipr::Type&); Deref* make_deref(const ipr::Expr&, Optional = {}); Expr_list* make_expr_list(); Alignof* make_alignof(const ipr::Expr&, Optional = { }); Sizeof* make_sizeof(const ipr::Expr&, Optional = { }); Args_cardinality* make_args_cardinality(const ipr::Expr&, Optional = { }); Typeid* make_typeid(const ipr::Expr&, Optional = { }); Restriction* make_restriction(const ipr::Expr&); impl::Id_expr* make_id_expr(const ipr::Name&, Optional = {}); Id_expr* make_id_expr(const ipr::Decl&); Label* make_label(const ipr::Identifier&, Optional = {}); Materialization* make_materialization(const ipr::Expr&, const ipr::Type&); Not* make_not(const ipr::Expr&, Optional = {}); Enclosure* make_enclosure(ipr::Delimiter, const ipr::Expr&, Optional = { }); Post_increment* make_post_increment(const ipr::Expr&, Optional = {}); Post_decrement* make_post_decrement(const ipr::Expr&, Optional = {}); Pre_increment* make_pre_increment(const ipr::Expr&, Optional = {}); Pre_decrement* make_pre_decrement(const ipr::Expr&, Optional = {}); Promotion* make_promotion(const ipr::Expr&, const ipr::Type&); Read* make_read(const ipr::Expr&, const ipr::Type&); Throw* make_throw(const ipr::Expr&, Optional = {}); Unary_minus* make_unary_minus(const ipr::Expr&, Optional = {}); Unary_plus* make_unary_plus(const ipr::Expr&, Optional = {}); Expansion* make_expansion(const ipr::Expr&, Optional = {}); Construction* make_construction(const ipr::Type&, const ipr::Enclosure&); Noexcept* make_noexcept(const ipr::Expr&, Optional = { }); Rewrite* make_rewrite(const ipr::Expr&, const ipr::Expr&); And* make_and(const ipr::Expr&, const ipr::Expr&, Optional = {}); Array_ref* make_array_ref(const ipr::Expr&, const ipr::Expr&, Optional = {}); Arrow* make_arrow(const ipr::Expr&, const ipr::Expr&, Optional = {}); Arrow_star* make_arrow_star(const ipr::Expr&, const ipr::Expr&, Optional = {}); Assign* make_assign(const ipr::Expr&, const ipr::Expr&, Optional = {}); Bitand* make_bitand(const ipr::Expr&, const ipr::Expr&, Optional = {}); Bitand_assign* make_bitand_assign(const ipr::Expr&, const ipr::Expr&, Optional = {}); Bitor* make_bitor(const ipr::Expr&, const ipr::Expr&, Optional = {}); Bitor_assign* make_bitor_assign(const ipr::Expr&, const ipr::Expr&, Optional = {}); Bitxor* make_bitxor(const ipr::Expr&, const ipr::Expr&, Optional = {}); Bitxor_assign* make_bitxor_assign(const ipr::Expr&, const ipr::Expr&, Optional = {}); Cast* make_cast(const ipr::Type&, const ipr::Expr&); Call* make_call(const ipr::Expr&, const ipr::Expr_list&, Optional = {}); Coercion* make_coercion(const ipr::Expr&, const ipr::Type&, const ipr::Type&); Comma* make_comma(const ipr::Expr&, const ipr::Expr&, Optional = {}); Const_cast* make_const_cast(const ipr::Type&, const ipr::Expr&); Div* make_div(const ipr::Expr&, const ipr::Expr&, Optional = {}); Div_assign* make_div_assign(const ipr::Expr&, const ipr::Expr&, Optional = {}); Dot* make_dot(const ipr::Expr&, const ipr::Expr&, Optional = {}); Dot_star* make_dot_star(const ipr::Expr&, const ipr::Expr&, Optional = {}); Dynamic_cast* make_dynamic_cast(const ipr::Type&, const ipr::Expr&); Equal* make_equal(const ipr::Expr&, const ipr::Expr&, Optional = {}); Greater* make_greater(const ipr::Expr&, const ipr::Expr&, Optional = {}); Greater_equal* make_greater_equal(const ipr::Expr&, const ipr::Expr&, Optional = {}); Less* make_less(const ipr::Expr&, const ipr::Expr&, Optional = {}); Less_equal* make_less_equal(const ipr::Expr&, const ipr::Expr&, Optional = {}); Lshift* make_lshift(const ipr::Expr&, const ipr::Expr&, Optional = {}); Lshift_assign* make_lshift_assign(const ipr::Expr&, const ipr::Expr&, Optional = {}); Member_init* make_member_init(const ipr::Expr&, const ipr::Expr&, Optional = {}); Minus* make_minus(const ipr::Expr&, const ipr::Expr&, Optional = {}); Minus_assign* make_minus_assign(const ipr::Expr&, const ipr::Expr&, Optional = {}); Modulo* make_modulo(const ipr::Expr&, const ipr::Expr&, Optional = {}); Modulo_assign* make_modulo_assign(const ipr::Expr&, const ipr::Expr&, Optional = {}); Mul* make_mul(const ipr::Expr&, const ipr::Expr&, Optional = {}); Mul_assign* make_mul_assign(const ipr::Expr&, const ipr::Expr&, Optional = {}); Narrow* make_narrow(const ipr::Expr&, const ipr::Type&, const ipr::Type&); Not_equal* make_not_equal(const ipr::Expr&, const ipr::Expr&, Optional = {}); Or* make_or(const ipr::Expr&, const ipr::Expr&, Optional = {}); Plus* make_plus(const ipr::Expr&, const ipr::Expr&, Optional = {}); Plus_assign* make_plus_assign(const ipr::Expr&, const ipr::Expr&, Optional = {}); Pretend* make_pretend(const ipr::Expr&, const ipr::Type&, const ipr::Type&); Qualification* make_qualification(const ipr::Expr&, ipr::Qualifiers, const ipr::Type&); Reinterpret_cast* make_reinterpret_cast(const ipr::Type&, const ipr::Expr&); Scope_ref* make_scope_ref(const ipr::Expr&, const ipr::Expr&, Optional = {}); Rshift* make_rshift(const ipr::Expr&, const ipr::Expr&, Optional = {}); Rshift_assign* make_rshift_assign(const ipr::Expr&, const ipr::Expr&, Optional = {}); Template_id* make_template_id(const ipr::Expr&, const ipr::Expr_list&); Static_cast* make_static_cast(const ipr::Type&, const ipr::Expr&); Widen* make_widen(const ipr::Expr&, const ipr::Type&, const ipr::Type&); Binary_fold* make_binary_fold(Category_code, const ipr::Expr&, const ipr::Expr&, Optional = {}); Where* make_where(const ipr::Region&); Where_no_decl* make_where(const ipr::Expr&, const ipr::Expr&); impl::Instantiation* make_instantiation(const ipr::Expr&, const ipr::Substitution&); New* make_new(Optional, const ipr::Construction&, Optional = {}); Conditional* make_conditional(const ipr::Expr&, const ipr::Expr&, const ipr::Expr&, Optional = {}); Mapping* make_mapping(const ipr::Region&, Mapping_level); Lambda* make_lambda(const ipr::Region&, Mapping_level); Requires* make_requires(const ipr::Region&, Mapping_level); Elementary_substitution* make_elementary_substitution(const ipr::Parameter&, const ipr::Expr&); General_substitution* make_general_substitution(); protected: // Note: These factory functions are not to be called directly. See impl::Lexicon for their equivalent. impl::Asm* make_asm_expr(const ipr::String&); impl::Static_assert* make_static_assert_expr(const ipr::Expr&, Optional = { }); private: util::rb_tree::container linkages; util::rb_tree::container conventions; util::rb_tree::container lits; util::rb_tree::container template_ids; stable_farm phantoms; stable_farm eclipses; util::rb_tree::container symbols; stable_farm alignofs; stable_farm sizeofs; stable_farm xtypeids; stable_farm addresses; stable_farm annotations; stable_farm array_deletes; stable_farm asms; stable_farm complements; stable_farm deletes; stable_farm demotions; stable_farm derefs; stable_farm xlists; stable_farm id_exprs; stable_farm labels; stable_farm materializations; stable_farm nots; stable_farm enclosures; stable_farm pre_increments; stable_farm pre_decrements; stable_farm post_increments; stable_farm post_decrements; stable_farm promotions; stable_farm reads; stable_farm throws; stable_farm unary_minuses; stable_farm unary_pluses; stable_farm expansions; stable_farm constructions; stable_farm noexcepts; stable_farm cardinalities; stable_farm restrictions; stable_farm rewrites; stable_farm scope_refs; stable_farm ands; stable_farm array_refs; stable_farm arrows; stable_farm arrow_stars; stable_farm assigns; stable_farm bitands; stable_farm bitand_assigns; stable_farm bitors; stable_farm bitor_assigns; stable_farm bitxors; stable_farm bitxor_assigns; stable_farm casts; stable_farm calls; stable_farm commas; stable_farm ccasts; stable_farm divs; stable_farm div_assigns; stable_farm dots; stable_farm dot_stars; stable_farm dcasts; stable_farm equals; stable_farm greaters; stable_farm greater_equals; stable_farm lesses; stable_farm less_equals; stable_farm lshifts; stable_farm lshift_assigns; stable_farm member_inits; stable_farm minuses; stable_farm minus_assigns; stable_farm modulos; stable_farm modulo_assigns; stable_farm muls; stable_farm mul_assigns; stable_farm narrows; stable_farm not_equals; stable_farm ors; stable_farm pluses; stable_farm plus_assigns; stable_farm pretends; stable_farm qualifications; stable_farm rcasts; stable_farm rshifts; stable_farm rshift_assigns; stable_farm scasts; stable_farm widens; stable_farm folds; stable_farm where_nodecls; stable_farm wheres; stable_farm asserts; stable_farm insts; stable_farm news; stable_farm coercions; stable_farm conds; stable_farm mappings; stable_farm lambdas; stable_farm reqs; stable_farm elem_substs; stable_farm gen_substs; }; struct dir_factory { impl::Specifiers_spread* make_specifiers_spread(); impl::Structured_binding* make_structured_binding(); impl::single_using_declaration* make_using_declaration(const ipr::Scope_ref&, ipr::Using_declaration::Designator::Mode); impl::Using_declaration* make_using_declaration(); impl::Using_directive* make_using_directive(const ipr::Scope&, const ipr::Type&); impl::Phased_evaluation* make_phased_evaluation(const ipr::Expr&, Phases); impl::Pragma* make_pragma(); private: stable_farm spreads; stable_farm bindings; stable_farm singles; stable_farm usings; stable_farm dirs; stable_farm phaseds; stable_farm pragmas; }; // This factory class takes on the implementation burden of // allocating storage for statement nodes and their constructions. struct stmt_factory : expr_factory, dir_factory { impl::Break* make_break(); impl::Continue* make_continue(); impl::Block* make_block(const ipr::Region&, Optional = { }); impl::Ctor_body* make_ctor_body(const ipr::Expr_list&, const ipr::Block&); impl::Expr_stmt* make_expr_stmt(const ipr::Expr&); impl::Goto* make_goto(const ipr::Expr&); impl::Return* make_return(const ipr::Expr&); impl::Do* make_do(); impl::If* make_if(const ipr::Expr&, const ipr::Expr&); impl::If* make_if(const ipr::Expr&, const ipr::Expr&, const ipr::Expr&); impl::Switch* make_switch(); impl::Labeled_stmt* make_labeled_stmt(const ipr::Expr&, const ipr::Expr&); impl::While* make_while(); impl::For* make_for(); impl::For_in* make_for_in(); protected: stable_farm breaks; stable_farm continues; stable_farm blocks; stable_farm expr_stmts; stable_farm gotos; stable_farm returns; stable_farm ctor_bodies; stable_farm dos; stable_farm ifs; stable_farm handlers; stable_farm labeled_stmts; stable_farm switches; stable_farm whiles; stable_farm fors; stable_farm for_ins; }; // -- impl::Lexicon -- struct Lexicon : ipr::Lexicon, type_factory, stmt_factory { Lexicon(); ~Lexicon(); const ipr::Language_linkage& cxx_linkage() const final; const ipr::Language_linkage& c_linkage() const final; ipr::Specifiers export_specifier() const final; ipr::Specifiers static_specifier() const final; ipr::Specifiers extern_specifier() const final; ipr::Specifiers mutable_specifier() const final; ipr::Specifiers constinit_specifier() const final; ipr::Specifiers thread_local_specifier() const final; ipr::Specifiers register_specifier() const final; ipr::Specifiers inline_specifier() const final; ipr::Specifiers constexpr_specifier() const final; ipr::Specifiers consteval_specifier() const final; ipr::Specifiers virtual_specifier() const final; ipr::Specifiers abstract_specifier() const final; ipr::Specifiers explicit_specifier() const final; ipr::Specifiers friend_specifier() const final; ipr::Specifiers typedef_specifier() const final; ipr::Specifiers public_specifier() const final; ipr::Specifiers protected_specifier() const final; ipr::Specifiers private_specifier() const final; ipr::Specifiers specifiers(ipr::Basic_specifier) const final; std::vector decompose(ipr::Specifiers) const final; ipr::Qualifiers const_qualifier() const final; ipr::Qualifiers volatile_qualifier() const final; ipr::Qualifiers restrict_qualifier() const final; ipr::Qualifiers qualifiers(ipr::Basic_qualifier) const final; std::vector decompose(ipr::Qualifiers) const final; const ipr::Template_id& get_template_id(const ipr::Expr&, const ipr::Expr_list&); const ipr::Literal& get_literal(const ipr::Type&, util::word_view); const ipr::Literal& get_literal(const ipr::Type&, const ipr::String&); const ipr::Type& void_type() const final; const ipr::Type& bool_type() const final; const ipr::Type& char_type() const final; const ipr::Type& schar_type() const final; const ipr::Type& uchar_type() const final; const ipr::Type& wchar_t_type() const final; const ipr::Type& char8_t_type() const final; const ipr::Type& char16_t_type() const final; const ipr::Type& char32_t_type() const final; const ipr::Type& short_type() const final; const ipr::Type& ushort_type() const final; const ipr::Type& int_type() const final; const ipr::Type& uint_type() const final; const ipr::Type& long_type() const final; const ipr::Type& ulong_type() const final; const ipr::Type& long_long_type() const final; const ipr::Type& ulong_long_type() const final; const ipr::Type& float_type() const final; const ipr::Type& double_type() const final; const ipr::Type& long_double_type() const final; const ipr::Type& ellipsis_type() const final; const ipr::Type& typename_type() const final; const ipr::Type& class_type() const final; const ipr::Type& union_type() const final; const ipr::Type& enum_type() const final; const ipr::Type& namespace_type() const final; const ipr::Symbol& false_value() const final; const ipr::Symbol& true_value() const final; const ipr::Symbol& nullptr_value() const final; const ipr::Symbol& default_value() const final; const ipr::Symbol& delete_value() const final; impl::Phased_evaluation* make_asm(const ipr::String&); impl::Phased_evaluation* make_static_assert(const ipr::Expr&, Optional); impl::Mapping* make_mapping(const ipr::Region&, Mapping_level = { }); const impl::Token* make_token(const ipr::String&, const Source_location&, TokenValue, TokenCategory); private: stable_farm tokens; util::rb_tree::container> expr_seqs; }; template struct unit_base : T { using Interface = T; unit_base(impl::Lexicon& l) : context{ l }, global_ns{nullptr} { global_ns.id = &context.get_identifier(u8""); } void accept(Translation_unit::Visitor& v) const override { v.visit(*this); } const ipr::Namespace& global_namespace() const final { return global_ns; } const ipr::Sequence& imported_modules() const final { return modules_imported; } Region* global_region() { return &global_ns.body; } Scope* global_scope() { return &global_ns.body.scope; } ref_sequence* imports() { return &modules_imported; } private: impl::Lexicon& context; impl::Namespace global_ns; ref_sequence modules_imported; }; template struct basic_unit : unit_base { const ipr::Module& parent; impl::ref_sequence owned_decls; basic_unit(impl::Lexicon& l, const ipr::Module& m) : unit_base{ l }, parent{ m } { } const ipr::Module& parent_module() const final { return parent; } const ipr::Sequence& purview() const final { return owned_decls; } }; using Translation_unit = unit_base; using Module_unit = basic_unit; struct Interface_unit : basic_unit { impl::ref_sequence modules_exported; impl::ref_sequence decls_exported; Interface_unit(impl::Lexicon&, const ipr::Module&); const ipr::Sequence& exported_modules() const final; const ipr::Sequence& exported_declarations() const final; }; struct Module : ipr::Module { using ImplUnits = impl::obj_list; impl::Lexicon& lexicon; impl::Module_name stems; impl::Interface_unit iface; ImplUnits units; Module(impl::Lexicon&); const ipr::Module_name& name() const final; const ipr::Interface_unit& interface_unit() const final; const ipr::Sequence& implementation_units() const final; impl::Module_unit* make_unit(); }; } #endif ================================================ FILE: include/ipr/input ================================================ // -*- C++ -*- // // This file is part of The Pivot framework. // Written by Gabriel Dos Reis. // See LICENSE for copyright and license notices. // #include #include #include #include namespace ipr::input { // Type for the error code values used by the host OS. #ifdef _WIN32 using ErrorCode = DWORD; #else using ErrorCode = int; #endif // String type preferred by the host OS to specify pathnames. using SystemPath = std::filesystem::path::string_type; // Exception type used to signal inability of the host OS to access a file. struct AccessError { SystemPath path; ErrorCode error_code; }; // Exception type used to signal the file designated by th `path` is not a regular file. struct RegularFileError { SystemPath path; }; // Exception type used to signal inability of the host OS to memory-map a file. struct FileMappingError { SystemPath path; ErrorCode error_code; }; // Abstract number of items in a non-empty collection. enum class Multiplicity : std::uint8_t { One = 0x0, Many = 0x1 }; // A morsel is a piece of source text either as contiguous sequence of bytes (isle), // or as a sequence of isles (archipelago). struct Isle; struct Archipelago; struct Morsel { Multiplicity multiplicity() const { return static_cast(tag); } auto start() const { return where; } auto size() const { return extent; } private: Morsel(std::uint64_t w, std::uint64_t e, std::uint64_t t) : where{w}, extent{e}, tag{t} { } std::uint64_t where : 47; // where to find items in this morsel. std::uint64_t extent : 16; // number of items in this morsel. const std::uint64_t tag : 1; // discriminant between isle and achipelago. friend Isle; friend Archipelago; }; static_assert(sizeof(Morsel) == sizeof(std::uint64_t)); // An isle of text is contiguous morsel, coded over 63-bit out of the 64-bit precision of a morsel. struct Isle { std::uint64_t offset : 47; // offset from the beginning of containing text std::uint64_t length : 16; // number of bytes from the start const std::uint64_t tag : 1 = 0; // discriminant between isle and archipelago. Always 0. operator Morsel() const { return { offset, length, tag }; } }; static_assert(sizeof(Isle) == sizeof(Morsel)); // An archipelago of text is a collection of isles, coded over 63-bit of the 64-bit precision of a morsel. struct Archipelago { std::uint64_t start : 47; // index of the first isle in this archipelago. std::uint64_t count : 16; // number of isles in this archipelago. const std::uint64_t tag : 1 = 1; // discriminant between isle and archipelago. Always 1. operator Morsel() const { return { start, count, tag }; } }; static_assert(sizeof(Archipelago) == sizeof(Morsel)); // Descriptor for a physical line from an input source file. struct PhysicalLine { Isle isle { }; std::uint64_t number : 48 { }; std::uint64_t indent : 16 { }; bool empty() const { return isle.length == 0; } }; static_assert(sizeof(PhysicalLine) == 2 * sizeof(Morsel)); // A logical line is either a simple phyiscal line or a composite of multiple // physical lines spliced together. enum class LineSort : std::uint8_t { Simple = 0x00, // A simple, non-continuating physical line. Composite = 0x01, // Result of spliced multiple physical lines. }; // Classification of input logical lines. enum class LineSpecies : std::uint8_t { Unknown = 0x00, Text, // text line SolitaryHash, // `#` by itself on a line If, // `#if` line Ifdef, // `#ifdef` line Ifndef, // `#ifndef` line Elif, // `#elif` line Elifdef, // `#elifdef` line Elifndef, // `#elifndef` line Else, // `#else` line Endif, // `#endif` line Include, // `#include` line Export, // `export` line Import, // `import` line Embed, // `#embed` line Define, // `#define` line Undef, // `#undef` line Line, // `#line` line Error, // `#error` line Warning, // `#warning` line Pragma, // `#pragma` line ExtendedDirective, // Some other `#` directive }; // This predicate holds if its argument designates an explicitly listed enumerator. bool valid_species(LineSpecies); // A physical line that is a logical line by itself, e.g. not ending with a backslash. struct SimpleLine { PhysicalLine line; }; // An aggregate of physical lines spliced to form a logical line. struct CompositeLine { std::vector lines; }; // A descriptor for a logical line: // (a) whether it is simple or composite // (b) preprocessing line classification // (c) index into the appropriate table to retrieve its physical location // Note: a line descritptor is designed to fit in a 64-bit integer storage. struct LineDescriptor { LineDescriptor(LineSort, LineSpecies, std::uint64_t); LineSort sort() const { return static_cast(srt); } LineSpecies species() const { return static_cast(spc); } std::uint64_t index() const { return idx; } private: std::uint64_t srt : 1; std::uint64_t spc : 5; std::uint64_t idx : 58; }; static_assert(sizeof(LineDescriptor) == sizeof(std::uint64_t)); // Input source file mapped to memory as sequence of raw bytes. // UTF-8 is assumed as the encoding of the text. struct SourceFile { using View = std::span; struct LineRange; explicit SourceFile(const SystemPath&); SourceFile(SourceFile&&) noexcept; ~SourceFile(); LineRange lines() const noexcept; View contents() const noexcept { return view; } View contents(Isle m) const noexcept; private: View view; }; // A source file line range is an input_range of isles, each representing a physical // line in the input source file. struct SourceFile::LineRange { using difference_type = std::ptrdiff_t; struct iterator; explicit LineRange(const SourceFile&); iterator begin() noexcept; iterator end() noexcept; private: const SourceFile* src; const char8_t* ptr; PhysicalLine cache { }; void next_line() noexcept; }; // An iterator for input source file line range. struct SourceFile::LineRange::iterator { using difference_type = std::ptrdiff_t; using value_type = Isle; using iterator_category = std::input_iterator_tag; explicit iterator(LineRange* r) noexcept : range{r} { } PhysicalLine operator*() const noexcept; iterator& operator++() noexcept; void operator++(int) noexcept { ++(*this); } bool operator==(const iterator& that) const noexcept { return range == that.range; } bool operator!=(const iterator& that) const noexcept = default; private: LineRange* range; }; inline SourceFile::LineRange SourceFile::lines() const noexcept { return LineRange{*this}; } inline SourceFile::LineRange::iterator SourceFile::LineRange::begin() noexcept { return iterator{this}; } inline SourceFile::LineRange::iterator SourceFile::LineRange::end() noexcept { return iterator{nullptr}; } // A depot of lines read from an input source file. struct LineDepot { std::vector simples; std::vector composites; std::vector indices; }; // An input source listing is a source file with its lines read into logical lines. struct SourceListing : SourceFile { explicit SourceListing(const SystemPath&); const SimpleLine& simple_line(LineDescriptor) const; const CompositeLine& composite_line(LineDescriptor) const; const std::vector& logical_lines() const { return depot.indices; } private: LineDepot depot; }; } ================================================ FILE: include/ipr/lexer ================================================ // -*- C++ -*- // // This file is part of The Pivot framework. // Written by Gabriel Dos Reis. // See LICENSE for copyright and license notices. // #ifndef IPR_XPR_LEXER_INCLUDED #define IPR_XPR_LEXER_INCLUDED #include #include #include #include #include namespace xpr { struct Token { enum Kind { Unknown, LeftBrace = '{', RightBrace = '}', LeftParen = '(', RightParen = ')', LeftBracket = '[', RightBracket = ']', Dot = '.', Question = '?', Colon = ':', Bar = '|', Plus = '+', Minus = '-', Star = '*', Slash = '/', Percent = '%', LeftAngle = '<', RightAngle = '>', Not = '!', Assign = '=', Semicolon = ';', Comma =',', Complement = '~', At = '@', Ampersand = '&', Dollar = '$', Hash = '#', ColonColon = ':' + 256 * ':', HashHash = '#' + 256 * '#', PercentPercent = '%' + 256 * '%', AmpersandAmpersand = '&' + 256 * '&', BarBar = '|' + 256 * '|', LeftShift = '<' + 256 * '<', RightShift = '>' + 256 * '>', PlusPlus = '+' + 256 * '+', MinusMinus = '-' + 256 * '-', AssignAssign = '=' + 256 * '=', PlusAssign = '+' + 256 * '=', MinusAssign = '-' + 256 * '=', StarAssign = '*' + 256 * '=', SlashAssign = '/' + 256 * '=', PercentAssign = '%' + 256 * '=', Le = '<' + 256 * '=', Ge = '>' + 256 * '=', NotAssign = '!' + 256 * '=', BarAssign = '|' + 256 * '=', AmpersandAssign = '&' + 256 * '=', Arrow = '-' + 256 * '>', LeftSpec = '[' + 256 * '<', RightSpec = '>' + 256 * ']', DotStar = '.' + 256 * '*', Get = '<' + 256 * '-', LeftShiftAssign = LeftShift + 256 * 256 * '=', RightShiftAssign = RightShift + 256 * 256 * '=', ArrowStar = Arrow + 256 * 256 * '*', Boolean, // boolean literal Integer, // integer literal FloatingPoint, // floating-poitn literal Character = '\'', // character literal String = '"', // string literal Identifier, Comment, // well, for comments Auto, // "auto" Class, // "class" Union, // "union" Enum, // "enum" Namespace, // "namespace" Concept, // "concept" Virtual, // "virtual" Const, // "const" Volatile, // "volatile" Restrict, // "restrict" Public, // "public" Protected, // "protected" Private, // "private" Inline, // "inline" Explicit, // "explicit" Friend, // "friend" Export, // "export" Extern, // "extern" Static, // "static" Register, // "register" Sizeof, // "sizeof" Typeid, // "typeid" Throw, // "throw" StaticCast, // "static_cast" DynamicCast, // "dynamic_cast" ConstCast, // "const_cast" ReinterpretCast, // "reinterpret_cast" New, // "new" Delete, // "delete" For, // "for" If, // "if" Else, // "else" Switch, // "switch" Continue, // "continue" Break, // "break" Return, // "return" Goto, // "goto" Case, // "case" Default, // "default" While, // "while Do, // "do" Catch, // "catch" EndOfInput // end of character input stream }; // Comparator function object type, useful for inserting // tokens in an associative containers. struct Compare { bool operator()(const Token& lhs, const Token& rhs) const { return lhs.text < rhs.text; } }; Token() : kind() { } Token(Kind k, const char* s = 0) : kind(k), text(s) { } Kind kind; ipr::Source_location location; std::string text; }; struct Lexer { explicit Lexer(ipr::impl::Unit&); Token& peek(int = 0); void discard() { tokens.pop_front(); } void input_file(const char*); void syntax_error(); protected: ipr::impl::Unit& unit; private: using Keyword_set = std::set; struct Token_stream : std::deque { }; Token_stream tokens; Keyword_set keywords; ipr::Source_location locus; std::string line; std::ifstream input; const char* cursor; const char* end_of_line; void insert_keyword(Token::Kind, const char*); std::istream& read_line(); void read_quoted_text(std::string&); inline bool next_char_is(char); inline Token::Kind if_duplicate_char(Token::Kind, Token::Kind); inline bool escape_sequence() const; void identifier(Token&); void number(Token&); void unknown(Token&); inline const Token* keyword(const std::string&) const; }; } #endif // IPR_XPR_LEXER_INCLUDED ================================================ FILE: include/ipr/node-category ================================================ // -*- C++ -*- // // This file is part of The Pivot framework. // Written by Gabriel Dos Reis. // See LICENSE for copyright and license notices. // // The existence of these enumerators reflect a weakness in the implementation // language (C++), for their purposes is only to convert types to runtime // values, and vice versa, while supporting constant time dispatch. Unknown, // ipr::Node -- abstract Annotation, // ipr::Annotation Region, // ipr::Region Comment, // ipr::Comment String, // ipr::String Parameter_list, // ipr::Parameter_list Overload, // ipr::Overload Array, // ipr::Array Class, // ipr::Class Decltype, // ipr::Decltype As_type, // ipr::As_type Enum, // ipr::Enum Tor, // ipr::Tor Function, // ipr::Function Namespace, // ipr::Namespace Pointer, // ipr::Pointer Ptr_to_member, // ipr::Ptr_to_member Product, // ipr::Product Qualified, // ipr::Qualified Reference, // ipr::Reference Rvalue_reference, // ipr::Rvalue_reference Sum, // ipr::Sum Forall, // ipr::Forall Union, // ipr::Union Auto, // ipr::Auto Closure, // ipr::Closure Identifier, // ipr::Identifier Operator, // ipr::Operator Suffix, // ipr::Suffix Conversion, // ipr::Conversion Template_id, // ipr::Template_id Type_id, // ipr::Type_id Ctor_name, // ipr::Ctor_name Dtor_name, // ipr::Dtor_name Guide_name, // ipr::Guide_name Phantom, // ipr::Phantom Eclipsis, // ipr::Eclipsis Lambda, // ipr::Lambda Requires, // ipr::Requires Symbol, // ipr::Symbol Address, // ipr::Address Array_delete, // ipr::Array_delete Asm, // ipr::Asm Complement, // ipr::Complement Delete, // ipr::Delete Demotion, // ipr::Demotion Deref, // ipr::Deref Expr_list, // ipr::Expr_list Alignof, // ipr::Alignof Sizeof, // ipr::Sizeof Typeid, // ipr::Typeid Id_expr, // ipr::Id_expr Label, // ipr::Label Materialization, // ipr::Materialization Not, // ipr::Not Enclosure, // ipr::Enclosure Post_decrement, // ipr::Post_decrement Post_increment, // ipr::Post_increment Pre_decrement, // ipr::Pre_decrement Pre_increment, // ipr::Pre_increment Promotion, // ipr::Promotion Read, // ipr::Read Throw, // ipr::Throw Unary_minus, // ipr::Unary_minus Unary_plus, // ipr::Unary_plus Expansion, // ipr::Expansion Noexcept, // ipr::Noexcept Args_cardinality, // ipr::Args_cardinality Restriction, // ipr::Restriction Rewrite, // ipr::Rewrite Scope_ref, // ipr::Scope_ref Plus, // ipr::Plus Plus_assign, // ipr::Plus_assign And, // ipr::And Array_ref, // ipr::Array_ref Arrow, // ipr::Arrow Arrow_star, // ipr::Arrow_star Assign, // ipr::Assign Bitand, // ipr::Bitand Bitand_assign, // ipr::Bitand_assign Bitor, // ipr::Bitor Bitor_assign, // ipr::Bitor_assign Bitxor, // ipr::Bitxor Bitxor_assign, // ipr::Bitxor_assign Call, // ipr::Call Cast, // ipr::Cast Coercion, // ipr::Coercion Comma, // ipr::Comma Const_cast, // ipr::Const_cast Construction, // ipr::Construction Div, // ipr::Div Div_assign, // ipr::Div_assign Dot, // ipr::Dot Dot_star, // ipr::Dot_star Dynamic_cast, // ipr::Dynamic_cast Equal, // ipr::Equal Greater, // ipr::Greater Greater_equal, // ipr::Greater_equal Less, // ipr::Less Less_equal, // ipr::Less_equal Literal, // ipr::Literal Lshift, // ipr::Lshift Lshift_assign, // ipr::Lshift_assign Mapping, // ipr::Mapping Member_init, // ipr::Member_init Modulo, // ipr::Modulo Modulo_assign, // ipr::Modulo_assign Mul, // ipr::Mul Mul_assign, // ipr::Mul_assign Narrow, // ipr::Narrow Not_equal, // ipr::Not_equal Or, // ipr::Or Pretend, // ipr::Pretend Qualification, // ipr::Qualification Reinterpret_cast, // ipr::Reinterpret_cast Rshift, // ipr::Rshift, Rshift_assign, // ipr::Rshift_assign Static_cast, // ipr::Static_cast Widen, // ipr::Widen Minus, // ipr::Minus Minus_assign, // ipr::Minus_assign Binary_fold, // ipr::Binary_fold Where, // ipr::Where Static_assert, // ipr::Static_assert Instantiation, // ipr::Instantiation New, // ipr::New Conditional, // ipr::Conditional Scope, // ipr::Scope Deduction_guide, // ipr::Deduction_guide Specifiers_spread, // ipr::Specifiers_spread Structured_binding, // ipr::Structured_binding Using_declaration, // ipr::Using_declaration Using_directive, // ipr::Using_directive Phased_evaluation, // ipr::Phased_evaluation Pragma, // ipr::Pragma Block, // ipr::Block Break, // ipr::Break Continue, // ipr::Continue Ctor_body, // ipr::Ctor_body Do, // ipr::Do Expr_stmt, // ipr::Expr_stmt, For, // ipr::For For_in, // ipr::For_in Goto, // ipr::Goto Handler, // ipr::Handler If, // ipr::If Labeled_stmt, // ipr::Labeled_stmt Return, // ipr::Return Switch, // ipr::Switch While, // ipr::While Alias, // ipr::Alias Base_type, // ipr::Base_type Enumerator, // ipr::Enumerator Field, // ipr::Field Bitfield, // ipr::Bitfield Fundecl, // ipr::Fundecl Template, // ipr::Template Concept, // ipr::Concept Parameter, // ipr::Parameter Typedecl, // ipr::Typedecl Var, // ipr::Var EH_parameter, // ipr::EH_parameter Unit, // ipr::Unit last_code_cat ================================================ FILE: include/ipr/std-preamble ================================================ // -*- C++ -*- // // This file is part of The Pivot framework. // Written by Gabriel Dos Reis. // See LICENSE for copyright and license notices. // // Standard library preamble for translation units that use `import cxx.ipr;` // followed by `#include ` (or other IPR project headers). // // TEMPORARY: This header exists because the implementation layer () // is not yet modularized. MSVC requires all standard #includes to appear // before `import cxx.ipr;` to avoid redefinition errors. Once // becomes a module, this preamble is no longer needed. // // Usage: // #include // import cxx.ipr; // #include #ifndef IPR_STD_PREAMBLE_INCLUDED #define IPR_STD_PREAMBLE_INCLUDED #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #endif // IPR_STD_PREAMBLE_INCLUDED ================================================ FILE: include/ipr/utility-impl ================================================ // -*- C++ -*- // // This file is part of The Pivot framework. // Written by Gabriel Dos Reis. // See LICENSE for copyright and license notices. // // Implementation-only utilities for and . // The interface-facing subset of the old is now // part of the cxx.ipr module. #ifndef IPR_UTILITY_IMPL_INCLUDED #define IPR_UTILITY_IMPL_INCLUDED // Standard headers required by this file must be included by the consumer // BEFORE including this header, to avoid clashes with import cxx.ipr. namespace ipr::util { // At various places in the implementation of the IPR interface, certain logical // references are stored as pointers because of implementation necessity, or sometimes convenience. // This wrapper class ensures that when the data behind those logical references are // access the implementation pointer is not null. Note that the role served by this // class is not the same as ipr::Optional (which is a genuine representation of optional information), // or std::reference_wrapper (which assumes existence of reference to begin with). template struct ref { ref(T* p = { }) : ptr{p} { } T& get() const { return *util::check(ptr); } private: T* ptr; }; // -------------------- // -- Red-back trees -- // -------------------- // The implementation found here is based on ideas in // T. H. Cormen, C. E. Leiserson, R. L. Rivest and C. Strein: // "Introduction to Algorithms", 2nd edition. // One reason why we (re-)implement our own "set" data structures // instead of using the standard ones is because standard sets // do not allow for in-place modification. That puts an // unreasonable burden on how we can write the codes for IPR. // Another reason is that, while the standard set is really a // a red-lack tree in disguise, there is no way one can have // access to that structure. Furthermore, we use red-black // trees in both "intrusive" and "non-intrusive" forms. namespace rb_tree { // Marker used to designate a tree node as either 'black' or 'red'. enum class Color { Black, Red }; // The type of the links used to chain together data in // a red-black tree. template struct link { enum Dir { Left, Right, Parent }; Node*& parent() { return arm[Parent]; } Node*& left() { return arm[Left]; } Node*& right() { return arm[Right]; } Node* arm[3] { }; Color color = Color::Red; }; template struct core { std::ptrdiff_t size() const { return count; } protected: Node* root { }; std::ptrdiff_t count { }; // Do a left rotation about X. X->left() is assumed nonnull, // which after the manoeuvre becomes X's parent. void rotate_left(Node*); // Same as rotate_left, except that the rotation does right. void rotate_right(Node*); // After raw insertion, the tree is unbalanced again; this // function re-balance the tree, fixing up properties destroyed. void fixup_insert(Node*); }; template void core::rotate_left(Node* x) { Node* y = x->right(); // Make y's left a subtree of x's right subtree. x->right() = y->left(); if (y->left() != nullptr) y->left()->parent() = x; // Update y's parent and its left or right arms. y->parent() = x->parent(); if (x->parent() == nullptr) // x was the root of the tree; make y the new root. this->root = y; else if (x->parent()->left() == x) x->parent()->left() = y; else x->parent()->right() = y; // Now, x must go on y's left. y->left() = x; x->parent() = y; } template void core::rotate_right(Node* x) { Node* y = x->left(); x->left() = y->right(); if (y->right() != nullptr) y->right()->parent() = x; y->parent() = x->parent(); if (x->parent() == nullptr) this->root = y; else if (x->parent()->right() == x) x->parent()->right() = y; else x->parent()->left() = y; y->right() = x; x->parent() = y; } template void core::fixup_insert(Node* z) { while (z != root and z->parent()->color == Color::Red) { if (z->parent() == z->parent()->parent()->left()) { Node* y = z->parent()->parent()->right(); if (y != nullptr and y->color == Color::Red) { z->parent()->color = Color::Black; y->color = Color::Black; z->parent()->parent()->color = Color::Red; z = z->parent()->parent(); } else { if (z->parent()->right() == z) { z = z->parent(); rotate_left(z); } z->parent()->color = Color::Black; z->parent()->parent()->color = Color::Red; rotate_right(z->parent()->parent()); } } else { Node* y = z->parent()->parent()->left(); if (y != nullptr and y->color == Color::Red) { z->parent()->color = Color::Black; y->color = Color::Black; z->parent()->parent()->color = Color::Red; z = z->parent()->parent(); } else { if (z->parent()->left() == z) { z = z->parent(); rotate_right(z); } z->parent()->color = Color::Black; z->parent()->parent()->color = Color::Red; rotate_left(z->parent()->parent()); } } } root->color = Color::Black; } // A chain is an rb-tree that supports search and insertion, with // the comparison object passed as a parameter instead of being built // into the tree type directly. The comparison object `cmp` is always // invoked as `cmp(data, key)` where `data` designates an existing // object stored at a node in the chain, and `key` is the parameter by which // the tree is searched. template struct chain : core { template Node* insert(Node*, Comp); template Node* find(const Key&, Comp) const; }; template template Node* chain::find(const Key& key, Comp comp) const { bool found = false; Node* result = this->root; while (result != nullptr and not found) { auto ordering = comp(*result, key) ; if (ordering < 0) result = result->left(); else if (ordering > 0) result = result->right(); else found = true; } return result; } template template Node* chain::insert(Node* z, Comp comp) { Node** slot = &this->root; Node* up = nullptr; bool found = false; while (not found and *slot != nullptr) { auto ordering = comp(**slot, *z); if (ordering < 0) { up = *slot; slot = &up->left(); } else if (ordering > 0) { up = *slot; slot = &up->right(); } else found = true; } if (this->root == nullptr) { // This is the first time we're inserting into the tree. this->root = z; z->color = Color::Black; } else if (*slot == nullptr) { // key is not present, do what we're asked to do. *slot = z; z->parent() = up; z->color = Color::Red; this->fixup_insert(z); } ++this->count; return z; } template struct node : link> { T data; }; template struct container : core>, private std::allocator> { template T* find(const Key&, Comp) const; // We want to insert a node constructed out of a Key, using // an admissible comparator LESS. Returns a pointer to the // newly created node, if successfully inserted, or the old // one if the Key is already present. template T* insert(const Key&, Comp); private: template node* make_node(const U& u) { node* n = this->allocate(1); new (&n->data) T(u); n->left() = nullptr; n->right() = nullptr; n->parent() = nullptr; return n; } void destroy_node(node* n) { if (n != nullptr) { n->data.~T(); this->deallocate(n, 1); } } }; template template T* container::find(const Key& key, Comp comp) const { for (node* x = this->root; x != nullptr; ) { auto ordering = comp(x->data, key); if (ordering < 0) x = x->left(); else if (ordering > 0) x = x->right(); else return &x->data; } return nullptr; } template template T* container::insert(const Key& key, Comp comp) { if (this->root == nullptr) { // This is the first time we're inserting into the tree. this->root = make_node(key); this->root->color = Color::Black; ++this->count; return &this->root->data; } node** slot = &this->root; node* parent = nullptr; node* where = nullptr; bool found = false; for (where = this->root; where != nullptr and not found; where = *slot) { auto ordering = comp(where->data, key); if (ordering < 0) { parent = where; slot = &where->left(); } else if (ordering > 0) { parent = where; slot = &where->right(); } else found = true; } if (where == nullptr) { // key is not present, do what we're asked to do. where = *slot = make_node(key); where->parent() = parent; where->color = Color::Red; ++this->count; this->fixup_insert(where); } return &where->data; } } // -- helper for implementing permanent string objects. They uniquely // -- represent their contents throughout their lifetime. Ideally, // -- they are allocated from a pool. struct string { struct arena; using size_type = std::ptrdiff_t; // integer type that string length // number of characters directly contained in this header // of the string. Taken to be the number of bytes in size_type. static constexpr size_type padding_count = sizeof (size_type); size_type size() const { return length; } char operator[](size_type) const; const char8_t* begin() const { return data; } const char8_t* end() const { return begin() + length; } size_type length; char8_t data[padding_count]; }; struct string::arena { arena(); ~arena(); const string* make_string(const char8_t*, size_type); private: util::string* allocate(size_type); auto remaining_header_count() const { return mem->storage + bufsz - next_header; } struct pool; static constexpr size_type headersz = sizeof (util::string); static constexpr size_type bufsz = headersz << (20 - sizeof (pool*)); struct pool { pool* previous; util::string storage[bufsz]; }; static constexpr size_type poolsz = sizeof (pool); pool* mem; string* next_header; }; struct lexicographical_compare { template int operator()(In1 first1, In1 last1, In2 first2, In2 last2, Compare compare) const { for (; first1 != last1 and first2 != last2; ++first1, ++first2) if (auto cmp = compare(*first1, *first2)) return cmp; return first1 == last1 ? (first2 == last2 ? 0 : -1) : 1; } }; } #endif // IPR_UTILITY_IMPL_INCLUDED ================================================ FILE: infra/Analysis.ruleset ================================================ ================================================ FILE: src/ChangeLog ================================================ 2015-11-29 Gabriel Dos Reis * impl.cxx: Implement modifications to ipr::Enum. 2010-07-23 Gabriel Dos Reis * impl.C (impl::expr_factory::make_id_expr): Define new overload. 2010-05-28 Gabriel Dos Reis * io.C: Pretty print Initializer_list. 2010-05-11 Gabriel Dos Reis * io.C (operator<<(Printer&, xpr_initializer)): Print declarations too. 2010-04-26 Gabriel Dos Reis * impl.C (impl::Unit::get_typename): Define. 2010-04-26 Gabriel Dos Reis * io.C (xpr::Decl): Print function specifiers. 2010-03-29 Gabriel Dos Reis * impl.C (expr_factory::make_identifier(const char*)): Define. (expr_factory::make_identifier(const std::string&)): Likewise. (expr_factory::make_operator(const char*)): Likewise. (expr_factory::make_operator(const std::string&)): Likewise. 2010-03-29 Gabriel Dos Reis * impl.C (expr_factory::get_linkage): Define. (Unit::get_cxx_linkage): Define in terms of expr_factory::get_linkage. (Unit::get_c_linkage): Likewise. (Unit::Unit): Call get_cxx_linkage iin lieu of cxx_linkage. * traversal.C (Visitor::visit(const C_linkage&)): Remove. (Visitor::visit(const Cxx_linkage&)): Linkage. 2010-03-29 Gabriel Dos Reis * impl.C (expr_factory::make_literal): Define new overloads. 2010-03-29 Gabriel Dos Reis * impl.C (ipr): Reflect changes from Unit::get_string to expr_factory::get_string. 2009-09-22 Gabriel Dos Reis * impl.C (type_factory::make_rvalue_reference): Define. (Unit::get_rvalue_reference): Likewise. 2009-07-27 Gabriel Dos Reis * impl.C (impl::For_in::type): Define. 2009-06-23 Gabriel Dos Reis * impl.C: Define For_in members. * io.C: Define printer for For_in nodes. 2008-11-24 Gabriel Dos Reis * impl.C (impl::Mapping::has_result): Remove. * io.C (impl::xpr_mapping_expression_visitor): Tidy. 2008-09-20 Gabriel Dos Reis * traversal.C (Visitor::visit(const Datm&)): Forward as Classic. 2008-09-15 Gabriel Dos Reis * traversal.C (Visitor::visit(const Classic&)): Implement. Propagate forwarding visitor functions. 2008-06-20 Gabriel Dos Reis * impl.C (impl::Var::membership): Remove. (impl::Var::Var): Don't initialize member_of. 2008-05-29 Gabriel Dos Reis * impl.C (Var::lexical_region): Define. (Typedecl::lexical_region): Likewise. (Parameter::lexical_region): Likewise. (Parameter::home_region): Likewise. (Named_map::lexical_region): Likewise. (Fundecl::lexical_region): Likewise. (Field::lexical_region): Likewise. (Field::home_region): Likewise. (Enumerator::lexical_region): Likewise. (Enumerator::home_region): Likewise. (Base_type::lexical_region): Likewise. (Base_type::home_region): Likewise. (Bitfield::lexical_region): Likewise. (Bitfield::home_region): Likewise. (Alias::lexical_region): Likewise. 2005-11-10 Gabriel Dos Reis * utility.C: New. 2005-09-03 Gabriel Dos Reis * impl.C (impl::Linkage::Linkage): Define. * traversal.C (Visitor::visit(const Linkage&)): Define. 2005-07-03 Gabriel Dos Reis * io.C (operator<<(Printer&, xpr_type)::Impl::visit(const Ptr_to_member&)): New. (operator<<(Printer&, xpr_type_expr)::Impl::visit(const Ptr_to_member&&)): Likewise. 2005-06-17 Gabriel Dos Reis * utility.C:New. * impl.C (impl::Enumerator::Enumerator): Don't forget initization of impl::Enumerator::init. 2005-05-20 Gabriel Dos Reis * impl.C (impl::expr_factory::make_mapping): Adjust parameter list. (impl::Parameter::Parameter): Tidy. (impl::Parameter::type): Likewise. (impl::Parameter::position): Define. * io.C (xpr::Name::visit(const ipr::Rname&))): Define. * impl.C (impl::Rname::Rname): Define. (impl::Rname::type): Likewise. (impl::Rname::position): Likewise. (impl::Rname::level): Likewise. * traversal.C (Visitor::visit(const Rname&)): Define. 2005-04-07 Gabriel Dos Reis * io.C (operator<<(Printer&, xpr_decl)): Print semi-colon if needed. (xpr::Decl::visit(const Scope&)): Tidy. (operator<<(Printer&, xpr_expr)::Impl::visit(const Scope&)): Tidy. (operator<<(Printer&, xpr_expr)::Impl::visit(const Decl&)): New. (xpr::Decl::visit(const Scope&)): Remove. (xpr::Stmt::visit(const Decl&)): New. (xpr::Stmt): Derive from xpr::Assignment_expr. 2005-03-23 Gabriel Dos Reis * io.C (xpr_quote): Remove. (operator<<(Printer&, xpr_quote)): Move body to... (xpr::Primary_expr::visit(const Literal&)): ... here. Define outline. * interface.C (stats::node_usage_counts): New local data. (stats::all_nodes_count): Define. (stats::node_count): Likewise. (Node::Node): Increment usage count. * io.C (new_style_cast): Make a template. (offset_with_pm): Likewise. (xpr::Initializer::visit(const Expr&)): New. (xpr::Initializer::visit(const Stmt&)): Likewise. (xpr::Stmt): New. (operator<<(Printer&, xpr_stmt)): Use it. Move local class definition to xpr::Stmt. (xpr::Decl): New. (operator<<(Printer&, xpr_decl)): Use it. Move local class definition to xpr::Decl. (operator<<(Printer&, const Decl&)): Remove. (insert_xtoken): Remove. (Token_helper<>): New. (token<>): Likewise. (operator<<(Printer&, Token_helper<>)): Likewise. 2005-03-22 Gabriel Dos Reis * interface.C (Node::Node): Adjust. 2005-03-21 Gabriel Dos Reis * impl.C (impl::expr_factory::make_id_expr): Define. (impl::Overload::accept): Remove. (impl::singleton_overload::accept): Likewise. (impl::empty_overload::accept): Likewise. (impl::Break::accept): Likewise. (impl::Continue::accept): Likewise. (impl::Comment::accept): Likewise. (impl::Expr_list::accept): Likewise. (impl::Identifier::accept): Likewise. (impl::Id_expr::accept): Likewise. (impl::Literal::accept): Likewise. (impl::Mapping::accept): Likewise. (impl::Operator::accept): Likewise. (impl::Paren_expr::accept): Likewise. (impl::Scope::accept): Likewise. (impl::Region::accept): Likewise. (impl::Unit::accept): Likewise. (impl::Block::accept): Likewise. (impl::For::accept): Likewise. (impl::Literal::accept): Likewise. * io.C (xpr::Primary_expr::visit(const Id_expr&)): Defie. * traversal.C (Visitor::visit(const Id_expr&)): Define. * impl.C (impl::Id_expr::Id_expr): Define. (impl::Id_expr::type): Likewise. (impl::Id_expr::operand): Likewise. (impl::Id_expr::resolution): Likewise. (impl::Id_accept): Likewise. * io.C (xpr_initializer): New. (operator<<(Printer&, xpr_initializer): New. (operator<<(Printer&, xpr_mapping_expression): Use it. * impl.C (impl::Mapping::Mapping): Initialize member value_type. (impl::Mapping::result_type): Define. 2005-03-17 Gabriel Dos Reis * io.C (operator<<(Printer&, const Expr&)): Remove. (operator<<(Printer&, const Type&)): Likewise. (operator<<(Printer&, const Decl&)): Likewise. (print_sequence): Likewise. (operator<<(Printer&, const Product&)): Likewise. (operator<<(Printer&, const Sum&)): Likewise. (operator<<(Printer&, const Expr_list&): Tidy. (operator<<(Printer&, const Sequence&): Likewise. (operator<<(Printer&, const Parameter_list&): Likewise. (operator<<(Printer&, const Conversion&): Likewise. (operator<<(Printer&, const Type_id&): Likewise. (operator<<(Printer&, const Datum&): Likewise. (operator<<(Printer&, const Cast_expr&): Likewise. (new_style_cast): Likewise. (operator<<(Printer&, const Cast&): Likewise. (operator<<(Printer&, xpr_exception_spec): Likewise. (operator<<(Printer&, xpr_base_classes): Likewise. (operator<<(Printer&, const Decltype&): Likewise. (operator<<(Printer&, const Reference&): Likewise. (operator<<(Printer&, const Pointer&): Likewise. (operator<<(Printer&, const Array&): Likewise. (operator<<(Printer&, xpr_type): Likewise. (operator<<(Printer&, const Base_type&): Likewise. (operator<<(Printer&, const Typedecl&): Likewise. (xpr::Name): New. (operator<<(Printer&, const Name&)): Use it. (xpr::Primary_expr): New. (operator<<(Printer&, xpr_primary_expr): Use it. (xpr::Postfix_expr): New. (operator<<(Printer&, xpr_postfix_expr): Use it. (xpr::Unary_expr): New. (unary_operation): Use it. (operator<<(Printer&, xpr_unary_expr): Tidy. (xpr::Cast_expr): New. (operator<<(Printer&, xpr_cast_expr): Use it. (xpr::Pm_expr): New. (operator<<(Printer&, xpr_pm_expr): Use it. (xpr::Mul_expr): New. (operator<<(Printer&, xpr_mul_expr): Use it. (xpr::Shift_expr): New. (operator<<(Printer&, xpr_shift_expr): Use it. (xpr::Add_expr): New. (operator<<(Printer&, xpr_add_expr): Use it. (xpr::Rel_expr): New. (operator<<(Printer&, xpr_rel_expr): Use it. (xpr::Eq_expr): New. (operator<<(Printer&, xpr_eq_expr): Use it. (xpr::And_expr): New. (operator<<(Printer&, xpr_and_expr): Use it. (xpr::Xor_expr): New. (operator<<(Printer&, xpr_xor_expr): Use it. (xpr::Ior_expr): New. (operator<<(Printer&, xpr_ior_expr): Use it. (xpr::Land_expr): New. (operator<<(Printer&, xpr_land_expr): Use it. (xpr::Cond_expr): New. (operator<<(Printer&, xpr_cond_expr): Use it. (xpr::Assignment_expr): New. (operator<<(Printer&, xpr_assignment_expr): Use it. 2005-03-12 Gabriel Dos Reis * io.C (operator<<(Printer&, const xpr_decl)::Impl::visit(const Named_map)): Fix thinko. 2005-03-03 Gabriel Dos Reis * io.C (ipr::operator<<(Printer&, xpr_mapping_expression)): Fix thinko. (ipr::operator<<(Printer&, xpr_decl)): Fix thinko. 2005-03-02 Gabriel Dos Reis * Mark for 0.34 2005-02-17 Gabriel Dos Reis * io.C (ipr): Rename Expr_as_type to As_type. * traversal.C: Likewise. * impl.C: Likewise. Rename make_expr_as_type to make_as_type. Rename heterogeneous_scope ro Scope. Rename heterogeneous_region to Region. 2005-02-16 Gabriel Dos Reis * impl.C (impl::heterogeneous_scope::operator[]): Tidy. 2005-02-14 Gabriel Dos Reis * impl.C (impl::Unit::make_template_id): Define. (impl::Enum::add_member): Define. (impl::Named_map::Named_map): Tidy. (impl::Named_map::primary_named_map): Likewise. (impl::Named_map::specializations): Likewise. (impl::Named_map::mapping): Likewise. (impl::Named_map::has_initializer): Likewise. (impl::Named_map::initializer): Likewise. (impl::Enum::add_member): Define. (impl::heterogeneous_scope::make_primary_map): Rework. (impl::heterogeneous_scope::make_secondary_map): Likewise. (impl::Unit::make_template_id): Define. 2005-02-12 Gabriel Dos Reis * io.C (Printer::operator<<): Remove definitions of overloads for characters. 2005-02-10 Gabriel Dos Reis * impl.C (impl::Unit::make_ctor_name): Define. (impl::Unit::make_dtor_name): Likewise. 2005-02-08 Gabriel Dos Reis * impl.C (impl::Class::add_base): Define. (impl::Class::Class): Set impl::Class::base_subobjects::scope::owned_by. 2005-02-06 Gabriel Dos Reis * Mark for 0.33. * io.C (xpr_exception_spec): Tidy. (operator<<(Printer&, xpr_mapping_expression): Define. * impl.C (impl::Fundecl::Fundecl): Propagate changes. (impl::Expr_list): Define all member functions. (impl::Mapping::param): Define. (impl::stmt_factory::make_for): Likewise. (impl::heterogeneous_region::make_subregion): Likewise. (impl::Unit::make_function): Likewise. (Unit::make_expr_list): Remove. 2005-02-03 Gabriel Dos Reis * impl.C (unary_less()): Compare ref_sequence<>s too. (impl::Unit::make_product): Define. (impl::Unit:make_sum): Likewise. (impl::Unit::make_expr_list): Likewise. 2004-12-02 Gabriel Dos Reis * io.C: #include "ipr/travsersal.H". Propogate renaming in "ipr/interface.H". 2004-11-24 Gabriel Dos Reis * ipr.C (Lshift): Rename from Shift_left. (Lshift_assign): Rename from Shift_left_assign. (Rshift): Rename from Shift_right. (Rshift_assign): Rename from Shift_right_assign. 2004-11-23 Gabriel Dos Reis * ipr.C (Visitor::visit(const Multi_comment&)): Remove. 2004-11-21 Gabriel Dos Reis * ipr.C (Visitor::visit(const Comment&)): Define. (Visitor::visit(const Multi_comment&)): Likewise. (Visitor::visit(const Unit&)): Likewise. 2004-11-09 Gabriel Dos Reis * ipr.C: Replace Expr_as_type with Type_expr. Replace Specialization with Template_id. * io.C: Likewise. 2004-11-07 Gabriel Dos Reis * ipr.C (Visitor::visit(const Asm&)): Define. 2004-10-28 Gabriel Dos Reis * Mark for 0.26. 2004-10-28 Gabriel Dos Reis * ipr.C (Stmt_builder::make_expr_stmt): Tidy. (Stmt_builder::make_labeled_stmt): Likewise. (Stmt_builder::make_ctor_body): Likewise. (Stmt_builder::make_if_then): Likewise. (Stmt_builder::make_if_then_else): Likewise. (Stmt_builder::make_goto): Likewise. 2004-10-26 Gabriel Dos Reis * ipr.C (Stmt_builder::make_break): Define here. (Stmt_builder::make_continue): Likewise. (Stmt_builder::make_block): Likewise. (Stmt_builder::make_ctor_body): Likewise. (Stmt_builder::make_expr_stmt): Likewise. (Stmt_builder::make_goto): Likewise. (Stmt_builder::make_return): Likewise. (Stmt_builder::make_do): Likewise. (Stmt_builder::make_if_then): Likewise. (Stmt_builder::make_switch): Likewise. (Stmt_builder::make_handler): Likewise. (Stmt_builder::make_labeled_stmt): Likewise. (Stmt_builder::make_while): Likewise. (Stmt_builder::make_if_then_else): Likewise. (Stmt_builder::make_for): Likewise. 2004-10-21 Gabriel Dos Reis * ipr.C (Unit_impl::make_specialization): define. 2004-10-01 Gabriel Dos Reis * io.C (operator<<(Printer&, xpr_primary_expression)::Impl::visit(const Expr_in_parens&)): Define. * ipr.C (Visitor::visit(const Expr_in_parens&)): Define. 2004-09-23 Gabriel Dos Reis * Mark for 0.25. * ipr.C (Unit_impl::make_ctor_name): Define. (Unit_impl::make_dtor_name): Likewise.. * io.C (operator<<(Printer&, xpr_declaration)::Impl::visit(const Constructor&)): Remove. (operator<<(Printer&, xpr_declaration)::Impl::visit(const Destructor&)): Remove. (operator<<(Printer&, xpr_name)::Impl::visit(const Ctor_name&)): Define. (operator<<(Printer&, xpr_name)::Impl::visit(const Dtor_name&)): Likewise. (operator<<(Printer&, xpr_type_expr)::Impl::visit(const Udt&)): Tidy. * ipr.C (Visitor::visit(const Dtor_name&)): Define. (Visitor::visit(const Ctor_name&)): Likewise. (Visitor::visit(const Constructor&)): Remove. (Visitor::visit(const Destructor&)): Likewise. (Unit_impl::make_constructor): Likewise. (Unit_impl::make_destructor): Likewise. 2004-09-20 Gabriel Dos Reis * ipr.C (Visitor::visit(const Name&)): Define. 2004-09-20 Gabriel Dos Reis * Mark for 0.24 2004-09-20 Gabriel Dos Reis * ipr.C (Parameter_list_impl::add_param): Use Dcl_impl<>::register_to. 2004-09-16 Gabriel Dos Reis * ipr.C (view): New. (Unit_impl::make_expr_as_type): Use it. 2004-09-15 Gabriel Dos Reis * ipr.C (Unit_impl::make_fun_decl): Tidy. (Unit_impl::make_destructor): Define. 2004-09-13 Gabriel Dos Reis * io.C (operator<<(Printer&, xpr_expression_list)::Impl::visit(const Sum&)): Define. (operator<<(Printer&, xpr_type)::Impl::visit(const Sum&)): Define. * ipr.C (Visitor::visit(const Sum&)): Define. (Unit_impl::make_sum): Define. (Unit_impl::make_function): Update. Tidy. 2004-09-02 Gabriel Dos Reis * io.C (operator<<(Printer&, xpr_quote)): Tidy. 2004-08-30 Gabriel Dos Reis * Mark for 0.23. 2004-08-31 Gabriel Dos Reis * ipr.C (Unit_impl::make_expr_as_type): Ensure idempotency. * io.C (operator<<(Printer&, xpr_quote)): New. (operator<<(Printer&, xpr_primary_expression)::Impl::visit(const Literal&)): Use it. 2004-08-30 Gabriel Dos Reis * ipr.C (Visitor::visit(const Enumerator&)): Define. 2004-08-28 Gabriel Dos Reis * ipr.C (Visitor::visit(const Enum&)): Fallthrough to Udt. 2004-08-27 Gabriel Dos Reis * io.C (operator<<(Printer&, xpr_name)::Impl::visit(const Specialization&)): Move from operator<<(Printer&, xpr_primary_expression). (operator<<(Printer&, xpr_type_expr)::Impl::visit(const Template&)): Tidy. (operator<<(Printer&, xpr_type_expr)::Impl:visit(const Decltype&)): New. (pp_base::visit(const Type&)): Fallthrough to Expr. * ipr.C (Visitor::visit(const Specialization&)): Fallthrough to Name. 2004-08-26 Gabriel Dos Reis * ipr.C (Unit_impl::make_operator): Define. (Unit_impl::make_identifier): Likewise. (Unit_impl::make_conversion): Likewise. (Unit_impl::make_scope_ref): Likewise. * io.C (operator<<(Printer&, xpr_name)): Handle Scope_ref. (at): New helper function. * ipr.C (Visitor::visit(const Scope_ref&)): Fallthrough to Name. (Visitor::visit(const Union&)): Fallthrough to Udt. (Visitor::visit(const Namespace&)): Likewise. (Visitor::visit(const Class&)): Likewise. (Visitor::visit(const Decltype&)): Fallthrough to Type. (Unit_impl::make_decltype): Define. 2004-08-25 Gabriel Dos Reis * Mark for 0.22. 2004-08-25 Gabriel Dos Reis * ipr.C (Unit_impl::make_alias): Don't set type_impl() here. 2004-08-24 Gabriel Dos Reis * ipr.C (Visitor::visit(const Scope_ref&)): Tidy. 2004-08-23 Gabriel Dos Reis * gnu-tu.H: New file. * gnu-tu.C: Likewise. 2004-08-15 Gabriel Dos Reis * Mark for 0.21. 2004-08-15 Gabriel Dos Reis * io.C (operator<<(Printer&, Decl::Specifier)): Fix typo. 2004-08-14 Gabriel Dos Reis * ipr.C: Simplify Do_while to Do. * io.C: Likewise. (operator<<(Printer&, xpr_statement)): Tidy. 2004-08-10 Gabriel Dos Reis * Mark for 0.20. 2004-08-10 Gabriel Dos Reis * io.C (operator<<(Printer&, xpr_type_expr)): Collapse printing for Union and Enum into one place that handles Udt by default. * ipr.C (Visitor::visit(const Udt&)): Define. 2004-08-07 Gabriel Dos Reis * ipr.C (Visitor::visit(const Ctor_body&)): Define. (Visitor::visit(const Expr_stmt&)): Likewise. (Unit_impl::make_handler_seq): Remove. * io.C (operator<<(Printer&, xpr_expression)): Don't print Function_body. (operator<<(Printer&, xpr_statement)): Handle Ctor_body. 2004-08-03 Gabriel Dos Reis * Mark for 0.19. 2004-08-03 Gabriel Dos Reis * ipr.C (Unit_impl::make_handler_seq): * io.C (operator<<(Printer&, xpr_unary_expression)): Don't forget to print Unary_plus. (operator<<(Printer&, xpr_type_expr)): Print parameter-list in parenthesis. (operator<<(Printer&, xpr_declaration)): Likewise. 2004-07-18 Gabriel Dos Reis * io.C (operator<<(Printer&, xpr_type)): Fix thinko. 2004-07-16 Gabriel Dos Reis * Mark for 0.18. 2004-07-16 Gabriel Dos Reis * io.C (operator<<(Printer&, xpr_expression)): Handle Member_init and member-initializer list in constructor body. 2004-07-15 Gabriel Dos Reis * Mark for 0.17. 2004-07-15 Gabriel Dos Reis * ipr.C (Visitor::visit(const Type_id&)): Define. (Visitor::visit(const Name_as_expr&)): Remove. (Unit_impl::make_product): Tidy. (Unit_impl::make_expr_list): Likewise. (CV_type<>): New helper class. (CV_unary<>): Likewise. (CV_binary<>): Likewise. (CV_ternary<>): Likewise. (CV_udt<>): Likewise. (Unit_impl::make_cv_qualified): Rework. Build names for cv-qualified types too. (Unit_impl::make_class): Tidy. (Unit_impl::make_union): Tidy. (Unit_impl::make_enum): Tidy. (Unit_impl::make_namespace): Tidy. (Unit_impl::make_var): Move here from impl.H. * io.C (pp_base::visit(const Name&)): Visit as an expression. (operator<<(Printer&, xpr_expression)): Handle Expr_list. (operator<<(Printer&, xpr_name)): Render Type_id too. (operator<<(Printer&, xpr_primary_expression)): Remove case for Name_as_expr. (operator<<(Printer&, Type::Qualifier)): New. (operator<<(Printer&, xpr_type_expr)): Handle Expr_as_type. 2004-07-08 Gabriel Dos Reis * Mark for 0.16 2004-07-08 Gabriel Dos Reis * io.C (operator<<(Printer&, xpr_type)): Uncomment out the case for template printing. (operator<<(Printer&, xpr_declaration)): Handle Bit_field. (hash): New. (xpr_base_classes): New. (operator<<(Printer&, xpr_base_classes)): Handle base classes. 2004-07-07 Gabriel Dos Reis * ipr.C (Unit_impl::make_field): Define. (Unit_impl::make_bit_field): Likewise. (Visitor::visit(const Field&)): Likewise. (Visitor::visit(const Bit_field&)): Likewise. 2004-07-06 Gabriel Dos Reis * io.C (operator<<(Printer&, xpr::statement)::Impl ::visit(const For&)): Fix thinko. 2004-07-05 Gabriel Dos Reis * io.C: Tidy. (operator<<(Printer&, xpr_expression)::Impl): Print Scope here. 2004-07-05 Gabriel Dos Reis * Mark for 0.15. 2004-07-04 Gabriel Dos Reis * Mark for 0.14. 2004-07-04 Gabriel Dos Reis * ipr.C (Visitor::visit(const Field&)): New. * io.C (newline): Tidy. (pp_base::visit(const Decl&)): Call visit(const Expr&). 2004-07-02 Gabriel Dos Reis * Mark for 0.13. 2004-07-02 Gabriel Dos Reis * ipr.C (Visitor::visit(const Conditional&)): Define. (Visitor::visit(const Type_decl&)): Likewise. (Unit_impl::make_type_decl): Likewise. * io.C: Reimplement from scratch. 2004-07-01 Gabriel Dos Reis * Mark for 0.12. 2004-07-01 Gabriel Dos Reis * ipr.C (Unit_impl::make_alias): Define. * io.C (printer::Base::visit(const Node&)): Define. (printer::Base::visit(const Name&)): Likewise. (printer::Base::visit(const Expr&)): Likewise. (printer::Base::visit(const Type&)): Likewise. (printer::Base::visit(const Stmt&)): Likewise. (printer::Base::visit(const Decl&)): Likewise. (printer::whitespace): Likewise. (printer::left_paren): Likewise. (printer::right_paren): Likewise. (printer::left_brace): Likewise. (printer::right_brace): Likewise. (printer::left_bracket): Likewise. (printer::right_bracket): Likewise. (printer::xpr::Expression::visit(const Add_assign&)): Likewise. (printer::xpr::Expression::visit(const Bitand_assign&)): Likewise. (printer::xpr::Expression::visit(const Bitor_assign&)): Likewise. (printer::xpr::Expression::visit(const Bitxor_assign&)): Likewise. (printer::xpr::Expression::visit(const Div_assign&)): Likewise. (printer::xpr::Expression::visit(const Mul_assign&)): Likewise. (printer::xpr::Expression::visit(const Shift_left_assign&)): Likewise. (printer::xpr::Expression::visit(const Shift_right_assign&)): Likewise. (printer::xpr::Expression::visit(const Sub_assign&)): Likewise. (printer::xpr::Expression::visit(const Destructor&)): Likewise. 2004-06-29 Gabriel Dos Reis * Mark for 0.11. 2004-06-29 Gabriel Dos Reis * ipr.C (Visitor::visit(const Instantiation&)): Remove. (Visitor::visit(const Qualified&)): Remove. (Visitor::visit(const Object_type&)): Remove. (Visitor::visit(const Expr_as_type&)): Define. (Visitor::visit(const Specialization&)): Likewise. (Unit_impl::make_cv_qualified::Builder): Simplify. (Unit_impl::Unit_impl): Tidy. 2004-06-29 Gabriel Dos Reis * ipr.C: Clean up. (Visitor::visit(const Product&)): Define. (Visitor::visit(const Scope&)): Likewise. (Visitor::visit(const Expr_list&)): Likewise. (Class_impl::): Remove. (Union_impl::): Remove. (Enum_impl::): Remove. (Parameter_impl::): Remove. (Parameter_list_impl::add_param): Define. (Unit_impl::make_expr_list): Rename from Unit_impl::make_sequence. (Unit_impl::make_product): Likewise. (Unit_impl::make_fun_decl): Define. (Unit_impl::make_enumerator): Likewise. (Unit_impl::make_base_type): Liekwise. (Unit_impl::make_constructor): Likewise. * io.C (printer::xpr::Expression::visit(const Scope&)): Reimplement. (printer::xpr::Expression::visit(const Function&)): Tidy. (printer::xpr::Expression::visit(const Product&)): Define. (printer::xpr::Expression::visit(const Expr_list&)): Likewise. (printer::xpr::Expression::visit(const Function_body&)): Likewise. (printer::xpr::Declaration::visit(const Parameter_list&)): Likewise. (printer::xpr::Declaration::visit(const Enumerator&)): Likewise. (printer::xpr::Declaration::visit(const Base_type&)): Likewise. 2004-06-19 Gabriel Dos Reis * io.C: Reflect changes as fallout of removal of unnecessary codes in ipr/interface.H. (printer::xpr::Expression::visit(const Throw&)): Define. * ipr.C (Visitor::visit(const Throw&)): Define. (Visitor::visit(const Base_type&)): Likewise. (New_impl::): Remove. (Function_body_impl::): Comment out. (Unit_impl::make_pointer): Remove. (Unit_impl::make_reference): Remove. (Unit_impl::make_array): Remove. (Unit_impl::make_funuction): Remove. (Unit_impl::make_if_then_else): Remove. (Unit_impl::make_for): Remove. (Unit_impl::make_literal): Remove. (Unit_impl::make_annotation): Remove. 2004-06-12 Gabriel Dos Reis * Mark for 0.10. 2004-06-12 Gabriel Dos Reis * io.C: Throught replace direct formatting to Base::stream with an operation on printers. (print_namable): Remove. (printer::xpr::Statement::visit(const Signature&)): Remove. (printer::xpr::Declaration::visit(const Signature&)): Remove. * ipr.C (validate_index): Remove. (error): Likewise. (default_to): Likewise. (as): New function. Throughout Visitor member functions, replace default_to<> with a call to visit(as<>()). (scope_impl): Remove. (Instantiation_impl::name): Remove. (Instantiation_impl::scope_impl): Likewise. (Instantiation_impl::args): Likewise. (Overload_impl::size): Likewise. (Overload_impl::get): Likewise. (Overload_impl::operator[]): Likewise. (Overload_impl::accept): Likewise. (Overload_impl::register_decl): Likewise. (Signature): Remove all member definitions. (Function_impl): Likewise. (Member_impl): Likewise. (Block_impl): Likewise. (Handler_impl): Likewise. (Member_decl_impl): Likewise. (Fun_decl_impl): Likewise. 2004-06-05 Gabriel Dos Reis * ipr.C (Unit_impl::make_signature): Remove. (Signature_impl): Remove. (Unit_impl::make_function): Tidy. 2004-05-23 Gabriel Dos Reis * ipr.C (Call_impl): Remove. (Object_construction_impl): Likewise. (Unit_impl::make_labeled_stmt): Remove. (Unit_impl::make_if_then): Likewise. (Unit_impl::make_switch): Likewise. (Unit_impl::make_while): Likewise. (Unit_impl::make_do_while): Likewise. (Unit_impl::make_break): Likewise. (Unit_impl::make_continue): Likewise. (Unit_impl::make_goto): Likewise. (Unit_impl::make_return): Likewise. (Unit_impl::make_handler): Likewise. (Unit_impl::make_expr_sizeof): Likewise. (Unit_impl::make_type_sizeof): Likewise. (Unit_impl::make_expr_typeid): Likewise. (Unit_impl::make_type_typeid): Likewise. * io.C: Match renaming in ipr/interface.H (ipr::printer::xpr::Statement::visit(const Labeled_stmt&)): Tidy. * ipr.C: Replace "Program" with "Unit", throughout. (Scope_impl::make_identifier::Impl::scope): Override Name::scope. (Scope_impl::make_operator::Impl::scope): Likewise. (Scope_impl::make_conversion::Impl::scope): Likewise. (Function_body_impl::members): Remove. (Function_body_impl::handlers): Likewise. (Visitor::visit(const Assign&)): Define. (Visitor::visit(const Class_template&)): Remove. (Visitor::visit(const Fun_decl_template&)): Likewise. (Visitor::visit(const Template&)): Default to Visitor::visit(const Type&). 2004-05-17 Gabriel Dos Reis * Mark for 0.08. 2004-05-17 Gabriel Dos Reis * io.C (Expression_printer::visit(const Type&)): Handle constructed types. 2004-05-14 Gabriel Dos Reis * ipr.C (Program_impl::make_pointer): Cache computed value. (Program_impl::make_reference): Likewise. (Program_impl::make_cv_qualified): Define. (make_array): Cache computed value. 2004-05-13 Gabriel Dos Reis * Mark for 0.07. 2004-05-13 Gabriel Dos Reis * ipr.C (Visitor::visit): Rework defaults. Remove definitions for classes removed from ipr/impl.H. See ipr/ChangeLog. (Scope_impl::make_block): Make node out of local class impl. (Scope_impl::make_identifier): Likewise. (Scope_impl::make_operator): Likewise. (Scope_impl::make_conversion): Likewise. (Scope_impl::make_qualified): Likewise. (Scope_impl::make_instantiation): Likewise. (Program_impl::make_pointer): Likewise. (Program_impl::make_reference): Likewise. (Program_impl::make_array): Likewise. (Program_impl::make_function): Likewise. (Program_impl::make_if_then): Likewise. (Program_impl:make_if_then_else): Likewise. (Program_impl::make_switch): Likewise. (Program_impl::make_while): Likewise. (Program_impl::make_do_while): Likewise. (Program_impl::make_for): Likewise. (Program_impl::make_break): Likewise. (Program_impl::make_continue): Likewise. (Program_impl::make_goto): Likewise. (Program_impl::make_return): Likewise. (Program_impl::make_handler): Likewise. (Program_impl::make_annotation): Likewise. * io.C: Rework pretty-printers. 2004-05-05 Gabriel Dos Reis * Mark for 0.06. 2004-05-05 Gabriel Dos Reis * ipr.C (Program_impl::Program_impl): Initialize members classtype, uniontype, enumtype and namespacetype. 2004-05-03 Gabriel Dos Reis * io.C (missing_overrider): Don't make static. (print_namable): Make member of namespace ipr. (missing_overrider): Move from here to... * ipr.C (missing_overrider): ...there. (Parameter_impl::Parameter_impl): Rervet previous changes. (Var_impl::Var_impl): Likewise. 2004-05-01 Gabriel Dos Reis * Mark for 0.05. 2004-05-01 Gabriel Dos Reis * io.C (missing_overrider): New. (print_namable): Likewise. (Expression_printer::visit(const Node&)): Implement. Default to missing_overrider. (Expression_printer::visit(const Name&)): Likewise. (Expression_printer::visit(const Expr&)): Likewise. (Expression_printer::visit(const Statement&)): Define. (Expression_printer::visit(const Type&)): Implement in terms of print_namable. (Expression_printer::visit(const Decl&)): Likewise. (Statement_printer::visit(const Node&)): Define. Default to missing_overrider. (Statement_printer::visit(const Name&)): Likewise. (Statement_printer::visit(const Expr&)): Likewise. (Statement_printer::visit(const Type&)): Likewise. (Statement_printer::visit(const Statement&)): Likewise. (Statement_printer::visit(const Decl&)): Likewise. (Declaration_printer::visit(const Node&)): Likewise. (Declaration_printer::visit(const Name&)): Likewise. (Declaration_printer::visit(const Expr&)): Likewise. (Declaration_printer::visit(const Type&)): Likewise. (Declaration_printer::visit(const Statement&)): Likewise. (Sequence_printer::visit(const Node&)): Likewise. (Sequence_printer::visit(const Name&)): Likewise. (Sequence_printer::visit(const Expr&)): Likewise. (Sequence_printer::visit(const Type&)): Likewise. (Sequence_printer::visit(const Statement&)): Likewise. (Sequence_printer::visit(const Decl&)): Likewise. * ipr.C (Visitor::visit(const Node&)): Don't implement. (Visitor::visit(const Name&)): Likewise. (Visitor::visit(const Type&)): Likewise. (Visitor::visit(const Expr&)): Likewise. (Visitor::visit(const Statement&)): Likewise. (Visitor::visit(const Decl&)): Likewise. (Program_impl::Builtin<>::is_named): Rename to ...::has_name. (Visitor::visit(const Bit_xor&)): Tidy. (Fun_decl_impl): Adjust call to Decl_impl<>. (Var_impl): Likewise. 2004-04-25 Gabriel Dos Reis * io.C (ipr::Expression::visit): Implement more expression pretty-printer routines. (::accept): Remove. 2004-04-21 Gabriel Dos Reis * ipr.C (Visitor::visit): Try to make room for reasonable defaults. * io.C (Declaration_printer): New. Implement basic functionalities. (Expression_printer): Same. 2004-04-08 Gabriel Dos Reis * ipr.C (Program_impl::Program_impl): Initializer built-in type objects. 2004-03-23 Gabriel Dos Reis * ipr.C (Mul_impl): Implement constructor taking two Expr. (Div_impl): Likewise. (Modulo_impl): Likewise. (Add_impl): Likewise. (Sub_impl): Likewise. (Shift_left_impl): Likewise. (Shift_right_impl): Likewise. (Less_impl): Likewise. (Greater_impl): Likewise. (Less_equal_impl): Likewise. (Greater_equal_impl): Likewise. (Equal_impl): Likewise. (Not_equal_impl): Likewise. (Bit_and_impl): Likewise. (Bit_xor_impl): Likewise. (Bit_or_impl): Likewise. (And_impl): Likewise. (Or_impl): Likewise. (Comma_impl): Likewise. (Assign_impl): Likewise. (Array_select_impl): Likewise. (Dot_select_impl): Likewise. (Arrow_select_impl): Likewise. (Scope_select_impl): Likewise. 2004-03-22 Gabriel Dos Reis * ipr.C (Name_expr_impl): Implement explicit constructor. (Pre_increment_impl): Likewise. (Post_increment_impl): Likewise. (Pre_decrement_impl): Likewise. (Post_decrement_impl): Likewise. (Expr_typeid_impl): Likewise. (Type_typeid_impl): Likewise. (Expr_sizeof_impl): Likewise. (Type_sizeof_impl): Likewise. (Deref_impl): Likewise. (Address_impl): Likewise. (Unary_plus_impl): Likewise. (Negate_impl): Likewise. (Not_impl): Likewise. (Complement_impl): Likewise. (Delete_impl): Likewise. (Array_delete_impl): Likewise. (Dot_star_impl): Likewise. (Arrow_star_impl): Likewise. ================================================ FILE: src/builtin.def ================================================ // // This file is part of The Pivot framework. // Written by Gabriel Dos Reis. // See LICENSE for copright and license notices. // BUILTIN_TYPE(Typename, u8"typename") BUILTIN_TYPE(Class, u8"class") BUILTIN_TYPE(Union, u8"union") BUILTIN_TYPE(Enum, u8"enum") BUILTIN_TYPE(Namespace, u8"namespace") BUILTIN_TYPE(Auto, u8"auto") BUILTIN_TYPE(Void, u8"void") BUILTIN_TYPE(Bool, u8"bool") BUILTIN_TYPE(Char, u8"char") BUILTIN_TYPE(Schar, u8"signed char") BUILTIN_TYPE(Uchar, u8"unsigned char") BUILTIN_TYPE(Wchar_t, u8"wchar_t") BUILTIN_TYPE(Char8_t, u8"char8_t") BUILTIN_TYPE(Char16_t, u8"char16_t") BUILTIN_TYPE(Char32_t, u8"char32_t") BUILTIN_TYPE(Short, u8"short") BUILTIN_TYPE(Ushort, u8"unsigned short") BUILTIN_TYPE(Int, u8"int") BUILTIN_TYPE(Uint, u8"unsigned int") BUILTIN_TYPE(Long, u8"long") BUILTIN_TYPE(Ulong, u8"unsigned long") BUILTIN_TYPE(Long_long, u8"long long") BUILTIN_TYPE(Ulong_long, u8"unsigned long long") BUILTIN_TYPE(Float, u8"float") BUILTIN_TYPE(Double, u8"double") BUILTIN_TYPE(Long_double, u8"long double") BUILTIN_TYPE(Ellipsis, u8"...") ================================================ FILE: src/cxx-ipr-io.cxx ================================================ // // This file is part of The Pivot framework. // Written by Gabriel Dos Reis. // See LICENSE for copyright and license notices. // // Module implementation unit for cxx.ipr.io. // Contains the XPR pretty-printer visitor hierarchies and // definitions of Printer member functions. module; #include #include #include #include #include module cxx.ipr.io; import cxx.ipr.traversal; namespace ipr { // Format a sequence of items via a transformer. template static Printer& sequenced(Printer& pp, const Sequence& s, F f) { std::for_each(s.begin(), s.end(), f); return pp; } // Format a comma-separated sequence of items template static Printer& comma_separated(Printer& pp, const Sequence& s) { auto worker = [&pp, first = true](auto& x) mutable { if (not first) pp << ", "; pp << F(x); first = false; }; std::for_each(s.begin(), s.end(), worker); return pp; } Printer& operator<<(Printer& p, Mapping_level x) { auto n = static_cast(x); p.channel() << n; return p; } Printer& operator<<(Printer& p, Decl_position x) { auto n = static_cast(x); p.channel() << n; return p; } struct pp_base : Constant_visitor { explicit pp_base(Printer& p) : pp(p) { } using Visitor::visit; void visit(const Type& t) override { visit(as(t)); } void visit(const Decl& d) override { visit(as(d)); } protected: Printer& pp; }; Printer::Printer(const Lexicon& lex, std::ostream& os) : lexicon{lex}, stream{os}, pad{Padding::None}, emit_newline{false}, pending_indentation{0} { } Printer& Printer::operator<<(const char8_t* s) { write(s); return *this; } void Printer::write(util::word_view w) { // FIXME: workaround lack of standard support for insertion of // sequence of utf-8 code units. std::copy(w.begin(), w.end(), std::ostream_iterator(this->stream)); } template struct Token_helper { T const value; Token_helper(T t) : value(t) { } }; template inline Token_helper token(T t) { return Token_helper(t); } template inline Printer& operator<<(Printer& printer, Token_helper t) { return printer << t.value << Printer::Padding::None; } inline Printer& insert_xtoken(Printer& printer, const char* s) { return printer << s << Printer::Padding::None; } struct needs_newline { }; inline Printer& operator<<(Printer& printer, needs_newline) { printer.needs_newline(true); return printer; } struct newline { }; Printer& operator<<(Printer& printer, newline) { printer << token('\n'); const int n = printer.indent(); for (int i = 0; i < n; ++i) printer << ' '; printer.needs_newline(false); return printer; } // -- An Expr_list is mostly a expression-seq. See print_sequence(). static inline Printer& operator<<(Printer& pp, const Expr_list& l) { return comma_separated(pp, l.elements()); } // -- Print out a sequence of type. static inline Printer& operator<<(Printer& pp, const Sequence& s) { return comma_separated(pp, s); } // -- A Parameter_list is mostly a Parameter-seq. See print_sequence(). static inline Printer& operator<<(Printer& pp, const Parameter_list& l) { return comma_separated(pp, l.elements()); } struct xpr_initializer { const ipr::Expr& expr; explicit xpr_initializer(const ipr::Expr& e) : expr(e) { } }; static Printer& operator<<(Printer&, xpr_initializer); struct indentation { const int amount; indentation(int n) : amount(n) { } }; inline Printer& operator<<(Printer& printer, indentation i) { printer.indent(i.amount); return printer; } struct newline_and_indent { const int indentation; newline_and_indent(int n = 0) : indentation(n) { } }; inline Printer& operator<<(Printer& printer, newline_and_indent m) { printer << indentation(m.indentation) << newline(); return printer; } struct xpr_primary_expr { const Expr& expr; explicit xpr_primary_expr(const Expr& e) : expr(e) { } }; static Printer& operator<<(Printer&, xpr_primary_expr); struct xpr_cast_expr { const Expr& expr; xpr_cast_expr(const Expr& e) : expr(e) { } }; static Printer& operator<<(Printer&, xpr_cast_expr); struct xpr_assignment_expression { const Expr& expr; xpr_assignment_expression(const Expr& e) : expr(e) { } }; static Printer& operator<<(Printer&, xpr_assignment_expression); struct xpr_exception_spec { const Expr& spec; }; static Printer& operator<<(Printer&, xpr_exception_spec); // Pretty-print identifiers. struct xpr_identifier { util::word_view txt; explicit xpr_identifier(const ipr::String& s) : txt{s.characters()} { } template explicit xpr_identifier(const char8_t (&s)[N]) : txt{s} { } }; static inline Printer& operator<<(Printer& printer, xpr_identifier id) { if (printer.padding() == Printer::Padding::Before) printer << ' '; printer.write(id.txt); return printer << Printer::Padding::Before; } Printer& operator<<(Printer& pp, const Identifier& id) { return pp << xpr_identifier(id.string()); } Printer& operator<<(Printer& pp, const Logogram& l) { return pp << xpr_identifier(l.what()); } // ------------------------------ // -- Pretty printing of names -- // ------------------------------ // ------------------------------------------- // -- Pretty-printing of classic expression -- // ------------------------------------------- // -- name: // identifier // operator-function-id // conversion-function-id // type-id // scope-ref // template-id // ctor-name // dtor-name // template-parameter-canonical-name namespace xpr { struct Name : pp_base { explicit Name(Printer& p) : pp_base(p) { } void visit(const Identifier& id) final { pp << id; } // -- operator-function-id: // operator operator-name // // operator-name: one of // + ++ -= - -- -= = == ! != % %= // * *= / /= ^ ^= & && &= | || |= // ~ , () [] < << <<= <= > >> // >>= >= new new[] delete delete[] void visit(const Operator& o) final { pp << xpr_identifier(u8"operator"); const ipr::String& s = o.opname(); if (not std::isalpha(*s.begin())) { pp.write(s.characters()); pp << Printer::Padding::None; } else pp << xpr_identifier(s); } // -- conversion-function-id: // operator type-id // -- NOTE: This production is very different from ISO Standard // -- production for conversion-function-id. void visit(const Conversion& c) final { // For now only regular cast, later we'll add support for overloading // dynamic_cast, reinterpret_cast, const_cast and static_cast pp << xpr_identifier(u8"operator") << xpr_identifier(u8"cast") << token("<|") << xpr_type(c.target()) << token("|>"); } // Suffix is a user defined literal, as in operator""_km. // We display it as operator "_km". void visit(const Suffix& s) final { pp << xpr_identifier(u8"operator") << token('"') << s.name() << token('"'); } // A type-id is just the spelling of the type expression. void visit(const Type_id& n) final { pp << xpr_type(n.type_expr()); } // -- A Scope_ref corresponds to Standard C++ notion of // -- qualified-id. Here, it takes the production of // scope-ref: // @ name ( identifier ) void visit(const Scope_ref& n) final { pp << xpr_expr(n.scope()) << token("::") << xpr_expr(n.member()); } // -- template-id: // primary-expression < expression-seq > void visit(const Template_id& n) final { n.template_name().accept(*this); pp << token("<|") << n.args() << token("|>"); } // -- ctor-name: // # ctor void visit(const Ctor_name&) final { pp << xpr_identifier(u8"#ctor"); } // -- dtor-name // # dtor void visit(const Dtor_name&) final { pp << xpr_identifier(u8"#dtor"); } }; } struct xpr_name { const Name& name; explicit xpr_name(const Name& n) : name(n) { } explicit xpr_name(const Decl& d) : name(d.name()) { } }; static inline Printer& operator<<(Printer& printer, xpr_name x) { xpr::Name pp { printer }; x.name.accept(pp); return printer; } // -- primary-expression: // name // label // type // ( expression ) // { expression-seq } namespace xpr { struct Primary_expr : xpr::Name { Primary_expr(Printer& pp) : xpr::Name(pp) { } void visit(const Label& l) final { xpr::Name::visit(l.name()); } void visit(const Id_expr& id) final { pp << xpr_name{ id.name() }; } void visit(const Literal&) final; void visit(const Symbol& s) final { pp << xpr_name{ s.name() }; } void visit(const As_type& t) final { if (denote_builtin_type(t)) pp << xpr_name(t.name()); else pp << xpr_primary_expr(t.expr()); } void visit(const Phantom&) final { } // nothing to print void visit(const Enclosure& e) final { static constexpr const char* syntax[] = { "\0\0", "()", "{}", "[]", "<>" }; const auto delimiters = syntax[util::rep(e.delimiters())]; pp << token(delimiters[0]) << xpr_expr(e.expr()) << token(delimiters[1]); } void visit(const Expr& e) override { pp << token('(') << xpr_expr(e) << token(')'); } void visit(const Decl& d) override { d.name().accept(*this); } }; void Primary_expr::visit(const ipr::Literal& l) { const String& s = l.string(); String::iterator cur = s.begin(); String::iterator end = s.end(); for (; cur != end; ++cur) switch (*cur) { default: pp << static_cast(*cur); break; case '\n': pp << "\\n"; break; case '\r': pp << "\\r"; break; case '\f': pp << "\\f"; break; case '\t': pp << "\\t"; break; case '\v': pp << "\\v"; break; case '\b': pp << "\\b"; break; case '\a': pp << "\\a"; break; case '\\': pp << "\\\\"; break; // case '\'': // pp << "\\'"; // break; case '\0': pp << "\\0"; break; case '\1': case '\2': case '\3': pp << "\\0" << std::oct << static_cast(*cur); break; } } } static inline Printer& operator<<(Printer& printer, xpr_primary_expr x) { xpr::Primary_expr pp(printer); x.expr.accept(pp); return printer; } struct xpr_postfix_expr { const Expr& expr; explicit xpr_postfix_expr(const Expr& e) : expr(e) { } }; static Printer& operator<<(Printer&, xpr_postfix_expr); template void new_style_cast(Printer& pp, const Cast_expr& e, const char8_t (&op)[N]) { pp << xpr_identifier(op) << token("<|") << xpr_type(e.type()) << token("|>") << token('(') << xpr_expr(e.expr()) << token(')'); } namespace xpr { // -- postfix-expression: // primary-expression struct Postfix_expr : xpr::Primary_expr { Postfix_expr(Printer& pp) : xpr::Primary_expr(pp) { } // postfix-expression [ expression ] void visit(const Array_ref& e) final { pp << xpr_postfix_expr(e.base()) << token('[') << xpr_expr(e.member()) << token(']'); } // postfix-expression . primary-expression void visit(const Dot& e) final { pp << xpr_postfix_expr(e.base()) << token('.') << xpr_primary_expr(e.member()); } // postfix-expression -> primary-expression void visit(const Arrow& e) final { pp << xpr_postfix_expr(e.base()) << token("->") << xpr_primary_expr(e.member()); } // postfix-expression ( expression-list ) void visit(const Call& e) final { pp << xpr_postfix_expr(e.function()) << token('(') << e.args() << token(')'); } void visit(const Construction& e) final { pp << xpr_type(e.type()) << xpr_primary_expr(e.arguments()); } // postfix-expression -- void visit(const Post_decrement& e) final { pp << xpr_postfix_expr(e.operand()) << token("--"); } // postfix-expression ++ void visit(const Post_increment& e) final { pp << xpr_postfix_expr(e.operand()) << token("++"); } // dynamic_cast < type > ( expression ) void visit(const Dynamic_cast& e) final { new_style_cast(pp, e, u8"dynamic_cast"); } // static_cast < type > ( expression ) void visit(const Static_cast& e) final { new_style_cast(pp, e, u8"static_cast"); } // const_cast < type > ( expression ) void visit(const Const_cast& e) final { new_style_cast(pp, e, u8"const_cast"); } // reinterpret_cast < type > ( expression ) void visit(const Reinterpret_cast& e) final { new_style_cast(pp, e, u8"reinterpret_cast"); } // typeid ( expression ) void visit(const Typeid& e) final { pp << xpr_identifier(u8"typeid") << token('(') << xpr_expr(e.operand()) << token(')'); } // noexcept '(' expression ')' void visit(const Noexcept& e) final { pp << xpr_identifier(u8"noexcept") << token('(') << xpr_expr(e.operand()) << token(')'); } }; } static inline Printer& operator<<(Printer& printer, xpr_postfix_expr x) { xpr::Postfix_expr pp(printer); x.expr.accept(pp); return printer; } // -- unary-expression: // -- cast-expression // ++ cast-expression // & cast-expression // ~ cast-expression template inline void unary_operation(Printer& pp, const Unary& e, const char* op) { pp << token(op) << xpr_cast_expr(e.operand()); } namespace xpr { struct Unary_expr : xpr::Postfix_expr { Unary_expr(Printer& pp) : xpr::Postfix_expr(pp) { } void visit(const Pre_decrement& e) final { unary_operation(pp, e, "--"); } void visit(const Pre_increment& e) final { unary_operation(pp, e, "++"); } void visit(const Address& e) final { unary_operation(pp, e, "&"); } void visit(const Complement& e) final { unary_operation(pp, e, "~"); } void visit(const Deref& e) final { unary_operation(pp, e, "*"); } void visit(const Unary_minus& e) final { unary_operation(pp, e, "-"); } void visit(const Not& e) final { unary_operation(pp, e, "!"); } void visit(const Sizeof& e) final { pp << xpr_identifier(u8"sizeof") << token(' ') << xpr_expr(e.operand()); } void visit(const Args_cardinality& e) final { pp << xpr_identifier(u8"sizeof") << token("...") << token('(') << xpr_expr(e.operand()) << token(')'); } void visit(const Unary_plus& e) final { pp << token('+') << xpr_expr(e.operand()); } void visit(const New& e) final { pp << xpr_identifier(u8"new") << token(' '); if (auto p = e.placement()) pp << token('(') << p.get() << token(") "); // Note: The following does not exactly conform to the ISO C++ grammar (because of ambiguity). pp << xpr_expr(e.initializer()); } void visit(const Delete& e) final { pp << xpr_identifier(u8"delete") << token(' ') << xpr_cast_expr(e.storage()); } void visit(const Array_delete& e) final { pp << xpr_identifier(u8"delete[]") << token(' ') << xpr_cast_expr(e.storage()); } }; } static Printer& operator<<(Printer&, xpr_cast_expr); namespace xpr { struct Cast_expr : xpr::Unary_expr { Cast_expr(Printer& p) : xpr::Unary_expr(p) { } // -- cast-expression // unary-expression // "(" type ")" cast-expression void visit(const Cast& e) final { new_style_cast(pp, e, u8"cast"); } }; } static inline Printer& operator<<(Printer& printer, xpr_cast_expr x) { xpr::Cast_expr pp(printer); x.expr.accept(pp); return printer; } // -- pm-expression: // cast-expression // pm-expression ".*" cast-expression // pm-expression "->*" cast-expression struct xpr_pm_expr { const Expr& expr; explicit xpr_pm_expr(const Expr& e) : expr(e) { } }; static Printer& operator<<(Printer&, xpr_pm_expr); template static void offset_with_pm(Printer& pp, const Member_selection& e, const char* op) { pp << xpr_pm_expr(e.base()) << op << xpr_cast_expr(e.member()); } namespace xpr { struct Pm_expr : xpr::Cast_expr { Pm_expr(Printer& p) : xpr::Cast_expr(p) { } void visit(const Dot_star& e) final { offset_with_pm(pp, e, ".*"); } void visit(const Arrow_star& e) final { offset_with_pm(pp, e, "->*"); } }; } static inline Printer& operator<<(Printer& printer, xpr_pm_expr x) { xpr::Pm_expr pp(printer); x.expr.accept(pp); return printer; } // -- Print a binary expression. Instantiated by the relevant // -- grammar production, with precedence built-in. template inline void binary_expression(Printer& pp, const E& e, Op op) { pp << Left(e.first()) << token(' ') << op << token(' ') << Right(e.second()); } // -- multiplicative-expression: // pm-expression // multiplicative-expression * pm-expression // multiplicative-expression / pm-expression // multiplicative-expression % pm-expression struct xpr_mul_expr { const Expr& expr; explicit xpr_mul_expr(const Expr& e) : expr(e) { } }; namespace xpr { struct Mul_expr : xpr::Pm_expr { Mul_expr(Printer& p) : xpr::Pm_expr(p) { } void visit(const Mul& e) final { binary_expression(pp, e, '*'); } void visit(const Div& e) final { binary_expression(pp, e, '/'); } void visit(const Modulo& e) final { binary_expression(pp, e, '%'); } }; } static inline Printer& operator<<(Printer& printer, xpr_mul_expr x) { xpr::Mul_expr pp(printer); x.expr.accept(pp); return printer; } // -- additive-expression: // multiplicative-expression // additive-expression + multiplicative-expression // additive-expression - multiplicative-expression struct xpr_add_expr { const Expr& expr; explicit xpr_add_expr(const Expr& e) : expr(e) { } }; namespace xpr { struct Add_expr : xpr::Mul_expr { Add_expr(Printer& p) : xpr::Mul_expr(p) { } void visit(const Plus& e) final { binary_expression(pp, e, '+'); } void visit(const Minus& e) final { binary_expression(pp, e, '-'); } }; } static inline Printer& operator<<(Printer& printer, xpr_add_expr x) { xpr::Add_expr pp(printer); x.expr.accept(pp); return printer; } // -- shift-expression: // additive-expression // shift-expression << additive-expression // shift-expression >> additive-expression struct xpr_shift_expr { const Expr& expr; explicit xpr_shift_expr(const Expr& e) : expr(e) { } }; namespace xpr { struct Shift_expr : xpr::Add_expr { Shift_expr(Printer& p) : xpr::Add_expr(p) { } void visit(const Lshift& e) final { binary_expression(pp, e, "<<"); } void visit(const Rshift& e) final { binary_expression(pp, e, ">>"); } }; } static inline Printer& operator<<(Printer& printer, xpr_shift_expr x) { xpr::Shift_expr pp(printer); x.expr.accept(pp); return printer; } // -- relational-expression // shift-expression // relational-expression < shift-expression // relational-expression > shift-expression // relational-expression <= shift-expression // relational-expression >= shift-expression struct xpr_rel_expr { const Expr& expr; explicit xpr_rel_expr(const Expr& e) : expr(e) { } }; namespace xpr { struct Rel_expr : xpr::Shift_expr { Rel_expr(Printer& p) : xpr::Shift_expr(p) { } void visit(const Less& e) final { binary_expression(pp, e, '<'); } void visit(const Less_equal& e) final { binary_expression(pp, e, "<="); } void visit(const Greater& e) final { binary_expression(pp, e, '>'); } void visit(const Greater_equal& e) final { binary_expression(pp, e, ">="); } }; } static inline Printer& operator<<(Printer& printer, xpr_rel_expr x) { xpr::Rel_expr pp(printer); x.expr.accept(pp); return printer; } // -- equality-expression // relational-expression // equality-expression == relational-expression // equality-expression != relational-expression struct xpr_eq_expr { const Expr& expr; explicit xpr_eq_expr(const Expr& e) : expr(e) { } }; namespace xpr { struct Eq_expr : xpr::Rel_expr { Eq_expr(Printer& p) : xpr::Rel_expr(p) { } void visit(const Equal& e) final { binary_expression(pp, e, "=="); } void visit(const Not_equal& e) final { binary_expression(pp, e, "!="); } }; } static inline Printer& operator<<(Printer& printer, xpr_eq_expr x) { xpr::Eq_expr pp(printer); x.expr.accept(pp); return printer; } // -- and-expression // equality-expression // and-expression & equality-expression struct xpr_and_expr { const Expr& expr; explicit xpr_and_expr(const Expr& e) : expr(e) { } }; namespace xpr { struct And_expr : xpr::Eq_expr { And_expr(Printer& p) : xpr::Eq_expr(p) { } void visit(const Bitand& e) final { binary_expression(pp, e, '&'); } }; } static inline Printer& operator<<(Printer& printer, xpr_and_expr x) { xpr::And_expr pp(printer); x.expr.accept(pp); return printer; } // -- exclusive-or-expression // and-expression // exclusive-or-expression ^ and-expression struct xpr_xor_expr { const Expr& expr; explicit xpr_xor_expr(const Expr& e) : expr(e) { } }; namespace xpr { struct Xor_expr : xpr::And_expr { Xor_expr(Printer& p) : xpr::And_expr(p) { } void visit(const Bitxor& e) final { binary_expression(pp, e, '^'); } }; } static inline Printer& operator<<(Printer& printer, xpr_xor_expr x) { xpr::Xor_expr pp(printer); x.expr.accept(pp); return printer; } // -- inclusive-or-expression // exclusive-or-exclusive // inclusive-or-expression | exclusive-or-expression struct xpr_ior_expr { const Expr& expr; explicit xpr_ior_expr(const Expr& e) : expr(e) { } }; namespace xpr { struct Ior_expr : xpr::Xor_expr { Ior_expr(Printer& p) : xpr::Xor_expr(p) { } void visit(const Bitor& e) final { binary_expression(pp, e, '|'); } }; } static inline Printer& operator<<(Printer& printer, xpr_ior_expr x) { xpr::Ior_expr pp(printer); x.expr.accept(pp); return printer; } // -- logical-and-expression // inclusive-or-expression // logical-and-expression && inclusive-or-expression struct xpr_land_expr { const Expr& expr; explicit xpr_land_expr(const Expr& e) : expr(e) { } }; namespace xpr { struct Land_expr : xpr::Ior_expr { Land_expr(Printer& p) : xpr::Ior_expr(p) { } void visit(const And& e) final { binary_expression(pp, e, "&&"); } }; } static inline Printer& operator<<(Printer& printer, xpr_land_expr x) { xpr::Land_expr pp(printer); x.expr.accept(pp); return printer; } // -- logical-or-expression // logical-and-expression // logical-or-expression || logical-and-expression struct xpr_lor_expr { const Expr& expr; explicit xpr_lor_expr(const Expr& e) : expr(e) { } }; namespace xpr { struct Lor_expr : xpr::Land_expr { Lor_expr(Printer& p) : xpr::Land_expr(p) { } void visit(const Or& e) final { binary_expression(pp, e, "||"); } }; } static inline Printer& operator<<(Printer& printer, xpr_lor_expr x) { xpr::Lor_expr pp(printer); x.expr.accept(pp); return printer; } // -- conditional-expression // logical-or-expression // logical-or-expression ? expression : assignment-expression namespace xpr { struct Cond_expr : xpr::Lor_expr { Cond_expr(Printer& p) : xpr::Lor_expr(p) { } void visit(const Conditional& e) final { pp << xpr_lor_expr(e.condition()) << token(" ? ") << xpr_expr(e.then_expr()) << token(" : ") << xpr_assignment_expression(e.else_expr()); } }; } struct xpr_mapping_expression { const Mapping& mapping; explicit xpr_mapping_expression(const Mapping& m) : mapping(m) { } }; // >>>> Yuriy Solodkyy: 2006/05/31 // MSVC 7.1 has problem with calling same function from its local class. // Therefore this class was moved from being a local class of subsequent // function to being just a regular class, which that function uses. struct xpr_mapping_expression_visitor : pp_base { const ipr::Mapping& map; xpr_mapping_expression_visitor(Printer& p, const ipr::Mapping& m) : pp_base(p), map(m) { } void visit(const Function& t) final { pp << token('('); pp << map.parameters(); pp << token(')'); pp << xpr_exception_spec{ t.throws() }; pp << xpr_initializer(map.result()); } void visit(const Forall&) final { pp << token('<'); pp << map.parameters(); pp << token('>'); pp << xpr_initializer(map.result()); } }; // <<<< Yuriy Solodkyy: 2006/05/31 Printer& operator<<(Printer& pp, xpr_mapping_expression e) { xpr_mapping_expression_visitor impl(pp, e.mapping); e.mapping.type().accept(impl); return pp; } // -- assignment-expression: // conditional-expression // logical-or-expression assignment-operator assignment-expression // throw expression // // assignment-operator: one of // = *= /= %= += -= >>= <<= &= ^= |= namespace xpr { struct Assignment_expr : xpr::Cond_expr { Assignment_expr(Printer& p) : xpr::Cond_expr(p) { } void visit(const Assign& e) final { binary_expression (pp, e, '='); } void visit(const Plus_assign& e) final { binary_expression (pp, e, "+="); } void visit(const Bitand_assign& e) final { binary_expression (pp, e, "&="); } void visit(const Bitor_assign& e) final { binary_expression (pp, e, "|="); } void visit(const Bitxor_assign& e) final { binary_expression (pp, e, "^="); } void visit(const Div_assign& e) final { binary_expression (pp, e, "/="); } void visit(const Modulo_assign& e) final { binary_expression (pp, e, "%="); } void visit(const Mul_assign& e) final { binary_expression (pp, e, "*="); } void visit(const Lshift_assign& e) final { binary_expression (pp, e, "<<="); } void visit(const Rshift_assign& e) final { binary_expression (pp, e, ">>="); } void visit(const Minus_assign& e) final { binary_expression (pp, e, "-="); } void visit(const Throw& e) final { pp << xpr_identifier(u8"throw") << token(' ') << xpr_assignment_expression(e.operand()); } void visit(const Mapping& m) final { pp << xpr_mapping_expression (m); } }; } Printer& operator<<(Printer& printer, xpr_assignment_expression x) { xpr::Assignment_expr pp(printer); x.expr.accept(pp); return printer; } // >>>> Yuriy Solodkyy: 2006/05/31 // MSVC 7.1 has problem with calling same function from its local class. // Therefore this class was moved from being a local class of subsequent // function to being just a regular class, which that function uses. struct xpr_expr_visitor : pp_base { xpr_expr_visitor(Printer& p) : pp_base(p) { } void visit(const Comma& e) final { pp << xpr_expr(e.first()) << token("@, ") << xpr_assignment_expression(e.second()); } void visit(const Scope& s) final { sequenced(pp, s.elements(), [&pp = pp](auto& d) { pp << xpr_decl(d, true) << newline(); }); } void visit(const Expr_list& e) final { pp << e; } void visit(const Member_init& e) final { pp << xpr_expr(e.member()) << token('(') << xpr_expr(e.initializer()) << token(')'); } void visit(const Type& t) final { pp << xpr_type(t); } void visit(const Expr& e) final { pp << xpr_assignment_expression(e); } void visit(const Stmt& s) final { pp << xpr_stmt(s); } void visit(const Decl& d) final { // A declaration used as an expression must have appeared // as a primary-expression. pp << xpr_primary_expr(d); } }; // <<<< Yuriy Solodkyy: 2006/05/31 Printer& operator<<(Printer& printer, xpr_expr x) { xpr_expr_visitor impl(printer); x.expr.accept(impl); return printer; } // -- Types -- static Printer& operator<<(Printer& printer, xpr_exception_spec x) { struct Visitor : pp_base { Visitor(Printer& p) : pp_base{ p } { } void visit(const Type& t) final { pp << xpr_identifier(u8"throw") << token('(') << xpr_type(t) << token(')'); } void visit(const Expr& e) final { pp << xpr_identifier(u8"noexcept") << token('(') << xpr_expr(e) << token(')'); } }; printer << token(' '); Visitor v { printer }; x.spec.accept(v); return printer; } struct xpr_base_classes { const ipr::Sequence& bases; explicit xpr_base_classes(const ipr::Sequence& b) : bases(b) { } }; static Printer& operator<<(Printer& pp, xpr_base_classes x) { const Sequence& bases = x.bases; if (not bases.empty()) { pp << token('('); comma_separated(pp, bases); pp << token(')'); } return pp; } Printer& Printer::operator<<(Qualifiers quals) { for (auto q : lexicon.decompose(quals)) *this << q.logogram(); return *this; } struct xpr_type_expr { const Expr& type; explicit xpr_type_expr(const Expr& t) : type(t) { } }; template static Printer& operator<<(Printer& pp, const ipr::Udt& t) { return pp << token(' ') << token('{') << newline_and_indent(3) << xpr_expr(t.scope()) << newline_and_indent(-3) << token('}') << needs_newline(); } // >>>> Yuriy Solodkyy: 2006/05/31 // MSVC 7.1 has problem with calling same function from its local class. // Therefore this class was moved from being a local class of subsequent // function to being just a regular class, which that function uses. static Printer& operator<<(Printer& printer, xpr_type_expr x); struct xpr_type_expr_visitor : pp_base { xpr_type_expr_visitor(Printer& p) : pp_base(p) { } void visit(const Array& a) final { pp << token('[') << xpr_expr(a.bound()) << token(']') << xpr_type(a.element_type()); } void visit(const As_type& t) final { pp << xpr_expr(t.expr()); } void visit(const Class& c) final { pp << xpr_base_classes(c.bases()) << token(' ') << token('{') << newline_and_indent(3) << xpr_expr(c.scope()) << newline_and_indent(-3) << token('}') << needs_newline(); } void visit(const Decltype& t) final { pp << xpr_identifier(u8"decltype") << token(' ') << token('(') << xpr_expr(t.expr()) << token(')'); } void visit(const Function& f) final { pp << token('(') << f.source().operand() << token(')') << xpr_exception_spec{ f.throws() } << xpr_type(f.target()); } void visit(const Pointer& t) final { pp << token('*') << xpr_type(t.points_to()); } void visit(const Ptr_to_member& t) final { pp << token('*') << token('[') << xpr_type(t.containing_type()) << token(']') << token(',') << xpr_type(t.member_type()); } void visit(const Qualified& t) final { pp << t.qualifiers() << xpr_type(t.main_variant()); } void visit(const Reference& t) final { pp << token('&') << xpr_type(t.refers_to()); } void visit(const Rvalue_reference& t) final { pp << token('&') << token('&') << xpr_type(t.refers_to()); } void visit(const Forall& t) final { pp << token('<') << t.source().operand() << token('>') << token(' ') << xpr_type_expr(t.target()); } void visit(const Union& t) final { pp << t; } void visit(const Enum& t) final { pp << t; } void visit(const Namespace& t) final { pp << t; } }; // <<<< Yuriy Solodkyy: 2006/05/31 static Printer& operator<<(Printer& printer, xpr_type_expr x) { xpr_type_expr_visitor impl(printer); x.type.accept(impl); return printer; } // >>>> Yuriy Solodkyy: 2006/05/31 // MSVC 7.1 has problem with calling same function from its local class. // Therefore this class was moved from being a local class of subsequent // function to being just a regular class, which that function uses. struct xpr_type_visitor : pp_base { xpr_type_visitor(Printer& p) : pp_base(p) { } void visit(const As_type& t) final { if (denote_builtin_type(t)) pp << xpr_name(t.name()); else pp << xpr_expr(t.expr()); } void visit(const Array& a) final { pp << xpr_type_expr(a); } void visit(const Function& f) final { pp << xpr_type_expr(f); } void visit(const Pointer& t) final { pp << xpr_type_expr(t); } void visit(const Ptr_to_member& t) final { pp << xpr_type_expr(t); } void visit(const Qualified& t) final { pp << xpr_type_expr(t); } void visit(const Reference& t) final { pp << xpr_type_expr(t); } void visit(const Rvalue_reference& t) final { pp << xpr_type_expr(t); } void visit(const Forall& t) final { pp << xpr_type_expr(t); } void visit(const Type& t) final { // FIXME: Check. pp << xpr_name(t.name()); } void visit(const Product& t) final { pp << t.operand(); } void visit(const Sum& t) final { pp << t.operand(); } }; // <<<< Yuriy Solodkyy: 2006/05/31 Printer& operator<<(Printer& printer, xpr_type x) { xpr_type_visitor impl(printer); x.type.accept(impl); return printer; } // Initializer expressions. static Printer& operator<<(Printer& printer, xpr_initializer x) { struct V : xpr::Assignment_expr { V(Printer& p) : xpr::Assignment_expr(p) { } void visit(const ipr::Type& t) final { pp << xpr_type_expr(t); } void visit(const ipr::Expr& e) final { pp << xpr_expr(e); } void visit(const ipr::Stmt& e) final { pp << xpr_stmt(e); } void visit(const ipr::Decl& e) final { pp << xpr_decl(e); } }; V pp(printer); x.expr.accept(pp); return printer; } // -- Statements -- namespace xpr { struct Stmt : xpr::Assignment_expr { Stmt(Printer& p) : xpr::Assignment_expr(p) { } void visit(const Expr_stmt& e) final { pp << xpr_expr(e.expr()) << token(';') << needs_newline(); } void visit(const Labeled_stmt& s) final { if (pp.needs_newline()) pp << newline_and_indent(-3); else pp << indentation(-3); pp << xpr_identifier(u8"label") << token(' ') << xpr_expr(s.label()) << token(':') << indentation(3) << needs_newline() << xpr_stmt(s.stmt()) << needs_newline(); } void visit(const Block& s) final { pp << token('{') << needs_newline() << indentation(3); sequenced(pp, s.body(), [&pp = pp](auto& e) { pp << xpr_stmt(e) << needs_newline(); }); pp << newline_and_indent(-3) << token('}') << needs_newline(); sequenced(pp, s.handlers(), [&pp = pp](auto& h) { pp << xpr_stmt(h, false); }); } void visit(const Ctor_body& b) final { const Expr_list& inits = b.inits(); if (inits.size() > 0) pp << token(" : ") << inits << needs_newline(); pp << needs_newline() << xpr_stmt(b.block()); } void visit(const If& s) final { pp << xpr_identifier(u8"if") << token(' ') << token('(') << xpr_expr(s.condition()) << token(')') << newline_and_indent(3) << xpr_stmt(s.consequence()); if (auto alt = s.alternative()) { pp << newline_and_indent(-3) << xpr_identifier(u8"else") << newline_and_indent(3) << xpr_stmt(alt.get()); } pp << indentation(-3) << needs_newline(); } void visit(const Return& s) final { pp << xpr_identifier(u8"return") << token(' ') << xpr_expr(s.value()) << token(';') << needs_newline(); } void visit(const Switch& s) final { pp << xpr_identifier(u8"switch") << token(' ') << token('(') << xpr_expr(s.condition()) << token(')') << newline_and_indent(3) << xpr_stmt(s.body()) << newline_and_indent(-3); } void visit(const While& s) final { pp << xpr_identifier(u8"while") << token(' ') << token('(') << xpr_expr(s.condition()) << token(')') << newline_and_indent(3) << xpr_stmt(s.body()) << needs_newline() << indentation(-3); } void visit(const Do& s) final { pp << xpr_identifier(u8"do") << newline_and_indent(3) << xpr_stmt(s.body()) << newline_and_indent(-3) << xpr_identifier(u8"while") << token(' ') << token('(') << xpr_expr(s.condition()) << token(')') << token(';') << needs_newline(); } void visit(const For& s) final { pp << xpr_identifier(u8"for") << token(" (") << xpr_expr(s.initializer()) << token("; ") << xpr_expr(s.condition()) << token("; ") << xpr_expr(s.increment()) << token(')') << newline_and_indent(3) << xpr_stmt(s.body()) << indentation(-3) << needs_newline(); } void visit(const For_in& s) final { pp << xpr_identifier(u8"for") << token(" (") << xpr_decl(s.variable()) << token(" <- ") << xpr_expr(s.sequence()) << token(')') << newline_and_indent(3) << xpr_stmt(s.body()) << indentation(-3) << needs_newline(); } void visit(const Break&) final { pp << xpr_identifier(u8"break") << token(';') << needs_newline(); } void visit(const Continue&) final { pp << xpr_identifier(u8"continue") << token(';') << needs_newline(); } void visit(const Goto& s) final { pp << xpr_identifier(u8"goto") << token(' ') << xpr_expr(s.target()) << token(';') << needs_newline(); } void visit(const Handler& s) final { pp << xpr_identifier(u8"catch") << token(' ') << token('(') << xpr_decl(s.exception()) << token(')') << newline_and_indent(3) << xpr_stmt(s.body()) << newline_and_indent(-3); } void visit(const Decl& d) override { // These are declaration statements, so they end up // with a semicolon. pp << xpr_decl(d, true); } }; struct Location_printer { Printer* pp; // Location is only present on nodes derived from ipr::Stmt. // Nothing to print for other types of nodes at the moment. void operator()(const ipr::Node&) {} void operator()(const ipr::Stmt& stmt) { auto& locus = stmt.source_location(); if (locus.file != File_index{}) { *pp << token("F") << util::rep(locus.file) << token(':') << util::rep(locus.line); if (locus.column != Column_number{}) *pp << token(':') << util::rep(locus.column); *pp << token(' '); } } static void print(Printer& printer, const ipr::Node& node) { if (printer.print_locations) { Constant_visitor location_printer; location_printer.pp = &printer; node.accept(location_printer); } } }; } Printer& operator<<(Printer& printer, xpr_stmt x) { if(printer.needs_newline()) printer << newline_and_indent(); xpr::Location_printer::print(printer, x.stmt); xpr::Stmt impl(printer); x.stmt.accept(impl); return printer; } Printer& Printer::operator<<(Specifiers spec) { for (auto s : lexicon.decompose(spec)) *this << s.logogram(); return *this; } // -- Declarations -- namespace xpr { struct Decl : xpr::Stmt { Decl(Printer& p) : xpr::Stmt(p) { } void visit(const ipr::Alias& d) final { pp << ipr::xpr_name(d) << token(" : ") << d.specifiers() << token(" typedef ") << xpr_expr(d.initializer().get()); } void visit(const ipr::Decl& d) override { pp << ipr::xpr_name(d) << token(" : ") << d.specifiers() << xpr_type(d.type()); if (auto init = d.initializer()) { pp << token('(') << xpr_expr(init.get()) << token(')'); } } void visit(const Typedecl& d) final { pp << ipr::xpr_name(d) << token(" : ") << xpr_type(d.type()); if (auto init = d.initializer()) pp << xpr_type_expr(init.get()); } void visit(const Enumerator& e) final { e.name().accept(*this); if (auto init = e.initializer()) pp << token('(') << xpr_expr(init.get()) << token(')'); } void visit(const Bitfield& b) final { b.name().accept(*this); pp << token(" : #") << xpr_identifier(u8"bitfield") << token('(') << xpr_expr(b.precision()) << token(')') << xpr_type(b.type()); } void visit(const Base_type& b) final { pp << b.specifiers() << xpr_type(b.type()); } void visit(const Fundecl& f) final { pp << ipr::xpr_name(f) << token(" : ") << f.specifiers() << ' ' << token('(') << f.parameters() << token(')'); const Function* pfn = util::view(f.type()); if (pfn) pp << xpr_type(pfn->target()); // dump result type if (auto init = f.initializer()) pp << needs_newline() << xpr_stmt(init.get()); } void visit(const Template& m) final { m.name().accept(*this); pp << token(" : ") << xpr_mapping_expression(m.mapping()); } }; } Printer& operator<<(Printer& printer, xpr_decl x) { if (printer.needs_newline()) printer << newline_and_indent(); xpr::Location_printer::print(printer, x.decl); xpr::Decl impl(printer); x.decl.accept(impl); if (x.needs_semicolon) printer << token(';'); return printer; } Printer& operator<<(Printer& pp, const Translation_unit& unit) { return pp << xpr_expr(unit.global_namespace().scope()); } } // of namespace ipr ================================================ FILE: src/cxx-ipr-io.ixx ================================================ // -*- C++ -*- // // This file is part of The Pivot framework. // Written by Gabriel Dos Reis. // See LICENSE for copyright and license notices. // // Module interface: cxx.ipr.io // The XPR pretty-printer for the IPR node hierarchy. module; #include export module cxx.ipr.io; import cxx.ipr; namespace ipr::util { // A predicate for types with values that can be inserted into standard streams. template concept std_insertable = requires(std::ostream& os, const T& t) { os << t; }; } namespace ipr { // FIXME: Remove these data structures. // A data structure used to map different uses of a name to the // corresponding declaration. It is used by XPR printer to print name // disambiguation information. A similar but a bit more elaborated structure // is used by XPR parser to relink uses of names to appropriate declarations. // The key of the map is the node_id of the name used, while the value // is the information about corresponding declaration. export struct disambiguation_map_type : std::map> { using declarations = std::vector; using size_type = std::ptrdiff_t; // Given a name and a declaration that corresponds to it, looks up // or allocates a disambiguation id for them. size_type get_disambiguation(const ipr::Name& name, const ipr::Decl& declaration) { declarations& decls = (*this)[&name]; declarations::const_iterator p = std::find(decls.begin(), decls.end(), &declaration); if (p == decls.end()) { decls.push_back(&declaration); return decls.size(); // Because disambiguations are 1-based } return (p - decls.begin()) + 1; // Because disambiguations are 1-based } }; export struct Printer { enum class Padding { None, Before, After }; Printer(const Lexicon&, std::ostream&); Padding padding() const { return pad; } bool needs_newline() const { return emit_newline; } void needs_newline(bool b) { emit_newline = b; } void indent(int n) { pending_indentation += n; } int indent() const { return pending_indentation; } std::ostream& channel() { return stream; } Printer& operator<<(const char8_t*); Printer& operator<<(Specifiers); Printer& operator<<(Qualifiers); template requires util::std_insertable Printer& operator<<(T t) { stream << t; return *this; } // Setting padding flags Printer& operator<<(Padding p) { pad = p; return *this; } void write(util::word_view); private: const Lexicon& lexicon; std::ostream& stream; Padding pad; bool emit_newline; int pending_indentation; public: disambiguation_map_type disambiguation_map; bool print_locations = false; }; export struct xpr_decl { const Expr& decl; const bool needs_semicolon; // false, in most cases. xpr_decl(const Expr& d, bool add_semicolon = false) : decl(d), needs_semicolon(add_semicolon) { } }; export struct xpr_stmt { const Expr& stmt; const bool needs_semicolon; // false, in most cases. explicit xpr_stmt(const Expr& s, bool add_semicolon = true) : stmt(s), needs_semicolon(add_semicolon) { } }; export struct xpr_type { const Type& type; explicit xpr_type(const Type& e) : type(e) { } }; export struct xpr_expr { const Expr& expr; explicit xpr_expr(const Expr& e) : expr(e) { } }; export Printer& operator<<(Printer&, xpr_decl); export Printer& operator<<(Printer&, xpr_stmt); export Printer& operator<<(Printer&, xpr_type); export Printer& operator<<(Printer&, xpr_expr); export Printer& operator<<(Printer&, const Translation_unit&); export Printer& operator<<(Printer&, const Identifier&); export Printer& operator<<(Printer&, const Logogram&); export Printer& operator<<(Printer&, Mapping_level); export Printer& operator<<(Printer&, Decl_position); } ================================================ FILE: src/cxx-ipr-syntax.ixx ================================================ // -*- C++ -*- // // This file is part of The Pivot framework. // Written by Gabriel Dos Reis. // See LICENSE for copyright and license notices. // // Module partition: cxx.ipr:syntax // C++ syntactic forms: tokens, attributes, declarators, constraints, // initialization provisions, and designators. // // -- The IPR focuses primarily on capturing the semantics of C++, not mimic - with // -- high fidelity - the ISO C++ source-level syntax minutiae, grammars, and other obscurities. // -- The IPR model of declaration reflects that semantics-oriented view. Occasionally, it // -- is necessary to bridge the gap between the generalized semantics model and the // -- irregularities and other grammatical hacks via IPR directives. Some of those // -- directives need to refer to some form of input source level constructs, especially in // -- cases of templated declarations involving non-ground types. // -- This partition defines interfaces for some syntactic forms, i.e. abstractions of syntactic // -- input, to aid complete embedding of the ISO C++ specification. // -- Note: There is no effort here to reify the parse trees of the ISO C++ grammar. module; #include export module cxx.ipr:syntax; import :vocabulary; // ----------------------------------- // -- Basic syntactic units: Tokens -- // ----------------------------------- // The IPR focuses primarily on semantic aspects of C++ constructs. // Certain constructs, such as uninstantiated template definitions, // are primarily syntactic with minimal semantic processing. IPR nodes // can fully represent those structurally well-defined syntactic entities. // However, C++11 added attributes which are essentially token soups. These // attributes make the IPR interface less abstract than wanted. namespace ipr { // General classification of tokens, e.g. identifier, number, space, comment, etc. export enum class TokenCategory : std::uint8_t { }; // A numerical value associated with each token. export enum class TokenValue : std::uint16_t { }; export struct Lexeme { virtual const String& spelling() const = 0; virtual const Source_location& locus() const = 0; }; export struct Token { virtual const Lexeme& lexeme() const = 0; virtual TokenValue value() const = 0; virtual TokenCategory category() const = 0; }; export struct Attribute { struct Visitor; virtual void accept(Visitor&) const = 0; }; // A simple token used as attribute. export struct BasicAttribute : Unary { const Token& token() const { return operand(); } }; // An attribute of the form `token1 :: token2' export struct ScopedAttribute : Binary { const Token& scope() const { return first(); } const Token& member() const { return second(); } }; // An attribute of the form `token : attribute'. export struct LabeledAttribute : Binary { const Token& label() const { return first(); } const Attribute& attribute() const { return second(); } }; // An attribute of the form `f(args)'. export struct CalledAttribute : Binary&> { const Attribute& function() const { return first(); } const Sequence& arguments() const { return second(); } }; // An attribute of the form `attribute...' export struct ExpandedAttribute : Binary { const Token& expander() const { return first(); } const Attribute& operand() const { return second(); } }; // An attribute of the form `[[using check: memory(3), type(2)]]' export struct FactoredAttribute : Binary&> { const Token& factor() const { return first(); } const Sequence& terms() const { return second(); } }; // An attribute of the form `[[ expr ]]', where `expr' is the elaboration result // of parsing and semantics analysis of the enclosed token sequence. This is a // common non-standard form of attribute. export struct ElaboratedAttribute : Unary { const Expr& elaboration() const { return operand(); } }; struct Attribute::Visitor { virtual void visit(const BasicAttribute&) = 0; virtual void visit(const ScopedAttribute&) = 0; virtual void visit(const LabeledAttribute&) = 0; virtual void visit(const CalledAttribute&) = 0; virtual void visit(const ExpandedAttribute&) = 0; virtual void visit(const FactoredAttribute&) = 0; virtual void visit(const ElaboratedAttribute&) = 0; }; } // -- C++ syntactic forms -- namespace ipr::cxx_form { // -- Syntactic constraints. // -- C++ Concepts, as supported since C++20, focus primarily on the syntactic requirements // -- on combinations of template arguments. The semantics counterpart is not yet supported by // -- the base language. // -- A constraint generally applies a concept to a compile-time argument list. Hence the syntax // -- uses the "template specialization" syntax for the application. The result is a Boolean // -- expression indicating whether the syntactic constraints contained in the body if the // -- concept are satisfied or not. Given a monadic concept (a concept with exactly one parameter), // -- the concept name, as a predicate, can be viewed as a type for a template-type parameter. // -- That is known as the "short-hand notation" . Similar notational convenience for non-type // -- template parameter isn't as straightforward. // -- Given a polyadic concept (a concept with more N parameters, N > 1), a constraint can be formed // -- by applying the concept name to N-1 compile-time arguments (the "trailing arguments"). export struct Constraint_visitor; export struct Constraint { struct Monadic; // -> C; struct Polyadic; // -> C; virtual void accept(Constraint_visitor&) const = 0; }; // An object of this type corresponds to an instance of the first alternative of the ISO C++ // grammar production for type-constraint // nested-name-specifier_opt concept-name struct Constraint::Monadic : Constraint { virtual Optional scope() const = 0; virtual const Identifier& concept_name() const = 0; }; // An object of this type corresponds to an instance of the second alternative of the ISO C++ // grammar production for type-constraint // nested-name-specifier_opt concept-name `< template-argument--list_opt `>` struct Constraint::Polyadic : Constraint { virtual Optional scope() const = 0; virtual const Identifier& concept_name() const = 0; virtual const Sequence& trailing_arguments() const = 0; }; export struct Constraint_visitor { virtual void visit(const Constraint::Monadic&) = 0; virtual void visit(const Constraint::Polyadic&) = 0; }; // Base class for navigation of requirements. export struct Requirement_visitor; // -- Typically, the definition of a concept is expressed in terms of syntactic requirements. // -- Those requirements are formulated as proof obliggations for the validity of stylized // -- expressions (usage patterns). export struct Requirement { struct Simple; // *p = t; struct Type; // typename C::iterator; struct Compound; // { &*p } -> same_as; struct Nested; // requires Trivial; virtual void accept(Requirement_visitor&) const = 0; }; // An object of this type corresponds to an instance of the ISO C++ grammar production // for simple-requirement: // expression `;` struct Requirement::Simple : Requirement { virtual const Expr& expr() const = 0; }; // An object of this type corresponds to an instance of the ISO C++ grammar production // for type-requirement: // `typename` nested-name-specifier_opt type-namexpression `;` struct Requirement::Type : Requirement { virtual Optional scope() const = 0; virtual const Name& type_name() const = 0; }; // An object of this type corresponds to an instance of the ISO C++ grammar production // for compound-requirement: // `{` expression `}` `noexcept`_opt return-type-requirement_opt `;` // where the grammar production for return-type-requirement is // `->` type-constraint struct Requirement::Compound : Requirement { virtual const Expr& expr() const = 0; virtual Optional constraint() const = 0; virtual bool nothrow() const = 0; }; // An object of this type corresponds to an instance of the ISO C++ grammar production // for nested-requirement: // `requires` constant-expression `;` struct Requirement::Nested : Requirement { virtual const Expr& condition() const = 0; }; export struct Requirement_visitor { virtual void visit(const Requirement::Simple&) = 0; virtual void visit(const Requirement::Type&) = 0; virtual void visit(const Requirement::Compound&) = 0; virtual void visit(const Requirement::Nested&) = 0; }; // -- Abstraction of ISO C++ Declarators: // -- The C and C++ languages have tortuous grammars for declarations, whereby // -- declarations are structured to mimic use (operator precedence). A declaration // -- is made of two essential pieces (decl-specifiers-seq and declarator), with an // -- an optional component representing the initializer. The decl-specifier-seq // -- is a soup made of decl-specifier such as `static`, `extern`, etc. and simple-type // -- names such as `int`, and type qualifiers. Compound types are made with // -- type-constructors such as `*` (pointer) or `&` (reference), `[N]` (array), etc. // -- The type-constructors (which really imply some form of data indirection) are specified // -- along with the name being introduced, making up the declarator. // -- Pointer-style type-constructors are modeled by `Indirector`. // -- Mapping-style type constructors (functions, arrays) are modeled by classes in // -- `Morphism` hierarchy. // -- Complete declarators that have mapping-style type contructors are modelled in the // -- `Species_declarator` class hierarchy. export struct Indirector_visitor; // Base class of visitors for traversing `Indirector`s. // Base class for objects representing ptr-operator (C++ grammar). // While the source-level grammar of ISO C++ does not allow attributes on // reference-style ptr-operator, the representation adopted here makes room for that possibility. // Hence all `Indirector` objects have an `attributes()` operation. // At the input source level, `Indirector`s precede the name being declared in the declarator. export struct Indirector { struct Pointer; // pointer indirector struct Reference; // reference indirector struct Member; // non-static member indirector virtual const Sequence& attributes() const = 0; virtual void accept(Indirector_visitor&) const = 0; }; // A `Pointer` indirector object is a simple possibly cv-qualified pointer indirection. // And object of this type corresponds to the instance of the ptr-operator C++ grammar: // `*` attribute-specifier-seq_opt cv-qualifier-seq_opt struct Indirector::Pointer : Indirector { virtual Qualifiers qualifiers() const = 0; }; // Syntactic indication of reference binding flavor. export enum class Reference_flavor { Lvalue, // "&" ptr-operator in an indirector Rvalue // "&&" ptr-operator in an indirector }; // A `Reference` indirector object is a reference indirection. // An object of this type corresponds to an instance of any of the following alternatives // of the ptr-operator C++ grammar production: // `&` attribute-specifier-seq_opt // `&&` attribute-specifier-seq_opt struct Indirector::Reference : Indirector { virtual Reference_flavor flavor() const = 0; }; // A member `Indirector` object represents a ptr-operator that specifies a pointer to member. // An object of this type corresponds to an instance of the following alternative of the // ptr-operator C++ grammar production: // nested-named-specifier `*` attribute-specifier-seq_opt cv-qualifier-seq_opt struct Indirector::Member : Indirector { virtual const Expr& scope() const = 0; virtual Qualifiers qualifiers() const = 0; }; // Traversal of Indirector objects is facilitated by visitor classes deriving // from this interface. export struct Indirector_visitor { virtual void visit(const Indirector::Pointer&) = 0; virtual void visit(const Indirector::Reference&) = 0; virtual void visit(const Indirector::Member&) = 0; }; // -- Species and Morphisms // A species declarator introduces a name along with typical usage of a named being declared. // This usage pattern is captured by a suffix of `Morphism`s. A `Morphism` is a form that // specifies the type constuctor from the declared entity to the type of typical uses of that // entity in expressions. // - Function: a callable expression, the entity is typically called // - Array: a table expression, the entity is typically indexed // -- Morphism and navigation export struct Morphism_visitor; export struct Morphism { struct Function; // -- (T, int) & noexcept struct Array; // -- [34][] [[S::A]] virtual const Sequence& attributes() const = 0; virtual void accept(Morphism_visitor&) const = 0; }; export struct Morphism_visitor { virtual void visit(const Morphism::Function&) = 0; virtual void visit(const Morphism::Array&) = 0; }; // -- Species and navigation export struct Species_visitor; // A species declarator is either a name (possibly a pack), or a parenthesized term declarator, followed // by a possibly empty sequence of morphisms. For instance, in the declaration // bool (*pfs[8])(int) // that declares pfs as an array of 8 pointers to functions taking an `int` returning a `bool`, the complete // declarator `(*pfs[8])(int)` is a parenthesized species (`(*pfs[8])`) with the suffix consisting exactly of // the singleton sequence of function morphism `(int)`. // The term declarator `*pfs[8]` in turn has the indirectors is comprised of the single pointer indirector `*`, // and of the id species `pfs` with array suffix `[8]`. export struct Species_declarator { struct Unqualified_id; // -- int p; struct Pack; // -- Ts... xs struct Qualified_id; // -- int X::count; struct Parenthesized; // parenthesized term declarator -- (*p) virtual const Sequence& suffix() const = 0; virtual void accept(Species_visitor&) const = 0; }; // Base class for representing instances of the C++ production id-expression in the C++ // grammar for declarator-id. export struct Declarator_id : Species_declarator { virtual const Sequence& attributes() const = 0; }; // Representation of an unqualified-id in an instance of the C++ grammar production for declarator-id. // The operation `name()` returns an optional value for use in representing instances of the C++ // grammar production for noptr-abstract-declarator. struct Species_declarator::Unqualified_id : Declarator_id { virtual Optional name() const = 0; }; // Representation of a pack parameter in an instance of the C++ grammar production for declarator-id // The operation `name()` returns an optional value for use in representing instances of the C++ // grammar production for noptr-abstract-abstract-declarator. // Example: // ... x struct Species_declarator::Pack : Declarator_id { virtual Optional name() const = 0; }; // Representation of a qualified-id in an instance of the C++ grammar production for declarator-id // Such qualified-id are used in the out-of-class definition of an entity. struct Species_declarator::Qualified_id : Declarator_id { virtual const ipr::Expr& scope() const = 0; virtual const ipr::Name& member() const = 0; }; // -- Declarator. // -- A declarator is either a term, or a species with a target type. // -- A term is a sequence of indirectors followed by a species. // -- A species indicates the typical usage syntactic structure of the named being declared. export struct Declarator_visitor; export struct Declarator { struct Term; // *p or (&a)[42] struct Targeted; // f(T& p) -> int virtual const Species_declarator& species() const = 0; virtual void accept(Declarator_visitor&) const = 0; }; // A term declarator is a sequence of `Indirector`s followed by a species declarator. // An object of this type represents an instance of the first alternative of the C++ // grammar production for declarator: // ptr-declarator // Examples: // (&a)[42] // *f(T) noexcept struct Declarator::Term : Declarator { virtual const Sequence& indirectors() const = 0; }; // A targeted declarator is a species declarator with a trailing return type, // given by the `target()` operation. // An object of this type represents an instance of the second alternative of the C++ // grammar production for declarator: // noptr-declarator paramters-and-qualifiers trailing-return-type // In particular, the `species()` of a targeted declarator has a function Morphism as // last element in its `suffix()`. Furthermore, a targeted declarator lacks indirectors. // Examples: // f(T a, U b) -> decltype(a + b) struct Declarator::Targeted : Declarator { virtual const Type& target() const = 0; }; // Traversal of declarator objects is facilitated by visitor classes deriving // from this interface. export struct Declarator_visitor { virtual void visit(const Declarator::Term&) = 0; virtual void visit(const Declarator::Targeted&) = 0; }; // A term declarator requiring parentheses to obey operator precedence rules, or just // a redundant parentheses. An object of this type represents an instance of the fourth // alternative of the C++ grammar for noptr-declarator: // `(` ptr-declarator `)` // Example: // (a) // (*p) struct Species_declarator::Parenthesized : Species_declarator { virtual const Declarator::Term& term() const = 0; }; // -- Function Morphism // A morphism for a declarator indicating something that can be called. // An object of this type captures the components introduced by an instance the second // alternative of the C++ grammar of noptr-delarator: // noptr-declarator parameters-and-qualifiers struct Morphism::Function : Morphism { virtual const Parameter_list& parameters() const = 0; virtual Qualifiers qualifiers() const = 0; virtual Binding_mode binding_mode() const = 0; virtual Optional throws() const = 0; }; // -- Array Morphism // A morphism for a declarator indicating something that can be indexed. // An object of this type captures the components introduced by an instance the third // alternative of the C++ grammar of noptr-delarator: // noptr-declarator `[` constant-expression_opt `]` attribute-specifier-seq_opt struct Morphism::Array : Morphism { virtual Optional bound() const = 0; }; // Traversal of species objects is facilitated by visitor classes deriving // from this interface. export struct Species_visitor { virtual void visit(const Species_declarator::Unqualified_id&) = 0; virtual void visit(const Species_declarator::Pack&) = 0; virtual void visit(const Species_declarator::Qualified_id&) = 0; virtual void visit(const Species_declarator::Parenthesized&) = 0; }; // -- Proclamator. // A proclamator is a complete declarator along with an initializer, or with a constraint. // A proclamator represents an instance of the C++ grammar for init-declarator. Furtermore, // a proclamator holds the result of elaborating its declarator with a decl-specifier-seq that // preceds it in a declaration. // -- Initialization provision. // -- An initialization provision is an initializer form that stipulates how an entity // -- (the name of which is introduced by a declarator) shall be initialized. Choices range from // -- classic provision introduced by the `=` sign, to recent innovations such as parenthesized // -- expression list or brace enclosed initializer list for uniform initialization. export struct Provision_visitor; // Base class of initialization provision classes. They don't share much in common other than // appearing in top-level init-declarators. export struct Initialization_provision { virtual void accept(Provision_visitor&) const = 0; }; // Base for navigating elemental initializers. export struct Initializer_visitor; // Base class of initializer form (other than earmarked initializer) that can appear in a // brace-enclosed initilizer form. export struct Elemental_initializer { virtual void accept(Initializer_visitor&) const = 0; }; // Initialization provision of the form // `=` initializer-clause export struct Classic_provision : Initialization_provision { virtual const Elemental_initializer& initializer() const = 0; }; // Initialization provision of the form // `(` expression-list `)`; export struct Parenthesized_provision : Initialization_provision { virtual const Expr& initializer() const = 0; }; // Embedding of an expression in an elemental initializer hierarchy. // Note that the expression here can be either // expression // in general, or an // expression-list_opt export struct Expr_initializer : Elemental_initializer { virtual const Expr& expression() const = 0; }; // Initialization provision of the form // `{` initializer-list_opt `}` export struct Braced_provision : Initialization_provision, Elemental_initializer { virtual const Sequence& elements() const = 0; }; // An earmarked initializer is a form that specifies an initializer for a specific subobject // of a structure or an array, in a designated-initializer-list. Strictly speaking, ISO C++ // (unlike C99) currently supports only designated-initializer-list for structures, but not // arrays. But since the C99 designated-initialization for arrays is in practice accepted // by major compilers as extensions, and that extension fits the general model here, the form // data structures below make room for them. export struct Earmarked_initializer; // An initializer provision of the form // `{` designated-initializer-list_opt `}` export struct Designated_list_provision : Initialization_provision, Elemental_initializer { virtual const Sequence& elements() const = 0; }; // Base class for navigation of initialization provisions. export struct Provision_visitor { virtual void visit(const Classic_provision&) = 0; virtual void visit(const Parenthesized_provision&) = 0; virtual void visit(const Braced_provision&) = 0; virtual void visit(const Designated_list_provision&) = 0; }; // -- Designator. // A designator is a form that identifies an immediate subobject of a structure (by name), // or an array (by slot index). For example, in designated-initializer-list, the element // .galaxy { "Milky Way"} // provides a braced-initializer for the field named `galaxy`. Similarly the element // [4] = "Tau'ri" // provides a classic-initializer for the fifth slot of the array being initialized. export struct Designator_visitor; export struct Subobject_designator { virtual void accept(Designator_visitor&) const = 0; }; // Designator of a field. // Example: // . location export struct Field_designator : Subobject_designator { virtual const Identifier& name() const = 0; }; // Designator of an array slot. // Example: // [ 13 ] export struct Slot_designator : Subobject_designator { virtual const Expr& index() const = 0; }; export struct Designator_visitor { virtual void visit(const Field_designator&) = 0; virtual void visit(const Slot_designator&) = 0; }; // An earmarked initializer is an element of a designated-initializer-list that provides // an initialier for a direct subobject of a structure or an array. // Example: // .galaxy { "Milky Way" } // [4] = "Tau'ri" export struct Earmarked_initializer { virtual const Subobject_designator& subobject() const = 0; virtual const Initialization_provision& initializer() const = 0; }; // Base class for navigation of elemental initializers. export struct Initializer_visitor { virtual void visit(const Expr_initializer&) = 0; virtual void visit(const Braced_provision&) = 0; virtual void visit(const Designated_list_provision&) = 0; }; export struct Proclamator_visitor; export struct Proclamator { struct Initialized; // int ary[] { 1, 2, 3 }; struct Constrained; virtual const Declarator& declarator() const = 0; virtual Decl& result() const = 0; virtual void accept(Proclamator_visitor&) const = 0; }; struct Proclamator::Initialized : Proclamator { virtual Optional initializer() const = 0; }; struct Proclamator::Constrained : Proclamator { virtual const Expr& constraint() const = 0; }; export struct Proclamator_visitor { virtual void visit(const Proclamator::Initialized&) = 0; virtual void visit(const Proclamator::Constrained&) = 0; }; } ================================================ FILE: src/cxx-ipr-traversal.cxx ================================================ // // This file is part of The Pivot framework. // Written by Gabriel Dos Reis. // See LICENSE for copyright and license notices. // // Module implementation unit for cxx.ipr.traversal. // Contains out-of-line definitions: structurally_same template overloads // for Unary<>, Binary<>, Ternary<>, and Missing_overrider::operator(). module; #include #include module cxx.ipr.traversal; namespace ipr { template inline bool structurally_same(const Unary& lhs, const Unary& rhs) { return physically_same(lhs, rhs) or structurally_same(lhs.operand(), rhs.operand()); } template inline bool structurally_same(const Binary& lhs, const Binary& rhs) { return physically_same(lhs, rhs) or (structurally_same(lhs.first(), rhs.first()) and structurally_same(lhs.second(), rhs.second())); } template inline bool structurally_same(const Ternary& lhs, const Ternary& rhs) { return physically_same(lhs, rhs) or (structurally_same(lhs.first(), rhs.first()) and structurally_same(lhs.second(), rhs.second()) and structurally_same(lhs.third(), rhs.third())); } } void ipr::Missing_overrider::operator()(const ipr::Node& n) const { throw std::logic_error(std::string("missing overrider for ") + typeid(n).name()); } ================================================ FILE: src/cxx-ipr-traversal.ixx ================================================ // -*- C++ -*- // // This file is part of The Pivot framework. // Written by Gabriel Dos Reis. // See LICENSE for copyright and license notices. // // Module interface: cxx.ipr.traversal // Utility functions and visitor adapters for traversing the IPR node hierarchy. module; #include export module cxx.ipr.traversal; import cxx.ipr; namespace ipr { // This collection of routines implements structural equality // of Nodes. They are, for example, useful in determining // when two (type-) expressions are same, from structural // point of view in context like dependent types. export bool structurally_same(const Node&, const Node&); // -- builtin types // This predicate holds for representation of built types: they are // the fix points of the As_type functor. export inline bool denote_builtin_type(const As_type& t) { return physically_same(t, t.expr()); } // This visitor class applies the same function to all major nodes. // A typical example of use is to throw an exception or do nothing. export template struct Constant_visitor : Visitor, F { void visit(const Node& n) override { (*this)(n); } void visit(const Name& n) override { (*this)(n); } void visit(const Expr& n) override { (*this)(n); } void visit(const Type& n) override { (*this)(n); } void visit(const Directive& n) override { (*this)(n); } void visit(const Stmt& n) override { (*this)(n); } void visit(const Decl& n) override { (*this)(n); } }; // This function object class implement "no-op" semantics. Useful // with the above Visitor. export struct No_op { void operator()(const Node&) const { } }; // This function object type throw an exception indicating that a // Visitor::visit() is missing for a particular IPR node type. export struct Missing_overrider { void operator()(const Node&) const; }; namespace util { // This helper function returns a pointer to its argument, if that // node is from the category indicated by the template parameter. // This is a cheap, specialized version of dynamic cast. export template inline const T* view(const Node& n) { struct visitor : Constant_visitor { const T* result = nullptr; void visit(const T& n) final { result = &n; } }; visitor vis { }; n.accept(vis); return vis.result; } } } ================================================ FILE: src/cxx-ipr-vocabulary.ixx ================================================ // -*- C++ -*- // // This file is part of The Pivot framework. // Written by Gabriel Dos Reis. // See LICENSE for copyright and license notices. // // Module partition: cxx.ipr:vocabulary // Foundational types for the IPR interface: structural templates, // strong enums, source locations, sequences, and optionals. module; #include export module cxx.ipr:vocabulary; // -- Utility subset: only the symbols needed by the interface -- namespace ipr::util { // Predicate to detect enumeration types. export template concept EnumType = std::is_enum_v; // The underlying type of an enumeration export template using raw = std::underlying_type_t; // Return the value representation of an enumeration value. export template requires EnumType constexpr auto rep(T t) { return static_cast>(t); } // Type of view over words as stored internally. export using word_view = std::u8string_view; // -- Check for nonnull pointer. export template inline T* check(T* ptr) { if (ptr == nullptr) throw std::logic_error("attempt to dereference a null pointer"); return ptr; } } // -- Forward declarations for the entire node hierarchy -- // These are needed by ancillary types (Sequence, Optional, etc.) // and by the :syntax partition. namespace ipr { export struct Node; // universal base class for all IPR nodes export struct Visitor; // base class for all IPR visitor classes export struct Annotation; // node annotations export struct Region; // declarative region export struct Comment; // C-style and BCPL-style comments export struct String; // literal string export struct Language_linkage; // general language linkage export struct Expr; // general expressions export struct Name; // general names export struct Type; // general types export struct Directive; // general directives export struct Stmt; // general statements export struct Decl; // general declarations export struct Scope; // declarations in a region export struct Overload; // overload set export struct Parameter_list; // function/template parameter list // ------------------------------------------- // -- results of type constructor constants -- // ------------------------------------------- export struct Array; // array type export struct As_type; // use-expression as type export struct Class; // user-defined type - declared as "class" or "struct" export struct Decltype; // strict type of a declaration/expression export struct Enum; // user-defined type - declared as "enum" or "class enum" export struct Tor; // types of constructors and destructors export struct Function; // function type export struct Namespace; // user-defined type - declared as "namespace" export struct Pointer; // pointer type export struct Ptr_to_member; // pointer-to-member type export struct Product; // product type - not ISO C++ type export struct Qualified; // cv-qualified types export struct Reference; // reference type export struct Rvalue_reference; // rvalue-reference type export struct Sum; // sum type - not ISO C++ type export struct Forall; // universally quantified type export struct Union; // user-defined type - declared as "union" export struct Auto; // "auto" -- each occurrence is generative export struct Closure; // closure type -- type of lambda expression // ------------------------------------------ // -- results of name constructor constants -- // ------------------------------------------ export struct Identifier; // identifier foo export struct Suffix; // Literal operator suffix "Plato"sv export struct Operator; // C++ operator name operator+ export struct Conversion; // conversion function name operator int export struct Template_id; // C++ template-id S export struct Type_id; // C++ type-id const int* export struct Ctor_name; // constructor name T::T export struct Dtor_name; // destructor name T::~T export struct Guide_name; // deduction guide name // -------------------------------------------------------- // -- results of nullary expression constructor constants -- // -------------------------------------------------------- export struct Phantom; // placeholder for arrays of unknown bounds, // rethrow, empty parts of a For, etc... export struct Eclipsis; // the `...' in a unary fold export struct Lambda; // Lambda expression export struct Requires; // requires-expression // ------------------------------------------------------- // -- results of unary expression constructor constants -- // ------------------------------------------------------- export struct Symbol; // self-evaluating symbolic values export struct Address; // address-of &a export struct Array_delete; // array delete-expression delete[] p export struct Asm; // asm-declaration export struct Complement; // bitwise complement ~m export struct Delete; // delete-expression delete p export struct Demotion; // inverse of an integral/floating-point // promotion -- implicit conversion export struct Deref; // dereference expression *p export struct Expr_list; // comma-separated expression list export struct Alignof; // alignment query alignof(T) export struct Sizeof; // sizeof expression export struct Typeid; // typeid expression export struct Id_expr; // use of a name as an expression export struct Label; // a label - target of a goto-statement // or entry of a switch-statement export struct Materialization; // temporary materialization export struct Not; // logical negation !cond export struct Enclosure; // expression in paired delimiters export struct Post_decrement; // post-decrement p-- export struct Post_increment; // post-increment p++ export struct Pre_decrement; // pre-decrement --p export struct Pre_increment; // pre-increment ++p export struct Promotion; // integral or floating-point promotion export struct Read; // lvalue to rvalue conversion export struct Throw; // throw expression throw a export struct Unary_minus; // unary minus -a export struct Unary_plus; // unary plus +a export struct Expansion; // pack expansion t... export struct Noexcept; // noexcept expression noexcept(e) export struct Args_cardinality; // sizeof...(args) export struct Restriction; // requires-clause // -------------------------------------------------------- // -- results of binary expression constructor constants -- // -------------------------------------------------------- export struct Plus; // addition a + b export struct Plus_assign; // in-place addition a += b export struct And; // logical and a && b export struct Array_ref; // array member selection a[i] export struct Arrow; // indirect member selection p->m export struct Arrow_star; // indirect member indirection p->*m export struct Assign; // assignment a = b export struct Bitand; // bitwise and a & b export struct Bitand_assign; // in-place bitwise and a &= b export struct Bitor; // bitwise or a | b export struct Bitor_assign; // in-place bitwise or a |= b export struct Bitxor; // bitwise exclusive or a ^ b export struct Bitxor_assign; // in-place bitwise exclusive or a ^= b export struct Call; // function call f(u, v) export struct Cast; // C-style cast (T) e export struct Coercion; // generalized type conversion export struct Comma; // comma-operator a, b export struct Const_cast; // const_cast-expression export struct Construction; // object construction T(v) export struct Div; // division a / b export struct Div_assign; // in-place division a /= b export struct Dot; // direct member selection x.m export struct Dot_star; // direct member indirection x.*pm export struct Dynamic_cast; // dynamic_cast-expression export struct Equal; // equality comparison a == b export struct Greater; // greater comparison a > b export struct Greater_equal; // greater-or-equal comparison a >= b export struct Less; // less comparison a < b export struct Less_equal; // less-equal comparison a <= b export struct Literal; // literal expressions 3.14 export struct Lshift; // left shift a << b export struct Lshift_assign; // in-place left shift a <<= b export struct Member_init; // member initialization : m(v) export struct Minus; // subtraction a - b export struct Minus_assign; // in-place subtraction a -= b export struct Modulo; // modulo arithmetic a % b export struct Modulo_assign; // in-place modulo arithmetic a %= b export struct Mul; // multiplication a * b export struct Mul_assign; // in-place multiplication a *= b export struct Narrow; // checked base to derived conversion export struct Not_equal; // not-equality comparison a != b export struct Or; // logical or a || b export struct Pretend; // generalization of bitcast/reinterpret cast export struct Qualification; // cv-qualification conversion export struct Reinterpret_cast; // reinterpret_cast-expression export struct Rshift; // right shift a >> b export struct Rshift_assign; // in-place right shift a >>= b export struct Scope_ref; // qualified name N::f export struct Static_cast; // static_cast-expression export struct Widen; // derived to base class conversion export struct Binary_fold; // primary expression (a op ... op b) export struct Mapping; // function export struct Rewrite; // semantics by translation export struct Where; // expression with local bindings export struct Static_assert; // static-assert declaration export struct Instantiation; // substitution into parameterized expression // -------------------------------------------------------- // -- result of trinary expression constructor constants -- // -------------------------------------------------------- export struct New; // new-expression new (p) T(v) export struct Conditional; // conditional p ? a : b // ----------------------------------------------- // -- result of statement constructor constants -- // ----------------------------------------------- export struct Block; // brace-enclosed statement sequence export struct Break; // break-statement export struct Continue; // continue-statement export struct Ctor_body; // constructor-body export struct Do; // do-statement export struct Expr_stmt; // expression-statement export struct For; // for-statement export struct For_in; // structured for-statement export struct Goto; // goto-statement export struct Handler; // exception handler statement export struct If; // if-statement export struct Labeled_stmt; // labeled-statement export struct Return; // return-statement export struct Switch; // switch-statement export struct While; // while-statement // ----------------------------------------------- // -- result of directive constructor constants -- // ----------------------------------------------- export struct Specifiers_spread; // spread of decl-specifier-seq over a sequence of declarators export struct Structured_binding; // structured-binding declaration export struct Using_declaration; // using-declaration export struct Using_directive; // using-directive export struct Phased_evaluation; // evaluation at designated phases of translation export struct Pragma; // language-level pragma directive // ------------------------------------------------- // -- result of declaration constructor constants -- // ------------------------------------------------- export struct Template; // parameterized declaration export struct Enumerator; // classic enumerator export struct Alias; // alias declaration (typedef, namespace-alias, etc.) export struct Base_type; // base-specifier export struct Parameter; // parameter declaration export struct EH_parameter; // exception handler parameter export struct Fundecl; // function declaration export struct Concept; // concept declaration export struct Var; // variable declaration export struct Field; // nonstatic data member export struct Bitfield; // bit-field data member export struct Typedecl; // type declaration // ------------------------ // -- distinguished node -- // ------------------------ export struct Translation_unit; export struct Module_unit; export struct Interface_unit; export struct Module; export struct Lexicon; } // -- Various Location Types -- // C++ constructs span locations. There are at least four flavours of // locations: // (a) physical source location; // (b) logical source location; // (c) physical unit location; and // (d) logical unit location. // Physical and logical source locations are locations as witnessed // at translation phase 2 (see ISO C++, §2.1/1). // Physical and logical unit locations are locations as manifest when // looking at a translation unit, at translation phase 4. // // Many IPR nodes will have a source and unit locations. Instead of // storing a source file and unit name as a string in every // location data type, we save space by mapping file-names and // unit-names to IDs (integers). That mapping is managed by the Unit // instance. namespace ipr { export enum class Line_number : std::uint32_t { }; export enum class Column_number : std::uint32_t { }; export struct Basic_location { Line_number line = { }; Column_number column = { }; }; export enum class File_index : std::uint32_t { }; export struct Source_location : Basic_location { File_index file = { }; // ID of the file }; export enum class Unit_index : std::uint32_t { }; export struct Unit_location : Basic_location { Unit_index unit = { }; // ID of the unit }; } // -- Ancillary types -- namespace ipr { // --------------------------- // -- Phases of translation -- // --------------------------- // A bitmask type for the various phases of C++ program translation. export enum class Phases { Unknown = 0x0000, // Unknown translation phase Reading = 0x0001, // Opening and reading an input source Lexing = 0x0002, // Lexical decomposition of input source Preprocessing = 0x0004, // Macro expansion and friends Parsing = 0x0008, // Grammatical decomposition of the input source Name_resolution = 0x0010, // Name lookup Typing = 0x0020, // Type assignment of expressions Evaluation = 0x0040, // Compile-time evaluation Instantiation = 0x0080, // Template instantiation phase Code_generation = 0x0100, // Code generation phase Linking = 0x0200, // Linking phase Loading = 0x0400, // Program loading phase Execution = 0x0800, // Runtime execution Elaboration = Name_resolution | Typing | Evaluation | Instantiation, All = ~0x0, }; // -- Qualifiers -- // Abstract data type capturing compositions of standard and extended type qualifiers. // Composition of type qualifiers is commutative. export enum class Qualifiers : std::uintptr_t { }; // -- Binding_mode -- // Mode of binding of a object value to a name (parameter, variable, alias, etc). export enum class Binding_mode : std::uint8_t { Copy, // by copy operation; default binding mode of C and C++ Reference, // by ref; the parameter or varable has an lvalue reference type Move, // by move operation; transfer of ownership Default = Copy, }; // -- Delimiter -- // Enclosure delimiters of expressions export enum class Delimiter { Nothing, // no delimiter Paren, // "()" Brace, // "{}" Bracket, // "[]" Angle, // "<>" }; } // -- Vocabulary-level predicates -- namespace ipr { // Returns true if both operands share the same physical storage. export constexpr bool physically_same(const Node& lhs, const Node& rhs) { return &lhs == &rhs; } // Helper function, for implicit conversion Derived -> Base. // It lets view a node, from a more concrete node category (Derived), // as a member of more abstract node category (Base). export template inline const T& as(const U& u) { return u; } } // -- Structural base templates -- namespace ipr { // -- Basic_unary -- // A structure entirely determined by its sole component, the `operand()`. export template struct Basic_unary { using Arg_type = Operand; virtual Operand operand() const = 0; }; // -- Unary<> -- // A unary-expression is a specification of an operation that takes // only one operand, an expression. By extension, a unary-node is a // node category that is essentially determined only by one node, // its "operand". Usually, such an operand node is a classic expression. // Occasionally, it can be a type (e.g. sizeof (T)), we don't want to // loose that information, therefore we add a template-parameter // (the second) to indicate the precise type of the operand. The first // template-parameter designates the actual node subcategory this class // provides an interface for. export template struct Unary : Cat, Basic_unary { }; // -- Basic_binary -- // A structure entirely determined by its two components, the `first()` // and `second()`. export template struct Basic_binary { using Arg1_type = First; using Arg2_type = Second; virtual First first() const = 0; virtual Second second() const = 0; }; // -- Binary<> -- // In full generality, a binary-expression is an expression that // consists in (a) an operator, and (b) two operands. In Standard // C++, the two operands often are of the same type (and if they are not, // they are implicitly converted). In IPR, they need not be // of the same type. This generality allows representations of // cast-expressions which are conceptually binary-expressions -- they // take a type and an expression. Also, a function call is // conceptually a binary-expression that applies a function to // a list of arguments. By extension, a binary node is any node that // is essentially dermined by two nodes, its "operands". // As for Unary<> nodes, we indicate the operands' type information // through the template-parameters First and Second. export template struct Binary : Cat, Basic_binary { }; // -- Ternary<> -- // Similar to Unary<> and Binary<> categories. This is for // ternary-expressions, or more generally for ternary nodes. // An example of a ternary node is a Conditional node. export template struct Ternary : Cat { using Arg1_type = First; using Arg2_type = Second; using Arg3_type = Third; virtual First first() const = 0; virtual Second second() const = 0; virtual Third third() const = 0; }; } // -- Sequence and Optional -- namespace ipr { // -- Sequence<> -- // Often, we use a notion of sequence to represent intermediate // abstractions like base-classes, enumerators, catch-clauses, // parameter-type-list, etc. A "Sequence" is a collection abstractly // described by its "begin()" and "end()" iterators. It is made // abstract because it may admit different implementations depending // on concrete constraints. For example, a scope (a sequence of // declarations, that additionally supports look-up by name) may // implement this interface either as a associative-array that maps // Names to Declarations (with no particular order) or as // vector (in order of their appearance in programs), or // as a much more elaborated data structure. export template struct Sequence { struct Iterator; // An iterator is a pair of (sequence, // position). The position indicates // the particular value referenced in // the sequence. // Provide STL-style interface, for use with some STL algorithm // helper classes. Sequence<> is an immutable sequence. using value_type = T; using reference = const T&; using pointer = const T*; using Index = std::size_t; using iterator = Iterator; virtual Index size() const = 0; bool empty() const { return not (size() > 0); } Iterator begin() const; Iterator end() const; Iterator position(Index) const; protected: virtual const T& get(Index) const = 0; }; // -- Sequence<>::Iterator -- // This iterator class is as abstract as it could be, for useful // purposes. It forwards most operations to the "Sequence" class // it provides a view for. template struct Sequence::Iterator { using self_type = Sequence::Iterator; using value_type = const T; using reference = const T&; using pointer = const T*; using difference_type = ptrdiff_t; using Index = typename Sequence::Index; using iterator_category = std::bidirectional_iterator_tag; Iterator() {} Iterator(const Sequence* s, Index i) : seq{ s }, index{ i } { } const T& operator*() const { return seq->get(index); } const T* operator->() const { return &seq->get(index); } Iterator& operator++() { ++index; return *this; } Iterator& operator--() { --index; return *this; } Iterator operator++(int) { Iterator tmp = *this; ++index; return tmp; } Iterator operator--(int) { Iterator tmp = *this; --index; return tmp; } bool operator==(Iterator other) const { return seq == other.seq and index == other.index; } bool operator!=(Iterator other) const { return not(*this == other); } private: const Sequence* seq { }; Index index { }; }; template inline typename Sequence::Iterator Sequence::position(Index i) const { return { this, i }; } template inline typename Sequence::Iterator Sequence::begin() const { return { this, 0 }; } template inline typename Sequence::Iterator Sequence::end() const { return { this, size() }; } // -- Optional<> -- // Occasionally, a node has an optional property (e.g. a variable // has an optional initializer). This class template captures // that commonality and provides a checked access. export template struct Optional { constexpr Optional(const T* p = nullptr) : ptr{p} { } constexpr Optional(const T& t) requires std::is_abstract_v : ptr{&t} { } const T& get() const { return *util::check(ptr); } bool is_valid() const { return ptr != nullptr; } explicit operator bool() const { return is_valid(); } template> operator Optional() const { return { ptr }; } private: const T* ptr; }; } ================================================ FILE: src/cxx-ipr.cxx ================================================ // -*- C++ -*- // // This file is part of The Pivot framework. // Written by Gabriel Dos Reis. // See LICENSE for copyright and license notices. // // Module implementation unit for cxx.ipr. // Contains out-of-line definitions of functions declared in the module // interface whose definitions depend on implementation types from . // // TEMPORARY: This file exists because the implementation layer () // is not yet modularized. Once becomes a module, these definitions // should move to its module implementation unit. module; #include module cxx.ipr; // Minimal replica of impl::Node — just enough to define // String::empty_string(). The full is not included here to // avoid duplicate definitions with impl.cxx. // TEMPORARY: This workaround disappears when is modularized. namespace ipr::impl { template struct Node : T { using Interface = T; void accept(ipr::Visitor& v) const final { v.visit(*this); } }; } namespace ipr { const String& String::empty_string() { struct Empty_string final : impl::Node { constexpr util::word_view characters() const final { return u8""; } }; static constexpr Empty_string empty { }; return empty; } } // -- ipr::Visitor -- // Because Name is a very high-level interface to // Identifier, Operator, Conversion, Instantiation and // Qualified and these share common very high-level // semantics, it is convenient to have the implementation // of the corresponding Visitor::visit() functions forward to // Visitor::visit(const Name&). That way, code duplication // can be substantially reduced. The same goes for other // sub-hierarchies. void ipr::Visitor::visit(const Annotation& a) { visit(as(a)); } void ipr::Visitor::visit(const Region& r) { visit(as(r)); } void ipr::Visitor::visit(const Comment& c) { visit(as(c)); } void ipr::Visitor::visit(const String& s) { visit(as(s)); } void ipr::Visitor::visit(const Classic& e) { visit(as(e)); } void ipr::Visitor::visit(const Identifier& id) { visit(as(id)); } void ipr::Visitor::visit(const Suffix& s) { visit(as(s)); } void ipr::Visitor::visit(const Operator& op) { visit(as(op)); } void ipr::Visitor::visit(const Conversion& conv) { visit(as(conv)); } void ipr::Visitor::visit(const Template_id& e) { visit(as(e)); } void ipr::Visitor::visit(const Type_id& n) { visit(as(n)); } void ipr::Visitor::visit(const Ctor_name& n) { visit(as(n)); } void ipr::Visitor::visit(const Dtor_name& n) { visit(as(n)); } void ipr::Visitor::visit(const Guide_name& n) { visit(as(n)); } // -- Types visiting hooks -- void ipr::Visitor::visit(const Array& t) { visit(as(t)); } void ipr::Visitor::visit(const Class& t) { visit(as(t)); } void ipr::Visitor::visit(const Closure& t) { visit(as(t)); } void ipr::Visitor::visit(const Decltype& t) { visit(as(t)); } void ipr::Visitor::visit(const Enum& t) { visit(as(t)); } void ipr::Visitor::visit(const As_type& t) { visit(as(t)); } void ipr::Visitor::visit(const Tor& t) { visit(as(t)); } void ipr::Visitor::visit(const Function& t) { visit(as(t)); } void ipr::Visitor::visit(const Namespace& t) { visit(as(t)); } void ipr::Visitor::visit(const Pointer& t) { visit(as(t)); } void ipr::Visitor::visit(const Product& t) { visit(as(t)); } void ipr::Visitor::visit(const Ptr_to_member& t) { visit(as(t)); } void ipr::Visitor::visit(const Qualified& t) { visit(as(t)); } void ipr::Visitor::visit(const Reference& t) { visit(as(t)); } void ipr::Visitor::visit(const Rvalue_reference& t) { visit(as(t)); } void ipr::Visitor::visit(const Sum& t) { visit(as(t)); } void ipr::Visitor::visit(const Forall& t) { visit(as(t)); } void ipr::Visitor::visit(const Auto& t) { visit(as(t)); } void ipr::Visitor::visit(const Union& t) { visit(as(t)); } // -- Expressions visiting hooks -- void ipr::Visitor::visit(const Expr_list& e) { visit(as(e)); } void ipr::Visitor::visit(const Overload& e) { visit(as(e)); } void ipr::Visitor::visit(const Scope& e) { visit(as(e)); } void ipr::Visitor::visit(const Phantom& e) { visit(as(e)); } void ipr::Visitor::visit(const Eclipsis& e) { visit(as(e)); } void ipr::Visitor::visit(const Lambda& e) { visit(as(e)); } void ipr::Visitor::visit(const Requires& e) { visit(as(e)); } void ipr::Visitor::visit(const Symbol& e) { visit(as(e)); } void ipr::Visitor::visit(const Address& e) { visit(as(e)); } void ipr::Visitor::visit(const Array_delete& e) { visit(as(e)); } void ipr::Visitor::visit(const Asm& e) { visit(as(e)); } void ipr::Visitor::visit(const Complement& e) { visit(as(e)); } void ipr::Visitor::visit(const Delete& e) { visit(as(e)); } void ipr::Visitor::visit(const Demotion& e) { visit(as(e)); } void ipr::Visitor::visit(const Deref& e) { visit(as(e)); } void ipr::Visitor::visit(const Enclosure& e) { visit(as(e)); } void ipr::Visitor::visit(const Alignof& e) { visit(as(e)); } void ipr::Visitor::visit(const Sizeof& e) { visit(as(e)); } void ipr::Visitor::visit(const Args_cardinality& e) { visit(as(e)); } void ipr::Visitor::visit(const Restriction& e) { visit(as(e)); } void ipr::Visitor::visit(const Typeid& e) { visit(as(e)); } void ipr::Visitor::visit(const Id_expr& e) { visit(as(e)); } void ipr::Visitor::visit(const Label& e) { visit(as(e)); } void ipr::Visitor::visit(const Unary_minus& e) { visit(as(e)); } void ipr::Visitor::visit(const Materialization& e) { visit(as(e)); } void ipr::Visitor::visit(const Not& e) { visit(as(e)); } void ipr::Visitor::visit(const Post_decrement& e) { visit(as(e)); } void ipr::Visitor::visit(const Post_increment& e) { visit(as(e)); } void ipr::Visitor::visit(const Pre_decrement& e) { visit(as(e)); } void ipr::Visitor::visit(const Pre_increment& e) { visit(as(e)); } void ipr::Visitor::visit(const Promotion& e) { visit(as(e)); } void ipr::Visitor::visit(const Read& e) { visit(as(e)); } void ipr::Visitor::visit(const Throw& e) { visit(as(e)); } void ipr::Visitor::visit(const Unary_plus& e) { visit(as(e)); } void ipr::Visitor::visit(const Expansion& e) { visit(as(e)); } void ipr::Visitor::visit(const Noexcept& e) { visit(as(e)); } void ipr::Visitor::visit(const Rewrite& e) { visit(as(e)); } void ipr::Visitor::visit(const Scope_ref& n) { visit(as(n)); } void ipr::Visitor::visit(const Plus& e) { visit(as(e)); } void ipr::Visitor::visit(const Plus_assign& e) { visit(as(e)); } void ipr::Visitor::visit(const And& e) { visit(as(e)); } void ipr::Visitor::visit(const Array_ref& e) { visit(as(e)); } void ipr::Visitor::visit(const Arrow& e) { visit(as(e)); } void ipr::Visitor::visit(const Arrow_star& e) { visit(as(e)); } void ipr::Visitor::visit(const Assign& e) { visit(as(e)); } void ipr::Visitor::visit(const Bitand& e) { visit(as(e)); } void ipr::Visitor::visit(const Bitand_assign& e) { visit(as(e)); } void ipr::Visitor::visit(const Bitor& e) { visit(as(e)); } void ipr::Visitor::visit(const Bitor_assign& e) { visit(as(e)); } void ipr::Visitor::visit(const Bitxor& e) { visit(as(e)); } void ipr::Visitor::visit(const Bitxor_assign& e) { visit(as(e)); } void ipr::Visitor::visit(const Cast& e) { visit(as(e)); } void ipr::Visitor::visit(const Call& e) { visit(as(e)); } void ipr::Visitor::visit(const Coercion& e) { visit(as(e)); } void ipr::Visitor::visit(const Comma& e) { visit(as(e)); } void ipr::Visitor::visit(const Const_cast& e) { visit(as(e)); } void ipr::Visitor::visit(const Div& e) { visit(as(e)); } void ipr::Visitor::visit(const Div_assign& e) { visit(as(e)); } void ipr::Visitor::visit(const Dot& e) { visit(as(e)); } void ipr::Visitor::visit(const Dot_star& e) { visit(as(e)); } void ipr::Visitor::visit(const Dynamic_cast& e) { visit(as(e)); } void ipr::Visitor::visit(const Equal& e) { visit(as(e)); } void ipr::Visitor::visit(const Greater& e) { visit(as(e)); } void ipr::Visitor::visit(const Greater_equal& e) { visit(as(e)); } void ipr::Visitor::visit(const Less& e) { visit(as(e)); } void ipr::Visitor::visit(const Less_equal& e) { visit(as(e)); } void ipr::Visitor::visit(const Literal& e) { visit(as(e)); } void ipr::Visitor::visit(const Member_init& e) { visit(as(e)); } void ipr::Visitor::visit(const Modulo& e) { visit(as(e)); } void ipr::Visitor::visit(const Modulo_assign& e) { visit(as(e)); } void ipr::Visitor::visit(const Mul& e) { visit(as(e)); } void ipr::Visitor::visit(const Mul_assign& e) { visit(as(e)); } void ipr::Visitor::visit(const Narrow& e) { visit(as(e)); } void ipr::Visitor::visit(const Not_equal& e) { visit(as(e)); } void ipr::Visitor::visit(const Construction& e) { visit(as(e)); } void ipr::Visitor::visit(const Or& e) { visit(as(e)); } void ipr::Visitor::visit(const Pretend& e) { visit(as(e)); } void ipr::Visitor::visit(const Qualification& e) { visit(as(e)); } void ipr::Visitor::visit(const Reinterpret_cast& e) { visit(as(e)); } void ipr::Visitor::visit(const Lshift& e) { visit(as(e)); } void ipr::Visitor::visit(const Lshift_assign& e) { visit(as(e)); } void ipr::Visitor::visit(const Rshift& e) { visit(as(e)); } void ipr::Visitor::visit(const Rshift_assign& e) { visit(as(e)); } void ipr::Visitor::visit(const Static_cast& e) { visit(as(e)); } void ipr::Visitor::visit(const Widen& e) { visit(as(e)); } void ipr::Visitor::visit(const Minus& e) { visit(as(e)); } void ipr::Visitor::visit(const Minus_assign& e) { visit(as(e)); } void ipr::Visitor::visit(const Binary_fold& e) { visit(as(e)); } void ipr::Visitor::visit(const Where& e) { visit(as(e)); } void ipr::Visitor::visit(const Static_assert& e) { visit(as(e)); } void ipr::Visitor::visit(const Instantiation& e) { visit(as(e)); } void ipr::Visitor::visit(const Conditional& e) { visit(as(e)); } void ipr::Visitor::visit(const New& e) { visit(as(e)); } void ipr::Visitor::visit(const Mapping& s) { visit(as(s)); } // -- Directives visiting hooks -- void ipr::Visitor::visit(const Specifiers_spread& d) { visit(as(d)); } void ipr::Visitor::visit(const Structured_binding& d) { visit(as(d)); } void ipr::Visitor::visit(const Using_declaration& d) { visit(as(d)); } void ipr::Visitor::visit(const Using_directive& d) { visit(as(d)); } void ipr::Visitor::visit(const Phased_evaluation& d) { visit(as(d)); } void ipr::Visitor::visit(const Pragma& d) { visit(as(d)); } // -- Statements visiting hooks -- void ipr::Visitor::visit(const Labeled_stmt& s) { visit(as(s)); } void ipr::Visitor::visit(const Block& s) { visit(as(s)); } void ipr::Visitor::visit(const Ctor_body& e) { visit(as(e)); } void ipr::Visitor::visit(const Expr_stmt& s) { visit(as(s)); } void ipr::Visitor::visit(const If& s) { visit(as(s)); } void ipr::Visitor::visit(const Switch& s) { visit(as(s)); } void ipr::Visitor::visit(const While& s) { visit(as(s)); } void ipr::Visitor::visit(const Do& s) { visit(as(s)); } void ipr::Visitor::visit(const For& s) { visit(as(s)); } void ipr::Visitor::visit(const For_in& s) { visit(as(s)); } void ipr::Visitor::visit(const Break& s) { visit(as(s)); } void ipr::Visitor::visit(const Continue& s) { visit(as(s)); } void ipr::Visitor::visit(const Goto& s) { visit(as(s)); } void ipr::Visitor::visit(const Return& s) { visit(as(s)); } void ipr::Visitor::visit(const Handler& s) { visit(as(s)); } // Forward Visitor::visit() that operates on nodes // derived from Decl to whatever derived visitors do // for general declarations. void ipr::Visitor::visit(const Alias& d) { visit(as(d)); } void ipr::Visitor::visit(const Base_type& d) { visit(as(d)); } void ipr::Visitor::visit(const Bitfield& d) { visit(as(d)); } void ipr::Visitor::visit(const Enumerator& d) { visit(as(d)); } void ipr::Visitor::visit(const Field& d) { visit(as(d)); } void ipr::Visitor::visit(const Fundecl& d) { visit(as(d)); } void ipr::Visitor::visit(const Concept& d) { visit(as(d)); } void ipr::Visitor::visit(const Parameter& d) { visit(as(d)); } void ipr::Visitor::visit(const Parameter_list& l) { visit(as(l)); } void ipr::Visitor::visit(const Typedecl& d) { visit(as(d)); } void ipr::Visitor::visit(const Template& d) { visit(as(d)); } void ipr::Visitor::visit(const Var& d) { visit(as(d)); } void ipr::Visitor::visit(const EH_parameter& d) { visit(as(d)); } // -- Translation_unit::Visitor -- void ipr::Translation_unit::Visitor::visit(const Module_unit& u) { visit(as(u)); } void ipr::Translation_unit::Visitor::visit(const Interface_unit& u) { visit(as(u)); } ================================================ FILE: src/cxx-ipr.ixx ================================================ // -*- C++ -*- // // This file is part of The Pivot framework. // Written by Gabriel Dos Reis. // See LICENSE for copyright and license notices. // // Primary module interface: cxx.ipr // The semantic node hierarchy for the Internal Program Representation. module; #include export module cxx.ipr; export import :vocabulary; export import :syntax; namespace ipr { // IPR is designed to be regular, fully general enough to // (1) represent programs written in full Standard C++ // -- except macros -- Standard C, and possibly Fortran // and other C-like languages; // (2) be at the basis of semantics-based program analyses // and transformations; // (3) be ahead of time, i.e. accommodate future possible // extensions to C++, e.g. "concepts"; // (4) be compiler-neutral, i.e. not the reflection of internal // data structures of any particular compiler, no matter how popular. // // The "meta-language" implemented by IPR is expression-based. It // does not try to mimic the barnacles that the ISO C++ specification // has grown over decades -- that way lies unwarranted complexity. Rather, // the IPR aims for a regular, general semantics language within which the // various restrictions and irregularities of ISO C++ are accounted for. // The focus is on semantics because that is where the essence of C++ lies. // // IPR nodes "climbing" is based on the Visitor Design Pattern. // // IPR class nodes come in two flavors: // (a) the interface classes, found in this header; // (b) the implementation classes, found in . // The primary reason for this separation is, we don't want consumers of // the library to critically depend on the particular implementations // details of the moment. Also, it is our intent that the interfaces // could be implemented in various ways depending on the particular // constraints of the target tools. Furthermore, a given interface class // can admit several implementation classes. // // A third header file offers a set of traversal // facilities. At the moment, it contains only trivial visitor classes. // A fourth header, , contains interfaces to input/output // operations. At the moment, only output in form of XPR is supported. // FIXME: the XPR grammar is out-of-date and is no longer known to capture IPR. // // The interface classes are non-mutating, i.e. there is no way // a client program could modify a node through its interface. // In particular, all member functions of the interface classes // are const member functions. // // There is an additional requirement on IPR nodes: type nodes // shall have maximal sharing, i.e. be unified. The design choices // behind that decision are discussed in the paper "Representing // C++ Directly, Compactly and Efficiently." // // Warning: If you add any new "leaf" node type, you need to update // "ipr/node-category" for the numerical mapping. // -- Category_node -- // A list of numerical codes in one-to-one correspondence with // IPR node "interface types". In a sufficiently expressive and // efficient type system, we would not need to manually maintain // this list. This can be seen as an optimization of the traditional // double-dispatch process for determining the dynamic type of a node. export enum class Category_code { #include }; // -- General structural utility types and functions. // -- General node category class. export template struct Category : T { protected: constexpr Category() : T{ Cat } { } }; // Nesting level of a mapping export enum class Mapping_level : std::size_t { }; // Position of a declaration in its declarative region export enum class Decl_position : std::size_t { }; // A parameterization is an abstraction over an entity, charted by a collection of parameters. export template struct Parameterization { virtual const Parameter_list& parameters() const = 0; virtual const T& result() const = 0; }; // -- Logogram -- // A class of words (from a C++ input source) standing for themselves, with no particular elaboration. export struct Logogram : Basic_unary { const String& what() const { return operand(); } bool operator==(const Logogram& x) const { return &what() == &x.what(); } bool operator!=(const Logogram&) const = default; }; // -- Calling_convention -- // The ISO C++ standards does not explicitly acknowledge function calling conventions, as they // are considered low-level implementation details (beyond the fact that a language linkage is a thing). // However, in practice, those implementation details bubble up (leaky abstraction) into // the system system used by such implementations, in unpleasant ways. As illustration, just have // a look at the chaos introduced by language linkage in function types. // Note: language linkage is represented separately; see `Linkage`. // An object of this type provides a symbolic representation of a calling convention via an logogram // given by the `name()` operation. export struct Calling_convention { explicit constexpr Calling_convention(const Logogram& l) : conv{l} { } const Logogram& name() const { return conv; } bool operator==(const Calling_convention& x) const { return conv == x.conv; } bool operator!=(const Calling_convention&) const = default; private: const Logogram& conv; }; // -- Language_linkage -- // An object of this type represents language linkage, e.g., "C" in `extern "C"'. // The ISO standard mandates at least two language linkages: "C" and // "C++". If those were the only language linkages used in practice, // then, we would have just used a 2-valued enum. However, some C++ // implementations do have support for things like "Java" language // linkage and whatnot. Consequently, the most general representation // is adopted in the form of a logogram. From ISO C++ point of view, language // linkage applies only to function names, variable names, and function types. // Furthermore, a language linkage applied to a function type is a calling convention. export struct Language_linkage { explicit constexpr Language_linkage(const Logogram& l) : lang{l} { } const Logogram& language() const { return lang; } bool operator==(const Language_linkage& x) const { return lang == x.lang; } bool operator!=(const Language_linkage&) const = default; private: const Logogram& lang; }; // -- Transfer -- // A summary of characteristics of data or control tranfer. Both language linkage and // calling convention affect how data are transferred to subroutines in function calls. // Furthermore, language linkage also affects linking across translation units. export struct Transfer : Basic_binary { const Language_linkage& language_linkage() const { return first(); } const Calling_convention& convention() const { return second(); } bool operator==(const Transfer& t) const { return language_linkage() == t.language_linkage() and convention() == t.convention(); } bool operator!=(const Transfer&) const = default; }; // -- Basic_specifier -- // A symbolic semantic denotation of an instance of a the C++ grammar for decl-specifier // that is not a defining-type-specifier. This type is intended to capture the result of // semantic elaboration, not mirror of a syntactic construct. // Note: This symbolic form allows for extensions of decl-specifier beyond those explicitly // listed in the C++ standards. export struct Basic_specifier { constexpr Basic_specifier(const Logogram& l) : spec{&l} { } constexpr const Logogram& logogram() const { return *spec; } constexpr bool operator==(const Basic_specifier&) const = default; private: const Logogram* spec; }; // -- Specifiers -- // An algebraic denotation of sets of basic specifiers. // ISO C++ requires that decl-specifiers can appear in any order in the decl-specifier-seq // of a declaration in the input source program. Furthermore, any such decl-specifier // can appear at most once. Taken together, those requirements mean that a decl-specifier-seq // is in fact a set of basic specifiers. Therefore, the set of standard basic specifiers act // as a basis of the space of decl-specifier-seqs, and the coordinates or a decl-specifier-seq // are numbers drown from ZZ/2ZZ. The space of those coordinates is represented by `Specifiers`. // This space is kept abstract to allow extensions beyond the restricted set of speecifiers // listed in the ISO C++ standards. For example, from the IPR model perspective the ISO C++ // access control label `public`, `protected`, `private` are basic specifiers. export enum class Specifiers : std::uintptr_t { }; // -- Basic_qualifier -- // Symbolic semantic denotation of a type qualifier. Note that a type qualifier is a monadic // type constructor. The set of ISO C++ type qualifiers is { `const`, `volatile` }. In practice, // C++ implementations support more type qualifiers, including ISO C's `restrict`. The name // of the qualifier is indicated by the `logogram()` operation. export struct Basic_qualifier { constexpr Basic_qualifier(const Logogram& l) : qual{&l} { } constexpr const Logogram& logogram() const { return *qual; } constexpr bool operator==(const Basic_qualifier&) const = default; private: const Logogram* qual; }; // Algebraic operations on symbolic denotations expressed as bitwise operations (i.e. ZZ/2ZZ). export template constexpr T operator|(T a, T b) { return T{util::rep(a) | util::rep(b)}; } export template constexpr T& operator|=(T& a, T b) { return a = a | b; } export template constexpr T operator&(T a, T b) { return T{util::rep(a) & util::rep(b)}; } export template constexpr T& operator&=(T& a, T b) { return a = a & b; } export template constexpr T operator^(T a, T b) { return T{util::rep(a) ^ util::rep(b)}; } export template constexpr T& operator^=(T& a, T b) { return a = a ^ b; } export template constexpr bool implies(T a, T b) { return (a & b) == b; } // -- Lambda_specifiers -- // Declaration specifiers that can appear in a lambda expression. export enum class Lambda_specifiers : std::uint32_t { None = 0, Mutable = 1 << 0, // `mutable` lambda Constexpr = 1 << 1, // `constexpr` lambda Consteval = 1 << 2, // `consteval` lambda ExplicitObject = 1 << 3, // explicit object parameter `this`. }; // -- Module_name -- export struct Module_name { virtual const Sequence& stems() const = 0; }; // -- Node -- // Universal base class of all IPR nodes, in the traditional // OO design sense. Its primary purpose is to provide a hook // for the Visitor Design Pattern. export struct Node { // the category the complete node object belongs to. In a sufficiently // expressive and efficient type system, we would not need this member, // for it could be read directly from the type of the object. const Category_code category; // Hook for visitor classes. virtual void accept(Visitor&) const = 0; protected: // It is an error to create a complete object of this type. constexpr Node(Category_code c) : category{ c } { } // This class does not have a declared virtual destructor // because we don't plan to have Nodes manage resources, and // therefore no deletion through pointers to this base class. ~Node() = default; }; // -- String -- // Strings in IPR are immutable, and therefore unified. export struct String : Category { using iterator = util::word_view::const_iterator; using Index = std::size_t; virtual util::word_view characters() const = 0; bool operator==(const String& s) const { return this == &s; } bool operator!=(const String&) const = default; Index size() const { return characters().size(); } iterator begin() const { return characters().begin(); } iterator end() const { return characters().end(); } static const String& empty_string(); // dedicated node for an empty string }; // -- Comment -- // This node represents comments, either C-style or BCPL-style. Notice // that the comment delimiters are part of Comment::text. export struct Comment : Unary, const String&> { Arg_type text() const { return operand(); } }; // -- Annotation -- // A pair "(name, value)" used to communicate information // between external tools that use IPR. export struct Annotation : Binary, const String&, const Literal&> { Arg1_type name() const { return first(); } Arg2_type value() const { return second(); } }; // -- Region -- // A Region node represents a region of program text processed into a sequence // of expressions or directives. It is mostly useful for capturing the notion of // declarative region, and of scope (in Standard C++ sense). As such, a genrative // entity that can contain declarations has a region: that region is said to be // owned by that entity. On the other hand, not all regions are associated with // generative entities. For example, in // int phi(int (*f)(double x)); // the declarative region of the parameter scope of the function `phi` is owned by // the Decl for `phi`. However, the declarative region of the parameter scope of // the function parameter (of type 'point to function') is not owned by any entity, // not even the type `int (double)` since types are not generative (types are // unified in the IPR semantics graph!). // // In IPR, we're using a generalized notion of Scope (a sequence of // declarations). The notion of Region helps make precise when // some implicit actions like cleanup-ups happen, or nesting of scopes. // The sequence of (generalized) expressions appearing in a Region makes up the // (compile-time) body of that region. Evaluating that body usually produces the // bindings of that Region. export struct Region : Category { using Location_span = std::pair; virtual const Location_span& span() const = 0; virtual const Region& enclosing() const = 0; virtual Optional owner() const = 0; virtual const Sequence& body() const = 0; virtual const Scope& bindings() const = 0; virtual bool global() const = 0; // is this region the global region? }; // -- Expr -- // An expression is a sequence of operators and operands that specifies // a computation. Such a computation can be static (constant, // object, function, type, template, namespace, concept) or dynamic // (object, function). Every expression has a type. export struct Expr : Node { virtual const Type& type() const = 0; protected: constexpr Expr(Category_code c) : Node{ c } { } }; // -- Classic -- // Classic expressions are those constructed with operators // directly expressible in standard C++. Most of those operators // can be overloaded and given user-defined implementation. For // instance, cout << 2, involves a user-defined operator. The IPR // representation is not a function call but, something like // // << ---> implementation // / \. // cout 2 // // where we record the fact that the left-shift operator has a // user-defined meaning. In IPR, we take the general approach that // all operators can be given user-defined meanings. Consequently, // we define this class to represent the idea that an expression // can have a user-supplied meaning. // // IPR defines a language that is a superset of C++. The term classic // refers to the C++ language. export struct Classic : Expr { // For an operation that is given a user-supplied meaning, retrieve // the implementation. In non-templated context this returns a // user-supplied declaration. In templated contexts it might return // an overload set. virtual Optional implementation() const = 0; protected: constexpr Classic(Category_code c) : Expr{ c } { } }; // -- Name -- // Standard C++ says that 'a name is a use of identifier to designate // an entity'. In IPR we take the view that a name is a symbol // (or combination thereof) interpreted in their most abstract sense, // i.e. whose meaning depends on binding contexts (Scopes). // That definition accounts for the following (in the standard C++ sense) // - unqualified-id -- Identifier // - operator-function-id -- Operator // - conversion-function-id -- Conversion // - template-id -- Template_id // - type-id -- Type_id // // Most names are introduced by declarations into a Region. export struct Name : Node { // At the moment, this class is empty because there is no // interesting operation that could be provided here without // imposing too much of implementation details. protected: constexpr Name(Category_code c) : Node{ c } { } }; // -- Identifier -- // An identifier is a sequence of alphanumeric characters starting // with either a letter or an underbar ('_'). export struct Identifier : Unary, const String&> { // The character sequence of this identifier Arg_type string() const { return operand(); } }; // -- Suffix -- // The suffix of a user-defined literal is essential just an identifier // with a distinguished interpretation. export struct Suffix : Unary, const Identifier&> { Arg_type name() const { return operand(); } }; // -- Operator -- // For a function operator "operator @", "opname()" is the "@" // sub-part. Notice that for the array forms of allocation // and deallocation operator functions, this is respectively // "new[]" and "delete[]", with no space before, between, or after // the square brackets. export struct Operator : Unary, const String&> { Arg_type opname() const { return operand(); } }; // -- Conversion -- // A conversion-function-id is the name of a user-defined // conversion function. export struct Conversion : Unary, const Type&> { // The type this conversion-function converts values to. Arg_type target() const { return operand(); } }; // -- Template_id -- // A Template_id is a name of the form // template-expr // which 'applies' a template to a template-argument list. export struct Template_id : Binary, const Expr&, const Expr_list&> { Arg1_type template_name() const { return first(); } Arg2_type args() const { return second(); } }; // -- Ctor_name -- // Standard C++ is not very consistent about constructor // name. On one hand, it says constructors do not have names; // but on another hand, there are clearly cases where they // act as if they had name. For example, "S::S" designates // constructors of class S. Similarly, if class S has a template // constructor then explicit or partial specializations need ways // to refer to particular set of specializations. By consistency // with other declarations, and symmetry with destrcutors, we have // introduced this node class. export struct Ctor_name : Unary, const Type&> { Arg_type object_type() const { return operand(); } }; // -- Dtor_name -- // This node represent a destructor name of the form "~T", where T // is a type. export struct Dtor_name : Unary, const Type&> { Arg_type object_type() const { return operand(); } }; // -- Guide_name -- // This interface class represents the name of a deduction guide. // Deduction guides do not have names in Standard C++, and they cannot // be found by name lookup. Yet, they are entities (like templates) // that generate compiler-internal instructions (their initializers) // that when executed yield deduced template-arguments. As such, they // fit the IPR model of a declaration being an introduction of a name // in a scope, with a type and optional initializer. export struct Guide_name : Unary, const Template&> { Arg_type mapping_decl() const { return operand(); } }; // -- Type_id -- // This node is used for elaborated expressions that designate types. // For example, "const T*" is a Type_id , so is "int (T&)". export struct Type_id : Unary, const Type&> { Arg_type type_expr() const { return operand(); } }; // -- Overload -- // An overload-set is an expression whose value is the set of all // declarations for a name in a given scope. An overload-set supports // look-up by type. The result of such lookup is the canonical declaration // of all declarations with that given type, in that scope. export struct Overload : Category { virtual Optional operator[](const Type&) const = 0; }; // -- Scope -- // A "declaration" is a type specification for a name. A "Scope" is // a "sequence" of declarations, that additionally supports "lookup" // by "Name". A name may have more than one declarations in a given // scope; such a name is said "overloaded". The result of // looking up a name in a scope is a set of all declarations, called // an "overload set", for that name in that scope. An "overload set" // is a "sequence" of declarations, that additionally supports // lookup by "Type". export struct Scope : Category { using Iterator = Sequence::Iterator; // The sequence of declarations this scope contain. virtual const Sequence& elements() const = 0; // Look-up by name returns the overload-set of all declarations, // for the subscripting name, contained in this scope. virtual Optional operator[](const Name&) const = 0; // How may declarations are there in this Scope. auto size() const { return elements().size(); } Iterator begin() const { return elements().begin(); } Iterator end() const { return elements().end(); } }; // -- General types -- // A type is a collection of constraints and operations that preserve // some invariants. Since a Type is also an Expression, it has a type. // A type of a type is an instance of a concept - a constraint. A type // in IPR has a much broader significance than Standard C++ types // (henceforth called "classic type"). In particular, in IPR, "namespace" // is a type. Similarly, an overload-set has a type. // Some C++ implementations define "extended built-in" types with // language linkage, e.g. `extern "Java"' or `extern "Fortran"', for // interoperating with other languages. The current representation // includes language linkage as integral part of Type. // Almost all types have "C++" language linkage. The exceptions are: // a. As_type built with explicit language specification or more generally // data or control transfer // b. Function built with explicit language specification or more generally // data or control transfer export struct Type : Expr { virtual const Name& name() const = 0; virtual const Transfer& transfer() const = 0; protected: constexpr Type(Category_code c) : Expr{ c } { } }; // -- Array -- // An array-type describes object expressions that designate C-style // homogenous object containers that meet the random-access // requirements. When an array-type is declared with unspecified // bound, "bound()" returns a null-expression. // An alternate design choice would have been to have a predicate // "has_unknown_bound()", which when true would make "bound()" throw // an exception if accessed. export struct Array : Binary, const Type&> { Arg1_type element_type() const { return first(); } Arg2_type bound() const { return second(); } }; // -- As_type -- // This node represents the use of a general expression as // a type. Such situation arises in cases where a declaration // node can be used to designate a type, as in: // struct S; // typedef int count; // typename T::size_type s = 90; // template ... export struct As_type : Unary> { Arg_type expr() const { return operand(); } }; // -- Decltype -- // This node represents query for the "generalized declared type" // of an expression. export struct Decltype : Unary, const Expr&> { Arg_type expr() const { return operand(); } }; // -- Tor -- // A node of this class represents a Type for either a constructor or a destructor. // Formally, ISO C++ does not assign types to those special functions; however, a // type-based representation of C++ semantics must assign types to such declarations. // Although a low-level (e.g. ABI-level) representation of a contructor and a destructor // may look at them as just some regular function, and assign them a Function type, // the higher level semantics of C++ is best served by a dedicated type to abstract // over ABI interpretation. export struct Tor : Binary, const Expr&, const Type&> { // The parameter-list to a tor type. Always empty for a Standard C++ destructor. Arg1_type source() const { return first(); } // The exception-specification for this tor type. Arg2_type throws() const { return second(); } }; // -- Function -- // This node class represents a Type that describes an expression // that refers to a function. In full generality, a template // is a also a function (it describes Type-valued functions); // however, we've made a special node for template. ISO C++ specifies // that function types have language linkages and two function types // with different language linkages are different. // Furthermore, low-level implementation details such as calling conventions // are accounted for through `Type::transfer()`. export struct Function : Ternary, const Product&, const Type&, const Expr&> { // Parameter-type-list of a function of this type. In full // generality, this also describes template signature. Arg1_type source() const { return first(); } // return-type Arg2_type target() const { return second(); } // Either a boolean expression indicating when values of this type can throw, // or a list of exception types a function of this type may throw. Arg3_type throws() const { return third(); } }; // -- Pointer -- // A pointer-type is type that describes an Address node. export struct Pointer : Unary, const Type&> { // The type of the entity whose address an object of this // type may hold. Arg_type points_to() const { return operand(); } }; // -- Product -- // A Product represents a Cartesian product of Types. Pragmatically, // it may be viewed as a Sequence of Types. It is a Type. export struct Product : Unary, const Sequence&> { using Index = std::size_t; Arg_type elements() const { return operand(); } auto size() const { return elements().size(); } const Type& operator[](Index i) const { return *elements().position(i); } }; // -- Ptr_to_member -- // This is for pointer-to-member type, e.g. int A::* or void (A::*)(). // A pointer to member really is not a pointer type, it is much closer // a pair of a type and offset that usual pointer types. export struct Ptr_to_member : Binary, const Type&, const Type&> { Arg1_type containing_type() const { return first(); } Arg2_type member_type() const { return second(); } }; // -- Qualified -- // A cv-qualified type. Representing a cv-qualified type with all // available information attainable in at most one indirection is // very tricky. Consequently, we currently represents a cv-qualified // type with a separate node as a binary operator. Notice that we // maintain the invariant // Qualified(cv2, Qualified(cv1, T)) = Qualified(cv1 | cv2, T) // In particular, the Qualified::main_variant is never a Qualified node. // We also maintain the invariant that Qualified::qualifiers is never // Type::None, consequently it is an error to attempt to create such a node. export struct Qualified : Binary, ipr::Qualifiers, const Type&> { Arg1_type qualifiers() const { return first(); } Arg2_type main_variant() const { return second(); } }; // -- Reference -- // A reference-type describes an expression that acts like an alias // for a object or function. However, unlike a pointer-type, it is // not an object-type. export struct Reference : Unary, const Type&> { // The type of the object or function an expression of this // type refers to. Arg_type refers_to() const { return operand(); } }; // -- Rvalue_reference -- // An rvalue-reference-type to support move semantics. export struct Rvalue_reference : Unary, const Type&> { // The type of the object or function an expression of this // type refers to. Arg_type refers_to() const { return operand(); } }; // -- Sum -- // A Sum type represents a distinct union of types. This is currently // used only for dynamic exception specification in Function type. export struct Sum : Unary, const Sequence&> { using Index = std::size_t; Arg_type elements() const { return operand(); } auto size() const { return elements().size(); } const Type& operator[](Index i) const { return *elements().position(i); } }; // -- Forall -- // This represents a universally quantified type, parameterized by any compile-time sort; // that is, all values for the parameters must designate compile-time entities. // It is useful for representing the type of a template declaration, and more. In the near future, // when "concepts" are integrated, it will become a Ternary node where the // third operand will represent the "where-clause". export struct Forall : Binary, const Product&, const Type&> { // The constraints or types of the template-parameters. const Product& source() const { return first(); } // The type of the instantiation result. const Type& target() const { return second(); } }; // -- Udt -- // Base class for user-defined types Nodes -- factor out common properties. export template struct Udt : Type { // The general interface to members of this user-defined type. using Member = T; // The region delimited by the definition of this Udt. virtual const Region& region() const = 0; const Scope& scope() const { return region().bindings(); } virtual const Sequence& members() const = 0; protected: // It is an error to create a node of this type. constexpr Udt(Category_code c) : Type{ c } { } }; // -- Namespace -- // A Standard C++ namespace is a compile-time accumulative singleton variable // that is initialized with a sequence of declarations. The type of // such a variable is given by an IPR "namespace" type. export struct Namespace : Category> { const Sequence& members() const final { return scope().elements(); } }; // -- Class -- export struct Class : Category> { const Sequence& members() const final { return scope().elements(); } virtual const Sequence& bases() const = 0; }; // -- Union -- export struct Union : Category> { const Sequence& members() const final { return scope().elements(); } }; // -- Enum -- // An enumeration is an object-type whose members are named constants // the definitions of which as part of the definition of the enumeration // itself. By historical accident, enumerators are not "properly scoped". // The underlying type of the enumeration is given by `base()`, when explicitly // specified or inferred from the enumerator list. export struct Enum : Category> { enum class Kind : std::uint8_t { // The kind of enum. Legacy, // traditional C-style enum Scoped // scoped enum (C++11) }; virtual Kind kind() const = 0; virtual Optional base() const = 0; }; // -- Auto -- export struct Auto : Category { }; // A capture is a description of how a local entity is referenced in a lambda expression. // It is essentially a pair (Binding_mode, Decl) where the first component tells how the // entity is referenced (by value or by reference), and a declaration that specifies the // declarative semantic properties of the captured entity. // Note: A Capture should not be confused with a Capture_specification: the specification // is a prescription of how an entity should be captured (either explicitly or implicitly) // in a lambda, whereas a capture is a result of executing that prescription. export struct Capture { virtual Binding_mode mode() const = 0; virtual const Decl& entity() const = 0; }; // -- Closure -- // A closure type is the type of a lambda expression, which ISO C++ confusingly requires // to be a non-union class type. The logical representation in the IPR has it as a distinct // (generative) user-defined type whose members are captures. Each lambda expression has // its own unique type, even if its members would be otherwise the same. export struct Closure : Category> { }; // -- Phantom -- // This nodes represents a missing expression, as in the "bound" of an array type with // unknown bound, e.g. in "unsigned char charset[];", or in an empty expression-statement. // We do not unify Phantom expressions, as two arrays with // unknown bounds may not designate the same type. export struct Phantom : Category { }; // -- Eclipsis -- // Representation of the `...' in a unary fold, e.g. `... + e'. // Note: that `...' is not the pack expansion operator. There is no operand to the `...' here. // While the syntax looks the same, the meaning is really different. It stands for something // was omitted, hence the name. export struct Eclipsis : Category { }; // Semantic description of the variety of lambda capture specifications: // - default captures: indicated by the squiggles "=" (by value), and "&" (by reference) at source level // - implicit object: indicated by "this" (by reference), and "*this" (by value) at source level // - named symbols: local enclosing variable, and locally introduced variable (with initializer) at source level export struct Capture_specification { struct Default; // default lambda capture specification struct Implicit_object; // capture mode of implicit object struct Named; // named simple capture; base of Simple and With_initializer struct Enclosing_local; // simply named captured local entity struct Binding; // capture with initialization struct Expansion; // expansion of a lambda capture struct Visitor; virtual void accept(Visitor&) const = 0; }; // -- Lambda -- export struct Lambda : Category, Parameterization { virtual const Closure& type() const override = 0; // a lambda is of a (unique) closure type virtual Optional target() const = 0; // return type, optionally specified at source level virtual Optional requirement() const = 0; // declaration constraint possibly involving parameters virtual const Sequence& attributes() const = 0; // optional attribute-list applying to the lambda virtual Optional eh_specification() const = 0; virtual Lambda_specifiers specifiers() const = 0; virtual const Sequence& captures() const = 0; }; // Default lambda capture specification struct Capture_specification::Default : Capture_specification { virtual Binding_mode mode() const = 0; }; // Lambda capture specification of the implicit object in a non-static member context. // The designation of the capture is defined by how(), where: // - Binding_mode::Reference means 'this' // - Binding_mode::Copy means '*this' // - anything else is invalid at this point. // Note: an alternative design is to introduced a distinct enum for representing the // designation of the implicit object (say 'This' and 'Self), but that would // have just obscured the fundamental semantics correspondence. struct Capture_specification::Implicit_object : Capture_specification { virtual Binding_mode how() const = 0; }; // Base of lambda captures designated by an given name. struct Capture_specification::Named : Capture_specification { virtual const Identifier& name() const = 0; virtual Binding_mode mode() const = 0; }; // Lambda capture referencing a local entity from the enclosing context. struct Capture_specification::Enclosing_local : Capture_specification::Named { virtual const Decl& declaration() const = 0; }; // Lambda capture introducing an alias bound to the result of evaluating // an expression in the (local) context of the lambda body. struct Capture_specification::Binding : Capture_specification::Named { virtual const Expr& initializer() const = 0; }; // Lambda capture expansion of either an enclosing variable pack, or a local binding pack. struct Capture_specification::Expansion : Capture_specification { virtual const Named& what() const = 0; }; struct Capture_specification::Visitor { virtual void visit(const Default&) = 0; virtual void visit(const Implicit_object&) = 0; virtual void visit(const Enclosing_local&) = 0; virtual void visit(const Binding&) = 0; virtual void visit(const Expansion&) = 0; }; // -- Symbol -- // Representation of self-evaluating (generalized) expressions, i.e. irreducible expressions. // That includes builtin types, as well as designated special values such as `nullptr'. // Note: A symbol is uniquely identified by the pair (name, type), so a symbol can be `overloaded'. // Symbol nodes are unified, as they are self-evaluating, e.g. core values. export struct Symbol : Unary, const Name&> { const Name& name() const { return operand(); } }; // -- Address -- // Address-of expression -- "&expr" export struct Address : Unary> { }; // -- Array_delete -- // Array-form of delete-expression -- "delete[] p" export struct Array_delete : Unary> { const Expr& storage() const { return operand(); } }; // -- Asm -- // An ISO C++ asm-declaration, an expression in IPR. // When evaluated, this expression produces executable instructions or // affects linking behavior. Its type is `void`. // Note: A node of this type is typically an operand of `Phased_evaluation`. export struct Asm : Unary, const String&> { const String& text() const { return operand(); } }; // -- Complement -- // Complement-expression -- "~expr" export struct Complement : Unary> { }; // -- Delete -- // Delete-expression -- "delete p" export struct Delete : Unary> { const Expr& storage() const { return operand(); } }; // -- Demotion -- // Integral or floating point conversion export struct Demotion : Unary> { }; // -- Deref -- // Dereference-expression -- "*expr" export struct Deref : Unary> { }; // -- Enclosure -- // Expresion enclosed in matching pair of delimiters. This might seem purely // syntactical but it also has semantic implications, like when an // argument-dependent lookup should be done or not, or order of evaluation,the accuracy // of an expression evaluation. // Note: an empty brace-init translates to a brace enclosure of a Phantom node. export struct Enclosure : Unary> { virtual Delimiter delimiters() const = 0; const Expr& expr() const { return operand(); } }; // -- Expr_list -- // A sequence of expressions "e1, e2, ..., eN". This form of expression // is a tuple of expressions. In particular, an Expr_list is different // from a Comma expression -- where each sub-expression is evaluated, // discarded except the last one. The type of an Expr_list // is a Product. export struct Expr_list : Unary, const Sequence&> { using Index = std::size_t; Arg_type elements() const { return operand(); } auto size() const { return elements().size(); } }; // -- Alignof -- // Alignment query of a type, or any expression. export struct Alignof : Unary> { }; // -- Sizeof -- // sizeof-expression -- "sizeof expr" or "sizeof (int)" export struct Sizeof : Unary> { }; // -- Args_cardinality -- export struct Args_cardinality : Unary> { }; // -- Restriction -- // Representation of what ISO C++ calls "requires-clause", an expression of the form // `requires` constant-expression // at the input source level. Not to be confusion with "requires-expression". export struct Restriction : Unary> { }; // -- Noexcept -- export struct Noexcept : Unary> { }; // -- Typeid -- // typeid-expression -- "typeid (expr)", or "typeid (int)" export struct Typeid : Unary> { }; // -- Id_expr -- // This node represents use of a name to designate an entity. // In general, names are introduced by a declaration. In a well-formed // fully elaborated program, a use of name refers to exactly one declaration. // However, in a partially elaborated program (e.g. a template definition) // a dependent name may refer to zero, one, or many (overloaded) declarations. export struct Id_expr : Unary, const Name&> { // The set of declarations the name refers to, at the elaboration point. virtual Optional resolution() const = 0; Arg_type name() const { return operand(); } }; // -- Label -- // label-expression. Appears in goto-statements. export struct Label : Unary, const Identifier&> { Arg_type name() const { return operand(); } }; // -- Materialization -- // Temporary materialization export struct Materialization : Unary> { }; // -- Not -- // logical-not-expression -- "!expr" export struct Not : Unary> { }; // -- Post_decrement -- // post-decrement-expression -- "expr--". export struct Post_decrement : Unary> { }; // -- Post_increment -- // post-increment-expression -- "expr++". export struct Post_increment : Unary> { }; // -- Pre_decrement -- // pre-decrement-expression -- "--expr". export struct Pre_decrement : Unary> { }; // -- Pre_increment -- // pre-increment-expression -- "++expr". export struct Pre_increment : Unary> { }; // -- Promotion -- // Integral or floating point promotion -- "(int)'2'" export struct Promotion : Unary> { }; // -- Construction -- // An expression of the form "T(e1, e2, .. en)" or "T{e1, e2, .. en}" // where "T" is a type and "ei"s are expressions. This is not a function // call -- although syntactically it looks like so. "T" will // be the `type()' of this expression. // The contructor selected for the construction operation is indicated // by implementation, which is inherited from Classic. When the enclosure // is a singleton, the node can also denote a functional cast expression. export struct Construction : Unary, const Enclosure&> { // The sequence of arguments used for this construction Arg_type arguments() const { return operand(); } }; // -- Read -- // Lvalue-to-rvalue conversion -- "= var" export struct Read : Unary> { }; // -- Throw -- // A node that represents a C++ expression of the form `throw ex'. // As a special case, when exception() is a null-expression, then // this node represents the flow-control "throw;", which actually // could be represented by a dedicated statement node, Rethrow for // instance. export struct Throw : Unary> { const Expr& exception() const { return operand(); } }; // -- Unary_minus -- // unary-minus-expression -- "-expr" export struct Unary_minus : Unary> { }; // -- Unary_plus -- // unary-plus-expression -- "+expr" export struct Unary_plus : Unary> { }; // -- Expansion -- // Pack expansion. The operand can be both a classic expression // or a type. Standard C++ does not permit overloading of expansion // operator, but the general IPR model here allows it. export struct Expansion : Unary> { }; // -- Rewrite -- // Various ISO C++ syntactic constructs are defined - unwwisely - // via translation to more elaborate sequences of tokens or parse trees. // The `source()` is the construct as written in the input source code, and the // `target()` is the internal expression giving meaning to the source. export struct Rewrite : Binary> { const Expr& source() const { return first(); } const Expr& target() const { return second(); } const Type& type() const final { return second().type(); } }; // -- Member_selection<> -- // This class factorizes the commonalities of various object member // selection operation. export template struct Member_selection : Binary> { const Expr& base() const { return this->first(); } const Expr& member() const { return this->second(); } }; // -- Cast_expr<> -- // This classes factorizes the commonalities of various cast-expressions export template struct Cast_expr : Binary, const Type&> { // The type() of a cast expression is its first() operand. However, // we do not define (override) that member here, as it implies two // indirections to get the information. Therefore, it is better // to define it at the implementation side where we have all the bits // necessary to make it a single indirection. const Expr& expr() const { return this->second(); } }; // -- Scope_ref -- // A qualified name of the form "scope::member". // Although the scope resolution operator ('::') in this expression // cannot be overloaded in standard C++ (yet), it is conceptually // dual to the dot operator, for which overloading has been // suggested multiple time with an operational model. Consequently, // a qualified name is modelled as a classic expression. export struct Scope_ref : Binary> { Arg1_type scope() const { return first(); } Arg2_type member() const { return second(); } }; // -- Plus -- // Addition-expression -- "a + b" export struct Plus : Binary> { }; // -- Plus_assign -- // In-place addition-expression -- "a += b" export struct Plus_assign : Binary> { }; // -- And -- // Logical-and-expression -- "a && b" export struct And : Binary> { }; // -- Array_ref -- // This is for an expression that designate a particular slot // in an array expression `array[slot]'. export struct Array_ref : Member_selection { }; // -- Arrow -- // This node type models object member selection, based on pointer, // using the arrow-notation. See `Dot'. export struct Arrow : Member_selection { }; // -- Arrow_star -- // Member selection through pointer to member -- "p->*" export struct Arrow_star : Member_selection { }; // -- Assign -- // Assignment-expression -- "a = b" export struct Assign : Binary> { }; // -- Bitand -- // Bit-and-expression -- "a & b" export struct Bitand : Binary> { }; // -- Bitand_assign -- // In-place bit-and-expression -- "a &= b" export struct Bitand_assign : Binary> { }; // -- Bitor -- // Bit-or expression -- "a | b" export struct Bitor : Binary> { }; // -- Bitor_assign -- // In-place bit-or-expression -- "a |= b" export struct Bitor_assign : Binary> { }; // -- Bitxor -- // Exclusive bit-or-expression -- "a ^ b" export struct Bitxor : Binary> { }; // -- Bitxor_assign -- // In-place exclusive bit-or-expression -- "a ^= b" export struct Bitxor_assign : Binary> { }; // -- Cast -- // An expression of the form "(type) expr", representing the // old-style/C-style cast. export struct Cast : Cast_expr { }; // -- Call -- // A function call with an argument list. Notice that in the abstract // this is not really different from an Template_id. However, having // a separate node means less clutter in codes. export struct Call : Binary, const Expr&, const Expr_list&> { Arg1_type function() const { return first(); } Arg2_type args() const { return second(); } }; // -- Coercion -- // A generalized type conversion either built-in to the language or // programmatically defined by either constructors or conversion // functions. export struct Coercion : Binary, const Expr&, const Type&> { Arg1_type expr() const { return first(); } Arg2_type target() const { return second(); } }; // -- Comma -- // comma-expression -- "a, b" export struct Comma : Binary> { }; // -- Const_cast -- // const_cast-expression -- "const_cast(expr)". export struct Const_cast : Cast_expr { }; // -- Div -- // Division-expression -- "a / b" export struct Div : Binary> { }; // -- Div_assign -- // In-place division-expression -- "a /= b" export struct Div_assign : Binary> { }; // -- Dot -- // This node type represents a member selection on object using // the dot-notation "object.member": both "object" and "member" // can be general expressions. export struct Dot : Member_selection { }; // -- Dot_star -- // An expression of the form "object.*pm". export struct Dot_star : Member_selection { }; // -- Dynamic_cast -- // An expression of the from "dynamic_cast(expr)". export struct Dynamic_cast : Cast_expr { }; // -- Equal -- // Equality-comparison-expression -- "a == b" export struct Equal : Binary> { }; // -- Greater -- // greater-comparison-expression -- "a > b" export struct Greater : Binary> { }; // -- Greater_equal -- // greater-or-equal-comparison-expression -- "a >= b" export struct Greater_equal : Binary> { }; // -- Less -- // less-comparison-expression -- "a < b" export struct Less : Binary> { }; // -- Less_equal -- // less-equal-comparison-expression -- "a <= b" export struct Less_equal : Binary> { }; // -- Literal -- // An IPR literal is just like a standard C++ literal. export struct Literal : Binary, const Type&, const String&> { // See comments for the cast operators regarding type(). // The textual representation of this literal as it appears // in the program text. Arg2_type string() const { return second(); } }; // -- Binary_fold -- // Representation of a binary fold expression, e.g. `(x + ... + y)'. // The first() operand designates `x', and the second() operand designates `y'. // The operation() is the category code for the operation being folded. export struct Binary_fold : Binary> { virtual Category_code operation() const = 0; }; // -- Mapping -- // This node represents a parameterized expression. // Its type is a Function in case of parameterized classic expression, // and Forall otherwise. export struct Mapping : Category, Parameterization { }; // -- Member_init -- // Node that represent a member initialization, in constructor // definition. export struct Member_init : Binary> { Arg1_type member() const { return first(); } Arg2_type initializer() const { return second(); } }; // -- Minus -- // Subtraction-expression -- "a - b" export struct Minus : Binary> { }; // -- Minus_assign -- // In-place subtraction-expression -- "a -= b". export struct Minus_assign : Binary> { }; // -- Modulo -- // Modulo-expression -- "a % b" export struct Modulo : Binary> { }; // -- Modulo_assign -- // In-place modulo-expression -- "a %= b" export struct Modulo_assign : Binary> { }; // -- Mul -- // Multiplication-expression -- "a * b" export struct Mul : Binary> { }; // -- Mul_assign -- // In-place multiplication-expression -- "a *= b" export struct Mul_assign : Binary> { }; // -- Narrow -- // Checked base to derived class conversion. This is always a non-implicit // conversion and exists to add additional semantic context to otherwise // explicit conversion nodes. export struct Narrow : Binary, const Expr&, const Type&> { Arg1_type expr() const { return first(); } Arg2_type derived() const { return second(); } }; // -- Not_equal -- // Inequality-comparison-expression -- "a != b" export struct Not_equal : Binary> { }; // -- Or -- // logical-or-expression -- "a || b" export struct Or : Binary> { }; // -- Pretend -- // Generalization of bitcast and reinterpret cast export struct Pretend : Binary, const Expr&, const Type&> { Arg1_type expr() const { return first(); } Arg2_type target() const { return second(); } }; // -- Qualification -- // A conversion that add cv-qualifiers export struct Qualification : Binary, const Expr&, Qualifiers> { Arg1_type expr() const { return first(); } Arg2_type qualifiers() const { return second(); } }; // -- Reinterpret_cast -- // An expression of the form "reinterpret_cast(expr)" export struct Reinterpret_cast : Cast_expr { }; // -- Lshift -- // left-shift-expression -- "a << b" export struct Lshift : Binary> { }; // -- Lshift_assign -- // In-place left-shift-expression -- "a <<= b". export struct Lshift_assign : Binary> { }; // -- Rshift -- // Right-shift-expression -- "a >> b" export struct Rshift : Binary> { }; // -- Rshift_assign -- // In-place right-shift-expression -- "a >>= b" export struct Rshift_assign : Binary> { }; // -- Static_cast -- // An expression of the form "static_cast(expr)". export struct Static_cast : Cast_expr { }; // -- Widen -- // Derived to base class conversion export struct Widen : Binary, const Expr&, const Type&> { Arg1_type expr() const { return first(); } Arg2_type base() const { return second(); } }; // -- Where -- // An expression referencing declarations or restrictions, local to that expression. // The expressions or declarations in `attendant()` are evaluated first, and then // `main()` is evaluated in the dynamic evaluation cone established by `attendant()`. // As usual, variables declared by bindings in `attendant()` are destructed in reverse // order of declarations at the end of their scopes, which occur after the complete // evaluation of `main()`. // Note: An `attendant()` of type `Scope` indicates that the where-expression introduces // local bindings. export struct Where : Binary> { const Expr& main() const { return first(); } const Expr& attendant() const { return second(); } const Type& type() const final { return main().type(); } }; // -- Static_assert -- // A static assertion: failure of the `condition()` halts the elaboration phase. // No runtime code is ever generated. Its type is `bool`. // Note: A node of this type is typically an operand of `Phased_evaluation`. export struct Static_assert : Binary, const Expr&, Optional> { const Expr& condition() const { return first(); } virtual Optional message() const { return second(); } }; // -- Substitution -- // A "Substitution" is a mapping of a parameter to an expression (its value). export struct Substitution { virtual const Expr& operator[](const Parameter&) const = 0; }; // -- Instantiation -- // An instantiation is a computation that substitutes parameters into an expression. // While an implicit specialization of a template is a notorious example of an instantiation, // just about any expression can be instantiated. For example, a hidden friend function // defined in a class template is typically instantiated, even though it is not itself a // function template; the branches of a `constexpr if` are instantiated. Consequently, // an Instantiarion is different from a Template_id (a Name). // The result of an instantiation may be cached on the side, or directly stored in this node. export struct Instantiation : Category { virtual const Expr& pattern() const = 0; virtual const Substitution& substitution() const = 0; virtual Optional instance() const = 0; const Type& type() const final { return instance().get().type(); } }; // -- Requires -- // Representation of a requires-expression. Despite the presence of `parameters()`, this expression // is not a parameterization of expressions in the conventional sense. In particular the type of // a requires-expression is never a mapping in any sense. It is always `bool`. export struct Requires : Category { virtual const Parameter_list& parameters() const = 0; virtual const Sequence& body() const = 0; }; // -- New -- // This node represents a new-expression: // ::_opt new new-placement_opt new-type-id new-initializer_opt // ::_opt new new-placement_opt ( type-id ) new-initializer_opt // Semantically, this has a binary structure where the first operand // is the placement-list, and the second operand is a construction expression // that indicates the new-type-id or the type-id coupled with the new-initializer if any. // When the new-initializer is missing, the Enclosure of the Contruction has no delimiter // and the arguments of the Enclosure is a Phantom node. // The allocator function is indicated by the implementation(). export struct New : Binary, Optional, const Construction&> { // This predicate holds if the new-expression explicitly requested a global allocator. virtual bool global_requested() const = 0; Optional placement() const { return first(); } const Construction& initializer() const { return second(); } }; // -- Conditional -- // This represents the C++ ternary operator ?:. In principle, it is redundant // with both If and Switch nodes from semantics point of view. However, // it is necessary for representing templated code export struct Conditional : Ternary> { Arg1_type condition() const { return first(); } Arg2_type then_expr() const { return second(); } Arg3_type else_expr() const { return third(); } }; // -- Parameter_list -- // The sequence of parameter declarations to an abstraction (mapping). A parameter-list // has a declarative region that covers the entirety of the abstraction it is associated with. // While a parameter-list node is essentially a container, viewing like an expression allows // an operation such as `type()`, yielding a `Product`, that can be useful in constructing the type // of the mapping being parameterized. export struct Parameter_list : Category { virtual const Region& region() const = 0; virtual Mapping_level level() const = 0; const Product& type() const override = 0; virtual const Sequence& elements() const = 0; auto begin() const { return elements().begin(); } auto end() const { return elements().end(); } auto size() const { return elements().size(); } }; // -- Directive -- // Standard C++ has grammar hacks where certain constructs are // classified as declarations, by they don't actually declare any name. // Rather, the modify the behavior of the implementation in certain ways. // For example, a using-declaration does not declare a name: it changes // the usual name lookup rules to consider new scopes. A static-assert // does not declare anything: it halts translation when a certain condition // does not hold; otherwise a no-op. // These constructs are generally known as directives. They were made // declarations in Standard C++, so that they could appear at non-local // scopes -- where general declarations live. export struct Directive : Expr { virtual Phases phases() const = 0; protected: constexpr Directive(Category_code c) : Expr{ c } { } }; // -- Directive -- // Certain expressions are evaluated at designated phased of translation of a C++ program. // For example, static_assert-declaration (a declaration in ISO C++, but an expression // in IPR) is evaluated at compile-time. On the other hand, an asm-declaration (again a // declaration in ISO C++, but an expression in the IPR) is generally evaluated at // code-generation time. export struct Phased_evaluation : Category { const Type& type() const final { return expression().type(); } virtual const Expr& expression() const = 0; }; // -- Specifiers_spread -- // A directive to spread a decl-specifier-seq over a collection of init-declarators. // - The init-declarators are given by the `targets()` operation. // - The type component of the decl-specifier-seq is given by `type()`. // - The non-type component of the decl-specifier-seq is given by `specifiers()`. export struct Specifiers_spread : Category { virtual Specifiers specifiers() const = 0; virtual const Sequence& targets() const = 0; }; // -- Structured_binding -- // A structured binding is a directive to the compiler to introduce a set of names // (either an Alias or a Var) in the current binding environment by taking apart // the object value designated by the initializer. export struct Structured_binding : Category { virtual Specifiers specifiers() const = 0; virtual Binding_mode mode() const = 0; virtual const Sequence& names() const = 0; virtual const Expr& initializer() const = 0; virtual const Sequence& bindings() const = 0; }; // -- Using_declaration -- // A using-declaration is directive that instructs the C++ translator to look up // a set of names ('designator'), and for each declaration in the qualified lookup // result of a designator create a synonymous declaration, subject to constraints. export struct Using_declaration : Category { struct Designator; virtual const Sequence& designators() const = 0; }; // A using-declarator: a qualified name, along with a denomination mode. struct Using_declaration::Designator { enum class Mode { Normal = 0x00, // Normal interpretation of qualified name lookup Type = 0x01, // The qualified name is assumed to name a type Expansion = 0x02, // The designator needs expansion }; Designator(const Scope_ref& p, Mode m) : sr{&p}, md{m} { } const Scope_ref& path() const { return *sr; } Mode mode() const { return md; } private: const Scope_ref* sr; Mode md; }; // -- Using_directive -- // A standard C++ using-directive or a using-enum-declaration // (nominating additional scopes) for elaboration purposes. // Its type should be "namespace" if it nominates a namespace, // or "enum" or "enum class" if it nominates an enumeration. export struct Using_directive : Category { virtual const Scope& nominated_scope() const = 0; }; // -- Pragma -- export struct Pragma : Unary, const Sequence&> { const Sequence& incantation() const { return operand(); } }; // -- Stmt -- // The view that a statement is kind of expression does not follow from // Standard C++ specification. It is a design choice we made based on // the following facts: // 1. We need to have an IPR node that can represent both a // declaration and an expression. That need is exemplified // by constructs like // if (std::cin >> c) { /* .... */ } // and // if (Call* c = dynamic_cast(e)) { /* ... */ } // In the first case we have an expression and in the second // case we have a declaration. Therefore, we have a diagram // that loooks like // // ,----> E.D. <----, // | | // Expression Declaration // // // 2. Standard C++ says some declarations are statements, and when // a declaration declares a variable with initialization, the // initializer usually requests code execution. Therefore, we // chose to say that a declaration is a statement and we get // a similar diagram // // ,----> D.S. <----, // | | // Declaration Statement // // 3. In general, IPR is an expression-based language. // // // When combined, the two diagrams yield // // ,----> E.D. <---- ----> D.S <----, // | | | // Expression Declaration Statement // // // Somehow, we have to satisfy that equation. In a previous design // we chose what seemed to be the simplest, i.e. deriving a Statement // from an Expr and a Declaration from a Statement. Doing so // however led to numerous questions about some very basic operations on // expressions like `arity()' or `type()'. // // In a second design, we derived a Statement from Node and a // Decl from both Expr and Statement. This led to complications in the // class hierarchy and unnecessary complexities. Therefore, we're back to // the view that a statement is an expression, with some simplifications. export struct Stmt : Expr { // The location of this statement in its unit. virtual const Unit_location& unit_location() const = 0; virtual const Source_location& source_location() const = 0; virtual const Sequence& annotation() const = 0; virtual const Sequence& attributes() const = 0; protected: constexpr Stmt(Category_code c) : Expr{ c } { } }; // -- Expr_stmt -- // This node class represents an expression statement, e.g. // std::cout << "Hello World" << std::endl; // "expr()" is the Expression to evaluate. // The type of the entire statement is the type of the expresion. export struct Expr_stmt : Unary> { const Type& type() const final { return expr().type(); } Arg_type expr() const { return operand(); } }; // -- Labeled_stmt -- // This node is for labeled-statement: // a. identifier : statement // b. case constant-expression : statement // c. default : statement // IPR represents that `label() as an expression so that it can // handle "identifier", "case cst-expr" and "default", where cst-expr // is an arbitrary constant-expression. // In scenario (a), the `label()` is a Symbol whose `name()` is the identifier. // The Symbol's `type()` is `void`. // In scenario (b), the `label()` is represented directly by the constant-expression. // In scenario (c), the `label()` is should be `Lexicon::default_value()`. export struct Labeled_stmt : Binary, const Expr&, const Expr&> { const Type& type() const final { return stmt().type(); } const Expr& label() const { return first(); } const Expr& stmt() const { return second(); } }; // -- Block -- // A Block is any sequence of general expressions bracketed by curly braces. // A block may additionally have exception handlers, in which case it corresponds // to Standard C++ syntax heavy try-block. Conventional, plain blocks carry // no handlers. export struct Block : Category { virtual const Region& region() const = 0; const Sequence& body() const { return region().body(); } virtual const Sequence& handlers() const = 0; bool plain_block() const { return handlers().size() == 0; } }; // -- Ctor_body -- // This node represents a constructor "body" consisting of: // (a) member-initializer list, if present; and // (b) the actual body of the constructor export struct Ctor_body : Binary, const Expr_list&, const Block&> { Arg1_type inits() const { return first(); } Arg2_type block() const { return second(); } }; // -- If // A classic if-statement. export struct If : Ternary, const Expr&, const Expr&, Optional> { Arg1_type condition() const { return first(); } Arg2_type consequence() const { return second(); } Arg3_type alternative() const { return third(); } }; // -- Switch -- // A classic switch-statement. export struct Switch : Binary, const Expr&, const Expr&> { Arg1_type condition() const { return first(); } Arg2_type body() const { return second(); } }; // -- While -- // A classic while-statement export struct While : Binary, const Expr&, const Expr&> { Arg1_type condition() const { return first(); } Arg2_type body() const { return second(); } }; // -- Do -- // A classic do-statement export struct Do : Binary, const Expr&, const Expr&> { Arg1_type condition() const { return first(); } Arg2_type body() const { return second(); } }; // -- For -- // A classic for-statement export struct For : Category { virtual const Expr& initializer() const = 0; virtual const Expr& condition() const = 0; virtual const Expr& increment() const = 0; virtual const Expr& body() const = 0; }; // -- For_in -- export struct For_in : Category { virtual const Var& variable() const = 0; virtual const Expr& sequence() const = 0; virtual const Expr& body() const = 0; }; // -- Break -- // A classic break-statement. // It has type `void`. export struct Break : Category { virtual const Stmt& from() const = 0; }; // -- Continue -- // A classic continue-statement. // It has type `void`. export struct Continue : Category { virtual const Stmt& iteration() const = 0; }; // -- Goto -- // A classic goto-statement. // The type of the entire statement is the `type()` of the operand. export struct Goto : Unary> { const Type& type() const final { return target().type(); } Arg_type target() const { return operand(); } }; // -- Return -- // A classic return-statement. export struct Return : Unary> { Arg_type value() const { return operand(); } }; // -- Handler -- // This represents a catch-clause. Notice that there is no node // for "try" as every Block is implicitly a try-block. Ideally, we // should have every expression as a "try-block". export struct Handler : Category { virtual const EH_parameter& exception() const = 0; virtual const Block& body() const = 0; }; // -- Decl -- // Only declaration-statements are statements from Standard C++ perspective. // However, we find it simpler just to take the general rule that a declaration // is a statement. Furthermore, it should be observed that declarations like // (a) function-definition // (b) template-declaration // (c) explicit-instantiation // (d) explicit-specialization // (e) linkage-specification // (f) namespace-definition // are not statement in Standard C++ sense. // // We take the view that all declarations can be parameterized. // Therefore, declaration nodes potentially are instantiations of a // a template given by "pattern()", with a set of template-arguments, // given by "substitutions()". A declaration for which we have // an empty substitutions() is either // (a) a primary template; or // (b) a non-template declaration. export struct Decl : Stmt { virtual Specifiers specifiers() const = 0; virtual const Language_linkage& language_linkage() const = 0; virtual const Name& name() const = 0; // The region where the declaration really belongs to. virtual const Region& home_region() const = 0; // The region where this declaration appears -- purely lexical. // For many declarations, this is the same as the home region. // Exceptions are invisible friends, member functions defined // outside of their enclosing class or namespaces. virtual const Region& lexical_region() const = 0; virtual Optional initializer() const = 0; // This is the first seen declaration for name() in a given // translation unit. The master declaration is therefore the // first element of the declaration-set. virtual const Decl& master() const = 0; virtual const Sequence& decl_set() const = 0; protected: constexpr Decl(Category_code c) : Stmt{ c } { } }; // -- Template -- // This represents a parameterized declaration. If its // template-parameter list is empty, that means that it is // either an explicit specialization -- if result() has a // non-empty substitutions() -- or a really non-template // declaration -- if result().substitutions() is empty. // The list of specializations of this template (either // partial or explicit) is given by specializations(). export struct Template : Category { virtual const Template& primary_template() const = 0; virtual const Sequence& specializations() const = 0; virtual const Mapping& mapping() const = 0; const Parameter_list& parameters() const { return mapping().parameters(); } const Expr& result() const { return mapping().result(); } virtual Optional