pax_global_header00006660000000000000000000000064146471204770014526gustar00rootroot0000000000000052 comment=f33ca743fb105bf54293cc822f56cf2794d15bf2 toml11-4.1.0/000077500000000000000000000000001464712047700126455ustar00rootroot00000000000000toml11-4.1.0/.clusterfuzzlite/000077500000000000000000000000001464712047700162015ustar00rootroot00000000000000toml11-4.1.0/.clusterfuzzlite/Dockerfile000066400000000000000000000003041464712047700201700ustar00rootroot00000000000000FROM gcr.io/oss-fuzz-base/base-builder RUN apt-get update && apt-get install -y make autoconf automake libtool COPY . $SRC/toml11 COPY .clusterfuzzlite/build.sh $SRC/build.sh WORKDIR $SRC/toml11 toml11-4.1.0/.clusterfuzzlite/README.md000066400000000000000000000001761464712047700174640ustar00rootroot00000000000000# ClusterFuzzLite set up This folder contains a fuzzing set for [ClusterFuzzLite](https://google.github.io/clusterfuzzlite). toml11-4.1.0/.clusterfuzzlite/build.sh000066400000000000000000000002711464712047700176340ustar00rootroot00000000000000#!/bin/bash -eu # Copy fuzzer executable to $OUT/ $CXX $CFLAGS $LIB_FUZZING_ENGINE \ $SRC/toml11/.clusterfuzzlite/parse_fuzzer.cpp \ -o $OUT/parse_fuzzer \ -I$SRC/toml11/include/ toml11-4.1.0/.clusterfuzzlite/parse_fuzzer.cpp000066400000000000000000000003561464712047700214300ustar00rootroot00000000000000#include #include extern "C" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) { std::string s(reinterpret_cast(data), size); const auto ref = toml::try_parse_str(s); return 0; } toml11-4.1.0/.clusterfuzzlite/project.yaml000066400000000000000000000000161464712047700205300ustar00rootroot00000000000000language: c++ toml11-4.1.0/.editorconfig000066400000000000000000000002021464712047700153140ustar00rootroot00000000000000root = true [*] indent_style = space [CMakeLists.txt] indent_size = 4 [*.{c,h}*] indent_size = 4 [*.{md,yml}] indent_size = 2 toml11-4.1.0/.github/000077500000000000000000000000001464712047700142055ustar00rootroot00000000000000toml11-4.1.0/.github/workflows/000077500000000000000000000000001464712047700162425ustar00rootroot00000000000000toml11-4.1.0/.github/workflows/document.yml000066400000000000000000000013541464712047700206060ustar00rootroot00000000000000name: document on: push: branches: - main jobs: deploy: runs-on: Ubuntu-22.04 steps: - name: Checkout uses: actions/checkout@v4 with: submodules: true - name: Install Hugo uses: peaceiris/actions-hugo@v3 with: hugo-version: 'latest' extended: true - name: Build Webpage working-directory: ./docs/ run: | hugo --minify - name: Deploy uses: peaceiris/actions-gh-pages@v4 with: github_token: ${{ secrets.GITHUB_TOKEN }} publish_branch: gh-pages publish_dir: ./docs/public force_orphan: true commit_message: ${{ github.event.head_commit.message }} toml11-4.1.0/.github/workflows/fuzzing.yml000066400000000000000000000014101464712047700204550ustar00rootroot00000000000000name: ClusterFuzzLite fuzzing on: [push, pull_request] permissions: read-all jobs: fuzzing: runs-on: ubuntu-22.04 strategy: fail-fast: false matrix: sanitizer: [address] steps: - name: Build Fuzzers (${{ matrix.sanitizer }}) id: build uses: google/clusterfuzzlite/actions/build_fuzzers@v1 with: sanitizer: ${{ matrix.sanitizer }} language: c++ bad-build-check: false - name: Run Fuzzers (${{ matrix.sanitizer }}) id: run uses: google/clusterfuzzlite/actions/run_fuzzers@v1 with: github-token: ${{ secrets.GITHUB_TOKEN }} fuzz-seconds: 240 mode: 'code-change' report-unreproducible-crashes: false sanitizer: ${{ matrix.sanitizer }} toml11-4.1.0/.github/workflows/main.yml000066400000000000000000000147551464712047700177250ustar00rootroot00000000000000name: build on: [push, pull_request] jobs: build-linux-gcc: runs-on: Ubuntu-22.04 strategy: matrix: compiler: ['g++-12', 'g++-11', 'g++-10', 'g++-9'] standard: ['11', '14', '17', '20'] precompile: ['ON', 'OFF'] steps: - name: Get number of CPU cores uses: SimenB/github-actions-cpu-cores@v2 id: cpu-cores - name: Checkout uses: actions/checkout@v4 with: submodules: true - name: Install run: | sudo apt-get update sudo apt-get install language-pack-fr # test serializer w/ locale sudo apt-get install ${{ matrix.compiler }} - name: Configure run: | cmake -B build/ -DCMAKE_CXX_COMPILER=${{ matrix.compiler }} -DCMAKE_CXX_STANDARD=${{ matrix.standard }} -DTOML11_BUILD_TESTS=ON -DTOML11_PRECOMPILE=${{ matrix.precompile }} - name: Build run: | cmake --build build/ -j${{ steps.cpu-cores.outputs.count }} - name: Test run: | ctest --output-on-failure --test-dir build/ build-linux-clang: runs-on: Ubuntu-22.04 strategy: matrix: compiler: ['15', '14', '13', '12', '11'] standard: ['11', '14', '17', '20'] precompile: ['ON', 'OFF'] exclude: - {compiler: '14', standard: '20'} # to avoid using gcc-13 libstdc++ - {compiler: '13', standard: '20'} # with older clang - {compiler: '12', standard: '20'} - {compiler: '11', standard: '20'} steps: - name: Checkout uses: actions/checkout@v4 with: submodules: true - name: Install run: | sudo apt-get update sudo apt-get install language-pack-fr # test serializer w/ locale sudo apt-get install clang-${{ matrix.compiler }} - name: Configure run: | cmake -B build/ -DCMAKE_CXX_COMPILER=clang++-${{ matrix.compiler }} -DCMAKE_CXX_STANDARD=${{ matrix.standard }} -DTOML11_BUILD_TESTS=ON -DTOML11_PRECOMPILE=${{ matrix.precompile }} - name: Build run: | cmake --build build/ -j${{ steps.cpu-cores.outputs.count }} - name: Test run: | ctest --output-on-failure --test-dir build/ build-linux-old-gcc: runs-on: Ubuntu-20.04 strategy: matrix: compiler: ['g++-8', 'g++-7'] standard: ['11', '14', '17', '20'] precompile: ['ON', 'OFF'] exclude: - {compiler: 'g++-7', standard: '20'} - {compiler: 'g++-8', standard: '17'} - {compiler: 'g++-8', standard: '20'} steps: - name: Checkout uses: actions/checkout@v4 with: submodules: true - name: Install run: | sudo apt-get update sudo apt-get install language-pack-fr # test serializer w/ locale sudo apt-get install ${{ matrix.compiler }} - name: Configure run: | cmake -B build/ -DCMAKE_CXX_COMPILER=${{ matrix.compiler }} -DCMAKE_CXX_STANDARD=${{ matrix.standard }} -DTOML11_BUILD_TESTS=ON -DTOML11_PRECOMPILE=${{ matrix.precompile }} - name: Build run: | cmake --build build/ -j${{ steps.cpu-cores.outputs.count }} - name: Test run: | ctest --output-on-failure --test-dir build/ build-linux-old-clang: runs-on: Ubuntu-20.04 strategy: matrix: compiler: ['10', '9', '8', '7', '6.0'] standard: ['11', '14', '17', '20'] precompile: ['ON', 'OFF'] exclude: - {compiler: '6.0', standard: '20'} - {compiler: '7', standard: '20'} - {compiler: '8', standard: '20'} - {compiler: '9', standard: '20'} steps: - name: Checkout uses: actions/checkout@v4 with: submodules: true - name: Install run: | sudo apt-get update sudo apt-get install language-pack-fr # test serializer w/ locale sudo apt-get install clang-${{ matrix.compiler }} - name: Configure run: | cmake -B build/ -DCMAKE_CXX_COMPILER=clang++-${{ matrix.compiler }} -DCMAKE_CXX_STANDARD=${{ matrix.standard }} -DTOML11_BUILD_TESTS=ON -DTOML11_PRECOMPILE=${{ matrix.precompile }} - name: Build run: | cmake --build build/ -j${{ steps.cpu-cores.outputs.count }} - name: Test run: | ctest --output-on-failure --test-dir build/ build-osx-13: runs-on: macos-13 strategy: matrix: standard: ['11', '14', '17', '20'] precompile: ['ON', 'OFF'] steps: - name: Checkout uses: actions/checkout@v4 with: submodules: true - name: Configure run: | cmake -B build/ -DCMAKE_CXX_STANDARD=${{ matrix.standard }} -DTOML11_BUILD_TESTS=ON -DTOML11_PRECOMPILE=${{ matrix.precompile }} - name: Build run: | cmake --build build/ -j${{ steps.cpu-cores.outputs.count }} - name: Test run: | ctest --output-on-failure --test-dir build/ build-osx-12: runs-on: macos-12 strategy: matrix: standard: ['11', '14', '17', '20'] precompile: ['ON', 'OFF'] steps: - name: Checkout uses: actions/checkout@v4 with: submodules: true - name: Configure run: | cmake -B build/ -DCMAKE_CXX_STANDARD=${{ matrix.standard }} -DTOML11_BUILD_TESTS=ON -DTOML11_PRECOMPILE=${{ matrix.precompile }} - name: Build run: | cmake --build build/ -j${{ steps.cpu-cores.outputs.count }} - name: Test run: | ctest --output-on-failure --test-dir build/ build-windows-msvc: runs-on: windows-2022 strategy: matrix: standard: ['11', '14', '17', '20'] config: ['Release', 'Debug'] precompile: ['ON', 'OFF'] steps: - name: Checkout uses: actions/checkout@v4 with: submodules: true - uses: ilammy/msvc-dev-cmd@v1 - name: Configure shell: cmd run: | cmake -B build/ -G "NMake Makefiles" -DTOML11_BUILD_TESTS=ON -DCMAKE_CXX_STANDARD=${{ matrix.standard }} -DTOML11_PRECOMPILE=${{ matrix.precompile }} - name: Build run: | cmake --build ./build --config "${{ matrix.config }}" -j${{ steps.cpu-cores.outputs.count }} - name: Test run: | ctest --build-config "${{ matrix.config }}" --test-dir build/ --output-on-failure toml11-4.1.0/.github/workflows/single-include.yml000066400000000000000000000017401464712047700216710ustar00rootroot00000000000000name: gen-singlue-include on: push: branches: - main jobs: generate: runs-on: Ubuntu-22.04 steps: - name: Checkout uses: actions/checkout@v4 - name: Build run: | g++-12 -std=c++20 -O2 tools/expand/main.cpp -o expand ./expand include/toml.hpp > single_include.hpp - name: Check diff id: check-diff continue-on-error: true run: | diff single_include.hpp single_include/toml.hpp - name: Commit and Push if: steps.check-diff.outcome == 'failure' run: | mv single_include.hpp single_include/toml.hpp git config --global user.name "ToruNiina" git config --global user.email "ToruNiina@users.noreply.github.com" git add single_include/toml.hpp git commit -m "feat [skip ci]: update single_include" git push origin main env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} toml11-4.1.0/.github/workflows/toml-test.yml000066400000000000000000000022661464712047700207230ustar00rootroot00000000000000name: toml-test on: [push, pull_request] jobs: toml-test: runs-on: Ubuntu-22.04 strategy: matrix: compiler: ['g++-12'] standard: ['11', '14', '17', '20'] steps: - name: Checkout uses: actions/checkout@v4 with: submodules: true - name: Install run: | sudo apt update sudo apt install -y ${{ matrix.compiler }} - name: Setup Go uses: actions/setup-go@v5 - name: Configure run: | cmake -B build/ -DBUILD_TESTING=OFF -DCMAKE_CXX_COMPILER=${{ matrix.compiler }} -DCMAKE_CXX_STANDARD=${{ matrix.standard }} -DTOML11_BUILD_TOML_TESTS=ON - name: Build run: | cmake --build build/ - name: Test run: | go install github.com/toml-lang/toml-test/cmd/toml-test@latest toml-test ./build/tests/toml11_decoder # skipped tests does not conform the latest commit in toml-lang/toml toml-test -toml 1.1.0 ./build/tests/toml11_decoder_v1_1_0 -skip invalid/control/comment-del,invalid/control/comment-lf,invalid/control/comment-us toml-test -encoder ./build/tests/toml11_encoder toml11-4.1.0/.gitignore000066400000000000000000000000611464712047700146320ustar00rootroot00000000000000build/* .cache/* .clangd/* compile_commands.json toml11-4.1.0/.gitmodules000066400000000000000000000005111464712047700150170ustar00rootroot00000000000000[submodule "tests/extlib/doctest"] path = tests/extlib/doctest url = https://github.com/doctest/doctest.git [submodule "tests/extlib/json"] path = tests/extlib/json url = https://github.com/nlohmann/json.git [submodule "docs/themes/hugo-book"] path = docs/themes/hugo-book url = https://github.com/alex-shpak/hugo-book.git toml11-4.1.0/CMakeLists.txt000066400000000000000000000102611464712047700154050ustar00rootroot00000000000000cmake_minimum_required(VERSION 3.16) # project_source_dir has not been set yet file(STRINGS "${CMAKE_CURRENT_SOURCE_DIR}/include/toml11/version.hpp" TOML11_MAJOR_VERSION_STRING REGEX "#define TOML11_VERSION_MAJOR ([0-9]+)") file(STRINGS "${CMAKE_CURRENT_SOURCE_DIR}/include/toml11/version.hpp" TOML11_MINOR_VERSION_STRING REGEX "#define TOML11_VERSION_MINOR ([0-9]+)") file(STRINGS "${CMAKE_CURRENT_SOURCE_DIR}/include/toml11/version.hpp" TOML11_PATCH_VERSION_STRING REGEX "#define TOML11_VERSION_PATCH ([0-9]+)") string(REGEX REPLACE "#define TOML11_VERSION_MAJOR ([0-9]+)" "\\1" TOML11_VERSION_MAJOR "${TOML11_MAJOR_VERSION_STRING}") string(REGEX REPLACE "#define TOML11_VERSION_MINOR ([0-9]+)" "\\1" TOML11_VERSION_MINOR "${TOML11_MINOR_VERSION_STRING}") string(REGEX REPLACE "#define TOML11_VERSION_PATCH ([0-9]+)" "\\1" TOML11_VERSION_PATCH "${TOML11_PATCH_VERSION_STRING}") project(toml11 LANGUAGES CXX VERSION "${TOML11_VERSION_MAJOR}.${TOML11_VERSION_MINOR}.${TOML11_VERSION_PATCH}") include(CTest) # to use ${BUILD_TESTING} option(TOML11_PRECOMPILE "precompile toml11 library" OFF) include(CMakeDependentOption) cmake_policy(PUSH) cmake_policy(SET CMP0127 OLD) # syntax of condition changed in 3.22 cmake_dependent_option(TOML11_INSTALL "install toml11 library" ON "${CMAKE_PROJECT_NAME} STREQUAL ${PROJECT_NAME}" OFF) cmake_dependent_option(TOML11_BUILD_EXAMPLES "build toml11 examples" OFF "${CMAKE_PROJECT_NAME} STREQUAL ${PROJECT_NAME}" OFF) cmake_dependent_option(TOML11_BUILD_TESTS "build toml11 unit tests" OFF "${CMAKE_PROJECT_NAME} STREQUAL ${PROJECT_NAME}; ${BUILD_TESTING}" OFF) cmake_dependent_option(TOML11_BUILD_TOML_TESTS "build toml11 toml-test encoder & decoder" OFF "${CMAKE_PROJECT_NAME} STREQUAL ${PROJECT_NAME}" OFF) cmake_policy(POP) cmake_dependent_option(TOML11_TEST_WITH_ASAN "build toml11 unit tests with asan" OFF "${TOML11_BUILD_TESTS}" OFF) cmake_dependent_option(TOML11_TEST_WITH_UBSAN "build toml11 unit tests with ubsan" OFF "${TOML11_BUILD_TESTS}" OFF) if(${TOML11_TEST_WITH_ASAN} AND ${TOML11_TEST_WITH_UBSAN}) message(FATAL_ERROR "trying to build tests with BOTH asan and ubsan") endif() include(CheckCXXCompilerFlag) check_cxx_compiler_flag("-Wall" TOML11_COMPILER_SUPPORTS_WALL) check_cxx_compiler_flag("-Wextra" TOML11_COMPILER_SUPPORTS_WEXTRA) check_cxx_compiler_flag("-Wpedantic" TOML11_COMPILER_SUPPORTS_WPEDANTIC) check_cxx_compiler_flag("-Werror" TOML11_COMPILER_SUPPORTS_WERROR) check_cxx_compiler_flag("-Wsign-conversion" TOML11_COMPILER_SUPPORTS_WSIGN_CONVERSION) check_cxx_compiler_flag("-Wconversion" TOML11_COMPILER_SUPPORTS_WCONVERSION) check_cxx_compiler_flag("-Wduplicated-cond" TOML11_COMPILER_SUPPORTS_WDUPLICATED_COND) check_cxx_compiler_flag("-Wduplicated-branches" TOML11_COMPILER_SUPPORTS_WDUPLICATED_BRANCHES) check_cxx_compiler_flag("-Wlogical-op" TOML11_COMPILER_SUPPORTS_WLOGICAL_OP) check_cxx_compiler_flag("-Wdouble-promotion" TOML11_COMPILER_SUPPORTS_WDOUBLE_PROMOTION) check_cxx_compiler_flag("-Wrange-loop-analysis" TOML11_COMPILER_SUPPORTS_WRANGE_LOOP_ANALYSIS) check_cxx_compiler_flag("-Wundef" TOML11_COMPILER_SUPPORTS_WUNDEF) check_cxx_compiler_flag("-Wshadow" TOML11_COMPILER_SUPPORTS_WSHADOW) include(GNUInstallDirs) set(TOML11_INSTALL_CMAKE_DIR ${CMAKE_INSTALL_LIBDIR}/cmake/toml11) set(TOML11_INSTALL_INCLUDE_DIR ${CMAKE_INSTALL_INCLUDEDIR}) set(TOML11_CONFIG_DIR ${CMAKE_CURRENT_BINARY_DIR}/cmake) set(TOML11_CONFIG ${TOML11_CONFIG_DIR}/toml11Config.cmake) set(TOML11_CONFIG_VERSION ${TOML11_CONFIG_DIR}/toml11ConfigVersion.cmake) # root project? if(${CMAKE_PROJECT_NAME} STREQUAL ${PROJECT_NAME}) if(NOT DEFINED CMAKE_CXX_STANDARD) set(CMAKE_CXX_STANDARD 11) endif() if(NOT DEFINED CMAKE_CXX_EXTENSIONS) set(CMAKE_CXX_EXTENSIONS OFF) endif() if(NOT DEFINED CMAKE_CXX_STANDARD_REQUIRED) set(CMAKE_CXX_STANDARD_REQUIRED ON) endif() if(${TOML11_BUILD_TESTS} OR ${TOML11_BUILD_TOML_TESTS}) add_subdirectory(tests) endif() if(${TOML11_BUILD_EXAMPLES}) add_subdirectory(examples) endif() endif() add_subdirectory(src) toml11-4.1.0/LICENSE000066400000000000000000000020651464712047700136550ustar00rootroot00000000000000The MIT License (MIT) Copyright (c) 2017 Toru Niina Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. toml11-4.1.0/README.md000066400000000000000000000510761464712047700141350ustar00rootroot00000000000000# toml11 [![Build Status on GitHub Actions](https://github.com/ToruNiina/toml11/workflows/build/badge.svg)](https://github.com/ToruNiina/toml11/actions) [![Build status](https://ci.appveyor.com/api/projects/status/m2n08a926asvg5mg/branch/main?svg=true)](https://ci.appveyor.com/project/ToruNiina/toml11/branch/main) [![Version](https://img.shields.io/github/release/ToruNiina/toml11.svg?style=flat)](https://github.com/ToruNiina/toml11/releases) [![License](https://img.shields.io/github/license/ToruNiina/toml11.svg?style=flat)](LICENSE) [![DOI](https://zenodo.org/badge/DOI/10.5281/zenodo.1209136.svg)](https://doi.org/10.5281/zenodo.1209136) [日本語版](https://github.com/ToruNiina/toml11/blob/main/README_ja.md) toml11 is a feature-rich TOML language library for C++11/14/17/20. - It complies with [the latest TOML language specification](https://toml.io/en/v1.0.0). - It passes all the standard TOML language [test cases](https://github.com/toml-lang/toml-test). - It supports new features merged into the upcoming TOML version (v1.1.0). - It provides clear error messages, including the location of the error. - It parses and retains comments, associating them with corresponding values. - It maintains formatting information such as hex integers and considers these during serialization. - It provides exception-less parse function. - It supports complex type conversions from TOML values. - It allows customization of the types stored in `toml::value`. - It provides some extensions not present in the TOML language standard. ## Example ```cpp #include #include // ```toml // title = "an example toml file" // nums = [3, 1, 4, 1, 5] # pi! // ``` int main() { // select TOML version at runtime (optional) auto data = toml::parse("example.toml", toml::spec::v(1,1,0)); // find a value with the specified type from a table std::string title = toml::find(data, "title"); // convert the whole array into STL-like container automatically std::vector nums = toml::find>(data, "nums"); // access with STL-like manner if( ! data.contains("foo")) { data["foo"] = "bar"; } if(data.at("nums").is_array()) { data.push_back(9); } // check comments assert(data.at("nums").comments().at(0) == "# pi!"); // pass a fallback std::string name = toml::find_or(data, "name", "not found"); // serialization considering format info data.at("nums").as_array_fmt().fmt = toml::array_format::multiline; data.at("nums").as_array_fmt().indent_type = toml::indent_char::space; data.at("nums").as_array_fmt().body_indent = 2; std::cout << toml::format(data) << std::endl; return 0; } ``` For more details, please refer to the [documentation](https://toruniina.github.io/toml11/). ## Table of Contents - [Integration](#integration) - [Features](#features) - [parsing a file](#parsing-a-file) - [finding a value](#finding-a-value) - [comments](#comments) - [error messages](#error-messages) - [serialization](#serialization) - [Breaking Changes from v3](#changes-from-v3) - [Contributors](#contributors) - [Licensing Terms](#licensing-terms) ## Integration There are several ways to use toml11. Here is a brief overview of each method. For more details, please refer to the [documentation](https://toruniina.github.io/toml11/docs/installation/). ### Single Include File Copy `single_include/toml.hpp` to your preferred location and add it to your include path. ### git submodule By adding toml11 as a subdirectory using `git submodule` (or any other way), you can either add `toml11/include` to your include path or use `add_subdirectory(toml11)` in your CMake project. ### CMake `FetchContent` Using `FetchContent`, you can automatically download it. ```cmake include(FetchContent) FetchContent_Declare( toml11 GIT_REPOSITORY https://github.com/ToruNiina/toml11.git GIT_TAG v4.0.3 ) FetchContent_MakeAvailable(toml11) add_executable(main main.cpp) target_link_libraries(main PRIVATE toml11::toml11) ``` ### Install Using CMake You can install toml11 using CMake with the following steps: ```console $ git clone https://github.com/ToruNiina/toml11 $ cd toml11 $ git submodule update --init --recursive $ cmake -B ./build/ $ cmake --build ./build/ $ cmake --install ./build/ --prefix /path/to/toml11 ``` ### Precompile Library By setting `-DTOML11_PRECOMPILE=ON`, you can precompile some of the library functions. In this case, the standard library features available will vary with the C++ version, and part of the interface will change. Therefore, you need to specify `CMAKE_CXX_STANDARD`. ```console $ cmake -B ./build/ -DTOML11_PRECOMPILE=ON -DCMAKE_CXX_STANDARD=11/14/17/20 $ cmake --build ./build/ ``` When linking the library, use `target_link_libraries` in CMake ```cmake target_link_libraries(your_target PUBLIC toml11::toml11) ``` or pass `-DTOML11_COMPILE_SOURCES` to the compiler. ### Building Example To compile the examples in the `examples/` directory, set `-DTOML11_BUILD_EXAMPLES=ON`. ```console $ cmake -B ./build/ -DTOML11_BUILD_EXAMPLES=ON $ cmake --build ./build/ ``` ### Building Tests To compile unit tests, set `-DTOML11_BUILD_TESTS=ON`. Additionally, to compile the encoder and decoder for toml-test, set `-DTOML11_BUILD_TOML_TESTS=ON`. ```console $ cmake -B ./build/ -DTOML11_BUILD_EXAMPLES=ON $ cmake --build ./build/ ``` ## Features Here is a brief overview of the features provided by toml11. For more details, please refer to the [documentation](https://toruniina.github.io/toml11/docs/features/). ### Parsing a File To parse a file, use `toml::parse`. The overall type of the file is always a table. However, since `toml::value` contains metadata such as comments and formatting information, `toml::parse` returns a `toml::value` rather than a `toml::table`. ```cpp const toml::value input = toml::parse("input.toml"); ``` To parse a string directly, use `toml::parse_str`. ```cpp const toml::value input = toml::parse_str("a = 42"); ``` When parsing string literals, you can use the `""_toml` literal. ```cpp using namespace toml::literals::toml_literals; const toml::value lit = "a = 42"_toml; ``` `toml::parse`, `parse_str` and `_toml` literal throw a `toml::syntax_error` exception in case of a syntax error. The error message obtained with `what()` will look like this: ``` [error] bad integer: `_` must be surrounded by digits --> internal string at line 64 in file main.cpp | 1 | a = 123__456 | ^-- invalid underscore Hint: valid : -42, 1_000, 1_2_3_4_5, 0xC0FFEE, 0b0010, 0o755 Hint: invalid: _42, 1__000, 0123 ``` Error messages can also be colorized by calling `toml::color::enable()`. By using `toml::try_parse`, you can receive a `toml::result>` without throwing exceptions. ```cpp const auto input = toml::try_parse("input.toml"); if(input.is_ok()) { std::cout << input.unwrap().at("a").as_integer() << std::endl; } ``` Additionally, toml11 allows easy and precise control over the version of the TOML language being used. You can enable specific new features from TOML v1.1.0 while using TOML v1.0.0. ```cpp toml::spec s = toml::spec::v(1, 0, 0); s.v1_1_0_allow_trailing_comma_in_inline_tables = true; const toml::value input = toml::parse("input.toml"); ``` Moreover, several language extensions not included in the TOML standard are available. ```cpp toml::spec s = toml::spec::v(1, 0, 0); s.ext_hex_float = true; // this allows hexadecimal floating-point numbers s.ext_null_value = true; // this allows `key = null` value s.ext_num_suffix = true; // this allows numeric suffixes like `100_msec` ``` For more detail and reference of each feature, please refer to the [documentation](https://toruniina.github.io/toml11/docs/features/). ### finding a value `toml::value` provides member functions for accessing values, such as `at()`, `is_xxx()`, and `as_xxx()`. ```cpp const toml::value input = toml::parse("input.toml"); if(input.contains("a") && input.at("a").is_integer()) { std::cout << input.at("a").as_integer() << std::endl; } ``` By using `toml::find`, you can perform type conversion and search simultaneously. ```cpp const toml::value input = toml::parse("input.toml"); std::cout << toml::find(input, "a") << std::endl; ``` If type conversion or value lookup fails, a `toml::type_error` is thrown. The error message will look like this: ``` [error] toml::value::as_string(): bad_cast to string --> input.toml | 1 | a = 123_456 | ^^^^^^^-- the actual type is integer ``` You can access nested tables or arrays of tables in the same way. ```cpp // [a] // b = [ // {c = 42}, // {c = 54} // ] const toml::value input = toml::parse("input.toml"); std::cout << toml::find(input, "a", "b", 1, "c") << std::endl; ``` Most STL containers and those with similar interfaces can be converted. ```cpp // array = [3,1,4,1,5] const toml::value input = toml::parse("input.toml"); const auto a1 = toml::find>(input, "array"); const auto a2 = toml::find>(input, "array"); const auto a3 = toml::find>(input, "array"); const auto a4 = toml::find>(input, "array"); ``` You can perform advanced type conversions on complex TOML values. ```toml mixed_array = [ 42, 3.14, {a = "foo", b = "bar"} ] ``` ```cpp const toml::value input = toml::parse("input.toml"); const auto mixed = toml::find< std::tuple> >(input, "mixed_array") << std::endl; ``` User-defined types can also be converted by using macros or defining some specific functions. ```cpp namespace extlib { struct foo { int a; std::string b; }; } // extlib TOML11_DEFINE_CONVERSION_NON_INTRUSIVE(extlib::foo, a, b) // ... const auto input = R"( [foo] a = 42 b = "bar" )"_toml; const extlib::foo f = toml::find(input, "foo"); ``` Using `toml::find_or`, you can get a default value in case of failure. ```cpp const toml::value input = toml::parse("input.toml"); std::cout << toml::find_or(input, "a", 6*9) << std::endl; ``` For more details, please refer to the [documentation](https://toruniina.github.io/toml11/docs/features/value/). ### comments toml11 stores comments related to values within the value itself. Comments related to a value include a series of comments written immediately before the value and any comments written after the value without a newline in between. ```toml # This is a comment for a. # This is also a comment for a. a = 42 # This is also a comment for a. # This is separated by a newline, so it is not a comment for b. # This is a comment for b. b = "foo" ``` These comments are stored in `toml::value` with an interface similar to `std::vector`. ```cpp const toml::value input = toml::parse("input.toml"); std::cout << input.at("a").comments().size() << std::endl; std::cout << input.at("a").comments().at(0) << std::endl; ``` For more details, please refer to the [documentation](https://toruniina.github.io/toml11/docs/features/value/#accessing-comments). ### error messages One of the goals of toml11 is to provide clear and understandable error messages. For parsing errors, you might see an error message like the following: ``` [error] bad integer: `_` must be surrounded by digits --> internal string at line 64 in file main.cpp | 1 | a = 123__456 | ^-- invalid underscore Hint: valid : -42, 1_000, 1_2_3_4_5, 0xC0FFEE, 0b0010, 0o755 Hint: invalid: _42, 1__000, 0123 ``` If you request a type different from the actual stored type, an error message like this will be displayed: ``` [error] toml::value::as_string(): bad_cast to string --> input.toml | 1 | a = 123_456 | ^^^^^^^-- the actual type is integer ``` Such error messages can also be produced for user-specific algorithm that is not related to TOML syntax. ```cpp const toml::value& a = input.at("a"); if(a.as_integer() < 0) { const toml::error_info err = toml::make_error_info( "positive integer is required", a, "but got negative value" ); std::cerr << toml::format_error(err) << std::endl; } ``` For more details, please refer to the [documentation](https://toruniina.github.io/toml11/docs/features/error_message/). ### serialization You can format a `toml::value` into a `std::string` using `toml::format`. ```cpp const toml::value input = toml::parse("input.toml"); std::cout << toml::format(input) << std::endl; ``` `toml::format` references the formatting information, allowing you to change the formatting method. ```cpp toml::value output(toml::table{ {"a", 0xDEADBEEF} }); output.at("a").as_integer_fmt().fmt = toml::integer_format::hex; output.at("a").as_integer_fmt().spacer = 4; // position of `_` std::cout << toml::format(input) << std::endl; // a = 0xdead_beef ``` You can also specify the formatting for tables and arrays. ```cpp toml::value output(toml::table{ {"array-of-tables", toml::array{}}, {"subtable", toml::table{}}, }); auto& aot = output.at("array-of-tables"); aot.as_array_fmt().fmt = toml::array_format::multiline; // one element per line aot.as_array_fmt().body_indent = 4; aot.as_array_fmt().closing_indent = 2; toml::value v1(toml::table{ {"a", 42}, {"b", 3.14} }); v1.as_table_fmt().fmt = toml::table_format::oneline; aot.push_back(std::move(v1)); toml::value v2(toml::table{ {"a", 42}, {"b", 3.14} }); v2.as_table_fmt().fmt = toml::table_format::oneline; aot.push_back(std::move(v2)); output.at("subtable").as_table_fmt().fmt = toml::table_format::dotted; output.at("subtable")["a"] = 42; output.at("subtable")["b"] = 3.14; std::cout << toml::format(output) << std::endl; // subtable.b = 3.14 // subtable.a = 42 // array-of-tables = [ // {b = 3.14, a = 42}, // {b = 3.14, a = 42}, // ] ``` These settings are read during parsing and will be maintained as long as the value type does not change when modified. For details on possible formatting specifications, please refer to the [documentation](https://toruniina.github.io/toml11/docs/features/serialize/). ### Configuring Types Many types held by `toml::value`, such as `integer_type` and `array_type`, can be modified by changing the `type_config` type. Refer to the [`examples` directory](https://github.com/ToruNiina/toml11/tree/main/examples) for complex use cases such as using multi-precision integers, changing containers, and normalizing Unicode. Use these examples as references for implementing such configurations. ## Examples The [`examples`](https://github.com/ToruNiina/toml11/tree/main/examples) directory provides various implementation examples in addition to type configurations. - [boost_container](https://github.com/ToruNiina/toml11/tree/main/examples/boost_container) - This example shows how to use `boost::container` containers for `array_type` and `table_type`. - [boost_multiprecision](https://github.com/ToruNiina/toml11/tree/main/examples/boost_multiprecision) - This example demonstrates the use of `boost::multiprecision` multi-precision numeric types for `integer_type` and `floating_type`. - [parse_file](https://github.com/ToruNiina/toml11/tree/main/examples/parse_file) - This example includes type conversion implementations, covering slightly more complex cases. The corresponding TOML file is included. - [reflect](https://github.com/ToruNiina/toml11/tree/main/examples/reflect) - This example shows self-type conversion using boost-ext/reflect for user-defined types. - [unicode](https://github.com/ToruNiina/toml11/tree/main/examples/unicode) - This example demonstrates normalizing Unicode strings when searching for keys using uni-algo. ## Changes from v3 toml11 v4 introduces several breaking changes. Efforts have been made to minimize the need for changes for those using simple functionality. However, if you were utilizing advanced features, some adjustments may be necessary. Nevertheless, we believe that the added or streamlined features will provide enhanced convenience in return. ### Breaking Changes - Changed branch from `master` to `main`. - Changed template arguments of `toml::basic_value`. - Removed `toml::string` and explicitly store formatting information of all the values. - Modified arguments of `toml::format` to accommodate formatting information. - Changed default arguments of `toml::parse` to take `toml::spec` for specifying TOML version information. - Updated the interface of `toml::source_location` to accommodate multiline regions. - Modified arguments of `toml::format_error`. - Renamed `toml::format_underline` to `toml::format_location`. - Unified control method of `toml::color` to `toml::color::enable/disable()`. ### Added features - Added `toml::parse_str`. - Added `toml::try_parse`. - Added support for parsing byte sequences. - Added formatting information to `toml::value`. - Changed to saving comments in `toml::value` by default. - Added `single_include/toml.hpp`. - Enabled to use as a precompiled library. ## Contributors I appreciate the help of the contributors who introduced the great feature to this library. - Guillaume Fraux (@Luthaf) - Windows support and CI on Appvayor - Intel Compiler support - Quentin Khan (@xaxousis) - Found & Fixed a bug around ODR - Improved error messages for invalid keys to show the location where the parser fails - Petr Beneš (@wbenny) - Fixed warnings on MSVC - Ivan Shynkarenka (@chronoxor) - Fixed Visual Studio 2019 warnings - Fix compilation error in `` with MinGW - Khoi Dinh Trinh (@khoitd1997) - Fixed warnings while type conversion - @KerstinKeller - Added installation script to CMake - J.C. Moyer (@jcmoyer) - Fixed an example code in the documentation - Jt Freeman (@blockparty-sh) - Fixed feature test macro around `localtime_s` - Suppress warnings in Debug mode - OGAWA Kenichi (@kenichiice) - Suppress warnings on intel compiler - Fix include path in README - Jordan Williams (@jwillikers) - Fixed clang range-loop-analysis warnings - Fixed feature test macro to suppress -Wundef - Use cache variables in CMakeLists.txt - Automate test set fetching, update and refactor CMakeLists.txt - Scott McCaskill - Parse 9 digits (nanoseconds) of fractional seconds in a `local_time` - Shu Wang (@halfelf) - fix "Finding a value in an array" example in README - @maass-tv and @SeverinLeonhardt - Fix MSVC warning C4866 - Mohammed Alyousef (@MoAlyousef) - Made testing optional in CMake - Alex Merry (@amerry) - Add missing include files - sneakypete81 (@sneakypete81) - Fix typo in error message - Oliver Kahrmann (@founderio) - Fix missing filename in error message if parsed file is empty - Karl Nilsson (@karl-nilsson) - Fix many spelling errors - ohdarling88 (@ohdarling) - Fix a bug in a constructor of serializer - estshorter (@estshorter) - Fix MSVC warning C26478 - Philip Top (@phlptp) - Improve checking standard library feature availability check - Louis Marascio (@marascio) - Fix free-nonheap-object warning - Axel Huebl (@ax3l) - Make installation optional if the library is embedded - Ken Matsui (@ken-matsui) - Support user-defined error message prefix - Support dynamic color mode - Giel van Schijndel (@muggenhor) - Remove needless copy in `parse` function - Lukáš Hrázký (@lukash) - Add a `parse(FILE *)` interface and improve file-related error messages - spiderman idog (@spiderman-idog) - Fix typo in README - Jajauma's GitHub (@Jajauma) - Avoid possible lexer truncation warnings - Moritz Klammler (@ctcmkl) - Many patches in (#200) including: - Improve CMake scripts, build process, and test file handling - Detect error when `discard_comments` is accessed - And more. - Chris White (@cxw42) - Fix address-sanitizer error when parsing literal strings having invalid UTF-8 characters - Fix function name in error messages - offa (@offa) - Update checkout action to v3 - Update Required CMake version - Cleanup old CI settings - Sergey Vidyuk (@VestniK) - Fix for case when vector iterator is raw pointer - Kfir Gollan (@kfirgollan) - Add installation example with checkinstall and cmake - Martin Tournoij (@arp242) - Escape control characters in keys - @DavidKorczynski - Add fuzzing test based on ClusterFuzzLite - Esonhugh Skyworship (@Esonhugh) - Fix function signature of `strerror_r` on macos - Alberto (@0X1A) - Fix issues with CMake package configuration when used with vcpkg - Egor Pugin (@egorpugin) - Fix incorrect operator<<() argument type that gives build error - Andreas Keller (@andreaskeller96) - Fix not checking for \r\n when parsing line comments - 萧迩珀 (@CDK6182CHR) - Support template into_toml members ## Licensing terms This product is licensed under the terms of the [MIT License](LICENSE). - Copyright (c) 2017-2024 Toru Niina All rights reserved. toml11-4.1.0/README_ja.md000066400000000000000000000540611464712047700146040ustar00rootroot00000000000000# toml11 v4 [![Build Status on GitHub Actions](https://github.com/ToruNiina/toml11/workflows/build/badge.svg)](https://github.com/ToruNiina/toml11/actions) [![Version](https://img.shields.io/github/release/ToruNiina/toml11.svg?style=flat)](https://github.com/ToruNiina/toml11/releases) [![License](https://img.shields.io/github/license/ToruNiina/toml11.svg?style=flat)](LICENSE) [![DOI](https://zenodo.org/badge/DOI/10.5281/zenodo.1209136.svg)](https://doi.org/10.5281/zenodo.1209136) toml11は、C++11,14,17,20のための豊富な機能を持つTOML言語ライブラリです。 - [TOML言語の最新規格](https://toml.io/ja/v1.0.0)に準拠しています。 - TOML言語標準のテストケースすべてにパスしています。 - TOML言語の次期バージョン (v1.1.0) にマージされた新機能のそれぞれを試すことができます。 - エラーが起きた位置を含めたわかりやすいエラーメッセージを出力します。 - コメントもパースし、対応する値に保存します。 - 16進整数やクオートの種類などのフォーマット情報を保持し、シリアライズ時に考慮します。 - 例外を投げないバージョンをサポートします。 - TOML値からの複雑な型変換をサポートします。 - 整数、浮動小数点数、コンテナ等の型を変更可能です。 - TOML言語にない一部の拡張機能を試すことができます。 ## Example ```cpp #include #include // ```toml // title = "an example toml file" // nums = [3, 1, 4, 1, 5] # pi! // ``` int main() { // select TOML version at runtime (optional) auto data = toml::parse("example.toml", toml::spec::v(1,1,0)); // find a value with the specified type from a table std::string title = toml::find(data, "title"); // convert the whole array into STL-like container automatically std::vector nums = toml::find>(data, "nums"); // access with STL-like manner if( ! data.contains("foo")) { data["foo"] = "bar"; } if(data.at("nums").is_array()) { data.push_back(9); } // check comments assert(data.at("nums").comments().at(0) == "# pi!"); // pass a fallback std::string name = toml::find_or(data, "name", "not found"); // serialization considering format info data.at("nums").as_array_fmt().fmt = toml::array_format::multiline; data.at("nums").as_array_fmt().indent_type = toml::indent_char::space; data.at("nums").as_array_fmt().body_indent = 2; std::cout << toml::format(data) << std::endl; return 0; } ``` 詳細な機能とリファレンスに関しては、[ドキュメント](https://toruniina.github.io/toml11/ja/)を参照してください。 ## Table of Contents - [Integration](#integration) - [Features](#features) - [parsing a file](#parsing-a-file) - [finding a value](#finding-a-value) - [comments](#comments) - [error messages](#error-messages) - [serialization](#serialization) - [Breaking Changes from v3](#changes-from-v3) - [Contributors](#contributors) - [Licensing Terms](#licensing-terms) ## Integration toml11を使うには複数の方法があります。 ここではそれぞれを短く紹介します。詳細は、[ドキュメント](https://toruniina.github.io/toml11/ja/docs/installation/)を参照してください。 ### Single include file `single_include/toml.hpp`を好きなところにコピーして、インクルードパスを通してください。 ### git submodule git submoduleなどでサブディレクトリにすれば、`toml11/include`にインクルードパスを通すか、 `add_subdirectory(toml11)` とすることで使用できます。 ### CMake `FetchContent` CMakeの `FetchContent`を使用することで、`build`ディレクトリに自動でダウンロードすることができます。 ```cmake include(FetchContent) FetchContent_Declare( toml11 GIT_REPOSITORY https://github.com/ToruNiina/toml11.git GIT_TAG v4.0.3 ) FetchContent_MakeAvailable(toml11) add_executable(main main.cpp) target_link_libraries(main PRIVATE toml11::toml11) ``` ### Install using CMake 以下の手順で、CMakeを使ってインストールすることができます。 ```console $ git clone https://github.com/ToruNiina/toml11 $ cd toml11 $ git submodule update --init --recursive $ cmake -B ./build/ $ cmake --build ./build/ $ cmake --install ./build --prefix /path/to/toml11 ``` ### Precompile library `-DTOML11_PRECOMPILE=ON` とすることで、ライブラリの一部の関数をコンパイルできます。 この場合、C++バージョンによって使用できる標準ライブラリ機能が変化し、 インターフェースの一部が変わるため、`CMAKE_CXX_STANDARD`を指定する必要があります。 ```console $ cmake -B ./build/ -DTOML11_PRECOMPILE=ON -DCMAKE_CXX_STANDARD=11/14/17/20 $ cmake --build ./build/ ``` ライブラリをリンクする場合は、CMakeで ```cmake target_link_libraries(your_target PUBLIC toml11::toml11) ``` とするか、コンパイラに`-DTOML11_COMPILE_SOURCES`を渡してください。 ### Building example `-DTOML11_BUILD_EXAMPLES=ON`とすることで、`examples/`をコンパイルできます。 ```console $ cmake -B ./build/ -DTOML11_BUILD_EXAMPLES=ON $ cmake --build ./build/ ``` ### Building Tests `-DTOML11_BUILD_TESTS=ON`とすることで、ユニットテストをコンパイルできます。 また、`-DTOML11_BUILD_TOML_TESTS=ON`とすることで、toml-test用の`encoder`と`decoder`をコンパイルできます。 ```console $ cmake -B ./build/ -DTOML11_BUILD_EXAMPLES=ON $ cmake --build ./build/ ``` ## Features ここでは、toml11の持つ機能を短く紹介します。 詳細については[ドキュメント](https://toruniina.github.io/toml11/ja/docs/features/)を参照してください。 ### parsing a file ファイルをパースする場合は、`toml::parse`を使います。 ファイル全体の型は常にテーブルですが、 `toml::value` はコメントや フォーマット情報などのメタデータを持つので、`toml::parse`からは `toml::table` ではなく `toml::value` が返ります。 ```cpp const toml::value input = toml::parse("input.toml"); ``` 文字列そのものをパースする場合は、`toml::parse_str`を使います。 ```cpp const toml::value input = toml::parse_str("a = 42"); ``` 文字列リテラルをパースする際は、`""_toml`リテラルを使うことができます。 ```cpp using namespace toml::literals::toml_literals; const toml::value lit = "a = 42"_toml; ``` `toml::parse`や`parse_str`は文法エラーの際に `toml::syntax_error` 例外を投げます。 `what()`で得られるエラーメッセージは以下のようになります。 ``` [error] bad integer: `_` must be surrounded by digits --> internal string at line 64 in file main.cpp | 1 | a = 123__456 | ^-- invalid underscore Hint: valid : -42, 1_000, 1_2_3_4_5, 0xC0FFEE, 0b0010, 0o755 Hint: invalid: _42, 1__000, 0123 ``` エラーメッセージには`toml::color::enable()`を呼ぶことで色を付けることも可能です。 `toml::try_parse`を使うことで、例外を投げずに `toml::result>` を受け取ることができます。 ```cpp const auto input = toml::try_parse("input.toml"); if(input.is_ok()) { std::cout << input.unwrap().at("a").as_integer() << std::endl; } ``` また、使用するTOML言語のバージョンを簡単に、かつ細かく変更できるようになりました。 TOML v1.0.0に加えて、v1.1.0の新機能を一部だけ使用するというような制御が可能です。 ```cpp toml::spec s = toml::spec::v(1, 0, 0); s.v1_1_0_allow_trailing_comma_in_inline_tables = true; const toml::value input = toml::parse("input.toml"); ``` また、TOML言語自体に含まれない言語拡張もいくつか用意しています。 ```cpp toml::spec s = toml::spec::v(1, 0, 0); s.ext_hex_float = true; // 16進数浮動小数点数を許可 s.ext_null_value = true; // 空の値 `null` を許可 s.ext_num_suffix = true; // `100_msec`などのsuffixを許可 ``` 各機能の紹介とリファレンスは、[ドキュメント](https://toruniina.github.io/toml11/ja/docs/features/)を参照してください。 ### finding a value `toml::value` はアクセス用のメンバ関数、`at()` や `is_xxx()`, `as_xxx()` を持ちます。 ```cpp const toml::value input = toml::parse("input.toml"); if(input.contains("a") && input.at("a").is_integer()) { std::cout << input.at("a").as_integer() << std::endl; } ``` `toml::find` を使うことで、型変換と検索を同時に行うことができます。 ```cpp const toml::value input = toml::parse("input.toml"); std::cout << toml::find(input, "a") << std::endl; ``` 型変換や値の検索に失敗した場合は、`toml::type_error`が送出されます。 その場合のエラーメッセージは以下のようになります。 ``` [error] toml::value::as_string(): bad_cast to string --> input.toml | 1 | a = 123_456 | ^^^^^^^-- the actual type is integer ``` ネストされたテーブルやテーブルの配列にも同じ方法でアクセスできます。 ```cpp // [a] // b = [ // {c = 42}, // {c = 54} // ] const toml::value input = toml::parse("input.toml"); std::cout << toml::find(input, "a", "b", 1, "c") << std::endl; ``` ほとんどのSTLコンテナや、同様のインターフェースを持つコンテナへ変換が可能です。 ```cpp // array = [3,1,4,1,5] const toml::value input = toml::parse("input.toml"); const auto a1 = toml::find >(input, "array") << std::endl; const auto a2 = toml::find>(input, "array") << std::endl; const auto a3 = toml::find >(input, "array") << std::endl; const auto a4 = toml::find>(input, "array") << std::endl; ``` また、複雑なTOML値に対して、高度な型変換を行うことができます。 ```toml mixed_array = [ 42, 3.14, {a = "foo", b = "bar"} ] ``` ```cpp const toml::value input = toml::parse("input.toml"); const auto mixed = toml::find< std::tuple> >(input, "mixed_array") << std::endl; ``` マクロの使用または特定の関数を定義することで、ユーザー定義型にも変換が可能です。 ```cpp namespace extlib { struct foo { int a; std::string b; }; } // extlib TOML11_DEFINE_CONVERSION_NON_INTRUSIVE(extlib::foo, a, b) // ... const auto input = R"( [foo] a = 42 b = "bar" )"_toml; const extlib::foo f = toml::find(input, "foo"); ``` `toml::find_or`を使うことで、失敗時にデフォルト値を得ることができます。 ```cpp const toml::value input = toml::parse("input.toml"); std::cout << toml::find_or(input, "a", 6*9) << std::endl; ``` 詳細については[ドキュメント](https://toruniina.github.io/toml11/ja/docs/features/value/)を参照してください。 ### comments toml11は、値に関するコメントを値に保存します。 値に関するコメントとは、ある値の直前に書かれた一連のコメントと、値の直後に改行を挟まず書かれたコメントです。 ```toml # これは a のコメントです。 # これも a のコメントです。 a = 42 # これも a のコメントです。 # これは改行で分かれているので、 b のコメントではありません。 # これは b のコメントです。 b = "foo" ``` これは `std::vector` と同じインターフェースで `toml::value` に格納されます。 ```cpp const toml::value input = toml::parse("input.toml"); std::cout << input.at("a").comments().size() << std::endl; std::cout << input.at("a").comments().at(0) << std::endl; ``` 詳細については[ドキュメント](https://toruniina.github.io/toml11/ja/docs/features/value/#%E3%82%B3%E3%83%A1%E3%83%B3%E3%83%88%E3%81%AB%E3%82%A2%E3%82%AF%E3%82%BB%E3%82%B9%E3%81%99%E3%82%8B)を参照してください。 ### error messages toml11の目標の一つは、わかりやすいエラーメッセージを出力することです。 パースエラーに対しては以下のようなエラーメッセージが、 ``` [error] bad integer: `_` must be surrounded by digits --> internal string at line 64 in file main.cpp | 1 | a = 123__456 | ^-- invalid underscore Hint: valid : -42, 1_000, 1_2_3_4_5, 0xC0FFEE, 0b0010, 0o755 Hint: invalid: _42, 1__000, 0123 ``` 実際に格納されている型と異なる型を要求した場合には以下のようなエラーメッセージが表示されます。 ``` [error] toml::value::as_string(): bad_cast to string --> input.toml | 1 | a = 123_456 | ^^^^^^^-- the actual type is integer ``` このようなエラーメッセージを、TOMLの文法とは関係のないユーザー特有の処理に対しても出力することが可能です。 ```cpp const toml::value& a = input.at("a"); if(a.as_integer() < 0) { const toml::error_info err = toml::make_error_info( "positive integer is required", a, "but got negative value" ); std::cerr << toml::format_error(err) << std::endl; } ``` 詳細は[ドキュメント](https://toruniina.github.io/toml11/ja/docs/features/error_message/)を参照してください。 ### serialization `toml::format` を使うことで、`toml::value` を `std::string` にフォーマットできます。 ```cpp const toml::value input = toml::parse("input.toml"); std::cout << toml::format(input) << std::endl; ``` `toml::format` はフォーマット情報を参照します。 なので、フォーマット方法を変更することが可能です。 ```cpp toml::value output(toml::table{ {"a", 0xDEADBEEF} }); output.at("a").as_integer_fmt().fmt = toml::integer_format::hex; output.at("a").as_integer_fmt().spacer = 4; std::cout << toml::format(output) << std::endl; // a = 0xdead_beef ``` テーブルや配列のフォーマットも指定が可能です。 ```cpp toml::value output(toml::table{ {"array-of-tables", toml::array{}}, {"subtable", toml::table{}}, }); auto& aot = output.at("array-of-tables"); aot.as_array_fmt().fmt = toml::array_format::multiline; aot.as_array_fmt().body_indent = 4; aot.as_array_fmt().closing_indent = 2; toml::value v1(toml::table{ {"a", 42}, {"b", 3.14} }); v1.as_table_fmt().fmt = toml::table_format::oneline; aot.push_back(std::move(v1)); toml::value v2(toml::table{ {"a", 42}, {"b", 3.14} }); v2.as_table_fmt().fmt = toml::table_format::oneline; aot.push_back(std::move(v2)); output.at("subtable").as_table_fmt().fmt = toml::table_format::dotted; output.at("subtable")["a"] = 42; output.at("subtable")["b"] = 3.14; std::cout << toml::format(output) << std::endl; // subtable.b = 3.14 // subtable.a = 42 // array-of-tables = [ // {b = 3.14, a = 42}, // {b = 2.71, a = 54}, // ] ``` これらの設定はパース時に読み取られ、値を変更した際も型が変わらない限り維持されます。 どのような指定が可能かなどの詳細は[ドキュメント](https://toruniina.github.io/toml11/ja/docs/features/serialize/)を参照してください。 ### configuring types `toml::value`が持つ型の多く、`integer_type`や`array_type`などは`type_config`型を変更することで変更可能です。 [`examples`ディレクトリ](https://github.com/ToruNiina/toml11/tree/main/examples)には、 多倍長整数を使用する場合やコンテナを変更する場合、ユニコードを正規化する場合などの複雑な使用例を用意しています。 そのような状況での実装例として参照してください。 ## Examples [`examples`ディレクトリ](https://github.com/ToruNiina/toml11/tree/main/examples)では、 型の設定の他にも実装例を紹介しています。 - [boost_container](https://github.com/ToruNiina/toml11/tree/main/examples/boost_container) - `array_type`や`table_type`に`boost::container`のコンテナを使う例です。 - [boost_multiprecision](https://github.com/ToruNiina/toml11/tree/main/examples/boost_multiprecision) - `integer_type`や`floating_type`に`boost::multiprecision`の多倍長数値型を使う例です。 - [parse_file](https://github.com/ToruNiina/toml11/tree/main/examples/parse_file) - 少し複雑な場合も含めた、型変換の実装例です。対応するTOMLファイルが同梱されています。 - [reflect](https://github.com/ToruNiina/toml11/tree/main/examples/reflect) - boost-ext/reflectを用いたユーザー定義型との自型変換の例です。 - [unicode](https://github.com/ToruNiina/toml11/tree/main/examples/unicode) - uni-algoを用いて、キーを検索する際にユニコード文字列を正規化する例です。 ## Changes from v3 toml11 v3からは複数の破壊的変更が追加されています。 シンプルな使い方をしている場合にはほとんど変更せずに済むように努力はしていますが、 高度な機能を利用していた場合は多少の変更が必要になります。 しかし、追加された機能や整理された機能は、その分の利便性を提供できると考えています。 ### 破壊的な変更 - ブランチを `master` から `main` に変更 - `toml::basic_value` の `template` 引数を変更 - フォーマット情報を陽に格納するため`toml::string` を廃止 - フォーマット情報を使用するため`toml::format`の引数を変更 - TOMLバージョン情報を格納する`toml::spec`を追加するため`toml::parse`のデフォルト引数を変更 - `toml::source_location` のインターフェースを複数行を前提とした形に変更 - `toml::format_error` の引数を変更 - `toml::format_underline` の名称を `toml::format_location` に変更 - `toml::color` の制御方法を `toml::color::enable()`に統一 ### 破壊的でない変更 - `toml::parse_str`の追加 - `toml::try_parse`の追加 - バイト列のパースをサポート - `toml::value` にフォーマット情報を追加 - コメントをデフォルトで保存するよう変更 - `single_include/toml.hpp`の追加 - コンパイル済みライブラリとしての使用を可能に ## Contributors このライブラリに新機能を追加、あるいはバグを修正してくださったコントリビュータの方々に感謝します。 - Guillaume Fraux (@Luthaf) - Windows support and CI on Appvayor - Intel Compiler support - Quentin Khan (@xaxousis) - Found & Fixed a bug around ODR - Improved error messages for invalid keys to show the location where the parser fails - Petr Beneš (@wbenny) - Fixed warnings on MSVC - Ivan Shynkarenka (@chronoxor) - Fixed Visual Studio 2019 warnings - Fix compilation error in `` with MinGW - Khoi Dinh Trinh (@khoitd1997) - Fixed warnings while type conversion - @KerstinKeller - Added installation script to CMake - J.C. Moyer (@jcmoyer) - Fixed an example code in the documentation - Jt Freeman (@blockparty-sh) - Fixed feature test macro around `localtime_s` - Suppress warnings in Debug mode - OGAWA Kenichi (@kenichiice) - Suppress warnings on intel compiler - Fix include path in README - Jordan Williams (@jwillikers) - Fixed clang range-loop-analysis warnings - Fixed feature test macro to suppress -Wundef - Use cache variables in CMakeLists.txt - Automate test set fetching, update and refactor CMakeLists.txt - Scott McCaskill - Parse 9 digits (nanoseconds) of fractional seconds in a `local_time` - Shu Wang (@halfelf) - fix "Finding a value in an array" example in README - @maass-tv and @SeverinLeonhardt - Fix MSVC warning C4866 - Mohammed Alyousef (@MoAlyousef) - Made testing optional in CMake - Alex Merry (@amerry) - Add missing include files - sneakypete81 (@sneakypete81) - Fix typo in error message - Oliver Kahrmann (@founderio) - Fix missing filename in error message if parsed file is empty - Karl Nilsson (@karl-nilsson) - Fix many spelling errors - ohdarling88 (@ohdarling) - Fix a bug in a constructor of serializer - estshorter (@estshorter) - Fix MSVC warning C26478 - Philip Top (@phlptp) - Improve checking standard library feature availability check - Louis Marascio (@marascio) - Fix free-nonheap-object warning - Axel Huebl (@ax3l) - Make installation optional if the library is embedded - Ken Matsui (@ken-matsui) - Support user-defined error message prefix - Support dynamic color mode - Giel van Schijndel (@muggenhor) - Remove needless copy in `parse` function - Lukáš Hrázký (@lukash) - Add a `parse(FILE *)` interface and improve file-related error messages - spiderman idog (@spiderman-idog) - Fix typo in README - Jajauma's GitHub (@Jajauma) - Avoid possible lexer truncation warnings - Moritz Klammler (@ctcmkl) - Many patches in (#200) including: - Improve CMake scripts, build process, and test file handling - Detect error when `discard_comments` is accessed - And more. - Chris White (@cxw42) - Fix address-sanitizer error when parsing literal strings having invalid UTF-8 characters - Fix function name in error messages - offa (@offa) - Update checkout action to v3 - Update Required CMake version - Cleanup old CI settings - Sergey Vidyuk (@VestniK) - Fix for case when vector iterator is raw pointer - Kfir Gollan (@kfirgollan) - Add installation example with checkinstall and cmake - Martin Tournoij (@arp242) - Escape control characters in keys - @DavidKorczynski - Add fuzzing test based on ClusterFuzzLite - Esonhugh Skyworship (@Esonhugh) - Fix function signature of `strerror_r` on macos - Alberto (@0X1A) - Fix issues with CMake package configuration when used with vcpkg - Egor Pugin (@egorpugin) - Fix incorrect operator<<() argument type that gives build error - Andreas Keller (@andreaskeller96) - Fix not checking for \r\n when parsing line comments - 萧迩珀 (@CDK6182CHR) - Support template into_toml members ## Licensing terms This product is licensed under the terms of the [MIT License](LICENSE). - Copyright (c) 2017-2024 Toru Niina All rights reserved. toml11-4.1.0/appveyor.yml000066400000000000000000000013711464712047700152370ustar00rootroot00000000000000version: "{build}" environment: matrix: - APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2019 generator: Visual Studio 16 2019 systemver: 10.0.18362.0 - APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2022 generator: Visual Studio 17 2022 systemver: 10.0.22621.0 configuration: - Release - Debug clone_depth: 10 clone_folder: c:\toml11 install: - git submodule update --init --recursive build_script: - cd C:\toml11 - cmake -B build -G"%generator%" -DCMAKE_SYSTEM_VERSION="%systemver%" -A x64 -DCMAKE_CXX_STANDARD=11 -DTOML11_BUILD_TESTS=ON -DTOML11_PRECOMPILE=ON - cmake --build build --config "%configuration%" test_script: - ctest --test-dir build --build-config "%configuration%" --timeout 300 --output-on-failure toml11-4.1.0/cmake/000077500000000000000000000000001464712047700137255ustar00rootroot00000000000000toml11-4.1.0/cmake/toml11Config.cmake.in000066400000000000000000000002221464712047700175730ustar00rootroot00000000000000@PACKAGE_INIT@ include("${CMAKE_CURRENT_LIST_DIR}/toml11Targets.cmake") set_and_check(TOML11_INCLUDE_DIR "@PACKAGE_TOML11_INSTALL_INCLUDE_DIR@/") toml11-4.1.0/docs/000077500000000000000000000000001464712047700135755ustar00rootroot00000000000000toml11-4.1.0/docs/.gitignore000066400000000000000000000000461464712047700155650ustar00rootroot00000000000000public/* resources/* .hugo_build.lock toml11-4.1.0/docs/config.toml000066400000000000000000000012041464712047700157340ustar00rootroot00000000000000baseURL = "https://toruniina.github.io/toml11" title = "toml11" languageCode = "en-us" theme = "hugo-book" disablePathToLower = true [languages] [languages.en] weight = 1 languageName = "English" contentDir = "content.en" [languages.ja] weight = 2 languageName = "日本語" contentDir = "content.ja" [menu] [[menu.after]] name = "GitHub" url = "https://github.com/ToruNiina/toml11" weight = 10 [params] BookTheme = 'auto' BookToC = true BookSection = 'docs' BookSearch = true BookComments = false BookRepo = 'https://github.com/ToruNiina/toml11' [markup.tableOfContents] startLevel = 1 endLevel = 3 toml11-4.1.0/docs/content.en/000077500000000000000000000000001464712047700156505ustar00rootroot00000000000000toml11-4.1.0/docs/content.en/_index.md000066400000000000000000000007261464712047700174450ustar00rootroot00000000000000+++ title = "Introduction" type = "docs" +++ # Introduction 日本語版は[こちら](/toml11/ja/) ## [Installation](docs/installation) This section describes how to build and install toml11. ## [Features](docs/features) This section describes how to use toml11's features with examples. ## [Reference](docs/reference) This section details toml11 functions and classes. ## [ChangeLog](docs/changelog) This section describes changes from release to release. toml11-4.1.0/docs/content.en/docs/000077500000000000000000000000001464712047700166005ustar00rootroot00000000000000toml11-4.1.0/docs/content.en/docs/changelog/000077500000000000000000000000001464712047700205275ustar00rootroot00000000000000toml11-4.1.0/docs/content.en/docs/changelog/_index.md000066400000000000000000000346001464712047700223220ustar00rootroot00000000000000+++ title = "changelog" type = "docs" weight = 4 +++ # Change Log # v4.1.0 ## Added - support `std::get` - support `toml::value(std::u8string)` - support `string_type = std::u8string` - support `operator<<(std::ostream&, toml::value)` - add `bool uppercase` to `toml::integer_format` - support `template into_toml()` (by 萧迩珀) ## Fixed - Fix not checking for `\r\n` when parsing line comments (by Andreas Keller) ## Changed - speedup CI tests by multicore build # v4.0.3 ## Fixed - remove default template argument from forward declaration in `toml_fwd.hpp` - enable to call `toml::make_error_info` with multiple `toml::value`s - enable to copy/move `toml::result` having `std::reference_wrapper` - fix document generation error with the latest version of hugo - fix several tiny errors that causes warning - fix CMake compatibility warning ## Changed add `-Werror` / `/WX` to build script set MSVC warning level to `/W4` add explanation of features to README # v4.0.2 ## Fixed - check return value of fread in `parse(FILE*)` - version macro defined in `toml11/version.hpp` - update docs about compilation - update docs about file open mode ## Changed - use version macros defined in `toml11/version.hpp` as the project version in `CMakeLists.txt` # v4.0.1 ## Fixed - Resolved naming conflict of `sematic_version::{major, minor}` with macros defined in `` - Fixed the definition of `operator<<` in `discard_comments` (by Egor Pugin) - Fixed the issue where the first blank line was not output in `format_location` - Fixed the issue where error messages pointing to `source_location` referring to lines containing only newline characters were displayed in two lines - Corrected links in the README - Corrected the title of the README in `example/unicode` ## Added - Configured CI to automatically update `single_include/toml.hpp` when changes are made to `main` # Changes from v3 to v4 ## Breaking Changes ### Changed `template` Parameters of `toml::basic_value` In toml11 v3, `toml::basic_value` took separate template arguments for the comment container, table-type container, and array-type container. ```cpp template class Table = std::unordered_map, template class Array = std::vector> class basic_value; ``` However, this approach does not accommodate changes to types such as `integer_type`. In toml11 v4, `toml::basic_value` now accepts a single `TypeConfig`, allowing for more types to be customized. ```cpp template class basic_value; ``` By default, the types stored in `toml::value` remain unchanged. For more information on changing types, please refer to the [`type_config`]({{< ref "/docs/reference/types.md">}}) documentation. ### Removed `std::initializer_list` Support for `toml::basic_value` In toml11 v3, there was an overload for `toml::value` that accepted `std::initializer_list`. This allowed for more intuitive initialization of `toml::value` with arrays and tables. ```cpp // toml11 v3 toml::value v{1,2,3,4,5}; toml::value v{ {"a", 42}, {"b", "foo"} }; ``` However, this caused the following issues: First, it was impossible to distinguish between a single-element array and a regular value, as it always became an array. ```cpp // toml11 v3 toml::value v{1}; // Becomes [1,] instead of 1 ``` With the widespread use of uniform initialization, this became very inconvenient. Second, it was unclear whether the value represented a table with all string values or a nested array. ```cpp // toml11 v3 toml::value v{ {"a", "foo"}, {"b", "bar"} }; // Could be either: // {a = "foo", b = "bar"} // [["a", "foo"], ["b", "bar"]] ``` These issues were difficult to resolve due to language specifications. To avoid confusion, toml11 v4 has removed `std::initializer_list` support. When initializing `toml::value` with an array, you must explicitly specify `toml::array`, and when initializing with a table, you must explicitly specify `toml::table`. ```cpp // toml11 v4 toml::value v(toml::array{1,2,3,4,5}); toml::value v(toml::table{ {"a", 42}, {"b", "foo"} }); toml::value v{toml::array{1}}; // [1,] toml::value v{1} // 1 toml::value v{toml::table{{"a", "foo"}, {"b", "bar"}}}; toml::value v{toml::array{toml::array{"a", "foo"}, toml::array{"b", "bar"}}}; ``` While this makes initializing `toml::value` with tables or arrays slightly less convenient, it ensures that the values will not become unpredictable by requiring explicit type information. ### Renamed `toml::basic_value::is_uninitialized()` to `is_empty()` In toml11 v3, the function to check whether a `basic_value` was uninitialized was called `is_uninitialized`. However, in toml11 v4, the library supports `null` values as an extension, allowing for the intentional construction of empty values. Therefore, the function has been renamed to `is_empty` to reflect this change. ### Added Format Information and Removed `toml::string` In toml11 v3, to retain information on whether a string was `basic` or `literal`, the library used a thin wrapper around `std::string` called `toml::string`. ```cpp // toml11 v3 namespace toml { enum class string_t : std::uint8_t { basic = 0, literal = 1, }; struct string { string_t kind; std::string str; }; } // namespace toml ``` In toml11 v4, to accommodate more format information such as the numeric base or whether arrays should be multiline, every type now has an associated `xxx_format` type, which is stored alongside the value. ```cpp // toml11 v4 enum class string_format : std::uint8_t { basic = 0, literal = 1, multiline_basic = 2, multiline_literal = 3 }; struct string_format_info { string_format fmt = string_format::basic; bool start_with_newline = false; }; ``` This change allows for more detailed format information to be preserved, ensuring that format specifics for numeric types, arrays, and tables are maintained even after parsing. ### Changed Arguments of `toml::format` In toml11 v3, `toml::format` accepted values such as the precision and width of numeric types. However, this approach did not allow for detailed formatting specifications, resulting in serialized files that did not match expectations. In toml11 v4, each `toml::value` now carries its own format information, enabling more detailed formatting options to be preserved within the `toml::value` itself. As a result, `toml::format` no longer accepts specific formatting values. Instead, it now only takes a `toml::spec`, which includes language feature flags used during formatting. ### Changed Member Functions of `toml::source_location` In toml11 v3, the member types of `toml::source_location` were designed to handle only single lines. In toml11 v4, the member types of `toml::source_location` are designed to handle multiple lines. ### Renamed `toml::format_underline` to `toml::format_location` In toml11 v3, the function used to format location information from `toml::source_location` was called `toml::format_underline`. To make the name clearer, it has been renamed to `toml::format_location`. ## Changed Arguments of `toml::format_error` In toml11 v3, there was no class to represent error information, resulting in complex arguments for `toml::format_error`. ```cpp template class T, template class A> std::string format_error(const std::string& err_msg, const basic_value& v, const std::string& comment, std::vector hints = {}, const bool colorize = TOML11_ERROR_MESSAGE_COLORIZED); template class T, template class A> inline std::string format_error(const std::string& err_msg, const toml::basic_value& v1, const std::string& comment1, const toml::basic_value& v2, const std::string& comment2, std::vector hints = {}, const bool colorize = TOML11_ERROR_MESSAGE_COLORIZED); template class T, template class A> inline std::string format_error(const std::string& err_msg, const toml::basic_value& v1, const std::string& comment1, const toml::basic_value& v2, const std::string& comment2, const toml::basic_value& v3, const std::string& comment3, std::vector hints = {}, const bool colorize = TOML11_ERROR_MESSAGE_COLORIZED); ``` In toml11 v4, we have introduced `class error_info` and `make_error_info`, simplifying the arguments for `format_error`. ```cpp std::string format_error(const error_info& err); std::string format_error(const std::string& errkind, const error_info& err); template std::string format_error(std::string title, source_location loc, std::string msg, Ts&& ... tail); template std::string format_error(std::string title, const basic_value& v, std::string msg, Ts&& ... tail); ``` ### Changed Control of `toml::color` In toml11 v3, to control whether to colorize the output, we used the manipulator `toml::colorize` in conjunction with `toml::color::enable/disable`. The manipulator allowed us to decide whether to apply color to each stream, but in v4, the frequency of using streams has decreased, and issues such as the fact that `std::ios_base::xalloc` used internally is not thread-safe in C++11 have arisen. Therefore, we have decided to use only `toml::color::enable/disable` and have removed `toml::colorize`. ## Non-Breaking Changes ### Added `parse_str` In toml11 v3, there was no function to directly parse a string itself. Therefore, when parsing a string, it was necessary to use `std::istringstream`. To address this inconvenience, we have added `toml::parse_str`, allowing for direct parsing of strings. ### Added `try_parse` In toml11 v3, when a parser encountered an error, it threw `toml::syntax_error`. However, there are cases where you do not want to throw exceptions due to environments where exceptions cannot be thrown, or for reasons of performance. In toml11 v4, we have implemented `toml::try_parse` using `toml::result`, which communicates parsing failures without throwing exceptions. This doesn't mean exceptions are never thrown. Errors in the standard library being used, such as `std::bad_alloc` due to allocation failure from memory exhaustion, may still be thrown. ### Support for Parsing Byte Sequences To allow for parsing TOML content obtained through means other than files, we have added `toml::parse` and `toml::try_parse` functions that accept `std::vector`. ### Added `toml::spec` In toml11 v3, all new features of the TOML language were incorporated, and features that were decided to be introduced in future versions of the TOML language were controlled by the macro `TOML11_USE_UNRELEASED_TOML_FEATURES`. This was because, at the time of developing toml11 v3, the TOML language had not yet reached version 1.0.0, being at versions 0.4.0 to 0.5.0. As not all users are familiar with the latest TOML language specification, displaying error messages based on an older language usage could confuse the entire community. Therefore, until reaching version 1.0.0, it was necessary to provide new language specifications as quickly as possible and encourage users to update. However, the current TOML language specification is at version 1.0.0. Therefore, there was a need to be mindful of the choice to continue using version 1.0.0 even after TOML v1.1.0 was released. To allow for more flexibility in selecting the TOML language specification, we introduced `toml::spec`, enabling the TOML language version to be changed at runtime. Additionally, in `toml::spec`, flags are set for each language feature, allowing for the testing of specific language features only. This mechanism is also used for TOML-specific language extensions in toml11 v4. ### Added Format Information In toml11 v3, format information was not saved except for strings, and during serialization, only width and precision were considered. However, this resulted in hexadecimal integers being serialized as decimal integers and no reliable way to ensure inline tables. In toml11 v4, format information (`integer_format`, etc.) has been added to all TOML types, and it is now considered during parsing and serialization. This allows for more detailed format information to be set for values, such as hexadecimal integers or inline tables. ### Changed to `preserve_comments` by Default In toml11 v3, comments were not parsed by default and were also not serialized. This was because comments were a later introduced feature and were being read through a special hack. In toml11 v4, comments are parsed, preserved, and serialized by default. Furthermore, the parser implementation has been significantly changed to ensure comments are parsed alongside other elements. ### Changed to Preserve Comments by Default In toml11 v3, comments were neither parsed nor serialized by default. This was because comment support was a late addition, implemented through a special hack. In toml11 v4, comments are now parsed, preserved, and serialized by default. The parser implementation has also been significantly revised so that comments are parsed just like any other element. ### Added `single_include/toml.hpp` toml11 is a versatile library with different header files for various features to enhance development efficiency. However, this required a certain amount of effort to install. Starting with toml11 v4, a `single_include/toml.hpp` file has been added, which combines all the header files in the correct order. This allows the library to be installed by simply copying a single file. ### Option to Use Precompiled Library Due to the extensive use of templates in toml11, compile times have been long. In toml11 v4, the number of precompilable functions has been increased, allowing them to be compiled ahead of time into a library. This is expected to reduce compile times when using the library in large-scale development projects. ### Reference Documentation Previously, all features were documented in the README, with no detailed function definitions or reference materials available in Japanese. In toml11 v4, reference documentation has been included, provided in both Japanese and English. However, since the library author is a native Japanese speaker, the Japanese content is considered primary. If there are discrepancies between the Japanese and English content, the Japanese version takes precedence. toml11-4.1.0/docs/content.en/docs/features/000077500000000000000000000000001464712047700204165ustar00rootroot00000000000000toml11-4.1.0/docs/content.en/docs/features/_index.md000066400000000000000000000052011464712047700222040ustar00rootroot00000000000000+++ title = "features" type = "docs" weight = 2 bookCollapseSection = true +++ # Features This section explains the main features provided by toml11, with examples. ## [Parsing Files and Strings](parsing_files) Describes the functions for parsing files and strings, and how to handle the errors they produce. Includes: - Parsing files - Parsing strings - Parsing byte arrays - Parsing files without throwing exceptions - Parsing strings without throwing exceptions - Parsing byte arrays without throwing exceptions ## [Extracting Values from `toml::value`](value) Explains how to examine, extract, and convert the data types held by `toml::value`. Includes: - Checking the type of a value using member functions - Accessing values using member functions - Accessing comments - Handling inline tables and dotted keys - Handling date information - Using `toml::get` for conversion - Using `toml::get_or` to specify a fallback value - Using `toml::find` for searching and conversion - Using `toml::find_or` to specify a fallback value - Defining conversions with user-defined types - Applying functions with `toml::visit` - Constructing `toml::value` ## [Creating Error Messages](error_message) Explains how to generate error messages with location information from a TOML file using `toml::value`. Includes: - Extracting location information from `toml::value` - Constructing error messages - Adding color to the output ## [Serializing TOML Files](serialize) Describes how to format the values of `toml::value` and the available formatting options. Includes: - Specifying formats for each value of `toml::value` - Formatting `toml::value` into a string ## [Configuring Types of `toml::value`](configure_types) Explains how to customize the types stored in `toml::value` (such as `integer_type` and `table_type`). Includes: - Defining `type_config` - Using `ordered_type_config` - Disabling comment preservation - Using different containers like `std::deque` - Using different numeric types like `boost::multiprecision` ## [TOML Literals](literal) Explains the `_toml` literal for embedding TOML files directly in C++ code. Includes: - Using TOML literals ## [TOML Language Version](toml_spec) Describes the versions of the TOML language supported by toml11 and how to control language features added in TOML-v1.1.0. Includes: - Using TOML language version 1.1.0 - Using specific features of TOML language version 1.1.0 ## [TOML Language Extensions](extension) Explains the custom extensions to the TOML language provided by toml11. Includes: - Supporting `null` - Supporting hexadecimal format for floating-point numbers - Allowing units for numbers toml11-4.1.0/docs/content.en/docs/features/configure_types.md000066400000000000000000000165541464712047700241600ustar00rootroot00000000000000+++ title = "configuring types" type = "docs" weight = 50 +++ # Customizing Types The `toml::value` class uses `std::int64_t` for `integer_type` and `std::unordered_map` for `table_type`. However, in some cases, you may want to use `boost::multiprecision::int128_t` or `std::map`. To accommodate this, `toml::value` is implemented with template parameters that allow you to change the stored types. Just as `std::string` is actually an alias for `std::basic_string, std::allocator>`, `toml::value` is an alias for `toml::basic_value`. Here, we will explain the types contained in `toml::type_config` and how to define a different `config` type. ## `type_config` The `type_config` class contains the following member types and `static` member functions: ```cpp namespace toml { struct type_config { using comment_type = preserve_comments; using boolean_type = bool; using integer_type = std::int64_t; using floating_type = double; using string_type = std::string; template using array_type = std::vector; template using table_type = std::unordered_map; static result parse_int(const std::string& str, const source_location src, const std::uint8_t base); static result parse_float(const std::string& str, const source_location src, const bool is_hex); }; } ``` `toml::basic_value` defines the stored `boolean_type` as `TypeConfig::boolean_type`, the stored `integer_type` as `TypeConfig::integer_type`, and so on. Additionally, `array_type` is defined as `TypeConfig::array_type>` and `table_type` is defined as `TypeConfig::table_type>`. By passing a class that defines these member types and functions to `toml::basic_value`, you can customize the types used by that `toml::basic_value`. The `parse_int` and `parse_float` functions provide parsing methods when custom numeric types are used. These functions receive strings with prefixes like `0x` and digit separators like `_` removed, such as `123456` or `DEADBEEF`. The `base` parameter will be one of `10`, `16`, `8`, or `2`. Implement these functions to parse your custom `integer_type` and `floating_type`. As a default implementation, `toml::read_int` and `toml::read_float` are provided. ```cpp static result parse_int(const std::string& str, const source_location src, const std::uint8_t base) { return toml::read_int(str, src, base); } static result parse_float(const std::string& str, const source_location src, const bool is_hex) { return toml::read_float(str, src, is_hex); } ``` The `read_int` function uses `istream` and employs `std::hex` and `std::oct` for hexadecimal and octal parsing, respectively. For binary parsing, it is implemented using multiplication and addition. If your type supports these operations, you can use `read_int` as-is. The `read_float` function also uses `istream`. Hexadecimal floating-point numbers are only supported for `double` and `float` types. If `read_float` is called with any other type and `hexfloat` is used, it will always return a parse error. Therefore, if you need to use a floating-point type other than `double` or `float` with `hexfloat`, you will need to implement support for that. If `hexfloat` is not used, no additional implementation is necessary. ## Preserving Order of Values in Tables In addition to the default `toml::type_config`, there is also `toml::ordered_type_config`. This changes the `table_type` to an [ordered_map]({{< ref "docs/reference/ordered_map" >}}). Using this, `toml::ordered_value` is defined, along with aliases for its array and table types as `toml::ordered_array` and `toml::ordered_table`, respectively. You can use `toml::ordered_value` by calling `toml::parse(...)` as `toml::parse(...)`. ## Not Preserving Comments The `type_config` defines a container for storing comments via `comment_type`. If comments do not contain significant information and can be discarded during parsing, specify `toml::discard_comments` for `comment_type`. ```cpp struct wo_comment_config { using comment_type = toml::discard_comments; // XXX using boolean_type = bool; using integer_type = std::int64_t; using floating_type = double; using string_type = std::string; template using array_type = std::vector; template using table_type = std::unordered_map; static result parse_int(const std::string& str, const source_location src, const std::uint8_t base) { return toml::read_int(str, src, base); } static result parse_float(const std::string& str, const source_location src, const bool is_hex) { return toml::read_float(str, src, is_hex); } }; ``` ## Using Containers Other Than `std::vector` for Arrays To use a container other than `vector` (e.g., `std::deque`) for implementing TOML arrays, modify `array_type` as follows. Similarly, to use a container other than `unordered_map` (e.g., `std::map`) for table types, modify `table_type` as shown below. ```cpp struct deque_map_config { using comment_type = toml::preserve_comments; using boolean_type = bool; using integer_type = std::int64_t; using floating_type = double; using string_type = std::string; template using array_type = std::deque; // XXX template using table_type = std::map; // XXX static result parse_int(const std::string& str, const source_location src, const std::uint8_t base) { return toml::read_int(str, src, base); } static result parse_float(const std::string& str, const source_location src, const bool is_hex) { return toml::read_float(str, src, is_hex); } }; ``` ## Using `boost::multiprecision` for Numeric Types By using `boost::multiprecision::cpp_int` and `boost::multiprecision::cpp_bin_float_oct`, you can utilize a wider integer type and a more precise floating-point type. These types implement stream operators, so you can use the default implementations of `read_int` and `read_float` without modification. ```cpp struct large_num_config { using comment_type = toml::preserve_comments; using boolean_type = bool; using integer_type = boost::multiprecision::cpp_int; using floating_type = boost::multiprecision::cpp_bin_float_oct; using string_type = std::string; template using array_type = std::vector; template using table_type = std::unordered_map; static toml::result parse_int(const std::string& str, const toml::source_location src, const std::uint8_t base) { return toml::read_int(str, src, base); } static toml::result parse_float(const std::string& str, const toml::source_location src, const bool is_hex) { return toml::read_float(str, src, is_hex); } }; ``` toml11-4.1.0/docs/content.en/docs/features/error_message.md000066400000000000000000000166601464712047700236060ustar00rootroot00000000000000+++ title = "error message" type = "docs" weight = 30 +++ # Outputting Error Messages `toml11` provides error messages that include location information within the file when using functions like `toml::parse`, `toml::get/find`, and `as_integer()`, among others. For instance, if a syntax error in an integer is detected during parsing, an error message might look like this: ``` [error] bad integer: `_` must be surrounded by digits --> internal string at line 64 in file main.cpp | 1 | a = 123__456 | ^-- invalid underscore Hint: valid : -42, 1_000, 1_2_3_4_5, 0xC0FFEE, 0b0010, 0o755 Hint: invalid: _42, 1__000, 0123 ``` Or, if a type different from the one actually stored is requested: ``` [error] toml::value::as_string(): bad_cast to string --> input.toml | 1 | a = 123_456 | ^^^^^^^-- the actual type is integer ``` `toml11` provides methods to create such error messages from `toml::value`. By utilizing this feature, you can inform users not only about TOML syntax errors but also about application-specific errors. For example, if a negative number appears where only positive values are allowed, you can highlight the location within the TOML file to convey the error to the user. ## Creating Error Messages from `toml::value` Location Information `toml::value` retains information about the location where it was parsed. This information is encapsulated in `toml::source_location` and can be retrieved using `toml::value::location()`. ```cpp const toml::value& a = input.at("a"); const toml::source_location src = a.location(); ``` When a file is parsed with `toml::parse`, the TOML filename and line numbers are stored. If parsed with `toml::parse_str`, the TOML filename is not available, but instead, the filename and line number of the C++ source code that called `toml::parse_str` are stored as the TOML filename. The first example on this page was output from `toml::parse_str`. Note the filename part. For details, see the [reference]({{}}). You can build error information by passing a `toml::source_location` or `toml::value` and the associated error message to `toml::make_error_info`. Passing this to `toml::format_error` formats the error message into a `std::string`. ```cpp const toml::value& a = input.at("a"); if(a.as_integer() < 0) { const toml::error_info err = toml::make_error_info( "positive integer is required", // Error title a, "but got negative value" // Message next to the value ); std::cerr << toml::format_error(err) << std::endl; } ``` This will output: ``` [error] positive integer is required --> input.toml | 1 | a = -123456 | ^^^^^^^-- but got negative value ``` You can also add a supplementary message at the end. This part is not indented. ```cpp const toml::value& a = input.at("a"); if(a.as_integer() < 0) { const toml::error_info err = toml::make_error_info( "positive integer is required", // Error title a, "but got negative value", // Message next to the value "Hint: `a` means length of the data" // Supplementary message ); std::cerr << toml::format_error(err) << std::endl; } ``` This will output: ``` [error] positive integer is required --> input.toml | 1 | a = -123456 | ^^^^^^^-- but got negative value Hint: `a` means length of the data ``` {{}} The ability to output lines from the file using `toml::value` is because the parsed file is retained in memory as a string. The parsed string is shared by `toml::value` via a `std::shared_ptr`. Copying it does not duplicate the entire file string. The file information is freed from memory when all `toml::value` instances constructed from parsing the file are destructed. Therefore, when using this in an application, it is recommended to extract and store the required values during loading rather than directly storing `toml::value`. {{}} ## Adding Colors to Strings You can add color to error messages using ANSI escape codes. If `TOML11_COLORIZE_ERROR_MESSAGE` is defined at compile time, the error messages output by toml11 will be colored by default. If not, you can enable color for subsequent error messages by calling `toml::color::enable()`. Conversely, if you do not want colored output, for example, because the output is not to a console, call `toml::color::disable()`. You can check whether coloring is enabled at any point by calling `toml::color::should_color()`. Additionally, while the error title, error message, and supplementary information are not colored by default, you can use manipulators from toml::color to add color to them. ```cpp std::ostringstream oss; oss << toml::color::red << "but got negative value"; const toml::error_info err = toml::make_error_info( "positive integer is required", // Error title a, oss.str(), // Message next to the value "Hint: `a` means length of the data" // Supplementary message ); ``` For more details, see the [reference]({{}}). ## Changing the Prefix of Error Messages from `[error]` There may be different types of errors, and the default `[error]` prefix might not always be appropriate. With `toml::format_error`, you can provide a `std::string` before `toml::error_info` to replace the `[error]` prefix. For example: ```cpp const toml::value& a = input.at("a"); if(a.as_integer() < 0) { const toml::error_info err = toml::make_error_info( "positive integer is required", // Error title a, "but got negative value" // Message next to the value ); std::ostringstream prefix; prefix << toml::color::bold << toml::color::yellow << "[warn]"; std::cerr << toml::format_error(prefix.str(), err) << std::endl; return 0; } else { return a.as_integer(); } ``` This will output a warning starting with `[warn]`. Additionally, you can create error messages without the `[error]` prefix by directly passing the components of `error_info` to `toml::format_error`. ```cpp const toml::value& a = input.at("a"); if(a.as_integer() < 0) { std::cerr << toml::format_error( "[warn] positive integer is required", // Error title a, "but got negative value" // Message next to the value ) << std::endl; return 0; } else { return a.as_integer() } ``` ## Creating Error Messages Referencing Multiple `toml::value` In application settings, the range of permissible values might change based on previously read values. In such cases, you may want to output the values causing the error simultaneously. `toml::format_error` and `toml::make_error_info` can take multiple pairs of `toml::value` and their corresponding error messages as `std::string`. ```cpp std::cerr << toml::format_error( "[error] invalid range", a, "minimum value is defined here", b, "maximum value is defined here", c, "and it exceeds the range" ) << std::endl; ``` You can also add supplementary information at the end. ```cpp std::cerr << toml::format_error( "[error] invalid range", a, "minimum value is defined here", b, "maximum value is defined here", c, "and it exceeds the range", "Hint: all the values must be in the range [a, b)" ) << std::endl; ``` When passing `toml::value` or `toml::source_location`, an error message related to it must follow. If not, it will result in a very confusing compilation error. toml11-4.1.0/docs/content.en/docs/features/extension.md000066400000000000000000000070101464712047700227520ustar00rootroot00000000000000+++ title = "extension" type = "docs" weight = 80 +++ # TOML Language Extensions The TOML language is currently at version v1.0.0, but several new features have been discussed and merged, with ongoing discussions for v1.1.0. Among the proposed features, some were deemed to have limited use cases, some faced implementation challenges in their proposed form, and others were not adopted at all. In toml11, we have experimentally implemented a selection of these features. Please note that these features are supported in toml11 but are not supported by other parsers and are unlikely to be supported in the future. Additionally, these features are disabled by default. To use them, you must explicitly set the corresponding feature flags to `true`. This design choice ensures that non-standard features are only used intentionally. Some of these features may eventually be merged into the TOML language itself. If a feature is officially adopted, the corresponding experimental implementation in toml11 may be removed in a minor version update after the official feature is implemented. ## `null` This feature allows the use of `null` as a value in TOML files. ``` a = null b = [ 1, 2, 3, null, 5] ``` To enable this, set the `ext_null_value` flag in `toml::spec` to `true`. When parsed, it will be treated as `toml::value_t::empty`, similar to a default-constructed value. However, the location information within the file will be set. `null` is parsed only in the context of values. Therefore, if `null` is used as a key, it will be interpreted as the string `"null"`, as it has been in the standard TOML. ```cpp #include int main() { toml::spec spec; spec.ext_null_value = true; const auto v = toml::parse_str("a = null", spec); assert(v.at("a").is_empty()); assert(v.at("a").is(toml::value_t::empty)); return 0; } ``` ## Hexadecimal Format for Floating-Point Numbers This feature allows the use of hexadecimal format for floating-point numbers in TOML files. ``` a = 0x1.91eb851eb851fp+1 # 3.14 ``` To enable this, set the `ext_hex_float` flag in `toml::spec` to `true`. The format follows the `printf` specification for `%a/%A`. ```cpp #include int main() { toml::spec spec; spec.ext_hex_float = true; const auto v = toml::parse_str("a = 0x1.91eb851eb851fp+1", spec); assert(v.at("a").is_floating()); assert(v.at("a").as_floating() == 3.14); return 0; } ``` ## Suffixes for Integers and Floating-Point Numbers This feature allows the use of suffixes for numbers in TOML files. It can be applied to integers and floating-point numbers in decimal notation. This is particularly useful for displaying units. ``` a = 86_400_sec b = 3.1416_rad c = 10_μm ``` However, these are purely suffixes and do not perform any unit conversion. If unit conversion is needed, users should implement it by referencing the suffix. To enable this, set the `ext_num_suffix` flag in `toml::spec` to `true`. The suffix must be separated from the number by an underscore (`_`). For clarity, the suffix cannot start with a digit. ``` distance = 100_m # valid distance = 10_0m # invalid distance = 10_0_m # valid ``` The suffix is stored as `std::string suffix` in the format information. ```cpp #include int main() { toml::spec spec; spec.ext_hex_float = true; const auto v = toml::parse_str("a = 86_400_sec", spec); assert(v.at("a").is_integer()); assert(v.at("a").as_integer() == 86400); assert(v.at("a").as_integer_fmt().suffix == "sec"); return 0; } ``` toml11-4.1.0/docs/content.en/docs/features/literal.md000066400000000000000000000031521464712047700223750ustar00rootroot00000000000000+++ title = "toml literal" type = "docs" weight = 60 +++ # `_toml` Literal With the `""_toml` literal, you can format TOML files inline. ```cpp #include int main() { using namespace toml::literals::toml_literals; const auto v = "a = 42"_toml; assert(v.at("a").as_integer() == 42); return 0; } ``` When including line breaks, raw string literals come in handy. ```cpp #include int main() { using namespace toml::literals::toml_literals; const auto v = R"( a = 42 b = "foo" )"_toml; assert(v.at("a").as_integer() == 42); assert(v.at("b").as_string() == "foo"); return 0; } ``` If a value is written on its own, that value is returned. ```cpp #include int main() { using namespace toml::literals::toml_literals; const auto a = "42"_toml; const auto b = "12:34:56"_toml; assert(a.as_integer() == 42); assert(b.as_local_time().hour == 12); assert(b.as_local_time().minute == 34); assert(b.as_local_time().second == 56); return 0; } ``` TOML allows keys consisting solely of numbers. Therefore, `[1]` is a valid table name. When there's ambiguity between table definitions and arrays, table definitions take precedence. To interpret as an array, please use a trailing comma. ```cpp #include int main() { using namespace toml::literals::toml_literals; const auto t = "[1]"_toml; // {1 = {}} const auto a = "[1,]"_toml; // [1,] assert(t.is_table()); assert(t.at("1").is_table()); assert(a.is_array()); assert(a.at(0).as_integer() == 1); return 0; } ``` toml11-4.1.0/docs/content.en/docs/features/parsing_files.md000066400000000000000000000211361464712047700235700ustar00rootroot00000000000000+++ title = "parsing files" type = "docs" weight = 10 +++ # Parsing Files and Strings In toml11, you can parse files, strings, and byte arrays using `toml::parse` or `toml::try_parse`. Upon success, these functions return a `toml::value`. Although the parsed file is always a table, the return type is not `toml::table`. This is because `toml::value` contains metadata about the file, whereas `toml::table` is merely an alias for `std::unordered_map`. To include metadata, a `toml::value` is returned instead of a `toml::table`. The `toml::value` corresponding to the root of the file will always hold a `table_type`. ## Parsing Files To parse files, use either [`toml::parse`]({{< ref "docs/reference/parser#parse" >}}) or [`toml::try_parse`]({{< ref "docs/reference/parser#try_parse" >}}). ### `toml::parse` #### Specifying the Filename with `std::string` [`toml::parse`]({{< ref "docs/reference/parser#parse" >}}) accepts a filename as a string, opens the file, and parses it. The following sample code parses a file named `input.toml`, extracts the `title` variable as a string, and prints it. ```cpp #include #include int main() { const toml::value input = toml::parse("input.toml"); std::cout << input.at("title").as_string() << std::endl; return 0; } ``` #### Specifying a File with `std::filesystem::path` [`toml::parse`]({{< ref "docs/reference/parser#parse" >}}) can accept a `std::filesystem::path`. This requires C++17 or later, as it relies on the `` support. #### Specifying an Input Stream with `std::istream` [`toml::parse`]({{< ref "docs/reference/parser#parse" >}}) can also accept an `std::istream`. Open a stream in binary mode by passing `std::ios::binary` to avoid inconsistency between the file size and the number of characters due to automatic conversion of newline characters by the standard library. Without the filename information, error messages will display `"unknown file"`. To avoid this, you can pass the filename as a `std::string` in the second argument when using `std::istream`. You can use streams other than `std::ifstream`, such as `std::istringstream`. Note that the entire content is readable at the time of the call. ```cpp #include #include int main() { std::string filename("input.toml"); std::ifstream ifs(filename); const toml::value input = toml::parse(ifs, filename); std::cout << input.at("title").as_string() << std::endl; return 0; } ``` #### Specifying a File with `FILE*` [`toml::parse`]({{< ref "docs/reference/parser#parse" >}}) can also accept a `FILE*`. Open a stream in binary mode by passing `"rb"` to avoid inconsistency between the file size and the number of characters due to automatic conversion of newline characters by the standard library. As with `std::istream`, you need to provide the filename as a string in the second argument. When passing a `FILE*`, if the file read fails, `errno` will be reported. #### Error Handling [`toml::parse`]({{< ref "docs/reference/parser#parse" >}}) throws a [`toml::syntax_error`]({{< ref "docs/reference/parser#syntax_error" >}}) if it encounters a syntax error. [`toml::syntax_error`]({{< ref "docs/reference/parser#syntax_error" >}}) is derived from [`toml::exception`]({{< ref "docs/reference/exception" >}}), which in turn is derived from `std::exception`. Therefore, you can use the `what()` member function to retrieve the error message from a [`toml::syntax_error`]({{< ref "docs/reference/parser#syntax_error" >}}). Additionally, [`toml::syntax_error`]({{< ref "docs/reference/parser#syntax_error" >}}) contains a [`std::vector`]({{< ref "docs/reference/error_info" >}}), which can be accessed using the `errors()` member function. `toml::parse` attempts to recover from minor errors and report multiple errors whenever possible. While it can often recover from simple errors like number format issues, errors within arrays or tables might not be recoverable and may lead to multiple similar error reports. If you find the error messages redundant, you can use only the `front()` of the `std::vector` to get a message about the most critical issue. ```cpp #include int main() { try { const toml::value input = toml::parse("input.toml"); std::cout << input.at("title").as_string() << std::endl; } catch(const toml::syntax_error& err) { // report all the errors std::cerr << err.what() << std::endl; // report the first error only std::cerr << err.errors().front() << std::endl; } } ``` ### `toml::try_parse` While `toml::parse` throws an exception on failure, `toml::try_parse` does not throw exceptions when it fails. Instead, its return type is [`toml::result>`]({{}}). The [`result`]({{}}) type holds either a success value or a failure value, similar to Rust's `Result` or Haskell's `Either`. ```cpp #include int main() { const auto parse_result = toml::try_parse("input.toml"); if(parse_result.is_ok()) { std::cout << parse_result.unwrap().at("title").as_string() << std::endl; } else { std::cerr << parse_result.unwrap_err().at(0) << std::endl; } return 0; } ``` To check which value the [`result`]({{}}) type holds, use the `is_ok()` and `is_err()` functions. The success or failure value can be retrieved using `unwrap()` and `unwrap_err()`, respectively. If `unwrap` fails, it throws a `bad_result_access` exception. Using the `as_ok()` and `as_err()` functions does not throw exceptions on failure, but results in undefined behavior. {{}} Although `try_parse` does not throw `syntax_error` or `file_io_error`, it returns the same `toml::error_info` as the failure type in the `result`. However, it is not entirely exception-free. If an internal standard library error occurs, such as `std::bad_alloc` when a `vector` fails to allocate memory, `toml::try_parse` does not catch this and will let it propagate. Thus, exceptions originating from the standard library may still be thrown. {{}} ## Parsing Strings ### `toml::parse_str` [`toml::parse_str`]({{}}) accepts the string to be parsed directly, instead of a filename. For the part of the error message that corresponds to the TOML file's name, if the `std::source_location` equivalent compiler extension is available, the name and line number of the C++ file that called `parse_str` will be used instead. ```cpp #include #include int main() { const toml::value input = toml::parse_str("title = \"parse_str\""); std::cout << input.at("title").as_string() << std::endl; return 0; } ``` ### `toml::try_parse_str` [`toml::try_parse_str`]({{}}) also takes the string to be parsed directly, similar to `parse_str`. Like `try_parse`, it uses [`toml::result`]({{}}) to report errors. ```cpp #include #include int main() { const auto parse_result = toml::try_parse_str("title = \"parse_str\""); if(parse_result.is_ok()) { std::cout << parse_result.unwrap().at("title").as_string() << std::endl; } else { std::cerr << parse_result.unwrap_err().at(0) << std::endl; } return 0; } ``` ## Parsing Byte Arrays It is also possible to parse byte arrays instead of files. Since the byte arrays must be encoded in UTF-8, `unsigned char` is used. ### `toml::parse(std::vector)` The behavior is the same as [`toml::parse`]({{}}). When parsing byte arrays, a `filename` is required. ```cpp #include #include int main() { std::vector bytes{/* ... */}; const toml::value input = toml::parse(bytes, "internal bytes"); std::cout << input.at("title").as_string() << std::endl; return 0; } ``` ### `toml::try_parse(std::vector)` The behavior is the same as [`toml::try_parse`]({{}}). When parsing byte arrays, a `filename` is required. ```cpp #include #include int main() { std::vector bytes{/* ... */}; const auto parse_result = toml::try_parse(bytes, "internal bytes"); if(parse_result.is_ok()) { std::cout << parse_result.unwrap().at("title").as_string() << std::endl; } else { std::cerr << parse_result.unwrap_err().at(0) << std::endl; } return 0; } ``` toml11-4.1.0/docs/content.en/docs/features/serialize.md000066400000000000000000000143471464712047700227400ustar00rootroot00000000000000+++ title = "serializing values" type = "docs" weight = 40 +++ # Outputting TOML Files Using `toml::format`, you can convert a `toml::value` to a string. ```cpp #include #include int main() { const toml::value v(toml::table{ {"a", 42}, {"b", "foo"}, }); const std::string s = toml::format(v); const toml::value u = toml::parse_str(s); assert(u.at("a").as_integer() == 42); assert(u.at("b").as_string() == "foo"); return 0; } ``` If the `toml::value` contains a `table_type`, it is interpreted as the root table of the file. If a `toml::value` containing anything other than `table_type` is passed, only that value is formatted. Certain format specifications may require a key to be provided for formatting. For example, `toml::array_format::array_of_tables` formats as `[[array.of.tables]]`, which requires key access. If a format specification that requires a key is provided without a key, a `toml::serialization_error` is thrown. Additionally, if there are values that contradict the format specification, a `toml::serialization_error` is thrown. For instance, specifying `integer_format::hex` for a negative integer, or `string_format::literal` for a string containing newlines, will cause an error. The method for specifying formats is explained later. ## Outputting with Keys You can pass a key to `toml::format` as a `std::string`. In this case, the key is considered to be under the root table, and the passed value corresponds to that key. For nested keys, you can pass a `std::vector`. ```cpp #include #include int main() { const toml::value v(toml::table{ {"a", 42}, {"b", "foo"}, }); const std::string s = toml::format("bar", v); const toml::value u = toml::parse_str(s); assert(u.at("bar").at("a").as_integer() == 42); assert(u.at("bar").at("b").as_string() == "foo"); return 0; } ``` ## Specifying Formats Each type in `toml::value` has a corresponding format information type. For `toml::value::integer_type`, there is `toml::integer_format_info`. For `toml::value::table_type`, there is `toml::table_format_info`. These format information types are set when parsing and are retained even if the value is changed, as long as the type remains the same. You can access and directly edit these formats using member functions like `as_integer_fmt()` or `as_table_fmt()`. Below are some examples explaining how to use these formats. For more details on how to access formats, refer to the [`toml::value` reference]({{< ref "/docs/reference/value" >}}). For a complete list and detailed information on format information classes, see the [format reference]({{< ref "/docs/reference/format" >}}). ### Specifying Integer Formats For integers, you can specify the radix, width, and the position of `_`. When using `hex`, `oct`, or `bin`, values are padded with zeros until the specified width is reached. For `dec`, the width specification adds spaces, which are not parsed. For more details, see the [integer format reference]({{< ref "/docs/reference/format#integer_format" >}}). ### Single-Line and Multi-Line Arrays For arrays, you can specify `toml::array_format::oneline` or `toml::array_format::multiline`. ```toml # oneline a = [1, 2, 3, 4, 5] # multiline a = [ 1, 2, 3, 4, 5 ] ``` When using `multiline`, you can specify the indentation. Each element is indented by the amount specified in `body_indent`, and the closing bracket `]` is indented by the amount specified in `closing_indent`. The type of character used for indentation is specified by `indent_type`, and you can choose between `toml::indent_char::space` or `toml::indent_char::tab`. {{}} Ensure that the same type of character is used for indentation throughout the document. If different types of characters are specified for indentation within the same file, the result is undefined. Some form of indentation will be applied, but the type of character and the depth of the indentation may be inconsistent. {{}} If all elements of an `array` have `table_type`, you can specify `toml::array_format::array_of_tables`. If you do not specify `array_of_tables` and use `multiline`, the tables will be formatted as inline tables. ```toml # multiline a = [ {foo = 42}, {bar = "hoge"}, ] # array_of_tables [[a]] foo = 42 [[a]] bar = "hoge" ``` By default, `toml::array_format::default_format` is used. This automatically selects an appropriate format. For example, with `default_format`, if all elements are `table_type`, it will choose `array_of_tables`. Short arrays are formatted as `oneline`, while long or nested arrays, or those with complex elements, are formatted as `multiline`. For more details, see the [array format reference]({{< ref "/docs/reference/format#array_format" >}}). ### Inline Tables To format a table as an inline table, specify `toml::table_format::oneline`. For standard tables, use `toml::table_format::multiline`. ```toml oneline = {a = 42, b = "foo"} [multiline] a = 42 b = "foo" ``` In TOML v1.1.0, line breaks within inline tables are allowed. In this case, use `toml::table_format::multiline_oneline`. This is only applied if the corresponding feature flag is set to `true` as per the TOML version specification described later. ```toml multiline_oneline = { a = 42, b = "foo" } ``` For more details, see the [table format reference]({{< ref "/docs/reference/format#table_format" >}}). ## Specifying the TOML Language Version for Output Certain language features, such as line breaks within inline tables and `\x` escape sequences, are only available after TOML v1.1.0. The `toml::format` function accepts a `toml::spec` as its argument. This allows you to specify the version of TOML to use during serialization. When you use `toml::parse` with a `toml::spec` to leverage new features, the parsed values may contain format information that is only compatible with that specific version. Ensure that you pass the same `toml::spec` to `toml::format` to maintain compatibility. ```cpp #include #include int main() { const auto spec = toml::spec::v(1, 1, 0); const toml::value v = toml::parse("input.toml", spec); std::cout << toml::format(v, spec) << std::endl; return 0; } ``` toml11-4.1.0/docs/content.en/docs/features/toml_spec.md000066400000000000000000000053651464712047700227360ustar00rootroot00000000000000+++ title = "toml spec" type = "docs" weight = 70 +++ # TOML Language Version You can specify the version of the TOML language and individual feature flags to use with `toml::parse` or `toml::format` through [`toml::spec`](docs/reference/spec#tomlspec). ## Specifying TOML Version You can construct a [`toml::spec`](docs/reference/spec#tomlspec) from [`toml::semantic_version`](docs/reference/spec#tomlsemantic_version). ```cpp #include int main() { toml::spec spec(toml::semantic_version(1, 1, 0)); return 0; } ``` However, to make this shorter, the `toml::spec::v()` function is provided. ```cpp #include int main() { toml::spec spec = toml::spec::v(1, 1, 0); return 0; } ``` If not specified explicitly, `toml::spec::default_version()` is used to construct with default values. The default value depends on the version of toml11 and follows the latest version of the TOML language released at that time. As of v4.0.0, TOML v1.1.0 has not been released yet, so the default TOML version is v1.0.0. {{}} Some features of TOML v1.1.0 are still under fairly lengthy discussion and may still be reverted. If they are indeed reverted, toml11 will remove those features in a minor version upgrade or move them to a corresponding later version. As such, any features related to future versions should be considered unstable. {{}} ### Parsing with Version Specification The overload of [`toml::parse`](docs/reference/parser) takes a `toml::spec` following the file name. This allows you to change the TOML version being used. ```cpp #include int main() { toml::value input = toml::parse("input.toml", toml::spec::v(1, 1, 0)); return 0; } ``` ### Serializing with Version Specification The overload of [`toml::format`](docs/reference/serializer) takes a `toml::spec` following the `toml::value`. This allows you to change the TOML version being used. ```cpp #include int main() { toml::value v = toml::parse("input.toml", toml::spec::v(1, 1, 0)); std::cout << toml::format(v, toml::spec::v(1, 1, 0)) << std::endl; return 0; } ``` If a format is passed that is not permitted by the provided `toml::spec`, it will be ignored, and another format will be used as a fallback. ## Specifying Newly Added Features in TOML With version upgrades in TOML, multiple new features are introduced, and it's possible to enable only some of them. ```cpp #include int main() { toml::spec spec = toml::spec::v(1, 0, 0); // Allowing newlines in inline tables spec.v1_1_0_allow_newlines_in_inline_tables = true; toml::value input = toml::parse("input.toml", spec); return 0; } ``` For a full list of all flags, refer to [`toml::spec`](docs/reference/spec#tomlspec). toml11-4.1.0/docs/content.en/docs/features/value.md000066400000000000000000000544501464712047700220640ustar00rootroot00000000000000+++ title = "getting values" type = "docs" weight = 20 +++ # Retrieving Values This section explains how to access the values stored in `toml::value`. ## Accessing Values Using Member Functions ### `is_something` and `as_something` `toml::value` has member functions like `is_boolean()` and `is_integer()` which allow you to check the type of the stored value. Additionally, it has member functions like `as_boolean()` and `as_integer()` that allow you to access the value of that type. For a complete list, refer to the [`toml::value` reference]({{}}). ```cpp toml::value v = /* ... */; if(v.is_string()) { std::cout << v.as_string() << std::endl; } ``` If the stored value is of a different type than specified, a [`toml::type_error`]({{}}) is thrown. The `what()` method will contain a message like the following: ``` [error] toml::value::as_string(): bad_cast to string --> input.toml | 1 | a = 123_456 | ^^^^^^^-- the actual type is integer ``` ### `toml::value_t` Type information can be identified using [`enum class toml::value_t`]({{}}). The [`type()`]({{}}) member function returns the type information of the currently stored value. ```cpp toml::value v = /* ... */; switch(v.type()) { case toml:value_t::empty : { /*...*/ break; } case toml:value_t::boolean : { /*...*/ break; } case toml:value_t::integer : { /*...*/ break; } case toml:value_t::floating : { /*...*/ break; } case toml:value_t::string : { /*...*/ break; } case toml:value_t::offset_datetime: { /*...*/ break; } case toml:value_t::local_datetime : { /*...*/ break; } case toml:value_t::local_date : { /*...*/ break; } case toml:value_t::local_time : { /*...*/ break; } case toml:value_t::array : { /*...*/ break; } case toml:value_t::table : { /*...*/ break; } default: {break;} } ``` The [`is(toml::value_t)`]({{}}) member function returns `true` if the stored value is of the given `value_t` type, otherwise it returns `false`. ```cpp toml::value v = /* ... */; if(v.is(toml::value_t::integer)) { std::cout << v.as_integer() << std::endl; } ``` ### `at`, `[]`, `contains`, `size`, `push_back`, `emplace_back` `toml::value` provides some member functions similar to those of standard library containers. These functions internally convert `toml::value` to the corresponding type and call the respective member functions. #### `at(std::size_t i)`, `operator[](std::size_t i)` These are equivalent to `as_array().at(i)` and `as_array()[i]`. `toml::value` uses `std::vector` as `array_type` by default, so `at` throws `std::out_of_range` on error, while `operator[]` results in undefined behavior. ```cpp toml::value v(toml::array{1,2,3}); std::cout << v.at(1); ``` If the stored type is not `array_type`, a `type_error` is thrown. #### `at(std::string key)`, `operator[](std::string key)` These are equivalent to `as_table().at(key)` and `as_table()[key]`. `toml::value` uses `std::unordered_map` as `table_type` by default, so if the corresponding value does not exist, `at` throws `std::out_of_range`, while `operator[]` constructs a new `toml::value` and returns a reference to it. Therefore, there is no `const` version of `operator[]`. ```cpp toml::value v(toml::table{}); v["a"] = 42; ``` If the stored type is not `table_type`, a `type_error` is thrown. #### `size()` Returns the length. For `array_type` or `table_type`, it returns the number of elements; for `string_type`, it returns the number of characters (in bytes). If the stored type is none of these, a `type_error` is thrown. #### `push_back()`, `emplace_back()` These are equivalent to `as_array().push_back()` and `as_array().emplace_back()`. If the stored type is not `array_type`, a `type_error` is thrown. ## Accessing Comments In toml11, comments are parsed by default and saved line by line with the corresponding value. A comment corresponds to the value that comes immediately after the consecutive lines of comments, or to the value on the same line as the comment. If there is no value immediately before or after the comment, it is not associated with any value and is ignored. ```toml # input.toml # This is a comment about a. a = 42 b = 3.14 # This is a comment about b. # This comment is ignored because it has no corresponding value. # This is the 1st comment about c. # This is the 2nd comment about c. c = "foo" # This is the final comment about c. # This comment is NOT a comment about c. ``` Comments corresponding to a value can be accessed using the `comments()` member function of `toml::value`. `comments()` returns a container that has the same member functions as `std::vector`. ```cpp const auto v = toml::parse("input.toml"); const auto& a = v.at("a"); const auto& b = v.at("b"); const auto& c = v.at("c"); assert(a.comments().size() == 1); assert(a.comments().at(0) == "# This is a comment about a."); assert(b.comments().size() == 1); assert(b.comments().at(0) == "# This is a comment about b."); assert(c.comments().size() == 3); assert(c.comments().at(0) == "# This is the 1st comment about c."); assert(c.comments().at(1) == "# This is the 2nd comment about c."); assert(c.comments().at(2) == "# This is the final comment about c."); ``` Comments related to the root table of the entire file are written at the beginning of the file. ```toml # This is a comment about the root table. # This is also a comment about the root table. # This comment is ignored. # This is a comment about a. a = 42 ``` However, if a value immediately follows the initial comment, the comment is interpreted as pertaining to that value, and there are no comments for the root table. ```toml # This is a comment about a. # This is also a comment about a. a = 42 ``` ## Handling Inline Tables and Dotted Keys An inline table is simply a table, and there is no difference in handling it compared to other tables in C++ code. ```toml a = {b = 42, c = "foo"} ``` Dotted keys are also just tables, with no difference in handling compared to other tables in C++ code. ```toml a.b = 42 a.c = "foo" ``` These TOML files are identical to the following file. ```toml [a] b = 42 c = "foo" ``` Thus, they can all be processed with the exact same code. ```cpp const auto input = toml::parse("input.toml"); assert(input.at("a").at("b").as_integer() == 42); assert(input.at("a").at("c").as_string() == "foo"); ``` However, it is possible to distinguish them based on format information. ```cpp const auto input = toml::parse("input.toml"); switch(input.at("a").as_table_fmt().fmt) { case toml::table_format::oneline: { std::cout << "inline table" << std::endl; break; } case toml::table_format::multiline: { std::cout << "normal table" << std::endl; break; } case toml::table_format::dotted: { std::cout << "dotted keys" << std::endl; break; } } ``` This format information is also considered during serialization, which will be described later. ## Handling Date and Time Information [`local_date`]({{}}), [`local_time`]({{}}), [`local_datetime`]({{}}), and [`offset_datetime`]({{}}) are parsed into dedicated structures with corresponding member variables in toml11. When using these, you can directly extract the values or use `toml::get` and `toml::find` to convert them into types such as `std::chrono::system_clock::time_point` or `std::tm`. ## Converting Using `toml::get` `toml::get` is a function that converts and retrieves the value stored in `toml::value`. Specify the desired target type as `T`. ```cpp const toml::value v = /*...*/; std::cout << toml::get(v) << std::endl; ``` The `toml::find` function, described later, also has the same functionality for type conversion. If an unsupported type is specified for the stored value, a `toml::type_error` is thrown. ### Simple Conversions #### boolean_type Conversion from `boolean_type` is possible only to `bool`. #### integer_type Any type for which `std::is_integral` is `true`, except `bool`, can be converted from `integer_type`. ```cpp toml::value v(42); const auto u32 = toml::get(v); const auto i16 = toml::get(v); ``` #### floating_type Any type for which `std::is_floating_point` is `true` can be converted from `floating_type`. ```cpp toml::value v(3.14); const auto f64 = toml::get(v); const auto f32 = toml::get(v); ``` #### string_type `string_type` can be converted to `std::string`. From C++17 onwards, it can also be converted to `std::string_view`. ```cpp toml::value v("foo"); const auto s = toml::get(v); // C++17 const auto sv = toml::get(v); ``` #### datetime variants [`local_date`]({{}}), [`local_datetime`]({{}}), and [`offset_datetime`]({{}}) represent specific dates and times, so they can be converted to `std::chrono::system_clock::time_point`. However, since [`local_time`]({{}}) does not include date information, it supports conversion to `std::chrono::duration` as the elapsed time from `00:00.00`. Additionally, `local_date` and `local_datetime` conversions take the executing machine's timezone into account. ```toml date = 2024-01-23 time = 12:30:00 l_dt = 2024-01-23T12:30:00 o_dt = 2024-01-23T12:30:00+09:00 ``` ```cpp const auto input = toml::parse("input.toml"); const auto date = toml::get(input.at("date")); const auto l_dt = toml::get(input.at("l_dt")); const auto o_dt = toml::get(input.at("o_dt")); const auto time = toml::get(input.at("time")); // 12 * 60 + 30 min ``` ### Conditions for Obtaining References `toml::get` can return a reference if `T` is the exact type stored in `toml::value`. Conversely, if a conversion is necessary (e.g., extracting an integer stored as `std::int64_t` into `std::uint32_t`), it is not possible to return a reference to the converted type. When no conversion is needed, the returned reference can be used to modify the value. ```cpp toml::value v(42); toml::get(v) = 6 * 9; assert(v.as_integer() == 54); ``` ### Converting Arrays to STL Containers If all elements in an array have the same type and can be converted to `T`, it is possible to convert them to `std::vector`. ```toml a = [1, 2, 3, 4, 5] ``` ```cpp const auto a = toml::get>(input.at("a")); ``` Other STL containers can also be used. ```cpp const auto a1 = toml::get>(input.at("a")); const auto a2 = toml::get>(input.at("a")); const auto a3 = toml::get>(input.at("a")); ``` When converting to `std::array`, the number of elements must match. If they don't match, a `std::out_of_range` exception is thrown. Non-STL containers that have a default constructor and a `push_back` method can also be converted using `toml::get`. ```cpp const auto a = toml::get>(input.at("a")); ``` ### Converting Arrays to `std::pair` or `std::tuple` If an array contains elements of different types, it can be converted to `std::pair` or `std::tuple`. ```toml a = [true, 3.14] b = [42, 2.718, "foo"] ``` ```cpp const auto a = toml::get>(input.at("a")); const auto b = toml::get>(input.at("b")); ``` As with `std::array`, the length of the array must match the number of elements in the `std::pair` or `std::tuple`. If they don't match, a `std::out_of_range` exception is thrown. Additionally, each element must be convertible to the corresponding element in the `std::pair` or `std::tuple`. If conversion is not possible, a `toml::type_error` exception is thrown. ### Converting Nested Arrays Nested arrays can be converted to nested containers. ```toml a = [ [1, 2, 3], [4, 5, 6] ] ``` ```cpp const auto a = toml::get>>(input.at("a")); ``` If the types are different, `std::pair` or `std::tuple` can be useful. ```toml a = [ [1, 2, 3], ["foo", "bar"] ] ``` ```cpp const auto a = toml::get< std::pair, std::vector> >(input.at("a")); ``` ### Converting Tables to `std::map` If all values in a table have the same type, they can be converted to `std::map` or `std::unordered_map`. ```toml t = {a = 1, b = 2} ``` ```cpp const auto t = toml::get>(input.at("t")); ``` Non-STL containers that have a default constructor and an `emplace(key, mapped)` method can also be converted using `toml::get`. ```cpp const auto t = toml::get>(input.at("t")); ``` If the conversion of any element fails, a `toml::type_error` exception is thrown. ## Using `toml::get_or` to Specify a Value on Failure `toml::get` throws a `toml::type_error` exception if the conversion fails. By using `toml::get_or`, you can specify a default value to return instead of an exception in case of a conversion failure. Unlike `toml::get`, `get_or` infers the target type from the arguments, so there is no need to specify ``. ```cpp const auto a = toml::get_or(input.at("a"), 42); ``` The types that can be converted are the same as for `toml::get`. If you specify `toml::value::xxx_type`, you can also retrieve a reference, but in that case, the argument must also be a reference. ```cpp toml::value::integer_type a_default = 42; auto a& = toml::get_or(input.at("a"), a_default); ``` ## Using `toml::find` to Search and Convert Simultaneously `toml::find` is a function that searches for a value in a `toml::value` holding a table and simultaneously performs the same type conversion as `toml::get`. ```cpp const auto a = toml::find(input, "a"); // Equivalent to: const auto a = toml::get(input.at("a")); ``` `toml::find` can also be used with arrays. ```cpp const auto a = input.at("a"); const auto a2 = toml::find(a, 2); // Equivalent to: const auto a2 = toml::get(input.at("a").at(2)); ``` If an error occurs during type conversion, `toml::find` throws the same `toml::type_error` as `toml::get`. If the key is not found or the index does not exist, it throws `std::out_of_range`. If no type is specified, `toml::find` returns a `toml::value` without type conversion. ```cpp const auto a = toml::find(input, "a"); // Equivalent to: const auto a = input.at("a"); ``` `toml::find` can access values recursively. ```toml a = {b = {c = 42}} ``` ```cpp const auto a_b_c = toml::find(input, "a", "b", "c"); // Equivalent to: const auto a = toml::get(input.at("a").at("b").at("c")); ``` You can mix keys and indexes. ```toml a = [ {b = 1}, {b = 2}, {b = 3} ] ``` ```cpp const auto a_2_b = toml::find(input, "a", 2, "b"); // Equivalent to: const auto a = toml::get(input.at("a").at(2).at("c")); ``` {{}} TOML supports a feature called quoted keys, which allows using normally disallowed characters in keys by enclosing them in `""` or `''`. Within quoted keys, `.` does **not** introduce tables. ```toml "127.0.0.1" = "value" site."google.com" = true ``` You can read this TOML file as follows: ```cpp const auto input = toml::parse("input.toml"); assert(input.at("127.0.0.1").as_string() == "value"); assert(input.at("site").at("google.com").as_boolean()); ``` To handle such cases seamlessly, toml11 does not automatically split keys containing `.`. Explicitly specifying the table hierarchy helps in structuring the input file correctly. Reference: [toml.io/Key](https://toml.io/en/v1.0.0#key) {{}} ## Using `toml::find_or` to Search and Specify a Value on Failure `toml::find_or` works similarly to `toml::find`, but allows specifying a default value to return if the search or conversion fails. This is useful when you want to provide a fallback value instead of handling exceptions. ```cpp const auto a = toml::find_or(input, "a", 42); ``` ## Defining Conversions for User-Defined Types With `toml::get` and `toml::find`, you can use user-defined types by employing one of the following methods. ### Defining `toml::from` In toml11, there is a `toml::from` type that supports conversions from user-defined types by specializing it as follows: ```cpp namespace extlib { struct foo { int a; std::string b; }; } // extlib namespace toml { template<> struct from { static extlib::foo from_toml(const toml::value& v) { return extlib::foo{ toml::find(v, "a"), toml::find(v, "b") }; } }; } // toml ``` If you also want to support `toml::value` with a modified type configuration, do as follows: ```cpp namespace extlib { struct foo { int a; std::string b; }; } // extlib namespace toml { template<> struct from { template static extlib::foo from_toml(const toml::basic_value& v) { return extlib::foo{ toml::find(v, "a"), toml::find(v, "b") }; } }; } // toml ``` This definition can be automatically generated using `TOML11_DEFINE_CONVERSION_NON_INTRUSIVE`. ```cpp namespace extlib { struct foo { int a; std::string b; }; } // extlib TOML11_DEFINE_CONVERSION_NON_INTRUSIVE(extlib::foo, a, b) ``` Alternatively, you can use a reflection library. Refer to the sample in `example` using `boost-ext/reflect`. ### Defining a `from_toml` Member Function You can also define a conversion by defining a `from_toml` member function. When using this method, a default constructor is required. ```cpp struct bar { int a; std::string b; void from_toml(const toml::value& v) { this->a = toml::find(v, "a"); this->b = toml::find(v, "b"); return ; } }; ``` If both are defined, `toml::from` takes precedence. ### Constructor Accepting `toml::value` If there is a constructor that accepts `toml::value`, conversion via `toml::get` can be performed. ```cpp struct baz { explicit baz(const toml::value& v) : a(toml::find(v, "a")), b(toml::find(v, "b")) {} int a; std::string b; }; ``` If both are defined, `toml::from` and `from_toml` take precedence. ## Applying Functions with `toml::visit` If you have a function object that can be applied to all types stored in `toml::value`, you can directly call that function without type conversion using `toml::visit`. ```cpp struct type_name_of { std::string operator()(const toml::value::boolean_type &) const {return "boolean";} std::string operator()(const toml::value::integer_type &) const {return "integer";} std::string operator()(const toml::value::floating_type &) const {return "floating";} std::string operator()(const toml::value::string_type &) const {return "string";} std::string operator()(const toml::value::local_time_type &) const {return "local_time";} std::string operator()(const toml::value::local_date_type &) const {return "local_date";} std::string operator()(const toml::value::local_datetime_type &) const {return "local_datetime";} std::string operator()(const toml::value::offset_datetime_type&) const {return "offset_datetime";} std::string operator()(const toml::value::array_type &) const {return "array";} std::string operator()(const toml::value::table_type &) const {return "table";} }; toml::value v(3.14); std::cout << toml::visit(type_name_of{}, v) << std::endl; // floating ``` ## Constructing `toml::value` `toml::value` can be constructed not only internally by the parser but also in user code. You can construct it by passing a type that is either the same as or convertible to the types stored in `toml::value`. ```cpp toml::value v1(true); toml::value v2(42); toml::value v3(3.14); ``` For arrays, you can use `toml::array`: ```cpp toml::value v(toml::array{1, 2, 3}); ``` Alternatively, you can pass containers such as `std::vector` directly: ```cpp const std::vector a{1,2,3}; toml::value v(a); ``` For tables, you can use `toml::table`: ```cpp toml::value v(toml::table{{"foo", 1}, {"bar", 2}, {"baz", 3}}); ``` Or pass containers such as `std::map` directly: ```cpp const std::map t{ {"foo", 1}, {"bar", 2}, {"baz", 3} } toml::value v(t); ``` You can pass `format_info` and comments to the constructor. The type of comments is `std::vector`. Each element corresponds to a line. ```cpp toml::integer_format_info fmt; fmt.fmt = toml::integer_format::hex; fmt.spacer = 4; toml::value v1(0xDEADBEEF, fmt); toml::value v2(0xC0FFEE, fmt, {"hex value!"}); ``` ## Converting to `toml::value` When constructing `toml::value` from user-defined types, you can customize the behavior by defining `toml::into` or `into_toml`. `toml::into` is particularly useful when converting types from other libraries. ### Defining `toml::into` By specializing `toml::into`, you can enable conversions to `toml::value`. This is useful for types from external libraries that do not provide a conversion to `toml::value`. Since `toml::value` passes `type_config` during conversion, you need to accept the `template` argument of `basic_value`. ```cpp namespace extlib { struct foo { int a; std::string b; }; } // extlib template<> struct into { template static toml::basic_value into_toml(const extlib::foo& f) { return toml::basic_value(typename toml::basic_value::table_type{{"a", f.a}, {"b", f.b}}); } }; ``` ### Defining `into_toml` Member Function Similar to `from_toml`, you can define the conversion through a member function. If `toml::into` is defined, it takes precedence. ```cpp struct bar { int a; std::string b; template toml::basic_value into_toml() const { return toml::basic_value(typename toml::basic_value::table_type{ {"a", this->a}, {"b", this->b} }); } }; ``` toml11-4.1.0/docs/content.en/docs/installation/000077500000000000000000000000001464712047700213015ustar00rootroot00000000000000toml11-4.1.0/docs/content.en/docs/installation/_index.md000066400000000000000000000072261464712047700231000ustar00rootroot00000000000000+++ title = "installation" type = "docs" weight = 1 +++ # Installation ## Using `single_include` The `ingle_include/toml.hpp`s is a single-file, header-only library that consolidates all the functionalities of toml11 into one file. The simplest way to use it is to copy this file to a location included in your `INCLUDE_PATH` and then add `#include ` to your source code. The MIT license notice is included in both the comments and the `toml::license_notice()` function. If you are redistributing the software without releasing the source code, you must either copy the toml11 license file and include it with your distribution, or ensure that the `toml::license_notice()` function can be called. ## Cloning toml11 and using cmake If you place toml11 within your repository, for example by using git submodule, and you are using cmake, you can make use of it by adding `add_subdirectory(toml11)` to your `CMakeLists.txt`. ```cmake add_subdirectory(toml11) add_executable(main main.cpp) target_link_libraries(main PUBLIC toml11::toml11) ``` toml11 will only run tests and install when it is the root project. ## Installing using cmake After cloning toml11, you can install it using cmake. ```console $ cmake -B ./build/ -DTOML11_BUILD_TESTS=ON $ cmake --install ./build/ --prefix=/opt/toml11 ``` If you want to run the test programs before installation, make sure to set `-DTOML11_BUILD_TESTS=ON` first. Once the installation is complete, you can use it as follows: ```cmake find_package(toml11) add_executable(main main.cpp) target_link_libraries(main PRIVATE toml11::toml11) ``` ## Compiling with cmake to Create a Static Library By defining `-DTOML11_PRECOMPILE=ON` when running cmake, you can precompile some of the functions in toml11, thereby reducing the overall compile time. ```console $ cmake -B ./build/ -DTOML11_PRECOMPILE=ON ``` When linking the library, use `target_link_libraries` in CMake ```cmake target_link_libraries(your_target PUBLIC toml11::toml11) ``` or pass `-DTOML11_COMPILE_SOURCES` to the compiler to suppress header-only features. However, since toml11 supports multiple C++ versions and may switch types based on the value of `__cplusplus`, there is a possibility of link failures if the version used during build differs from the version used during usage. If you encounter issues, set the required version using `CMAKE_CXX_STANDARD` during compilation. If this is difficult, use it as a header-only library as usual. The `find_package(toml11)` command defines `TOML11_INCLUDE_DIR`. Even if you install it as a precompiled library, you can still use it as a header-only library by adding `TOML11_INCLUDE_DIR` to `target_include_directories` and avoiding the use of `target_link_libraries`. ```cmake find_package(toml11) add_executable(main main.cpp) # Include only, do not link target_include_directories(main PRIVATE ${TOML11_INCLUDE_DIR}) ``` ## Compiling Examples You can compile the `examples/` directory by setting `-DTOML11_BUILD_EXAMPLES=ON`. ```console $ cmake -B ./build/ -DTOML11_BUILD_EXAMPLES=ON $ cmake --build ./build/ ``` The executable binaries for the examples will be generated in the `examples/` directory. ## Running Tests To build the tests, set `-DTOML11_BUILD_TESTS=ON`. ```console $ git submodule update --init --recursive $ cmake -B ./build/ -DTOML11_BUILD_TESTS=ON $ cmake --build ./build/ $ ctest --test-dir ./build/ ``` To run the `toml-lang/toml-tests`, set `-DTOML11_BUILD_TOML_TESTS=ON`. This will build `toml11_decoder` and `toml11_encoder` in the `tests/` directory. ```console $ git submodule update --init --recursive $ cmake -B ./build/ -DTOML11_BUILD_TOML_TESTS=ON $ cmake --build ./build/ $ ctest --test-dir ./build/ ``` toml11-4.1.0/docs/content.en/docs/reference/000077500000000000000000000000001464712047700205365ustar00rootroot00000000000000toml11-4.1.0/docs/content.en/docs/reference/_index.md000066400000000000000000000061731464712047700223350ustar00rootroot00000000000000+++ title = "reference" type = "docs" weight = 3 bookCollapseSection = true +++ # Reference Here, we explain the effects of the classes and functions provided by toml11. ## Directory Structure `toml.hpp` and `toml_fwd.hpp` reside in `${TOML11_INCLUDE_DIR}`. Other files are located in `${TOML11_INCLUDE_DIR}/toml11`. If you want to `#include` each feature's file individually, use `#include `. If you want to include all at once, use `#include `. ## [color.hpp](color) Defines functions related to colorizing error messages. ## [comments.hpp](comments) Defines types `preserve_comment` and `discard_comment` for preserving comments. ## [conversion.hpp](conversion) Defines macros to automatically convert `toml::value` and user-defined classes. ## [datetime.hpp](datetime) Defines classes for datetime information. ## [error_info.hpp](error_info) Defines a class for error information. ## [exception.hpp](exception) Defines the base class for exceptions used in toml11, `toml::exception`. ## [find.hpp](find) Defines the `toml::find` function to search for and convert values. ## [format.hpp](format) Defines classes for formatting information of values. ## [from.hpp](from) Forward declaration of the `from` type for converting user-defined types. ## [get.hpp](get) Defines the `toml::get` function to retrieve and convert values from `toml::value`. ## [into.hpp](into) Forward declaration of the `into` type for converting user-defined types. ## [literal.hpp](literal) Defines the `operator"" _toml` literal. ## [ordered_map.hpp](ordered_map) Defines `toml::ordered_map`. ## [parser.hpp](parser) Defines functions to parse files or strings. ## [result.hpp](result) Defines the `result` type for representing success or failure values used as return types in other functions. ## [serializer.hpp](serializer) Defines the `toml::format` function and `toml::serializer` used for serialization. ## [source_location.hpp](source_location) Defines the `source_location` type used for error information, pointing to a location within a file. ## [spec.hpp](spec) Defines the `toml::semantic_version` and `toml::spec` types to control TOML language version information and feature flags. ## [toml.hpp](toml) `toml.hpp` includes all other headers, making all toml11 features available. ## [toml_fwd.hpp](toml_fwd) `toml_fwd.hpp` contains forward declarations of structs defined in toml11 and macro definitions. ## [types.hpp](types) Defines the `toml::type_config` type for controlling the types held by `toml::value`. ## [value.hpp](value) Defines the `toml::value` type. ## [value_t.hpp](value_t) Defines the `toml::value_t` enumeration. ## [version.hpp](version) Defines the version information for toml11. ## [visit.hpp](visit) Defines the `toml::visit` function to apply functions to the values held by `toml::value`. ## Notes Functions not explicitly mentioned here (mostly those defined under `namespace toml::detail` or `namespace toml::cxx`) are available by inspecting the source code but are not guaranteed to maintain their interface across future versions (including patch version updates). toml11-4.1.0/docs/content.en/docs/reference/color.md000066400000000000000000000046271464712047700222070ustar00rootroot00000000000000+++ title = "color.hpp" type = "docs" +++ # color.hpp In `color.hpp`, functions related to colorizing error messages are defined. Colors are specified using ANSI escape code. In terminals or other output destinations that do not support ANSI escape code, the output may become difficult to read. ## Macros ```cpp TOML11_COLORIZE_ERROR_MESSAGE ``` If this macro is defined during compilation (`-DTOML11_COLORIZE_ERROR_MESASGE`), error messages are colored by default. If not defined, colors are not applied by default. You need to specify them using `toml::color::enable()`. ## Functions ### `enable()` ```cpp namespace toml { namespace color { void enable(); } // color } // toml ``` Enables colorization using ANSI escape code. #### Example ```cpp #include int main() { toml::color::enable(); // All subsequent errors will be colored. const auto input = toml::parse("input.toml"); return 0; } ``` ### `disable()` ```cpp namespace toml { namespace color { void disable(); } // color } // toml ``` Disables colorization using ANSI escape code. #### Example ```cpp #include int main() { toml::color::disable(); // All subsequent errors will not be colored. const auto input = toml::parse("input.toml"); return 0; } ``` ### `should_color()` ```cpp namespace toml { namespace color { bool should_color(); } // color } // toml ``` Returns `true` if colorization is enabled, `false` otherwise. #### Example ```cpp #include #include #include int main() { std::cout << "colorized? : " << std::boolalpha << toml::color::should_color() << std::endl; return 0; } ``` ## Manipulators ```cpp namespace toml { namespace color { std::ostream& reset (std::ostream&); std::ostream& bold (std::ostream&); std::ostream& grey (std::ostream&); std::ostream& gray (std::ostream&); std::ostream& red (std::ostream&); std::ostream& green (std::ostream&); std::ostream& yellow (std::ostream&); std::ostream& blue (std::ostream&); std::ostream& magenta(std::ostream&); std::ostream& cyan (std::ostream&); std::ostream& white (std::ostream&); } // color } // toml ``` Colorizes the foreground with ANSI escape code. #### Example ```cpp #include #include int main() { std::cout << toml::color::red << "red!" << toml::color::reset << std::endl; return 0; } ``` # Related - [error_info.hpp]({{}}) toml11-4.1.0/docs/content.en/docs/reference/comments.md000066400000000000000000000407601464712047700227140ustar00rootroot00000000000000+++ title = "comments.hpp" type = "docs" +++ # comments.hpp In `comments.hpp`, comment containers are provided. # `toml::preserve_comments` `preserve_comments` is a container that preserves comments. It has all the member functions of `std::vector`. Comments are preserved as `std::string`. If the comment does not start with `#`, it will be prefixed with `#` during output. However, this prefixing is not done when adding comments to the container. Spaces are not automatically added, so if you want a space immediately after `#`, either start the comment with a space or pass the comment with `#`. ```cpp namespace toml { class preserve_comments; bool operator==(const preserve_comments&, const preserve_comments&); bool operator!=(const preserve_comments&, const preserve_comments&); bool operator< (const preserve_comments&, const preserve_comments&); bool operator<=(const preserve_comments&, const preserve_comments&); bool operator> (const preserve_comments&, const preserve_comments&); bool operator>=(const preserve_comments&, const preserve_comments&); void swap(preserve_comments&, preserve_comments&); void swap(preserve_comments&, std::vector&); void swap(std::vector&, preserve_comments&); std::ostream& operator<<(std::ostream&, const preserve_comments&); } //toml ``` ## Member types ```cpp using container_type = std::vector; using size_type = container_type::size_type; using difference_type = container_type::difference_type; using value_type = container_type::value_type; using reference = container_type::reference; using const_reference = container_type::const_reference; using pointer = container_type::pointer; using const_pointer = container_type::const_pointer; using iterator = container_type::iterator; using const_iterator = container_type::const_iterator; using reverse_iterator = container_type::reverse_iterator; using const_reverse_iterator = container_type::const_reverse_iterator; ``` ## Member Functions ### Default Constructor ```cpp preserve_comments() = default; ``` Constructs an empty `preserve_comments`. ### Copy and Move Constructors ```cpp preserve_comments(preserve_comments const&) = default; preserve_comments(preserve_comments &&) = default; ``` Constructs a `preserve_comments` by copying or moving. ### Constructor (`std::vector`) ```cpp explicit preserve_comments(const std::vector& c); explicit preserve_comments(std::vector&& c); ``` Constructs a `preserve_comments` holding the contents of `std::vector`. ### Constructor (`discard_comments`) ```cpp explicit preserve_comments(const discard_comments&); ``` Constructs an empty `preserve_comments`. ### Constructor (`Iterator`) ```cpp template preserve_comments(InputIterator first, InputIterator last); ``` Constructs a `preserve_comments` from the range represented by `InputIterator` pointing to `std::string`. ### Constructor (`std::initializer_list`) ```cpp preserve_comments(std::initializer_list x); ``` Constructs a `preserve_comments` from the range represented by `std::initializer_list`. ### Constructor (Size Specified) ```cpp explicit preserve_comments(size_type n); preserve_comments(size_type n, const std::string& x); ``` Constructs a `preserve_comments` with `n` comments. If a `std::string` is passed, it replicates that comment `n` times. ### Destructor ```cpp ~preserve_comments() = default; ``` Destroys the `preserve_comments`. ### `operator=(preserve_comments)` ```cpp preserve_comments& operator=(preserve_comments const&) = default; preserve_comments& operator=(preserve_comments &&) = default; ``` Assigns a `preserve_comments` by copying or moving. ### `operator=(std::vector)` ```cpp preserve_comments& operator=(const std::vector& c); preserve_comments& operator=(std::vector&& c); ``` Assigns a `std::vector` by copying or moving. ### `assign` ```cpp template void assign(InputIterator first, InputIterator last); void assign(std::initializer_list ini); void assign(size_type n, const std::string& val); ``` The same as `std::vector::assign`. ### `insert` ```cpp iterator insert(const_iterator p, const std::string& x); iterator insert(const_iterator p, std::string&& x); iterator insert(const_iterator p, size_type n, const std::string& x); template iterator insert(const_iterator p, InputIterator first, InputIterator last); iterator insert(const_iterator p, std::initializer_list ini); ``` The same as `std::vector::insert`. ### `emplace` ```cpp template iterator emplace(const_iterator p, Ts&& ... args); ``` The same as `std::vector::emplace`. ### `erase` ```cpp iterator erase(const_iterator pos); iterator erase(const_iterator first, const_iterator last); ``` The same as `std::vector::erase`. ### `swap` ```cpp void swap(preserve_comments& other); ``` Swaps the comments. ### `push_back` ```cpp void push_back(const std::string& v); void push_back(std::string&& v); ``` The same as `std::vector::push_back`. ### `pop_back` ```cpp void pop_back(); ``` The same as `std::vector::pop_back`. ### `emplace_back` ```cpp template void emplace_back(Ts&& ... args); ``` The same as `std::vector::emplace_back`. ### `clear` ```cpp void clear(); ``` The same as `std::vector::clear`. ### `size` ```cpp size_type size() const noexcept; ``` The same as `std::vector::size`. ### `max_size` ```cpp size_type max_size() const noexcept; ``` The same as `std::vector::max_size`. ### `capacity` ```cpp size_type capacity() const noexcept; ``` The same as `std::vector::capacity`. ### `empty` ```cpp bool empty() const noexcept; ``` The same as `std::vector::empty`. ### `reserve` ```cpp void reserve(size_type n); ``` The same as `std::vector::reserve`. ### `resize` ```cpp void resize(size_type n); void resize(size_type n, const std::string& c); ``` The same as `std::vector::resize`. ### `shrink_to_fit` ```cpp void shrink_to_fit(); ``` The same as `std::vector::shrink_to_fit`. ### `operator[]` ```cpp reference operator[](const size_type n) noexcept; const_reference operator[](const size_type n) const noexcept; ``` The same as `std::vector::operator[]`. ### `at` ```cpp reference at(const size_type n) ; const_reference at(const size_type n) const; ``` The same as `std::vector::at`. ### `front` ```cpp reference front() noexcept; const_reference front() const noexcept; ``` The same as `std::vector::front`. ### `back` ```cpp reference back() noexcept; const_reference back() const noexcept; ``` The same as `std::vector::back`. ### `data` ```cpp pointer data() noexcept; const_pointer data() const noexcept; ``` The same as `std::vector::data`. ### `begin/end` ```cpp iterator begin() noexcept; iterator end() noexcept; const_iterator begin() const noexcept; const_iterator end() const noexcept; const_iterator cbegin() const noexcept; const_iterator cend() const noexcept; ``` The same as `std::vector::begin/end`. ### `rbegin/rend` ```cpp reverse_iterator rbegin() noexcept; reverse_iterator rend() noexcept; const_reverse_iterator rbegin() const noexcept; const_reverse_iterator rend() const noexcept; const_reverse_iterator crbegin() const noexcept; const_reverse_iterator crend() const noexcept; ``` The same as `std::vector::rbegin/rend` ## non-member functions ### Comparison operator ```cpp bool operator==(const preserve_comments&, const preserve_comments&); bool operator!=(const preserve_comments&, const preserve_comments&); bool operator< (const preserve_comments&, const preserve_comments&); bool operator<=(const preserve_comments&, const preserve_comments&); bool operator> (const preserve_comments&, const preserve_comments&); bool operator>=(const preserve_comments&, const preserve_comments&); ``` It compares two `preserve_comments` in the same way as `std::vector`. ### `swap` ```cpp void swap(preserve_comments&, preserve_comments&); void swap(preserve_comments&, std::vector&); void swap(std::vector&, preserve_comments&); ``` ### Stream operator ```cpp std::ostream& operator<<(std::ostream&, const preserve_comments&); ``` Outputs as TOML comments. If the first character is not `#`, it adds `#`. # `toml::discard_comments` `discard_comments` serves as a container for discarding comments. It encompasses all the member functions of `std::vector`, but invoking functions that modify the content has no effect, leaving it perpetually empty. ```cpp namespace toml { class discard_comments; bool operator==(const discard_comments&, const discard_comments&); bool operator!=(const discard_comments&, const discard_comments&); bool operator< (const discard_comments&, const discard_comments&); bool operator<=(const discard_comments&, const discard_comments&); bool operator> (const discard_comments&, const discard_comments&); bool operator>=(const discard_comments&, const discard_comments&); void swap(discard_comments&, discard_comments&); std::ostream& operator<<(std::ostream&, const discard_comments&); } //toml ``` ## Member types ```cpp // container_type is not defined using size_type = std::size_t; using difference_type = std::ptrdiff_t; using value_type = std::string; using reference = std::string&; using const_reference = std::string const&; using pointer = std::string*; using const_pointer = std::string const*; using iterator = /* internal type: empty-iterator */ using const_iterator = /* internal type: empty-iterator */ using reverse_iterator = /* internal type: empty-iterator */ using const_reverse_iterator = /* internal type: empty-iterator */ ``` ### Default Constructor ```cpp discard_comments() = default; ``` Constructs an empty `discard_comments`. ### Copy / Move Constructor ```cpp discard_comments(discard_comments const&) = default; discard_comments(discard_comments &&) = default; ``` Constructs a `discard_comments` by copying or moving. ### Constructor(`std::vector`) ```cpp explicit discard_comments(const std::vector& c); explicit discard_comments(std::vector&& c); ``` Constructs an empty `discard_comments`. Arguments are ignored. ### Constructor(`preserve_comments`) ```cpp explicit discard_comments(const preserve_comments&); ``` Constructs an empty `discard_comments`. Arguments are ignored. ### Constructor(`Iterator`) ```cpp template discard_comments(InputIterator first, InputIterator last); ``` Constructs an empty `discard_comments`. Arguments are ignored. ### Constructor(`std::initializer_list`) ```cpp discard_comments(std::initializer_list x); ``` Constructs an empty `discard_comments`. Arguments are ignored. ### Constructor(サイズ指定) ```cpp explicit discard_comments(size_type n); discard_comments(size_type n, const std::string& x); ``` Constructs an empty `discard_comments`. Arguments are ignored. ### Destructor ```cpp ~discard_comments() = default; ``` Destroys `discard_comments`. ### `operator=(discard_comments)` ```cpp discard_comments& operator=(discard_comments const&) = default; discard_comments& operator=(discard_comments &&) = default; ``` Assigns `discard_comments` by copying or moving. ### `operator=(std::vector)` ```cpp discard_comments& operator=(const std::vector& c); discard_comments& operator=(std::vector&& c); ``` Does nothing. ### `assign` ```cpp template void assign(InputIterator first, InputIterator last); void assign(std::initializer_list ini); void assign(size_type n, const std::string& val); ``` Does nothing. ### `insert` ```cpp iterator insert(const_iterator p, const std::string& x); iterator insert(const_iterator p, std::string&& x); iterator insert(const_iterator p, size_type n, const std::string& x); template iterator insert(const_iterator p, InputIterator first, InputIterator last); iterator insert(const_iterator p, std::initializer_list ini); ``` Does nothing. ### `emplace` ```cpp template iterator emplace(const_iterator p, Ts&& ... args); ``` Does nothing. ### `erase` ```cpp iterator erase(const_iterator pos); iterator erase(const_iterator first, const_iterator last); ``` Does nothing. ### `swap` ```cpp void swap(discard_comments& other); ``` Swaps two `discard_comments`. Effectively it does nothing. ### `push_back` ```cpp void push_back(const std::string& v); void push_back(std::string&& v); ``` Does nothing. ### `pop_back` ```cpp void pop_back(); ``` Does nothing. ### `emplace_back` ```cpp template void emplace_back(Ts&& ... args); ``` Does nothing. ### `clear` ```cpp void clear(); ``` Does nothing. ### `size` ```cpp size_type size() const noexcept; ``` Returns `0`. ### `max_size` ```cpp size_type max_size() const noexcept; ``` Returns `0`. ### `capacity` ```cpp size_type capacity() const noexcept; ``` Returns `0`. ### `empty` ```cpp bool empty() const noexcept; ``` Returns `true`. ### `reserve` ```cpp void reserve(size_type n); ``` Does nothing. ### `resize` ```cpp void resize(size_type n); void resize(size_type n, const std::string& c); ``` Does nothing. ### `shrink_to_fit` ```cpp void shrink_to_fit(); ``` Does nothing. ### `operator[]` ```cpp reference operator[](const size_type n) noexcept; const_reference operator[](const size_type n) const noexcept; ``` Causes undefined behavior. ### `at` ```cpp reference at(const size_type n) ; const_reference at(const size_type n) const; ``` Throws `std::out_of_range`. ### `front` ```cpp reference front() noexcept; const_reference front() const noexcept; ``` Causes undefined behavior. ### `back` ```cpp reference back() noexcept; const_reference back() const noexcept; ``` Causes undefined behavior. ### `data` ```cpp pointer data() noexcept; const_pointer data() const noexcept; ``` Returns `nullptr`. ### `begin/end` ```cpp iterator begin() noexcept; iterator end() noexcept; const_iterator begin() const noexcept; const_iterator end() const noexcept; const_iterator cbegin() const noexcept; const_iterator cend() const noexcept; ``` Returns an internally defined `empty-iterator`. The `empty-iterator` remains at the same value after incrementing or decrementing and all values are equivalent. Accessing the target of the `empty-iterator` always calls `std::terminate`. ### `rbegin/rend` ```cpp reverse_iterator rbegin() noexcept; reverse_iterator rend() noexcept; const_reverse_iterator rbegin() const noexcept; const_reverse_iterator rend() const noexcept; const_reverse_iterator crbegin() const noexcept; const_reverse_iterator crend() const noexcept; ``` Returns an internally defined `empty-iterator`. The `empty-iterator` remains at the same value after incrementing or decrementing and all values are equivalent. Accessing the target of the `empty-iterator` always calls `std::terminate`. ## non-member functions ### Comparison operator ```cpp bool operator==(const discard_comments&, const discard_comments&); bool operator!=(const discard_comments&, const discard_comments&); bool operator< (const discard_comments&, const discard_comments&); bool operator<=(const discard_comments&, const discard_comments&); bool operator> (const discard_comments&, const discard_comments&); bool operator>=(const discard_comments&, const discard_comments&); ``` All the instances of `discard_comments` are the same value. `operator==` returns `true`, `operator=!` returns `false`. ### `swap` ```cpp void swap(discard_comments&, discard_comments&); ``` Swaps `discard_comments`. Effectively it does nothing. ### Stream operator ```cpp std::ostream& operator<<(std::ostream&, const discard_comments&); ``` Outputs nothing. # Related - [value.hpp]({{}}) toml11-4.1.0/docs/content.en/docs/reference/conversion.md000066400000000000000000000010101464712047700232350ustar00rootroot00000000000000+++ title = "conversion.hpp" type = "docs" +++ # conversion.hpp Provides macros to automatically define conversion functions for supporting user-defined types with `toml::get` and `toml::find`. ```cpp TOML11_DEFINE_CONVERSION_NON_INTRUSIVE(NAME, ...) ``` ## Example ```cpp namespace foo { struct Foo { std::string s; double d; int i; }; } // foo TOML11_DEFINE_CONVERSION_NON_INTRUSIVE(foo::Foo, s, d, i) ``` # Related - [from.hpp]({{}}) - [into.hpp]({{}}) toml11-4.1.0/docs/content.en/docs/reference/datetime.md000066400000000000000000000412211464712047700226540ustar00rootroot00000000000000+++ title = "datetime.hpp" type = "docs" +++ # datetime.hpp Defines a class that stores date and time information used in TOML's `datetime`. # `enum class month_t` Enum class to specify months. Due to its relationship with `std::tm`, `local_date` treats January as `0`. To avoid confusion, `month_t` allows specification of months by their names. ```cpp namespace toml { enum class month_t : std::uint8_t { Jan = 0, Feb = 1, Mar = 2, Apr = 3, May = 4, Jun = 5, Jul = 6, Aug = 7, Sep = 8, Oct = 9, Nov = 10, Dec = 11 }; } ``` # `local_date` `local_date` holds a date. `year` represents the year in AD. For `month`, January is represented as `0` to align with `std::tm`. `day` holds the day of the month. ```cpp namespace toml { struct local_date { std::int16_t year; std::uint8_t month; std::uint8_t day; local_date() = default; ~local_date() = default; local_date(local_date const&) = default; local_date(local_date&&) = default; local_date& operator=(local_date const&) = default; local_date& operator=(local_date&&) = default; local_date(int y, month_t m, int d); explicit local_date(const std::tm& t); explicit local_date(const std::chrono::system_clock::time_point& tp); explicit local_date(const std::time_t t); operator std::chrono::system_clock::time_point() const; operator std::time_t() const; }; bool operator==(const local_date&, const local_date&); bool operator!=(const local_date&, const local_date&); bool operator< (const local_date&, const local_date&); bool operator<=(const local_date&, const local_date&); bool operator> (const local_date&, const local_date&); bool operator>=(const local_date&, const local_date&); std::ostream& operator<<(std::ostream& os, const local_date& date); std::string to_string(const local_date& date); } ``` ## Member Variables ### `year` ```cpp std::int16_t year; ``` Represents the year in AD. There's no offset. `2024` is simply `2024`. ### `month` ```cpp std::uint8_t month; ``` Represents the month. To align with `std::tm`, January is `0`, February is `1`, and so on. To avoid confusion, use `month_t` during construction. ### `day` ```cpp std::uint8_t day; ``` Represents the day of the month. The first day is `1`. ## Member Functions ### Constructor ```cpp local_date() = default; ``` Uses the default implementation. ### Destructor ```cpp ~local_date() = default; ``` Uses the default implementation. ### Copy and Move Constructors ```cpp local_date(local_date const&) = default; local_date(local_date&&) = default; ``` Uses the default implementations. ### Copy and Move Assignment Operators ```cpp local_date& operator=(local_date const&) = default; local_date& operator=(local_date&&) = default; ``` Uses the default implementations. ### Constructor (`int year, month_t month, int day`) ```cpp local_date(int y, month_t m, int d); ``` Constructs a `local_date` from the specified values. Does not perform boundary checks. ### Constructor (`std::tm`) ```cpp local_date(const std::tm&); ``` Constructs a `local_date` from the specified `std::tm` value. ### Constructor (`std::chrono::system_clock::time_point`) ```cpp local_date(const std::chrono::system_clock::time_point&); ``` Constructs a `local_date` from the specified `std::chrono::system_clock::time_point` value. Time zone is determined by the environment. ### Constructor (`std::time_t`) ```cpp local_date(const std::time_t); ``` Constructs a `local_date` from the specified `std::time_t` value. Time zone is determined by the environment. ### `operator std::chrono::system_clock::time_point` ```cpp operator std::chrono::system_clock::time_point() const; ``` Converts to `std::chrono::system_clock::time_point`. Time zone is determined by the environment. Time is set to 0 hours and 0 minutes. ### `operator std::time_t` ```cpp operator std::time_t() const; ``` Converts to `std::time_t`. Time zone is determined by the execution environment. Time is set to 0 hours and 0 minutes. ## Non-member Functions ### Comparison Operators ```cpp bool operator==(const local_date&, const local_date&); bool operator!=(const local_date&, const local_date&); bool operator< (const local_date&, const local_date&); bool operator<=(const local_date&, const local_date&); bool operator> (const local_date&, const local_date&); bool operator>=(const local_date&, const local_date&); ``` Compares two dates. ### Stream Operators ```cpp std::ostream& operator<<(std::ostream& os, const local_date& date); ``` Outputs in the default TOML format. ### `to_string` ```cpp std::string to_string(const local_date& date); ``` Converts to a string in the default TOML format. # `local_time` ```cpp namespace toml { struct local_time { std::uint8_t hour; // [0, 23] std::uint8_t minute; // [0, 59] std::uint8_t second; // [0, 60] std::uint16_t millisecond; // [0, 999] std::uint16_t microsecond; // [0, 999] std::uint16_t nanosecond; // [0, 999] local_time(int h, int m, int s, int ms = 0, int us = 0, int ns = 0); explicit local_time(const std::tm& t); template explicit local_time(const std::chrono::duration& t); operator std::chrono::nanoseconds() const; local_time() = default; ~local_time() = default; local_time(local_time const&) = default; local_time(local_time&&) = default; local_time& operator=(local_time const&) = default; local_time& operator=(local_time&&) = default; }; bool operator==(const local_time& lhs, const local_time& rhs); bool operator!=(const local_time& lhs, const local_time& rhs); bool operator< (const local_time& lhs, const local_time& rhs); bool operator<=(const local_time& lhs, const local_time& rhs); bool operator> (const local_time& lhs, const local_time& rhs); bool operator>=(const local_time& lhs, const local_time& rhs); std::ostream& operator<<(std::ostream& os, const local_time& time); std::string to_string(const local_time& time); } ``` ## Member Values ### `hour` ```cpp std::uint8_t hour; ``` Represents the hour. Values range from `0` to `23`. ### `minute` ```cpp std::uint8_t minute; // [0, 59] ``` Represents the minute. Values range from `0` to `59`. ### `second` ```cpp std::uint8_t second; // [0, 60] ``` Represents the second. Values range from `0` to `60`. ### `millisecond` ```cpp std::uint16_t millisecond; // [0, 999] ``` Represents the millisecond. Values range from `0` to `999`. ### `microsecond` ```cpp std::uint16_t microsecond; // [0, 999] ``` Represents the microsecond. Values range from `0` to `999`. ### `nanosecond` ```cpp std::uint16_t nanosecond; // [0, 999] ``` Represents the nanosecond. Values range from `0` to `999`. ## Member Functions ### default constructor ```cpp local_time() = default; ``` Initializes all values to `0`. ### constructor (h, m, s, ms = 0, us = 0, ns = 0) ```cpp local_time(int h, int m, int s, int ms = 0, int us = 0, int ns = 0); ``` Constructs using the specified time components. No boundary checks are performed. ### constructor(`std::tm`) ```cpp explicit local_time(const std::tm& t); ``` Constructs using `tm_hour`, `tm_min`, and `tm_sec` from `std::tm`. Subseconds are initialized to `0`. ### constructor(`std::chrono::duration`) ```cpp template explicit local_time(const std::chrono::duration& t); ``` Constructs as the time of day from `0` hours of the day specified by `duration`. ### `operator std::chrono::nanoseconds` ```cpp operator std::chrono::nanoseconds() const; ``` Converts to `std::chrono::nanoseconds`. ## Non-member Functions ### Comparison Operators ```cpp bool operator==(const local_time& lhs, const local_time& rhs); bool operator!=(const local_time& lhs, const local_time& rhs); bool operator< (const local_time& lhs, const local_time& rhs); bool operator<=(const local_time& lhs, const local_time& rhs); bool operator> (const local_time& lhs, const local_time& rhs); bool operator>=(const local_time& lhs, const local_time& rhs); ``` Compares based on time values. ### Stream Operator ```cpp std::ostream& operator<<(std::ostream& os, const local_time& time); ``` Outputs in the default TOML format. ### `to_string` ```cpp std::string to_string(const local_time& time); ``` Converts to a string in the default TOML format. # `time_offset` ```cpp namespace toml { struct time_offset { std::int8_t hour{0}; // [-12, 12] std::int8_t minute{0}; // [-59, 59] time_offset(int h, int m); operator std::chrono::minutes() const; time_offset() = default; ~time_offset() = default; time_offset(time_offset const&) = default; time_offset(time_offset&&) = default; time_offset& operator=(time_offset const&) = default; time_offset& operator=(time_offset&&) = default; }; bool operator==(const time_offset&, const time_offset&); bool operator!=(const time_offset&, const time_offset&); bool operator< (const time_offset&, const time_offset&); bool operator<=(const time_offset&, const time_offset&); bool operator> (const time_offset&, const time_offset&); bool operator>=(const time_offset&, const time_offset&); std::ostream& operator<<(std::ostream& os, const time_offset& offset); std::string to_string(const time_offset& offset); } ``` ## Member Variables ### `hour` ```cpp std::int8_t hour{0}; // [-12, 12] ``` Represents the hour offset, ranging from -12 to +12. ### `minute` ```cpp std::int8_t minute{0}; // [-59, 59] ``` Represents the minute offset, ranging from -59 to +59. ## Member Functions ### Constructor ```cpp time_offset(int h, int m); ``` Constructs with given hours and minutes. No boundary checking is performed. ### `operator std::chrono::minutes` ```cpp operator std::chrono::minutes() const; ``` Converts to `std::chrono::minutes`. ## Non-member Functions ### Comparison Operators ```cpp bool operator==(const time_offset&, const time_offset&); bool operator!=(const time_offset&, const time_offset&); bool operator< (const time_offset&, const time_offset&); bool operator<=(const time_offset&, const time_offset&); bool operator> (const time_offset&, const time_offset&); bool operator>=(const time_offset&, const time_offset&); ``` Compares based on time length. ### Stream Output Operator ```cpp std::ostream& operator<<(std::ostream& os, const time_offset&); ``` Outputs in the default TOML format. ### `to_string` ```cpp std::string to_string(const time_offset&); ``` Converts to a string in the default TOML format. # `local_datetime` ```cpp namespace toml { struct local_datetime { local_date date; local_time time; local_datetime(local_date d, local_time t); explicit local_datetime(const std::tm& t); explicit local_datetime(const std::chrono::system_clock::time_point& tp); explicit local_datetime(const std::time_t t); operator std::chrono::system_clock::time_point() const; operator std::time_t() const; local_datetime() = default; ~local_datetime() = default; local_datetime(local_datetime const&) = default; local_datetime(local_datetime&&) = default; local_datetime& operator=(local_datetime const&) = default; local_datetime& operator=(local_datetime&&) = default; }; bool operator==(const local_datetime&, const local_datetime&); bool operator!=(const local_datetime&, const local_datetime&); bool operator< (const local_datetime&, const local_datetime&); bool operator<=(const local_datetime&, const local_datetime&); bool operator> (const local_datetime&, const local_datetime&); bool operator>=(const local_datetime&, const local_datetime&); std::ostream& operator<<(std::ostream& os, const local_datetime& dt); std::string to_string(const local_datetime& dt); } ``` ## Member Variables ### `local_date date` ```cpp local_date date; ``` Stores the date component data. ### `local_time time` ```cpp local_time time; ``` Stores the time component data. ## Member Functions ### Default Constructor Constructs both `date` and `time` with default values. ### Constructor (`local_date, local_time`) Constructs with the specified `date` and `time`. ### Constructor (`std::tm`) Constructs from `std::tm`. The timezone is selected based on the execution environment. ### Constructor (`std::chrono::system_clock::time_point`) Constructs from `std::chrono::system_clock::time_point`. The timezone is selected based on the execution environment. ### Constructor (`std::time_t`) Constructs from `std::time_t`. The timezone is selected based on the execution environment. ### `operator std::chrono::system_clock::time_point` Converts to `std::chrono::system_clock::time_point`. ### `operator std::time_t` Converts to `std::time_t`. ## Non-member Functions ### Comparison Operators ```cpp bool operator==(const local_datetime&, const local_datetime&); bool operator!=(const local_datetime&, const local_datetime&); bool operator< (const local_datetime&, const local_datetime&); bool operator<=(const local_datetime&, const local_datetime&); bool operator> (const local_datetime&, const local_datetime&); bool operator>=(const local_datetime&, const local_datetime&); ``` Compares based on chronological order. ### Stream Output Operator ```cpp std::ostream& operator<<(std::ostream& os, const local_datetime&); ``` Outputs in the default TOML format. ### `to_string` ```cpp std::string to_string(const local_datetime&); ``` Converts to a string in the default TOML format. # `offset_datetime` ```cpp namespace toml { struct offset_datetime { local_date date; local_time time; time_offset offset; offset_datetime(local_date d, local_time t, time_offset o); offset_datetime(const local_datetime& dt, time_offset o); explicit offset_datetime(const local_datetime& ld); explicit offset_datetime(const std::chrono::system_clock::time_point& tp); explicit offset_datetime(const std::time_t& t); explicit offset_datetime(const std::tm& t); operator std::chrono::system_clock::time_point() const; operator std::time_t() const; offset_datetime() = default; ~offset_datetime() = default; offset_datetime(offset_datetime const&) = default; offset_datetime(offset_datetime&&) = default; offset_datetime& operator=(offset_datetime const&) = default; offset_datetime& operator=(offset_datetime&&) = default; }; bool operator==(const offset_datetime&, const offset_datetime&); bool operator!=(const offset_datetime&, const offset_datetime&); bool operator< (const offset_datetime&, const offset_datetime&); bool operator<=(const offset_datetime&, const offset_datetime&); bool operator> (const offset_datetime&, const offset_datetime&); bool operator>=(const offset_datetime&, const offset_datetime&); std::ostream& operator<<(std::ostream& os, const offset_datetime& dt); std::string to_string(const offset_datetime& dt); } ``` ## Member Variables ### `date` ```cpp local_date date; ``` Stores the date component. ### `time` ```cpp local_time time; ``` Stores the time component. ### `offset` ```cpp time_offset offset; ``` Stores the offset component. ## Member Functions ### Default Constructor Constructs `date`, `time`, and `offset` with default values. ### Constructor (`local_date, local_time, time_offset`) Constructs with the specified `date`, `time`, and `offset`. ### Constructor (`local_datetime, time_offset`) Constructs from `local_datetime` and `offset`. ### Constructor (`std::tm`) Constructs from `std::tm`. The timezone is UTC (00:00). ### Constructor (`std::chrono::system_clock::time_point`) Constructs from `std::chrono::system_clock::time_point`. The timezone is UTC (00:00). ### Constructor (`std::time_t`) Constructs from `std::time_t`. The timezone is UTC (00:00). ### `operator std::chrono::system_clock::time_point` Converts to `std::chrono::system_clock::time_point`. The timezone is UTC (00:00). ### `operator std::time_t` Converts to `std::time_t`. The timezone is UTC (00:00). ## Non-member Functions ### Comparison Operators ```cpp bool operator==(const offset_datetime&, const offset_datetime&); bool operator!=(const offset_datetime&, const offset_datetime&); bool operator< (const offset_datetime&, const offset_datetime&); bool operator<=(const offset_datetime&, const offset_datetime&); bool operator> (const offset_datetime&, const offset_datetime&); bool operator>=(const offset_datetime&, const offset_datetime&); ``` Compares based on chronological order. If dates are the same, compares based on timezone order. ### Stream Output Operator ```cpp std::ostream& operator<<(std::ostream& os, const offset_datetime&); ``` Outputs in the default TOML format. ### `to_string` ```cpp std::string to_string(const offset_datetime&); ``` Converts to a string in the default TOML format. toml11-4.1.0/docs/content.en/docs/reference/error_info.md000066400000000000000000000075251464712047700232350ustar00rootroot00000000000000+++ title = "error_info.hpp" type = "docs" +++ # error_info.hpp In `error_info.hpp`, definitions for `error_info` and functions to format it are provided. # `toml::error_info` ```cpp namespace toml { struct error_info { error_info(std::string t, source_location l, std::string m, std::string s = ""); error_info(std::string t, std::vector> l, std::string s = ""); std::string const& title() const noexcept; std::string & title() noexcept; std::vector> const& locations() const noexcept; void add_locations(source_location loc, std::string msg) noexcept; std::string const& suffix() const noexcept; std::string & suffix() noexcept; }; template error_info make_error_info( std::string title, source_location loc, std::string msg, Ts&& ... tail); std::string format_error(const std::string& errkind, const error_info& err); std::string format_error(const error_info& err); template std::string format_error(std::string title, source_location loc, std::string msg, Ts&& ... tail); std::ostream& operator<<(std::ostream& os, const error_info& e); } ``` ## Member Functions ### Constructor (`title, loc, msg, suffix`) Constructs `error_info` with specified `title`, location information `loc`, message `msg`, and optional `suffix`. `suffix` defaults to empty. ### Constructor (`title, [{loc, msg}, ...], suffix`) Constructs `error_info` with specified `title`, an array of location-message pairs `[{loc, msg}, ...]`, and optional `suffix`. `suffix` defaults to empty. ### `std::string title()` Returns the title of the error message. ### `std::vector> locations()` Returns the list of locations where errors occurred along with their respective messages. Multiple locations can be specified. ### `std::string suffix()` Returns the suffix message to display at the end, providing hints or additional information. ## Non-Member Functions ### `make_error_info` ```cpp template error_info make_error_info( std::string title, source_location loc, std::string msg, Ts&& ... tail); ``` Creates a new `error_info`. Must be followed by a `msg` related to `source_location` or `basic_value`. Overloads are added in [`value.hpp`](docs/reference/value#tomlmake_error_info) when passing `toml::basic_value` instead of `source_location`. Possible to pass `suffix` at the end. ### `format_error` Formats `error_info` as follows: ``` {title} --> {locations().at(0).first.file_name()} | 1 | {locations().at(0).first.line()} | ^-- {locations().at(0).second} | 2 | {locations().at(1).first.line()} | ^-- {locations().at(1).second} {suffix} ``` If file names differ between two `source_location`, the file name is displayed again. ```cpp std::string format_error(const std::string& errkind, const error_info& err); std::string format_error(const error_info& err); ``` Formats `error_info`. If `errkind` is not provided, a red-bold `[error]` prefix is added before `title`. If `errkind` is provided (including an empty string), it replaces `[error]`. ```cpp namespace toml { template std::string format_error(std::string title, source_location loc, std::string msg, Ts&& ... tail); } // toml ``` Returns a formatted string using `format_error` for `error_info` created with `make_error_info`. Overloads are added in [`value.hpp`](docs/reference/value#tomlformat_error) when passing `toml::basic_value` instead of `source_location`. ### Stream Operator ```cpp std::ostream& operator<<(std::ostream& os, const error_info& e); ``` Calls `format_error(e)` and outputs it. # Related - [color.hpp](color.md) - [parser.hpp](parser.md) - [source_location.hpp](source_location.md) toml11-4.1.0/docs/content.en/docs/reference/exception.md000066400000000000000000000011471464712047700230610ustar00rootroot00000000000000+++ title = "exception.hpp" type = "docs" +++ # exception.hpp # `toml::exception` Base class for exception types defined in toml11. ```cpp namespace toml { struct exception : public std::exception { public: virtual ~exception() noexcept override = default; virtual const char* what() const noexcept override {return "";} }; } // toml ``` ## Member Functions ### Destructor ```cpp virtual ~exception() noexcept override = default; ``` Override when derived. ### `what` ```cpp virtual const char* what() const noexcept override {return "";} ``` Returns the error message. Override when derived. toml11-4.1.0/docs/content.en/docs/reference/find.md000066400000000000000000000202421464712047700220000ustar00rootroot00000000000000+++ title = "find.hpp" type = "docs" +++ # find.hpp This function searches for a value in a `toml::value` and performs type conversion if necessary. {{< hint info >}} `toml::value` can change the type it stores, and `toml::find` accommodates these types. Technically, all functions use `toml::basic_value`. However, for simplicity, we refer to it as `toml::value` in explanations unless a distinction is necessary. In the documentation, if the template parameter `TC` changes the type, assume that types like `toml::value::integer_type` will also change accordingly. {{< /hint >}} # `toml::find` ## Overview `toml::find` uses template arguments for the type you want to retrieve and function arguments for the key of the value you want to find. ```cpp template T find(const basic_value& v, Keys ... keys); ``` The supported types for `T` and the behavior of the conversion are the same as for `toml::get`. If `T` is not specified, a `toml::value` will be returned. Keys can be of type `toml::value::key_type` or `std::size_t`. When multiple keys are provided, the function will search recursively through sub-tables or arrays. If a `toml::value::key_type` is given, `toml::value` is interpreted as a `toml::table`; if a `std::size_t` is given, `toml::value` is interpreted as a `toml::array`. ### Note on Recursive Search TOML allows for bare keys as well as quoted keys (enclosed in `"` or `'`). Using a quoted key like `"foo.bar" = "baz"` means no sub-table is constructed, and the key is `"foo.bar"`. To handle such patterns, toml11 does not split keys containing `.` and searches using the full string. Consider the following TOML file: ```toml [foo] [foo.bar] baz = "hoge" ["foo.bar"] baz = "fuga" ``` The corresponding usage of `toml::find` is shown below: ```cpp const auto input = toml::parse("input.toml"); const auto baz1 = toml::find(input, "foo", "bar", "baz"); // hoge const auto baz2 = toml::find(input, "foo.bar", "baz"); // fuga ``` cf. [toml.io/en/v1.0.0#keys](https://toml.io/en/v1.0.0#keys) ## `toml::find(value, key)` Searches for `key` in `value` as if `value` were a `toml::table`, then converts using `toml::get`. ```cpp template /* Equivalent to toml::get(const value&) */ find( const basic_value& v, const typename basic_value::key_type& ky); template /* Equivalent to toml::get(value&) */ find( basic_value& v, const typename basic_value::key_type& ky); template /* Equivalent to toml::get(value&&) */ find( basic_value&& v, const typename basic_value::key_type& ky); ``` If `T` is not specified, the function returns a `toml::value` without conversion. ```cpp template basic_value const& find( basic_value const& v, const typename basic_value::key_type& ky); template basic_value& find( basic_value& v, const typename basic_value::key_type& ky); template basic_value find( basic_value&& v, const typename basic_value::key_type& ky); ``` ### Exceptions If the `toml::value` does not contain a `table`, a `toml::type_error` is thrown. If the contained `table` does not have the specified element, a `std::out_of_range` is thrown. If the specified element cannot be converted to `T` (i.e., `toml::get` fails), a `toml::type_error` is thrown. ## `toml::find(value, index)` Accesses the `index`-th element of `value` as if `value` were a `toml::array`, then converts using `toml::get`. ```cpp template /* Equivalent to toml::get(const value&) */ find( const basic_value& v, const std::size_t index); template /* Equivalent to toml::get(value&) */ find( basic_value& v, const std::size_t index); template /* Equivalent to toml::get(value&&) */ find( basic_value&& v, const std::size_t index); ``` If `T` is not specified, the function returns a `toml::value` without conversion. ```cpp template basic_value const& find(basic_value const& v, const std::size_t ky); template basic_value& find(basic_value& v, const std::size_t ky); template basic_value find(basic_value&& v, const std::size_t ky); ``` ### Exceptions If the `toml::value` does not contain an `array`, a `toml::type_error` is thrown. If the contained `array` does not have the specified number of elements, a `std::out_of_range` is thrown. If the specified element cannot be converted to `T` (i.e., `toml::get` fails), a `toml::type_error` is thrown. ## `toml::find(value, keys...)` ```cpp template /* Equivalent to toml::get(const value&) */ find( const basic_value& v, const K1& k1, const K2& k2, const Ks& ... ks); template /* Equivalent to toml::get(value&) */ find( basic_value& v, const K1& k1, const K2& k2, const Ks& ... ks); template /* Equivalent to toml::get(value&&) */ find( basic_value&& v, const K1& k1, const K2& k2, const Ks& ... ks); ``` This function calls `toml::find` recursively. The failure conditions and the exceptions thrown are the same as those for `toml::find`. # `toml::find_or(value, key, fallback)` ```cpp template T find_or(const basic_value& v, const Key& key, T&& opt); ``` The `find_or` function takes a default value to avoid throwing an exception when the search fails. The default value must be of the same type as the return type `T`. Therefore, unlike `toml::find`, `find_or` infers the type `T`. You can specify `T` explicitly with `find_or`, but this always returns a new value. To obtain a reference, do not specify `T`. ## When `T` is `basic_value` ```cpp template basic_value& find_or(basic_value& v, const K& key, basic_value& opt) noexcept template basic_value const& find_or(const basic_value& v, const K& key, const basic_value& opt) noexcept ``` Searches for the corresponding value and returns it without conversion. Because no conversion is needed, a reference can be returned. If the value is not found, the default value is returned. ## When `T` is `toml::value::{some_type}` ```cpp template T& find_or(basic_value& v, const K& key, T& opt) noexcept template T const& find_or(const basic_value& v, const K& key, const T& opt) noexcept ``` Searches for the corresponding value and returns it without conversion. Because no conversion is needed, a reference can be returned. If the value is not found or if a different type is stored, the default value is returned. ## When `T` is `const char*` ```cpp template T find_or(const basic_value& v, const K& key, T opt) ``` Searches for the corresponding value and returns it as a `std::string`. Since the fallback is constructed from `const char*` to `std::string`, a reference cannot be returned in case of failure. ## When `T` is any other type ```cpp template T find_or(const basic_value& v, const K& key, T opt); ``` Searches for the corresponding value and converts it to `T` before returning it. Because conversion is performed, a reference cannot be returned. ## When multiple keys are provided ```cpp template auto find_or(Value&& v, const K1& k1, const K2& k2, K3&& k3, Ks&& ... keys) noexcept -> decltype(find_or(v, k2, std::forward(k3), std::forward(keys)...)) ``` Interprets the last element in the key sequence as the default value and applies `find_or` recursively. If the inferred type of `T` is `toml::value` or `toml::value::some_type`, a reference can be returned. If `T` is explicitly specified, conversion is always performed. # Related - [get.hpp]({{}}) - [value.hpp]({{}}) toml11-4.1.0/docs/content.en/docs/reference/format.md000066400000000000000000000261441464712047700223570ustar00rootroot00000000000000+++ title = "format.hpp" type = "docs" +++ # format.hpp Defines structures and enumerations related to formatting information for `toml::value`. # `indent_char` An enumeration representing the indentation character choice. ```cpp enum class indent_char : std::uint8_t { space, // use space tab, // use tab none // no indent }; std::ostream& operator<<(std::ostream& os, const indent_char& c); std::string to_string(const indent_char); ``` Choosing `none` means no indentation is used, regardless of the value in super tables. If both `space` and `tab` are specified within the serializable value, the behavior is unspecified; typically, the unspecified indentation character appears. # `boolean_format_info` Formatting information for `boolean`. ```cpp struct boolean_format_info {}; bool operator==(const boolean_format_info&, const boolean_format_info&) noexcept; bool operator!=(const boolean_format_info&, const boolean_format_info&) noexcept; ``` There is only one way to format `boolean`, so no configurable values are provided. # `integer_format` ```cpp enum class integer_format : std::uint8_t { dec = 0, bin = 1, oct = 2, hex = 3, }; std::ostream& operator<<(std::ostream& os, const integer_format f); std::string to_string(const integer_format); ``` Specifies the radix of an `integer`. # `integer_format_info` ```cpp struct integer_format_info { integer_format fmt = integer_format::dec; bool uppercase = true; // use uppercase letters std::size_t width = 0; // minimal width (may exceed) std::size_t spacer = 0; // position of `_` (if 0, no spacer) std::string suffix = ""; // _suffix (library extension) }; bool operator==(const integer_format_info&, const integer_format_info&) noexcept; bool operator!=(const integer_format_info&, const integer_format_info&) noexcept; ``` ## Member Variables ### `integer_format fmt` Specifies the radix. ### `bool uppercase` Uses uppercase letters when formatted as a hexadecimal integer. ### `std::size_t width` Specifies the minimum width. The formatted value may exceed this width. For values smaller than this width, if `integer_format::dec`, no effect. Otherwise, leading zeros are added. ### `std::size_t spacer` Specifies the width at which underscores `_` are inserted. - If `3`, formatted as `1_234_567`. - If `4`, formatted as `0xdead_beef`. - If `0`, no underscores are inserted. Irregular widths are not allowed. ### `std::string suffix` Stores the suffix when `spec::ext_num_suffix` of toml11 extension is `true`. cf. [spec.hpp](spec.md) # `floating_format` ```cpp enum class floating_format : std::uint8_t { defaultfloat = 0, fixed = 1, // does not include exponential part scientific = 2, // always include exponential part hex = 3 // hexfloat extension }; std::ostream& operator<<(std::ostream& os, const floating_format f); std::string to_string(const floating_format); ``` Specifies the formatting style for `floating` numbers. Corresponds to `std::defaultfloat`, `std::fixed`, `std::scientific`, `std::hexfloat`. `hexfloat` is available only if `toml::spec::ext_hex_float` is `true`. cf. [spec.hpp](spec.md) # `floating_format_info` ```cpp struct floating_format_info { floating_format fmt = floating_format::defaultfloat; std::size_t prec = 0; // precision (if 0, use the default) std::string suffix = ""; // 1.0e+2_suffix (library extension) }; bool operator==(const floating_format_info&, const floating_format_info&) noexcept; bool operator!=(const floating_format_info&, const floating_format_info&) noexcept; ``` ## Member Variables ### `floating_format fmt` Specifies the formatting style. ### `std::size_t prec` Specifies the precision after the decimal point. ### `std::string suffix` Stores the suffix when `spec::ext_num_suffix` of toml11 extension is `true`. cf. [spec.hpp](spec.md) # `string_format` ```cpp enum class string_format : std::uint8_t { basic = 0, literal = 1, multiline_basic = 2, multiline_literal = 3 }; std::ostream& operator<<(std::ostream& os, const string_format f); std::string to_string(const string_format); ``` Specifies the formatting style for strings. # `string_format_info` ```cpp struct string_format_info { string_format fmt = string_format::basic; bool start_with_newline = false; }; bool operator==(const string_format_info&, const string_format_info&) noexcept; bool operator!=(const string_format_info&, const string_format_info&) noexcept; ``` ## Member Variables ### `string_format fmt` Specifies the formatting information for strings. ### `bool start_with_newline` For `multiline_basic` or `multiline_literal`, specifies whether to include a newline after the initial `"""` or `'''`. # `datetime_delimiter_kind` ```cpp enum class datetime_delimiter_kind : std::uint8_t { upper_T = 0, lower_t = 1, space = 2, }; std::ostream& operator<<(std::ostream& os, const datetime_delimiter_kind d); std::string to_string(const datetime_delimiter_kind); ``` Specifies the delimiter used between date and time in `datetime`. Possible options include `T`, `t`, and a space ` `. # `offset_datetime_format_info` ```cpp struct offset_datetime_format_info { datetime_delimiter_kind delimiter = datetime_delimiter_kind::upper_T; bool has_seconds = true; std::size_t subsecond_precision = 6; // [us] }; bool operator==(const offset_datetime_format_info&, const offset_datetime_format_info&) noexcept; bool operator!=(const offset_datetime_format_info&, const offset_datetime_format_info&) noexcept; ``` ## Member Variables ### `datetime_delimiter_kind delimiter` Specifies the delimiter to use. ### `bool has_seconds` Specifies whether to omit seconds. ### `std::size_t subsecond_precision` Specifies how many digits to output for subseconds. # `local_datetime_format_info` ```cpp struct local_datetime_format_info { datetime_delimiter_kind delimiter = datetime_delimiter_kind::upper_T; bool has_seconds = true; std::size_t subsecond_precision = 6; // [us] }; bool operator==(const local_datetime_format_info&, const local_datetime_format_info&) noexcept; bool operator!=(const local_datetime_format_info&, const local_datetime_format_info&) noexcept; ``` ## Member Variables ### `datetime_delimiter_kind delimiter` Specifies the delimiter to use. ### `bool has_seconds` Specifies whether to omit seconds. ### `std::size_t subsecond_precision` Specifies how many digits to output for subseconds. # `local_date_format_info` ```cpp struct local_date_format_info { // nothing, for now }; bool operator==(const local_date_format_info&, const local_date_format_info&) noexcept; bool operator!=(const local_date_format_info&, const local_date_format_info&) noexcept; ``` No formatting parameters are specified for `local_date`. # `local_time_format_info` ```cpp struct local_time_format_info { bool has_seconds = true; std::size_t subsecond_precision = 6; // [us] }; bool operator==(const local_time_format_info&, const local_time_format_info&) noexcept; bool operator!=(const local_time_format_info&, const local_time_format_info&) noexcept; ``` ## Member Variables ### `bool has_seconds` Specifies whether to omit seconds. ### `std::size_t subsecond_precision` Specifies how many digits to output for subseconds. # `array_format` ```cpp enum class array_format : std::uint8_t { default_format = 0, oneline = 1, multiline = 2, array_of_tables = 3 // [[format.in.this.way]] }; std::ostream& operator<<(std::ostream& os, const array_format f); std::string to_string(const array_format); ``` - `default_format` - Automatically selects the appropriate format. Longer arrays may span multiple lines. - `oneline` - Formats all elements in a single line. - `multiline` - Outputs each element on its own line. - `array_of_tables` - Formats in the `[[array.of.tables]]` style. Cannot contain elements other than `table`. # `array_format_info` ```cpp struct array_format_info { array_format fmt = array_format::default_format; indent_char indent_type = indent_char::space; std::int32_t body_indent = 4; // indent in case of multiline std::int32_t closing_indent = 0; // indent of `]` }; bool operator==(const array_format_info&, const array_format_info&) noexcept; bool operator!=(const array_format_info&, const array_format_info&) noexcept; ``` ## Member Variables ### `array_format fmt` Specifies the format style. ### `indent_char indent_type` Selects the type of character used for indentation. ### `std::int32_t body_indent` Specifies the number of characters to indent before each element in `array_format::multiline`. ### `std::int32_t closing_indent` Specifies the number of characters to indent before the closing bracket `]` in `array_format::multiline`. # `table_format` ```cpp enum class table_format : std::uint8_t { multiline = 0, // [foo] \n bar = "baz" oneline = 1, // foo = {bar = "baz"} dotted = 2, // foo.bar = "baz" multiline_oneline = 3, // foo = { \n bar = "baz" \n } implicit = 4 // [x] defined by [x.y.z]. skip in serializer. }; std::ostream& operator<<(std::ostream& os, const table_format f); std::string to_string(const table_format); ``` - `multiline` - Formats as a multiline normal table. - `oneline` - Formats as an inline table. - `dotted` - Formats in the form of `a.b.c = "d"`. - `multiline_oneline` - Formats as a multiline inline table with line breaks. Available from TOML v1.1.0 onwards. - cf. [spec.hpp](spec.md) - `implicit` - Skips implicit definitions like `[x.y.z.w]`, leaving `[x]`, `[x.y]`, `[x.y.z]` as implicit. {{< hint warning >}} According to TOML syntax, `dotted` table can have sub-tables: ```toml [fruit] apple.color = "red" apple.taste.sweet = true # [fruit.apple] # INVALID # [fruit.apple.taste] # INVALID [fruit.apple.texture] # you can add sub-tables smooth = true ``` toml11 currently does not support this format. Sub-tables under a `dotted` table would all be `dotted`, and tables are forced into inline table format. {{< /hint >}} # `table_format_info` ```cpp struct table_format_info { table_format fmt = table_format::multiline; indent_char indent_type = indent_char::space; std::int32_t body_indent = 0; // indent of values std::int32_t name_indent = 0; // indent of [table] std::int32_t closing_indent = 0; // in case of {inline-table} }; bool operator==(const table_format_info&, const table_format_info&) noexcept; bool operator!=(const table_format_info&, const table_format_info&) noexcept; ``` ## Member Variables ### `table_format fmt` Specifies the formatting method. ### `indent_char indent_type` Specifies the character used for indentation. ### `std::int32_t body_indent` Specifies the width of indentation before keys. The indentation width of the super table is not added. ### `std::int32_t name_indent` Specifies the indentation of keys in `[table]` format. The indentation width of the super table is not added. ### `std::int32_t closing_indent` Specifies the indentation width before the closing brace `}` in the case of `multiline_oneline`. toml11-4.1.0/docs/content.en/docs/reference/from.md000066400000000000000000000016661464712047700220340ustar00rootroot00000000000000+++ title = "from.hpp" type = "docs" +++ # from.hpp Defines a `struct` used for conversion from `toml::value` in `toml::get` and `toml::find`. You can achieve the same functionality by adding a `from_toml` member function, but for classes where you cannot add member functions, use `from`. This file does not provide specific implementations. Please specialize this `struct` when using. ```cpp namespace toml { template struct from; } // toml ``` ## Example ```cpp namespace extlib { struct foo { int a; std::string b; }; } // extlib #include namespace toml { template<> struct from { template static extlib::foo from_toml(const toml::basic_value& v) { return extlib::foo{toml::find(v, "a"), toml::find(v, "b")}; } }; } // toml ``` # Related - [conversion.hpp]({{}}) - [into.hpp]({{}}) toml11-4.1.0/docs/content.en/docs/reference/get.md000066400000000000000000000323511464712047700216430ustar00rootroot00000000000000+++ title = "get.hpp" type = "docs" +++ # get.hpp These are functions for extracting values from `toml::value` and performing type conversions if necessary. {{< hint info >}} `toml::value` can change the type it stores, and `toml::get` accommodates these types. Technically, all functions use `toml::basic_value`. However, for simplicity, we refer to it as `toml::value` in explanations unless a distinction is necessary. In the documentation, if the template parameter `TC` changes the type, assume that types like `toml::value::integer_type` will also change accordingly. {{< /hint >}} # `toml::get` ## Overview Generally, `toml::get` behaves as follows: You specify `T` as in `toml::get(v)`. ```cpp template T get(const basic_value& v); ``` However, depending on the type of `T`, `toml::get` can exhibit different behaviors. The types of `T` can be categorized into: 1. Types that do not require conversion 2. Types that require conversion Detailed conditions and the specific types supported are discussed later. ### Types that Do Not Require Conversion No conversion is needed if the provided `toml::value` is already storing the desired type. For instance, since `toml::value::integer_type` is an alias for `std::int64_t`, `toml::get(v)` requires no conversion. In this case, `toml::get` retrieves the `integer` value from `toml::value` and returns a reference to it. If the provided `toml::value` is a mutable reference (`&`), the returned value is also a mutable reference (`&`). If it is an immutable reference (`const&`), the returned value will also be an immutable reference (`const&`). Returning a mutable reference allows you to overwrite the value stored in `toml::value` through that reference. ### Types that Require Conversion Types other than the ones mentioned above require conversion. For example, since `toml::value::integer_type` is an alias for `std::int64_t`, `toml::get(toml::value&)` requires conversion. In this case, `toml::get` retrieves the `integer` value from `toml::value` and casts it to return the appropriate type. toml11 supports not only simple casts but also complex type conversions like converting from `toml::array` to `std::tuple`, `std::array`, or from `toml::table` to `std::map`. For specifics, refer to the subsequent sections. ### When Conversion Fails Sometimes, the expected type conversion cannot be performed. For example, applying `toml::get(v)` to a `toml::value` that holds a `table`. In such cases, an attempt to convert to the type most similar to the desired type (in this case, `int` using `as_integer`) fails, and a `toml::type_error` is thrown. When parsing from a file, an error message similar to the following is output: ``` terminate called after throwing an instance of 'toml::type_error' what(): toml::value::as_integer(): bad_cast to integer --> input.toml | 6 | [fruit] | ^^^^^^^-- the actual type is table ``` ## When `T` is identical to `toml::value` ```cpp template T& get(basic_value& v); template T const& get(const basic_value& v); template T get(basic_value&& v); ``` Condition: - `std::is_same>` is satisfied. Since this involves retrieving `toml::value` from `toml::value`, no conversion is performed, and the value is returned as is. This exists solely to generalize the implementation of other functions. This does not fail. ## When `T` is one of `toml::value::{some_type}` ```cpp template T& get(basic_value& v); template T const& get(const basic_value& v); template T get(basic_value&& v); ``` Condition: - `T` must be the same as one of the types that `toml::value` can store (e.g., `toml::value::boolean_type`). If `toml::value` is storing a type that matches the specified type in `toml::get`, such as `toml::value::integer_type`, no type conversion is needed, and a reference can be returned. If a different type is stored, a `toml::type_error` is thrown. ## When `T` is `basic_value` with a different `TypeConfig` ```cpp template T get(basic_value& v); ``` Condition: - `T` is not `toml::basic_value`. - `T` is `toml::basic_value`. When a `basic_value` that can store different types is specified, conversion is performed. Since type conversion occurs, the returned value is a new value and not a reference. This does not fail (except in cases like memory exhaustion). ## When `T` is an integer type ```cpp template T get(basic_value& v); ``` Condition: - `std::is_integral` is satisfied - `T` is not `bool` - `T` is not `toml::value::integer_type` The function assumes that `toml::value` holds an `integer_type`, retrieves its value, converts it to `T`, and returns it. If a type other than `toml::value::integer_type` is stored, a `toml::type_error` is thrown. ## When `T` is a floating-point type ```cpp template T get(basic_value& v); ``` Condition: - `std::is_floating_point` is satisfied - `T` is not `toml::value::floating_type` The function assumes that `toml::value` holds a `floating_type`, retrieves its value, converts it to `T`, and returns it. If a type other than `toml::value::floating_type` is stored, a `toml::type_error` is thrown. ## When `T` is `std::string_view` This is only available in C++17 and later. ```cpp template T get(basic_value& v); ``` Condition: - `std::is_same` is satisfied The function assumes that `toml::value` holds a `string_type`, retrieves its value, constructs a `std::string_view` from it, and returns it. If a type other than `toml::value::string_type` is stored, a `toml::type_error` is thrown. ## When `T` is `std::chrono::duration` ```cpp template T get(basic_value& v); ``` Condition: - `T` is `std::chrono::duration` The function assumes that `toml::value` holds a `local_time`, retrieves its value, converts it to `std::chrono::duration`, and returns it. If a type other than `toml::value::local_time` is stored, a `toml::type_error` is thrown. ## When `T` is `std::chrono::system_clock::time_point` ```cpp template T get(basic_value& v); ``` Condition: - `std::is_same` is satisfied If the `toml::value` holds a `local_date`, `local_datetime`, or `offset_datetime`, this function retrieves the value and converts it to `std::chrono::system_clock::time_point`, returning the result. If the value is of a type other than `local_date`, `local_datetime`, or `offset_datetime`, a `toml::type_error` is thrown. ## When `T` is array-like ```cpp template T get(basic_value& v); ``` Conditions: - `T` has an `iterator` - `T` has a `value_type` - `T` supports `push_back(x)` - `T` is not `toml::value::array_type` - `T` is not `std::string` - `T` is not `std::string_view` - `T` is not map-like - `T` does not have `from_toml()` member function - `toml::from` is not defined - A constructor from `toml::basic_value` is not defined This includes types like `std::vector` and `std::deque`. If the `toml::value` holds an `array`, this function retrieves the value and converts it to the specified container type, returning the result. If the value is of a type other than `toml::value::array_type`, a `toml::type_error` is thrown. ## When `T` is `std::array` ```cpp template T get(basic_value& v); ``` 条件: - `T` is `std::array` If the `toml::value` holds an `array`, this function retrieves the value and converts it to the specified container type, returning the result. If the value is of a type other than `toml::value::array_type`, a `toml::type_error` is thrown. If the `array` held by `toml::value` does not contain enough elements, a `std::out_of_range` is thrown. ## When `T` is `std::forward_list` ```cpp template T get(basic_value& v); ``` Condition: - `T` is `std::forward_list` If the `toml::value` holds an `array`, this function retrieves the value and converts it to a `std::forward_list`, returning the result. If the value is of a type other than `toml::value::array_type`, a `toml::type_error` is thrown. ## When `T` is `std::pair` ```cpp template T get(basic_value& v); ``` Condition: - `T` is `std::pair` If the `toml::value` holds an `array`, this function retrieves the value and converts it to `std::pair`, returning the result. The `first` and `second` elements are recursively converted. If the value is of a type other than `basic_value::array_type`, a `toml::type_error` is thrown. If the `array` held by `toml::value` does not contain exactly 2 elements, a `std::out_of_range` is thrown. ## When `T` is `std::tuple` ```cpp template T get(basic_value& v); ``` Condition: - `T` is `std::tuple` If the `toml::value` holds an `array`, this function retrieves the value and converts it to `std::tuple`, returning the result. Each element is recursively converted. If the value is of a type other than `basic_value::array_type`, a `toml::type_error` is thrown. If the `array` held by `toml::value` does not contain exactly `std::tuple_size::value` elements, a `std::out_of_range` is thrown. ## When `T` is map-like ```cpp template T get(basic_value& v); ``` Conditions: - `T` has an `iterator` - `T` has a `key_type` - `T` has a `value_type` - `T` has a `mapped_type` - `T` is not `toml::value::table_type` - `T` does not have a `from_toml()` member function - `toml::from` is not defined - A constructor from `toml::basic_value` is not defined This includes types like `std::map` and `std::unordered_map`. If the `toml::value` holds a `table`, this function retrieves the value and converts it to `T`, returning the result. Elements are recursively converted. If the value is of a type other than `basic_value::table_type`, a `toml::type_error` is thrown. ## When `T` is a user-defined type with a specialization of `toml::from` ```cpp template T get(basic_value& v); ``` Condition: - A specialization of `toml::from` is defined If a specialization of `toml::from` for `T` is defined, it is used for type conversion. Ensure this does not conflict with individually supported types (`std::array`, `std::pair`, `std::tuple` etc). ## When `T` is a user-defined type with a `from_toml` member function ```cpp template T get(basic_value& v); ``` Conditions: - `toml::from` is not defined - `T` has `from_toml()` member function If `T` has a `from_toml(toml::basic_value)` member function, it is used for type conversion. If `toml::from` is defined, it takes precedence. ## When `T` is a user-defined type with a constructor that takes `toml::basic_value` ```cpp template T get(basic_value& v); ``` Conditions: - `toml::from` is not defined - `T` does not have `from_toml()` member function - `T` has a constructor that takes `toml::basic_value` If `T` has a constructor that takes `toml::basic_value`, it is used for type conversion. If `toml::from` or `T::from_toml` is defined, they take precedence. # `toml::get_or` `get_or` takes a default value for use when the conversion fails, avoiding exceptions. The default value must be of the same type as the target type `T`. Therefore, unlike `toml::get`, `T` can be inferred in `get_or`. ## When `T` is `basic_value` ```cpp template basic_value const& get_or(const basic_value& v, const basic_value& opt) template basic_value & get_or(basic_value& v, basic_value& opt) template basic_value get_or(basic_value&& v, basic_value&& opt) ``` Since the conversion target is the same `toml::value`, this never fails. It exists solely to generalize the implementation of other functions. ## When `T` is `basic_value::{some_type}` ```cpp template T const& get_or(const basic_value& v, const T& opt) noexcept template T & get_or(basic_value& v, T& opt) noexcept template T get_or(basic_value&& v, T&& opt) noexcept ``` Performs the same conversion as `toml::get`. If it fails, the second argument is returned. ## When `T` is `const char*` ```cpp template typename basic_value::string_type get_or(const basic_value& v, const typename basic_value::string_type::value_type* opt); ``` When `const char*` is passed, the conversion target is interpreted as `std::string`. ## When `T` is something else ```cpp template typename std::remove_cv::type>::type get_or(const basic_value& v, T&& opt); ``` Performs the same conversion as `toml::get`. If it fails, the second argument is returned. # Related - [find.hpp]({{}}) - [from.hpp]({{}}) - [value.hpp]({{}}) toml11-4.1.0/docs/content.en/docs/reference/into.md000066400000000000000000000020231464712047700220260ustar00rootroot00000000000000+++ title = "into.hpp" type = "docs" +++ # into.hpp Defines a `struct` used for conversion from user-defined types into `toml::value` constructors. You can achieve the same functionality by adding an `into_toml` member function, but for classes where you cannot add member functions, use `into`. This file does not provide specific implementations. Please specialize this `struct` when using. ```cpp namespace toml { template struct into; } // toml ``` ## Example ```cpp namespace extlib { struct foo { int a; std::string b; }; } // extlib #include namespace toml { template<> struct into { template static toml::basic_value into_toml(const extlib::foo& f) { using value_type = toml::basic_value; using table_type = typename value_type::table_type; return value_type(table_type{{"a", f.a}, {"b", f.b}}); } }; } // toml ``` # Related - [conversion.hpp]({{}}) - [from.hpp]({{}}) toml11-4.1.0/docs/content.en/docs/reference/literal.md000066400000000000000000000033131464712047700225140ustar00rootroot00000000000000+++ title = "literal.hpp" type = "docs" +++ # literal.hpp In `literal.hpp`, the `_toml` literal is defined. The `_toml` literal parses string literals into `toml::value`. ```cpp namespace toml { inline namespace literals { inline namespace toml_literals { toml::value operator"" _toml(const char* str, std::size_t len); toml::value operator"" _toml(const char8_t* str, std::size_t len); // Available in C++20 and later } // toml_literals } // literals } // toml ``` ## Free Functions ### `operator"" _toml(const char*)` ```cpp toml::value operator"" _toml(const char* str, std::size_t len); ``` Converts a string literal into a `toml::value` by parsing it. For typical TOML files, this performs equivalent parsing to `toml::parse`. ```cpp const auto v1 = "a = 'foo'"_toml; // v1: {a = 'foo'} ``` When dealing with multiline content, raw string literal is convenient. ```cpp const auto v1 = R"( a = 42 b = "foo" )"_toml; ``` If the value is a standalone entity, it represents that value. ```cpp const auto v2 = "'foo'"_toml; // v2: 'foo' ``` TOML allows keys consisting solely of numbers. When distinguishing between table definitions and arrays is ambiguous (e.g., `[1]`), table definitions take precedence. To interpret as an array, use a trailing comma. ```cpp const auto v3 = "[1]"_toml; // v3: {1 = {}} const auto v4 = "[1,]"_toml; // v4: [1,] ``` ### `operator"" _toml(const char8_t*)` Defined when `char8_t` is available. Otherwise identical in functionality, differing only in argument type. # Example ```cpp #include int main() { using namespace toml::literals::toml_literals; const auto v = "a = \"foo\""_toml; assert(v.at("a").as_string() == "foo"); return 0; } ``` toml11-4.1.0/docs/content.en/docs/reference/ordered_map.md000066400000000000000000000202171464712047700233430ustar00rootroot00000000000000+++ title = "ordered_map.hpp" type = "docs" +++ # ordered_map.hpp Defines `toml::ordered_map`, which is used to maintain the order of values in a file. # `class ordered_map` ```cpp namespace toml { template, typename Allocator = std::allocator>> class ordered_map; } ``` The `ordered_map` is a `map` type that preserves the insertion order of values, allowing iteration in that order. As a linear container, searches require `O(n)` time relative to the number of elements. Use this when search operations are infrequent and maintaining the order of values is important. ## Non-Member Types ```cpp namespace toml { struct ordered_type_config; using ordered_value = basic_value; using ordered_table = typename ordered_value::table_type; using ordered_array = typename ordered_value::array_type; } ``` Use these in place of `toml::type_config` and `toml::value`. {{< hint info >}} Since `toml::parse` defaults to using `type_config`, specify ```cpp const auto input = toml::parse("input.toml"); ``` when parsing. {{< /hint >}} ## Member Types ```cpp using key_type = Key; using mapped_type = Val; using value_type = std::pair; using key_compare = Cmp; using allocator_type = Allocator; using container_type = std::vector; using reference = typename container_type::reference; using pointer = typename container_type::pointer; using const_reference = typename container_type::const_reference; using const_pointer = typename container_type::const_pointer; using iterator = typename container_type::iterator; using const_iterator = typename container_type::const_iterator; using size_type = typename container_type::size_type; using difference_type = typename container_type::difference_type; ``` ## Member Functions ### Constructors ```cpp ordered_map() = default; ``` Constructs an empty `ordered_map`. ### Constructors (Comparator, Allocator) ```cpp explicit ordered_map(const Cmp& cmp, const Allocator& alloc = Allocator()); explicit ordered_map(const Allocator& alloc); ``` Constructs an `ordered_map` with a specified comparator for key comparison and an allocator for memory management. ### Copy and Move Constructors ```cpp ordered_map(const ordered_map&) = default; ordered_map(ordered_map&&) = default; ordered_map(const ordered_map& other, const Allocator& alloc); ordered_map(ordered_map&& other, const Allocator& alloc); ``` Constructs an `ordered_map` by copying or moving the contents from another `ordered_map`. An allocator can also be specified for memory management. ### Constructors (Iterator) ```cpp template ordered_map(InputIterator first, InputIterator last, const Cmp& cmp = Cmp(), const Allocator& alloc = Allocator()); template ordered_map(InputIterator first, InputIterator last, const Allocator& alloc = Allocator()); ``` Constructs an `ordered_map` with a range represented by iterators. The order of the elements follows the order of the iterators. ### Constructors (std::initializer_list) ```cpp ordered_map(std::initializer_list v, const Cmp& cmp = Cmp(), const Allocator& alloc = Allocator()); ordered_map(std::initializer_list v, const Allocator& alloc); ``` Initializes the `ordered_map` using an initializer list. ### Copy and Move Assignment Operators ```cpp ordered_map& operator=(const ordered_map&) = default; ordered_map& operator=(ordered_map&&) = default; ``` Assigns the contents of another `ordered_map` to this one, using copy or move semantics. ### Assignment Operator (std::initializer_list) ```cpp ordered_map& operator=(std::initializer_list v); ``` Assigns the contents of an initializer list to the `ordered_map`. ### Destructor ```cpp ~ordered_map() = default; ``` Destroys the `ordered_map`. ### `begin()`, `end()` ```cpp iterator begin() noexcept; iterator end() noexcept; const_iterator begin() const noexcept; const_iterator end() const noexcept; const_iterator cbegin() const noexcept; const_iterator cend() const noexcept; ``` Returns iterators to the beginning and end of the container, allowing iteration over its contents in order. ### `empty()` ```cpp bool empty() const noexcept; ``` Returns `true` if the `ordered_map` is empty, `false` otherwise. ### `size()` ```cpp std::size_t size() const noexcept; ``` Returns the number of elements in the `ordered_map`. ### `max_size()` ```cpp std::size_t max_size() const noexcept; ``` Returns the maximum number of elements the `ordered_map` can hold. ### `clear()` ```cpp void clear(); ``` Clears all elements from the `ordered_map`. ### `push_back(kv)` ```cpp void push_back(const value_type&); void push_back(value_type&&); ``` Appends a key-value pair to the end of the `ordered_map`. ### `emplace_back(k, v)` ```cpp void emplace_back(key_type, mapped_type); ``` Appends a key-value pair to the end of the `ordered_map` by constructing it in place. ### `pop_back()` ```cpp void pop_back(); ``` Removes the last element from the `ordered_map`. ### `insert(kv)` ```cpp void insert(value_type); ``` Inserts a key-value pair at the end of the `ordered_map`. ### `emplace(k, v)` ```cpp void emplace(key_type, mapped_type); ``` Inserts a key-value pair at the end of the `ordered_map` by constructing it in place. ### `count(k)` ```cpp std::size_t count(const key_type&) const noexcept; ``` Returns the number of elements with the specified key. Since duplicate keys are not allowed, this will return either `1` if the key exists or `0` if it does not. ### `contains(k)` ```cpp bool contains(const key_type&) const noexcept; ``` Returns `true` if the `ordered_map` contains an element with the specified key, `false` otherwise. ### `find(k)` ```cpp iterator find(const key_type& key) noexcept; const_iterator find(const key_type& key) const noexcept; ``` Finds an element with the specified key and returns an iterator to it. If the key is not found, returns `end()`. ### `at(k)` ```cpp mapped_type& at(const key_type& k); mapped_type const& at(const key_type& k) const; ``` Finds an element with the specified key and returns a reference to its value. Throws `std::out_of_range` if the key is not found. ### `operator[](k)` ```cpp mapped_type& operator[](const key_type& k); mapped_type const& operator[](const key_type& k) const; ``` Finds an element with the specified key and returns a reference to its value. If the key is not found, a new value is constructed and returned. If the `ordered_map` is `const`, throws `std::out_of_range` instead. ### `key_comp()` ```cpp key_compare key_comp() const; ``` Returns the comparator used for key comparison. ## Notes ### Key Modification {{< hint warning >}} Since `ordered_map` uses `std::pair` for `value_type`, it is possible to modify the key through an iterator. However, this practice is not recommended. If you modify a key this way and it conflicts with an existing key, one of the conflicting keys will become unsearchable. When using `operator[]`, `push_back`, or `insert`, collisions with existing keys are detected. {{< /hint >}} ### Order Preservation Details {{< hint warning >}} `ordered_map` maintains the order of keys, but this order preservation applies only to keys defined within the same table. Order across different tables is not maintained. For example, the order in the following file will be preserved: ```cpp apple.type = "fruit" apple.skin = "thin" apple.color = "red" orange.type = "fruit" orange.skin = "thick" orange.color = "orange" ``` In contrast, the order in the following file will not be preserved: ```cpp apple.type = "fruit" orange.type = "fruit" apple.skin = "thin" orange.skin = "thick" apple.color = "red" orange.color = "orange" ``` `ordered_map` preserves the order of the `apple` and `orange` definitions at the root table level, and the order of `type`, `skin`, `color` within each `apple` and `orange` table. {{< /hint >}} ## Related - [parser.hpp]({{}}) - [types.hpp]({{}}) - [value.hpp]({{}}) toml11-4.1.0/docs/content.en/docs/reference/parser.md000066400000000000000000000252431464712047700223620ustar00rootroot00000000000000+++ title = "parser.hpp" type = "docs" +++ # parser.hpp Defines functions for parsing files or strings and the exceptions they use. While `parse` throws an exception on failure, `try_parse` returns error information. # `parse` Parses the content of a given file and returns a `toml::basic_value`. In case of failure, `toml::syntax_error` is thrown. The type information of `basic_value` is provided by a `template`, and the TOML language version is specified by `toml::spec`. ### `parse(std::string filename, toml::spec)` ```cpp namespace toml { template basic_value parse(std::string fname, spec s = spec::default_version()); } ``` Parses the content of the given filename. If reading the file fails, `toml::file_io_error` is thrown. If parsing fails, `toml::syntax_error` is thrown. ### `parse(const char (&)[N] filename, toml::spec)` ```cpp namespace toml { template basic_value parse(const char (&fname)[N], spec s = spec::default_version()); } ``` Parses the content of the given filename from a string literal. If reading the file fails, `toml::file_io_error` is thrown. If parsing fails, `toml::syntax_error` is thrown. ### `parse(std::filesystem::path, toml::spec)` ```cpp namespace toml { template basic_value parse(const std::filesystem::path& fpath, spec s = spec::default_version()); } ``` This is defined only if `` is available. Parses the content of the file at the given file path. If reading the file fails, `toml::file_io_error` is thrown. If parsing fails, `toml::syntax_error` is thrown. ### `parse(std::istream&, std::string filename, toml::spec)` ```cpp namespace toml { template basic_value parse(std::istream& is, std::string fname = "unknown file", spec s = spec::default_version()); } ``` Parses the content of the given `std::istream&`. Open a stream in binary mode by passing `std::ios::binary` to avoid inconsistency between the file size and the number of characters due to automatic conversion of newline characters by the standard library. The filename information is taken as the third argument. If the filename is not provided, it defaults to `"unknown file"`. ### `parse(FILE*, std::string filename, toml::spec)` ```cpp namespace toml { template result, std::vector> parse(FILE* fp, std::string filename, spec s = spec::default_version()); } ``` Parses the content of the file pointed to by `FILE*`. Open a stream in binary mode by passing `"rb"` to avoid inconsistency between the file size and the number of characters due to automatic conversion of newline characters by the standard library. If reading the file fails, `file_io_error` containing `errno` is thrown. If parsing fails, `syntax_error` is thrown. ### `parse(std::vector, std::string filename, toml::spec)` ```cpp namespace toml { template basic_value parse(std::vector content, std::string filename, spec s = spec::default_version()); } ``` Parses the byte sequence as a TOML file. If parsing fails, `toml::syntax_error` is thrown. # `parse_str` ### `parse_str(std::string, toml::spec)` ```cpp namespace toml { template basic_value parse_str(std::string content, spec s = spec::default_version(), cxx::source_location loc = cxx::source_location::current()); } ``` Parses a string as a TOML file. In case of failure, `toml::syntax_error` is thrown. The type information of `basic_value` is provided by a `template`, and the TOML language version is specified by `toml::spec`. You generally don't need to manually set the third argument, `cxx::source_location`. If `std::source_location`, `std::experimental::source_location`, or `__builtin_FILE` is available, the location information where `parse_str` was called will be stored. # `try_parse` Parses the contents of the given file and returns a `toml::basic_value` if successful, or a `std::vector` if it fails. The type information of `basic_value` is specified by `template`, and the version of the TOML language is specified by `toml::spec`. {{< hint warning >}} Unlike `parse`, `try_parse` does not throw exceptions defined in toml11 such as `syntax_error`. However, please note that exceptions thrown by the standard library will still propagate. For instance, errors occurring internally within `std::ifstream` or memory exhaustion in `std::vector` will throw exceptions. {{< /hint >}} ### `try_parse(std::string filename, toml::spec)` ```cpp namespace toml { template result, std::vector> try_parse(std::string fname, spec s = spec::default_version()); } ``` Takes a file name and parses its content. If parsing fails, a `result` holding the error type `std::vector` is returned. If successful, a `result` holding a `basic_value` is returned. ### `try_parse(const char (&)[N] filename, toml::spec)` ```cpp namespace toml { template result, std::vector> try_parse(const char (&fname)[N], spec s = spec::default_version()); } ``` Takes a string literal as a file name and parses its content. If parsing fails, a `result` holding the error type `std::vector` is returned. If successful, a `result` holding a `basic_value` is returned. ### `try_parse(std::filesystem::path, toml::spec)` ```cpp namespace toml { template result, std::vector> try_parse(const std::filesystem::path& fpath, spec s = spec::default_version()); } ``` Takes a file path and parses its content. If parsing fails, a `result` holding the error type `std::vector` is returned. If successful, a `result` holding a `basic_value` is returned. ### `try_parse(std::istream&, std::string filename, toml::spec)` ```cpp namespace toml { template result, std::vector> try_parse(std::istream& is, std::string fname = "unknown file", spec s = spec::default_version()); } ``` Takes a `std::istream&` and parses its content. Open a stream in binary mode by passing `std::ios::binary` to avoid inconsistency between the file size and the number of characters due to automatic conversion of newline characters by the standard library. The file name information is taken as the second argument. If a file name is not provided, it defaults to `"unknown file"`. If parsing fails, a `result` holding the error type `std::vector` is returned. If successful, a `result` holding a `basic_value` is returned. ### `try_parse(FILE*, std::string filename, toml::spec)` ```cpp namespace toml { template result, std::vector> try_parse(FILE* fp, std::string filename, spec s = spec::default_version()); } ``` Takes a `FILE*` and parses its content. Open a stream in binary mode by passing `"rb"` to avoid inconsistency between the file size and the number of characters due to automatic conversion of newline characters by the standard library. If parsing fails, a `result` holding the error type `std::vector` is returned. If successful, a `result` holding a `basic_value` is returned. ### `try_parse(std::vector, std::string filename, toml::spec)` ```cpp namespace toml { template result, std::vector> try_parse(std::vector content, std::string filename, spec s = spec::default_version()); } ``` Takes a byte array and parses its content as a TOML file. If parsing fails, a `result` holding the error type `std::vector` is returned. If successful, a `result` holding a `basic_value` is returned. # `try_parse_str` ### `try_parse_str(std::string, toml::spec)` ```cpp namespace toml { template result, std::vector> try_parse_str(std::string content, spec s = spec::default_version(), cxx::source_location loc = cxx::source_location::current()); } ``` Parses a string as a TOML file, returning a `toml::basic_value` if successful, or a `std::vector` if it fails. Unlike `parse_str`, it does not throw `syntax_error`, but instead returns error information as the failure type of the `result`. If `std::source_location`, `std::experimental::source_location`, or `__builtin_FILE` is available, it will record the location information. Typically, you do not need to manually set the third argument `cxx::source_location`. If any of `std::source_location`, `std::experimental::source_location`, or `__builtin_FILE` are available, the information of the location where `parse_str` was called will be saved as the location information. {{< hint warning >}} Unlike `parse`, `try_parse` does not throw exceptions defined in toml11 such as `syntax_error`. However, please note that exceptions thrown by the standard library will still propagate. For instance, errors occurring internally within `std::ifstream` or memory exhaustion in `std::vector` will throw exceptions. {{< /hint >}} # `syntax_error` ```cpp namespace toml { struct syntax_error final : public ::toml::exception { public: syntax_error(std::string what_arg, std::vector err); ~syntax_error() noexcept override = default; const char* what() const noexcept override; std::vector const& errors() const noexcept }; } ``` An exception thrown when a syntax error is detected in TOML. It is thrown by `parse` but not by `try_parse`. # `file_io_error` ```cpp namespace toml { struct file_io_error final : public ::toml::exception { public: file_io_error(const std::string& msg, const std::string& fname); file_io_error(int errnum, const std::string& msg, const std::string& fname); ~file_io_error() noexcept override = default; const char* what() const noexcept override; bool has_errno() const noexcept; int get_errno() const noexcept; }; } ``` An exception thrown when reading the contents of a file fails. When using `FILE*` to read a file, `errno` is set. ### `has_errno` If `std::ifstream` fails, `errno` is not set. In this case, `has_errno` returns `false`. ### `get_errno` Particularly when passing a `FILE*`, retrieves the value of `errno`. If `has_errno` is `false`, it returns `0`. # Related - [error_info.hpp]({{}}) - [result.hpp]({{}}) - [spec.hpp]({{}}) - [value.hpp]({{}}) toml11-4.1.0/docs/content.en/docs/reference/result.md000066400000000000000000000250401464712047700223770ustar00rootroot00000000000000+++ title = "result.hpp" type = "docs" +++ # result.hpp `result.hpp` defines the `result` type, which can hold either a success value or a failure value. This is used as the return type for `toml::try_parse`, which does not throw exceptions. # success A type that holds a success value. ```cpp namespace toml { template struct success { using value_type = T; explicit success(value_type v); ~success() = default; success(const success&) = default; success(success&&) = default; success& operator=(const success&) = default; success& operator=(success&&) = default; template explicit success(U&& v); template explicit success(success v); value_type& get() noexcept; value_type const& get() const noexcept; }; template success::type> ok(T&& v); template success ok(const char (&literal)[N]) } ``` ## Member Types ```cpp using value_type = T; ``` The type of the success value. ## Member Functions ### Constructor ```cpp explicit success(value_type v); ``` Constructs with a `value_type` argument. ```cpp template explicit success(U&& v); ``` Constructs with another type that can be converted to `value_type`. ```cpp template explicit success(success v); ``` Constructs with another `success` type that can be converted to `value_type`. ### `get()` ```cpp value_type& get() noexcept; value_type const& get() const noexcept; ``` Accesses the stored value. ## Non-Member Functions ### `ok(T)` ```cpp template success::type> ok(T&& v); template success ok(const char (&literal)[N]); ``` Constructs and returns a `success` type from a success value. Converts a string literal into `std::string`. # `success>` Specialization of `success` for when the success value is a reference. ```cpp namespace toml { template struct success> { using value_type = T; explicit success(std::reference_wrapper v) noexcept; value_type& get() noexcept; value_type const& get() const noexcept; }; } ``` ## Member Types ```cpp using value_type = T; ``` The type of the success value. It is `T` from `std::reference_wrapper`, not the reference itself. ### `get()` ```cpp value_type& get() noexcept; value_type const& get() const noexcept; ``` Accesses the stored value. # failure A type that holds a failure value. ```cpp namespace toml { template struct failure { using value_type = T; explicit failure(value_type v); ~failure() = default; failure(const failure&) = default; failure(failure&&) = default; failure& operator=(const failure&) = default; failure& operator=(failure&&) = default; template explicit failure(U&& v); template explicit failure(failure v); value_type& get() noexcept; value_type const& get() const noexcept; }; template failure::type> err(T&& v); template failure err(const char (&literal)[N]); } ``` ## Member Types ```cpp using value_type = T; ``` The type of the failure value. ## Member Functions ### Constructor ```cpp explicit failure(value_type v); ``` Constructs with a `value_type` argument. ```cpp template explicit failure(U&& v); ``` Constructs with another type that can be converted to `value_type`. ```cpp template explicit failure(failure v); ``` Constructs with another `failure` type that can be converted to `value_type`. ### `get()` ```cpp value_type& get() noexcept; value_type const& get() const noexcept; ``` Accesses the stored value. ## Non-Member Functions ### `err(T)` ```cpp template failure::type> err(T&& v); template failure err(const char (&literal)[N]); ``` Constructs and returns a `failure` type from a failure value. Converts a string literal into `std::string`. # `failure>` Specialization of `failure` for when the failure value is a reference. ```cpp namespace toml { template struct failure> { using value_type = T; explicit failure(std::reference_wrapper v) noexcept; value_type& get() noexcept {return value.get();} value_type const& get() const noexcept {return value.get();} }; } ``` ## Member Types ```cpp using value_type = T; ``` The type of the failure value. It is `T` from `std::reference_wrapper`, not the reference itself. ### `get()` ```cpp value_type& get() noexcept; value_type const& get() const noexcept; ``` Accesses the stored value. # result A type that holds either a success value or a failure value. ```cpp namespace toml { template struct result { using success_type = success; using failure_type = failure; using value_type = typename success_type::value_type; using error_type = typename failure_type::value_type; result(success_type s); result(failure_type f); template result(success s); template result(failure f); result& operator=(success_type s); result& operator=(failure_type f); template result& operator=(success s); template result& operator=(failure f); ~result() noexcept; result(const result& other); result(result&& other); result& operator=(const result& other); result& operator=(result&& other); template result(result other); template result& operator=(result other); bool is_ok() const noexcept; bool is_err() const noexcept; explicit operator bool() const noexcept; value_type& unwrap(cxx::source_location loc = cxx::source_location::current()); value_type const& unwrap(cxx::source_location loc = cxx::source_location::current()) const; value_type& unwrap_or(value_type& opt) noexcept; value_type const& unwrap_or(value_type const& opt) const noexcept; error_type& unwrap_err(cxx::source_location loc = cxx::source_location::current()); error_type const& unwrap_err(cxx::source_location loc = cxx::source_location::current()) const; value_type& as_ok() noexcept; value_type const& as_ok() const noexcept; error_type& as_err() noexcept; error_type const& as_err() const noexcept; }; } ``` ## Member Types ### `success_type` `success`. ### `failure_type` `failure`. ### `value_type` The type `T` of the success value, alias for `success_type::value_type`. If `T` is `std::reference_wrapper`, then it is `U`. ### `error_type` The type `E` of the failure value, alias for `failure_type::value_type`. If `E` is `std::reference_wrapper`, then it is `F`. ## Member Functions ### Constructor ```cpp result() = delete; ``` Cannot construct `result` type by default. Needs to be given either a success or failure type. ```cpp result(success_type s); result(failure_type f); ``` Constructs with a `success_type` or `failure_type`. ```cpp template result(success s); template result(failure f); ``` Constructs with a `success` or `failure` that is convertible to `value_type` or `error_type`. ```cpp template result(result other); template result& operator=(result other); ``` Constructs from or assigns to another `result` with convertible `success` or `failure` types. ### Copy and Move Constructors ```cpp result(const result& other); result(result&& other); ``` Can be copy or move constructed. ### `operator=` ```cpp result& operator=(const result& other); result& operator=(result&& other); ``` Can be copy or move assigned. ```cpp template result& operator=(success s); template result& operator=(failure f); ``` Can be assigned from convertible `success` or `failure` types. ### `is_ok()` ```cpp bool is_ok() const noexcept; ``` Returns `true` if it holds a success value, `false` otherwise. ### `is_err()` ```cpp bool is_err() const noexcept; ``` Returns `true` if it holds a failure value, `false` otherwise. ### `operator bool()` ```cpp explicit operator bool() const noexcept; ``` Returns `true` if it holds a success value, `false` otherwise. ### `unwrap()` ```cpp value_type& unwrap(cxx::source_location loc = cxx::source_location::current()); value_type const& unwrap(cxx::source_location loc = cxx::source_location::current()) const; ``` Returns the stored success value. Throws `toml::bad_result_access` if it holds a failure value. If `std::source_location` or equivalent compiler extension is available, the file name and line number where `unwrap()` occurred are included in the `what()` string. ### `unwrap_or()` ```cpp value_type& unwrap_or(value_type& opt) noexcept; value_type const& unwrap_or(value_type const& opt) const noexcept; ``` Returns the stored success value if present, otherwise returns the provided default value. ### `unwrap_err()` ```cpp error_type& unwrap_err(cxx::source_location loc = cxx::source_location::current()); error_type const& unwrap_err(cxx::source_location loc = cxx::source_location::current()) const; ``` Returns the stored failure value. Throws `toml::bad_result_access` if it holds a success value. If `std::source_location` or equivalent compiler extension is available, the file name and line number where `unwrap_err()` occurred are included in the `what()` string. ### `as_ok()` ```cpp value_type& as_ok() noexcept; value_type const& as_ok() const noexcept; ``` Returns the success value without checking. Behavior is undefined if it holds a failure value. ### `as_err()` ```cpp error_type& as_err() noexcept; error_type const& as_err() const noexcept; ``` Returns the failure value without checking. Behavior is undefined if it holds a success value. # bad_result_access An exception thrown when `unwrap` or `unwrap_err` fails in a `result`. ```cpp namespace toml { struct bad_result_access : public ::toml::exception { public: explicit bad_result_access(const std::string& what_arg); virtual ~bad_result_access() noexcept override = default; virtual const char* what() const noexcept override; protected: std::string what_; }; } ``` toml11-4.1.0/docs/content.en/docs/reference/serializer.md000066400000000000000000000033431464712047700232340ustar00rootroot00000000000000+++ title = "serializer.hpp" type = "docs" +++ # serializer.hpp ## `format` Serializes the data. ```cpp namespace toml { template std::string format(const basic_value& v, const spec s = spec::default_version()); template std::string format(const typename basic_value::key_type& k, const basic_value& v, const spec s = spec::default_version()); template std::string format(const std::vector::key_type>& ks, const basic_value& v, const spec s = spec::default_version()); } ``` If there's a conflict between the format information and the `spec`, for example, when using `v1.0.0` with `table_format::multiline_oneline`, the `spec` takes precedence. ### `format(v, spec)` Formats a `toml::value` according to its format information and the provided `spec`. If it's a `table_type`, it's formatted as if it were the root table. Otherwise, only the value is formatted. ### `format(k, v, spec)` Formats a `toml::value` along with the given key. `v` is interpreted as being defined under that key. ### `format([k,...], v, spec)` `v` is interpreted as being defined under those keys. If multiple keys are provided, it's interpreted as a recursively defined table. ## `serialization_error` Reports errors that occurred during serialization. ```cpp namespace toml { struct serialization_error final : public ::toml::exception { public: explicit serialization_error(std::string what_arg, source_location loc); ~serialization_error() noexcept override = default; const char* what() const noexcept override; source_location const& location() const noexcept; }; } ``` toml11-4.1.0/docs/content.en/docs/reference/source_location.md000066400000000000000000000124561464712047700242600ustar00rootroot00000000000000+++ title = "source_location.hpp" type = "docs" +++ # source_location.hpp `source_location.hpp` defines a class representing a specific area within a TOML file. This class is used to represent problematic areas in error messages. # `toml::source_location` `source_location` is a class representing a specific area within a TOML file. ```cpp namespace toml { struct source_location { public: explicit source_location(/* implementation-defined */); ~source_location() = default; source_location(source_location const&) = default; source_location(source_location &&) = default; source_location& operator=(source_location const&) = default; source_location& operator=(source_location &&) = default; bool is_ok() const noexcept; std::size_t length() const noexcept; std::size_t first_line_number() const noexcept; std::size_t first_column_number() const noexcept; std::size_t last_line_number() const noexcept; std::size_t last_column_number() const noexcept; std::string const& file_name() const noexcept; std::size_t num_lines() const noexcept; std::string const& first_line() const; std::string const& last_line() const; std::vector const& lines() const noexcept; }; template std::string format_location(const source_location& loc, const std::string& msg, const Ts& ... locs_and_msgs); } //toml ``` ## Member Functions ### Constructor ```cpp explicit source_location(/* implementation-defined */); ``` `toml::source_location` can only be constructed via `toml::parse` or the `_toml` literal. ### `is_ok()` ```cpp bool is_ok() const noexcept; ``` Returns `true` if the `source_location` holds a valid value, `false` otherwise. The result of `location()` from `toml::value` constructed outside of `toml::parse` or `_toml` literals returns `false` for `is_ok` as it points to nothing. ### `length()` ```cpp std::size_t length() const noexcept; ``` Returns the length of the area pointed to by the `source_location`. Returns `0` if it does not hold a valid value. ### `first_line_number()` ```cpp std::size_t first_line_number() const noexcept; ``` Returns the line number of the first line of the area pointed to by the `source_location`. Returns `1` if it does not hold a valid value. ### `first_column_number()` ```cpp std::size_t first_column_number() const noexcept; ``` Returns the column number of the first column of the area pointed to by the `source_location`. Returns `1` if it does not hold a valid value. ### `last_line_number()` ```cpp std::size_t last_line_number() const noexcept; ``` Returns the line number of the last line of the area pointed to by the `source_location`. Returns `1` if it does not hold a valid value. ### `last_column_number()` ```cpp std::size_t last_column_number() const noexcept; ``` Returns the column number of the last column of the area pointed to by the `source_location`. Returns `1` if it does not hold a valid value. ### `file_name()` ```cpp std::string const& file_name() const noexcept; ``` Returns the file name containing the area pointed to by the `source_location`. Returns `"unknown file"` if it does not hold a valid value. ### `num_lines()` ```cpp std::size_t num_lines() const noexcept; ``` Returns the number of lines in the area pointed to by the `source_location`. Returns `0` if it does not hold a valid value. ### `first_line()` ```cpp std::string const& first_line() const; ``` Returns the first line of the area pointed to by the `source_location`. Throws `std::out_of_range` if it does not hold a valid value. ### `last_line()` ```cpp std::string const& last_line() const; ``` Returns the last line of the area pointed to by the `source_location`. Throws `std::out_of_range` if it does not hold a valid value. ### `lines()` ```cpp std::vector const& lines() const noexcept; ``` Returns all lines in the area pointed to by the `source_location`. Returns a reference to an empty `std::vector` if it does not hold a valid value. ## Non-Member Functions ### `format_location` ```cpp template std::string format_location(const source_location& loc, const std::string& msg, const Ts& ... locs_and_msgs); ``` Formats the specified `source_location` and its associated message as follows: ``` -> {filename.toml} | 1 | a = 42 | ^-- {message} ``` If colorization is enabled, ANSI escape sequences will be added for coloring. When multiple `locs_and_msgs` are provided, they must be in the order of `const source_location&` followed by `const std::string&`, and the next pair in the same order, and so on. #### Example: Multiple `source_location` and `std::string` When multiple `source_location` and `std::string` pairs are provided, they are formatted as follows: ```cpp source_location& loc0; source_location& loc1; source_location& loc2; std::string msg0; std::string msg1; std::string msg2; format_location(loc0, msg0, loc1, msg1, loc2, msg2); ``` ``` -> {filename0.toml} | 1 | a = 42 | ^-- {message0} | -> {filename1.toml} | 2 | b = 3.14 | ^-- {message1} | -> {filename2.toml} | 3 | c = "foo" | ^-- {message2} ``` # Related - [error_info.hpp]({{}}) - [value.hpp]({{}}) toml11-4.1.0/docs/content.en/docs/reference/spec.md000066400000000000000000000177201464712047700220210ustar00rootroot00000000000000+++ title = "spec.hpp" type = "docs" +++ # spec.hpp `spec.hpp` defines classes for specifying the version of TOML. # `toml::semantic_version` `semantic_version` is a class that stores version information. ```cpp namespace toml { struct semantic_version { constexpr semantic_version(std::uint32_t mjr, std::uint32_t mnr, std::uint32_t p) noexcept; std::uint32_t major; std::uint32_t minor; std::uint32_t patch; }; constexpr semantic_version make_semver(std::uint32_t major, std::uint32_t minor, std::uint32_t patch) noexcept; constexpr bool operator==(const semantic_version&, const semantic_version&) noexcept; constexpr bool operator!=(const semantic_version&, const semantic_version&) noexcept; constexpr bool operator< (const semantic_version&, const semantic_version&) noexcept; constexpr bool operator<=(const semantic_version&, const semantic_version&) noexcept; constexpr bool operator> (const semantic_version&, const semantic_version&) noexcept; constexpr bool operator>=(const semantic_version&, const semantic_version&) noexcept; std::ostream& operator<<(std::ostream& os, const semantic_version& ver); } //toml ``` ## Member Functions ### Constructor ```cpp constexpr semantic_version(std::uint32_t mjr, std::uint32_t mnr, std::uint32_t p) noexcept; ``` Constructs a `semantic_version` instance with the specified `major`, `minor`, and `patch` version numbers. ## Non-Member Functions ### Comparison Operators ```cpp constexpr bool operator==(const semantic_version&, const semantic_version&) noexcept; constexpr bool operator!=(const semantic_version&, const semantic_version&) noexcept; constexpr bool operator< (const semantic_version&, const semantic_version&) noexcept; constexpr bool operator<=(const semantic_version&, const semantic_version&) noexcept; constexpr bool operator> (const semantic_version&, const semantic_version&) noexcept; constexpr bool operator>=(const semantic_version&, const semantic_version&) noexcept; ``` Compares two `semantic_version` instances according to semantic versioning rules. ### Stream Operator ```cpp std::ostream& operator<<(std::ostream& os, const semantic_version& ver); ``` Outputs the version in the format `{major}.{minor}.{patch}`. ### `to_string` ```cpp std::string to_string(const semantic_version& ver); ``` Converts the version to a string in the format `{major}.{minor}.{patch}`. # `toml::spec` `spec` is a class that stores TOML version information. ```cpp struct spec { constexpr static spec default_version() noexcept; constexpr static spec v(std::uint32_t mjr, std::uint32_t mnr, std::uint32_t p) noexcept; constexpr explicit spec(const semantic_version& semver) noexcept; semantic_version version; // toml version // diff from v1.0.0 -> v1.1.0 bool v1_1_0_allow_control_characters_in_comments; bool v1_1_0_allow_newlines_in_inline_tables; bool v1_1_0_allow_trailing_comma_in_inline_tables; bool v1_1_0_allow_non_english_in_bare_keys; bool v1_1_0_add_escape_sequence_e; bool v1_1_0_add_escape_sequence_x; bool v1_1_0_make_seconds_optional; // library extensions bool ext_hex_float; // allow hex float bool ext_num_suffix; // allow number suffix bool ext_null_value; // allow null value }; ``` ## Member Functions ### Constructor ```cpp constexpr explicit spec(const semantic_version& semver) noexcept; ``` Constructs a `spec` with the specified TOML version. Supports TOML v1.0.0 and TOML v1.1.0. ### `default_version()` ```cpp constexpr static spec default_version() noexcept; ``` Constructs a `spec` with the default version. Used as the default value for `toml::parse` and `toml::format`. In toml11 v4.0.0, the value is v1.0.0. ### `v(major, minor, patch)` ```cpp constexpr static spec v(std::uint32_t mjr, std::uint32_t mnr, std::uint32_t p) noexcept; ``` Constructs a `spec` with the specified version. ## Member Variables Each flag is automatically set to `true` when the specified version includes the corresponding feature. You can modify these flags to change the behavior of `toml::parse` and `toml::format`. {{}} Some features of TOML v1.1.0 are still under fairly lengthy discussion and may still be reverted. If they are indeed reverted, toml11 will remove those features in a minor version upgrade or move them to a corresponding later version. As such, any features related to future versions should be considered unstable. {{}} ### Example ```cpp auto spec = toml::spec::v(1, 0, 0); // Allow newlines in inline tables in addition to v1.0.0 features. // Other v1.1.0 features are not enabled. spec.v1_1_0_allow_newlines_in_inline_tables = true; auto input = toml::parse("input_file.toml", spec); ``` ### `v1_1_0_allow_control_characters_in_comments` ```cpp bool v1_1_0_allow_control_characters_in_comments; ``` Allows most control characters in comments. Added in TOML v1.1.0. ### `v1_1_0_allow_newlines_in_inline_tables` ```cpp bool v1_1_0_allow_newlines_in_inline_tables; ``` Allows newlines in inline tables. Added in TOML v1.1.0. ### `v1_1_0_allow_trailing_comma_in_inline_tables` ```cpp bool v1_1_0_allow_trailing_comma_in_inline_tables; ``` Allows trailing commas in inline tables. Added in TOML v1.1.0. ### `v1_1_0_add_escape_sequence_e` ```cpp bool v1_1_0_add_escape_sequence_e; ``` Allows `\e` to represent the ESC character. Added in TOML v1.1.0. ### `v1_1_0_add_escape_sequence_x` ```cpp bool v1_1_0_add_escape_sequence_x; ``` Allows `\xHH` to represent a single byte character. Added in TOML v1.1.0. ### `v1_1_0_make_seconds_optional` ```cpp bool v1_1_0_make_seconds_optional; ``` Makes the seconds component in time optional. Unspecified seconds default to `0`. Added in TOML v1.1.0. ### `ext_hex_float` ```cpp bool ext_hex_float; ``` This is a language extension specific to toml11. It is initialized to `false` regardless of the specified version. You must explicitly set it to `true` if you want to use it. Allows hexadecimal representation of floating-point numbers. The hexadecimal representation conforms to the `printf` format specifier `%a/%A`. ``` hexf = 0xC0FFEEp-10 ``` `toml::format` will format using hexadecimal notation only if the passed `toml::spec` has `ext_hex_float` set to `true`. If the format specifier indicates `hex` but the `toml::spec` passed to `toml::format` has `ext_hex_float` set to `false`, the hexadecimal specification is ignored, and the number is output in decimal notation with maximum precision. ### `ext_num_suffix` ```cpp bool ext_num_suffix; ``` This is a language extension specific to toml11. It is initialized to `false` regardless of the specified version. You must explicitly set it to `true` if you want to use it. Allows the addition of suffixes to decimal integers and floating-point numbers. This does not apply to hexadecimal, octal, or binary notations. There must be an `_` separator between the number and the suffix. The suffix cannot start with a digit to avoid confusion with the numeric part. ``` distance = 10_m # valid distance = 10_2m # invalid distance = 10_2_m # valid ``` The suffix is stored in the format information as `std::string suffix`. The `_` separating the number from the suffix is not included in the `suffix`. ```cpp toml::value distance = toml::find(input, "distance"); assert(distance.as_integer_fmt().suffix == std::string("m")); ``` `toml::format` will format the value with the suffix only if the passed `toml::spec` has `ext_num_suffix` set to `true`. The `suffix` follows the grammar defined as: ```abnf non-digit-graph = ALPHA / non-ascii graph = ALPHA / DIGIT / non-ascii suffix = _ non-digit-graph *( graph / ( _ graph ) ) ``` ### `ext_null_value` ```cpp bool ext_null_value; ``` This is a language extension specific to toml11. Allows the use of `null` as a value. A `toml::value` specified as `null` will have no value, and `is_empty()` will return `true`. `toml::format` will format it as `null` only if the passed `toml::spec` has `ext_null_value` set to `true`. Otherwise, `toml::format` will terminate with an error. toml11-4.1.0/docs/content.en/docs/reference/toml.md000066400000000000000000000004521464712047700220340ustar00rootroot00000000000000+++ title = "toml.hpp" type = "docs" +++ # toml.hpp `toml.hpp` includes all other headers. This allows access to all features of toml11. This header file and `toml_fwd.hpp` are located under `${TOML11_INCLUDE_DIR}/`, while other header files are located under `${toml11_include_dir}/toml11/`. toml11-4.1.0/docs/content.en/docs/reference/toml_fwd.md000066400000000000000000000014061464712047700226740ustar00rootroot00000000000000+++ title = "toml_fwd.hpp" type = "docs" +++ # toml_fwd.hpp `toml_fwd.hpp` contains forward declarations of structures defined in toml11 and macro definitions. When only forward declarations of toml11 structures are needed and implementation is not required, including `toml_fwd.hpp` instead of `toml.hpp` can reduce compilation time. {{}} Since this file only contains forward declarations, you cannot use `toml::table`, defined as `toml::basic_value::table_type`, and similarly defined `toml::array`. This is because they require the implementation of `basic_value`. {{}} This header file and `toml.hpp` are located under `${TOML11_INCLUDE_DIR}/`, while other header files are located under `${TOML11_INCLUDE_DIR}/toml11/`. toml11-4.1.0/docs/content.en/docs/reference/types.md000066400000000000000000000111651464712047700222300ustar00rootroot00000000000000+++ title = "types.hpp" type = "docs" +++ # types.hpp This document defines classes that specifies type information. # `type_config` `type_config` is a type that encapsulates parameters given to `toml::basic_value`. When using different types within `toml::basic_value`, you need to define and pass this type separately. All elements listed are required. If you use numerical types that cannot use standard stream operators, define and replace the equivalents for `read_int` and `read_float`. ```cpp namespace toml { struct type_config { using comment_type = preserve_comments; using boolean_type = bool; using integer_type = std::int64_t; using floating_type = double; using string_type = std::string; template using array_type = std::vector; template using table_type = std::unordered_map; static result parse_int(const std::string& str, const source_location src, const std::uint8_t base); static result parse_float(const std::string& str, const source_location src, const bool is_hex); }; using value = basic_value; using table = typename value::table_type; using array = typename value::array_type; } // toml ``` ## `static` Member Functions ### `parse_int(str, src, base)` ```cpp static result parse_int(const std::string& str, const source_location src, const std::uint8_t base); ``` If you use a type as `integer_type` that cannot utilize standard stream operators, implement this function. Otherwise, use `read_int` described later. The `str` parameter receives a string with prefixes, leading zeros, and underscores removed. The `src` parameter receives a `source_location` pointing to where the string was defined. The `base` parameter receives one of `10`, `2`, `8`, or `16`. ### `parse_float(str, src, is_hex)` ```cpp static result parse_float(const std::string& str, const source_location src, const bool is_hex); ``` If you use a type as `floating_type` that cannot utilize standard stream operators, implement this function. Otherwise, use `read_float` described later. The `str` parameter receives a string with prefixes, leading zeros, and underscores removed. The `src` parameter receives a `source_location` pointing to where the string was defined. The `is_hex` parameter indicates whether the format is `hexfloat`. If you don't use the `hexfloat` extension, you don't need to implement this. For details on the `hexfloat` extension, refer to [spec.hpp]({{}}). ## Non-member Functions ### `read_int` ```cpp template result read_int(const std::string& str, const source_location src, const std::uint8_t base); ``` This is the default function used. It parses using `std::istringstream`. If `operator>>` and manipulators like `std::hex`, and `std::numeric_limits` are defined (such as for `boost::multiprecision`), you can use this without modifications. ### `read_float` ```cpp template result read_float(const std::string& str, const source_location src, const bool is_hex); ``` This is the default function used. It parses decimals using `std::istringstream` and hexfloats using `sscanf()`. It supports `double` and `float`. For other types, if `operator>>` is defined and `hex` is not used, you can use this function. # `ordered_type_config` `ordered_type_config` is a variation of `toml::type_config` where the table type is replaced with `toml::ordered_map`. Additionally, it defines the `toml::ordered_value` alias. Other than these changes, it is identical to `type_config`. ```cpp namespace toml { struct ordered_type_config { using comment_type = preserve_comments; using boolean_type = bool; using integer_type = std::int64_t; using floating_type = double; using string_type = std::string; template using array_type = std::vector; template using table_type = ordered_map; static result parse_int(const std::string& str, const source_location src, const std::uint8_t base) { return read_int(str, src, base); } static result parse_float(const std::string& str, const source_location src, const bool is_hex) { return read_float(str, src, is_hex); } }; using ordered_value = basic_value; using ordered_table = typename ordered_value::table_type; using ordered_array = typename ordered_value::array_type; } // toml ``` toml11-4.1.0/docs/content.en/docs/reference/value.md000066400000000000000000000634001464712047700221770ustar00rootroot00000000000000+++ title = "value.hpp" type = "docs" +++ # value.hpp `value.hpp` defines `basic_value`. # `toml::basic_value` `basic_value` is a class that stores TOML values. ```cpp namespace toml { template class basic_value; // Defined in types.hpp // using value = basic_value; // using table = typename basic_value::table_type; // using array = typename basic_value::array_type; template bool operator==(const basic_value&, const basic_value&); template bool operator!=(const basic_value&, const basic_value&); template bool operator< (const basic_value&, const basic_value&); template bool operator<=(const basic_value&, const basic_value&); template bool operator> (const basic_value&, const basic_value&); template bool operator>=(const basic_value&, const basic_value&); } //toml ``` ## Member Types The following member types are defined. You can modify the member types using `TypeConfig`. See also: [types.hpp]({{}}) | Name | Definition | |:-----------------------|:-------------------------------------| | `char_type` | `typename TypeConfig::char_type` | | `key_type` | `typename TypeConfig::string_type` | | `value_type` | `basic_value` | | `boolean_type` | `typename TypeConfig::boolean_type` | | `integer_type` | `typename TypeConfig::integer_type` | | `floating_type` | `typename TypeConfig::floating_type` | | `string_type` | `typename TypeConfig::string_type` | | `local_time_type` | `toml::local_time` | | `local_date_type` | `toml::local_date` | | `local_datetime_type` | `toml::local_datetime` | | `offset_datetime_type` | `toml::offset_datetime` | | `array_type` | `typename TypeConfig::template array_type`| | `table_type` | `typename TypeConfig::template table_type`| | `comment_type` | `typename TypeConfig::comment_type` | ## Member Functions ### Default Constructor ```cpp basic_value() noexcept ``` Constructs an empty `toml::value`. The constructed `toml::value` will be empty. ### Copy and Move Constructors ```cpp basic_value(const basic_value& v) basic_value(basic_value&& v) ``` Copies or moves all information including values, format information, comments, and file regions. ### Copy and Move Constructors with Comments ```cpp basic_value(basic_value v, std::vector com) ``` Copies or moves the object while overwriting comments. ### Conversion Constructors ```cpp template basic_value(basic_value other) template basic_value(basic_value other, std::vector com) ``` Copies or moves from a `basic_value` with a different `type_config`. ### Constructor (boolean) ```cpp basic_value(boolean_type x) basic_value(boolean_type x, boolean_format_info fmt) basic_value(boolean_type x, std::vector com) basic_value(boolean_type x, boolean_format_info fmt, std::vector com) ``` Constructs an object with a `bool`, its format information, and comments. ### Constructor (integer) ```cpp template is true */> basic_value(T x) template is true */> basic_value(T x, integer_format_info fmt) template is true */> basic_value(T x, std::vector com) template is true */> basic_value(T x, integer_format_info fmt, std::vector com) ``` Constructs an object with an `integer`, its format information, and comments. ### Constructor (floating) ```cpp template is true */> basic_value(T x) template is true */> basic_value(T x, floating_format_info fmt) template is true */> basic_value(T x, std::vector com) template is true */> basic_value(T x, floating_format_info fmt, std::vector com) ``` Constructs an object with a `floating` point number, its format information, and comments. ### Constructor (string) ```cpp basic_value(string_type x) basic_value(string_type x, string_format_info fmt) basic_value(string_type x, std::vector com) basic_value(string_type x, string_format_info fmt, std::vector com) basic_value(const string_type::value_type* x) basic_value(const string_type::value_type* x, string_format_info fmt) basic_value(const string_type::value_type* x, std::vector com) basic_value(const string_type::value_type* x, string_format_info fmt, std::vector com) // C++17以降 basic_value(string_view_type x) basic_value(string_view_type x, string_format_info fmt) basic_value(string_view_type x, std::vector com) basic_value(string_view_type x, string_format_info fmt, std::vector com) ``` Constructs an object with a `string`, its format information, and comments. `string_view_type` shares the same `value_type` and `traits_type` as `string_type`. ### Constructor (local_date) ```cpp basic_value(local_date_type x) basic_value(local_date_type x, local_date_format_info fmt) basic_value(local_date_type x, std::vector com) basic_value(local_date_type x, local_date_format_info fmt, std::vector com) ``` Constructs an object with a `local_date_type`, its format information, and comments. ### Constructor (local_time) ```cpp basic_value(local_time_type x) basic_value(local_time_type x, local_time_format_info fmt) basic_value(local_time_type x, std::vector com) basic_value(local_time_type x, local_time_format_info fmt, std::vector com) template basic_value(const std::chrono::duration& x) template basic_value(const std::chrono::duration& x, local_time_format_info fmt) template basic_value(const std::chrono::duration& x, std::vector com) template basic_value(const std::chrono::duration& x, local_time_format_info fmt, std::vector com) ``` Constructs an object with a `local_time_type`, its format information, and comments. For `std::chrono::duration`, constructs as a time span from `00:00:00`. ### Constructor (local_datetime) ```cpp basic_value(local_datetime_type x) basic_value(local_datetime_type x, local_date_format_info fmt) basic_value(local_datetime_type x, std::vector com) basic_value(local_datetime_type x, local_date_format_info fmt, std::vector com) ``` Constructs an object with a `local_datetime_type`, its format information, and comments. ### Constructor (offset_datetime) ```cpp basic_value(offset_datetime_type x) basic_value(offset_datetime_type x, offset_date_format_info fmt) basic_value(offset_datetime_type x, std::vector com) basic_value(offset_datetime_type x, offset_date_format_info fmt, std::vector com) basic_value(std::chrono::system_clock::time_point x) basic_value(std::chrono::system_clock::time_point x, offset_date_format_info fmt) basic_value(std::chrono::system_clock::time_point x, std::vector com) basic_value(std::chrono::system_clock::time_point x, offset_date_format_info fmt, std::vector com) ``` Constructs an object with an `offset_datetime_type`, its format information, and comments. For `std::chrono::system_clock::time_point`, constructs for the pointed time. ### Constructor (array) ```cpp basic_value(array_type x) basic_value(array_type x, integer_format_info fmt) basic_value(array_type x, std::vector com) basic_value(array_type x, integer_format_info fmt, std::vector com) template basic_value(T x) template basic_value(T x, array_format_info fmt) template basic_value(T x, std::vector com) template basic_value(T x, array_format_info fmt, std::vector com) ``` Constructs an object with an `array`, its format information, and comments. `array-like` types must meet the following criteria: - Has `T::iterator`. - Has `T::value_type`. - Does **not** have `T::key_type`. - Does **not** have `T::mapped_type`. - Is **not** `std::string`. - Is **not** `std::string_view` (since C++17). ### Constructor (table) ```cpp basic_value(table_type x) basic_value(table_type x, integer_format_info fmt) basic_value(table_type x, std::vector com) basic_value(table_type x, integer_format_info fmt, std::vector com) template basic_value(T x) template basic_value(T x, table_format_info fmt) template basic_value(T x, std::vector com) template basic_value(T x, table_format_info fmt, std::vector com) ``` Constructs an object with a `table`, its format information, and comments. `table-like` types must meet the following criteria: - Has `T::iterator`. - Has `T::value_type`. - Has `T::key_type`. - Has `T::mapped_type`. ### Constructor (user-defined) ```cpp template is defined */> basic_value(const T& ud); template is defined */> basic_value(const T& ud, std::vector com); template is not defined, but T{}.into_toml() exists */> basic_value(const T& ud); template is not defined, but T{}.into_toml() exists */> basic_value(const T& ud, std::vector com); ``` If `toml::into` is defined, constructs from the result of `toml::into(ud)`. If `toml::into` is not defined but `T` has a `into_toml()` member function, constructs from the result of `ud.into_toml()`. ----- ### `operator=(basic_value)` ```cpp basic_value& operator=(const basic_value& v) basic_value& operator=(basic_value&& v) template basic_value& operator=(basic_value other) ``` Assigns the right-hand side `basic_value` to the current object. ### `operator=(T)` ```cpp template basic_value& operator=(T x) ``` Assigns a value corresponding to `T`. The contents pointed to by `source_location` are discarded. If the object already holds a value of the same type, the original format information is retained. ----- ### `is()` ```cpp bool is() const noexcept ``` #### Requirements `T` must be an exact TOML type, meaning it corresponds to one of the `toml::value::xxx_type`. #### Return Value Returns `true` if the stored type matches `T`, otherwise returns `false`. ----- ### `is(toml::value_t)` ```cpp bool is(toml::value_t t) const noexcept ``` #### Return Value Returns `true` if the tag of the stored type matches `t`, otherwise returns `false`. ----- ### `is_xxx()` ```cpp bool is_boolean() const noexcept; bool is_integer() const noexcept; bool is_floating() const noexcept; bool is_string() const noexcept; bool is_offset_datetime() const noexcept; bool is_local_datetime() const noexcept; bool is_local_date() const noexcept; bool is_local_time() const noexcept; bool is_array() const noexcept; bool is_table() const noexcept; ``` #### Return Value Returns `true` if the stored type matches the corresponding type, otherwise returns `false`. ------ ### `is_empty()` ```cpp bool is_empty() const noexcept; ``` #### Return Value Returns `true` if the object is default constructed and no value is assigned, otherwise returns `false`. ### `is_array_of_tables()` ```cpp bool is_array_of_tables() const noexcept; ``` #### Return Value Returns `true` if the stored type is an array that is not empty and all elements are tables, otherwise returns `false`. ----- ### `type()` ```cpp toml::value_t type() const noexcept ``` #### Return Value Returns the tag corresponding to the stored type. ----- ### `as_xxx()` ```cpp boolean_type const& as_boolean () const; integer_type const& as_integer () const; floating_type const& as_floating () const; string_type const& as_string () const; offset_datetime_type const& as_offset_datetime() const; local_datetime_type const& as_local_datetime () const; local_date_type const& as_local_date () const; local_time_type const& as_local_time () const; array_type const& as_array () const; table_type const& as_table () const; boolean_type & as_boolean (); integer_type & as_integer (); floating_type & as_floating (); string_type & as_string (); offset_datetime_type& as_offset_datetime(); local_datetime_type & as_local_datetime (); local_date_type & as_local_date (); local_time_type & as_local_time (); array_type & as_array (); table_type & as_table (); ``` #### Return Value Returns a reference to the value of the specified type. #### Exception Throws `toml::type_error` if the stored value's type does not match the specified type. ----- ### `as_xxx(std::nothrow)` Invoke with a `std::nothrow` object. ```cpp boolean_type const& as_boolean (const std::nothrow_t&) const noexcept; integer_type const& as_integer (const std::nothrow_t&) const noexcept; floating_type const& as_floating (const std::nothrow_t&) const noexcept; string_type const& as_string (const std::nothrow_t&) const noexcept; offset_datetime_type const& as_offset_datetime(const std::nothrow_t&) const noexcept; local_datetime_type const& as_local_datetime (const std::nothrow_t&) const noexcept; local_date_type const& as_local_date (const std::nothrow_t&) const noexcept; local_time_type const& as_local_time (const std::nothrow_t&) const noexcept; array_type const& as_array (const std::nothrow_t&) const noexcept; table_type const& as_table (const std::nothrow_t&) const noexcept; boolean_type & as_boolean (const std::nothrow_t&) noexcept; integer_type & as_integer (const std::nothrow_t&) noexcept; floating_type & as_floating (const std::nothrow_t&) noexcept; string_type & as_string (const std::nothrow_t&) noexcept; offset_datetime_type& as_offset_datetime(const std::nothrow_t&) noexcept; local_datetime_type & as_local_datetime (const std::nothrow_t&) noexcept; local_date_type & as_local_date (const std::nothrow_t&) noexcept; local_time_type & as_local_time (const std::nothrow_t&) noexcept; array_type & as_array (const std::nothrow_t&) noexcept; table_type & as_table (const std::nothrow_t&) noexcept; ``` #### Return Value Returns a reference to the value of the specified type. #### Note If the type of the stored value does not match the specified type, the behavior is undefined. ----- ### `as_xxx_fmt()` Accesses format information. ```cpp boolean_format_info & as_boolean_fmt (); integer_format_info & as_integer_fmt (); floating_format_info & as_floating_fmt (); string_format_info & as_string_fmt (); offset_datetime_format_info& as_offset_datetime_fmt(); local_datetime_format_info & as_local_datetime_fmt (); local_date_format_info & as_local_date_fmt (); local_time_format_info & as_local_time_fmt (); array_format_info & as_array_fmt (); table_format_info & as_table_fmt (); boolean_format_info const& as_boolean_fmt () const; integer_format_info const& as_integer_fmt () const; floating_format_info const& as_floating_fmt () const; string_format_info const& as_string_fmt () const; offset_datetime_format_info const& as_offset_datetime_fmt() const; local_datetime_format_info const& as_local_datetime_fmt () const; local_date_format_info const& as_local_date_fmt () const; local_time_format_info const& as_local_time_fmt () const; array_format_info const& as_array_fmt () const; table_format_info const& as_table_fmt () const; ``` #### Return Value Returns a reference to the structure holding the format information for the specified type. #### Exception Throws `toml::type_error` if the stored value's type does not match the specified type. ----- ### `as_xxx_fmt(std::nothrow)` Invoke with a `std::nothrow` object. ```cpp boolean_format_info & as_boolean_fmt (const std::nothrow_t&) noexcept; integer_format_info & as_integer_fmt (const std::nothrow_t&) noexcept; floating_format_info & as_floating_fmt (const std::nothrow_t&) noexcept; string_format_info & as_string_fmt (const std::nothrow_t&) noexcept; offset_datetime_format_info& as_offset_datetime_fmt(const std::nothrow_t&) noexcept; local_datetime_format_info & as_local_datetime_fmt (const std::nothrow_t&) noexcept; local_date_format_info & as_local_date_fmt (const std::nothrow_t&) noexcept; local_time_format_info & as_local_time_fmt (const std::nothrow_t&) noexcept; array_format_info & as_array_fmt (const std::nothrow_t&) noexcept; table_format_info & as_table_fmt (const std::nothrow_t&) noexcept; boolean_format_info const& as_boolean_fmt (const std::nothrow_t&) const noexcept; integer_format_info const& as_integer_fmt (const std::nothrow_t&) const noexcept; floating_format_info const& as_floating_fmt (const std::nothrow_t&) const noexcept; string_format_info const& as_string_fmt (const std::nothrow_t&) const noexcept; offset_datetime_format_info const& as_offset_datetime_fmt(const std::nothrow_t&) const noexcept; local_datetime_format_info const& as_local_datetime_fmt (const std::nothrow_t&) const noexcept; local_date_format_info const& as_local_date_fmt (const std::nothrow_t&) const noexcept; local_time_format_info const& as_local_time_fmt (const std::nothrow_t&) const noexcept; array_format_info const& as_array_fmt (const std::nothrow_t&) const noexcept; table_format_info const& as_table_fmt (const std::nothrow_t&) const noexcept; ``` #### Return Value Returns a reference to the structure holding the format information for the specified type. #### Note If the type of the stored value does not match the specified type, the behavior is undefined. ----- ### `at(key)` ```cpp value_type& at(const key_type& key); value_type const& at(const key_type& key) const; ``` #### Return Value Casts the current `value` to a `table` and returns the element specified by `key`. #### Exception Throws `toml::type_error` if the stored value is not a `table`. Throws `std::out_of_range` if the `table` does not contain the specified element. ----- #### `operator[](key)` ```cpp value_type& operator[](const key_type& k); ``` ##### Return Value Casts the current `value` to a `table` and returns a reference to the element specified by `key`. If the element specified by `key` does not exist, it is default-constructed. ##### Exception Throws `toml::type_error` if the stored value is not a `table`. ----- ### `count(key)` ```cpp std::size_t count(const key_type& key) const; ``` #### Return Value Casts the current `value` to a `table` and returns `1` if the element corresponding to `key` is present, otherwise returns `0`. #### Exception Throws `toml::type_error` if the stored value is not a `table`. ----- ### `contains(key)` ```cpp bool contains(const key_type& key) const; ``` #### Return Value Casts the current `value` to a `table` and returns `true` if the element corresponding to `key` is present, otherwise returns `false`. #### Exception Throws `toml::type_error` if the stored value is not a `table`. ----- ### `at(idx)` ```cpp value_type& at(const std::size_t idx); value_type const& at(const std::size_t idx) const; ``` #### Return Value Casts the current `value` to an `array` and returns the element specified by `idx`. #### Exception Throws `toml::type_error` if the stored value is not an `array`. Throws `std::out_of_range` if the specified element does not exist in the `array`. ----- ### `operator[](idx)` ```cpp value_type& operator[](const std::size_t idx) noexcept; value_type const& operator[](const std::size_t idx) const noexcept; ``` #### Return Value Casts the current `value` to an `array` and returns a reference to the element specified by `idx`. #### Note Performs no checks. Behavior is undefined if the stored value is not an `array` or if the specified element does not exist. ----- ### `push_back(value)` ```cpp void push_back(const value_type& x); void push_back(value_type&& x); ``` Casts the current `value` to an `array` and performs `push_back` on the `array`. #### Return Value None. #### Exception Throws `toml::type_error` if the stored value is not an `array`. ----- ### `emplace_back(args...)` ```cpp template value_type& emplace_back(Ts&& ... args) ``` Casts the current `value` to an `array` and performs `emplace_back` on the `array`. #### Return Value A reference to the constructed value. #### Exception Throws `toml::type_error` if the stored value is not an `array`. ----- ### `size()` ```cpp std::size_t size() const; ``` #### Return Value Casts the current `value` to an `array`, `string`, or `table` and returns the number of elements. For a `string`, it returns the number of characters. #### Exception Throws `toml::type_error` if the stored value is not an `array`, `string`, or `table`. ----- ### `location()` ```cpp source_location location() const; ``` #### Return Value Returns a `source_location` object representing the position within the TOML document where the `value` is defined. If the `value` was not constructed by parsing a TOML document, returns a `source_location` that points to nowhere. ----- ### `comments()` ```cpp comment_type const& comments() const noexcept; comment_type& comments() noexcept; ``` #### Return Value Returns a reference to the comment container. ## Non-Member Functions ### `operator==` ```cpp template bool operator==(const basic_value&, const basic_value&); ``` Two `basic_value` instances are considered equal if they satisfy the following conditions: - The contained type is the same. - The contained values are identical. - The comments are identical at the byte level. ### `operator!=` ```cpp template bool operator!=(const basic_value& lhs, const basic_value& rhs) { return !(lhs == rhs); } ``` ### `operator<` Defined only if `array_type` and `table_type` have `operator<`. ```cpp template bool operator<(const basic_value&, const basic_value&); ``` Comparison order: 1. TOML type 2. If TOML types are the same, their values 3. If both the TOML types and values are the same, the comments TOML types have the following order (from smallest to largest): 1. `toml::value_t::empty` 2. `toml::value_t::boolean` 3. `toml::value_t::integer` 4. `toml::value_t::floating` 5. `toml::value_t::string` 6. `toml::value_t::offset_datetime` 7. `toml::value_t::local_datetime` 8. `toml::value_t::local_date` 9. `toml::value_t::local_time` 10. `toml::value_t::array` 11. `toml::value_t::table` ### `operator<=` Defined only if `array_type` and `table_type` have `operator<`. ```cpp template bool operator<=(const basic_value& lhs, const basic_value& rhs) { return (lhs < rhs) || (lhs == rhs); } ``` ### `operator>` Defined only if `array_type` and `table_type` have `operator<`. ```cpp template bool operator>(const basic_value& lhs, const basic_value& rhs) { return !(lhs <= rhs); } ``` ### `operator>=` Defined only if `array_type` and `table_type` have `operator<`. ```cpp template bool operator>=(const basic_value& lhs, const basic_value& rhs) { return !(lhs < rhs); } ``` # `toml::type_error` Exception thrown in case of a type error. Contains the location information of the value that caused the type error. ```cpp struct type_error final : public ::toml::exception { public: type_error(std::string what_arg, source_location loc); ~type_error() noexcept override = default; const char* what() const noexcept override; source_location const& location() const noexcept; }; ``` # `toml::make_error_info` ```cpp template error_info make_error_info( std::string title, const basic_value& v, std::string msg, Ts&& ... tail); ``` Calls `location()` on a `basic_value`, passes the resulting `source_location` to [`make_error_info`]({{}}) to create an `error_info`. Refer to [`error_info`]({{}}) for more details. # `toml::format_error` ```cpp template std::string format_error(std::string title, const basic_value& v, std::string msg, Ts&& ... tail); ``` Calls `location()` on a `basic_value`, passes the resulting `source_location` to [`format_error`]({{}}) to create an `error_info`, then converts it to a string and returns it. Refer to [`error_info`]({{}}) for more details. # Related - [comments.hpp]({{}}) - [source_location.hpp]({{}}) - [types.hpp]({{}}) - [visit.hpp]({{}}) toml11-4.1.0/docs/content.en/docs/reference/value_t.md000066400000000000000000000015721464712047700225240ustar00rootroot00000000000000+++ title = "value_t.hpp" type = "docs" +++ # value_t.hpp # `value_t` `value_t` is used to handle the type information of `toml::value`. ```cpp namespace toml { enum class value_t : std::uint8_t { empty = 0, boolean = 1, integer = 2, floating = 3, string = 4, offset_datetime = 5, local_datetime = 6, local_date = 7, local_time = 8, array = 9, table = 10 }; std::ostream& operator<<(std::ostream& os, value_t t); std::string to_string(value_t t); } // toml ``` ## Non-member Functions ### Stream Operator ```cpp std::ostream& operator<<(std::ostream& os, value_t t); ``` Outputs the string representation of the `value_t` to the stream. ### `to_string` ```cpp std::string to_string(value_t t); ``` Returns the string representation of the `value_t`. toml11-4.1.0/docs/content.en/docs/reference/version.md000066400000000000000000000010221464712047700225400ustar00rootroot00000000000000+++ title = "version.hpp" type = "docs" +++ # version.hpp In `version.hpp`, macros related to the version information of toml11 are defined. ## Macros ### `TOML11_VERSION_MAJOR` The major version of toml11. ### `TOML11_VERSION_MINOR` The minor version of toml11. ### `TOML11_VERSION_PATCH` The patch version of toml11. ## Function ### `license_notice` ```cpp namespace toml { const char* license_notice() noexcept; } ``` Returns the license notice. Provided for convenience when redistributing without source code. toml11-4.1.0/docs/content.en/docs/reference/visit.md000066400000000000000000000042311464712047700222160ustar00rootroot00000000000000+++ title = "visit.hpp" type = "docs" +++ # visit.hpp In `visit.hpp`, `toml::visit` is defined. # `toml::visit` ## Functions ```cpp namespace toml { template /* Return value when Visitor is called with a value of basic_value */ visit(Visitor&& visitor, const basic_value& v); template /* Return value when Visitor is called with a value of basic_value */ visit(Visitor&& visitor, basic_value& v); template /* Return value when Visitor is called with a value of basic_value */ visit(Visitor&& visitor, basic_value&& v); } ``` `toml::visit` calls the overload of `Visitor` corresponding to the type held by `basic_value`, and returns the result. #### Requirements `Visitor` must be a function or function object callable with any type held by `basic_value`. Additionally, the return value must be consistent across all overloads. #### Example ```cpp #include #include struct type_name_of { std::string operator()(const toml::value::boolean_type &) const {return "boolean";} std::string operator()(const toml::value::integer_type &) const {return "integer";} std::string operator()(const toml::value::floating_type &) const {return "floating";} std::string operator()(const toml::value::string_type &) const {return "string";} std::string operator()(const toml::value::local_time_type &) const {return "local_time";} std::string operator()(const toml::value::local_date_type &) const {return "local_date";} std::string operator()(const toml::value::local_datetime_type &) const {return "local_datetime";} std::string operator()(const toml::value::offset_datetime_type&) const {return "offset_datetime";} std::string operator()(const toml::value::array_type &) const {return "array";} std::string operator()(const toml::value::table_type &) const {return "table";} }; int main() { toml::value v(3.14); std::cout << toml::visit(type_name_of{}, v) << std::endl; // floating return 0; } ``` # Related - [value.hpp]({{}}) toml11-4.1.0/docs/content.ja/000077500000000000000000000000001464712047700156405ustar00rootroot00000000000000toml11-4.1.0/docs/content.ja/_index.md000066400000000000000000000007621464712047700174350ustar00rootroot00000000000000+++ title = "Introduction" type = "docs" +++ # Introduction English version is [here](../en/) ## [Installation](docs/installation) toml11のインストール方法について説明します。 ## [Features](docs/features) toml11の機能と使い方を例に沿って説明します。 ## [Reference](docs/reference) toml11が持つ関数・クラスの詳細を説明します。 ## [ChangeLog](docs/changelog) v3系から、そしてリリースごとの変化を説明します。 toml11-4.1.0/docs/content.ja/docs/000077500000000000000000000000001464712047700165705ustar00rootroot00000000000000toml11-4.1.0/docs/content.ja/docs/changelog/000077500000000000000000000000001464712047700205175ustar00rootroot00000000000000toml11-4.1.0/docs/content.ja/docs/changelog/_index.md000066400000000000000000000403161464712047700223130ustar00rootroot00000000000000+++ title = "changelog" type = "docs" weight = 4 +++ # Change Log # v4.1.0 ## Added - `std::get`をサポート - `toml::value(std::u8string)`をサポート - `string_type = std::u8string`をサポート - `operator<<(std::ostream&, toml::value)`をサポート - `toml::integer_format`に、16進数表示で大文字を使うことを指定する`bool uppercase`を追加 - `template`を使って実装された `into_toml()` をサポート (by 萧迩珀) ## Fixed - Windowsで`\r\n`が改行に使われた際、出力時に`\r`が重複する問題を修正 (by Andreas Keller) ## Changed - CIで複数コアを使うことでビルドを高速化 # v4.0.3 ## Fixed - `toml_fwd.hpp`を使用した際にデフォルト引数が重複する問題を修正 - `toml::value`を複数渡した際に`toml::make_error_info`が使えない問題を修正 - `std::reference_wrapper`を持つ`toml::result`をコピー・ムーブする際の問題を修正 - hugoの最新版でドキュメントがビルドできない問題を修正 - コンパイラによる軽微な警告を多数修正 - CMakeの互換性に関する警告を抑制 ## Changed - CIビルドで`-Werror`, `/WX`を追加 - CIビルドでMSVCのwarningレベルを`/W3`から`/W4`に変更 - READMEでより多くの機能を紹介するよう更新 # v4.0.2 ## Fixed - `parse(FILE*)` 内の `fread` の結果をチェック - `toml11/version.hpp`のマクロを修正 - コンパイルオプションに関するドキュメントの更新 - ファイルオープンモードに関するドキュメントの更新 ## Changed - `CMakeLists.txt`内のバージョン番号を`version.hpp`から取得するように変更 # v4.0.1 ## Fixed - `sematic_version::{major, minor}` と `` 内で定義されるマクロの衝突を解消 - `discard_comments` の `operator<<` の定義を修正 (by Egor Pugin) - `format_location`を使用した際に最初の空行が出ない問題を解決 - 改行文字のみを含む行を指す`source_location`でエラーメッセージを生成した際に、同じ行が二回表示される問題を解決 - `README.md`内のリンクを修正 - `example/unicode`のREADMEのタイトルを修正 ## Added - `main`に変更があったとき、`single_include/toml.hpp`を自動生成するようCIを設定 # v3からv4への変化 ## 破壊的変更 ### `toml::basic_value`の`template`引数を変更 toml11 v3では、`toml::basic_value`はコメントコンテナ、テーブル型コンテナ、配列型コンテナをそれぞれ取っていました。 ```cpp template class Table = std::unordered_map, template class Array = std::vector> class basic_value; ``` ですが、`integer_type`などを変更したいという場合にはこれでは対応できません。 toml11 v4では、`toml::basic_value`は単一の`TypeConfig`を受け取り、より多くの型を変更可能にします。 ```cpp template class basic_value; ``` デフォルトでは`toml::value`が格納する型は変わりません。 型を変更する際は、 [`type_config`]({{< ref "/docs/reference/types.md">}}) を参照してください。 ### `toml::basic_value`の`std::initializer_list`サポートを削除 toml11 v3では、 `toml::value` に `std::initializer_list` を取るオーバーロードが用意されていました。 これにより、 `toml::value` を配列やテーブルで初期化する際により直観的な書き方が可能でした。 ```cpp // toml11 v3 toml::value v{1,2,3,4,5}; toml::value v{ {"a", 42}, {"b", "foo"} }; ``` しかし、これは同時に以下のような問題を引き起こしました。 一つ目は、1要素の配列と通常の値の区別がつかず、常に配列になってしまうことです。 ```cpp // toml11 v3 toml::value v{1}; // 1 ではなく [1,] になってしまう ``` 統一初期化記法が普及した現在、これは非常に不便です。 二つ目は、値が全て文字列のテーブルと、ネストされた配列の区別がつかないことです。 ```cpp // toml11 v3 toml::value v{ {"a", "foo"}, {"b", "bar"} }; // {a = "foo", b = "bar"} // [["a", "foo"], ["b", "bar"]] // のどちらでもあり得る ``` これらの問題は言語仕様上解決が困難です。 toml11 v4では、混乱を避けるため、`std::initializer_list`サポートを削除しました。 `toml::value` を配列で初期化する際はexplicitに `toml::array` を、 テーブルで初期化する際はexplicitに `toml::table` を指定する必要があります。 ```cpp // toml11 v4 toml::value v(toml::array{1,2,3,4,5}); toml::value v(toml::table{ {"a", 42}, {"b", "foo"} }); toml::value v{toml::array{1}}; // [1,] toml::value v{1} // 1 toml::value v{toml::table{{"a", "foo"}, {"b", "bar"}}}; toml::value v{toml::array{toml::array{"a", "foo"}, toml::array{"b", "bar"}}}; ``` これにより `toml::value` をテーブルや配列で初期化する際に少し不便になりますが、 explicitに型情報を記述することにより予測不能な値になることは避けることができます。 ### `toml::basic_value::is_uninitialized()` を `is_empty()` に変更 toml11 v3では、初期化されていない `basic_value` かどうかを判定する関数は `is_uninitialized` でした。 しかし、toml11 v4では言語拡張で `null `をサポートするため、意図的に空にされた値が構築される可能性があります。 なので命名を変更し、 `is_empty` としました。 ### `toml::string` を廃止、フォーマット情報を陽に格納 toml11 v3では、文字列が `basic` か `literal` かの情報を保持するため、 格納する文字列型に `toml::string` という `std::string` の薄いラッパーを使用していました。 ```cpp // toml11 v3 namespace toml { enum class string_t : std::uint8_t { basic = 0, literal = 1, }; struct string { string_t kind; std::string str; }; } // toml ``` toml11 v4では、数値型の基数や配列を複数行にするかどうかなどのより多くのフォーマット情報を持たせるため、 全ての型に `xxx_format` 型を用意し、値とペアで格納することにしました。 ```cpp // toml11 v4 enum class string_format : std::uint8_t { basic = 0, literal = 1, multiline_basic = 2, multiline_literal = 3 }; struct string_format_info { string_format fmt = string_format::basic; bool start_with_newline = false; }; ``` これにより、より細かいフォーマット情報を保持することができるようになり、 特に数値型や配列、テーブル型のフォーマット情報がパース後も維持できるようになりました。 ### `toml::format`の引数を変更 toml11 v3では、 `toml::format` が数値型の精度や幅などの値を受け取っていました。 しかし、これでは細かなフォーマット指定ができず、予想したようなファイルにシリアライズできませんでした。 toml11 v4では、各 `toml::value` にそれぞれのフォーマット情報を格納したため、 より細かいフォーマット情報を `toml::value` 自体が持ち運べるようになりました。 これにより、 `toml::format` 自体はフォーマット指定を受け取らず、 フォーマット時に使用できる言語機能フラグを持つ `toml::spec` のみを取るようになりました。 ### `toml::source_location`のメンバ関数を変更 toml11 v3では、 `toml::source_location` のメンバ型は一行分だけを意識したものでした。 toml11 v4では、 `toml::source_location` のメンバ型は複数行が前提になります。 ### `toml::format_underline`を`toml::format_location`に変更 toml11 v3では、 `toml::source_location` を使用して位置情報を文字列化するとき、 `toml::format_underline` を使用していました。 しかしこれは名称としてわかりにくいため、 `toml::format_location` に変更しました。 ### `format_error`の引数を変更 toml11 v3ではエラー情報を表すクラスがなかったため、`toml::format_error`の引数が複雑になっていました。 ```cpp template class T, template class A> std::string format_error(const std::string& err_msg, const basic_value& v, const std::string& comment, std::vector hints = {}, const bool colorize = TOML11_ERROR_MESSAGE_COLORIZED); template class T, template class A> inline std::string format_error(const std::string& err_msg, const toml::basic_value& v1, const std::string& comment1, const toml::basic_value& v2, const std::string& comment2, std::vector hints = {}, const bool colorize = TOML11_ERROR_MESSAGE_COLORIZED); template class T, template class A> inline std::string format_error(const std::string& err_msg, const toml::basic_value& v1, const std::string& comment1, const toml::basic_value& v2, const std::string& comment2, const toml::basic_value& v3, const std::string& comment3, std::vector hints = {}, const bool colorize = TOML11_ERROR_MESSAGE_COLORIZED); ``` toml11 v4では、`class error_info`と`make_error_info`を導入し、`format_error`の引数を簡略化しました。 ```cpp std::string format_error(const error_info& err); std::string format_error(const std::string& errkind, const error_info& err); template std::string format_error(std::string title, source_location loc, std::string msg, Ts&& ... tail); template std::string format_error(std::string title, const basic_value& v, std::string msg, Ts&& ... tail); ``` ### `toml::color` の制御を変更 toml11 v3では、出力に色を付けるかどうかに `toml::colorize` というマニピュレータを、 `toml::color::enable/disable` と並列で使用していました。 マニピュレータはストリームごとに色を付けるかどうかを決定できるものでしたが、 v4でストリームを使用する頻度が下がったことと、内部で使用する `std::ios_base::xalloc` がC++11ではスレッドセーフではないことなどから、 `toml::color::enable/disable` のみを使用するようにし、 `toml::colorize` を削除しました。 ## 破壊的でない変更 ### `parse_str`の追加 toml11 v3では、文字列そのものを取る`toml::parse`関数はありませんでした。 なので文字列をパースする際には`std::istringstream`を使う必要がありました。 これは不便なので、`toml::parse_str`を追加し、文字列を直接パース出来るようにしました。 ### `try_parse`の追加 toml11 v3では、パーサはエラーを発見した際には`toml::syntax_error`を送出していました。 しかし、例外を投げられない環境や、パフォーマンスの都合や記法の都合によって例外を投げたくない場合があります。 toml11 v4では `toml::result` を使用して、例外を投げずにパース失敗を伝える `toml::try_parse` を実装しました。 これも必ず例外を投げないというわけではなく、使用している標準ライブラリの内部でのエラー、 例えばメモリ不足によるアロケーション失敗での `std::bad_alloc` などは送出される可能性があります。 ### バイト列のパースをサポート ファイルなど以外の方法で得られたTOMLコンテンツをパース出来るようにするため、 `std::vector` を受け取る `toml::parse`, `toml::try_parse` を追加しました。 ### `toml::spec`の追加 toml11 v3では、TOML言語側の新機能は全て取り込み、またTOML言語の新バージョンに導入されることが決定した機能も マクロ `TOML11_USE_UNRELEASED_TOML_FEATURES` によって制御していました。 これは、toml11 v3を開発していた時点では TOML言語は0.4.0から0.5.0で、1.0.0に到達していなかったためです。 最新のTOML言語仕様に全てのユーザーが詳しいわけではないため、 古い言語使用に基づいたエラーメッセージを表示すると、コミュニティ全体を混乱させてしまいます。 よって、v1.0.0に達するまでは、できる限り素早く新しい言語仕様を提供し、かつユーザーにもアップデートを促す必要がありました。 しかし、現在のTOML言語仕様はv1.0.0です。 そのため、TOML v1.1.0がリリースされた後でもv1.0.0を使用する、という選択肢に気を配る必要が生じました。 よって、より柔軟にTOML言語仕様を選択できるよう、`toml::spec`を導入して、TOML言語バージョンを実行時に変更できるようにしました。 また、`toml::spec`では言語機能ごとにフラグを設定し、特定の言語機能のみを試すこともできるようにしました。 これは、toml11 v4特有の言語拡張でも使用します。 ### フォーマット情報の追加 toml11 v3では、文字列を除いてフォーマット情報が保存されず、シリアライズ時に考慮できるのは幅と精度だけでした。 しかし、これでは16進数整数がシリアライズ時に10進数になってしまったり、確実にinline tableにする方法がありませんでした。 toml11 v4では、全てのTOML型にフォーマット情報(`integer_format` etc)を追加し、 パース時とシリアライズ時に考慮するようにしました。 これによって、16進数整数や、インラインテーブル等のフォーマット情報をより細かく、値ごとに設定できるようになりました。 ### デフォルトでコメントを維持するよう変更 toml11 v3では、デフォルトではコメントがパースされず、シリアライズもされませんでした。 これは、コメントが後期に導入された機能で、特別なハックによって読み込まれていたためです。 toml11 v4では、デフォルトでコメントをパースし、保存し、シリアライズするようになりました。 また、パーサの実装も大幅に変更され、コメントが他の要素と同様にパースされるようになりました。 ### `single_include/toml.hpp` の追加 toml11は多機能なライブラリなので、開発効率のために機能ごとに異なるヘッダファイルを持っています。 ですが、これはインストールにある程度の手間が必要ということでもあります。 そこで、toml11 v4から全てのヘッダファイルを適切な順序で結合した `single_include/toml.hpp` ファイルを追加し、 単一のファイルで完結するライブラリをコピーするだけで導入できるようにしました。 ### コンパイル済みライブラリを選択可能に toml11は `template` を多用しているため、コンパイル時間が長くなります。 toml11 v4では、できるだけコンパイル可能な関数を増やし、それらを先にライブラリとしてコンパイルできるようにしました。 これにより、大規模な開発で使用する際にコンパイル時間を短くできると期待できます。 ### リファレンス・ドキュメントの追加 これまでは、READMEにすべてのfeatureを記載しており、関数の詳細な定義を示すリファレンスや、また日本語での資料などがありませんでした。 toml11 v4では、リファレンスを追加したドキュメントを同梱し、また日本語・英語両方で同一の内容を記載します。 ただし、ライブラリ作者は日本語母語話者であるため、日本語の内容を第一とし、英語の内容がそれと異なっていた場合は日本語の内容が正しいものとします。 toml11-4.1.0/docs/content.ja/docs/features/000077500000000000000000000000001464712047700204065ustar00rootroot00000000000000toml11-4.1.0/docs/content.ja/docs/features/_index.md000066400000000000000000000065551464712047700222110ustar00rootroot00000000000000+++ title = "features" type = "docs" weight = 2 bookCollapseSection = true +++ # features ここでは、toml11が提供する主な機能について、例を挙げながら説明します。 ## [ファイル・文字列をパースする](parsing_files) ファイルや文字列をパースする関数と、それが出力するエラーの扱い方について説明します。 以下の内容を含みます。 - ファイルをパースする - 文字列をパースする - バイト列をパースする - 例外を投げずにファイルをパースする - 例外を投げずに文字列をパースする - 例外を投げずにバイト列をパースする ## [`toml::value`から値を取り出す](value) `toml::value`が持つデータの型を調べ、取り出す方法、型変換を行う方法について説明します。 以下の内容を含みます。 - メンバ関数を使って値の型を調べる - メンバ関数を使って値にアクセスする - コメントにアクセスする - インラインテーブル・ドットキーの取り扱い - 日付情報の取り扱い - `toml::get`を使って変換する - `toml::get_or`を使って失敗時の値を指定する - `toml::find`を使って検索と変換を行う - `toml::find_or`を使って失敗時の値を指定する - ユーザー定義型との変換を定義する - `toml::visit`で関数を適用する - `toml::value`を構築する ## [エラーメッセージを作る](error_message) `toml::value`の値を使って、TOMLファイル中の位置情報つきのエラーメッセージを生成する方法について説明します。 以下の内容を含みます。 - `toml::value` の位置情報を取り出す - エラーメッセージを構築する - 出力に色を付ける ## [TOMLファイルを出力する](serialize) `toml::value`の値をフォーマットする方法と、可能なフォーマット指定について説明します。 以下の内容を含みます。 - `toml::value`の値ごとにフォーマットを指定する - `toml::value`をフォーマットして文字列化する ## [`toml::value`の型を変更する](configure_types) `toml::value`が格納する型(`integer_type`や`table_type`をカスタマイズする方法について説明します。 以下の内容を含みます。 - `type_config`の定義 - `ordered_type_config`を使用する - コメントを保存しないようにする - `std::deque`などの異なるコンテナを使用する - `boost::multiprecision`などの異なる数値型を使用する ## [TOMLリテラル](literal) C++内にTOMLファイルを埋め込むための`_toml`リテラルについて説明します。 以下の内容を含みます。 - TOMLリテラルを使用する ## [TOML言語バージョン](toml_spec) toml11がサポートするTOML言語のバージョン、主にTOML-v1.1.0で追加された言語機能を制御する方法について説明します。 以下の内容を含みます。 - TOML言語の1.1.0を使用する - TOML言語の1.1.0の一部の機能のみ使用する ## [TOML言語拡張](extension) toml11独自のTOML言語拡張について説明します。 以下の内容を含みます。 - `null` をサポートする - 浮動小数点数の16進数フォーマットをサポートする - 数値に単位を付けられるようにする toml11-4.1.0/docs/content.ja/docs/features/configure_types.md000066400000000000000000000202011464712047700241300ustar00rootroot00000000000000+++ title = "configuring types" type = "docs" weight = 50 +++ # 型をカスタマイズする `toml::value` は `integer_type` として `std::int64_t` を、 `table_type` として `std::unordered_map` を使用します。 しかし、場合によっては `boost::multiprecision::int128_t` や、 `std::map` を使用したい場合もあります。 そのため、 `toml::value` は `template` 引数を取って格納する型を変えられるように実装されています。 `std::string` が実際には `std::basic_string, std::allocator>` の エイリアスであるように、 `toml::value` は実際には `toml::basic_value` のエイリアスです。 ここでは、 `toml::type_config` が持つ型と、異なる `config` 型を定義する方法を説明します。 ## `type_config` `type_config` は、以下のメンバ型と`static`メンバ関数を持つクラスです。 ```cpp namespace toml { struct type_config { using comment_type = preserve_comments; using boolean_type = bool; using integer_type = std::int64_t; using floating_type = double; using string_type = std::string; template using array_type = std::vector; template using table_type = std::unordered_map; static result parse_int(const std::string& str, const source_location src, const std::uint8_t base); static result parse_float(const std::string& str, const source_location src, const bool is_hex); }; } ``` `toml::basic_value` は、格納する `boolean_type` を `TypeConfig::boolean_type`、 格納する `integer_type` を `TypeConfig::integer_type` 、のようにして定義しています。 また、 `array_type` は `TypeConfig::array_type>` 、 `table_type` は `TypeConfig::table_type>` と 定義されます。 これらのメンバ型とメンバ関数を定義したクラスを `toml::basic_value` に 渡すことで、その `toml::basic_value` が持つ型を変更できます。 `parse_int` と `parse_float` は、数値型を独自実装のものに変更した際にパース方法を提供するための関数です。 これらの関数には `0x` などのprefixと桁区切りの `_` が取り除かれた文字列、 例えば `123456` や `DEADBEEF` が渡されます。 `base` には `10`, `16`, `8`, `2` のいずれかが渡されます。 これらを使って `integer_type` と `floating_type` をパース出来るような関数を渡してください。 デフォルト実装として、`toml::read_int` と `toml::read_float` が提供されています。 ```cpp static result parse_int(const std::string& str, const source_location src, const std::uint8_t base) { return toml::read_int(str, src, base); } static result parse_float(const std::string& str, const source_location src, const bool is_hex) { return toml::read_float(str, src, is_hex); } ``` `read_int` は `istream` を使用し、16進と8進の場合は `std::hex` と `std::oct` を使用します。2進の場合は掛け算と足し算で実装されています。 これらをサポートしている型であれば、 `read_int` をそのまま使用できます。 `read_float` は `istream` を使用します。 16進浮動小数点数は `double` と `float` の場合しかサポートされておらず、 それ以外の型のときに呼ばれると常にパースエラーを返す実装になっているので、 もし浮動小数点数型をこれら以外の型にし、かつ `hexfloat` を使用する場合は、 それを実装してください。 `hexfloat` を使用しないのであれば、実装する必要はありません。 ## テーブル内の値の順序を維持する デフォルトの `toml::type_config` の他に、 `toml::ordered_type_config` が提供されています。 これは、 `table_type` を [ordered_map]({{< ref "docs/reference/ordered_map" >}}) に変更したものです。 これを使用したものを `toml::ordered_value` 、その配列型とテーブル型のエイリアスを `toml::ordered_array` と `toml::ordered_table` と定義しています。 `toml::parse(...)` を `toml::parse(...)` として呼び出すことで、 `toml::ordered_value` を使用することができます。 ## コメントを保存しない `type_config` は `comment_type` でコメントを保存するコンテナを定義しています。 コメントに特に情報がなく、パースせずに捨ててしまっていい場合は、 `comment_type` に `toml::discard_comments` を指定してください。 ```cpp struct wo_comment_config { using comment_type = toml::discard_comments; // XXX using boolean_type = bool; using integer_type = std::int64_t; using floating_type = double; using string_type = std::string; template using array_type = std::vector; template using table_type = std::unordered_map; static result parse_int(const std::string& str, const source_location src, const std::uint8_t base) { return toml::read_int(str, src, base); } static result parse_float(const std::string& str, const source_location src, const bool is_hex) { return toml::read_float(str, src, is_hex); } }; ``` ## 配列に`std::vector`以外のコンテナを使用する TOML配列の実装に`vector`以外のコンテナ(例:`std::deque`)を使用するには、 `array_type` を以下のように変更してください。 また、テーブル型のコンテナに `unordered_map` 以外のコンテナ(例:`std::map`)を使用するには、 `table_type` を以下のように変更してください。 ```cpp struct deque_map_config { using comment_type = toml::preserve_comments; using boolean_type = bool; using integer_type = std::int64_t; using floating_type = double; using string_type = std::string; template using array_type = std::deque; // XXX template using table_type = std::map; // XXX static result parse_int(const std::string& str, const source_location src, const std::uint8_t base) { return toml::read_int(str, src, base); } static result parse_float(const std::string& str, const source_location src, const bool is_hex) { return toml::read_float(str, src, is_hex); } }; ``` ## 数値型に `boost::multiprecision` を使用する `boost::multiprecision::cpp_int` と `boost::multiprecision::cpp_bin_float_oct` を使用することで、より幅の広い整数型とより精度の良い浮動小数点数型を使用することができます。 これらの型はストリーム演算子を実装しているため、デフォルト実装の `read_int` と `read_float` をそのまま使用できます。 ```cpp struct large_num_config { using comment_type = toml::preserve_comments; using boolean_type = bool; using integer_type = boost::multiprecision::cpp_int; using floating_type = boost::multiprecision::cpp_bin_float_oct; using string_type = std::string; template using array_type = std::vector; template using table_type = std::unordered_map; static toml::result parse_int(const std::string& str, const toml::source_location src, const std::uint8_t base) { return toml::read_int(str, src, base); } static toml::result parse_float(const std::string& str, const toml::source_location src, const bool is_hex) { return toml::read_float(str, src, is_hex); } }; ``` toml11-4.1.0/docs/content.ja/docs/features/error_message.md000066400000000000000000000215211464712047700235660ustar00rootroot00000000000000+++ title = "error message" type = "docs" weight = 30 +++ # エラーメッセージを出力する toml11は `toml::parse` や `toml::get/find`, `as_integer()` などから ファイル内の位置情報を含んだエラーメッセージを出力します。 例えば、パース時に整数の文法エラーを発見した場合、 ``` [error] bad integer: `_` must be surrounded by digits --> internal string at line 64 in file main.cpp | 1 | a = 123__456 | ^-- invalid underscore Hint: valid : -42, 1_000, 1_2_3_4_5, 0xC0FFEE, 0b0010, 0o755 Hint: invalid: _42, 1__000, 0123 ``` あるいは実際に格納されている型と異なる型を要求した場合 ``` [error] toml::value::as_string(): bad_cast to string --> input.toml | 1 | a = 123_456 | ^^^^^^^-- the actual type is integer ``` toml11は `toml::value` からこのようなエラーメッセージを作成する方法を提供します。 この機能を利用することで、TOMLの文法エラーだけでなく、 例えば正の値でなければならないところに負数が現れた場合などの アプリケーション固有のエラーメッセージを、TOMLファイル内の位置を指摘しながら ユーザーに伝えられるということです。 ## `toml::value` の位置情報からエラーメッセージを作成する `toml::value` はそれがパースされた位置の情報を持っています。 その情報は `toml::source_location` にまとめられ、`toml::value::location()` で取得できます。 ```cpp const toml::value& a = input.at("a"); const toml::source_location src = a.location(); ``` ファイルを `toml::parse` でパースした場合、そのTOMLファイル名と行数が保存されています。 `toml::parse_str` でパースした場合TOMLファイル名はありませんが、代わりに `toml::parse_str` を呼び出したC++ソースコードのファイル名と行数がTOMLファイル名として保存されています。 このページの最初の例は `toml::parse_str` から出力された例でした。 ファイル名の部分に注目してください。 詳細は [reference]({{}}) を参照してください。 `toml::source_location` または `toml::value` とそれに付随するエラーメッセージを `toml::make_error_info` に渡すことで、エラー情報を構築できます。 これを`toml::format_error` に渡すと、エラーメッセージが `std::string` にフォーマットされます。 ```cpp const toml::value& a = input.at("a"); if(a.as_integer() < 0) { const toml::error_info err = toml::make_error_info( "positive integer is required", // エラーのタイトル a, "but got negative value" // 値の横に書くメッセージ ); std::cerr << toml::format_error(err) << std::endl; } ``` これは以下のようになります。 ``` [error] positive integer is required --> input.toml | 1 | a = -123456 | ^^^^^^^-- but got negative value ``` 最後に補足をつけ足すこともできます。これはインデントされません。 ```cpp const toml::value& a = input.at("a"); if(a.as_integer() < 0) { const toml::error_info err = toml::make_error_info( "positive integer is required", // エラーのタイトル a, "but got negative value", // 値の横に書くメッセージ "Hint: `a` means length of the data" // 補足 ); std::cerr << toml::format_error(err) << std::endl; } ``` ``` [error] positive integer is required --> input.toml | 1 | a = -123456 | ^^^^^^^-- but got negative value Hint: `a` means length of the data ``` {{}} `toml::value` からファイル内の行を出力できるのは、 パースしたファイルが文字列としてメモリの中に残されているからです。 パースした文字列はその全体が `std::shared_ptr` で `toml::value` に共有されています。 コピーしてもファイル文字列全体がコピーされることはありません。 また、そのファイルをパースして構築された `toml::value` が全てデストラクトされた時点で、 ファイル情報もメモリ上から解放されます。 ですので、アプリケーションで使用する際には、 `toml::value` を直接保存するのではなく 読み込み中に必要な値を全て取り出して、変換した値を保存した方がよいでしょう。 {{}} ## 文字列に色を付ける エラーメッセージにはANSIエスケープコードを使って色を付けることができます。 `TOML11_COLORIZE_ERROR_MESSAGE` をコンパイル時に定義していれば、 toml11の出力するエラーメッセージはデフォルトで色が付くようになります。 そうでない場合は、 `toml::color::enable()` を呼び出すことにより、それ以降で出力される エラーメッセージには色が付くようになります。 逆に出力先がコンソールではないなどの理由で色をつけたくない場合は、 `toml::color::disable()` を呼び出してください。 その時点で色が付くようになっているかどうかは、 `toml::color::should_color()` の返り値で判定できます。 詳細は [reference]({{}}) を参照してください。 また、エラーのタイトルやエラーメッセージ、補足にはデフォルトで色が付きませんが、 `toml::color` にあるマニピュレータを使って色を付けることも可能です。 ```cpp std::ostringstream oss; oss << toml::color::red << "but got negative value"; const toml::error_info err = toml::make_error_info( "positive integer is required", // Error title a, oss.str(), // Message next to the value "Hint: `a` means length of the data" // Supplementary message ); ``` こちらも、詳細は [reference]({{}}) を参照してください。 ## エラーメッセージのprefixを`[error]`から変更する エラーには種類があり、デフォルトの `[error]` ではよくない場合もあるでしょう。 `toml::format_error` では、 `toml::error_info` の前に `std::string` を取って、それを `[error]` の代わりに出力することができます。 例えば、 ```cpp const toml::value& a = input.at("a"); if(a.as_integer() < 0) { const toml::error_info err = toml::make_error_info( "positive integer is required", // エラーのタイトル a, "but got negative value" // 値の横に書くメッセージ ); std::ostringstream prefix; prefix << toml::color::bold << toml::color::yellow << "[warn]"; std::cerr << toml::format_error(prefix.str(), err) << std::endl; return 0; } else { return a.as_integer() } ``` このようにすると、 `[warn]` から始まる警告を出力することができます。 他にも、`toml::format_error` に直接 `error_info` の構成要素を渡すことで、 `[error]` なしのエラーメッセージを作成できます。 ```cpp const toml::value& a = input.at("a"); if(a.as_integer() < 0) { std::cerr << toml::format_error( "[warn] positive integer is required", // エラーのタイトル a, "but got negative value" // 値の横に書くメッセージ ) << std::endl; return 0; } else { return a.as_integer() } ``` ## 複数の `toml::value` を指すエラーメッセージを作成する アプリケーションの設定では、先に読み込んだ値によって後に読み込んだ値が取れる範囲が変わることがあるでしょう。 そのような場合には、エラーの原因となる別の値を同時に出力したいはずです。 `toml::format_error` と `toml::make_error_info` は、 `toml::value` とそれに対応するエラーメッセージ `std::string` のペアを任意個取ることができます。 ```cpp std::cerr << toml::format_error( "[error] invalid range", a, "minimum value is defined here", b, "maximum value is defined here", c, "and it exceeds the range" ) << std::endl; ``` こちらも末尾に補足を追加することができます。 ```cpp std::cerr << toml::format_error( "[error] invalid range", a, "minimum value is defined here", b, "maximum value is defined here", c, "and it exceeds the range", "Hint: all the value must be in the range, [a, b)" ) << std::endl; ``` `toml::value` または `toml::source_location` を渡した場合、必ずそれに関する エラーメッセージが続く必要があります。 そうしない場合、非常にわかりにくいコンパイルエラーになります。 toml11-4.1.0/docs/content.ja/docs/features/extension.md000066400000000000000000000101451464712047700227450ustar00rootroot00000000000000+++ title = "extension" type = "docs" weight = 80 +++ # TOML言語拡張 TOML言語は現在 v1.0.0 が最新版ですが、その後もいくつかの新機能が議論の末マージされ、 v1.1.0に向けて議論が続いています。 そこで議論された機能の中には、有用なケースが少ないと考えられたものや、 提案された際の方向性では導入が難しいもの、導入がされなかったものも多くあります。 toml11では、そのような機能のなかからいくつかを選んで、実験的に実装を行っています。 これらはtoml11ではサポートされていますが、他のパーサではサポートされておらず、また サポートされる予定もないことに注意してください。 また、これらの機能はデフォルトで使用されない設定になっており、 使用するためには機能フラグをそれぞれ `true` にしなければなりません。 非標準の機能なので、あえて明示的に書かなければ使えないように設計しています。 いくつかの機能は今後TOML言語自体に新機能としてマージされる可能性があります。 もし以下の拡張機能を完全に置き換えられる機能が導入された場合、拡張機能は 本来の機能の実装後にマイナーバージョンアップで削除される可能性があります。 ## `null` TOMLファイル内で値として`null`を使えるようになります。 ``` a = null b = [ 1, 2, 3, null, 5] ``` これを使用するには、 `toml::spec` の `ext_null_value` を `true` にします。 パースすると、デフォルト構築した場合と同様の `toml::value_t::empty` となります。 ただし、ファイル内の位置情報は設定されます。 `null` は値の文脈でのみパースされるので、キーに `null` を使用した際はこれまで通り `"null"` という文字列のキーとして解釈されます。 ```cpp #include int main() { toml::spec spec; spec.ext_null_value = true; const auto v = toml::parse_str("a = null", spec); assert(v.at("a").is_empty()); assert(v.at("a").is(toml::value_t::empty)); return 0; } ``` ## 浮動小数点数の16進数フォーマット TOMLファイル内で浮動小数点数に16進数フォーマットを使用できるようになります。 ``` a = 0x1.91eb851eb851fp+1 # 3.14 ``` これを使用するには、 `toml::spec` の `ext_hex_float` を `true` にします。 フォーマットは `printf` で `%a/%A` を指定した場合に準拠します。 ```cpp #include int main() { toml::spec spec; spec.ext_hex_float = true; const auto v = toml::parse_str("a = 0x1.91eb851eb851fp+1", spec); assert(v.at("a").is_floating()); assert(v.at("a").as_floating() == 3.14); return 0; } ``` ## 整数・浮動小数点数のsuffix TOMLファイル内で数値の後ろにsuffixをつけられるようになります。 10進数表記の整数と浮動小数点数で使用できます。 単位を表示するときなどに便利です。 ``` a = 86_400_sec b = 3.1416_rad c = 10_μm ``` ですが、これらはあくまで単なる `suffix` であり、単位換算は行われません。 単位換算が必要な場合は、ユーザーが `suffix` を参照して実装してください。 これを使用するには、 `toml::spec` の `ext_num_suffix` を `true` にします。 数値と接尾辞の間は`_`で区切られている必要があります。 数値部分との区別のため、suffixは数値で始まることはできません。 ``` distance = 100_m # valid distance = 10_0m # invalid distance = 10_0_m # valid ``` 接尾辞は`std::string suffix`としてフォーマット情報に保持されます。 ```cpp #include int main() { toml::spec spec; spec.ext_hex_float = true; const auto v = toml::parse_str("a = 86_400_sec", spec); assert(v.at("a").is_integer()); assert(v.at("a").as_integer() == 86400); assert(v.at("a").as_integer_fmt().suffix == "sec"); return 0; } ``` toml11-4.1.0/docs/content.ja/docs/features/literal.md000066400000000000000000000034431464712047700223700ustar00rootroot00000000000000+++ title = "toml literal" type = "docs" weight = 60 +++ # `_toml`リテラル `""_toml`リテラルによって、TOMLファイルをその場でフォーマットできます。 ```cpp #include int main() { using namespace toml::literals::toml_literals; const auto v = "a = 42"_toml; assert(v.at("a").as_integer() == 42); return 0; } ``` 改行を含む場合、生文字列リテラルが便利です。 ```cpp #include int main() { using namespace toml::literals::toml_literals; const auto v = R"( a = 42 b = "foo" )"_toml; assert(v.at("a").as_integer() == 42); assert(v.at("b").as_string() == "foo"); return 0; } ``` 値が単体で書かれていた場合、その値が返されます。 ```cpp #include int main() { using namespace toml::literals::toml_literals; const auto a = "42"_toml; const auto b = "12:34:56"_toml; assert(v.at("a").as_integer() == 42); assert(v.at("b").as_local_time().hour == 12); assert(v.at("b").as_local_time().minute == 34); assert(v.at("b").as_local_time().second == 56); return 0; } ``` TOMLは数値のみからなるキーを許可しています。 よって、`[1]`はテーブル名として合法です。 `[1]`のようにテーブル定義と配列の区別がつかない場合、テーブル定義が優先されます。 配列として解釈させるには、trailing commaを使用してください。 ```cpp #include int main() { using namespace toml::literals::toml_literals; const auto t = "[1]"_toml; // {1 = {}} const auto a = "[1,]"_toml; // [1,] assert(t.is_table()); assert(t.at("1").is_table()); assert(a.is_array()); assert(a.at(0).as_integer() == 1); return 0; } ``` toml11-4.1.0/docs/content.ja/docs/features/parsing_files.md000066400000000000000000000241621464712047700235620ustar00rootroot00000000000000+++ title = "parsing files" type = "docs" weight = 10 +++ # ファイル・文字列をパースする toml11では、`toml::parse` や `toml::try_parse` を使って、ファイルや文字列、バイト列をパースすることができます。 これらは成功時に `toml::value` を返します。 ファイルは常にテーブルになりますが、返り値が `toml::table` でないことに気を付けてください。 `toml::value` はファイルに関するメタデータを持っており、 `toml::table` は `std::unordered_map` のエイリアスでしかありません。 メタデータを返すために、 `toml::table` ではなく `toml::value` を返しています。 ファイルのルートに対応する `toml::value` は常に `table_type` を持ちます。 ## ファイルをパースする ファイルをパースする際は、 [`toml::parse`]({{< ref "docs/reference/parser#parse" >}}) または [`toml::try_parse`]({{< ref "docs/reference/parser#try_parse" >}}) を使います。 ### `toml::parse` #### `std::string`でファイル名を指定する [`toml::parse`]({{< ref "docs/reference/parser#parse" >}}) は、文字列でファイル名を受け取り、そのファイルを開いてパースします。 以下のサンプルは、`input.toml`というファイルをパースし、`title`という変数を文字列として取り出し、出力するコードです。 ```cpp #include #include int main() { const toml::value input = toml::parse("input.toml"); std::cout << input.at("title").as_string() << std::endl; return 0; } ``` #### `std::filesystem::path`でファイルを指定する [`toml::parse`]({{< ref "docs/reference/parser#parse" >}}) には、`std::filesystem::path`を渡すことも可能です。 当然ですが、``がサポートされるC++17以降でなければ使用できません。 #### `std::istream`で入力ストリームを指定する [`toml::parse`]({{< ref "docs/reference/parser#parse" >}}) には、`std::istream`を渡すことも可能です。 標準ライブラリが改行文字を自動変換することによるファイルサイズと文字数との不整合を避けるため、 `std::ios::binary`を使ってバイナリモードで開いてください。 その際、ファイル名の情報がなくなるため、エラーメッセージ中では `"unknown file"` となります。 これを避けるため、 `std::istream` を取る場合は第二引数に `std::string` でファイル名を取ることもできます。 `std::ifstream` 以外にも、 `std::istringstream` 等の別の`istream`を受け取ることができます。 ただし、呼び出した時点で内容が全て読み込める必要があります。 ```cpp #include #include int main() { std::string filename("input.toml"); std::ifstream ifs(filename); const toml::value input = toml::parse(ifs, filename); std::cout << input.at("title").as_string() << std::endl; return 0; } ``` #### `FILE*`でファイルを指定する [`toml::parse`]({{< ref "docs/reference/parser#parse" >}}) には、`FILE*`を渡すことも可能です。 標準ライブラリによる改行文字の自動変換によるファイルサイズと文字数との不整合を避けるため、 `fopen("example.toml", "rb")`のようにしてバイナリモードで開いてください。 この場合も、`std::istream`のときと同様に、第二引数に文字列でファイル名を与える必要があります。 `FILE*`を渡した場合、ファイルの読み込みに失敗した際には`errno`が報告されます。 #### エラー時の挙動 [`toml::parse`]({{< ref "docs/reference/parser#parse" >}}) は、文法エラーを発見した場合 [`toml::syntax_error`]({{}}) を送出します。 [`toml::syntax_error`]({{}}) は、 [`toml::exception`]({{}}) から派生しており、またそれは`std::exception`から派生します。 よって、 [`toml::syntax_error`]({{}}) からは `what()` メンバ関数を使ってエラーメッセージを取り出すことができます。 また、 [`toml::syntax_error`]({{}}) は [`std::vector`]({{}}) を持っており、`errors()`メンバ関数を使ってそれにアクセスすることもできます。 `toml::parse` はごく簡単なエラーならスキップして復帰し、複数のエラーを報告しようと努めます。 数値のフォーマットエラー程度なら復帰できることが多いですが、 配列やテーブルの中のエラーは復帰できずに似たようなエラーを複数回報告することもあります。 冗長であると感じた場合は、 `std::vector` の `front()` だけを使用するようにすると、 確実に問題のある個所に関するメッセージを得られます。 ```cpp #include int main() { // parse try { const toml::value input = toml::parse("input.toml"); std::cout << input.at("title").as_string() << std::endl; } catch(const toml::syntax_error& err) { // 全てのエラーを報告 std::cerr << err.what() << std::endl; // 最初のエラーのみ報告 std::cerr << err.errors().front() << std::endl; } } ``` ### `toml::try_parse` `toml::parse` は失敗時に例外を送出しますが、 `toml::try_parse` は失敗時に例外を投げません。 その代わり、返り値が `toml::value` ではなく [`toml::result>`]({{}}) になります。 [`result`]({{}}) 型は成功値または失敗値のどちらかを持つ型です。 Rustの `Result` やHaskellの `Either` に相当します。 ```cpp #include int main() { const auto parse_result = toml::try_parse("input.toml"); if(parse_result.is_ok()) { std::cout << parse_result.unwrap().at("title").as_string() << std::endl; } else { std::cerr << parse_result.unwrap_err().at(0) << std::endl; } return 0; } ``` [`result`]({{}}) 型がどちらの値を保持しているかは `is_ok()`, `is_err()` 関数を使って確認できます。 また、 `unwrap()`, `unwrap_err()` によって成功値、失敗値をそれぞれ取り出せます。 `unwrap` が失敗した場合は、 `bad_result_access` 例外が送出されます。 `as_ok()` と `as_err()` 関数を使用すると、失敗時には例外が送出されず、未定義動作となります。 {{}} `try_parse` は `syntax_error` や `file_io_error` を投げず、同じ `toml::error_info` を `result` の失敗型として返しますが、絶対に例外を投げないわけではありません。 標準ライブラリ内部でエラーが発生した場合、例えばメモリ不足の際に `vector` の `allocate` が失敗した場合などには `std::bad_alloc` が送出されますが、 `toml::try_parse` はこれが送出された場合は `catch` せずに通します。 そのため、それらのような標準ライブラリの内部で発生する例外は送出される恐れがあります。 {{}} ## 文字列をパースする ### `toml::parse_str` [`toml::parse_str`]({{}}) は、ファイル名ではなくパースする文字列そのものを受け取ります。 また、エラーメッセージのTOMLファイルの名前に相当する部分には、`std::source_location` 相当のコンパイラ拡張機能が使用できる場合、 `parse_str` を呼び出したC++ファイルの名前と行数が代わりに使用されます。 ```cpp #include #include int main() { const toml::value input = toml::parse_str("title = \"parse_str\""); std::cout << input.at("title").as_string() << std::endl; return 0; } ``` ### `toml::try_parse_str` [`toml::try_parse_str`]({{}}) は、 `parse_str` と同じくパースする文字列そのものを受け取り、 `try_parse` と同じくエラーの報告に [`toml::result`]({{}}) を使用します。 ```cpp #include #include int main() { const auto parse_result = toml::try_parse_str("title = \"parse_str\""); if(parse_result.is_ok()) { std::cout << parse_result.unwrap().at("title").as_string() << std::endl; } else { std::cerr << parse_result.unwrap_err().at(0) << std::endl; } return 0; } ``` ## バイト列をパースする ファイルではなくバイト列をパースすることも可能です。 UTF-8でエンコードされている必要があるため、`unsigned char`を使っています。 ### `toml::parse(std::vector)` 挙動は [`toml::parse`]({{}}) と同一です。 バイト列をパースする際は、 `filename` を要求します。 ```cpp #include #include int main() { std::vector bytes{/* ... */}; const toml::value input = toml::parse(bytes, "internal bytes"); std::cout << input.at("title").as_string() << std::endl; return 0; } ``` ### `toml::try_parse(std::vector)` 挙動は [`toml::try_parse`]({{}}) と同一です。 バイト列をパースする際は、 `filename` を要求します。 ```cpp #include #include int main() { std::vector bytes{/* ... */}; const auto parse_result = toml::try_parse(bytes, "internal bytes"); if(parse_result.is_ok()) { std::cout << parse_result.unwrap().at("title").as_string() << std::endl; } else { std::cerr << parse_result.unwrap_err().at(0) << std::endl; } return 0; } ``` toml11-4.1.0/docs/content.ja/docs/features/serialize.md000066400000000000000000000171301464712047700227210ustar00rootroot00000000000000+++ title = "serializing values" type = "docs" weight = 40 +++ # TOMLファイルを出力する `toml::format` を使うと、 `toml::value` を文字列にすることができます。 ```cpp #include #include int main() { const toml::value v(toml::table{ {"a", 42}, {"b", "foo"}, }); const std::string s = toml::format(v); const toml::value u = toml::parse_str(s); assert(u.at("a").as_integer() == 42); assert(u.at("b").as_string() == "foo"); return 0; } ``` `table_type` を格納している `toml::value` が渡されると、それがファイルのルートテーブルとして解釈されます。 もし `table_type` 以外を格納している `toml::value` が渡されると、その値だけがフォーマットされます。 一部のフォーマット指定では、キーが渡されていないとフォーマットできないことがあります。 例えば、 `toml::array_format::array_of_tables` は `[[array.of.tables]]` の形でフォーマットするので、 キーへのアクセスを要求します。 キーを要求するフォーマット指定の値がキーなしで渡された場合、 `toml::serialization_error` が送出されます。 他にも、フォーマット指定と矛盾する値が含まれる場合には、 `toml::serialization_error` が送出されます。 例えば、 `integer_format::hex` が指定された整数が負の値を持っている場合や、 `string_format::literal` が指定された文字列が改行を含んでいる場合などです。 フォーマットの指定方法は後述します。 ## キーを渡して出力する `toml::format` には `std::string` としてキーを渡すことが可能です。 その場合、ルートテーブルの下にそのキーがあり、渡した値はそのキーに対応すると解釈されます。 キーが複数段になる場合、 `std::vector` を渡すことができます。 ```cpp #include #include int main() { const toml::value v(toml::table{ {"a", 42}, {"b", "foo"}, }); const std::string s = toml::format("bar", v); const toml::value u = toml::parse_str(s); assert(u.at("bar").at("a").as_integer() == 42); assert(u.at("bar").at("b").as_string() == "foo"); return 0; } ``` ## フォーマットを指定する `toml::value` のそれぞれの型には、対応するフォーマット情報型があります。 `toml::value::integer_type` には `toml::integer_format_info` が、 `toml::value::table_type` には `toml::table_format_info` があります。 これらは、パースした際に設定され、型が変わらない限り値を変更しても引き継がれます。 また、 `as_integer_fmt()` や `as_table_fmt()` といったメンバ関数によってアクセスすることができ、 直接編集することが可能です。 以下ではいくつかの例を挙げて使い方を説明します。 フォーマットへのアクセス方法は [`toml::value`のリファレンス]({{< ref "/docs/reference/value" >}})を、 フォーマット情報クラスの完全なリストと詳細は [formatのリファレンス]({{< ref "/docs/reference/format" >}})を 参照してください。 ### 整数のフォーマットを指定する 整数は、基数と幅、そして `_` の位置を指定することができます。 `hex`, `oct`, `bin` のとき、指定された幅に達するまでゼロで埋められます。 `dec` の場合は幅指定はスペースを追加しますが、これはパースされません。 ```cpp #include int main() { toml::value v(0x00C0'FFEE); v.as_integer_fmt().fmt = toml::integer_format::hex; v.as_integer_fmt().width = 8; v.as_integer_fmt().spacer = 4; const std::stirng s = toml::format(v); assert(s == "0x00C0_FFEE"); return 0; } ``` 詳細は、[reference]({{< ref "/docs/reference/format#integer_format" >}}) を参照してください。 ### 配列を単一行・複数行にする 配列には、 `toml::array_format::oneline` や `toml::array_format::multiline` を指定できます。 ```toml # oneline a = [1, 2, 3, 4, 5] # multiline a = [ 1, 2, 3, 4, 5 ] ``` `multiline` のときは、インデントを指定できます。 各要素は `body_indent` の分だけインデントされ、閉じ括弧 `]` は `closing_indent` の分だけインデントされます。 文字種は `indent_type` で指定され、 `toml::indent_char::space` または `toml::indent_char::tab` が選択できます。 {{}} インデントに使用する文字種は統一してください。 一つのファイル内でインデントに異なる文字種が指定された場合、結果は未規定になります。 何らかのインデントがされますが、全ての箇所で文字種やインデントの深さは不定となります。 {{}} また、 `array` の要素が全て `table_type` を持つ場合、 `toml::array_format::array_of_tables` が指定できます。 `array_of_tables` を指定せずに `multiline` にした場合、テーブルはインラインテーブルになります。 ```toml # multiline a = [ {foo = 42}, {bar = "hoge"}, ] # array_of_tables [[a]] foo = 42 [[a]] bar = "hoge" ``` デフォルトでは、 `toml::array_format::default_format` が指定されます。 これは適したフォーマットを自動的に選択します。 例えば、 `default_format` で全要素が `table_type` だった場合、 `array_of_tables` が選択されます。 また、十分短い配列は `oneline` に、長い配列またはネストされた配列などの複雑な配列は `multiline` になります。 詳細は、[reference]({{< ref "/docs/reference/format#array_format" >}}) を参照してください。 ### テーブルをインラインテーブルにする テーブルをインラインテーブルにする際は `toml::table_format::oneline` を指定します。 通常のテーブルにする際は、 `toml::table_format::multiline` を指定します。 ```toml oneline = {a = 42, b = "foo"} [multiline] a = 42 b = "foo" ``` TOML v1.1.0ではインラインテーブル内での改行が許可されますが、その場合は `toml::table_format::multiline_oneline` とします。 これは、後述するTOMLバージョン指定で対応する機能フラグが`true`になっていない限り無視されます。 ```toml multiline_oneline = { a = 42, b = "foo" } ``` 詳細は、[reference]({{< ref "/docs/reference/format#table_format" >}}) を参照してください。 ## TOML言語バージョンを指定して出力する TOML v1.1.0で許可されたインラインテーブル内の改行や`\x`エスケープシーケンスのように、 TOMLバージョンによって使用できない言語機能があります。 `toml::format` は最後の引数に `toml::spec` を取ることができます。 これにより、シリアライズ時に使用するTOMLのバージョンを指定することができます。 特に、 `toml::parse` で `toml::spec` を使用して新機能を使用した場合は、 パースした値がそのバージョンでしか使えないフォーマット情報を持つ場合があるので、 `toml::format` にも同じ `toml::spec` を渡すことを忘れないようにしてください。 ```cpp #include #include int main() { const auto spec = toml::spec::v(1, 1, 0) const toml::value v = toml::parse("input.toml", spec); std::cout << toml::format(v, spec); return 0; } ``` toml11-4.1.0/docs/content.ja/docs/features/toml_spec.md000066400000000000000000000067021464712047700227220ustar00rootroot00000000000000+++ title = "toml spec" type = "docs" weight = 70 +++ # TOML言語バージョン [`toml::spec`]({{< ref "docs/reference/spec#tomlspec" >}}) によって、 `toml::parse` や `toml::format` で使用するTOML言語のバージョンや、個別の機能フラグを指定することができます。 ## TOMLのバージョンを指定する [`toml::spec`]({{< ref "docs/reference/spec#tomlspec" >}}) は [`toml::semantic_version`]({{< ref "docs/reference/spec#tomlsemantic_version" >}}) から構築できます。 ```cpp #include int main() { toml::spec spec(toml::semantic_version(1, 1, 0)); return 0; } ``` ですがこれは長いので、`toml::spec::v()`関数が用意されています。 ```cpp #include int main() { toml::spec spec = toml::spec::v(1, 1, 0); return 0; } ``` 特に指定しない場合、デフォルトの値で構築する `toml::spec::default_version()` が使用されます。 デフォルトの値はtoml11のバージョンによって変わりますが、その時点でリリースされているTOML言語の最新バージョンに追従します。 v4.0.0現在、TOML v1.1.0はまだリリースされていないため、デフォルトのTOMLバージョンはv1.0.0です。 {{}} TOML v1.1.0の一部の機能にはかなり長い議論が続いており、まだ差し戻される可能性があります。 実際に差し戻された場合、toml11はマイナーバージョンアップでそれらの機能を削除、もしくは対応するそれ以降のバージョンに移動します。 そのような意味で、将来のバージョンに関する機能は全て不安定なものと考えてください。 {{}} ### バージョン指定でパースする [`toml::parse`]({{< ref "docs/reference/parser" >}}) のオーバーロードは、ファイル名に続いて`toml::spec`を受け取ります。 これによって、使用するTOMLバージョンを変更できます。 ```cpp #include int main() { toml::value input = toml::parse("input.toml", toml::spec::v(1, 1, 0)); return 0; } ``` ### バージョン指定でシリアライズする [`toml::format`]({{< ref "docs/reference/serializer" >}}) のオーバーロードは、 `toml::value` に続いて `toml::spec` を受け取ります。 これによって、使用するTOMLバージョンを変更できます。 ```cpp #include int main() { toml::value v = toml::parse("input.toml", toml::spec::v(1, 1, 0)); std::cout << toml::format(v, toml::spec::v(1, 1, 0)) << std::endl; return 0; } ``` もしフォーマット変数などによって指定されているフォーマットが渡された `toml::spec` では許可されていないものだった場合、指定は無視されて他のフォーマットにフォールバックされます。 ## TOMLに追加された新機能を個別に指定する TOMLのバージョンアップで追加された機能は複数あり、そのうちの一部だけを有効にすることが可能です。 ```cpp #include int main() { toml::spec spec = toml::spec::v(1, 0, 0); // インラインテーブル内での改行を許可 spec.v1_1_0_allow_newlines_in_inline_tables = true; toml::value input = toml::parse("input.toml", spec); return 0; } ``` 全てのフラグのリストは、 [`toml::spec`]({{< ref "docs/reference/spec#tomlspec" >}}) を参照してください。 toml11-4.1.0/docs/content.ja/docs/features/value.md000066400000000000000000000614171464712047700220550ustar00rootroot00000000000000+++ title = "getting values" type = "docs" weight = 20 +++ # 値を取り出す ここでは、 `toml::value` が格納している値にアクセスする方法を説明します。 ## メンバ関数を使って値にアクセスする ### `is_something` と `as_something` `toml::value` は `is_boolean()` や `is_integer()` などのメンバ関数を持っており、 これらを使うと持っている型を調べることができます。 また、 `as_boolean()`, `as_integer()` などのメンバ関数も持っており、 これらを使ってその型にアクセスすることができます。 完全なリストは [`toml::value` のリファレンス]({{}}) を参照してください。 ```cpp toml::value v = /* ... */; if(v.is_integer()) { std::cout << v.as_integer() << std::endl; } ``` 指定された値と異なる型が格納されていた場合、 [`toml::type_error`]({{}}) が送出されます。 その `what()` は以下のようなメッセージを含みます。 ``` [error] toml::value::as_string(): bad_cast to string --> input.toml | 1 | a = 123_456 | ^^^^^^^-- the actual type is integer ``` ### `toml::value_t` 型情報は [`enum class toml::value_t`]({{}}) で識別できます。 [`type()`]({{}}) メンバ関数は、現時点で格納している値の型情報を返します。 ```cpp toml::value v = /* ... */; switch(v.type()) { case toml:value_t::empty : { /*...*/ break; } case toml:value_t::boolean : { /*...*/ break; } case toml:value_t::integer : { /*...*/ break; } case toml:value_t::floating : { /*...*/ break; } case toml:value_t::string : { /*...*/ break; } case toml:value_t::offset_datetime: { /*...*/ break; } case toml:value_t::local_datetime : { /*...*/ break; } case toml:value_t::local_date : { /*...*/ break; } case toml:value_t::local_time : { /*...*/ break; } case toml:value_t::array : { /*...*/ break; } case toml:value_t::table : { /*...*/ break; } default: {break;} } ``` [`is(toml::value_t)`]({{}}) メンバ関数は、渡された `value_t` と同じ型の値を格納している場合 `true` を、 それ以外の場合 `false` を返します。 ```cpp toml::value v = /* ... */; if(v.is(toml::value_t::integer)) { std::cout << v.as_integer() << std::endl; } ``` ### `at`, `[]`, `contains`, `size`, `push_back`, `emplace_back` 標準ライブラリコンテナが持つメンバ関数の一部は、 `toml::value` も提供しています。 これらは、内部で `toml::value` を対応する型に変換し、そのメンバ関数を呼び出します。 #### `at(std::size_t i)`, `operator[](std::size_t i)` `as_array().at(i)`, `as_array()[i]` と同等です。 `toml::value` はデフォルトで `std::vector` を `array_type` に使うので、 エラーが発生した際には `at` は `std::out_of_range` を送出し、`operator[]` は未定義動作となります。 ```cpp toml::value v(toml::array{1,2,3}); std::cout << v.at(1); ``` 格納している型が `array_type` ではなかった場合、 `type_error` を送出します。 #### `at(std::string key)`, `operator[](std::string key)` `as_table().at(key)`, `as_table()[key]` と同等です。 `toml::value` はデフォルトで `std::unordered_map` を `table_type` に使うので、 対応する値が存在しない場合は `at` は `std::out_of_range` を送出し、 `operator[]` は新しく `toml::value` を構築してそれへの参照を返します。 そのため、`operator[]` に `const` 版はありません。 ```cpp toml::value v(toml::table{}); v["a"] = 42; ``` 格納している型が `table_type` ではなかった場合、 `type_error` を送出します。 #### `size()` 長さを返します。 `array_type` または `table_type` の場合は要素数、 `string_type` の場合は文字数を返します。 格納している型がどれでもなかった場合、 `type_error` を送出します。 #### `push_back()`, `emplace_back()` `as_array().push_back()`, `as_array().emplace_back()` と同一です。 格納している型が `array_type` ではなかった場合、 `type_error` を送出します。 ## コメントにアクセスする toml11では、デフォルトでコメントがパースされ、対応する値に行ごとに保存されます。 対応する値は、連続するコメント行の直後に来る値か、もしくはそのコメントと同じ行に描かれている値です。 直前または直後に値がなければ、そのコメントはどこにも紐づけられず、無視されます。 ```toml # input.toml # これはaに関するコメントです。 a = 42 b = 3.14 # これはbに関するコメントです。 # このコメントには対応する値がないため無視されます。 # これは1番目のcに関するコメントです。 # これは2番目のcに関するコメントです。 c = "foo" # これは最後のcに関するコメントです。 ``` 値に対応するコメントには、`toml::value` の `comments()` メンバ関数を使ってアクセスします。 `comments()` は `std::vector` と同じメンバ関数を持つコンテナを返します。 ```cpp const auto v = toml::parse("input.toml"); const auto& a = v.at("a"); const auto& b = v.at("b"); const auto& c = v.at("c"); assert(a.comments().size() == 1); assert(a.comments().at(0) == "# これはaに関するコメントです。"); assert(b.comments().size() == 1); assert(b.comments().at(0) == "# これはbに関するコメントです。"); assert(c.comments().size() == 3); assert(c.comments().at(0) == "# これは1番目のcに関するコメントです。"); assert(c.comments().at(1) == "# これは2番目のcに関するコメントです。"); assert(c.comments().at(2) == "# これは最後のcに関するコメントです。"); ``` ファイル全体に対応するルートテーブルに関するコメントは、ファイルの先頭に書きます。 ```toml # ルートテーブルに関するコメントです。 # これもルートテーブルに関するコメントです。 # これはaに関するコメントです。 a = 42 ``` ただし、もしファイルの先頭のコメントの直後に値が来た場合、 そのコメントはその値に関するコメントと解釈され、ルートテーブルのコメントはなくなります。 ```toml # これはaに関するコメントです。 # これもaに関するコメントです。 a = 42 ``` ## インラインテーブル・ドットキーの取り扱い インラインテーブルは単にテーブルで、C++コード上で他のテーブルと異なる点はありません。 ```toml a = {b = 42, c = "foo"} ``` ドットキーも単にテーブルで、C++コード上で他のテーブルと異なる点はありません。 ```toml a.b = 42 a.c = "foo" ``` これらは以下のファイルと全く同じ構造を持ちます。 ```toml [a] b = 42 c = "foo" ``` なので、どの記法でも以下の全く同一のコードで処理できます。 ```cpp const auto input = toml::parse("input.toml"); assert(input.at("a").at("b").as_integer() == 42); assert(input.at("a").at("c").as_string() == "foo"); ``` ただし、フォーマット情報によって区別することは可能です。 ```cpp const auto input = toml::parse("input.toml"); switch(input.at("a").as_table_fmt().fmt) { case toml::table_format::oneline: { std::cout << "inline table" << std::endl; break; } case toml::table_format::multiline: { std::cout << "normal table" << std::endl; break; } case toml::table_format::dotted: { std::cout << "dotted keys" << std::endl; break; } } ``` このフォーマット情報は後述するシリアライズの際も考慮されます。 ## 日付情報の取り扱い [`local_date`]({{}}), [`local_time`]({{}}), [`local_datetime`]({{}}), そして [`offset_datetime`]({{}}) は、 toml11では対応するメンバ変数を持つ専用の構造体にパースされます。 使用する際は、直接値を取り出す他にも、後述する `toml::get` や `toml::find` を使用して、 `std::chrono::system_clock::time_point` や `std::tm` 等の型に変換することができます。 ## `toml::get`を使って変換する `toml::get` は、 `toml::value` の持つ値を変換して取り出す関数です。 `T` に変換先に指定したい型を指定します。 ```cpp const toml::value v = /*...*/; std::cout << toml::get(v) << std::endl; ``` 後述する `toml::find` も、型変換の部分は同一の機能を持ちます。 格納されている型のそれぞれについて、 変換ができない型が指定された場合、 `toml::type_error` が送出されます。 ### 単純な変換 #### boolean_type `boolean_type` から変換が可能なのは、 `bool` のみです。 #### integer_type `bool` 以外で `std::is_integral` が `true` になる型は、 `integer_type` から変換できます。 ```cpp toml::value v(42); const auto u32 = toml::get(v); const auto i16 = toml::get(v); ``` #### floating_type `std::is_floating_point` が `true` になる型は、`floating_type` から変換できます。 ```cpp toml::value v(3.14); const auto f64 = toml::get(v); const auto f32 = toml::get(v); ``` #### string_type `string_type` からは `std::string` へ変換できます。 また、C++17以降では、`std::string_view` へも変換できます。 ```cpp toml::value v("foo"); const auto s = toml::get(v); // C++17以降 const auto sv = toml::get(v); ``` #### datetime variants [`local_date`]({{}}), [`local_datetime`]({{}}), [`offset_datetime`]({{}}) は ある日付と時刻を指しているため、 `std::chrono::system_clock::time_point` への変換が可能です。 ただし、[`local_time`]({{}}) は 日付の情報がないため、0時0分からの経過時刻として `std::chrono::duration` への 変換をサポートします。 また、 `local_date` と `local_datetime` は実行中のマシンのタイムゾーンを取得して変換を行います。 ```toml date = 2024-01-23 time = 12:30:00 l_dt = 2024-01-23T12:30:00 o_dt = 2024-01-23T12:30:00+09:00 ``` ```cpp const auto input = toml::parse("input.toml"); const auto date = toml::get(input.at("date")); const auto l_dt = toml::get(input.at("l_dt")); const auto o_dt = toml::get(input.at("o_dt")); const auto time = toml::get(input.at("time")); // 12 * 60 + 30 min ``` ### 参照を取得できる条件 `toml::get` は、 `T` が `toml::value` が格納する型そのものだった場合、参照を返すことができます。 逆に、変換が必要な場合( `std::int64_t` で格納されている整数を `std::uint32_t` で取り出そうとした場合)は、 変換後の型への参照を返すことは不可能です。 変換が必要ない型の場合、返された参照を経由して値を書き換えることも可能です。 ```cpp toml::value v(42); toml::get(v) = 6 * 9; assert(v.as_integer() == 54); ``` ### 配列をSTLコンテナに 配列の要素型が全て同じ場合、要素型が `T` に変換可能であれば、 `std::vector` に変換可能です。 ```toml a = [1, 2, 3, 4, 5] ``` ```cpp const auto a = toml::get>(input.at("a")); ``` 他のSTLコンテナにも変換可能です。 ```cpp const auto a1 = toml::get>(input.at("a")); const auto a2 = toml::get>(input.at("a")); const auto a3 = toml::get>(input.at("a")); ``` `std::array` に変換する場合、要素数が一致している必要があります。 もし要素数が一致しなかった場合、 `std::out_of_range` が送出されます。 STL以外のコンテナであっても、デフォルトコンストラクタと `push_back` を持っている場合、 `toml::get` で変換が可能です。 ```cpp const auto a = toml::get>(input.at("a")); ``` ### 配列を `std::pair`, `std::tuple` に 配列の要素型が異なる場合、 `std::pair` や `std::tuple` に変換が可能です。 ```toml a = [true, 3.14] b = [42, 2.718, "foo"] ``` ```cpp const auto a = toml::get>(input.at("a")); const auto b = toml::get>(input.at("b")); ``` `std::array` の場合と同様に、配列の長さは `std::pair`, `std::tuple` の要素数と一致している必要があります。 もし要素数が一致しなかった場合、 `std::out_of_range` が送出されます。 また、各要素は対応する要素に変換できる必要があります。 変換できない場合、 `toml::type_error` が送出されます。 ### ネストされた配列の変換 ネストされた配列は、ネストされたコンテナに変換可能です。 ```toml a = [ [1, 2, 3], [4, 5, 6] ] ``` ```cpp const auto a = toml::get>>(input.at("a")); ``` 型が異なる場合には、 `std::pair/tuple` が便利です。 ```toml a = [ [1, 2, 3], ["foo", "bar"] ] ``` ```cpp const auto a = toml::get< std::pair, std::vector> >(input.at("a")); ``` ### テーブルを `std::map` に変換 テーブルに含まれる値の型が全て同じであれば、 `std::map` や `std::unordered_map` に変換が可能です。 ```toml t = {a = 1, b = 2} ``` ```cpp const auto t = toml::get>(input.at("t")); ``` STL以外のコンテナであっても、デフォルトコンストラクタと `emplace(key, mapped)` を持っている場合、 `toml::get` で変換が可能です。 ```cpp const auto t = toml::get>(input.at("t")); ``` 要素型の変換に失敗した場合は `toml::type_error` が送出されます。 ## `toml::get_or`を使って失敗時の値を指定する `toml::get` は変換に失敗した際に `toml::type_error` 例外を送出します。 `toml::get_or` を使用することで、変換に失敗した際に例外ではなくデフォルト値を返せるようになります。 `toml::get` とは異なり、 `get_or` は引数から変換先の型を推論するため、 `` の指定は不要です。 ```cpp const auto a = toml::get_or(input.at("a"), 42); ``` 変換可能な型は `toml::get` と同様です。 `toml::value::xxx_type`を指定した場合は、参照を取り出すことも可能ですが、その場合は引数も参照である必要があります。 ```cpp toml::value::integer_type a_default = 42; auto a& = toml::get_or(input.at("a"), a_default); ``` ## `toml::find`を使って検索と変換を同時に行う `toml::find` は、テーブルを持つ `toml::value` から値を検索し、同時に `toml::get` と同じ型変換を行って取り出す関数です。 ```cpp const auto a = toml::find(input, "a"); // const auto a = toml::get(input.at("a")); と同じ ``` `toml::find` は配列にも使用できます。 ```cpp const auto a = input.at("a"); const auto a2 = toml::find(a, 2); // const auto a2 = toml::get(input.at("a").at(2)); と同じ ``` 型変換の際にエラーが起きた場合、 `toml::get` と同じ `toml::type_error` を 送出します。 キーが見つからなかった場合またはインデックスが存在しない場合は、 `std::out_of_range` を送出します。 型を指定しなかった場合、型変換を行わず `toml::value` を取り出します。 ```cpp const auto a = toml::find(input, "a"); // const auto a = input.at("a"); と同じ ``` `toml::find` は再帰的に値にアクセスることもできます。 ```toml a = {b = {c = 42}} ``` ```cpp const auto a_b_c = toml::find(input, "a", "b", "c"); // const auto a = toml::get(input.at("a").at("b").at("c")); と同じ ``` このとき、キーとインデックスを混ぜることができます。 ```toml a = [ {b = 1}, {b = 2}, {b = 3} ] ``` ```cpp const auto a_2_b = toml::find(input, "a", 2, "b"); // const auto a = toml::get(input.at("a").at(2).at("c")); と同じ ``` {{}} TOMLはquoted keyという機能を持っています。 これは、 `""` や `''` を使うことで通常許可されない文字をキーに使えるというもので、 この中では `.` はテーブルを導入**しません**。 ```toml "127.0.0.1" = "value" site."google.com" = true ``` このTOMLファイルは以下のようにして読みます。 ```cpp const auto input = toml::parse("input.toml"); assert(input.at("127.0.0.1").as_string() == "value"); assert(input.at("site").at("google.com").as_boolean()); ``` このような場合にも違和感なく対応するため、toml11ではキーに `.` が含まれていても 自動で分割はしません。 テーブルの階層構造を陽に指定することが、適切な入力ファイルの構造化に資すると考えているからです。 参考: [toml.io キー](https://toml.io/ja/v1.0.0#%E3%82%AD%E3%83%BC) {{}} ## `toml::find_or`を使って失敗時の値を指定する `toml::find_or` は、 `toml::get_or` と同様に、失敗時のデフォルト値を渡します。 ```cpp const auto a = toml::find_or(input, "a", 42); ``` 型変換の失敗だけでなく、キーが見つからなかった場合もデフォルト値を返します。 ## ユーザー定義型との変換を定義する `toml::get` や `toml::find` では、以下のどれかの方法を使うことで ユーザー定義型を使用することができます。 ### `toml::from` の定義 toml11には `toml::from` という型があり、以下のように特殊化することでユーザー定義型からの 変換をサポートできます。 ```cpp namespace extlib { struct foo { int a; std::string b; }; } // extlib namespace toml { template<> struct from { static extlib::foo from_toml(const toml::value& v) { return extlib::foo{ toml::find(v, "a"), toml::find(v, "b") }; } }; } // toml ``` 後述する型設定を変更した `toml::value` もサポートする場合、以下のようにしてください。 ```cpp namespace extlib { struct foo { int a; std::string b; }; } // extlib namespace toml { template<> struct from { template static extlib::foo from_toml(const toml::basic_value& v) { return extlib::foo{ toml::find(v, "a"), toml::find(v, "b") }; } }; } // toml ``` この定義は、 `TOML11_DEFINE_CONVERSION_NON_INTRUSIVE` によって自動的に定義できます。 ```cpp namespace extlib { struct foo { int a; std::string b; }; } // extlib TOML11_DEFINE_CONVERSION_NON_INTRUSIVE(extlib::foo, a, b) ``` あるいは、リフレクションライブラリを使用することもできます。 `example` の `boost-ext/reflect` を使用したサンプルも参照してください。 ### `from_toml` メンバ関数の定義 `from_toml` メンバ関数を定義することによっても変換を定義することができます。 これを使用する場合、デフォルトコンストラクタが必要です。 ```cpp struct bar { int a; std::string b; void from_toml(const toml::value& v) { this->a = toml::find(v, "a"); this->b = toml::find(v, "b"); return ; } }; ``` 両方が定義されていた場合、 `toml::from` が優先されます。 ### `toml::value` を受け取るコンストラクタ `toml::value` を受け取るコンストラクタがあれば、 `toml::get` による変換ができます。 ```cpp struct baz { explicit baz(const toml::value& v) : a(toml::find(v, "a")), b(toml::find(v, "b")) {} int a; std::string b; }; ``` 両方が定義されていた場合、`toml::from` と `from_toml` が優先されます。 ## `toml::visit`で関数を適用する `toml::value` が格納する型すべてに適用できる関数オブジェクトがあれば、 `toml::visit` によって型変換を経ずに直接その関数を呼ぶことができます。 ```cpp struct type_name_of { std::string operator()(const toml::value::boolean_type &) const {return "boolean";} std::string operator()(const toml::value::integer_type &) const {return "integer";} std::string operator()(const toml::value::floating_type &) const {return "floating";} std::string operator()(const toml::value::string_type &) const {return "string";} std::string operator()(const toml::value::local_time_type &) const {return "local_time";} std::string operator()(const toml::value::local_date_type &) const {return "local_date";} std::string operator()(const toml::value::local_datetime_type &) const {return "local_datetime";} std::string operator()(const toml::value::offset_datetime_type&) const {return "offset_datetime";} std::string operator()(const toml::value::array_type &) const {return "array";} std::string operator()(const toml::value::table_type &) const {return "table";} }; toml::value v(3.14); std::cout << toml::visit(type_name_of{}, v) << std::endl; // floating ``` ## `toml::value` を構築する `toml::value` はパーサの内部だけでなく、ユーザーコードで構築することも可能です。 `toml::value` が格納する型と同じか変換可能な型を渡しての構築が可能です。 ```cpp toml::value v1(true); toml::value v2(42); toml::value v3(3.14); ``` 配列の場合、 `toml::array` を使うか、 ```cpp toml::value v(toml::array{1, 2, 3}); ``` 配列の場合、 `std::vector` などのコンテナを直接渡すことが可能です。 ```cpp const std::vector a{1,2,3}; toml::value v(a); ``` このコンテナには、 `toml::get` で変換可能なコンテナが使用できます。 テーブルの場合も同様に、 `toml::table` を使うか、 ```cpp toml::value v(toml::table{{"foo", 1}, {"bar", 2}, {"baz", 3}}); ``` `std::map` などのコンテナを直接渡します。 ```cpp const std::map t{ {"foo", 1}, {"bar", 2}, {"baz", 3} } toml::value v(t); ``` コンストラクタには、 `format_info` と コメントを渡すことができます。 コメントの型は `std::vector` です。 各要素が一行分に相当します。 ```cpp toml::integer_format_info fmt; fmt.fmt = toml::integer_format::hex; fmt.spacer = 4; toml::value v1(0xDEADBEEF, fmt); toml::value v2(0xC0FFEE, fmt, {"hex value!"}); ``` ## `toml::value` に変換する ユーザー定義型から `toml::value` を構築する際に、 `toml::into` や `into_toml` を 定義することで、その挙動をカスタマイズできます。 特に、別のライブラリの型などを変換する際に `toml::into` が便利です。 ### `toml::into`を定義する `toml::into` を特殊化することで `toml::value` への変換が可能になります。 `toml::value` への変換が用意されていない外部ライブラリの型などに対して有効です。 `toml::value` が変換時に `type_config` を渡すため、`basic_value` の `template` 引数を受け取る必要があります。 ```cpp namespace extlib { struct foo { int a; std::string b; }; } // extlib template<> struct into { template static toml::basic_value into_toml(const extlib::foo& f) { return toml::basic_value(typename toml::basic_value::table_type{{"a", f.a}, {"b", f.b}}); } }; ``` ### `into_toml` メンバ関数を定義する `from_toml` と同様、メンバ関数によっても変換を定義することができます。 `toml::into` が定義されていた場合、そちらが優先されます。 ```cpp struct bar { int a; std::string b; toml::value into_toml() const { return toml::value(toml::table{{"a", this->a}, {"b", this->b}}); } }; ``` toml11-4.1.0/docs/content.ja/docs/installation/000077500000000000000000000000001464712047700212715ustar00rootroot00000000000000toml11-4.1.0/docs/content.ja/docs/installation/_index.md000066400000000000000000000106551464712047700230700ustar00rootroot00000000000000+++ title = "installation" type = "docs" weight = 1 +++ # installation ## `single_include`を使用する `single_include/toml.hpp`は、`toml11`が持つ全ての機能を単一のファイルにまとめたシングルファイル・ヘッダオンリーライブラリです。 これを`INCLUDE_PATH`が通っている箇所にコピーして`#include `とするのが最も単純な使用方法です。 MITライセンスの許諾表示はコメントと`toml:license_notice()`関数の両方に含まれます。 ソースコードを公開せずに再頒布する場合は、toml11のライセンスファイルをコピーして同梱するか、この関数を呼び出せるようにしておいてください。 ## toml11をクローンし、`cmake`を使って使用する `toml11`を`git submodule`などによって自身のレポジトリ下に配置した場合、`cmake`を使用している場合は`add_subdirectory(toml11)`のようにすることで使用可能になります。 ```cmake add_subdirectory(toml11) add_executable(main main.cpp) target_link_libraries(main PUBLIC toml11::toml11) ``` `toml11`は自身がルートプロジェクトのときのみ、テストとインストールを行います。 ## `cmake`を使用してインストールする `toml11`をクローンしたのち、`cmake`を使ってインストールすることができます。 ```console $ cmake -B ./build/ -DTOML11_BUILD_TESTS=ON $ cmake --install ./build/ --prefix=/opt/toml11 ``` インストールの前にテストプログラムを実行する際は、最初に`-DTOML11_BUILD_TESTS=ON`を設定してください。 インストールが完了すれば、以下のようにして使用できます。 ```cmake find_package(toml11) add_executable(main main.cpp) target_link_libraries(main PRIVATE toml11::toml11) ``` ## `cmake`を使用してコンパイルし、静的ライブラリを作成する `cmake`の実行時に`-DTOML11_PRECOMPILE=ON`を定義することで、`toml11`の関数のうちコンパイルできるものを先にコンパイルして、全体のコンパイル時間を短縮することができます。 ```console $ cmake -B ./build/ -DTOML11_PRECOMPILE=ON ``` ライブラリをリンクする場合は、CMakeで ```cmake target_link_libraries(your_target PUBLIC toml11::toml11) ``` とするか、ヘッダ内の関数の`inline`化を避けるためにコンパイラに`-DTOML11_COMPILE_SOURCES`を渡してください。 ただし、toml11は複数のC++バージョンに対応するため、`__cplusplus`の値などによって型を切り替えることがあります。 そのため、ビルドした際のバージョンと使用時のバージョンが異なる場合、リンクに失敗する可能性があります。 問題が生じた場合は`CMAKE_CXX_STANDARD`によって必要なバージョンを設定してコンパイルしてください。 それが難しい場合は、通常通りヘッダオンリーライブラリとして使用してください。 `find_package(toml11)`によって`TOML11_INCLUDE_DIR`が定義されます。 コンパイル済みライブラリとしてインストールした場合でも、 `TOML11_INCLUDE_DIR` を `include_directories` に追加した上で `target_link_libraries` を 使用しないようにすれば、ヘッダオンリーライブラリとして使用可能です。 ```cmake find_package(toml11) add_executable(main main.cpp) # インクルードのみ可能にし、リンクを行わない target_include_directories(main PRIVATE ${TOML11_INCLUDE_DIR}) ``` ## examplesをコンパイルする `-DTOML11_BUILD_EXAMPLES=ON`とすることで、`examples/`をコンパイルできます。 ```console $ cmake -B ./build/ -DTOML11_BUILD_EXAMPLES=ON $ cmake --build ./build/ ``` `examples`の実行バイナリは`examples/`に生成されます。 ## テストを実行する テストをビルドするためには、`-DTOML11_BUILD_TESTS=ON`とします。 ```console $ git submodule update --init --recursive $ cmake -B ./build/ -DTOML11_BUILD_TESTS=ON $ cmake --build ./build/ $ ctest --test_dir ./build/ ``` toml-lang/toml-testsを実行するには、`-DTOML11_BUILD_TOML_TESTS=ON`とします。 すると、`tests/`に`toml11_decoder`と`toml11_encoder`がビルドされます。 ```console $ git submodule update --init --recursive $ cmake -B ./build/ -DTOML11_BUILD_TOML_TESTS=ON $ cmake --build ./build/ $ ctest --test_dir ./build/ ``` toml11-4.1.0/docs/content.ja/docs/reference/000077500000000000000000000000001464712047700205265ustar00rootroot00000000000000toml11-4.1.0/docs/content.ja/docs/reference/_index.md000066400000000000000000000071661464712047700223300ustar00rootroot00000000000000+++ title = "reference" type = "docs" weight = 3 bookCollapseSection = true +++ # Reference 以下では、toml11が公開するクラスと関数の効果を説明します。 ## ディレクトリ構造 `toml.hpp` と `toml_fwd.hpp` は `${TOML11_INCLUDE_DIR}` にあります。 他のファイルは、`${TOML11_INCLUDE_DIR}/toml11` にあります。 もし各機能のファイルを個別に `#include` したい場合は、 `#include ` としてください。 全てを一度に `#include` する場合は、 `#include ` としてください。 ## [color.hpp](color) エラーメッセージの色付けに関する関数を定義します。 ## [comments.hpp](comments) コメントを持つ`preserve_comment`型と`discard_comment`型を定義します。 ## [conversion.hpp](conversion) `toml::value`とユーザー定義クラスを自動的に変換するマクロを定義します。 ## [datetime.hpp](datetime) 日時情報を持つクラスを定義します。 ## [error_info.hpp](error_info) エラー情報を持つクラスを定義します。 ## [exception.hpp](exception) toml11で使用される例外の基底クラス、`toml::exception`を定義します。 ## [find.hpp](find) 値を探し変換する`toml::find`関数を定義します。 ## [format.hpp](format) 値のフォーマット情報を持つクラスを定義します。 ## [from.hpp](from) ユーザー定義型を変換するための`from`型の前方宣言です。 ## [get.hpp](get) `toml::value`の値を取り出し変換する`toml::get`関数を定義します。 ## [into.hpp](into) ユーザー定義型を変換するための`into`型の前方宣言です。 ## [literal.hpp](literal) `operator"" _toml`リテラルを定義します。 ## [ordered_map.hpp](ordered_map) `toml::ordered_map`を定義します。 ## [parser.hpp](parser) ファイルまたは文字列をパースする関数を定義します。 ## [result.hpp](result) 他の関数の返り値として使われる、成功値または失敗値を持つ`result`型を定義します。 ## [serializer.hpp](serializer) シリアライズに用いる`toml::format`関数と`toml::serializer`を定義します。 ## [source_location.hpp](source_location) エラー情報に用いられる、ファイル内のある領域を指す`source_location`型を定義します。 ## [spec.hpp](spec) TOML言語のバージョン情報と機能フラグを制御する、`toml::semantic_version`型と`toml::spec`型を定義します。 ## [toml.hpp](toml) `toml.hpp`は、他の全てのヘッダを `include` します。 toml11の全機能が使用可能になります。 ## [toml_fwd.hpp](toml_fwd) `toml_fwd.hpp`は、toml11で定義される構造体の前方宣言と、マクロ定義を持ちます。 ## [types.hpp](types) `toml::value`の持つ型を制御するための`toml::type_config`型を定義します。 ## [value.hpp](value) `toml::value`型を定義します。 ## [value_t.hpp](value_t) 列挙型`toml::value_t`を定義します。 ## [version.hpp](version) toml11のバージョン情報を定義します。 ## [visit.hpp](visit) `toml::value`の持つ値に関数を適用する`toml::visit`関数を定義します。 ## 備考 ここで明記されない関数(主に`namespace toml::detail`や`namespace toml::cxx`以下に定義されるもの)は、 ソースコードを見ることで利用可能ではあるものの、そのインターフェースは今後のいかなるバージョンアップでも(パッチバージョンアップを含む)維持される保証はありません。 toml11-4.1.0/docs/content.ja/docs/reference/color.md000066400000000000000000000054401464712047700221710ustar00rootroot00000000000000+++ title = "color.hpp" type = "docs" +++ # color.hpp `color.hpp`では、エラーメッセージの色付けに関する関数が定義されます。 色はANSIエスケープシーケンスによって指定されます。 ANSIエスケープシーケンスをサポートしていないターミナルやその他の出力先では、読みにくくなる可能性があります。 ## マクロ ```cpp TOML11_COLORIZE_ERROR_MESSAGE ``` コンパイル時にこのマクロが定義されていた場合(`-DTOML11_COLORIZE_ERROR_MESASGE`)、 デフォルトでエラーメッセージに色が付きます。 定義されていなかった場合、デフォルトでは色は付きません。以下の `toml::color::enable()` を 使用して指定する必要があります。 ## 関数 ### `enable()` ```cpp namespace toml { namespace color { void enable(); } // color } // toml ``` ANSIエスケープシーケンスによる色付けを行うよう設定します。 #### 例 ```cpp #include int main() { toml::color::enable(); // この後の全てのエラーがカラーになります。 const auto input = toml::parse("input.toml"); return 0; } ``` ### `disable()` ```cpp namespace toml { namespace color { void disable(); } // color } // toml ``` ANSIエスケープシーケンスによる色付けを行わないよう設定します。 #### 例 ```cpp #include int main() { toml::color::enable(); // この後の全てのエラーがカラーになります。 const auto input = toml::parse("input.toml"); return 0; } ``` ### `should_color()` ```cpp namespace toml { namespace color { bool should_color(); } // color } // toml ``` 色付けを行う設定になっている場合`true`が、そうでない場合`false`が返されます。 #### 例 ```cpp #include #include #include int main() { std::cout << "colorized? : " << std::boolalpha << toml::color::should_color() << std::endl; return 0; } ``` ## マニピュレータ ```cpp namespace toml { namespace color { std::ostream& reset (std::ostream&); std::ostream& bold (std::ostream&); std::ostream& grey (std::ostream&); std::ostream& gray (std::ostream&); std::ostream& red (std::ostream&); std::ostream& green (std::ostream&); std::ostream& yellow (std::ostream&); std::ostream& blue (std::ostream&); std::ostream& magenta(std::ostream&); std::ostream& cyan (std::ostream&); std::ostream& white (std::ostream&); } // color } // toml ``` ANSIエスケープシーケンスによって、`fg`を色付けします。 #### 例 ```cpp #include #include int main() { std::cout << toml::color::red << "red!" << std::endl; return 0; } ``` # 関連項目 - [error_info.hpp]({{}}) toml11-4.1.0/docs/content.ja/docs/reference/comments.md000066400000000000000000000444331464712047700227050ustar00rootroot00000000000000+++ title = "comments.hpp" type = "docs" +++ # comments.hpp `color.hpp`では、コメントを保持するクラスが提供されます。 # `toml::preserve_comments` `preserve_comments`は、コメントを保持するコンテナです。 `std::vector`が持つメンバ関数を全て持っています。 コメントは`std::string`として保持されます。 先頭が`#`でない場合、出力時に`#`が補われます。コンテナに要素として追加する段階では補われません。 スペースは補われないため、`#`の直後にスペースを入れたい場合、コメントをスペースから始めるか、`#`を含めたコメントを渡す必要があります。 ```cpp namespace toml { class preserve_comments; bool operator==(const preserve_comments&, const preserve_comments&); bool operator!=(const preserve_comments&, const preserve_comments&); bool operator< (const preserve_comments&, const preserve_comments&); bool operator<=(const preserve_comments&, const preserve_comments&); bool operator> (const preserve_comments&, const preserve_comments&); bool operator>=(const preserve_comments&, const preserve_comments&); void swap(preserve_comments&, preserve_comments&); void swap(preserve_comments&, std::vector&); void swap(std::vector&, preserve_comments&); std::ostream& operator<<(std::ostream&, const preserve_comments&); } //toml ``` ## メンバ型 ```cpp using container_type = std::vector; using size_type = container_type::size_type; using difference_type = container_type::difference_type; using value_type = container_type::value_type; using reference = container_type::reference; using const_reference = container_type::const_reference; using pointer = container_type::pointer; using const_pointer = container_type::const_pointer; using iterator = container_type::iterator; using const_iterator = container_type::const_iterator; using reverse_iterator = container_type::reverse_iterator; using const_reverse_iterator = container_type::const_reverse_iterator; ``` ## メンバ関数 ### デフォルトコンストラクタ ```cpp preserve_comments() = default; ``` 空の`preserve_comments`を構築します。 ### コピー・ムーブコンストラクタ ```cpp preserve_comments(preserve_comments const&) = default; preserve_comments(preserve_comments &&) = default; ``` `preserve_comments`をコピー・ムーブ構築します。 ### コンストラクタ(`std::vector`) ```cpp explicit preserve_comments(const std::vector& c); explicit preserve_comments(std::vector&& c); ``` `std::vector`の内容を持つ`preserve_comments`を構築します。 ### コンストラクタ(`discard_comments`) ```cpp explicit preserve_comments(const discard_comments&); ``` 空の`preserve_comments`を構築します。 ### コンストラクタ(`Iterator`) ```cpp template preserve_comments(InputIterator first, InputIterator last); ``` `std::string`を指す`InputIterator`が表す範囲から`preserve_comments`を構築します。 ### コンストラクタ(`std::initializer_list`) ```cpp preserve_comments(std::initializer_list x); ``` `std::initializer_list`が表す範囲から`preserve_comments`を構築します。 ### コンストラクタ(サイズ指定) ```cpp explicit preserve_comments(size_type n); preserve_comments(size_type n, const std::string& x); ``` `n`個のコメントを持つ`preserve_comments`を構築します。 `std::string`を渡した場合、そのコメントを`n`個に複製します。 ### デストラクタ ```cpp ~preserve_comments() = default; ``` `preserve_comments`を破棄します。 ### `operator=(preserve_comments)` ```cpp preserve_comments& operator=(preserve_comments const&) = default; preserve_comments& operator=(preserve_comments &&) = default; ``` `preserve_comments`をコピー・ムーブ代入します。 ### `operator=(std::vector)` ```cpp preserve_comments& operator=(const std::vector& c); preserve_comments& operator=(std::vector&& c); ``` `std::vector`をコピー・ムーブ代入します。 ### `assign` ```cpp template void assign(InputIterator first, InputIterator last); void assign(std::initializer_list ini); void assign(size_type n, const std::string& val); ``` `std::vector::assign`と同等の効果を持ちます。 ### `insert` ```cpp iterator insert(const_iterator p, const std::string& x); iterator insert(const_iterator p, std::string&& x); iterator insert(const_iterator p, size_type n, const std::string& x); template iterator insert(const_iterator p, InputIterator first, InputIterator last); iterator insert(const_iterator p, std::initializer_list ini); ``` `std::vector::insert`と同等の効果を持ちます。 ### `emplace` ```cpp template iterator emplace(const_iterator p, Ts&& ... args); ``` `std::vector::insert`と同等の効果を持ちます。 ### `erase` ```cpp iterator erase(const_iterator pos); iterator erase(const_iterator first, const_iterator last); ``` `std::vector::insert`と同等の効果を持ちます。 ### `swap` ```cpp void swap(preserve_comments& other); ``` 他の`preserve_comments`と内容を交換します。 ### `push_back` ```cpp void push_back(const std::string& v); void push_back(std::string&& v); ``` `std::vector::push_back`と同等の効果を持ちます。 ### `pop_back` ```cpp void pop_back(); ``` `std::vector::pop_back`と同等の効果を持ちます。 ### `emplace_back` ```cpp template void emplace_back(Ts&& ... args); ``` `std::vector::emplace_back`と同等の効果を持ちます。 ### `clear` ```cpp void clear(); ``` `std::vector::clear`と同等の効果を持ちます。 ### `size` ```cpp size_type size() const noexcept; ``` `std::vector::size`と同等の効果を持ちます。 ### `max_size` ```cpp size_type max_size() const noexcept; ``` `std::vector::max_size`と同等の効果を持ちます。 ### `capacity` ```cpp size_type capacity() const noexcept; ``` `std::vector::capacity`と同等の効果を持ちます。 ### `empty` ```cpp bool empty() const noexcept; ``` `std::vector::empty`と同等の効果を持ちます。 ### `reserve` ```cpp void reserve(size_type n); ``` `std::vector::reserve`と同等の効果を持ちます。 ### `resize` ```cpp void resize(size_type n); void resize(size_type n, const std::string& c); ``` `std::vector::resize`と同等の効果を持ちます。 ### `shrink_to_fit` ```cpp void shrink_to_fit(); ``` `std::vector::shrink_to_fit`と同等の効果を持ちます。 ### `operator[]` ```cpp reference operator[](const size_type n) noexcept; const_reference operator[](const size_type n) const noexcept; ``` `std::vector::operator[]`と同等の効果を持ちます。 ### `at` ```cpp reference at(const size_type n) ; const_reference at(const size_type n) const; ``` `std::vector::at`と同等の効果を持ちます。 ### `front` ```cpp reference front() noexcept; const_reference front() const noexcept; ``` `std::vector::front`と同等の効果を持ちます。 ### `back` ```cpp reference back() noexcept; const_reference back() const noexcept; ``` `std::vector::back`と同等の効果を持ちます。 ### `data` ```cpp pointer data() noexcept; const_pointer data() const noexcept; ``` `std::vector::data`と同等の効果を持ちます。 ### `begin/end` ```cpp iterator begin() noexcept; iterator end() noexcept; const_iterator begin() const noexcept; const_iterator end() const noexcept; const_iterator cbegin() const noexcept; const_iterator cend() const noexcept; ``` `std::vector::begin/end`と同等の効果を持ちます。 ### `rbegin/rend` ```cpp reverse_iterator rbegin() noexcept; reverse_iterator rend() noexcept; const_reverse_iterator rbegin() const noexcept; const_reverse_iterator rend() const noexcept; const_reverse_iterator crbegin() const noexcept; const_reverse_iterator crend() const noexcept; ``` `std::vector::rbegin/rend`と同等の効果を持ちます。 ## 非メンバ関数 ### 比較演算子 ```cpp bool operator==(const preserve_comments&, const preserve_comments&); bool operator!=(const preserve_comments&, const preserve_comments&); bool operator< (const preserve_comments&, const preserve_comments&); bool operator<=(const preserve_comments&, const preserve_comments&); bool operator> (const preserve_comments&, const preserve_comments&); bool operator>=(const preserve_comments&, const preserve_comments&); ``` `std::vector`と同様に比較を行います。 ### `swap` ```cpp void swap(preserve_comments&, preserve_comments&); void swap(preserve_comments&, std::vector&); void swap(std::vector&, preserve_comments&); ``` ### ストリーム演算子 ```cpp std::ostream& operator<<(std::ostream&, const preserve_comments&); ``` コメントとして出力します。 先頭が`#`でない場合、`#`が補われます。 # `toml::discard_comments` `discard_comments`は、コメントを破棄するコンテナです。 `std::vector`が持つメンバ関数を全て持っていますが、内容を変更する関数を呼び出しても何も効果はなく、常に空になります。 ```cpp namespace toml { class discard_comments; bool operator==(const discard_comments&, const discard_comments&); bool operator!=(const discard_comments&, const discard_comments&); bool operator< (const discard_comments&, const discard_comments&); bool operator<=(const discard_comments&, const discard_comments&); bool operator> (const discard_comments&, const discard_comments&); bool operator>=(const discard_comments&, const discard_comments&); void swap(discard_comments&, discard_comments&); std::ostream& operator<<(std::ostream&, const discard_comments&); } //toml ``` ## メンバ型 ```cpp // container_type is not defined using size_type = std::size_t; using difference_type = std::ptrdiff_t; using value_type = std::string; using reference = std::string&; using const_reference = std::string const&; using pointer = std::string*; using const_pointer = std::string const*; using iterator = /* internal type: empty-iterator */ using const_iterator = /* internal type: empty-iterator */ using reverse_iterator = /* internal type: empty-iterator */ using const_reverse_iterator = /* internal type: empty-iterator */ ``` ### デフォルトコンストラクタ ```cpp discard_comments() = default; ``` 空の`discard_comments`を構築します。 ### コピー・ムーブコンストラクタ ```cpp discard_comments(discard_comments const&) = default; discard_comments(discard_comments &&) = default; ``` `discard_comments`をコピー・ムーブ構築します。 ### コンストラクタ(`std::vector`) ```cpp explicit discard_comments(const std::vector& c); explicit discard_comments(std::vector&& c); ``` 空の`discard_comments`を構築します。引数の内容は無視されます。 ### コンストラクタ(`preserve_comments`) ```cpp explicit discard_comments(const preserve_comments&); ``` 空の`discard_comments`を構築します。引数の内容は無視されます。 ### コンストラクタ(`Iterator`) ```cpp template discard_comments(InputIterator first, InputIterator last); ``` 空の`discard_comments`を構築します。引数の内容は無視されます。 ### コンストラクタ(`std::initializer_list`) ```cpp discard_comments(std::initializer_list x); ``` 空の`discard_comments`を構築します。引数の内容は無視されます。 ### コンストラクタ(サイズ指定) ```cpp explicit discard_comments(size_type n); discard_comments(size_type n, const std::string& x); ``` 空の`discard_comments`を構築します。引数の内容は無視されます。 ### デストラクタ ```cpp ~discard_comments() = default; ``` `discard_comments`を破棄します。 ### `operator=(discard_comments)` ```cpp discard_comments& operator=(discard_comments const&) = default; discard_comments& operator=(discard_comments &&) = default; ``` `discard_comments`をコピー・ムーブ代入します。 ### `operator=(std::vector)` ```cpp discard_comments& operator=(const std::vector& c); discard_comments& operator=(std::vector&& c); ``` 何もしません。引数の内容は無視されます。 ### `assign` ```cpp template void assign(InputIterator first, InputIterator last); void assign(std::initializer_list ini); void assign(size_type n, const std::string& val); ``` 何もしません。引数の内容は無視されます。 ### `insert` ```cpp iterator insert(const_iterator p, const std::string& x); iterator insert(const_iterator p, std::string&& x); iterator insert(const_iterator p, size_type n, const std::string& x); template iterator insert(const_iterator p, InputIterator first, InputIterator last); iterator insert(const_iterator p, std::initializer_list ini); ``` 何もしません。引数の内容は無視されます。 ### `emplace` ```cpp template iterator emplace(const_iterator p, Ts&& ... args); ``` 何もしません。引数の内容は無視されます。 ### `erase` ```cpp iterator erase(const_iterator pos); iterator erase(const_iterator first, const_iterator last); ``` 何もしません。引数の内容は無視されます。 ### `swap` ```cpp void swap(discard_comments& other); ``` 他の`discard_comments`と内容を交換します。 ### `push_back` ```cpp void push_back(const std::string& v); void push_back(std::string&& v); ``` 何もしません。引数の内容は無視されます。 ### `pop_back` ```cpp void pop_back(); ``` 何もしません。引数の内容は無視されます。 ### `emplace_back` ```cpp template void emplace_back(Ts&& ... args); ``` 何もしません。引数の内容は無視されます。 ### `clear` ```cpp void clear(); ``` 何もしません。引数の内容は無視されます。 ### `size` ```cpp size_type size() const noexcept; ``` 常に`0`を返します。 ### `max_size` ```cpp size_type max_size() const noexcept; ``` 常に`0`を返します。 ### `capacity` ```cpp size_type capacity() const noexcept; ``` 常に`0`を返します。 ### `empty` ```cpp bool empty() const noexcept; ``` 常に`true`を返します。 ### `reserve` ```cpp void reserve(size_type n); ``` 何もしません。引数の内容は無視されます。 ### `resize` ```cpp void resize(size_type n); void resize(size_type n, const std::string& c); ``` 何もしません。引数の内容は無視されます。 ### `shrink_to_fit` ```cpp void shrink_to_fit(); ``` 何もしません。引数の内容は無視されます。 ### `operator[]` ```cpp reference operator[](const size_type n) noexcept; const_reference operator[](const size_type n) const noexcept; ``` 未定義動作です。 ### `at` ```cpp reference at(const size_type n) ; const_reference at(const size_type n) const; ``` `std::out_of_range`を送出します。 ### `front` ```cpp reference front() noexcept; const_reference front() const noexcept; ``` 未定義動作です。 ### `back` ```cpp reference back() noexcept; const_reference back() const noexcept; ``` 未定義動作です。 ### `data` ```cpp pointer data() noexcept; const_pointer data() const noexcept; ``` 常に`nullptr`を返します。 ### `begin/end` ```cpp iterator begin() noexcept; iterator end() noexcept; const_iterator begin() const noexcept; const_iterator end() const noexcept; const_iterator cbegin() const noexcept; const_iterator cend() const noexcept; ``` 内部で定義された`empty-iterator`を返します。 `empty-iterator`はインクリメントやデクリメントしても同じ値でとどまり、全ての値が同値です。 `empty-iterator`が指す先にアクセスすると常に`std::terminate`が呼び出されます。 ### `rbegin/rend` ```cpp reverse_iterator rbegin() noexcept; reverse_iterator rend() noexcept; const_reverse_iterator rbegin() const noexcept; const_reverse_iterator rend() const noexcept; const_reverse_iterator crbegin() const noexcept; const_reverse_iterator crend() const noexcept; ``` 内部で定義された`empty-iterator`を返します。 `empty-iterator`はインクリメントやデクリメントしても同じ値でとどまり、全ての値が同値です。 `empty-iterator`が指す先にアクセスすると常に`std::terminate`が呼び出されます。 ## 非メンバ関数 ### 比較演算子 ```cpp bool operator==(const discard_comments&, const discard_comments&); bool operator!=(const discard_comments&, const discard_comments&); bool operator< (const discard_comments&, const discard_comments&); bool operator<=(const discard_comments&, const discard_comments&); bool operator> (const discard_comments&, const discard_comments&); bool operator>=(const discard_comments&, const discard_comments&); ``` `discard_comments`は全て同じ値です。`==`には常に`true`を、`!=`には常に`false`を返します。 ### `swap` ```cpp void swap(discard_comments&, discard_comments&); ``` 二つの`discard_comments`を交換します。 ### ストリーム演算子 ```cpp std::ostream& operator<<(std::ostream&, const discard_comments&); ``` 何も出力しません。 # 関連項目 - [value.hpp]({{}}) toml11-4.1.0/docs/content.ja/docs/reference/conversion.md000066400000000000000000000010271464712047700232350ustar00rootroot00000000000000+++ title = "conversion.hpp" type = "docs" +++ # conversion.hpp `toml::get`や`toml::find`でユーザー定義型をサポートするための変換関数を自動定義するマクロを提供します。 ```cpp TOML11_DEFINE_CONVERSION_NON_INTRUSIVE(NAME, ...) ``` ## 例 ```cpp namespace foo { struct Foo { std::string s; double d; int i; }; } // foo TOML11_DEFINE_CONVERSION_NON_INTRUSIVE(foo::Foo, s, d, i) ``` # 関連項目 - [from.hpp]({{}}) - [into.hpp]({{}}) toml11-4.1.0/docs/content.ja/docs/reference/datetime.md000066400000000000000000000435361464712047700226570ustar00rootroot00000000000000+++ title = "datetime.hpp" type = "docs" +++ # datetime.hpp TOMLの`datetime`で使用される、日時情報を保存するクラスを定義します。 # `enum class month_t` 月を指定する`enum class`です。 `std::tm`との関係で、`local_date`は1月を`0`としています。 混乱を避けるため、月の名前で指定できるよう`month_t`が用意されています。 ```cpp namespace toml { enum class month_t : std::uint8_t { Jan = 0, Feb = 1, Mar = 2, Apr = 3, May = 4, Jun = 5, Jul = 6, Aug = 7, Sep = 8, Oct = 9, Nov = 10, Dec = 11 }; } ``` # `local_date` 日付を保持する構造体です。 `year`は西暦を、`month`は`std::tm`との対応のため1月を`0`として、`day`は日付を保持します。 ```cpp namespace toml { struct local_date { std::int16_t year; std::uint8_t month; std::uint8_t day; local_date() = default; ~local_date() = default; local_date(local_date const&) = default; local_date(local_date&&) = default; local_date& operator=(local_date const&) = default; local_date& operator=(local_date&&) = default; local_date(int y, month_t m, int d); explicit local_date(const std::tm& t); explicit local_date(const std::chrono::system_clock::time_point& tp); explicit local_date(const std::time_t t); operator std::chrono::system_clock::time_point() const; operator std::time_t() const; }; bool operator==(const local_date&, const local_date&); bool operator!=(const local_date&, const local_date&); bool operator< (const local_date&, const local_date&); bool operator<=(const local_date&, const local_date&); bool operator> (const local_date&, const local_date&); bool operator>=(const local_date&, const local_date&); std::ostream& operator<<(std::ostream& os, const local_date& date); std::string to_string(const local_date& date); } ``` ## メンバ変数 ### `year` ```cpp std::int16_t year; ``` 西暦です。オフセットはありません。`2024`年は`2024`です。 ### `month` ```cpp std::uint8_t month; ``` 月を表します。`std::tm`との対応のため、1月は`0`, 2月は`1`と続きます。 混乱を避けるため、構築の際は`month_t`を使用します。 ### `day` ```cpp std::uint8_t day; ``` 日付を表します。1日は`1`です。 ## メンバ関数 ### コンストラクタ ```cpp local_date() = default; ``` デフォルト実装を使用します。 ### デストラクタ ```cpp ~local_date() = default; ``` デフォルト実装を使用します。 ### コピー・ムーブコンストラクタ ```cpp local_date(local_date const&) = default; local_date(local_date&&) = default; ``` デフォルト実装を使用します。 ### コピー・ムーブ代入演算子 ```cpp local_date& operator=(local_date const&) = default; local_date& operator=(local_date&&) = default; ``` デフォルト実装を使用します。 ### コンストラクタ(`int year, month_t month, int day`) ```cpp local_date(int y, month_t m, int d); ``` 指定した値から`local_date`を構築します。 境界チェックなどは行いません。 ### コンストラクタ(`std::tm`) ```cpp local_date(const std::tm&); ``` 指定した値から`local_date`を構築します。 ### コンストラクタ(`std::chrono::system_clock::time_point`) ```cpp local_date(const std::chrono::system_clock::time_point&); ``` 指定した値から`local_date`を構築します。 タイムゾーンは実行環境でのものが選択されます。 ### コンストラクタ(`std::time_t`) ```cpp local_date(const std::time_t); ``` 指定した値から`local_date`を構築します。 タイムゾーンは実行環境でのものが選択されます。 ### `operator std::chrono::system_clock::time_point` ```cpp operator std::chrono::system_clock::time_point() const; ``` `std::chrono::system_clock::time_point`に変換します。 タイムゾーンは実行環境でのものが選択されます。 時刻は0時00分とします。 ### `operator std::time_t` ```cpp operator std::time_t() const; ``` `std::time_t`に変換します。 タイムゾーンは実行環境でのものが選択されます。 時刻は0時00分とします。 ## 非メンバ関数 ### 比較演算子 ```cpp bool operator==(const local_date&, const local_date&); bool operator!=(const local_date&, const local_date&); bool operator< (const local_date&, const local_date&); bool operator<=(const local_date&, const local_date&); bool operator> (const local_date&, const local_date&); bool operator>=(const local_date&, const local_date&); ``` 日付の順序によって比較します。 ### ストリーム出力演算子 ```cpp std::ostream& operator<<(std::ostream& os, const local_date& date); ``` TOMLのデフォルトのフォーマットで出力を行います。 ### `to_string` ```cpp std::string to_string(const local_date& date); ``` TOMLのデフォルトのフォーマットで文字列化します。 # `local_time` ```cpp namespace toml { struct local_time { std::uint8_t hour; // [0, 23] std::uint8_t minute; // [0, 59] std::uint8_t second; // [0, 60] std::uint16_t millisecond; // [0, 999] std::uint16_t microsecond; // [0, 999] std::uint16_t nanosecond; // [0, 999] local_time(int h, int m, int s, int ms = 0, int us = 0, int ns = 0); explicit local_time(const std::tm& t); template explicit local_time(const std::chrono::duration& t); operator std::chrono::nanoseconds() const; local_time() = default; ~local_time() = default; local_time(local_time const&) = default; local_time(local_time&&) = default; local_time& operator=(local_time const&) = default; local_time& operator=(local_time&&) = default; }; bool operator==(const local_time& lhs, const local_time& rhs); bool operator!=(const local_time& lhs, const local_time& rhs); bool operator< (const local_time& lhs, const local_time& rhs); bool operator<=(const local_time& lhs, const local_time& rhs); bool operator> (const local_time& lhs, const local_time& rhs); bool operator>=(const local_time& lhs, const local_time& rhs); std::ostream& operator<<(std::ostream& os, const local_time& time); std::string to_string(const local_time& time); } ``` ## メンバ変数 ### `hour` ```cpp std::uint8_t hour; ``` 時間を表します。`0`から`23`の値を取ります。 ### `minute` ```cpp std::uint8_t minute; // [0, 59] ``` 分を表します。`0`から`59`の値を取ります。 ### `second` ```cpp std::uint8_t second; // [0, 60] ``` 秒を表します。`0`から`60`の値を取ります。 ### `millisecond` ```cpp std::uint16_t millisecond; // [0, 999] ``` ミリ秒を表します。`0`から`999`の値を取ります。 ### `microsecond` ```cpp std::uint16_t microsecond; // [0, 999] ``` マイクロ秒を表します。`0`から`999`の値を取ります。 ### `nanosecond` ```cpp std::uint16_t nanosecond; // [0, 999] ``` ナノ秒を表します。`0`から`999`の値を取ります。 ## メンバ関数 ### デフォルトコンストラクタ ```cpp local_time() = default; ``` 全ての値を`0`で初期化します。 ### コンストラクタ(h, m, s, ms = 0, us = 0, ns = 0) ```cpp local_time(int h, int m, int s, int ms = 0, int us = 0, int ns = 0); ``` 指定された時刻を使って構築します。 境界チェックは行われません。 ### コンストラクタ(`std::tm`) ```cpp explicit local_time(const std::tm& t); ``` `std::tm`の`tm_hour`, `tm_min`, `tm_sec`を使って構築します。 サブセコンドは全て`0`で初期化されます。 ### コンストラクタ(`std::chrono::duration`) ```cpp template explicit local_time(const std::chrono::duration& t); ``` `duration`をその日の0時からの時間として構築します。 ### `operator std::chrono::nanoseconds` ```cpp operator std::chrono::nanoseconds() const; ``` `std::chrono::nanoseconds`へ変換します。 ## 非メンバ関数 ### 比較演算子 ```cpp bool operator==(const local_time& lhs, const local_time& rhs); bool operator!=(const local_time& lhs, const local_time& rhs); bool operator< (const local_time& lhs, const local_time& rhs); bool operator<=(const local_time& lhs, const local_time& rhs); bool operator> (const local_time& lhs, const local_time& rhs); bool operator>=(const local_time& lhs, const local_time& rhs); ``` 時刻の値によって比較を行います。 ### ストリーム演算子 ```cpp std::ostream& operator<<(std::ostream& os, const local_time& time); ``` TOMLのデフォルトのフォーマットで出力を行います。 ### `to_string` ```cpp std::string to_string(const local_time& time); ``` TOMLのデフォルトのフォーマットで文字列化します。 # `time_offset` ```cpp namespace toml { struct time_offset { std::int8_t hour{0}; // [-12, 12] std::int8_t minute{0}; // [-59, 59] time_offset(int h, int m); operator std::chrono::minutes() const; time_offset() = default; ~time_offset() = default; time_offset(time_offset const&) = default; time_offset(time_offset&&) = default; time_offset& operator=(time_offset const&) = default; time_offset& operator=(time_offset&&) = default; }; bool operator==(const time_offset&, const time_offset&); bool operator!=(const time_offset&, const time_offset&); bool operator< (const time_offset&, const time_offset&); bool operator<=(const time_offset&, const time_offset&); bool operator> (const time_offset&, const time_offset&); bool operator>=(const time_offset&, const time_offset&); std::ostream& operator<<(std::ostream& os, const time_offset& offset); std::string to_string(const time_offset& offset); } ``` ## メンバ変数 ### `hour` ```cpp std::int8_t hour{0}; // [-12, 12] ``` 時間のオフセットです。-12から+12の範囲の値を取ります。 ### `minute` ```cpp std::int8_t minute{0}; // [-59, 59] ``` 分のオフセットです。-59から+59の範囲の値を取ります。 ## メンバ関数 ### コンストラクタ ```cpp time_offset(int h, int m); ``` 時間と分を取って構築します。 境界チェックは行いません。 ### `operator std::chrono::minutes` ```cpp operator std::chrono::minutes() const; ``` `std::chrono::minutes`への変換を行います。 ## 非メンバ関数 ### 比較演算子 ```cpp bool operator==(const time_offset&, const time_offset&); bool operator!=(const time_offset&, const time_offset&); bool operator< (const time_offset&, const time_offset&); bool operator<=(const time_offset&, const time_offset&); bool operator> (const time_offset&, const time_offset&); bool operator>=(const time_offset&, const time_offset&); ``` 時刻の長さで比較します。 ### ストリーム出力演算子 ```cpp std::ostream& operator<<(std::ostream& os, const time_offset&); ``` TOMLのデフォルトのフォーマットで出力を行います。 ### `to_string` ```cpp std::string to_string(const time_offset&); ``` TOMLのデフォルトのフォーマットで文字列化します。 # `local_datetime` ```cpp namespace toml { struct local_datetime { local_date date; local_time time; local_datetime(local_date d, local_time t); explicit local_datetime(const std::tm& t); explicit local_datetime(const std::chrono::system_clock::time_point& tp); explicit local_datetime(const std::time_t t); operator std::chrono::system_clock::time_point() const; operator std::time_t() const; local_datetime() = default; ~local_datetime() = default; local_datetime(local_datetime const&) = default; local_datetime(local_datetime&&) = default; local_datetime& operator=(local_datetime const&) = default; local_datetime& operator=(local_datetime&&) = default; }; bool operator==(const local_datetime&, const local_datetime&); bool operator!=(const local_datetime&, const local_datetime&); bool operator< (const local_datetime&, const local_datetime&); bool operator<=(const local_datetime&, const local_datetime&); bool operator> (const local_datetime&, const local_datetime&); bool operator>=(const local_datetime&, const local_datetime&); std::ostream& operator<<(std::ostream& os, const local_datetime& dt); std::string to_string(const local_datetime& dt); } ``` ## メンバ変数 ### `local_date date` ```cpp local_date date; ``` 日付部分のデータを保持します。 ### `local_time time` ```cpp local_time time; ``` 時刻部分のデータを保持します。 ## メンバ関数 ### デフォルトコンストラクタ `date`, `time`の双方をデフォルト構築します。 ### コンストラクタ(`local_date, local_time`) 指定された`date`と`time`で構築します。 ### コンストラクタ(`std::tm`) `std::tm`から構築します。 タイムゾーンは実行環境でのものが選択されます。 ### コンストラクタ(`std::chrono::system_clock::time_point`) `std::chrono::system_clock::time_point`から構築します。 タイムゾーンは実行環境でのものが選択されます。 ### コンストラクタ(`std::time_t`) `std::time_t`から構築します。 タイムゾーンは実行環境でのものが選択されます。 ### `operator std::chrono::system_clock::time_point` `std::chrono::system_clock::time_point`へ変換します。 ### `operator std::time_t` `std::time_t`へ変換します。 ## 非メンバ関数 ### 比較演算子 ```cpp bool operator==(const local_datetime&, const local_datetime&); bool operator!=(const local_datetime&, const local_datetime&); bool operator< (const local_datetime&, const local_datetime&); bool operator<=(const local_datetime&, const local_datetime&); bool operator> (const local_datetime&, const local_datetime&); bool operator>=(const local_datetime&, const local_datetime&); ``` 日付順で比較します。 ### ストリーム出力演算子 ```cpp std::ostream& operator<<(std::ostream& os, const local_datetime&); ``` TOMLのデフォルトのフォーマットで出力を行います。 ### `to_string` ```cpp std::string to_string(const local_datetime&); ``` TOMLのデフォルトのフォーマットで文字列化します。 # `offset_datetime` ```cpp namespace toml { struct offset_datetime { local_date date; local_time time; time_offset offset; offset_datetime(local_date d, local_time t, time_offset o); offset_datetime(const local_datetime& dt, time_offset o); explicit offset_datetime(const local_datetime& ld); explicit offset_datetime(const std::chrono::system_clock::time_point& tp); explicit offset_datetime(const std::time_t& t); explicit offset_datetime(const std::tm& t); operator std::chrono::system_clock::time_point() const; operator std::time_t() const; offset_datetime() = default; ~offset_datetime() = default; offset_datetime(offset_datetime const&) = default; offset_datetime(offset_datetime&&) = default; offset_datetime& operator=(offset_datetime const&) = default; offset_datetime& operator=(offset_datetime&&) = default; }; bool operator==(const offset_datetime&, const offset_datetime&); bool operator!=(const offset_datetime&, const offset_datetime&); bool operator< (const offset_datetime&, const offset_datetime&); bool operator<=(const offset_datetime&, const offset_datetime&); bool operator> (const offset_datetime&, const offset_datetime&); bool operator>=(const offset_datetime&, const offset_datetime&); std::ostream& operator<<(std::ostream& os, const offset_datetime& dt); std::string to_string(const offset_datetime& dt); } ``` ## メンバ変数 ### `date` ```cpp local_date date; ``` 日付部分のデータを保持します。 ### `time` ```cpp local_time time; ``` 時刻部分のデータを保持します。 ### `offset` ```cpp time_offset offset; ``` オフセット部分のデータを保持します。 ## メンバ関数 ### デフォルトコンストラクタ `date`, `time`, `offset`を全てデフォルト構築します。 ### コンストラクタ(`local_date, local_time, time_offset`) 指定された`date`, `time`, `offset`で構築します。 ### コンストラクタ(`local_datetime, time_offset`) `local_datetime`と`offset`から構築します。 ### コンストラクタ(`std::tm`) `std::tm`から構築します。 タイムゾーンはUTC(00:00)になります。 ### コンストラクタ(`std::chrono::system_clock::time_point`) `std::chrono::system_clock::time_point`から構築します。 タイムゾーンはUTC(00:00)になります。 ### コンストラクタ(`std::time_t`) `std::time_t`から構築します。 タイムゾーンはUTC(00:00)になります。 ### `operator std::chrono::system_clock::time_point` `std::chrono::system_clock::time_point`へ変換します。 タイムゾーンはUTC(00:00)になります。 ### `operator std::time_t` `std::time_t`へ変換します。 タイムゾーンはUTC(00:00)になります。 ## 非メンバ関数 ### 比較演算子 ```cpp bool operator==(const offset_datetime&, const offset_datetime&); bool operator!=(const offset_datetime&, const offset_datetime&); bool operator< (const offset_datetime&, const offset_datetime&); bool operator<=(const offset_datetime&, const offset_datetime&); bool operator> (const offset_datetime&, const offset_datetime&); bool operator>=(const offset_datetime&, const offset_datetime&); ``` 日付順で比較します。 同じ日付の場合、タイムゾーン順で比較されます。 ### ストリーム出力演算子 ```cpp std::ostream& operator<<(std::ostream& os, const offset_datetime&); ``` TOMLのデフォルトのフォーマットで出力を行います。 ### `to_string` ```cpp std::string to_string(const offset_datetime&); ``` TOMLのデフォルトのフォーマットで文字列化します。 toml11-4.1.0/docs/content.ja/docs/reference/error_info.md000066400000000000000000000105301464712047700232130ustar00rootroot00000000000000+++ title = "error_info.hpp" type = "docs" +++ # error_info.hpp `error_info.hpp`では、`error_info`と、それをフォーマットする関数が定義されます。 # `toml::error_info` ```cpp namespace toml { struct error_info { error_info(std::string t, source_location l, std::string m, std::string s = ""); error_info(std::string t, std::vector> l, std::string s = ""); std::string const& title() const noexcept; std::string & title() noexcept; std::vector> const& locations() const noexcept; void add_locations(source_location loc, std::string msg) noexcept; std::string const& suffix() const noexcept; std::string & suffix() noexcept; }; template error_info make_error_info( std::string title, source_location loc, std::string msg, Ts&& ... tail); std::string format_error(const std::string& errkind, const error_info& err); std::string format_error(const error_info& err); template std::string format_error(std::string title, source_location loc, std::string msg, Ts&& ... tail); std::ostream& operator<<(std::ostream& os, const error_info& e); } ``` ## メンバ関数 ### コンストラクタ(`title, loc, msg, suffix`) 指定されたタイトル、位置情報、メッセージ、suffixから`error_info`を構築します。 `suffix`はデフォルトで空です。 ### コンストラクタ(`title, [{loc, msg}, ...], suffix`) 指定されたタイトル、位置情報とメッセージの配列、そして`suffix`から`error_info`を構築します。 `suffix`はデフォルトで空です。 ## メンバ関数 ### `std::string title()` エラーメッセージのタイトルです。 ### `std::vector> locations()` エラーの発生した位置とそれに関するメッセージです。 複数指定可能です。 ### `std::string suffix()` 最後に表示するメッセージです。ヒントや補足を表示します。 ## 非メンバ関数 ### `make_error_info` ```cpp template error_info make_error_info( std::string title, source_location loc, std::string msg, Ts&& ... tail); ``` 新しく`error_info`を構築します。 `source_location`または`basic_value`の後には、それに関する`msg`が続かなければなりません。 [`value.hpp`]({{}}) では、 `source_location` の代わりに `toml::basic_value` を渡した際のオーバーロードが追加されます。 末尾には`suffix`を渡すことが可能です。 ### `format_error` `error_info` を以下のようにフォーマットします。 ``` {title} --> {locations().at(0).first.file_name()} | 1 | {locations().at(0).first.line()} | ^-- {locations().at(0).second} | 2 | {locations().at(1).first.line()} | ^-- {locations().at(1).second} {suffix} ``` 二つの `source_location` のファイル名が異なる場合は、ファイル名が再度表示されます。 ```cpp std::string format_error(const std::string& errkind, const error_info& err); std::string format_error(const error_info& err); ``` `error_info`をフォーマットします。 `errkind`が与えられなかった場合、赤色太字の`[error]`が`title`の前につけ足されます。 `errkind`が与えられた場合(空文字列の場合も含みます)、それが`[error]`の代わりに表示されます。 ```cpp namespace toml { template std::string format_error(std::string title, source_location loc, std::string msg, Ts&& ... tail); } // toml ``` `make_error_info` を使って作成した `error_info` を `format_error` でフォーマットした文字列を返します。 [`value.hpp`]({{}}) では、 `source_location` の代わりに `toml::basic_value` を渡した際のオーバーロードが追加されます。 ### ストリーム演算子 ```cpp std::ostream& operator<<(std::ostream& os, const error_info& e); ``` `format_error(e)`を呼び出し、それを出力します。 # 関連項目 - [color.hpp]({{}}) - [parser.hpp]({{}}) - [source_location.hpp]({{}}) toml11-4.1.0/docs/content.ja/docs/reference/exception.md000066400000000000000000000012531464712047700230470ustar00rootroot00000000000000+++ title = "exception.hpp" type = "docs" +++ # exception.hpp # `toml::exception` toml11で定義される例外型の基底クラスです。 ```cpp namespace toml { struct exception : public std::exception { public: virtual ~exception() noexcept override = default; virtual const char* what() const noexcept override {return "";} }; } // toml ``` ## メンバ関数 ### デストラクタ ```cpp virtual ~exception() noexcept override = default; ``` 派生する際に上書きします。 ### `what` ```cpp virtual const char* what() const noexcept override {return "";} ``` エラーメッセージを返します。派生する際に上書きします。 toml11-4.1.0/docs/content.ja/docs/reference/find.md000066400000000000000000000222021464712047700217660ustar00rootroot00000000000000+++ title = "find.hpp" type = "docs" +++ # find.hpp `toml::value`から値を検索し、同時に(必要な場合)型変換を行う関数です。 {{< hint info >}} `toml::value` は格納する型を変更でき、`toml::find`はそれらに対応しているので、 厳密には全て `toml::basic_value` が使われています。ただしこれでは冗長なので、 関数の宣言と特に区別しなければならない場合を除いて、簡単のため説明文では `toml::value` と書きます。 説明文では、テンプレートパラメータ`TC`を変更して型が変更されていれば `toml::value::integer_type` などの型は追従して変更されると解釈してください。 {{< /hint >}} # `toml::find` ## 概要 `toml::find`には、取り出したい型をテンプレート引数で、検索したい値のキーを引数で与えます。 ```cpp template T find(const basic_value& v, Keys ... keys); ``` サポートされている `T` の種類と変換の挙動に関しては、 `toml::get` と同様です。 `T` が指定されなかった場合、 `toml::value` が返されます。 キーは、 `toml::value::key_type` または `std::size_t` です。 複数個のキーが与えられた場合は、サブテーブルや配列に対して再帰的に検索が行われます。 `toml::value::key_type` が与えられた場合は `toml::table` として、 `std::size_t` が与えられた場合は `toml::array` として解釈されます。 ### 再帰的な検索に関しての注意 TOMLには通常の bare key の他に、 `"` や `'` で囲まれた quoted key というものがあります。 quoted key を使うと、 `"foo.bar" = "baz"` というようなキーを書くことができ、この場合はサブテーブルは構築されず、キーは `foo.bar`となります。 このようなパターンに対応するため、toml11ではキーの中に`.`が含まれていても分割は行わず、そのままの文字列で検索を行います。 以下のTOMLファイルを考えます。 ```toml [foo] [foo.bar] baz = "hoge" ["foo.bar"] baz = "fuga" ``` これに対応する`toml::find`の書き方は以下の通りです。 ```cpp const auto input = toml::parse("input.toml"); const auto baz1 = toml::find(input, "foo", "bar", "baz"); // hoge const auto baz2 = toml::find(input, "foo.bar", "baz"); // fuga ``` 参考:[toml.io/ja/v1.0.0#キー](https://toml.io/ja/v1.0.0#%E3%82%AD%E3%83%BC) ## `toml::find(value, key)` `value` を `toml::table` として `key` を検索したあと、 `toml::get` で変換を行います。 ```cpp template /* toml::get(const value&) と同等 */ find( const basic_value& v, const typename basic_value::key_type& ky); template /* toml::get(value&) と同等 */ find( basic_value& v, const typename basic_value::key_type& ky); template /* toml::get(value&&) と同等 */ find( basic_value&& v, const typename basic_value::key_type& ky); ``` `T`が指定されない場合は、変換を行わず`toml::value`を返します。 ```cpp template basic_value const& find( basic_value const& v, const typename basic_value::key_type& ky); template basic_value& find( basic_value& v, const typename basic_value::key_type& ky); template basic_value find( basic_value&& v, const typename basic_value::key_type& ky); ``` ### 例外 `toml::value` が `table` を保持していなかった場合、 `toml::type_error` が送出されます。 格納している `table` が指定された要素を持っていなかった場合、 `std::out_of_range` が送出されます。 指定された要素が `T` に変換できない場合 ( `toml::get` が変換に失敗する場合) 、 `toml::type_error` が送出されます。 ## `toml::find(value, index)` `value` を `toml::array` として `index` 番目にアクセスし、 `toml::get` で変換を行います。 ```cpp template /* toml::get(const value&) と同等 */ find( const basic_value& v, const std::size_t index); template /* toml::get(value&) と同等 */ find( basic_value& v, const std::size_t index); template /* toml::get(value&&) と同等 */ find( basic_value&& v, const std::size_t index); ``` `T`が指定されない場合は、変換を行わず`toml::value`を返します。 ```cpp template basic_value const& find(basic_value const& v, const std::size_t ky); template basic_value& find(basic_value& v, const std::size_t ky); template basic_value find(basic_value&& v, const std::size_t ky); ``` ### 例外 `toml::value` が `array` を保持していなかった場合、 `toml::type_error` が送出されます。 格納している `array` が指定された数の要素を持っていなかった場合、`std::out_of_range`が送出されます。 指定された要素が `T` に変換できない場合 ( `toml::get` が変換に失敗する場合) 、 `toml::type_error` が送出されます。 ## `toml::find(value, keys...)` ```cpp template /* toml::get(const value&) と同等 */ find( const basic_value& v, const K1& k1, const K2& k2, const Ks& ... ks); template /* toml::get(value&) と同等 */ find( basic_value& v, const K1& k1, const K2& k2, const Ks& ... ks); template /* toml::get(value&&) と同等 */ find( basic_value&& v, const K1& k1, const K2& k2, const Ks& ... ks); ``` 再帰的に`toml::find`が呼び出されます。 失敗する条件とその際に送出される例外は `toml::find` と同じです。 # `toml::find_or(value, key, fallback)` ```cpp template T find_or(const basic_value& v, const Key& key, T&& opt); ``` `find_or` は失敗した際のためのデフォルト値を受け取ることで、失敗時に例外を投げないようにします。 このデフォルト値は受け取る型`T`と同じ型でなければなりません。 よって、 `toml::find` とは違って、 `find_or` では `T` を指定せずとも推論されます。 `find_or`のように `T` を指定することもできますが、その場合は常に新規な値が返されます。 参照を取得したい場合は、 `T` を指定しないでください。 ## `T`が`basic_value`である場合 ```cpp template basic_value& find_or(basic_value& v, const K& key, basic_value& opt) noexcept template basic_value const& find_or(const basic_value& v, const K& key, const basic_value& opt) noexcept ``` 対応する値を検索し、変換せずに返します。変換が必要ないため、参照を返すことができます。 値が見つからなかった場合、デフォルト値を返します。 ## `T`が`toml::value::{some_type}`である場合 ```cpp template T& find_or(basic_value& v, const K& key, T& opt) noexcept template T const& find_or(const basic_value& v, const K& key, const T& opt) noexcept ``` 対応する値を検索し、変換せずに返します。変換が必要ないため、参照を返すことができます。 値が見つからなかった場合、あるいは異なる型が格納されていた場合、デフォルト値を返します。 ## `T`が`const char*`である場合 ```cpp template std::string find_or(const basic_value& v, const K& k, const char* opt) ``` 対応する値を検索し、 `std::string` として返します。 失敗時に `const char*` から `std::string` を構築するため、参照を返すことはできません。 ## `T`がその他の型である場合 ```cpp template T find_or(const basic_value& v, const K& key, T opt) ``` 対応する値を検索し、 `T` に変換して返します。 変換を行うため、参照を返すことはできません。 ## 複数のキーが与えられた場合 ```cpp template auto find_or(Value&& v, const K1& k1, const K2& k2, K3&& k3, Ks&& ... keys) noexcept -> decltype(find_or(v, k2, std::forward(k3), std::forward(keys)...)) ``` キーの列の最後の要素がデフォルト値であると解釈して、再帰的に`find_or`を適用します。 `T` の推論結果が `toml::value` または `toml::value::some_type` になる場合、参照を返すことができます。 `T` を明示的に指定した場合、常に変換を行います。 # 関連項目 - [get.hpp]({{}}) - [value.hpp]({{}}) toml11-4.1.0/docs/content.ja/docs/reference/format.md000066400000000000000000000305211464712047700223410ustar00rootroot00000000000000+++ title = "format.hpp" type = "docs" +++ # format.hpp `toml::value`のフォーマット情報を持つ構造体と列挙型を定義します。 # `indent_char` インデント情報を表す列挙体です。 ```cpp enum class indent_char : std::uint8_t { space, // use space tab, // use tab none // no indent }; std::ostream& operator<<(std::ostream& os, const indent_char& c); std::string to_string(const indent_char); ``` `none`を選んだ場合、super tableでの値によらず、インデントは使用されません。 シリアライズ対象の値のなかに`space`と`tab`を指定する値が同時に存在していた場合、その動作は未規定で、指定していない方の文字が出現する可能性があります。 # `boolean_format_info` `boolean`のフォーマット情報です。 ```cpp struct boolean_format_info {}; bool operator==(const boolean_format_info&, const boolean_format_info&) noexcept; bool operator!=(const boolean_format_info&, const boolean_format_info&) noexcept; ``` `boolean`のフォーマット方法は一通りしかないため、設定できる値を持ちません。 # `integer_format` ```cpp enum class integer_format : std::uint8_t { dec = 0, bin = 1, oct = 2, hex = 3, }; std::ostream& operator<<(std::ostream& os, const integer_format f); std::string to_string(const integer_format); ``` `integer`の基数を指定します。 # `integer_format_info` ```cpp struct integer_format_info { integer_format fmt = integer_format::dec; bool uppercase = true; // use uppercase letters std::size_t width = 0; // minimal width (may exceed) std::size_t spacer = 0; // position of `_` (if 0, no spacer) std::string suffix = ""; // _suffix (library extension) }; bool operator==(const integer_format_info&, const integer_format_info&) noexcept; bool operator!=(const integer_format_info&, const integer_format_info&) noexcept; ``` ## メンバ変数 ### `integer_format fmt` 基数を指定します。 ### `bool uppercase` 16進数表記で大文字を使用します。 ### `std::size_t width` 最小の幅を指定します。値によってはこの幅を超えることがあります。 フォーマットした値がこの幅よりも小さい場合、`integer_format::dec`の場合は効果はありませんが、それ以外の場合は先頭にリーディング`0`が追加されます。 ### `std::size_t spacer` アンダースコア`_`を追加する幅を指定します。 `3`の場合`1_234_567`のように、`4`の場合`0xdead_beef`のようにフォーマットされます。 `0`の場合、アンダースコアは挿入されません。 不規則な幅を指定することはできません。 ### `std::string suffix` toml11拡張の`spec::ext_num_suffix`を`true`にしている場合、その`suffix`がここに保存されます。 参考:[spec.hpp]({{}}) # `floating_format` ```cpp enum class floating_format : std::uint8_t { defaultfloat = 0, fixed = 1, // does not include exponential part scientific = 2, // always include exponential part hex = 3 // hexfloat extension }; std::ostream& operator<<(std::ostream& os, const floating_format f); std::string to_string(const floating_format); ``` `floating`のフォーマット形式を指定します。 それぞれ、`std::defaultfloat`, `std::fixed`, `std::scientific`, `std::hexfloat`に対応します。 `hexfloat`は、`toml::spec::ext_hex_float`が`true`の場合のみ使用可能です。 参考:[spec.hpp]({{}}) # `floating_format_info` ```cpp struct floating_format_info { floating_format fmt = floating_format::defaultfloat; std::size_t prec = 0; // precision (if 0, use the default) std::string suffix = ""; // 1.0e+2_suffix (library extension) }; bool operator==(const floating_format_info&, const floating_format_info&) noexcept; bool operator!=(const floating_format_info&, const floating_format_info&) noexcept; ``` ## メンバ変数 ### `floating_format fmt` フォーマット形式を指定します。 ### `std::size_t prec` 小数点以下の精度を指定します。 ### `std::string suffix` toml11拡張の`spec::ext_num_suffix`を`true`にしている場合、その`suffix`がここに保存されます。 参考:[spec.hpp]({{}}) # `string_format` ```cpp enum class string_format : std::uint8_t { basic = 0, literal = 1, multiline_basic = 2, multiline_literal = 3 }; std::ostream& operator<<(std::ostream& os, const string_format f); std::string to_string(const string_format); ``` 文字列のフォーマット形式を指定します。 # `string_format_info` ```cpp struct string_format_info { string_format fmt = string_format::basic; bool start_with_newline = false; }; bool operator==(const string_format_info&, const string_format_info&) noexcept; bool operator!=(const string_format_info&, const string_format_info&) noexcept; ``` ## メンバ変数 ### `string_format fmt` 文字列のフォーマット情報を指定します。 ### `bool start_with_newline` `multiline_basic`または`multiline_literal`の場合、最初の`"""`や`'''`の後に改行を入れるかどうかを指定します。 # `datetime_delimiter_kind` ```cpp enum class datetime_delimiter_kind : std::uint8_t { upper_T = 0, lower_t = 1, space = 2, }; std::ostream& operator<<(std::ostream& os, const datetime_delimiter_kind d); std::string to_string(const datetime_delimiter_kind); ``` `datetime`で日付と時刻の間のデリミタにどの文字を使うかを指定します。 `T`, `t`, ` `が使用可能です。 # `offset_datetime_format_info` ```cpp struct offset_datetime_format_info { datetime_delimiter_kind delimiter = datetime_delimiter_kind::upper_T; bool has_seconds = true; std::size_t subsecond_precision = 6; // [us] }; bool operator==(const offset_datetime_format_info&, const offset_datetime_format_info&) noexcept; bool operator!=(const offset_datetime_format_info&, const offset_datetime_format_info&) noexcept; ``` ## メンバ変数 ### `datetime_delimiter_kind delimiter` 使用するデリミタを指定します。 ### `bool has_seconds` 秒数を省略するかどうかを指定します。 ### `std::size_t subsecond_precision` 秒以下の精度を何桁出力するかを指定します。 # `local_datetime_format_info` ```cpp struct local_datetime_format_info { datetime_delimiter_kind delimiter = datetime_delimiter_kind::upper_T; bool has_seconds = true; std::size_t subsecond_precision = 6; // [us] }; bool operator==(const local_datetime_format_info&, const local_datetime_format_info&) noexcept; bool operator!=(const local_datetime_format_info&, const local_datetime_format_info&) noexcept; ``` ## メンバ変数 ### `datetime_delimiter_kind delimiter` 使用するデリミタを指定します。 ### `bool has_seconds` 秒数を省略するかどうかを指定します。 ### `std::size_t subsecond_precision` 秒以下の精度を何桁出力するかを指定します。 # `local_date_format_info` ```cpp struct local_date_format_info { // nothing, for now }; bool operator==(const local_date_format_info&, const local_date_format_info&) noexcept; bool operator!=(const local_date_format_info&, const local_date_format_info&) noexcept; ``` `local_datetime`にはフォーマット指定するパラメータはありません。 # `local_time_format_info` ```cpp struct local_time_format_info { bool has_seconds = true; std::size_t subsecond_precision = 6; // [us] }; bool operator==(const local_time_format_info&, const local_time_format_info&) noexcept; bool operator!=(const local_time_format_info&, const local_time_format_info&) noexcept; ``` ## メンバ変数 ### `bool has_seconds` 秒数を省略するかどうかを指定します。 ### `std::size_t subsecond_precision` 秒以下の精度を何桁出力するかを指定します。 # `array_format` ```cpp enum class array_format : std::uint8_t { default_format = 0, oneline = 1, multiline = 2, array_of_tables = 3 // [[format.in.this.way]] }; std::ostream& operator<<(std::ostream& os, const array_format f); std::string to_string(const array_format); ``` - `default_format` - 適したフォーマットを自動的に選択します。ある程度長い配列は複数行になります。 - `oneline` - 全ての要素を一行でフォーマットします。 - `multiline` - 一行ごとに一つの要素を出力します。 - `array_of_tables` - `[[array.of.tables]]`の形式でフォーマットします。`table`以外の要素を含むことはできません。 # `array_format_info` ```cpp struct array_format_info { array_format fmt = array_format::default_format; indent_char indent_type = indent_char::space; std::int32_t body_indent = 4; // indent in case of multiline std::int32_t closing_indent = 0; // indent of `]` }; bool operator==(const array_format_info&, const array_format_info&) noexcept; bool operator!=(const array_format_info&, const array_format_info&) noexcept; ``` ## メンバ変数 ### `array_format fmt` フォーマット形式を指定します。 ### `indent_char indent_type` インデントに使用する文字の種類を選択します。 ### `std::int32_t body_indent` `array_format::multiline`の場合、要素の前に出力するインデントの文字数を指定します。 ### `std::int32_t closing_indent` `array_format::multiline`の場合、閉じ括弧`]`の前に出力するインデントの文字数を指定します。 # `table_format` ```cpp enum class table_format : std::uint8_t { multiline = 0, // [foo] \n bar = "baz" oneline = 1, // foo = {bar = "baz"} dotted = 2, // foo.bar = "baz" multiline_oneline = 3, // foo = { \n bar = "baz" \n } implicit = 4 // [x] defined by [x.y.z]. skip in serializer. }; std::ostream& operator<<(std::ostream& os, const table_format f); std::string to_string(const table_format); ``` - `multiline` - 複数行の通常のテーブルとしてフォーマットします。 - `oneline` - インラインテーブルとしてフォーマットします。 - `dotted` - `a.b.c = "d"`の形式でフォーマットします。 - `multiline_oneline` - 改行を含むインラインテーブルとしてフォーマットします。TOML v1.1.0以降で使用可能です。 - 参考:[spec.hpp]({{}}) - `implicit` - `[x.y.z.w]`だけが定義されている際の`[x]`, `[x.y]`, `[x.y.z]`のように、暗黙定義としてスキップします。 {{< hint warning >}} TOMLの文法上、`dotted`はサブテーブルを持つことができます。 ```toml [fruit] apple.color = "red" apple.taste.sweet = true # [fruit.apple] # INVALID # [fruit.apple.taste] # INVALID [fruit.apple.texture] # you can add sub-tables smooth = true ``` toml11は現時点ではこのフォーマットに対応していません。 `dotted`テーブルの下にあるテーブルは全て`dotted`になり、テーブルは強制的にインラインテーブルになります。 {{< /hint >}} # `table_format_info` ```cpp struct table_format_info { table_format fmt = table_format::multiline; indent_char indent_type = indent_char::space; std::int32_t body_indent = 0; // indent of values std::int32_t name_indent = 0; // indent of [table] std::int32_t closing_indent = 0; // in case of {inline-table} }; bool operator==(const table_format_info&, const table_format_info&) noexcept; bool operator!=(const table_format_info&, const table_format_info&) noexcept; ``` ## メンバ変数 ### `table_format fmt` フォーマット方法を指定します。 ### `indent_char indent_type` インデントに使用する文字を指定します。 ### `std::int32_t body_indent` キーの前に出力するインデントの幅を指定します。 スーパーテーブルのインデント幅は加算されません。 ### `std::int32_t name_indent` `[table]`形式のキーのインデントを指定します。 スーパーテーブルのインデント幅は加算されません。 ### `std::int32_t closing_indent` `multiline_oneline`の場合に、閉じ括弧`}`の前のインデント幅を指定します。 toml11-4.1.0/docs/content.ja/docs/reference/from.md000066400000000000000000000020371464712047700220150ustar00rootroot00000000000000+++ title = "from.hpp" type = "docs" +++ # from.hpp `toml::get`や`toml::find`で使用する、`toml::value`からの変換を定義する構造体です。 メンバ関数に`from_toml`を追加することによっても同じ機能を追加できますが、メンバ関数を追加できないクラスに対しては`from`を使用してください。 このファイルでは特定の実装は提供しません。使用する際に、この構造体を特殊化してください。 ```cpp namespace toml { template struct from; } // toml ``` ## 例 ```cpp namespace extlib { struct foo { int a; std::string b; }; } // extlib #include namespace toml { template<> struct from { template static extlib::foo from_toml(const toml::basic_value& v) { return extlib::foo{toml::find(v, "a"), toml::find(v, "b")}; } }; } // toml ``` # 関連項目 - [conversion.hpp]({{}}) - [into.hpp]({{}}) toml11-4.1.0/docs/content.ja/docs/reference/get.md000066400000000000000000000356701464712047700216420ustar00rootroot00000000000000+++ title = "get.hpp" type = "docs" +++ # get.hpp `toml::value`から値を取り出し、同時に(必要な場合)型変換を行う関数です。 {{< hint info >}} `toml::value` は格納する型を変更でき、`toml::get`はそれらに対応しているので、 厳密には全て `toml::basic_value` が使われています。ただしこれでは冗長なので、 関数の宣言と特に区別しなければならない場合を除いて、簡単のため説明文では `toml::value` と書きます。 説明文では、テンプレートパラメータ`TC`を変更して型が変更されていれば `toml::value::integer_type` などの型は追従して変更されると解釈してください。 {{< /hint >}} # `toml::get` ## 概要 基本的に、`toml::get`は以下のような関数として振る舞います。 `T`は`toml::get(v)`のようにして与えます。 ```cpp template T get(const basic_value& v); ``` ただし、`T`がどのような型であるかによって、`toml::get`は異なる挙動をします。 `T`の型の種類は、 1. 変換が必要ない型 2. 変換する必要がある型 に分けられます。 細かい条件と、特別にサポートしている具体的な型については後述します。 ### 変換が必要ない型 変換が必要ないのは、渡された `toml::value` が格納している型です。 例えば、 `toml::value::integer_type` は `std::int64_t` のエイリアスなので、 `toml::get(v)` は変換を必要としません。 この場合、 `toml:get` は `integer` の値を `toml::value` から取り出し、その参照を返します。 渡された`toml::value`が可変参照(`&`)である場合、返す値も可変参照(`&`)です。 不変参照(`const&`)の場合、返す値も不変参照(`const&`)となります。 可変参照を返した場合、その参照を通して`toml::value`に格納されている値に上書きできます。 ### 変換が必要な型 上記の型以外については変換が必要です。 例えば、`toml::value::integer_type`は`std::int64_t`のエイリアスなので、`toml::get(toml::value&)`は変換が必要です。 この場合、`toml:get`は`integer`の値を`toml::value`から取り出し、それをキャストして返します。 toml11は簡単なキャストだけでなく、 `toml::array`からや`std::tuple`や`std::array`、 `toml::table`から`std::map`などの複雑な型変換をサポートします。 具体的には、続くセクションを参照してください。 ### 失敗した場合 期待した型変換を行えない場合があります。例えば、`table`を持っている`toml::value`に`toml::get(v)`を適用した場合などです。 このような場合は、取り出そうとした型に最も似ている型への変換(今回は`int`なので、`as_integer`)が失敗したとして、`toml::type_error`が送出されます。 ファイルからパースした場合、以下のようなエラーメッセージが出力されます。 ``` terminate called after throwing an instance of 'toml::type_error' what(): toml::value::as_integer(): bad_cast to integer --> input.toml | 6 | [fruit] | ^^^^^^^-- the actual type is table ``` ## `T`が`toml::value`と同一のとき ```cpp template< T, typename TC> T& get(basic_value& v); template T const& get(const basic_value& v); template T get(basic_value&& v); ``` 条件: - `std::is_same>` を満たす `toml::value`から`toml::value`なので、変換は行われず、そのままの値が返されます。 他の関数の実装を一般化するためだけに存在しています。 失敗しません。 ## `T`が`toml::value::{some_type}`のいずれかのとき ```cpp template T& get(basic_value& v); template T const& get(const basic_value& v); template T get(basic_value&& v); ``` 条件: - `T`が`toml::value`が格納できる型(`toml::value::boolean_type`など)のどれかと同一であること `toml::value` が格納している型と同じ型、例えば `toml::value::integer_type` が `toml::get` の `T` として指定されたとき、型変換の必要がないため参照を返すことが可能です。 異なる型が格納されていた場合、`toml::type_error` が送出されます。 ## `T`が異なる`TypeConfig`を持つ`basic_value`のとき ```cpp template T get(basic_value& v); ``` 条件: - `T`が`toml::basic_value`ではない - `T`が`toml::basic_value`である 異なる型を格納し得る`basic_value`が指定された場合、変換が行われます。 型変換が発生するので、返す値は新規な値であり、参照ではありません。 失敗しません(メモリ枯渇などの場合を除く)。 ## `T`が整数型の場合 ```cpp template T get(basic_value& v); ``` 条件: - `std::is_integral` を満たす - `std::is_same` ではない - `toml::value::integer_type` ではない `toml::value` を `integer_type` を保持しているとしてその値を取得し、それを `T` に変換して返します。 `toml::value::integer_type` 以外の型が格納されていた場合、 `toml::type_error` が送出されます。 ## `T`が浮動小数点数型の場合 ```cpp template T get(basic_value& v); ``` 条件: - `std::is_floating_point` を満たす - `toml::value::floating_type` ではない `toml::value`を`floating_type`を保持しているとしてその値を取得し、それを`T`に変換して返します。 `toml::value::floating_type` 以外の型が格納されていた場合、 `toml::type_error` が送出されます。 ## `T`が`std::string_view`の場合 C++17以降でのみ使用可能です。 ```cpp template T get(basic_value& v); ``` 条件: - `std::is_same` を満たす `toml::value`を`string_type`を保持しているとしてその値を取得し、それから`std::string_view`を構築して返します。 `toml::value::string_type` 以外の型が格納されていた場合、 `toml::type_error` が送出されます。 ## `T`が`std::chrono::duration`の場合 ```cpp template T get(basic_value& v); ``` 条件: - `T`が`std::chrono::duration`である `toml::value`を`local_time`を保持しているとしてその値を取得し、それを`std::chrono::duration`に変換して返します。 `toml::value::local_time` 以外の型が格納されていた場合、 `toml::type_error` が送出されます。 ## `T`が`std::chrono::system_clock::time_point`の場合 ```cpp template T get(basic_value& v); ``` 条件: - `std::is_same`を満たす `toml::value` を `local_date`, `local_datetime`, `offset_datetime`のどれかを保持しているとしてその値を取得し、それを`std::chrono::system_clock::time_point`に変換して返します。 `local_date`, `local_datetime`, `offset_datetime` 以外の型が格納されていた場合、 `toml::type_error` が送出されます。 ## `T`が`array-like`である場合 ```cpp template T get(basic_value& v); ``` 条件: - `T`は`T::iterator`を持つ - `T`は`T::value_type`を持つ - `T`は`T::push_back(x)`を持つ - `T`は`toml::value::array_type`ではない - `T`は`std::string`ではない - `T`は`std::string_view`ではない - `T`は`map-like`ではない - `T`は`from_toml()`メンバ関数を持たない - `toml::from`が定義されていない - `toml::basic_value`からのコンストラクタが定義されていない `std::vector`や`std::deque`などが該当します。 `toml::value`を`array`を保持しているとしてその値を取得し、それを指定されたコンテナに変換して返します。 `toml::value::array_type` 以外の型が格納されていた場合、 `toml::type_error` が送出されます。 ## `T`が`std::array`である場合 ```cpp template T get(basic_value& v); ``` 条件: - `T`は`std::array`である `toml::value`を`array`を保持しているとしてその値を取得し、それを指定されたコンテナに変換して返します。 `toml::value::array_type` 以外の型が格納されていた場合、 `toml::type_error` が送出されます。 `toml::value` が持つ `array` が十分な数の要素を持っていなかった場合、`std::out_of_range`が送出されます。 ## `T`が`std::forward_list`である場合 ```cpp template T get(basic_value& v); ``` 条件: - `T`は`std::forward_list`である `toml::value`を`array`を保持しているとしてその値を取得し、それを`std::forward_list`に変換して返します。 `toml::value::array_type` 以外の型が格納されていた場合、 `toml::type_error` が送出されます。 `forward_list`は`push_back`を持たないので、別に実装されています。 ## `T`が`std::pair`である場合 ```cpp template T get(basic_value& v); ``` 条件: - `T`は`std::pair`である `toml::value`を`array`を保持しているとしてその値を取得し、それを`std::pair`に変換して返します。 `first` と `second` はそれぞれ再帰的に変換されます。 `toml::value::array_type` 以外の型が格納されていた場合、 `toml::type_error` が送出されます。 `toml::value` が持つ `array` の要素数がちょうど2個でなかった場合、`std::out_of_range`が送出されます。 ## `T`が`std::tuple`である場合 ```cpp template T get(basic_value& v); ``` 条件: - `T`は`std::tuple`である `toml::value`を`array`を保持しているとしてその値を取得し、それを`std::tuple`に変換して返します。 各要素はそれぞれ再帰的に変換されます。 `toml::value::array_type` 以外の型が格納されていた場合、 `toml::type_error` が送出されます。 `toml::value` が持つ `array` の要素数がちょうど `std::tuple_size::value` 個でなかった場合、`std::out_of_range`が送出されます。 ## `T`が`map-like`である場合 ```cpp template T get(basic_value& v); ``` 条件: - `T`は`T::iterator`を持つ - `T`は`T::key_type`を持つ - `T`は`T::value_type`を持つ - `T`は`T::mapped_type`を持つ - `T`は`toml::value::table_type`ではない - `T`は`from_toml()`メンバ関数を持たない - `toml::from`が定義されていない - `toml::basic_value`からのコンストラクタが定義されていない `std::map`や`std::unordered_map`などが該当します。 `toml::value`を`table`を保持しているとしてその値を取得し、それを `T` に変換して返します。 各要素はそれぞれ再帰的に変換されます。 `basic_value::table_type` 以外の型が格納されていた場合、 `toml::type_error` が送出されます。 ## `T`が`toml::from`の特殊化を持つユーザー定義型である場合 ```cpp template T get(basic_value& v); ``` 条件: - `toml::from`が定義されている `toml::from` の `T` に対する特殊化が定義されていた場合、それを使用した型変換が行われます。 個別にサポートされる型( `std::array`, `std::pair`, `std::tuple` )と衝突しないようにしてください。 ## `T`が`T::from_toml`メンバ関数を持つユーザー定義型である場合 ```cpp template T get(basic_value& v); ``` 条件: - `toml::from`が定義されていない - `T`は`from_toml()`メンバ関数を持つ `T` に `from_toml(toml::basic_value)` メンバ関数が定義されていた場合、それを使用した型変換が行われます。 `toml::from` が定義されていると、そちらが優先されます。 ## `T`が`toml::basic_value`を取るコンストラクタを持つユーザー定義型である場合 ```cpp template T get(basic_value& v); ``` 条件: - `toml::from`が定義されていない - `T`は`from_toml()`メンバ関数を持たない - `T`は`toml::basic_value`を取るコンストラクタを持つ `T` に `toml::basic_value` を取るコンストラクタが定義されていた場合、それを使用した型変換が行われます。 `toml::from` または `T::from_toml` が定義されていると、そちらが優先されます。 # `toml::get_or` `get_or` は失敗した際のためのデフォルト値を受け取ることで、失敗時に例外を投げないようにします。 このデフォルト値は受け取る型`T`と同じ型でなければなりません。 よって、 `toml::get` とは違って、 `get_or` では `T` を指定せずとも推論されます。 ## `T`が`basic_value`である場合 ```cpp template basic_value const& get_or(const basic_value& v, const basic_value& opt) template basic_value & get_or(basic_value& v, basic_value& opt) template basic_value get_or(basic_value&& v, basic_value&& opt) ``` 変換先は同一の`toml::value`なので、失敗しません。 他の関数の実装を一般化するためだけに存在しています。 ## `T`が`basic_value::{some_type}`である場合 ```cpp template T const& get_or(const basic_value& v, const T& opt) noexcept template T & get_or(basic_value& v, T& opt) noexcept template T get_or(basic_value&& v, T&& opt) noexcept ``` `toml::get`と同様の変換を行います。失敗した場合は第二引数が返されます。 ## `T`が`const char*`である場合 ```cpp template typename basic_value::string_type get_or(const basic_value& v, const typename basic_value::string_type::value_type* opt); ``` `const char*` が渡された場合、変換先は `std::string` として解釈されます。 ## `T`がその他の場合 ```cpp template typename std::remove_cv::type>::type get_or(const basic_value& v, T&& opt); ``` `toml::get`と同様の変換を行います。失敗した場合は第二引数が返されます。 # 関連項目 - [find.hpp]({{}}) - [from.hpp]({{}}) - [value.hpp]({{}}) toml11-4.1.0/docs/content.ja/docs/reference/into.md000066400000000000000000000022111464712047700220150ustar00rootroot00000000000000+++ title = "into.hpp" type = "docs" +++ # into.hpp `toml::value`のコンストラクタで使用する、ユーザー定義型からの変換を定義する構造体です。 メンバ関数に`into_toml`を追加することによっても同じ機能を追加できますが、メンバ関数を追加できないクラスに対しては`into`を使用してください。 このファイルでは特定の実装は提供しません。使用する際に、この構造体を特殊化してください。 ```cpp namespace toml { template struct into; } // toml ``` ## 例 ```cpp namespace extlib { struct foo { int a; std::string b; }; } // extlib #include namespace toml { template<> struct into { template static toml::basic_value into_toml(const extlib::foo& f) { using value_type = toml::basic_value; using table_type = typename value_type::table_type; return value_type(table_type{{"a", f.a}, {"b", f.b}}); } }; } // toml ``` # 関連項目 - [conversion.hpp]({{}}) - [from.hpp]({{}}) toml11-4.1.0/docs/content.ja/docs/reference/literal.md000066400000000000000000000034621464712047700225110ustar00rootroot00000000000000+++ title = "literal.hpp" type = "docs" +++ # literal.hpp `literal.hpp`では、`_toml`リテラルが定義されます。 `_toml`リテラルは、文字列リテラルをパースして`toml::value`に変換します。 ```cpp namespace toml { inline namespace literals { inline namespace toml_literals { toml::value operator"" _toml(const char* str, std::size_t len); toml::value operator"" _toml(const char8_t* str, std::size_t len); // C++20以降 } // toml_literals } // literals } // toml ``` ## 自由関数 ### `operator"" _toml(char)` ```cpp toml::value operator"" _toml(const char* str, std::size_t len); ``` 文字列リテラルをパースして`toml::value`に変換します。 通常のTOMLファイルの場合、`toml::parse`と同等の処理が行われます。 ```cpp const auto v1 = "a = 'foo'"_toml; // v1: {a = 'foo'} ``` 改行を含む場合、生文字列リテラルが便利です。 ```cpp const auto v1 = R"( a = 42 b = "foo" )"_toml; ``` 値が単体で書かれていた場合、その値になります。 ```cpp const auto v2 = "'foo'"_toml; // v2: 'foo' ``` TOMLは数値のみからなるキーを許可しています。 `[1]`のように、テーブル定義と配列の区別がつかない場合、テーブル定義が優先されます。 配列として解釈させるには、trailing commaを使用してください。 ```cpp const auto v3 = "[1]"_toml; // v3: {1 = {}} const auto v4 = "[1,]"_toml; // v4: [1,] ``` ### `operator"" _toml(char8_t)` `char8_t`が利用可能な場合に定義されます。引数の型のほかに違いはありません。 # 例 ```cpp #include int main() { using namespace toml::literals::toml_literals; const auto v = "a = \"foo\""_toml; assert(v.at("a").as_string() == "foo"); return 0; } ``` toml11-4.1.0/docs/content.ja/docs/reference/ordered_map.md000066400000000000000000000213151464712047700233330ustar00rootroot00000000000000+++ title = "ordered_map.hpp" type = "docs" +++ # ordered_map.hpp ファイル中の値の順番を維持するために使用する`toml::ordered_map`を定義します。 # `class ordered_map` ```cpp namespace toml { template, typename Allocator = std::allocator>> class ordered_map; } ``` `ordered_map`は、値を追加した順序を保ったまま値を保持し、その順でイテレートできる `map` 型です。 線形コンテナなので、検索には要素数に対して `O(n)` の時間がかかります。 検索を行う機会が少なく、値の順序を守る必要がある場合に使用してください。 ## 非メンバ型 ```cpp namespace toml { struct ordered_type_config; using ordered_value = basic_value; using ordered_table = typename ordered_value::table_type; using ordered_array = typename ordered_value::array_type; } ``` `toml::type_config` と `toml::value` の代わりに使用します。 {{< hint info >}} `toml::parse` はデフォルトで `type_config` を使用するので、パースする際に ```cpp const auto input = toml::parse("input.toml"); ``` としてください。 {{< /hint >}} ## メンバ型 ```cpp using key_type = Key; using mapped_type = Val; using value_type = std::pair; using key_compare = Cmp; using allocator_type = Allocator; using container_type = std::vector; using reference = typename container_type::reference; using pointer = typename container_type::pointer; using const_reference = typename container_type::const_reference; using const_pointer = typename container_type::const_pointer; using iterator = typename container_type::iterator; using const_iterator = typename container_type::const_iterator; using size_type = typename container_type::size_type; using difference_type = typename container_type::difference_type; ``` ## メンバ関数 ### コンストラクタ ```cpp ordered_map() = default; ``` 空の `ordered_map` を構築します。 ### コンストラクタ(コンパレータ、アロケータ) ```cpp explicit ordered_map(const Cmp& cmp, const Allocator& alloc = Allocator()); explicit ordered_map(const Allocator& alloc); ``` キーの比較に使用するコンパレータや、コンテナのメモリ確保に使用するアロケータを指定して構築します。 ### コピー・ムーブコンストラクタ ```cpp ordered_map(const ordered_map&) = default; ordered_map(ordered_map&&) = default; ordered_map(const ordered_map& other, const Allocator& alloc); ordered_map(ordered_map&& other, const Allocator& alloc); ``` 別の `ordered_map` の内容をコピー・ムーブして構築します。 コンテナのメモリ確保に使用するアロケータを指定することも可能です。 ### コンストラクタ(`Iterator`) ```cpp template ordered_map(InputIterator first, InputIterator last, const Cmp& cmp = Cmp(), const Allocator& alloc = Allocator()); template ordered_map(InputIterator first, InputIterator last, const Allocator& alloc = Allocator()); ``` イテレータ範囲を受け取って構築します。 順序は、イテレータの順序に従います。 ### コンストラクタ(`std::initializer_list`) ```cpp ordered_map(std::initializer_list v, const Cmp& cmp = Cmp(), const Allocator& alloc = Allocator()); ordered_map(std::initializer_list v, const Allocator& alloc); ``` `ordered_map{...}`の形式で初期化します。 ### コピー・ムーブ代入演算子 ```cpp ordered_map& operator=(const ordered_map&) = default; ordered_map& operator=(ordered_map&&) = default; ``` 別の `ordered_map` の内容をコピー・ムーブ代入します。 ### 代入演算子(`std::initializer_list`) ```cpp ordered_map& operator=(std::initializer_list v); ``` `std::initializer_list` の内容を代入します。 ### デストラクタ ```cpp ~ordered_map() = default; ``` `ordered_map` を破棄します。 ### `begin()`, `end()` ```cpp iterator begin() noexcept; iterator end() noexcept; const_iterator begin() const noexcept; const_iterator end() const noexcept; const_iterator cbegin() const noexcept; const_iterator cend() const noexcept; ``` コンテナの内容を順序通りにイテレートします。 ### `empty()` ```cpp bool empty() const noexcept; ``` `ordered_map` が空の場合 `true` を、そうでない場合 `false` を返します。 ### `size()` ```cpp std::size_t size() const noexcept; ``` `ordered_map` の要素数を返します。 ### `max_size()` ```cpp std::size_t max_size() const noexcept; ``` `ordered_map` が持つことのできる最大の要素数を返します。 ### `clear()` ```cpp void clear(); ``` 内容を消去します。 ### `push_back(kv)` ```cpp void push_back(const value_type&); void push_back(value_type&&); ``` キーと値のペアを末尾に追加します。 ### `emplace_back(k, v)` ```cpp void emplace_back(key_type, mapped_type); ``` キーと値のペアを末尾に追加します。 ### `pop_back()` ```cpp void pop_back(); ``` 末尾の値を削除します。 ### `insert(kv)` ```cpp void insert(value_type); ``` キーと値のペアを末尾に追加します。 ### `emplace(k, v)` ```cpp void emplace(key_type, mapped_type); ``` キーと値のペアを末尾に追加します。 ### `count(k)` ```cpp std::size_t count(const key_type&) const noexcept; ``` キーに対応する値の数を返します。 同じキーに複数の値を代入することはできないので、値が存在する場合は `1`, そうでない場合は `0` を返します。 ### `contains(k)` ```cpp bool contains(const key_type&) const noexcept; ``` キーに対応する値が存在するなら `true` を、そうでないなら `false` を返します。 ### `find(k)` ```cpp iterator find(const key_type& key) noexcept; const_iterator find(const key_type& key) const noexcept; ``` キーに対応する値を検索し、それを指すイテレータを返します。 存在しなかった場合、`end()`を返します。 ### `at(k)` ```cpp mapped_type& at(const key_type& k); mapped_type const& at(const key_type& k) const; ``` キーに対応する値を検索して返します。 存在しなかった場合、`std::out_of_range`を送出します。 ### `operator[](k)` ```cpp mapped_type& operator[](const key_type& k); mapped_type const& operator[](const key_type& k) const; ``` キーに対応する値を検索して返します。 存在しなかった場合、新規な値を構築して返します。 `ordered_map` が `const` の場合は新規な値を構築できないので、 `std::out_of_range` を送出します。 ### `key_comp()` ```cpp key_compare key_comp() const; ``` 比較に使用するコンパレータを返します。 ## 使用上の注意 ### キーの書き換え {{< hint warning >}} `ordered_map` は `value_type` に `std::pair` を使用しているので、イテレータを介してキーを書き換えることが可能になってしまっています。 この方法でキーを書き換えることは推奨されません。 キーを書き換えて既存のキーと衝突した場合、衝突したうちの片方が検索できなくなります。 `operator[]` や `push_back`, `insert` による書き込みの場合は、既存のキーとの衝突が検出されます。 {{< /hint >}} ### 保たれる順序の詳細 {{< hint warning >}} `ordered_map` はキーの順序を保ちますが、ここで保たれる順序は同じテーブルで定義されたキーの順序のみです。 よって、テーブルをまたいだ順序は保たれないことに注意してください。 例えば、以下のファイルの順序は保たれます。 ```cpp apple.type = "fruit" apple.skin = "thin" apple.color = "red" orange.type = "fruit" orange.skin = "thick" orange.color = "orange" ``` 対して以下のファイルの順序は保たれません。 ```cpp apple.type = "fruit" orange.type = "fruit" apple.skin = "thin" orange.skin = "thick" apple.color = "red" orange.color = "orange" ``` `ordered_map` が保つ順序は、rootテーブルに定義された `apple` と `orange` の順序と、 `apple` 内で定義された `type`, `skin`, `color` の順序、 また `orange` 内で定義された `type`, `skin`, `color` の順序のみです。 {{< /hint >}} ## 関連項目 - [parser.hpp]({{}}) - [types.hpp]({{}}) - [value.hpp]({{}}) toml11-4.1.0/docs/content.ja/docs/reference/parser.md000066400000000000000000000301731464712047700223500ustar00rootroot00000000000000+++ title = "parser.hpp" type = "docs" +++ # parser.hpp ファイルまたは文字列をパースする関数と、それが用いる例外を定義します。 `parse`は失敗した場合に例外を送出しますが、`try_parse`はエラー情報を返します。 # `parse` 与えられたファイルの内容をパースし、`toml::basic_value`を返します。 失敗した場合は`toml::syntax_error`が送出されます。 `basic_value`の持つ型情報は`template`で、TOML言語のバージョンは`toml::spec`で指定します。 ### `parse(std::string filename, toml::spec)` ```cpp namespace toml { template basic_value parse(std::string fname, spec s = spec::default_version()); } ``` ファイル名を受け取って、その内容をパースします。 ファイルの読み込みに失敗した場合、`file_io_error`が送出されます。 パースに失敗した場合、`syntax_error`が送出されます。 ### `parse(const char (&)[N] filename, toml::spec)` ```cpp namespace toml { template basic_value parse(const char (&fname)[N], spec s = spec::default_version()); } ``` 文字列リテラルを受け取って、そのファイルの内容をパースします。 ファイルの読み込みに失敗した場合、`file_io_error`が送出されます。 パースに失敗した場合、`syntax_error`が送出されます。 ### `parse(std::filesystem::path, toml::spec)` ```cpp namespace toml { template basic_value parse(const std::filesystem::path& fpath, spec s = spec::default_version()); } ``` ``が利用可能な場合のみ定義されます。 ファイルパスを受け取って、そのファイルの内容をパースします。 ファイルの読み込みに失敗した場合、`file_io_error`が送出されます。 パースに失敗した場合、`syntax_error`が送出されます。 ### `parse(std::istream&, std::string filename, toml::spec)` ```cpp namespace toml { template basic_value parse(std::istream& is, std::string fname = "unknown file", spec s = spec::default_version()); } ``` `std::istream&`を受け取ってその内容をパースします。 標準ライブラリが改行文字を自動変換することによるファイルサイズと文字数との不整合を避けるため、 `std::ios::binary`を使ってバイナリモードで開いてください。 ファイル名の情報は第三引数で受け取ります。ファイル名が渡されなかった場合、`"unknown file"`になります。 ### `parse(FILE*, std::string filename, toml::spec)` ```cpp namespace toml { template result, std::vector> parse(FILE* fp, std::string filename, spec s = spec::default_version()); } ``` `FILE*`が指すファイルを読み込んでパースします。 標準ライブラリが改行文字を自動変換することによるファイルサイズと文字数との不整合を避けるため、 `fopen`には`"rb"`などを渡してバイナリモードで開いてください。 ファイルの読み込みに失敗した場合、`errno`が含まれた`file_io_error`が送出されます。 パースに失敗した場合、`syntax_error`が送出されます。 ### `parse(std::vector, std::string filename, toml::spec)` ```cpp namespace toml { template basic_value parse(std::vector content, std::string filename, spec s = spec::default_version()); } ``` バイト列をTOMLファイルとしてパースします。 パースに失敗した場合、`syntax_error`が送出されます。 # `parse_str` ### `parse_str(std::string, toml::spec)` ```cpp namespace toml { template basic_value parse_str(std::string content, spec s = spec::default_version(), cxx::source_location loc = cxx::source_location::current()); } ``` 文字列をTOMLファイルとしてパースします。 失敗した場合は`toml::syntax_error`が送出されます。 `basic_value`の持つ型情報は`template`で、TOML言語のバージョンは`toml::spec`で指定します。 第三引数の`cxx::source_location`を手動で設定する必要は通常ありません。 `std::source_location`, `std::experimental::source_location`, `__builtin_FILE`のいずれかが利用可能な場合、 `parse_str`が呼ばれた地点の情報が位置情報として保存されます。 # `try_parse` 与えられたファイルの内容をパースし、成功した場合は`toml::basic_value`を、失敗した場合は`std::vector`を返します。 `basic_value`の持つ型情報は`template`で、TOML言語のバージョンは`toml::spec`で指定します。 {{< hint warning >}} `try_parse`は`parse`と異なり`syntax_error`などのtoml11で定義された例外は投げませんが、 標準ライブラリから送出される例外はそのまま送出されることに注意してください。 例えば、`std::ifstream`の内部で起きたエラーや、`std::vector`でのメモリ枯渇などは例外を送出します。 {{< /hint >}} ### `try_parse(std::string filename, toml::spec)` ```cpp namespace toml { template result, std::vector> try_parse(std::string fname, spec s = spec::default_version()); } ``` ファイル名を受け取って、その内容をパースします。 パースに失敗した場合、エラー型である`std::vector`を持つ`result`が返されます。 成功した場合、`basic_value`を持つ`result`が返されます。 ### `try_parse(const char (&)[N] filename, toml::spec)` ```cpp namespace toml { template result, std::vector> try_parse(const char (&fname)[N], spec s = spec::default_version()); } ``` 文字列リテラルをファイル名として受け取って、その内容をパースします。 パースに失敗した場合、エラー型である`std::vector`を持つ`result`が返されます。 成功した場合、`basic_value`を持つ`result`が返されます。 ### `try_parse(std::filesystem::path, toml::spec)` ```cpp namespace toml { template result, std::vector> try_parse(const std::filesystem::path& fpath, spec s = spec::default_version()); } ``` ファイルパスを受け取って、その内容をパースします。 パースに失敗した場合、エラー型である`std::vector`を持つ`result`が返されます。 成功した場合、`basic_value`を持つ`result`が返されます。 ### `try_parse(std::istream&, std::string filename, toml::spec)` ```cpp namespace toml { template result, std::vector> try_parse(std::istream& is, std::string fname = "unknown file", spec s = spec::default_version()); } ``` `std::istream&`を受け取ってその内容をパースします。 標準ライブラリが改行文字を自動変換することによるファイルサイズと文字数との不整合を避けるため、 `std::ios::binary`を使ってバイナリモードで開いてください。 ファイル名の情報は第二引数で受け取ります。ファイル名が渡されなかった場合、`"unknown file"`になります。 パースに失敗した場合、エラー型である`std::vector`を持つ`result`が返されます。 成功した場合、`basic_value`を持つ`result`が返されます。 ### `try_parse(FILE*, std::string filename, toml::spec)` ```cpp namespace toml { template result, std::vector> try_parse(FILE* fp, std::string filename, spec s = spec::default_version()); } ``` `FILE*`を受け取って、そのファイルの内容をパースします。 標準ライブラリが改行文字を自動変換することによるファイルサイズと文字数との不整合を避けるため、 `fopen`には`"rb"`などを渡してバイナリモードで開いてください。 パースに失敗した場合、エラー型である`std::vector`を持つ`result`が返されます。 成功した場合、`basic_value`を持つ`result`が返されます。 ### `try_parse(std::vector, std::string filename, toml::spec)` ```cpp namespace toml { template result, std::vector> try_parse(std::vector content, std::string filename, spec s = spec::default_version()); } ``` バイト列を受け取って、その内容をTOMLファイルとしてパースします。 パースに失敗した場合、エラー型である`std::vector`を持つ`result`が返されます。 成功した場合、`basic_value`を持つ`result`が返されます。 # `try_parse_str` ### `try_parse_str(std::string, toml::spec)` ```cpp namespace toml { template result, std::vector> try_parse_str(std::string content, spec s = spec::default_version(), cxx::source_location loc = cxx::source_location::current()); } ``` 文字列をTOMLファイルとしてパースし、成功した場合は`toml::basic_value`を、失敗した場合は`std::vector`を返します。 `parse_str`と異なり、`syntax_error`を送出せず、エラー情報を`result`の失敗型として返します。 `std::source_location`, `std::experimental::source_location`, `__builtin_FILE`のどれかが利用可能な場合、それを位置情報に記録します。 第三引数の`cxx::source_location`を手動で設定する必要は通常ありません。 `std::source_location`, `std::experimental::source_location`, `__builtin_FILE`のいずれかが利用可能な場合、 `parse_str`が呼ばれた地点の情報が位置情報として保存されます。 {{< hint warning >}} `try_parse`は`parse`と異なり`syntax_error`などのtoml11で定義された例外は投げませんが、 標準ライブラリから送出される例外はそのまま送出されることに注意してください。 例えば、`std::ifstream`の内部で起きたエラーや、`std::vector`でのメモリ枯渇などは例外を送出します。 {{< /hint >}} # `syntax_error` ```cpp namespace toml { struct syntax_error final : public ::toml::exception { public: syntax_error(std::string what_arg, std::vector err); ~syntax_error() noexcept override = default; const char* what() const noexcept override; std::vector const& errors() const noexcept }; } ``` TOML言語の文法エラーが発見された場合に送出される例外です。 `parse`からは送出されますが、`try_parse`からは送出されません。 # `file_io_error` ```cpp namespace toml { struct file_io_error final : public ::toml::exception { public: file_io_error(const std::string& msg, const std::string& fname); file_io_error(int errnum, const std::string& msg, const std::string& fname); ~file_io_error() noexcept override = default; const char* what() const noexcept override; bool has_errno() const noexcept; int get_errno() const noexcept; }; } ``` ファイルの内容を読むのに失敗した場合に送出される例外です。 `FILE*`を使ってファイルを読み込んだ場合は`errno`が設定されます。 ### `has_errno` `std::ifstream`が失敗した場合は`errno`は設定されません。 このとき、`has_errno`は`false`になります。 ### `get_errno` 特に`FILE*`を渡していた場合に、`errno`の値を取得します。 `has_errno`が`false`の場合は`0`となります。 # 関連項目 - [error_info.hpp]({{}}) - [result.hpp]({{}}) - [spec.hpp]({{}}) - [value.hpp]({{}}) toml11-4.1.0/docs/content.ja/docs/reference/result.md000066400000000000000000000270401464712047700223710ustar00rootroot00000000000000+++ title = "result.hpp" type = "docs" +++ # result.hpp `result.hpp`は、成功値か失敗値かのどちらかを持つ`result`型を定義します。 これは、例外を投げない`toml::try_parse`の返り値として使用されます。 # success 成功値を持つ型です。 ```cpp namespace toml { template struct success { using value_type = T; explicit success(value_type v); ~success() = default; success(const success&) = default; success(success&&) = default; success& operator=(const success&) = default; success& operator=(success&&) = default; template explicit success(U&& v); template explicit success(success v); value_type& get() noexcept; value_type const& get() const noexcept; }; template success::type> ok(T&& v); template success ok(const char (&literal)[N]) } ``` ## メンバ型 ```cpp using value_type = T; ``` 成功値の型です。 ## メンバ関数 ### コンストラクタ ```cpp explicit success(value_type v); ``` `value_type`を受け取って構築します。 ```cpp template explicit success(U&& v); ``` `value_type`に変換可能な他の型を受け取って構築します。 ```cpp template explicit success(success v); ``` `value_type`に変換可能な他の型を持つ`success`型を受け取って構築します。 ### `get()` ```cpp value_type& get() noexcept; value_type const& get() const noexcept; ``` 値にアクセスします。 ## 非メンバ関数 ### `ok(T)` ```cpp template success::type> ok(T&& v); template success ok(const char (&literal)[N]) ``` 成功値から`success`型を構築して返します。 文字列リテラルを与えた場合は、`std::string`にして返します。 # `success>` `success`の特殊化。成功値が参照であるときに使用されます。 ```cpp namespace toml { template struct success> { using value_type = T; explicit success(std::reference_wrapper v) noexcept; value_type& get() noexcept; value_type const& get() const noexcept; }; } ``` ## メンバ型 ```cpp using value_type = T; ``` 成功値の型です。参照ではなく、`std::reference_wrapper`の`T`です。 ### `get()` ```cpp value_type& get() noexcept; value_type const& get() const noexcept; ``` 値にアクセスします。 # failure 失敗値を持つ型です。 ```cpp namespace toml { template struct failure { using value_type = T; explicit failure(value_type v); ~failure() = default; failure(const failure&) = default; failure(failure&&) = default; failure& operator=(const failure&) = default; failure& operator=(failure&&) = default; template explicit failure(U&& v); template explicit failure(failure v); value_type& get() noexcept; value_type const& get() const noexcept; }; template failure::type> err(T&& v); template failure err(const char (&literal)[N]); } ``` ## メンバ型 ```cpp using value_type = T; ``` 失敗値の型です。 ## メンバ関数 ### コンストラクタ ```cpp explicit failure(value_type v); ``` `value_type`を受け取って構築します。 ```cpp template explicit failure(U&& v); ``` `value_type`に変換可能な他の型を受け取って構築します。 ```cpp template explicit failure(failure v); ``` `value_type`に変換可能な他の型を持つ`failure`型を受け取って構築します。 ### `get()` ```cpp value_type& get() noexcept; value_type const& get() const noexcept; ``` 値にアクセスします。 ## 非メンバ関数 ### `err(T)` ```cpp template failure::type> err(T&& v); template failure err(const char (&literal)[N]); ``` 失敗値から`failure`型を構築して返します。 文字列リテラルを与えた場合は、`std::string`にして返します。 # `failure>` `failure`の特殊化。失敗値が参照であるときに使用されます。 ```cpp namespace toml { template struct failure> { using value_type = T; explicit failure(std::reference_wrapper v) noexcept; value_type& get() noexcept {return value.get();} value_type const& get() const noexcept {return value.get();} }; } ``` ## メンバ型 ```cpp using value_type = T; ``` 失敗値の型です。参照ではなく、`std::reference_wrapper`の`T`です。 ### `get()` ```cpp value_type& get() noexcept value_type const& get() const noexcept ``` 値にアクセスします。 # result 成功値か失敗値かのどちらかを持つ型です。 ```cpp namespace toml { template struct result { using success_type = success; using failure_type = failure; using value_type = typename success_type::value_type; using error_type = typename failure_type::value_type; result(success_type s); result(failure_type f); template result(success s); template result(failure f); result& operator=(success_type s); result& operator=(failure_type f); template result& operator=(success s); template result& operator=(failure f); ~result() noexcept; result(const result& other); result(result&& other); result& operator=(const result& other); result& operator=(result&& other); template result(result other); template result& operator=(result other); bool is_ok() const noexcept; bool is_err() const noexcept; explicit operator bool() const noexcept; value_type& unwrap(cxx::source_location loc = cxx::source_location::current()); value_type const& unwrap(cxx::source_location loc = cxx::source_location::current()) const; value_type& unwrap_or(value_type& opt) noexcept; value_type const& unwrap_or(value_type const& opt) const noexcept; error_type& unwrap_err(cxx::source_location loc = cxx::source_location::current()); error_type const& unwrap_err(cxx::source_location loc = cxx::source_location::current()) const; value_type& as_ok() noexcept; value_type const& as_ok() const noexcept; error_type& as_err() noexcept; error_type const& as_err() const noexcept; }; } ``` ## メンバ型 ### `success_type` `success`です。 ### `failure_type` `failure`です。 ### `value_type` 成功型`T`です。`success_type::value_type`のエイリアスです。 成功型`T`として`std::reference_wrapper`が渡された場合、その引数型の`U`になります。 ### `error_type` 失敗型`E`です。`failure_type::value_type`のエイリアスです。 失敗型`E`として`std::reference_wrapper`が渡された場合、その引数型の`F`になります。 ## メンバ関数 ### コンストラクタ ```cpp result() = delete; ``` `result`型はデフォルトでは構築できません。 成功型か失敗型を与える必要があります。 ```cpp result(success_type s); result(failure_type f); ``` 成功型、失敗型を受け取って構築します。 ```cpp template result(success s); template result(failure f); ``` `value_type`と`error_type`に変換可能な成功型、失敗型を受け取って構築します。 ```cpp template result(result other); template result& operator=(result other); ``` 変換可能な成功型、失敗型を持つ`result`から構築します。 ### コピー・ムーブコンストラクタ ```cpp result(const result& other); result(result&& other); ``` コピー・ムーブ構築可能です。 ### `operator=` ```cpp result& operator=(const result& other); result& operator=(result&& other); ``` コピー・ムーブ代入可能です。 ```cpp template result& operator=(success s); template result& operator=(failure f); ``` 変換可能な型を持つ成功型、失敗型は代入可能です。 ### `is_ok()` ```cpp bool is_ok() const noexcept; ``` 成功値を持っている場合`true`を、そうでない場合`false`を返します。 ### `is_err()` ```cpp bool is_err() const noexcept; ``` 失敗値を持っている場合`true`を、そうでない場合`false`を返します。 ### `operator bool()` ```cpp explicit operator bool() const noexcept; ``` 成功値を持っている場合`true`を、そうでない場合`false`を返します。 ### `unwrap()` ```cpp value_type& unwrap(cxx::source_location loc = cxx::source_location::current()); value_type const& unwrap(cxx::source_location loc = cxx::source_location::current()) const; ``` 成功値を取り出します。 成功値を持っていない場合、`toml::bad_result_access`が送出されます。 `std::source_location`または`std::experimental::source_location`が利用可能か、 あるいはコンパイラ拡張によって同等の情報が利用可能な場合、`what()`文字列に `unwrap()`が発生したソースコードのファイル名と行数が表示されます。 利用可能でない場合、位置情報は表示されません。 ### `unwrap_or()` ```cpp value_type& unwrap_or(value_type& opt) noexcept; value_type const& unwrap_or(value_type const& opt) const noexcept; ``` 成功値を持っていた場合それを、持っていなかった場合は引数で与えられた値を返します。 ### `unwrap_err()` ```cpp error_type& unwrap_err(cxx::source_location loc = cxx::source_location::current()); error_type const& unwrap_err(cxx::source_location loc = cxx::source_location::current()) const; ``` 失敗値を取り出します。 失敗値を持っていない場合、`toml::bad_result_access`が送出されます。 `std::source_location`または`std::experimental::source_location`が利用可能か、 あるいはコンパイラ拡張によって同等の情報が利用可能な場合、`what()`文字列に `unwrap()`が発生したソースコードのファイル名と行数が表示されます。 利用可能でない場合、位置情報は表示されません。 ### `as_ok()` ```cpp value_type& as_ok() noexcept; value_type const& as_ok() const noexcept; ``` チェックなしで成功値を返します。 成功値を持っていなかった場合、その動作は未定義となります。 ### `as_err()` ```cpp error_type& as_err() noexcept; error_type const& as_err() const noexcept; ``` チェックなしで失敗値を返します。 失敗値を持っていなかった場合、その動作は未定義となります。 # bad_result_access `result`の`unwrap`または`unwrap_err`で失敗した際に送出される例外です。 ```cpp namespace toml { struct bad_result_access : public ::toml::exception { public: explicit bad_result_access(const std::string& what_arg); virtual ~bad_result_access() noexcept override = default; virtual const char* what() const noexcept override; protected: std::string what_; }; } ``` toml11-4.1.0/docs/content.ja/docs/reference/serializer.md000066400000000000000000000037171464712047700232310ustar00rootroot00000000000000+++ title = "serializer.hpp" type = "docs" +++ # serializer.hpp # `format` シリアライズを行います。 ```cpp namespace toml { template std::string format(const basic_value& v, const spec s = spec::default_version()); template std::string format(const typename basic_value::key_type& k, const basic_value& v, const spec s = spec::default_version()); template std::string format(const std::vector::key_type>& ks, const basic_value& v, const spec s = spec::default_version()); } ``` フォーマット情報と`spec`が矛盾する場合、例えば`v1.0.0`で`table_format::multiline_oneline`が指定されているときなどは、`spec`が優先されます。 ### `format(v, spec)` `toml::value`を、それが持つフォーマット情報と`spec`に従ってフォーマットします。 `table_type`だった場合、それがルートであるとしてフォーマットします。 それ以外の値だった場合、値のみをフォーマットします。 ### `format(k, v, spec)` `toml::value`を、与えられたキーと同時にフォーマットします。 `v`はそのキー以下に定義されていると解釈されます。 ### `format([k,...], v, spec)` `v`はそのキー以下に定義されていると解釈されます。 キーが複数与えられた場合、再帰的に定義されたテーブルとして解釈されます。 # `serialization_error` シリアライズ中に発生したエラーを報告します。 ```cpp namespace toml { struct serialization_error final : public ::toml::exception { public: explicit serialization_error(std::string what_arg, source_location loc); ~serialization_error() noexcept override = default; const char* what() const noexcept override; source_location const& location() const noexcept; }; } ``` toml11-4.1.0/docs/content.ja/docs/reference/source_location.md000066400000000000000000000131731464712047700242450ustar00rootroot00000000000000+++ title = "source_location.hpp" type = "docs" +++ # source_location.hpp `source_location.hpp`では、TOMLファイル内のある領域を指すクラスが定義されます。 このクラスは、エラーメッセージで問題の箇所を指摘するために使われます。 # `toml::source_location` `source_location`は、TOMLファイル内のある領域を指すクラスです。 ```cpp namespace toml { struct source_location { public: explicit source_location(/* implementation-defined */); ~source_location() = default; source_location(source_location const&) = default; source_location(source_location &&) = default; source_location& operator=(source_location const&) = default; source_location& operator=(source_location &&) = default; bool is_ok() const noexcept; std::size_t length() const noexcept; std::size_t first_line_number() const noexcept; std::size_t first_column_number() const noexcept; std::size_t last_line_number() const noexcept; std::size_t last_column_number() const noexcept; std::string const& file_name() const noexcept; std::size_t num_lines() const noexcept; std::string const& first_line() const; std::string const& last_line() const; std::vector const& lines() const noexcept; }; template std::string format_location(const source_location& loc, const std::string& msg, const Ts& ... locs_and_msgs); } //toml ``` ## メンバ関数 ### コンストラクタ ```cpp explicit source_location(/* implementation-defined */); ``` `toml::source_location`を`toml::parse`または`_toml`リテラル以外で構築することはできません。 ### `is_ok()` ```cpp bool is_ok() const noexcept; ``` `source_location`が有効な値を保持している場合、`true`を、そうでない場合`false`を返します。 `toml::parse`や`_toml`リテラル以外から構築した`toml::value`の`location()`の結果は、指す対象がないため、`is_ok`は`false`を返します。 ### `length()` ```cpp std::size_t length() const noexcept; ``` `source_location`が指す領域の長さを返します。 有効な値を保持していない場合、`0`を返します。 ### `first_line_number()` ```cpp std::size_t first_line_number() const noexcept; ``` `source_location`が指す領域の最初の行の行番号を返します。 有効な値を保持していない場合、`1`を返します。 ### `first_column_number()` ```cpp std::size_t first_column_number() const noexcept; ``` `source_location`が指す領域の最初の列の列番号を返します。 有効な値を保持していない場合、`1`を返します。 ### `last_line_number()` ```cpp std::size_t last_line_number() const noexcept; ``` `source_location`が指す領域の最後の行の行番号を返します。 有効な値を保持していない場合、`1`を返します。 ### `last_column_number()` ```cpp std::size_t last_column_number() const noexcept; ``` `source_location`が指す領域の最後の列の列番号を返します。 有効な値を保持していない場合、`1`を返します。 ### `file_name()` ```cpp std::string const& file_name() const noexcept; ``` `source_location`が指す領域を含むファイルのファイル名を返します。 有効な値を保持していない場合、`"unknown file"`を返します。 ### `num_lines()` ```cpp std::size_t num_lines() const noexcept; ``` `source_location`が指す領域の行数を返します。 有効な値を保持していない場合、`0`を返します。 ### `first_line()` ```cpp std::string const& first_line() const; ``` `source_location`が指す領域の最初の行を返します。 有効な値を保持していない場合、`std::out_of_range`例外が送出されます。 ### `last_line()` ```cpp std::string const& last_line() const; ``` `source_location`が指す領域の最後の行を返します。 有効な値を保持していない場合、`std::out_of_range`例外が送出されます。 ### `lines()` ```cpp std::vector const& lines() const noexcept; ``` `source_location`が指す領域の全ての行を返します。 有効な値を保持していない場合、空の`std::vector`への参照を返します。 ## 非メンバ関数 ### `format_location` ```cpp template std::string format_location(const source_location& loc, const std::string& msg, const Ts& ... locs_and_msgs); ``` `source_location`が指す箇所と、それについてのメッセージを以下のようにフォーマットします。 ``` -> {filename.toml} | 1 | a = 42 | ^-- {message} ``` この時、色付けがONになっている場合、ANSIエスケープシーケンスによって色情報が追加されます。 locs_and_msgsが複数個ある場合、それらは`const source_location&`と`const std::string&`の順である必要があります。 #### 例:複数の`source_location`と`std::string` 複数の`source_location`と`std::string`を渡した場合、以下のようにフォーマットされます。 ```cpp source_location& loc0; source_location& loc1; source_location& loc2; std::string msg0; std::string msg1; std::string msg2; format_location(loc0, msg0, loc1, msg1, loc2, msg2); ``` ``` -> {filename0.toml} | 1 | a = 42 | ^-- {message0} | -> {filename1.toml} | 2 | b = 3.14 | ^-- {message1} | -> {filename2.toml} | 3 | c = "foo" | ^-- {message2} ``` # 関連項目 - [error_info.hpp]({{}}) - [value.hpp]({{}}) toml11-4.1.0/docs/content.ja/docs/reference/spec.md000066400000000000000000000210221464712047700217770ustar00rootroot00000000000000+++ title = "spec.hpp" type = "docs" +++ # spec.hpp `spec.hpp`では、TOMLのバージョンを指定するためのクラスが定義されます。 # `toml::semantic_version` `semantic_version`は、バージョン情報を格納するクラスです。 ```cpp namespace toml { struct semantic_version { constexpr semantic_version(std::uint32_t mjr, std::uint32_t mnr, std::uint32_t p) noexcept; std::uint32_t major; std::uint32_t minor; std::uint32_t patch; }; constexpr semantic_version make_semver(std::uint32_t major, std::uint32_t minor, std::uint32_t patch) noexcept; constexpr bool operator==(const semantic_version&, const semantic_version&) noexcept; constexpr bool operator!=(const semantic_version&, const semantic_version&) noexcept; constexpr bool operator< (const semantic_version&, const semantic_version&) noexcept; constexpr bool operator<=(const semantic_version&, const semantic_version&) noexcept; constexpr bool operator> (const semantic_version&, const semantic_version&) noexcept; constexpr bool operator>=(const semantic_version&, const semantic_version&) noexcept; std::ostream& operator<<(std::ostream& os, const semantic_version& ver); } //toml ``` ## メンバ関数 ### コンストラクタ ```cpp constexpr semantic_version(std::uint32_t mjr, std::uint32_t mnr, std::uint32_t p) noexcept; ``` `major`, `minor`, `patch`バージョンを指定して構築します。 ## 非メンバ関数 ### 比較演算子 ```cpp constexpr bool operator==(const semantic_version&, const semantic_version&) noexcept; constexpr bool operator!=(const semantic_version&, const semantic_version&) noexcept; constexpr bool operator< (const semantic_version&, const semantic_version&) noexcept; constexpr bool operator<=(const semantic_version&, const semantic_version&) noexcept; constexpr bool operator> (const semantic_version&, const semantic_version&) noexcept; constexpr bool operator>=(const semantic_version&, const semantic_version&) noexcept; ``` semantic versioningに従って比較します。 ### ストリーム演算子 ```cpp std::ostream& operator<<(std::ostream& os, const semantic_version& ver); ``` `{major}.{minor}.{patch}`の形式で出力します。 ### `to_string` ```cpp std::string to_string(const semantic_version& ver); ``` `{major}.{minor}.{patch}`の形式で文字列化します。 # `toml::spec` `spec`は、TOMLのバージョン情報を格納するクラスです。 ```cpp struct spec { constexpr static spec default_version() noexcept; constexpr static spec v(std::uint32_t mjr, std::uint32_t mnr, std::uint32_t p) noexcept; constexpr explicit spec(const semantic_version& semver) noexcept; semantic_version version; // toml version // diff from v1.0.0 -> v1.1.0 bool v1_1_0_allow_control_characters_in_comments; bool v1_1_0_allow_newlines_in_inline_tables; bool v1_1_0_allow_trailing_comma_in_inline_tables; bool v1_1_0_allow_non_english_in_bare_keys; bool v1_1_0_add_escape_sequence_e; bool v1_1_0_add_escape_sequence_x; bool v1_1_0_make_seconds_optional; // library extensions bool ext_hex_float; // allow hex float bool ext_num_suffix; // allow number suffix bool ext_null_value; // allow null value }; ``` ## メンバ関数 ### コンストラクタ ```cpp constexpr explicit spec(const semantic_version& semver) noexcept; ``` 指定されたTOMLバージョンで`spec`を構築します。 TOML v1.0.0と、TOML v1.1.0に対応しています。 ### `default_version()` ```cpp constexpr static spec default_version() noexcept; ``` デフォルトのバージョンで`spec`を構築します。 `toml::parse`、`toml::format`でのデフォルト値として使われます。 toml11 v4.0.0での値は、v1.0.0です。 ### `v(major, minor, patch)` ```cpp constexpr static spec v(std::uint32_t mjr, std::uint32_t mnr, std::uint32_t p) noexcept; ``` 指定されたバージョンで`spec`を構築します。 ## メンバ変数 各フラグは機能追加がされたバージョンを指定されたとき、自動的に`true`になります。 変更して渡すことで、`toml::parse`、`toml::format`の挙動を変更できます。 {{}} TOML v1.1.0の一部の機能にはかなり長い議論が続いており、まだ差し戻される可能性があります。 実際に差し戻された場合、toml11はマイナーバージョンアップでそれらの機能を削除、もしくは対応するそれ以降のバージョンに移動します。 そのような意味で、将来のバージョンに関する機能は全て不安定なものと考えてください。 {{}} ### 例 ```cpp auto spec = toml::spec::v(1, 0, 0); // v1.0.0の機能に追加して、inline table内の改行を許可する。 // それ以外のv1.1.0の機能は有効化されない。 spec.v1_1_0_allow_newlines_in_inline_tables = true; auto input = toml::parse("input_file.toml", spec); ``` ### `v1_1_0_allow_control_characters_in_comments` ```cpp bool v1_1_0_allow_control_characters_in_comments; ``` ほとんどの制御文字をコメントに含むことを許可します。 toml v1.1.0で追加。 ### `v1_1_0_allow_newlines_in_inline_tables` ```cpp bool v1_1_0_allow_newlines_in_inline_tables; ``` inline table内で改行することを許可します。 toml v1.1.0で追加。 ### `v1_1_0_allow_trailing_comma_in_inline_tables` ```cpp bool v1_1_0_allow_trailing_comma_in_inline_tables; ``` inline table内での末尾コンマを許可します。 toml v1.1.0で追加。 ### `v1_1_0_add_escape_sequence_e` ```cpp bool v1_1_0_add_escape_sequence_e; ``` `\e`でESC文字を指定できるようになります。 toml v1.1.0で追加。 ### `v1_1_0_add_escape_sequence_x` ```cpp bool v1_1_0_add_escape_sequence_x; ``` `\xHH`で1バイトの文字を指定できるようになります。 toml v1.1.0で追加。 ### `v1_1_0_make_seconds_optional` ```cpp bool v1_1_0_make_seconds_optional; ``` 時刻での秒数指定を省略可能にします。 指定されなかった秒数は`0`で初期化されます。 toml v1.1.0で追加。 ### `ext_hex_float` ```cpp bool ext_hex_float; ``` toml11限定の言語拡張です。 どのバージョンを指定しても、`false`で初期化されます。 使用する際は個別に`true`にしてください。 浮動小数点数の16進数表記を許可します。 16進数表記は`printf`で`%a/%A`を指定した場合に準拠します。 ``` hexf = 0xC0FFEEp-10 ``` `toml::format` は、渡された `toml::spec` で `ext_hex_format` が `true` の場合のみ 16進表記でフォーマットします。 フォーマット情報で `hex` が指定されているにも関わらず `toml::format` に渡された `toml::spec` の `ext_hex_float` が `false` だった場合、16進数指定は無視され、 10進表記で最大の精度で出力されます。 ### `ext_num_suffix` ```cpp bool ext_num_suffix; ``` toml11限定の言語拡張です。 どのバージョンを指定しても、`false`で初期化されます。 使用する際は個別に`true`にしてください。 10進数の整数と浮動小数点数に接尾辞を追加します。型を問わず、16進や8進、2進表記には適用されません。 数値と接尾辞の間は`_`で区切られている必要があります。 数値部分との区別のため、接尾辞は数値で始まることはできません。 ``` distance = 10_m # valid distance = 10_2m # invalid distance = 10_2_m # valid ``` 接尾辞は `std::string suffix` としてフォーマット情報に保持されます。 数値部分とを分ける `_` は `suffix` に含まれません。 ```cpp toml::value distance = toml::find(input, "distance"); assert(distance.as_integer_fmt().suffix == std::string("m")); ``` `toml::format` は、渡された `toml::spec` の `ext_num_suffix` が `true` の場合のみ これをフォーマットします。 `suffix`は以下のような文法を持ちます。 ```abnf non-digit-graph = ALPHA / non-ascii graph = ALPHA / DIGIT / non-ascii suffix = _ non-digit-graph *( graph / ( _ graph ) ) ``` ### `ext_null_value` ```cpp bool ext_null_value; ``` toml11限定の言語拡張です。 値として `null` を許可します。 `null` を指定された `toml::value` は値を持たず、 `is_empty()` が `true` になります。 `toml::format` は、渡された `toml::spec` で `ext_null_value` が `true` の場合のみ `null` としてフォーマットします。 そうでない場合、 `toml::format` がエラーで終了します。 toml11-4.1.0/docs/content.ja/docs/reference/toml.md000066400000000000000000000005661464712047700220320ustar00rootroot00000000000000+++ title = "toml.hpp" type = "docs" +++ # toml.hpp `toml.hpp`は、他の全てのヘッダを `include` します。 これによって、toml11の全機能が使用可能になります。 このヘッダファイルと `toml_fwd.hpp` は `${TOML11_INCLUDE_DIR}/` 以下に、 他のヘッダファイルは `${toml11_include_dir}/toml11/` 以下にあります。 toml11-4.1.0/docs/content.ja/docs/reference/toml_fwd.md000066400000000000000000000015701464712047700226660ustar00rootroot00000000000000+++ title = "toml_fwd.hpp" type = "docs" +++ # toml_fwd.hpp `toml_fwd.hpp`は、toml11で定義される構造体の前方宣言と、マクロ定義を持ちます。 toml11の構造体についての前方宣言しか必要なく実装が必要ない場合、 `toml.hpp` のかわりにこちらを `include` することでコンパイル時間を短縮できます。 {{}} このファイルには前方宣言しか含まれていないため、 `toml::basic_value::table_type` として定義される `toml::table` と、同様に定義される `toml::array` は使用できません。 それらには `basic_value` の実装が必要だからです。 {{}} このヘッダファイルと `toml.hpp` は `${TOML11_INCLUDE_DIR}/` 以下に、 他のヘッダファイルは `${TOML11_INCLUDE_DIR}/toml11/` 以下にあります。 toml11-4.1.0/docs/content.ja/docs/reference/types.md000066400000000000000000000117711464712047700222230ustar00rootroot00000000000000+++ title = "types.hpp" type = "docs" +++ # types.hpp 型情報を与えるクラスが定義されます。 # `type_config` `type_config`は、`toml::basic_value`に与えられるパラメータをまとめた型です。 `toml::basic_value`内で異なる型を使用する場合、これを別に定義して渡します。 記載のある要素は全て必須の要素です。 通常のストリーム演算子を使用できない数値型を使用する場合、`read_int`、`read_float`に相当するものを定義し、置き換えてください。 ```cpp namespace toml { struct type_config { using comment_type = preserve_comments; using boolean_type = bool; using integer_type = std::int64_t; using floating_type = double; using string_type = std::string; template using array_type = std::vector; template using table_type = std::unordered_map; static result parse_int(const std::string& str, const source_location src, const std::uint8_t base); static result parse_float(const std::string& str, const source_location src, const bool is_hex); }; using value = basic_value; using table = typename value::table_type; using array = typename value::array_type; } // toml ``` ## `static` メンバ関数 ### `parse_int(str, src, base)` ```cpp static result parse_int(const std::string& str, const source_location src, const std::uint8_t base); ``` 通常のストリーム演算子などを使用できない型を`integer_type`として使用する場合、この関数を実装してください。 `str`には、prefix、(`0x`などの場合)leading zero、underscoreが取り除かれた文字列が渡されます。 `src`には、その文字列が定義されていた箇所を指す`source_location`が渡されます。 `base`には、`10`, `2`, `8`, `16`のいずれかが渡されます。 ### `parse_float(str, src, is_hex)` ```cpp static result parse_float(const std::string& str, const source_location src, const bool is_hex); ``` 通常のストリーム演算子などを使用できない型を`floating_type`として使用する場合、この関数を実装してください。 `str`には、prefix、leading zero、underscoreが取り除かれた文字列が渡されます。 `src`には、その文字列が定義されていた箇所を指す`source_location`が渡されます。 `is_hex`には、フォーマットが`hexfloat`であるかどうかが渡されます。`hexfloat`拡張を使用しない場合は使われないので、実装する必要はありません。 `hexfloat`拡張に関しては、[spec.hpp]({{}})を参照してください。 ## 非メンバ関数 ### `read_int` ```cpp template result read_int(const std::string& str, const source_location src, const std::uint8_t base); ``` デフォルトで使用される関数です。`std::istringstream`を使用してパースします。 `operator>>`と`std::hex`等のマニピュレータ、`std::numeric_limits`が定義されている場合(`boost::multiprecision`など)、特に変更なしにこれを使用できます。 ### `read_float` ```cpp template result read_float(const std::string& str, const source_location src, const bool is_hex); ``` デフォルトで使用される関数です。decimalの場合は`std::istringstream`を、hexfloatの場合は`sscanf()`を使用してパースします。 `double`、`float`に対応しています。 それ以外の型の場合、`operator>>`が定義されていて、かつ`hex`を使用しないなら、これを使用できます。 # `ordered_type_config` `ordered_type_config`は、`toml::type_config`のテーブル型を`toml::ordered_map`に変更したものです。 また、`toml::ordered_value`エイリアスを定義します。 そのほかに`type_config`との違いはありません。 ```cpp namespace toml { struct ordered_type_config { using comment_type = preserve_comments; using boolean_type = bool; using integer_type = std::int64_t; using floating_type = double; using string_type = std::string; template using array_type = std::vector; template using table_type = ordered_map; static result parse_int(const std::string& str, const source_location src, const std::uint8_t base) { return read_int(str, src, base); } static result parse_float(const std::string& str, const source_location src, const bool is_hex) { return read_float(str, src, is_hex); } }; using ordered_value = basic_value; using ordered_table = typename ordered_value::table_type; using ordered_array = typename ordered_value::array_type; } // toml ``` toml11-4.1.0/docs/content.ja/docs/reference/value.md000066400000000000000000000666511464712047700222020ustar00rootroot00000000000000+++ title = "value.hpp" type = "docs" +++ # value.hpp `value.hpp`では、`basic_value`が定義されます。 # `toml::basic_value` `basic_value`は、TOMLの値を格納するクラスです。 ```cpp namespace toml { template class basic_value; // 以下はtypes.hppで定義される // using value = basic_value; // using table = typename basic_value::table_type; // using array = typename basic_value::array_type; template bool operator==(const basic_value&, const basic_value&); template bool operator!=(const basic_value&, const basic_value&); template bool operator< (const basic_value&, const basic_value&); template bool operator<=(const basic_value&, const basic_value&); template bool operator> (const basic_value&, const basic_value&); template bool operator>=(const basic_value&, const basic_value&); } //toml ``` ## メンバ型 以下のメンバ型が定義されます。 `TypeConfig`を使って、メンバ型を変更することができます。 参考: [types.hpp]({{}}) | 名前 | 定義 | |:-----------------------|:-------------------------------------| | `char_type` | `typename TypeConfig::char_type` | | `key_type` | `typename TypeConfig::string_type` | | `value_type` | `basic_value` | | `boolean_type` | `typename TypeConfig::boolean_type` | | `integer_type` | `typename TypeConfig::integer_type` | | `floating_type` | `typename TypeConfig::floating_type` | | `string_type` | `typename TypeConfig::string_type` | | `local_time_type` | `toml::local_time` | | `local_date_type` | `toml::local_date` | | `local_datetime_type` | `toml::local_datetime` | | `offset_datetime_type` | `toml::offset_datetime` | | `array_type` | `typename TypeConfig::template array_type`| | `table_type` | `typename TypeConfig::template table_type`| | `comment_type` | `typename TypeConfig::comment_type` | ## メンバ関数 ### デフォルトコンストラクタ ```cpp basic_value() noexcept ``` 空の`toml::value`を構築します。 構築された`toml::value`は空になります。 ### コピー・ムーブコンストラクタ ```cpp basic_value(const basic_value& v) basic_value(basic_value&& v) ``` 値、フォーマット情報、コメント、ファイル領域の全ての情報をコピー・ムーブします。 ### コピー・ムーブコンストラクタ(コメント指定) ```cpp basic_value(basic_value v, std::vector com) ``` コメントを上書きしながらコピー・ムーブします。 ### 変換コンストラクタ ```cpp template basic_value(basic_value other) template basic_value(basic_value other, std::vector com) ``` 異なる`type_config`を持つ`basic_value`からコピー・ムーブします。 ### コンストラクタ (boolean) ```cpp basic_value(boolean_type x) basic_value(boolean_type x, boolean_format_info fmt) basic_value(boolean_type x, std::vector com) basic_value(boolean_type x, boolean_format_info fmt, std::vector com) ``` `bool`と、そのフォーマット情報、コメントを受け取って構築します。 ### コンストラクタ (integer) ```cpp basic_value(integer_type x) basic_value(integer_type x, integer_format_info fmt) basic_value(integer_type x, std::vector com) basic_value(integer_type x, integer_format_info fmt, std::vector com) ``` `integer`と、そのフォーマット情報、コメントを受け取って構築します。 ### コンストラクタ(floating) ```cpp template を満たす */> basic_value(T x) template を満たす */> basic_value(T x, floating_format_info fmt) template を満たす */> basic_value(T x, std::vector com) template を満たす */> basic_value(T x, floating_format_info fmt, std::vector com) ``` `floating`と、そのフォーマット情報、コメントを受け取って構築します。 ### コンストラクタ(string) ```cpp basic_value(string_type x) basic_value(string_type x, string_format_info fmt) basic_value(string_type x, std::vector com) basic_value(string_type x, string_format_info fmt, std::vector com) basic_value(const string_type::value_type* x) basic_value(const string_type::value_type* x, string_format_info fmt) basic_value(const string_type::value_type* x, std::vector com) basic_value(const string_type::value_type* x, string_format_info fmt, std::vector com) // C++17以降 basic_value(string_view_type x) basic_value(string_view_type x, string_format_info fmt) basic_value(string_view_type x, std::vector com) basic_value(string_view_type x, string_format_info fmt, std::vector com) ``` `string`と、そのフォーマット情報、コメントを受け取って構築します。 `string_view_type`は、`string_type`と同じ`value_type`と`traits_type`を持ちます。 ### コンストラクタ(local_date) ```cpp basic_value(local_date_type x) basic_value(local_date_type x, local_date_format_info fmt) basic_value(local_date_type x, std::vector com) basic_value(local_date_type x, local_date_format_info fmt, std::vector com) ``` `local_date_type`と、そのフォーマット情報、コメントを受け取って構築します。 ### コンストラクタ(local_time) ```cpp basic_value(local_time_type x) basic_value(local_time_type x, local_time_format_info fmt) basic_value(local_time_type x, std::vector com) basic_value(local_time_type x, local_time_format_info fmt, std::vector com) template basic_value(const std::chrono::duration& x) template basic_value(const std::chrono::duration& x, local_time_format_info fmt) template basic_value(const std::chrono::duration& x, std::vector com) template basic_value(const std::chrono::duration& x, local_time_format_info fmt, std::vector com) ``` `local_time_type`と、そのフォーマット情報、コメントを受け取って構築します。 `std::chrono::duration`は、`00:00:00`からの時間幅として構築します。 ### コンストラクタ(local_datetime) ```cpp basic_value(local_datetime_type x) basic_value(local_datetime_type x, local_date_format_info fmt) basic_value(local_datetime_type x, std::vector com) basic_value(local_datetime_type x, local_date_format_info fmt, std::vector com) ``` `local_datetime_type`と、そのフォーマット情報、コメントを受け取って構築します。 ### コンストラクタ(offset_datetime) ```cpp basic_value(offset_datetime_type x) basic_value(offset_datetime_type x, offset_date_format_info fmt) basic_value(offset_datetime_type x, std::vector com) basic_value(offset_datetime_type x, offset_date_format_info fmt, std::vector com) basic_value(std::chrono::system_clock::time_point x) basic_value(std::chrono::system_clock::time_point x, offset_date_format_info fmt) basic_value(std::chrono::system_clock::time_point x, std::vector com) basic_value(std::chrono::system_clock::time_point x, offset_date_format_info fmt, std::vector com) ``` `offset_datetime_type`と、そのフォーマット情報、コメントを受け取って構築します。 `std::chrono::system_clock::time_point`の場合、それが指す時点として構築します。 ### コンストラクタ(array) ```cpp basic_value(array_type x) basic_value(array_type x, integer_format_info fmt) basic_value(array_type x, std::vector com) basic_value(array_type x, integer_format_info fmt, std::vector com) template basic_value(T x) template basic_value(T x, array_format_info fmt) template basic_value(T x, std::vector com) template basic_value(T x, array_format_info fmt, std::vector com) ``` `array`と、そのフォーマット情報、コメントを受け取って構築します。 `array-like`は、以下の条件を満たす型です。 - `T::iterator` を持つ。 - `T::value_type` を持つ。 - `T::key_type` を持た**ない**。 - `T::mapped_type` を持た**ない**。 - `std::string` では**ない**。 - `std::string_view` では**ない**。(C++17以降) ### コンストラクタ(table) ```cpp basic_value(table_type x) basic_value(table_type x, integer_format_info fmt) basic_value(table_type x, std::vector com) basic_value(table_type x, integer_format_info fmt, std::vector com) template basic_value(T x) template basic_value(T x, table_format_info fmt) template basic_value(T x, std::vector com) template basic_value(T x, table_format_info fmt, std::vector com) ``` `table`と、そのフォーマット情報、コメントを受け取って構築します。 `table-like`は、以下の条件を満たす型です。 - `T::iterator` を持つ。 - `T::value_type` を持つ。 - `T::key_type` を持つ。 - `T::mapped_type` を持つ。 ### コンストラクタ(user-defined) ```cpp templateが定義されていること */> basic_value(const T& ud); templateが定義されていること */> basic_value(const T& ud, std::vector com); templateは定義されておらず、T{}.into_toml()が存在すること */> basic_value(const T& ud); templateは定義されておらず、T{}.into_toml()が存在すること */> basic_value(const T& ud, std::vector com); ``` `toml::into` が定義されていた場合、 `toml::into(ud)` の結果から構築します。 `toml::into` が定義されておらず、 `T` に `into_toml()` メンバ関数が定義されていた場合、 `ud.into_toml()`の結果から構築します。 ----- ### `operator=(basic_value)` ```cpp basic_value& operator=(const basic_value& v) basic_value& operator=(basic_value&& v) template basic_value& operator=(basic_value other) ``` 右辺の`basic_value`を代入します。 ### `operator=(T)` ```cpp template basic_value& operator=(T x) ``` Tに対応する値を代入します。 `source_location`の指す内容は破棄されます。 もし同じ型の値を持っていたなら、元のフォーマット情報が保持されます。 ----- ### `is()` ```cpp bool is() const noexcept ``` #### 条件 `T`は厳密にTOML型であること。つまり、値に対応する`toml::value::xxx_type`のいずれかであること。 #### 戻り値 格納している型が`T`と一致した場合`true`を、そうでない場合は`false`を返します。 ----- ### `is(toml::value_t)` ```cpp bool is(toml::value_t t) const noexcept ``` #### 戻り値 格納している型のタグが`t`と一致した場合`true`を、そうでない場合は`false`を返します。 ----- ### `is_xxx()` ```cpp bool is_boolean() const noexcept; bool is_integer() const noexcept; bool is_floating() const noexcept; bool is_string() const noexcept; bool is_offset_datetime() const noexcept; bool is_local_datetime() const noexcept; bool is_local_date() const noexcept; bool is_local_time() const noexcept; bool is_array() const noexcept; bool is_table() const noexcept; ``` #### 戻り値 格納している型がその型である場合`true`を、そうでない場合は`false`を返します。 ----- ### `is_empty()` ```cpp bool is_empty() const noexcept; ``` #### 戻り値 デフォルト構築され値が代入されていない場合`true`を、そうでない場合は`false`を返します。 ### `is_array_of_tables()` ```cpp bool is_array_of_tables() const noexcept; ``` #### 戻り値 格納している型が配列であり、空ではなく、全要素がテーブルの場合は`true`を、そうでない場合は`false`を返します。 ----- ### `type()` ```cpp toml::value_t type() const noexcept ``` #### 戻り値 格納している型に対応するタグを返します。 ----- ### `as_xxx()` ```cpp boolean_type const& as_boolean () const; integer_type const& as_integer () const; floating_type const& as_floating () const; string_type const& as_string () const; offset_datetime_type const& as_offset_datetime() const; local_datetime_type const& as_local_datetime () const; local_date_type const& as_local_date () const; local_time_type const& as_local_time () const; array_type const& as_array () const; table_type const& as_table () const; boolean_type & as_boolean (); integer_type & as_integer (); floating_type & as_floating (); string_type & as_string (); offset_datetime_type& as_offset_datetime(); local_datetime_type & as_local_datetime (); local_date_type & as_local_date (); local_time_type & as_local_time (); array_type & as_array (); table_type & as_table (); ``` #### 戻り値 指定された型への参照を返します。 #### 例外 格納されている値の型が指定と異なる場合、`toml::type_error`を送出します。 ----- ### `as_xxx(std::nothrow)` `std::nothrow`オブジェクトを渡して呼び出します。 ```cpp boolean_type const& as_boolean (const std::nothrow_t&) const noexcept; integer_type const& as_integer (const std::nothrow_t&) const noexcept; floating_type const& as_floating (const std::nothrow_t&) const noexcept; string_type const& as_string (const std::nothrow_t&) const noexcept; offset_datetime_type const& as_offset_datetime(const std::nothrow_t&) const noexcept; local_datetime_type const& as_local_datetime (const std::nothrow_t&) const noexcept; local_date_type const& as_local_date (const std::nothrow_t&) const noexcept; local_time_type const& as_local_time (const std::nothrow_t&) const noexcept; array_type const& as_array (const std::nothrow_t&) const noexcept; table_type const& as_table (const std::nothrow_t&) const noexcept; boolean_type & as_boolean (const std::nothrow_t&) noexcept; integer_type & as_integer (const std::nothrow_t&) noexcept; floating_type & as_floating (const std::nothrow_t&) noexcept; string_type & as_string (const std::nothrow_t&) noexcept; offset_datetime_type& as_offset_datetime(const std::nothrow_t&) noexcept; local_datetime_type & as_local_datetime (const std::nothrow_t&) noexcept; local_date_type & as_local_date (const std::nothrow_t&) noexcept; local_time_type & as_local_time (const std::nothrow_t&) noexcept; array_type & as_array (const std::nothrow_t&) noexcept; table_type & as_table (const std::nothrow_t&) noexcept; ``` #### 戻り値 指定された型への参照を返します。 #### 備考 格納されている値の型が指定と異なる場合、未定義動作となります。 ----- ### `as_xxx_fmt()` フォーマット情報にアクセスします。 ```cpp boolean_format_info & as_boolean_fmt (); integer_format_info & as_integer_fmt (); floating_format_info & as_floating_fmt (); string_format_info & as_string_fmt (); offset_datetime_format_info& as_offset_datetime_fmt(); local_datetime_format_info & as_local_datetime_fmt (); local_date_format_info & as_local_date_fmt (); local_time_format_info & as_local_time_fmt (); array_format_info & as_array_fmt (); table_format_info & as_table_fmt (); boolean_format_info const& as_boolean_fmt () const; integer_format_info const& as_integer_fmt () const; floating_format_info const& as_floating_fmt () const; string_format_info const& as_string_fmt () const; offset_datetime_format_info const& as_offset_datetime_fmt() const; local_datetime_format_info const& as_local_datetime_fmt () const; local_date_format_info const& as_local_date_fmt () const; local_time_format_info const& as_local_time_fmt () const; array_format_info const& as_array_fmt () const; table_format_info const& as_table_fmt () const; ``` #### 戻り値 指定された型のフォーマット情報を持つ構造体への参照を返します。 #### 例外 格納されている値の型が指定と異なる場合、`toml::type_error`を送出します。 ----- ### `as_xxx_fmt(std::nothrow)` `std::nothrow`オブジェクトを渡して呼び出します。 ```cpp boolean_format_info & as_boolean_fmt (const std::nothrow_t&) noexcept; integer_format_info & as_integer_fmt (const std::nothrow_t&) noexcept; floating_format_info & as_floating_fmt (const std::nothrow_t&) noexcept; string_format_info & as_string_fmt (const std::nothrow_t&) noexcept; offset_datetime_format_info& as_offset_datetime_fmt(const std::nothrow_t&) noexcept; local_datetime_format_info & as_local_datetime_fmt (const std::nothrow_t&) noexcept; local_date_format_info & as_local_date_fmt (const std::nothrow_t&) noexcept; local_time_format_info & as_local_time_fmt (const std::nothrow_t&) noexcept; array_format_info & as_array_fmt (const std::nothrow_t&) noexcept; table_format_info & as_table_fmt (const std::nothrow_t&) noexcept; boolean_format_info const& as_boolean_fmt (const std::nothrow_t&) const noexcept; integer_format_info const& as_integer_fmt (const std::nothrow_t&) const noexcept; floating_format_info const& as_floating_fmt (const std::nothrow_t&) const noexcept; string_format_info const& as_string_fmt (const std::nothrow_t&) const noexcept; offset_datetime_format_info const& as_offset_datetime_fmt(const std::nothrow_t&) const noexcept; local_datetime_format_info const& as_local_datetime_fmt (const std::nothrow_t&) const noexcept; local_date_format_info const& as_local_date_fmt (const std::nothrow_t&) const noexcept; local_time_format_info const& as_local_time_fmt (const std::nothrow_t&) const noexcept; array_format_info const& as_array_fmt (const std::nothrow_t&) const noexcept; table_format_info const& as_table_fmt (const std::nothrow_t&) const noexcept; ``` #### 戻り値 指定された型のフォーマット情報を持つ構造体への参照を返します。 #### 備考 格納されている値の型が指定と異なる場合、未定義動作となります。 ----- ### `at(key)` ```cpp value_type& at(const key_type& key); value_type const& at(const key_type& key) const; ``` #### 戻り値 今の`value`を`table`にキャストしたあと、`key`によって指定される要素を返します。 #### 例外 もし格納している値が`table`ではなかった場合、`toml::type_error`を送出します。 もし格納している`table`が指定された要素を持っていなかった場合、`std::out_of_range`を送出します。 ----- #### `operator[](key)` ```cpp value_type& operator[](const key_type& k); ``` ##### 戻り値 今の`value`を`table`にキャストしたあと、`key`によって指定される要素への参照です。 もし`key`によって指定される要素が存在しない場合、デフォルト構築されます。 ##### 例外 もし格納している値が`table`ではなかった場合、`toml::type_error`を送出します。 ----- ### `count(key)` ```cpp std::size_t count(const key_type& key) const; ``` #### 戻り値 今の`value`を`table`にキャストしたあと、`key`に対応する要素が含まれていれば`1`、そうでなければ`0`を返します。 #### 例外 もし格納している値が`table`ではなかった場合、`toml::type_error`を送出します。 ----- ### `contains(key)` ```cpp bool contains(const key_type& key) const; ``` #### 戻り値 今の`value`を`table`にキャストしたあと、`key`に対応する要素が含まれていれば`true`、そうでなければ`false`を返します。 #### 例外 もし格納している値が`table`ではなかった場合、`toml::type_error`を送出します。 ----- ### `at(idx)` ```cpp value_type& at(const std::size_t idx); value_type const& at(const std::size_t idx) const; ``` #### 戻り値 今の`value`を`array`にキャストしたあと、`idx`によって指定される要素を返します。 #### 例外 もし格納している値が`array`ではなかった場合、`toml::type_error`を送出します。 もし格納している`array`が指定された要素を持っていなかった場合、`std::out_of_range`を送出します。 ----- ### `operator[](idx)` ```cpp value_type& operator[](const std::size_t idx) noexcept; value_type const& operator[](const std::size_t idx) const noexcept; ``` #### 戻り値 今の`value`を`array`にキャストしたあと、`idx`によって指定される要素への参照を返します。 #### 備考 一切のチェックを行いません。 もし格納している値が`array`ではなかった場合、あるいは`idx`によって指定される要素が存在しない場合、未定義動作となります。 ----- ### `push_back(value)` ```cpp void push_back(const value_type& x); void push_back(value_type&& x); ``` `value`を`array`にキャストしたのち、その`array`に対して`push_back`を実行します。 #### 戻り値 なし。 #### 例外 格納している値が`array`ではなかった場合、`toml::type_error`を送出します。 ----- ### `emplace_back(args...)` ```cpp template value_type& emplace_back(Ts&& ... args) ``` `value`を`array`にキャストしたのち、その`array`に対して`emplace_back`を実行します。 #### 戻り値 構築した値への参照。 #### 例外 格納している値が`array`ではなかった場合、`toml::type_error`を送出します。 ----- ### `size()` ```cpp std::size_t size() const; ``` #### 戻り値 今の`value`を`array`、`string`、`table`のどれかにキャストしたあと、その要素数を返します。 `string`の場合、文字数を返します。 #### 例外 格納している値が`array`, `string`, `table`のどれでもなかった場合、`toml::type_error`を送出します。 ----- ### `location()` ```cpp source_location location() const; ``` #### 戻り値 その`value`が定義されたTOML文書内の位置を表す`source_location`オブジェクトを返します。 もしTOML文書のパースによって構築されたものでない場合、どこも指示さない`source_location`を返します。 ----- ### `comments()` ```cpp comment_type const& comments() const noexcept; comment_type& comments() noexcept; ``` #### 戻り値 コメント用コンテナへの参照を返します。 ## 非メンバ関数 ### `operator==` ```cpp template bool operator==(const basic_value&, const basic_value&); ``` 以下を満たすとき、2つの`basic_value`は同値となります。 - TOML型が同一 - 含む値が同一 - コメントがバイト単位で同一 ### `operator!=` ```cpp template bool operator!=(const basic_value& lhs, const basic_value& rhs) { return !(lhs == rhs); } ``` ### `operator<` `array_type`と`table_type`が`operator<`を持っている場合のみ定義されます。 ```cpp template bool operator<(const basic_value&, const basic_value&); ``` 以下の順番で比較されます。 1. TOML型 2. TOML型が同一の場合、その値 3. TOML型とその値が同一の場合、コメント TOML型は、以下の順に小さい値を持ちます。 1. `toml::value_t::empty` 2. `toml::value_t::boolean` 3. `toml::value_t::integer` 4. `toml::value_t::floating` 5. `toml::value_t::string` 6. `toml::value_t::offset_datetime` 7. `toml::value_t::local_datetime` 8. `toml::value_t::local_date` 9. `toml::value_t::local_time` 10. `toml::value_t::array` 11. `toml::value_t::table` ### `operator<=` `array_type`と`table_type`が`operator<`を持っている場合のみ定義されます。 ```cpp template bool operator<=(const basic_value& lhs, const basic_value& rhs) { return (lhs < rhs) || (lhs == rhs); } ``` ### `operator>` `array_type`と`table_type`が`operator<`を持っている場合のみ定義されます。 ```cpp template bool operator>(const basic_value& lhs, const basic_value& rhs) { return !(lhs <= rhs); } ``` ### `operator>=` `array_type`と`table_type`が`operator<`を持っている場合のみ定義されます。 ```cpp template bool operator>=(const basic_value& lhs, const basic_value& rhs) { return !(lhs < rhs); } ``` # `toml::type_error` 型エラーの際に送出される例外です。 型エラーが生じた値の位置情報が格納されています。 ```cpp struct type_error final : public ::toml::exception { public: type_error(std::string what_arg, source_location loc); ~type_error() noexcept override = default; const char* what() const noexcept override; source_location const& location() const noexcept; }; ``` # `toml::make_error_info` ```cpp template error_info make_error_info( std::string title, const basic_value& v, std::string msg, Ts&& ... tail); ``` `basic_value` の `location()` を呼び出して、その `source_location` を [`make_error_info`]({{}}) に渡して `error_info` を作成します。 詳しくは [`error_info`]({{}}) を参照してください。 # `toml::format_error` ```cpp template std::string format_error(std::string title, const basic_value& v, std::string msg, Ts&& ... tail); ``` `basic_value` の `location()` を呼び出して、その `source_location` を [`format_error`]({{}}) に渡して `error_info` を作成し、それを文字列化して返します。 詳しくは [`error_info`]({{}}) を参照してください。 # 関連項目 - [comments.hpp]({{}}) - [source_location.hpp]({{}}) - [types.hpp]({{}}) - [visit.hpp]({{}}) toml11-4.1.0/docs/content.ja/docs/reference/value_t.md000066400000000000000000000016741464712047700225170ustar00rootroot00000000000000+++ title = "value_t.hpp" type = "docs" +++ # value_t.hpp 型情報を表す列挙型です。 # `value_t` `value_t`は、`toml::value`が持つ型情報を扱う際に使用します。 ```cpp namespace toml { enum class value_t : std::uint8_t { empty = 0, boolean = 1, integer = 2, floating = 3, string = 4, offset_datetime = 5, local_datetime = 6, local_date = 7, local_time = 8, array = 9, table = 10 }; std::ostream& operator<<(std::ostream& os, value_t t); std::string to_string(value_t t); } // toml ``` ## 非メンバ関数 ### ストリーム演算子 ```cpp std::ostream& operator<<(std::ostream& os, value_t t); ``` `value_t`の値を文字列化してストリームへ出力します。 ### `to_string` ```cpp std::string to_string(value_t t); ``` `value_t`の値を文字列化して返します。 toml11-4.1.0/docs/content.ja/docs/reference/version.md000066400000000000000000000012141464712047700225330ustar00rootroot00000000000000+++ title = "version.hpp" type = "docs" +++ # version.hpp `version.hpp`では、toml11とC++のバージョン情報に関係するマクロが定義されます。 ## マクロ ### `TOML11_VERSION_MAJOR` toml11のメジャーバージョンです。 ### `TOML11_VERSION_MINOR` toml11のマイナーバージョンです。 ### `TOML11_VERSION_PATCH` toml11のパッチバージョンです。 ## 関数 ### `license_notice` ```cpp namespace toml { const char* license_notice() noexcept; } ``` ライセンス条項を返します。 ソースコードを公開せずに頒布する際の利便性のために用意されています。 toml11-4.1.0/docs/content.ja/docs/reference/visit.md000066400000000000000000000044311464712047700222100ustar00rootroot00000000000000+++ title = "visit.hpp" type = "docs" +++ # visit.hpp `visit.hpp`では、`toml::visit`が定義されます。 # `toml::visit` ## 関数 ```cpp namespace toml { template /* Visitor を basic_valueの値で呼び出した際の返り値 */ visit(Visitor&& visitor, const basic_value& v); template /* Visitor を basic_valueの値で呼び出した際の返り値 */ visit(Visitor&& visitor, basic_value& v) template /* Visitor を basic_valueの値で呼び出した際の返り値 */ visit(Visitor&& visitor, basic_value&& v) } ``` `basic_value`が保持している型に対応する`Visitor`のオーバーロードを呼び出し、その結果を返します。 #### 条件 `Visitor`は、`basic_value`が保持している型のどれに対しても呼び出し可能な関数または関数オブジェクトでなければなりません。 また、それぞれのオーバーロードで返り値は同じであることが要求されます。 #### 例 ```cpp #include #include struct type_name_of { std::string operator()(const toml::value::boolean_type &) const {return "boolean";} std::string operator()(const toml::value::integer_type &) const {return "integer";} std::string operator()(const toml::value::floating_type &) const {return "floating";} std::string operator()(const toml::value::string_type &) const {return "string";} std::string operator()(const toml::value::local_time_type &) const {return "local_time";} std::string operator()(const toml::value::local_date_type &) const {return "local_date";} std::string operator()(const toml::value::local_datetime_type &) const {return "local_datetime";} std::string operator()(const toml::value::offset_datetime_type&) const {return "offset_datetime";} std::string operator()(const toml::value::array_type &) const {return "array";} std::string operator()(const toml::value::table_type &) const {return "table";} }; int main() { toml::value v(3.14); std::cout << toml::visit(type_name_of{}, v) << std::endl; // floating return 0; } ``` # 関連項目 - [value.hpp]({{}}) toml11-4.1.0/docs/themes/000077500000000000000000000000001464712047700150625ustar00rootroot00000000000000toml11-4.1.0/docs/themes/hugo-book/000077500000000000000000000000001464712047700167545ustar00rootroot00000000000000toml11-4.1.0/examples/000077500000000000000000000000001464712047700144635ustar00rootroot00000000000000toml11-4.1.0/examples/CMakeLists.txt000066400000000000000000000002651464712047700172260ustar00rootroot00000000000000add_subdirectory(boost_container) add_subdirectory(boost_multiprecision) add_subdirectory(parse_file) add_subdirectory(reflect) add_subdirectory(u8string) add_subdirectory(unicode) toml11-4.1.0/examples/boost_container/000077500000000000000000000000001464712047700176535ustar00rootroot00000000000000toml11-4.1.0/examples/boost_container/.gitignore000066400000000000000000000000121464712047700216340ustar00rootroot00000000000000container toml11-4.1.0/examples/boost_container/CMakeLists.txt000066400000000000000000000004301464712047700224100ustar00rootroot00000000000000find_package(Boost 1.81.0) if(Boost_FOUND) add_executable(container container.cpp) target_link_libraries(container PRIVATE toml11::toml11 Boost::boost) set_target_properties(container PROPERTIES RUNTIME_OUTPUT_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}") endif() toml11-4.1.0/examples/boost_container/README.md000066400000000000000000000005121464712047700211300ustar00rootroot00000000000000# container Use `boost::container::vector` and `boost::unordered_flat_map` as `array_type` and `table_type`. ## build Install [boost](https://boost.org), especially after 1.81.0 that supports `unordered_flat_map`. Then, build toml11 with `-DTOML11_BUILD_EXAMPLES=ON` ```cpp $ cmake -B ./build/ -DTOML11_BUILD_EXAMPLES=ON ``` toml11-4.1.0/examples/boost_container/container.cpp000066400000000000000000000023751464712047700223500ustar00rootroot00000000000000#include #include #include #include struct boost_config { using comment_type = toml::preserve_comments; using boolean_type = bool; using integer_type = std::int64_t; using floating_type = double; using string_type = std::string; template using array_type = boost::container::small_vector; template using table_type = boost::unordered_flat_map; static toml::result parse_int(const std::string& str, const toml::source_location src, const std::uint8_t base) { return toml::read_int(str, src, base); } static toml::result parse_float(const std::string& str, const toml::source_location src, const bool is_hex) { return toml::read_float(str, src, is_hex); } }; int main(int argc, char** argv) { if(argc != 2) { std::cerr << "usage: ./container " << std::endl; return 1; } const auto input = toml::parse_str(argv[1]); std::cout << toml::format(input) << std::endl; return 0; } toml11-4.1.0/examples/boost_multiprecision/000077500000000000000000000000001464712047700207375ustar00rootroot00000000000000toml11-4.1.0/examples/boost_multiprecision/.gitignore000066400000000000000000000000171464712047700227250ustar00rootroot00000000000000multiprecision toml11-4.1.0/examples/boost_multiprecision/CMakeLists.txt000066400000000000000000000004541464712047700235020ustar00rootroot00000000000000find_package(Boost 1.67.0) if(Boost_FOUND) add_executable(multiprecision multiprecision.cpp) target_link_libraries(multiprecision PRIVATE toml11::toml11 Boost::boost) set_target_properties(multiprecision PROPERTIES RUNTIME_OUTPUT_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}") endif() toml11-4.1.0/examples/boost_multiprecision/README.md000066400000000000000000000003651464712047700222220ustar00rootroot00000000000000# multiprecision Use `boost::multiprecision` as `integer_type` and `floating_type`. ## build Install [boost](https://boost.org). Then, build toml11 with `-DTOML11_BUILD_EXAMPLES=ON` ```cpp $ cmake -B ./build/ -DTOML11_BUILD_EXAMPLES=ON ``` toml11-4.1.0/examples/boost_multiprecision/multiprecision.cpp000066400000000000000000000036151464712047700245160ustar00rootroot00000000000000#include #include #include #include struct large_num_config { using comment_type = toml::preserve_comments; using boolean_type = bool; using integer_type = boost::multiprecision::cpp_int; using floating_type = boost::multiprecision::cpp_bin_float_oct; using string_type = std::string; template using array_type = std::vector; template using table_type = std::unordered_map; static toml::result parse_int(const std::string& str, const toml::source_location src, const std::uint8_t base) { return toml::read_int(str, src, base); } static toml::result parse_float(const std::string& str, const toml::source_location src, const bool is_hex) { return toml::read_float(str, src, is_hex); } }; int main() { const std::string input_str(R"( large_int_dec = 10_000_000_000_000_000_000 large_int_hex = 0x0001_0000_0000_0000_0000 large_float_pi = 3.1415926535897932384626433832795028842 )"); const auto input = toml::parse_str(input_str); std::cout << "int64_t max = " << (std::numeric_limits::max)() << std::endl; std::cout << "large_int_dec = " << input.at("large_int_dec" ).as_integer() << std::endl; std::cout << "large_int_hex = " << input.at("large_int_hex" ).as_integer() << std::endl; std::cout << "large_float_pi = " << std::setprecision(std::numeric_limits::max_digits10 - 1) << input.at("large_float_pi").as_floating() << std::endl; std::cout << "=================" << std::endl; std::cout << toml::format(input) << std::endl; return 0; } toml11-4.1.0/examples/parse_file/000077500000000000000000000000001464712047700165745ustar00rootroot00000000000000toml11-4.1.0/examples/parse_file/.gitignore000066400000000000000000000000771464712047700205700ustar00rootroot00000000000000array_example array_of_tables_example key_example spec_example toml11-4.1.0/examples/parse_file/CMakeLists.txt000066400000000000000000000015731464712047700213420ustar00rootroot00000000000000add_executable(spec_example spec_example.cpp) target_link_libraries(spec_example PRIVATE toml11::toml11) set_target_properties(spec_example PROPERTIES RUNTIME_OUTPUT_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}") add_executable(key_example key_example.cpp) target_link_libraries(key_example PRIVATE toml11::toml11) set_target_properties(key_example PROPERTIES RUNTIME_OUTPUT_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}") add_executable(array_example array_example.cpp) target_link_libraries(array_example PRIVATE toml11::toml11) set_target_properties(array_example PROPERTIES RUNTIME_OUTPUT_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}") add_executable(array_of_tables_example array_of_tables_example.cpp) target_link_libraries(array_of_tables_example PRIVATE toml11::toml11) set_target_properties(array_of_tables_example PROPERTIES RUNTIME_OUTPUT_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}") toml11-4.1.0/examples/parse_file/array_example.cpp000066400000000000000000000152411464712047700221340ustar00rootroot00000000000000#include #include #include int main() { const auto root = toml::parse("array_example.toml"); // using member functions { assert(root.at("integers").is_array()); assert(root.at("integers").at(0).as_integer() == 1); assert(root.at("integers").at(1).as_integer() == 2); assert(root.at("integers").at(2).as_integer() == 3); assert(root.at("colors").at(0).as_string() == "red"); assert(root.at("colors").at(1).as_string() == "yellow"); assert(root.at("colors").at(2).as_string() == "green"); assert(root.at("nested_arrays_of_ints").at(0).at(0).as_integer() == 1); assert(root.at("nested_arrays_of_ints").at(0).at(1).as_integer() == 2); assert(root.at("nested_arrays_of_ints").at(1).at(0).as_integer() == 3); assert(root.at("nested_arrays_of_ints").at(1).at(1).as_integer() == 4); assert(root.at("nested_arrays_of_ints").at(1).at(2).as_integer() == 5); assert(root.at("nested_mixed_array").at(0).at(0).as_integer() == 1); assert(root.at("nested_mixed_array").at(0).at(1).as_integer() == 2); assert(root.at("nested_mixed_array").at(1).at(0).as_string() == "a"); assert(root.at("nested_mixed_array").at(1).at(1).as_string() == "b"); assert(root.at("nested_mixed_array").at(1).at(2).as_string() == "c"); assert(root.at("string_array").at(0).as_string() == "all"); assert(root.at("string_array").at(1).as_string() == "strings"); assert(root.at("string_array").at(2).as_string() == "are the same"); assert(root.at("string_array").at(3).as_string() == "type"); assert(root.at("numbers").at(0).as_floating() == std::stod("0.1")); assert(root.at("numbers").at(1).as_floating() == std::stod("0.2")); assert(root.at("numbers").at(2).as_floating() == std::stod("0.5")); assert(root.at("numbers").at(3).as_integer() == 1); assert(root.at("numbers").at(4).as_integer() == 2); assert(root.at("numbers").at(5).as_integer() == 5); assert(root.at("contributors").at(0).as_string() == "Foo Bar "); assert(root.at("contributors").at(1).at("name" ).as_string() == "Baz Qux"); assert(root.at("contributors").at(1).at("email").as_string() == "bazqux@example.com"); assert(root.at("contributors").at(1).at("url" ).as_string() == "https://example.com/bazqux"); } // using toml::find { assert(toml::find(root, "integers", 0) == 1); assert(toml::find(root, "integers", 1) == 2); assert(toml::find(root, "integers", 2) == 3); const auto integers = toml::find>(root, "integers"); assert(integers.at(0) == 1); assert(integers.at(1) == 2); assert(integers.at(2) == 3); assert(toml::find(root, "colors", 0) == "red"); assert(toml::find(root, "colors", 1) == "yellow"); assert(toml::find(root, "colors", 2) == "green"); const auto colors = toml::find>(root, "colors"); assert(colors.at(0) == "red"); assert(colors.at(1) == "yellow"); assert(colors.at(2) == "green"); assert(toml::find(root, "nested_arrays_of_ints", 0, 0) == 1); assert(toml::find(root, "nested_arrays_of_ints", 0, 1) == 2); assert(toml::find(root, "nested_arrays_of_ints", 1, 0) == 3); assert(toml::find(root, "nested_arrays_of_ints", 1, 1) == 4); assert(toml::find(root, "nested_arrays_of_ints", 1, 2) == 5); const auto nested_arrays_of_ints = toml::find>>(root, "nested_arrays_of_ints"); assert(nested_arrays_of_ints.at(0).at(0) == 1); assert(nested_arrays_of_ints.at(0).at(1) == 2); assert(nested_arrays_of_ints.at(1).at(0) == 3); assert(nested_arrays_of_ints.at(1).at(1) == 4); assert(nested_arrays_of_ints.at(1).at(2) == 5); assert(toml::find(root, "nested_mixed_array", 0, 0) == 1); assert(toml::find(root, "nested_mixed_array", 0, 1) == 2); assert(toml::find(root, "nested_mixed_array", 1, 0) == "a"); assert(toml::find(root, "nested_mixed_array", 1, 1) == "b"); assert(toml::find(root, "nested_mixed_array", 1, 2) == "c"); const auto nested_mixed_array = toml::find< std::pair, std::vector> >(root, "nested_mixed_array"); assert(nested_mixed_array.first .at(0) == 1); assert(nested_mixed_array.first .at(1) == 2); assert(nested_mixed_array.second.at(0) == "a"); assert(nested_mixed_array.second.at(1) == "b"); assert(nested_mixed_array.second.at(2) == "c"); assert(toml::find(root, "string_array", 0) == "all"); assert(toml::find(root, "string_array", 1) == "strings"); assert(toml::find(root, "string_array", 2) == "are the same"); assert(toml::find(root, "string_array", 3) == "type"); const auto numbers = toml::find< std::tuple >(root, "numbers"); assert(std::get<0>(numbers) == std::stod("0.1")); assert(std::get<1>(numbers) == std::stod("0.2")); assert(std::get<2>(numbers) == std::stod("0.5")); assert(std::get<3>(numbers) == 1); assert(std::get<4>(numbers) == 2); assert(std::get<5>(numbers) == 5); struct contributor_t { contributor_t(const toml::value& v) { if(v.is_string()) { name = v.as_string(); } else { assert(v.is_table()); name = toml::find(v, "name"); email = toml::find_or(v, "email", std::string("")); url = toml::find_or(v, "url" , std::string("")); } } std::string name; std::string email; std::string url; }; const auto contributors = toml::find>(root, "contributors"); assert(contributors.at(0).name == "Foo Bar "); assert(contributors.at(0).email == ""); assert(contributors.at(0).url == ""); assert(contributors.at(1).name == "Baz Qux"); assert(contributors.at(1).email == "bazqux@example.com"); assert(contributors.at(1).url == "https://example.com/bazqux"); } std::cout << "ok." << std::endl; return 0; } toml11-4.1.0/examples/parse_file/array_example.toml000066400000000000000000000006651464712047700223310ustar00rootroot00000000000000integers = [ 1, 2, 3 ] colors = [ "red", "yellow", "green" ] nested_arrays_of_ints = [ [ 1, 2 ], [3, 4, 5] ] nested_mixed_array = [ [ 1, 2 ], ["a", "b", "c"] ] string_array = [ "all", 'strings', """are the same""", '''type''' ] # Mixed-type arrays are allowed numbers = [ 0.1, 0.2, 0.5, 1, 2, 5 ] contributors = [ "Foo Bar ", { name = "Baz Qux", email = "bazqux@example.com", url = "https://example.com/bazqux" } ] toml11-4.1.0/examples/parse_file/array_of_tables_example.cpp000066400000000000000000000121761464712047700241560ustar00rootroot00000000000000#include #include #include #include int main() { const auto root = toml::parse("array_of_tables_example.toml"); // using member functions { assert(root.at("points").is_array()); assert(root.at("points").at(0).is_table()); assert(root.at("points").at(1).is_table()); assert(root.at("points").at(2).is_table()); assert(root.at("points").at(0).at("x").as_integer() == 1); assert(root.at("points").at(0).at("y").as_integer() == 2); assert(root.at("points").at(0).at("z").as_integer() == 3); assert(root.at("points").at(1).at("x").as_integer() == 7); assert(root.at("points").at(1).at("y").as_integer() == 8); assert(root.at("points").at(1).at("z").as_integer() == 9); assert(root.at("points").at(2).at("x").as_integer() == 2); assert(root.at("points").at(2).at("y").as_integer() == 4); assert(root.at("points").at(2).at("z").as_integer() == 8); assert(root.at("products").at(0).at("name").as_string() == "Hammer"); assert(root.at("products").at(0).at("sku" ).as_integer() == 738594937); assert(root.at("products").at(1).as_table().empty()); assert(root.at("products").at(2).at("name" ).as_string() == "Nail"); assert(root.at("products").at(2).at("sku" ).as_integer() == 284758393); assert(root.at("products").at(2).at("color").as_string() == "gray"); assert(root.at("fruits").at(0).at("name").as_string() == "apple"); assert(root.at("fruits").at(0).at("physical").at("color").as_string() == "red"); assert(root.at("fruits").at(0).at("physical").at("shape").as_string() == "round"); assert(root.at("fruits").at(0).at("varieties").at(0).at("name").as_string() == "red delicious"); assert(root.at("fruits").at(0).at("varieties").at(1).at("name").as_string() == "granny smith"); assert(root.at("fruits").at(1).at("name").as_string() == "banana"); assert(root.at("fruits").at(1).at("varieties").at(0).at("name").as_string() == "plantain"); } // using toml::find { assert(toml::find(root, "points", 0, "x") == 1); assert(toml::find(root, "points", 0, "y") == 2); assert(toml::find(root, "points", 0, "z") == 3); assert(toml::find(root, "points", 1, "x") == 7); assert(toml::find(root, "points", 1, "y") == 8); assert(toml::find(root, "points", 1, "z") == 9); assert(toml::find(root, "points", 2, "x") == 2); assert(toml::find(root, "points", 2, "y") == 4); assert(toml::find(root, "points", 2, "z") == 8); const auto points = toml::find< std::vector> >(root, "points"); assert(points.at(0).at("x") == 1); assert(points.at(0).at("y") == 2); assert(points.at(0).at("z") == 3); assert(points.at(1).at("x") == 7); assert(points.at(1).at("y") == 8); assert(points.at(1).at("z") == 9); assert(points.at(2).at("x") == 2); assert(points.at(2).at("y") == 4); assert(points.at(2).at("z") == 8); struct product_t { product_t(const toml::value& v) : name (toml::find_or(v, "name", "")), sku (toml::find_or(v, "sku", 0)), color(toml::find_or(v, "color", "")) {} std::string name; std::uint64_t sku; std::string color; }; const auto products = toml::find>(root, "products"); assert(products.at(0).name == "Hammer"); assert(products.at(0).sku == 738594937); assert(products.at(0).color == ""); assert(products.at(1).name == ""); assert(products.at(1).sku == 0); assert(products.at(1).color == ""); assert(products.at(2).name == "Nail"); assert(products.at(2).sku == 284758393); assert(products.at(2).color == "gray"); struct fruit_t { fruit_t(const toml::value& v) : name(toml::find(v, "name")), physical (toml::find>(v, "physical")), varieties(toml::find>>(v, "varieties")) {} std::string name; std::map physical; std::vector> varieties; }; const auto fruits = toml::find>(root, "fruits"); assert(fruits.at(0).name == "apple"); assert(fruits.at(0).physical.at("color") == "red"); assert(fruits.at(0).physical.at("shape") == "round"); assert(fruits.at(0).varieties.at(0).at("name") == "red delicious"); assert(fruits.at(0).varieties.at(1).at("name") == "granny smith"); assert(fruits.at(1).name == "banana"); assert(fruits.at(1).varieties.at(0).at("name") == "plantain"); } std::cout << "ok." << std::endl; return 0; } toml11-4.1.0/examples/parse_file/array_of_tables_example.toml000066400000000000000000000010241464712047700243350ustar00rootroot00000000000000points = [ { x = 1, y = 2, z = 3 }, { x = 7, y = 8, z = 9 }, { x = 2, y = 4, z = 8 } ] [[products]] name = "Hammer" sku = 738594937 [[products]] # empty table within the array [[products]] name = "Nail" sku = 284758393 color = "gray" [[fruits]] name = "apple" [fruits.physical] # subtable color = "red" shape = "round" [[fruits.varieties]] # nested array of tables name = "red delicious" [[fruits.varieties]] name = "granny smith" [[fruits]] name = "banana" [[fruits.varieties]] name = "plantain" toml11-4.1.0/examples/parse_file/key_example.cpp000066400000000000000000000061171464712047700216100ustar00rootroot00000000000000#include #include #include int main() { const auto root = toml::parse("key_example.toml"); assert(root.is_table()); assert(root.comments().size() == 2); assert(root.comments().at(0) == " This is a TOML document."); assert(root.comments().at(1) == " This contains most of the examples in the spec (from https://toml.io)."); assert(root.size() == 1); assert(root.contains("key")); assert(root.at("key").is_table()); // using member functions { const toml::value& keys = root.at("key"); assert(keys.at("key" ).as_string() == "value"); assert(keys.at("bare_key").as_string() == "value"); assert(keys.at("bare-key").as_string() == "value"); assert(keys.at("1234" ).as_string() == "value"); assert(keys.at("127.0.0.1" ).as_string() == "value"); assert(keys.at("character encoding").as_string() == "value"); assert(keys.at("ʎǝʞ" ).as_string() == "value"); // requires /utf-8 in MSVC assert(keys.at("key2" ).as_string() == "value"); assert(keys.at("quoted \"value\"" ).as_string() == "value"); assert(keys.at("").as_string() == "blank"); assert(keys.at("fruit").at("apple").at("skin" ).as_string() == "thin"); assert(keys.at("fruit").at("apple").at("color").as_string() == "red"); assert(keys.at("fruit").at("orange").at("skin" ).as_string() == "thick"); assert(keys.at("fruit").at("orange").at("color").as_string() == "orange"); assert(keys.at("site").at("google.com").as_boolean() == true); assert(keys.at("3").at("14159").as_string() == "pi"); } // using toml::find { assert(toml::find(root, "keys", "key" ) == "value"); assert(toml::find(root, "keys", "bare_key") == "value"); assert(toml::find(root, "keys", "bare-key") == "value"); assert(toml::find(root, "keys", "1234" ) == "value"); const toml::value& keys = toml::find(root, "keys"); assert(toml::find(keys, "127.0.0.1" ) == "value"); assert(toml::find(keys, "character encoding") == "value"); assert(toml::find(keys, "ʎǝʞ" ) == "value"); // requires /utf-8 in MSVC assert(toml::find(keys, "key2" ) == "value"); assert(toml::find(keys, "quoted \"value\"" ) == "value"); assert(toml::find(keys, "") == "blank"); assert(toml::find(keys, "fruit", "apple" , "skin" ) == "thin"); assert(toml::find(keys, "fruit", "apple" , "color") == "red"); assert(toml::find(keys, "fruit", "orange", "skin" ) == "thick"); assert(toml::find(keys, "fruit", "orange", "color") == "orange"); assert(toml::find(keys, "site", "google.com") == true); assert(toml::find(keys, "3", "14159") == "pi"); } std::cout << "ok." << std::endl; return 0; } toml11-4.1.0/examples/parse_file/key_example.toml000066400000000000000000000007721464712047700220020ustar00rootroot00000000000000# This is a TOML document. # This contains most of the examples in the spec (from https://toml.io). [keys] key = "value" bare_key = "value" bare-key = "value" 1234 = "value" "127.0.0.1" = "value" "character encoding" = "value" "ʎǝʞ" = "value" 'key2' = "value" 'quoted "value"' = "value" "" = "blank" fruits.apple.skin = "thin" fruits.apple.color = "red" fruits.orange.skin = "thick" fruits.orange.color = "orange" site."google.com" = true 3.14159 = "pi" toml11-4.1.0/examples/parse_file/spec_example.cpp000066400000000000000000000070661464712047700217560ustar00rootroot00000000000000#include #include #include #include int main() { const auto root = toml::parse("spec_example.toml"); // using member functions { assert(root.at("title").as_string() == "TOML Example"); assert(root.at("owner").at("name").as_string() == "Tom Preston-Werner"); const auto dob = root.at("owner").at("dob" ).as_offset_datetime(); assert(dob.date .year == 1979); assert(dob.date .month == static_cast(toml::month_t::May)); assert(dob.date .day == 27); assert(dob.time .hour == 7); assert(dob.time .minute == 32); assert(dob.time .second == 0); assert(dob.offset.hour == -8); assert(dob.offset.minute == 0); assert(root.at("database").at("enabled").as_boolean()); assert(root.at("database").at("ports").at(0).as_integer() == 8000); assert(root.at("database").at("ports").at(1).as_integer() == 8001); assert(root.at("database").at("ports").at(2).as_integer() == 8002); assert(root.at("database").at("data").at(0).at(0).as_string() == "delta"); assert(root.at("database").at("data").at(0).at(1).as_string() == "phi"); assert(root.at("database").at("data").at(1).at(0).as_floating() == 3.14); assert(root.at("database").at("temp_targets").at("cpu" ).as_floating() == 79.5); assert(root.at("database").at("temp_targets").at("case").as_floating() == 72.0); assert(root.at("servers").at("alpha").at("ip" ).as_string() == "10.0.0.1"); assert(root.at("servers").at("alpha").at("role").as_string() == "frontend"); assert(root.at("servers").at("beta" ).at("ip" ).as_string() == "10.0.0.2"); assert(root.at("servers").at("beta" ).at("role").as_string() == "backend"); } // using toml::find { assert(toml::find(root, "title") == "TOML Example"); assert(toml::find(root, "owner", "name") == "Tom Preston-Werner"); const auto dob = toml::find(root, "owner", "dob"); assert(dob.date .year == 1979); assert(dob.date .month == static_cast(toml::month_t::May)); assert(dob.date .day == 27); assert(dob.time .hour == 7); assert(dob.time .minute == 32); assert(dob.time .second == 0); assert(dob.offset.hour == -8); assert(dob.offset.minute == 0); assert(toml::find(root, "database", "enabled")); const auto ports = toml::find>(root, "database", "ports"); assert(ports.at(0) == 8000); assert(ports.at(1) == 8001); assert(ports.at(2) == 8002); const auto data = toml::find, std::vector>>(root, "database", "data"); assert(data.first.at(0) == "delta"); assert(data.first.at(1) == "phi"); assert(data.second.at(0) == 3.14); const auto temp_targets = toml::find>(root, "database", "temp_targets"); assert(temp_targets.at("cpu" ) == 79.5); assert(temp_targets.at("case") == 72.0); const auto servers = toml::find>>(root, "servers"); assert(servers.at("alpha").at("ip" ) == "10.0.0.1"); assert(servers.at("alpha").at("role") == "frontend"); assert(servers.at("beta" ).at("ip" ) == "10.0.0.2"); assert(servers.at("beta" ).at("role") == "backend" ); } std::cout << "ok." << std::endl; return 0; } toml11-4.1.0/examples/parse_file/spec_example.toml000066400000000000000000000005551464712047700221430ustar00rootroot00000000000000# This is a TOML document title = "TOML Example" [owner] name = "Tom Preston-Werner" dob = 1979-05-27T07:32:00-08:00 [database] enabled = true ports = [ 8000, 8001, 8002 ] data = [ ["delta", "phi"], [3.14] ] temp_targets = { cpu = 79.5, case = 72.0 } [servers] [servers.alpha] ip = "10.0.0.1" role = "frontend" [servers.beta] ip = "10.0.0.2" role = "backend" toml11-4.1.0/examples/reflect/000077500000000000000000000000001464712047700161075ustar00rootroot00000000000000toml11-4.1.0/examples/reflect/.gitignore000066400000000000000000000000101464712047700200660ustar00rootroot00000000000000reflect toml11-4.1.0/examples/reflect/CMakeLists.txt000066400000000000000000000011041464712047700206430ustar00rootroot00000000000000include(FetchContent) FetchContent_Declare( boost_ext_reflect GIT_REPOSITORY https://github.com/boost-ext/reflect GIT_SHALLOW ON # Download the branch without its history GIT_TAG v1.1.1 ) FetchContent_MakeAvailable(boost_ext_reflect) add_executable(reflect reflect.cpp) target_link_libraries(reflect PRIVATE toml11::toml11) target_include_directories(reflect PRIVATE ${boost_ext_reflect_SOURCE_DIR} ) target_compile_features(reflect PRIVATE cxx_std_20) set_target_properties(reflect PROPERTIES RUNTIME_OUTPUT_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}") toml11-4.1.0/examples/reflect/README.md000066400000000000000000000004061464712047700173660ustar00rootroot00000000000000# reflect Auto convert from user-defined `struct`s to `toml::value`. It depends on [boost-ext/reflect](https://github.com/boost-ext/reflect). ## build Build toml11 with `-DTOML11_BUILD_EXAMPLES=ON`. ```cpp $ cmake -B ./build/ -DTOML11_BUILD_EXAMPLES=ON ``` toml11-4.1.0/examples/reflect/reflect.cpp000066400000000000000000000012061464712047700202360ustar00rootroot00000000000000#include #include #include "reflect.hpp" struct Hoge { int foo; double bar; std::string baz; }; TOML11_REFLECT(Hoge) int main() { toml::value v(toml::table{ {"foo", 42}, {"bar", 3.14}, {"baz", "fuga"}, }); const Hoge h = toml::get(v); std::cout << "Hoge.foo = " << h.foo << std::endl; std::cout << "Hoge.bar = " << h.bar << std::endl; std::cout << "Hoge.baz = " << h.baz << std::endl; Hoge h2; h2.foo = 6 * 9; h2.bar = 2.718; h2.baz = "piyo"; toml::value v2(h2); std::cout << toml::format(v2); return 0; } toml11-4.1.0/examples/reflect/reflect.hpp000066400000000000000000000041321464712047700202440ustar00rootroot00000000000000#ifndef TOML11_REFLECT_HPP #define TOML11_REFLECT_HPP #include // boost-ext/reflect #include namespace toml { namespace refl { template T from(const basic_value& v) { T x; reflect::for_each([&v, &x](auto I) { using member_type = std::remove_cvref_t(x))>; const auto key = std::string(reflect::member_name(x)); reflect::get(x) = toml::find(v, key); }, x); return x; } template basic_value into(const T& x) { basic_value v(toml::table{}); reflect::for_each([&v, &x](auto I) { using member_type = std::remove_cvref_t(x))>; const auto key = std::string(reflect::member_name(x)); v[key] = reflect::get(x); }, x); return v; } } // refl } // toml #define TOML11_REFLECT(X) \ namespace toml { \ template<> \ struct into \ { \ template \ static toml::basic_value into_toml(const X& x) \ { \ return refl::into(x); \ } \ }; \ template<> \ struct from \ { \ template \ static X from_toml(const toml::basic_value& v) \ { \ return refl::from(v); \ } \ }; \ } /* toml */ #endif // TOML11_REFLECT_HPP toml11-4.1.0/examples/u8string/000077500000000000000000000000001464712047700162465ustar00rootroot00000000000000toml11-4.1.0/examples/u8string/.gitignore000066400000000000000000000000111464712047700202260ustar00rootroot00000000000000u8string toml11-4.1.0/examples/u8string/CMakeLists.txt000066400000000000000000000003701464712047700210060ustar00rootroot00000000000000add_executable(u8string u8string.cpp) target_link_libraries(u8string PRIVATE toml11::toml11) target_compile_features(u8string PRIVATE cxx_std_20) set_target_properties(u8string PROPERTIES RUNTIME_OUTPUT_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}") toml11-4.1.0/examples/u8string/spec_example.toml000066400000000000000000000005551464712047700216150ustar00rootroot00000000000000# This is a TOML document title = "TOML Example" [owner] name = "Tom Preston-Werner" dob = 1979-05-27T07:32:00-08:00 [database] enabled = true ports = [ 8000, 8001, 8002 ] data = [ ["delta", "phi"], [3.14] ] temp_targets = { cpu = 79.5, case = 72.0 } [servers] [servers.alpha] ip = "10.0.0.1" role = "frontend" [servers.beta] ip = "10.0.0.2" role = "backend" toml11-4.1.0/examples/u8string/u8string.cpp000066400000000000000000000115031464712047700205350ustar00rootroot00000000000000#include #include #include #include struct u8config { using comment_type = toml::preserve_comments; using boolean_type = bool; using integer_type = std::int64_t; using floating_type = double; using string_type = std::u8string; // XXX template using array_type = std::vector; template using table_type = std::unordered_map; static toml::result parse_int(const std::string& str, const toml::source_location src, const std::uint8_t base) { return toml::read_int(str, src, base); } static toml::result parse_float(const std::string& str, const toml::source_location src, const bool is_hex) { return toml::read_float(str, src, is_hex); } }; int main() { const auto root = toml::parse("spec_example.toml"); // using member functions { assert(root.at(u8"title").as_string() == u8"TOML Example"); assert(root.at(u8"owner").at(u8"name").as_string() == u8"Tom Preston-Werner"); const auto dob = root.at(u8"owner").at(u8"dob" ).as_offset_datetime(); assert(dob.date .year == 1979); assert(dob.date .month == static_cast(toml::month_t::May)); assert(dob.date .day == 27); assert(dob.time .hour == 7); assert(dob.time .minute == 32); assert(dob.time .second == 0); assert(dob.offset.hour == -8); assert(dob.offset.minute == 0); assert(root.at(u8"database").at(u8"enabled").as_boolean()); assert(root.at(u8"database").at(u8"ports").at(0).as_integer() == 8000); assert(root.at(u8"database").at(u8"ports").at(1).as_integer() == 8001); assert(root.at(u8"database").at(u8"ports").at(2).as_integer() == 8002); assert(root.at(u8"database").at(u8"data").at(0).at(0).as_string() == u8"delta"); assert(root.at(u8"database").at(u8"data").at(0).at(1).as_string() == u8"phi"); assert(root.at(u8"database").at(u8"data").at(1).at(0).as_floating() == 3.14); assert(root.at(u8"database").at(u8"temp_targets").at(u8"cpu" ).as_floating() == 79.5); assert(root.at(u8"database").at(u8"temp_targets").at(u8"case").as_floating() == 72.0); assert(root.at(u8"servers").at(u8"alpha").at(u8"ip" ).as_string() == u8"10.0.0.1"); assert(root.at(u8"servers").at(u8"alpha").at(u8"role").as_string() == u8"frontend"); assert(root.at(u8"servers").at(u8"beta" ).at(u8"ip" ).as_string() == u8"10.0.0.2"); assert(root.at(u8"servers").at(u8"beta" ).at(u8"role").as_string() == u8"backend"); } // using toml::find { // you can get as std::string from u8string, using toml::get/find assert(toml::find(root, u8"title") == "TOML Example"); assert(toml::find(root, u8"owner", u8"name") == "Tom Preston-Werner"); const auto dob = toml::find(root, u8"owner", u8"dob"); assert(dob.date .year == 1979); assert(dob.date .month == static_cast(toml::month_t::May)); assert(dob.date .day == 27); assert(dob.time .hour == 7); assert(dob.time .minute == 32); assert(dob.time .second == 0); assert(dob.offset.hour == -8); assert(dob.offset.minute == 0); assert(toml::find(root, u8"database", u8"enabled")); const auto ports = toml::find>(root, u8"database", u8"ports"); assert(ports.at(0) == 8000); assert(ports.at(1) == 8001); assert(ports.at(2) == 8002); const auto data = toml::find, std::vector>>(root, u8"database", u8"data"); assert(data.first.at(0) == "delta"); assert(data.first.at(1) == "phi"); assert(data.second.at(0) == 3.14); const auto temp_targets = toml::find>(root, u8"database", u8"temp_targets"); assert(temp_targets.at("cpu" ) == 79.5); assert(temp_targets.at("case") == 72.0); const auto servers = toml::find>>(root, u8"servers"); assert(servers.at("alpha").at("ip" ) == "10.0.0.1"); assert(servers.at("alpha").at("role") == "frontend"); assert(servers.at("beta" ).at("ip" ) == "10.0.0.2"); assert(servers.at("beta" ).at("role") == "backend" ); } const std::u8string out = toml::format(root); std::string printable; std::transform(out.begin(), out.end(), std::back_inserter(printable), [](const char8_t c) {return static_cast(c);}); std::cout << printable << std::endl; std::cout << "ok." << std::endl; return 0; } toml11-4.1.0/examples/unicode/000077500000000000000000000000001464712047700161115ustar00rootroot00000000000000toml11-4.1.0/examples/unicode/.gitignore000066400000000000000000000000151464712047700200750ustar00rootroot00000000000000canonicalize toml11-4.1.0/examples/unicode/CMakeLists.txt000066400000000000000000000010671464712047700206550ustar00rootroot00000000000000include(FetchContent) FetchContent_Declare(uni-algo GIT_REPOSITORY https://github.com/uni-algo/uni-algo.git GIT_SHALLOW ON # Download the branch without its history GIT_TAG v1.0.0) # The version you want to download # Be aware that FetchContent_MakeAvailable requires CMake 3.14 or higher FetchContent_MakeAvailable(uni-algo) add_executable(canonicalize canonicalize.cpp) target_link_libraries(canonicalize PRIVATE toml11::toml11 uni-algo::uni-algo) set_target_properties(canonicalize PROPERTIES RUNTIME_OUTPUT_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}") toml11-4.1.0/examples/unicode/README.md000066400000000000000000000005531464712047700173730ustar00rootroot00000000000000# unicode Compare TOML key after NFC canonicalization. It depends on [uni-algo](https://github.com/uni-algo/uni-algo.git). The example contains two keys that are different in bytewise comparison, but becomes the same after NFC normalization. ## build Build toml11 with `-DTOML11_BUILD_EXAMPLES=ON`. ```cpp $ cmake -B ./build/ -DTOML11_BUILD_EXAMPLES=ON ``` toml11-4.1.0/examples/unicode/canonicalize.cpp000066400000000000000000000040011464712047700212470ustar00rootroot00000000000000#include #include #include #include struct nfc_comparator { using first_argument_type = std::string; using second_argument_type = std::string; using result_type = bool; result_type operator()(const first_argument_type& lhs, const second_argument_type& rhs) const { return una::norm::to_nfc_utf8(lhs) < una::norm::to_nfc_utf8(rhs); } }; struct nfc_equal_to { using first_argument_type = std::string; using second_argument_type = std::string; using result_type = bool; result_type operator()(const first_argument_type& lhs, const second_argument_type& rhs) const { return una::norm::to_nfc_utf8(lhs) == una::norm::to_nfc_utf8(rhs); } }; struct nfc_hasher { std::size_t operator()(const std::string& s) const { return std::hash{}(una::norm::to_nfc_utf8(s)); } }; struct nfc_config { using comment_type = toml::preserve_comments; using boolean_type = bool; using integer_type = std::int64_t; using floating_type = double; using string_type = std::string; template using array_type = std::vector; template // using table_type = std::map; using table_type = std::unordered_map; static toml::result parse_int(const std::string& str, const toml::source_location src, const std::uint8_t base) { return toml::read_int(str, src, base); } static toml::result parse_float(const std::string& str, const toml::source_location src, const bool is_hex) { return toml::read_float(str, src, is_hex); } }; int main(int argc, char **argv) { if(argc != 2) { return 1; } const auto input = toml::parse(argv[1]); std::cout << toml::format(input) << std::endl; return 0; } toml11-4.1.0/examples/unicode/input.toml000066400000000000000000000001401464712047700201400ustar00rootroot00000000000000"W\u0302" = 1 # in NFC, this key is the same as the following key "Ŵ" = 2 # so it fails. toml11-4.1.0/include/000077500000000000000000000000001464712047700142705ustar00rootroot00000000000000toml11-4.1.0/include/toml.hpp000066400000000000000000000043201464712047700157530ustar00rootroot00000000000000#ifndef TOML11_TOML_HPP #define TOML11_TOML_HPP // The MIT License (MIT) // // Copyright (c) 2017-now Toru Niina // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. // IWYU pragma: begin_exports #include "toml11/color.hpp" #include "toml11/comments.hpp" #include "toml11/compat.hpp" #include "toml11/context.hpp" #include "toml11/conversion.hpp" #include "toml11/datetime.hpp" #include "toml11/error_info.hpp" #include "toml11/exception.hpp" #include "toml11/find.hpp" #include "toml11/format.hpp" #include "toml11/from.hpp" #include "toml11/get.hpp" #include "toml11/into.hpp" #include "toml11/literal.hpp" #include "toml11/location.hpp" #include "toml11/ordered_map.hpp" #include "toml11/parser.hpp" #include "toml11/region.hpp" #include "toml11/result.hpp" #include "toml11/scanner.hpp" #include "toml11/serializer.hpp" #include "toml11/skip.hpp" #include "toml11/source_location.hpp" #include "toml11/spec.hpp" #include "toml11/storage.hpp" #include "toml11/syntax.hpp" #include "toml11/traits.hpp" #include "toml11/types.hpp" #include "toml11/utility.hpp" #include "toml11/value.hpp" #include "toml11/value_t.hpp" #include "toml11/version.hpp" #include "toml11/visit.hpp" // IWYU pragma: end_exports #endif// TOML11_TOML_HPP toml11-4.1.0/include/toml11/000077500000000000000000000000001464712047700154055ustar00rootroot00000000000000toml11-4.1.0/include/toml11/color.hpp000066400000000000000000000003471464712047700172400ustar00rootroot00000000000000#ifndef TOML11_COLOR_HPP #define TOML11_COLOR_HPP #include "fwd/color_fwd.hpp" // IWYU pragma: export #if ! defined(TOML11_COMPILE_SOURCES) #include "impl/color_impl.hpp" // IWYU pragma: export #endif #endif // TOML11_COLOR_HPP toml11-4.1.0/include/toml11/comments.hpp000066400000000000000000000003661464712047700177500ustar00rootroot00000000000000#ifndef TOML11_COMMENTS_HPP #define TOML11_COMMENTS_HPP #include "fwd/comments_fwd.hpp" // IWYU pragma: export #if ! defined(TOML11_COMPILE_SOURCES) #include "impl/comments_impl.hpp" // IWYU pragma: export #endif #endif // TOML11_COMMENTS_HPP toml11-4.1.0/include/toml11/compat.hpp000066400000000000000000000431541464712047700174100ustar00rootroot00000000000000#ifndef TOML11_COMPAT_HPP #define TOML11_COMPAT_HPP #include "version.hpp" #include #include #include #include #include #include #if TOML11_CPLUSPLUS_STANDARD_VERSION >= TOML11_CXX20_VALUE # if __has_include() # include # endif #endif #include // ---------------------------------------------------------------------------- #if TOML11_CPLUSPLUS_STANDARD_VERSION >= TOML11_CXX14_VALUE # if __has_cpp_attribute(deprecated) # define TOML11_HAS_ATTR_DEPRECATED 1 # endif #endif #if defined(TOML11_HAS_ATTR_DEPRECATED) # define TOML11_DEPRECATED(msg) [[deprecated(msg)]] #elif defined(__GNUC__) # define TOML11_DEPRECATED(msg) __attribute__((deprecated(msg))) #elif defined(_MSC_VER) # define TOML11_DEPRECATED(msg) __declspec(deprecated(msg)) #else # define TOML11_DEPRECATED(msg) #endif // ---------------------------------------------------------------------------- #if defined(__cpp_if_constexpr) # if __cpp_if_constexpr >= 201606L # define TOML11_HAS_CONSTEXPR_IF 1 # endif #endif #if defined(TOML11_HAS_CONSTEXPR_IF) # define TOML11_CONSTEXPR_IF if constexpr #else # define TOML11_CONSTEXPR_IF if #endif // ---------------------------------------------------------------------------- #if TOML11_CPLUSPLUS_STANDARD_VERSION >= TOML11_CXX14_VALUE # if defined(__cpp_lib_make_unique) # if __cpp_lib_make_unique >= 201304L # define TOML11_HAS_STD_MAKE_UNIQUE 1 # endif # endif #endif namespace toml { namespace cxx { #if defined(TOML11_HAS_STD_MAKE_UNIQUE) using std::make_unique; #else template std::unique_ptr make_unique(Ts&& ... args) { return std::unique_ptr(new T(std::forward(args)...)); } #endif // TOML11_HAS_STD_MAKE_UNIQUE } // cxx } // toml // --------------------------------------------------------------------------- #if TOML11_CPLUSPLUS_STANDARD_VERSION >= TOML11_CXX14_VALUE # if defined(__cpp_lib_make_reverse_iterator) # if __cpp_lib_make_reverse_iterator >= 201402L # define TOML11_HAS_STD_MAKE_REVERSE_ITERATOR 1 # endif # endif #endif namespace toml { namespace cxx { # if defined(TOML11_HAS_STD_MAKE_REVERSE_ITERATOR) using std::make_reverse_iterator; #else template std::reverse_iterator make_reverse_iterator(Iterator iter) { return std::reverse_iterator(iter); } #endif // TOML11_HAS_STD_MAKE_REVERSE_ITERATOR } // cxx } // toml // --------------------------------------------------------------------------- #if TOML11_CPLUSPLUS_STANDARD_VERSION >= TOML11_CXX20_VALUE # if defined(__cpp_lib_clamp) # if __cpp_lib_clamp >= 201603L # define TOML11_HAS_STD_CLAMP 1 # endif # endif #endif namespace toml { namespace cxx { #if defined(TOML11_HAS_STD_CLAMP) using std::clamp; #else template T clamp(const T& x, const T& low, const T& high) noexcept { assert(low <= high); return (std::min)((std::max)(x, low), high); } #endif // TOML11_HAS_STD_CLAMP } // cxx } // toml // --------------------------------------------------------------------------- #if TOML11_CPLUSPLUS_STANDARD_VERSION >= TOML11_CXX20_VALUE # if defined(__cpp_lib_bit_cast) # if __cpp_lib_bit_cast >= 201806L # define TOML11_HAS_STD_BIT_CAST 1 # endif # endif #endif namespace toml { namespace cxx { #if defined(TOML11_HAS_STD_BIT_CAST) using std::bit_cast; #else template U bit_cast(const T& x) noexcept { static_assert(sizeof(T) == sizeof(U), ""); static_assert(std::is_default_constructible::value, ""); U z; std::memcpy(reinterpret_cast(std::addressof(z)), reinterpret_cast(std::addressof(x)), sizeof(T)); return z; } #endif // TOML11_HAS_STD_BIT_CAST } // cxx } // toml // --------------------------------------------------------------------------- // C++20 remove_cvref_t #if TOML11_CPLUSPLUS_STANDARD_VERSION >= TOML11_CXX20_VALUE # if defined(__cpp_lib_remove_cvref) # if __cpp_lib_remove_cvref >= 201711L # define TOML11_HAS_STD_REMOVE_CVREF 1 # endif # endif #endif namespace toml { namespace cxx { #if defined(TOML11_HAS_STD_REMOVE_CVREF) using std::remove_cvref; using std::remove_cvref_t; #else template struct remove_cvref { using type = typename std::remove_cv< typename std::remove_reference::type>::type; }; template using remove_cvref_t = typename remove_cvref::type; #endif // TOML11_HAS_STD_REMOVE_CVREF } // cxx } // toml // --------------------------------------------------------------------------- // C++17 and/or/not #if TOML11_CPLUSPLUS_STANDARD_VERSION >= TOML11_CXX17_VALUE # if defined(__cpp_lib_logical_traits) # if __cpp_lib_logical_traits >= 201510L # define TOML11_HAS_STD_CONJUNCTION 1 # endif # endif #endif namespace toml { namespace cxx { #if defined(TOML11_HAS_STD_CONJUNCTION) using std::conjunction; using std::disjunction; using std::negation; #else template struct conjunction : std::true_type{}; template struct conjunction : T{}; template struct conjunction : std::conditional(T::value), conjunction, T>::type {}; template struct disjunction : std::false_type{}; template struct disjunction : T {}; template struct disjunction : std::conditional(T::value), T, disjunction>::type {}; template struct negation : std::integral_constant(T::value)>{}; #endif // TOML11_HAS_STD_CONJUNCTION } // cxx } // toml // --------------------------------------------------------------------------- // C++14 index_sequence #if TOML11_CPLUSPLUS_STANDARD_VERSION >= TOML11_CXX14_VALUE # if defined(__cpp_lib_integer_sequence) # if __cpp_lib_integer_sequence >= 201304L # define TOML11_HAS_STD_INTEGER_SEQUENCE 1 # endif # endif #endif namespace toml { namespace cxx { #if defined(TOML11_HAS_STD_INTEGER_SEQUENCE) using std::index_sequence; using std::make_index_sequence; #else template struct index_sequence{}; template struct double_index_sequence; template struct double_index_sequence> { using type = index_sequence; }; template struct double_index_sequence> { using type = index_sequence; }; template struct index_sequence_maker { using type = typename double_index_sequence< N % 2 == 1, N/2, typename index_sequence_maker::type >::type; }; template<> struct index_sequence_maker<0> { using type = index_sequence<>; }; template using make_index_sequence = typename index_sequence_maker::type; #endif // TOML11_HAS_STD_INTEGER_SEQUENCE } // cxx } // toml // --------------------------------------------------------------------------- // C++14 enable_if_t #if TOML11_CPLUSPLUS_STANDARD_VERSION >= TOML11_CXX14_VALUE # if defined(__cpp_lib_transformation_trait_aliases) # if __cpp_lib_transformation_trait_aliases >= 201304L # define TOML11_HAS_STD_ENABLE_IF_T 1 # endif # endif #endif namespace toml { namespace cxx { #if defined(TOML11_HAS_STD_ENABLE_IF_T) using std::enable_if_t; #else template using enable_if_t = typename std::enable_if::type; #endif // TOML11_HAS_STD_ENABLE_IF_T } // cxx } // toml // --------------------------------------------------------------------------- // return_type_of_t #if TOML11_CPLUSPLUS_STANDARD_VERSION >= TOML11_CXX17_VALUE # if defined(__cpp_lib_is_invocable) # if __cpp_lib_is_invocable >= 201703 # define TOML11_HAS_STD_INVOKE_RESULT 1 # endif # endif #endif namespace toml { namespace cxx { #if defined(TOML11_HAS_STD_INVOKE_RESULT) template using return_type_of_t = std::invoke_result_t; #else // result_of is deprecated after C++17 template using return_type_of_t = typename std::result_of::type; #endif // TOML11_HAS_STD_INVOKE_RESULT } // cxx } // toml // ---------------------------------------------------------------------------- // (subset of) source_location #if TOML11_CPLUSPLUS_STANDARD_VERSION >= 202002L # if __has_include() # define TOML11_HAS_STD_SOURCE_LOCATION # endif // has_include #endif // c++20 #if ! defined(TOML11_HAS_STD_SOURCE_LOCATION) # if defined(__GNUC__) && ! defined(__clang__) # if TOML11_CPLUSPLUS_STANDARD_VERSION >= TOML11_CXX14_VALUE # if __has_include() # define TOML11_HAS_EXPERIMENTAL_SOURCE_LOCATION # endif # endif # endif // GNU g++ #endif // not TOML11_HAS_STD_SOURCE_LOCATION #if ! defined(TOML11_HAS_STD_SOURCE_LOCATION) && ! defined(TOML11_HAS_EXPERIMENTAL_SOURCE_LOCATION) # if defined(__GNUC__) && ! defined(__clang__) # if (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 9)) # define TOML11_HAS_BUILTIN_FILE_LINE 1 # define TOML11_BUILTIN_LINE_TYPE int # endif # elif defined(__clang__) // clang 9.0.0 implements builtin_FILE/LINE # if __has_builtin(__builtin_FILE) && __has_builtin(__builtin_LINE) # define TOML11_HAS_BUILTIN_FILE_LINE 1 # define TOML11_BUILTIN_LINE_TYPE unsigned int # endif # elif defined(_MSVC_LANG) && defined(_MSC_VER) # if _MSC_VER > 1926 # define TOML11_HAS_BUILTIN_FILE_LINE 1 # define TOML11_BUILTIN_LINE_TYPE int # endif # endif #endif #if defined(TOML11_HAS_STD_SOURCE_LOCATION) #include namespace toml { namespace cxx { using source_location = std::source_location; inline std::string to_string(const source_location& loc) { return std::string(" at line ") + std::to_string(loc.line()) + std::string(" in file ") + std::string(loc.file_name()); } } // cxx } // toml #elif defined(TOML11_HAS_EXPERIMENTAL_SOURCE_LOCATION) #include namespace toml { namespace cxx { using source_location = std::experimental::source_location; inline std::string to_string(const source_location& loc) { return std::string(" at line ") + std::to_string(loc.line()) + std::string(" in file ") + std::string(loc.file_name()); } } // cxx } // toml #elif defined(TOML11_HAS_BUILTIN_FILE_LINE) namespace toml { namespace cxx { struct source_location { using line_type = TOML11_BUILTIN_LINE_TYPE; static source_location current(const line_type line = __builtin_LINE(), const char* file = __builtin_FILE()) { return source_location(line, file); } source_location(const line_type line, const char* file) : line_(line), file_name_(file) {} line_type line() const noexcept {return line_;} const char* file_name() const noexcept {return file_name_;} private: line_type line_; const char* file_name_; }; inline std::string to_string(const source_location& loc) { return std::string(" at line ") + std::to_string(loc.line()) + std::string(" in file ") + std::string(loc.file_name()); } } // cxx } // toml #else // no builtin namespace toml { namespace cxx { struct source_location { static source_location current() { return source_location{}; } }; inline std::string to_string(const source_location&) { return std::string(""); } } // cxx } // toml #endif // TOML11_HAS_STD_SOURCE_LOCATION // ---------------------------------------------------------------------------- // (subset of) optional #if TOML11_CPLUSPLUS_STANDARD_VERSION >= TOML11_CXX17_VALUE # if __has_include() # include # endif // has_include(optional) #endif // C++17 #if TOML11_CPLUSPLUS_STANDARD_VERSION >= TOML11_CXX17_VALUE # if defined(__cpp_lib_optional) # if __cpp_lib_optional >= 201606L # define TOML11_HAS_STD_OPTIONAL 1 # endif # endif #endif #if defined(TOML11_HAS_STD_OPTIONAL) namespace toml { namespace cxx { using std::optional; inline std::nullopt_t make_nullopt() {return std::nullopt;} template std::basic_ostream& operator<<(std::basic_ostream& os, const std::nullopt_t&) { os << "nullopt"; return os; } } // cxx } // toml #else // TOML11_HAS_STD_OPTIONAL namespace toml { namespace cxx { struct nullopt_t{}; inline nullopt_t make_nullopt() {return nullopt_t{};} inline bool operator==(const nullopt_t&, const nullopt_t&) noexcept {return true;} inline bool operator!=(const nullopt_t&, const nullopt_t&) noexcept {return false;} inline bool operator< (const nullopt_t&, const nullopt_t&) noexcept {return false;} inline bool operator<=(const nullopt_t&, const nullopt_t&) noexcept {return true;} inline bool operator> (const nullopt_t&, const nullopt_t&) noexcept {return false;} inline bool operator>=(const nullopt_t&, const nullopt_t&) noexcept {return true;} template std::basic_ostream& operator<<(std::basic_ostream& os, const nullopt_t&) { os << "nullopt"; return os; } template class optional { public: using value_type = T; public: optional() noexcept : has_value_(false), null_('\0') {} optional(nullopt_t) noexcept : has_value_(false), null_('\0') {} optional(const T& x): has_value_(true), value_(x) {} optional(T&& x): has_value_(true), value_(std::move(x)) {} template::value, std::nullptr_t> = nullptr> explicit optional(U&& x): has_value_(true), value_(std::forward(x)) {} optional(const optional& rhs): has_value_(rhs.has_value_) { if(rhs.has_value_) { this->assigner(rhs.value_); } } optional(optional&& rhs): has_value_(rhs.has_value_) { if(this->has_value_) { this->assigner(std::move(rhs.value_)); } } optional& operator=(const optional& rhs) { if(this == std::addressof(rhs)) {return *this;} this->cleanup(); this->has_value_ = rhs.has_value_; if(this->has_value_) { this->assigner(rhs.value_); } return *this; } optional& operator=(optional&& rhs) { if(this == std::addressof(rhs)) {return *this;} this->cleanup(); this->has_value_ = rhs.has_value_; if(this->has_value_) { this->assigner(std::move(rhs.value_)); } return *this; } template>, std::is_constructible >::value, std::nullptr_t> = nullptr> explicit optional(const optional& rhs): has_value_(rhs.has_value_), null_('\0') { if(rhs.has_value_) { this->assigner(rhs.value_); } } template>, std::is_constructible >::value, std::nullptr_t> = nullptr> explicit optional(optional&& rhs): has_value_(rhs.has_value_), null_('\0') { if(this->has_value_) { this->assigner(std::move(rhs.value_)); } } template>, std::is_constructible >::value, std::nullptr_t> = nullptr> optional& operator=(const optional& rhs) { if(this == std::addressof(rhs)) {return *this;} this->cleanup(); this->has_value_ = rhs.has_value_; if(this->has_value_) { this->assigner(rhs.value_); } return *this; } template>, std::is_constructible >::value, std::nullptr_t> = nullptr> optional& operator=(optional&& rhs) { if(this == std::addressof(rhs)) {return *this;} this->cleanup(); this->has_value_ = rhs.has_value_; if(this->has_value_) { this->assigner(std::move(rhs.value_)); } return *this; } ~optional() noexcept { this->cleanup(); } explicit operator bool() const noexcept { return has_value_; } bool has_value() const noexcept {return has_value_;} value_type const& value(source_location loc = source_location::current()) const { if( ! this->has_value_) { throw std::runtime_error("optional::value(): bad_unwrap" + to_string(loc)); } return this->value_; } value_type& value(source_location loc = source_location::current()) { if( ! this->has_value_) { throw std::runtime_error("optional::value(): bad_unwrap" + to_string(loc)); } return this->value_; } value_type const& value_or(const value_type& opt) const { if(this->has_value_) {return this->value_;} else {return opt;} } value_type& value_or(value_type& opt) { if(this->has_value_) {return this->value_;} else {return opt;} } private: void cleanup() noexcept { if(this->has_value_) { value_.~T(); } } template void assigner(U&& x) { const auto tmp = ::new(std::addressof(this->value_)) value_type(std::forward(x)); assert(tmp == std::addressof(this->value_)); (void)tmp; } private: bool has_value_; union { char null_; T value_; }; }; } // cxx } // toml #endif // TOML11_HAS_STD_OPTIONAL #endif // TOML11_COMPAT_HPP toml11-4.1.0/include/toml11/context.hpp000066400000000000000000000026411464712047700176050ustar00rootroot00000000000000#ifndef TOML11_CONTEXT_HPP #define TOML11_CONTEXT_HPP #include "error_info.hpp" #include "spec.hpp" #include namespace toml { namespace detail { template class context { public: explicit context(const spec& toml_spec) : toml_spec_(toml_spec), errors_{} {} bool has_error() const noexcept {return !errors_.empty();} std::vector const& errors() const noexcept {return errors_;} semantic_version& toml_version() noexcept {return toml_spec_.version;} semantic_version const& toml_version() const noexcept {return toml_spec_.version;} spec& toml_spec() noexcept {return toml_spec_;} spec const& toml_spec() const noexcept {return toml_spec_;} void report_error(error_info err) { this->errors_.push_back(std::move(err)); } error_info pop_last_error() { assert( ! errors_.empty()); auto e = std::move(errors_.back()); errors_.pop_back(); return e; } private: spec toml_spec_; std::vector errors_; }; } // detail } // toml #if defined(TOML11_COMPILE_SOURCES) namespace toml { struct type_config; struct ordered_type_config; namespace detail { extern template class context<::toml::type_config>; extern template class context<::toml::ordered_type_config>; } // detail } // toml #endif // TOML11_COMPILE_SOURCES #endif // TOML11_CONTEXT_HPP toml11-4.1.0/include/toml11/conversion.hpp000066400000000000000000000201631464712047700203050ustar00rootroot00000000000000#ifndef TOML11_CONVERSION_HPP #define TOML11_CONVERSION_HPP #include "from.hpp" // IWYU pragma: keep #include "into.hpp" // IWYU pragma: keep // use it in the following way. // ```cpp // namespace foo // { // struct Foo // { // std::string s; // double d; // int i; // }; // } // foo // // TOML11_DEFINE_CONVERSION_NON_INTRUSIVE(foo::Foo, s, d, i) // ``` // // And then you can use `toml::get(v)` and `toml::find(file, "foo");` // #define TOML11_STRINGIZE_AUX(x) #x #define TOML11_STRINGIZE(x) TOML11_STRINGIZE_AUX(x) #define TOML11_CONCATENATE_AUX(x, y) x##y #define TOML11_CONCATENATE(x, y) TOML11_CONCATENATE_AUX(x, y) // ============================================================================ // TOML11_DEFINE_CONVERSION_NON_INTRUSIVE #ifndef TOML11_WITHOUT_DEFINE_NON_INTRUSIVE // ---------------------------------------------------------------------------- // TOML11_ARGS_SIZE #define TOML11_INDEX_RSEQ() \ 32, 31, 30, 29, 28, 27, 26, 25, 24, 23, 22, 21, 20, 19, 18, 17, \ 16, 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0 #define TOML11_ARGS_SIZE_IMPL(\ ARG1, ARG2, ARG3, ARG4, ARG5, ARG6, ARG7, ARG8, ARG9, ARG10, \ ARG11, ARG12, ARG13, ARG14, ARG15, ARG16, ARG17, ARG18, ARG19, ARG20, \ ARG21, ARG22, ARG23, ARG24, ARG25, ARG26, ARG27, ARG28, ARG29, ARG30, \ ARG31, ARG32, N, ...) N #define TOML11_ARGS_SIZE_AUX(...) TOML11_ARGS_SIZE_IMPL(__VA_ARGS__) #define TOML11_ARGS_SIZE(...) TOML11_ARGS_SIZE_AUX(__VA_ARGS__, TOML11_INDEX_RSEQ()) // ---------------------------------------------------------------------------- // TOML11_FOR_EACH_VA_ARGS #define TOML11_FOR_EACH_VA_ARGS_AUX_1( FUNCTOR, ARG1 ) FUNCTOR(ARG1) #define TOML11_FOR_EACH_VA_ARGS_AUX_2( FUNCTOR, ARG1, ...) FUNCTOR(ARG1) TOML11_FOR_EACH_VA_ARGS_AUX_1( FUNCTOR, __VA_ARGS__) #define TOML11_FOR_EACH_VA_ARGS_AUX_3( FUNCTOR, ARG1, ...) FUNCTOR(ARG1) TOML11_FOR_EACH_VA_ARGS_AUX_2( FUNCTOR, __VA_ARGS__) #define TOML11_FOR_EACH_VA_ARGS_AUX_4( FUNCTOR, ARG1, ...) FUNCTOR(ARG1) TOML11_FOR_EACH_VA_ARGS_AUX_3( FUNCTOR, __VA_ARGS__) #define TOML11_FOR_EACH_VA_ARGS_AUX_5( FUNCTOR, ARG1, ...) FUNCTOR(ARG1) TOML11_FOR_EACH_VA_ARGS_AUX_4( FUNCTOR, __VA_ARGS__) #define TOML11_FOR_EACH_VA_ARGS_AUX_6( FUNCTOR, ARG1, ...) FUNCTOR(ARG1) TOML11_FOR_EACH_VA_ARGS_AUX_5( FUNCTOR, __VA_ARGS__) #define TOML11_FOR_EACH_VA_ARGS_AUX_7( FUNCTOR, ARG1, ...) FUNCTOR(ARG1) TOML11_FOR_EACH_VA_ARGS_AUX_6( FUNCTOR, __VA_ARGS__) #define TOML11_FOR_EACH_VA_ARGS_AUX_8( FUNCTOR, ARG1, ...) FUNCTOR(ARG1) TOML11_FOR_EACH_VA_ARGS_AUX_7( FUNCTOR, __VA_ARGS__) #define TOML11_FOR_EACH_VA_ARGS_AUX_9( FUNCTOR, ARG1, ...) FUNCTOR(ARG1) TOML11_FOR_EACH_VA_ARGS_AUX_8( FUNCTOR, __VA_ARGS__) #define TOML11_FOR_EACH_VA_ARGS_AUX_10(FUNCTOR, ARG1, ...) FUNCTOR(ARG1) TOML11_FOR_EACH_VA_ARGS_AUX_9( FUNCTOR, __VA_ARGS__) #define TOML11_FOR_EACH_VA_ARGS_AUX_11(FUNCTOR, ARG1, ...) FUNCTOR(ARG1) TOML11_FOR_EACH_VA_ARGS_AUX_10(FUNCTOR, __VA_ARGS__) #define TOML11_FOR_EACH_VA_ARGS_AUX_12(FUNCTOR, ARG1, ...) FUNCTOR(ARG1) TOML11_FOR_EACH_VA_ARGS_AUX_11(FUNCTOR, __VA_ARGS__) #define TOML11_FOR_EACH_VA_ARGS_AUX_13(FUNCTOR, ARG1, ...) FUNCTOR(ARG1) TOML11_FOR_EACH_VA_ARGS_AUX_12(FUNCTOR, __VA_ARGS__) #define TOML11_FOR_EACH_VA_ARGS_AUX_14(FUNCTOR, ARG1, ...) FUNCTOR(ARG1) TOML11_FOR_EACH_VA_ARGS_AUX_13(FUNCTOR, __VA_ARGS__) #define TOML11_FOR_EACH_VA_ARGS_AUX_15(FUNCTOR, ARG1, ...) FUNCTOR(ARG1) TOML11_FOR_EACH_VA_ARGS_AUX_14(FUNCTOR, __VA_ARGS__) #define TOML11_FOR_EACH_VA_ARGS_AUX_16(FUNCTOR, ARG1, ...) FUNCTOR(ARG1) TOML11_FOR_EACH_VA_ARGS_AUX_15(FUNCTOR, __VA_ARGS__) #define TOML11_FOR_EACH_VA_ARGS_AUX_17(FUNCTOR, ARG1, ...) FUNCTOR(ARG1) TOML11_FOR_EACH_VA_ARGS_AUX_16(FUNCTOR, __VA_ARGS__) #define TOML11_FOR_EACH_VA_ARGS_AUX_18(FUNCTOR, ARG1, ...) FUNCTOR(ARG1) TOML11_FOR_EACH_VA_ARGS_AUX_17(FUNCTOR, __VA_ARGS__) #define TOML11_FOR_EACH_VA_ARGS_AUX_19(FUNCTOR, ARG1, ...) FUNCTOR(ARG1) TOML11_FOR_EACH_VA_ARGS_AUX_18(FUNCTOR, __VA_ARGS__) #define TOML11_FOR_EACH_VA_ARGS_AUX_20(FUNCTOR, ARG1, ...) FUNCTOR(ARG1) TOML11_FOR_EACH_VA_ARGS_AUX_19(FUNCTOR, __VA_ARGS__) #define TOML11_FOR_EACH_VA_ARGS_AUX_21(FUNCTOR, ARG1, ...) FUNCTOR(ARG1) TOML11_FOR_EACH_VA_ARGS_AUX_20(FUNCTOR, __VA_ARGS__) #define TOML11_FOR_EACH_VA_ARGS_AUX_22(FUNCTOR, ARG1, ...) FUNCTOR(ARG1) TOML11_FOR_EACH_VA_ARGS_AUX_21(FUNCTOR, __VA_ARGS__) #define TOML11_FOR_EACH_VA_ARGS_AUX_23(FUNCTOR, ARG1, ...) FUNCTOR(ARG1) TOML11_FOR_EACH_VA_ARGS_AUX_22(FUNCTOR, __VA_ARGS__) #define TOML11_FOR_EACH_VA_ARGS_AUX_24(FUNCTOR, ARG1, ...) FUNCTOR(ARG1) TOML11_FOR_EACH_VA_ARGS_AUX_23(FUNCTOR, __VA_ARGS__) #define TOML11_FOR_EACH_VA_ARGS_AUX_25(FUNCTOR, ARG1, ...) FUNCTOR(ARG1) TOML11_FOR_EACH_VA_ARGS_AUX_24(FUNCTOR, __VA_ARGS__) #define TOML11_FOR_EACH_VA_ARGS_AUX_26(FUNCTOR, ARG1, ...) FUNCTOR(ARG1) TOML11_FOR_EACH_VA_ARGS_AUX_25(FUNCTOR, __VA_ARGS__) #define TOML11_FOR_EACH_VA_ARGS_AUX_27(FUNCTOR, ARG1, ...) FUNCTOR(ARG1) TOML11_FOR_EACH_VA_ARGS_AUX_26(FUNCTOR, __VA_ARGS__) #define TOML11_FOR_EACH_VA_ARGS_AUX_28(FUNCTOR, ARG1, ...) FUNCTOR(ARG1) TOML11_FOR_EACH_VA_ARGS_AUX_27(FUNCTOR, __VA_ARGS__) #define TOML11_FOR_EACH_VA_ARGS_AUX_29(FUNCTOR, ARG1, ...) FUNCTOR(ARG1) TOML11_FOR_EACH_VA_ARGS_AUX_28(FUNCTOR, __VA_ARGS__) #define TOML11_FOR_EACH_VA_ARGS_AUX_30(FUNCTOR, ARG1, ...) FUNCTOR(ARG1) TOML11_FOR_EACH_VA_ARGS_AUX_29(FUNCTOR, __VA_ARGS__) #define TOML11_FOR_EACH_VA_ARGS_AUX_31(FUNCTOR, ARG1, ...) FUNCTOR(ARG1) TOML11_FOR_EACH_VA_ARGS_AUX_30(FUNCTOR, __VA_ARGS__) #define TOML11_FOR_EACH_VA_ARGS_AUX_32(FUNCTOR, ARG1, ...) FUNCTOR(ARG1) TOML11_FOR_EACH_VA_ARGS_AUX_31(FUNCTOR, __VA_ARGS__) #define TOML11_FOR_EACH_VA_ARGS(FUNCTOR, ...)\ TOML11_CONCATENATE(TOML11_FOR_EACH_VA_ARGS_AUX_, TOML11_ARGS_SIZE(__VA_ARGS__))(FUNCTOR, __VA_ARGS__) #define TOML11_FIND_MEMBER_VARIABLE_FROM_VALUE(VAR_NAME)\ obj.VAR_NAME = toml::find(v, TOML11_STRINGIZE(VAR_NAME)); #define TOML11_ASSIGN_MEMBER_VARIABLE_TO_VALUE(VAR_NAME)\ v[TOML11_STRINGIZE(VAR_NAME)] = obj.VAR_NAME; #define TOML11_DEFINE_CONVERSION_NON_INTRUSIVE(NAME, ...)\ namespace toml { \ template<> \ struct from \ { \ template \ static NAME from_toml(const basic_value& v) \ { \ NAME obj; \ TOML11_FOR_EACH_VA_ARGS(TOML11_FIND_MEMBER_VARIABLE_FROM_VALUE, __VA_ARGS__) \ return obj; \ } \ }; \ template<> \ struct into \ { \ template \ static basic_value into_toml(const NAME& obj) \ { \ ::toml::basic_value v = typename ::toml::basic_value::table_type{}; \ TOML11_FOR_EACH_VA_ARGS(TOML11_ASSIGN_MEMBER_VARIABLE_TO_VALUE, __VA_ARGS__) \ return v; \ } \ }; \ } /* toml */ #endif// TOML11_WITHOUT_DEFINE_NON_INTRUSIVE #endif // TOML11_CONVERSION_HPP toml11-4.1.0/include/toml11/datetime.hpp000066400000000000000000000003661464712047700177170ustar00rootroot00000000000000#ifndef TOML11_DATETIME_HPP #define TOML11_DATETIME_HPP #include "fwd/datetime_fwd.hpp" // IWYU pragma: export #if ! defined(TOML11_COMPILE_SOURCES) #include "impl/datetime_impl.hpp" // IWYU pragma: export #endif #endif // TOML11_DATETIME_HPP toml11-4.1.0/include/toml11/error_info.hpp000066400000000000000000000004001464712047700202540ustar00rootroot00000000000000#ifndef TOML11_ERROR_INFO_HPP #define TOML11_ERROR_INFO_HPP #include "fwd/error_info_fwd.hpp" // IWYU pragma: export #if ! defined(TOML11_COMPILE_SOURCES) #include "impl/error_info_impl.hpp" // IWYU pragma: export #endif #endif // TOML11_ERROR_INFO_HPP toml11-4.1.0/include/toml11/exception.hpp000066400000000000000000000004771464712047700201240ustar00rootroot00000000000000#ifndef TOML11_EXCEPTION_HPP #define TOML11_EXCEPTION_HPP #include namespace toml { struct exception : public std::exception { public: virtual ~exception() noexcept override = default; virtual const char* what() const noexcept override {return "";} }; } // toml #endif // TOMl11_EXCEPTION_HPP toml11-4.1.0/include/toml11/find.hpp000066400000000000000000000256511464712047700170470ustar00rootroot00000000000000#ifndef TOML11_FIND_HPP #define TOML11_FIND_HPP #include #include "get.hpp" #include "value.hpp" #if defined(TOML11_HAS_STRING_VIEW) #include #endif namespace toml { // ---------------------------------------------------------------------------- // find(value, key); template decltype(::toml::get(std::declval const&>())) find(const basic_value& v, const typename basic_value::key_type& ky) { return ::toml::get(v.at(ky)); } template decltype(::toml::get(std::declval&>())) find(basic_value& v, const typename basic_value::key_type& ky) { return ::toml::get(v.at(ky)); } template decltype(::toml::get(std::declval&&>())) find(basic_value&& v, const typename basic_value::key_type& ky) { return ::toml::get(std::move(v.at(ky))); } // ---------------------------------------------------------------------------- // find(value, idx) template decltype(::toml::get(std::declval const&>())) find(const basic_value& v, const std::size_t idx) { return ::toml::get(v.at(idx)); } template decltype(::toml::get(std::declval&>())) find(basic_value& v, const std::size_t idx) { return ::toml::get(v.at(idx)); } template decltype(::toml::get(std::declval&&>())) find(basic_value&& v, const std::size_t idx) { return ::toml::get(std::move(v.at(idx))); } // ---------------------------------------------------------------------------- // find(value, key/idx), w/o conversion template cxx::enable_if_t::value, basic_value>& find(basic_value& v, const typename basic_value::key_type& ky) { return v.at(ky); } template cxx::enable_if_t::value, basic_value> const& find(basic_value const& v, const typename basic_value::key_type& ky) { return v.at(ky); } template cxx::enable_if_t::value, basic_value> find(basic_value&& v, const typename basic_value::key_type& ky) { return basic_value(std::move(v.at(ky))); } template cxx::enable_if_t::value, basic_value>& find(basic_value& v, const std::size_t idx) { return v.at(idx); } template cxx::enable_if_t::value, basic_value> const& find(basic_value const& v, const std::size_t idx) { return v.at(idx); } template cxx::enable_if_t::value, basic_value> find(basic_value&& v, const std::size_t idx) { return basic_value(std::move(v.at(idx))); } // -------------------------------------------------------------------------- // toml::find(toml::value, toml::key, Ts&& ... keys) namespace detail { // It suppresses warnings by -Wsign-conversion when we pass integer literal // to toml::find. integer literal `0` is deduced as an int, and will be // converted to std::size_t. This causes sign-conversion. template std::size_t key_cast(const std::size_t& v) noexcept { return v; } template cxx::enable_if_t>::value, std::size_t> key_cast(const T& v) noexcept { return static_cast(v); } // for string-like (string, string literal, string_view) template typename basic_value::key_type const& key_cast(const typename basic_value::key_type& v) noexcept { return v; } template typename basic_value::key_type key_cast(const typename basic_value::key_type::value_type* v) { return typename basic_value::key_type(v); } #if defined(TOML11_HAS_STRING_VIEW) template typename basic_value::key_type key_cast(const std::string_view v) { return typename basic_value::key_type(v); } #endif // string_view } // detail // ---------------------------------------------------------------------------- // find(v, keys...) template cxx::enable_if_t::value, basic_value> const& find(const basic_value& v, const K1& k1, const K2& k2, const Ks& ... ks) { return find(v.at(detail::key_cast(k1)), detail::key_cast(k2), ks...); } template cxx::enable_if_t::value, basic_value>& find(basic_value& v, const K1& k1, const K2& k2, const Ks& ... ks) { return find(v.at(detail::key_cast(k1)), detail::key_cast(k2), ks...); } template cxx::enable_if_t::value, basic_value> find(basic_value&& v, const K1& k1, const K2& k2, const Ks& ... ks) { return find(std::move(v.at(detail::key_cast(k1))), detail::key_cast(k2), ks...); } // ---------------------------------------------------------------------------- // find(v, keys...) template decltype(::toml::get(std::declval&>())) find(const basic_value& v, const K1& k1, const K2& k2, const Ks& ... ks) { return find(v.at(detail::key_cast(k1)), detail::key_cast(k2), ks...); } template decltype(::toml::get(std::declval&>())) find(basic_value& v, const K1& k1, const K2& k2, const Ks& ... ks) { return find(v.at(detail::key_cast(k1)), detail::key_cast(k2), ks...); } template decltype(::toml::get(std::declval&&>())) find(basic_value&& v, const K1& k1, const K2& k2, const Ks& ... ks) { return find(std::move(v.at(detail::key_cast(k1))), detail::key_cast(k2), ks...); } // =========================================================================== // find_or(value, key, fallback) // --------------------------------------------------------------------------- // find_or(v, key, other_v) template cxx::enable_if_t::value, basic_value>& find_or(basic_value& v, const K& k, basic_value& opt) noexcept { try { return ::toml::find(v, detail::key_cast(k)); } catch(...) { return opt; } } template cxx::enable_if_t::value, basic_value> const& find_or(const basic_value& v, const K& k, const basic_value& opt) noexcept { try { return ::toml::find(v, detail::key_cast(k)); } catch(...) { return opt; } } template cxx::enable_if_t::value, basic_value> find_or(basic_value&& v, const K& k, basic_value&& opt) noexcept { try { return ::toml::find(v, detail::key_cast(k)); } catch(...) { return opt; } } // --------------------------------------------------------------------------- // toml types (return type can be a reference) template cxx::enable_if_t>::value, cxx::remove_cvref_t const&> find_or(const basic_value& v, const K& k, const T& opt) { try { return ::toml::get(v.at(detail::key_cast(k))); } catch(...) { return opt; } } template cxx::enable_if_t>, detail::is_exact_toml_type> >::value, cxx::remove_cvref_t&> find_or(basic_value& v, const K& k, T& opt) { try { return ::toml::get(v.at(detail::key_cast(k))); } catch(...) { return opt; } } template cxx::enable_if_t>::value, cxx::remove_cvref_t> find_or(basic_value&& v, const K& k, T opt) { try { return ::toml::get(std::move(v.at(detail::key_cast(k)))); } catch(...) { return T(std::move(opt)); } } // --------------------------------------------------------------------------- // string literal (deduced as std::string) // XXX to avoid confusion when T is explicitly specified in find_or(), // we restrict the string type as std::string. template cxx::enable_if_t::value, std::string> find_or(const basic_value& v, const K& k, const char* opt) { try { return ::toml::get(v.at(detail::key_cast(k))); } catch(...) { return std::string(opt); } } // --------------------------------------------------------------------------- // other types (requires type conversion and return type cannot be a reference) template cxx::enable_if_t>>, detail::is_not_toml_type, basic_value>, cxx::negation, const typename basic_value::string_type::value_type*>> >::value, cxx::remove_cvref_t> find_or(const basic_value& v, const K& ky, T opt) { try { return ::toml::get>(v.at(detail::key_cast(ky))); } catch(...) { return cxx::remove_cvref_t(std::move(opt)); } } // ---------------------------------------------------------------------------- // recursive namespace detail { template T& last_one(T& arg) { return arg; } template auto last_one(T1&, T2& arg, Ts& ... args) -> decltype(last_one(arg, args...)) { return last_one(arg, args...); } } // detail template auto find_or(Value&& v, const K1& k1, const K2& k2, K3&& k3, Ks&& ... keys) noexcept -> cxx::enable_if_t< detail::is_basic_value>::value, decltype(find_or(v, k2, std::forward(k3), std::forward(keys)...)) > { try { return find_or(v.at(k1), k2, std::forward(k3), std::forward(keys)...); } catch(...) { return detail::last_one(k3, keys...); } } template T find_or(const basic_value& v, const K1& k1, const K2& k2, const K3& k3, const Ks& ... keys) noexcept { try { return find_or(v.at(k1), k2, k3, keys...); } catch(...) { return static_cast(detail::last_one(k3, keys...)); } } } // toml #endif // TOML11_FIND_HPP toml11-4.1.0/include/toml11/format.hpp000066400000000000000000000003531464712047700174070ustar00rootroot00000000000000#ifndef TOML11_FORMAT_HPP #define TOML11_FORMAT_HPP #include "fwd/format_fwd.hpp" // IWYU pragma: export #if ! defined(TOML11_COMPILE_SOURCES) #include "impl/format_impl.hpp" // IWYU pragma: export #endif #endif// TOML11_FORMAT_HPP toml11-4.1.0/include/toml11/from.hpp000066400000000000000000000004021464712047700170550ustar00rootroot00000000000000#ifndef TOML11_FROM_HPP #define TOML11_FROM_HPP namespace toml { template struct from; // { // static T from_toml(const toml::value& v) // { // // User-defined conversions ... // } // }; } // toml #endif // TOML11_FROM_HPP toml11-4.1.0/include/toml11/fwd/000077500000000000000000000000001464712047700161655ustar00rootroot00000000000000toml11-4.1.0/include/toml11/fwd/color_fwd.hpp000066400000000000000000000030201464712047700206470ustar00rootroot00000000000000#ifndef TOML11_COLOR_FWD_HPP #define TOML11_COLOR_FWD_HPP #include #ifdef TOML11_COLORIZE_ERROR_MESSAGE #define TOML11_ERROR_MESSAGE_COLORIZED true #else #define TOML11_ERROR_MESSAGE_COLORIZED false #endif namespace toml { namespace color { // put ANSI escape sequence to ostream inline namespace ansi { namespace detail { // Control color mode globally class color_mode { public: void enable() noexcept { should_color_ = true; } void disable() noexcept { should_color_ = false; } bool should_color() const noexcept { return should_color_; } private: bool should_color_ = TOML11_ERROR_MESSAGE_COLORIZED; }; inline color_mode& color_status() noexcept { static thread_local color_mode status; return status; } } // detail std::ostream& reset (std::ostream& os); std::ostream& bold (std::ostream& os); std::ostream& grey (std::ostream& os); std::ostream& gray (std::ostream& os); std::ostream& red (std::ostream& os); std::ostream& green (std::ostream& os); std::ostream& yellow (std::ostream& os); std::ostream& blue (std::ostream& os); std::ostream& magenta(std::ostream& os); std::ostream& cyan (std::ostream& os); std::ostream& white (std::ostream& os); } // ansi inline void enable() { return detail::color_status().enable(); } inline void disable() { return detail::color_status().disable(); } inline bool should_color() { return detail::color_status().should_color(); } } // color } // toml #endif // TOML11_COLOR_FWD_HPP toml11-4.1.0/include/toml11/fwd/comments_fwd.hpp000066400000000000000000000474611464712047700213770ustar00rootroot00000000000000#ifndef TOML11_COMMENTS_FWD_HPP #define TOML11_COMMENTS_FWD_HPP // to use __has_builtin #include "../version.hpp" // IWYU pragma: keep #include #include #include #include #include #include #include #include // This file provides mainly two classes, `preserve_comments` and `discard_comments`. // Those two are a container that have the same interface as `std::vector` // but bahaves in the opposite way. `preserve_comments` is just the same as // `std::vector` and each `std::string` corresponds to a comment line. // Conversely, `discard_comments` discards all the strings and ignores everything // assigned in it. `discard_comments` is always empty and you will encounter an // error whenever you access to the element. namespace toml { class discard_comments; // forward decl class preserve_comments { public: // `container_type` is not provided in discard_comments. // do not use this inner-type in a generic code. using container_type = std::vector; using size_type = container_type::size_type; using difference_type = container_type::difference_type; using value_type = container_type::value_type; using reference = container_type::reference; using const_reference = container_type::const_reference; using pointer = container_type::pointer; using const_pointer = container_type::const_pointer; using iterator = container_type::iterator; using const_iterator = container_type::const_iterator; using reverse_iterator = container_type::reverse_iterator; using const_reverse_iterator = container_type::const_reverse_iterator; public: preserve_comments() = default; ~preserve_comments() = default; preserve_comments(preserve_comments const&) = default; preserve_comments(preserve_comments &&) = default; preserve_comments& operator=(preserve_comments const&) = default; preserve_comments& operator=(preserve_comments &&) = default; explicit preserve_comments(const std::vector& c): comments(c){} explicit preserve_comments(std::vector&& c) : comments(std::move(c)) {} preserve_comments& operator=(const std::vector& c) { comments = c; return *this; } preserve_comments& operator=(std::vector&& c) { comments = std::move(c); return *this; } explicit preserve_comments(const discard_comments&) {} explicit preserve_comments(size_type n): comments(n) {} preserve_comments(size_type n, const std::string& x): comments(n, x) {} preserve_comments(std::initializer_list x): comments(x) {} template preserve_comments(InputIterator first, InputIterator last) : comments(first, last) {} template void assign(InputIterator first, InputIterator last) {comments.assign(first, last);} void assign(std::initializer_list ini) {comments.assign(ini);} void assign(size_type n, const std::string& val) {comments.assign(n, val);} // Related to the issue #97. // // `std::vector::insert` and `std::vector::erase` in the STL implementation // included in GCC 4.8.5 takes `std::vector::iterator` instead of // `std::vector::const_iterator`. It causes compilation error in GCC 4.8.5. #if defined(__GNUC__) && defined(__GNUC_MINOR__) && defined(__GNUC_PATCHLEVEL__) && !defined(__clang__) # if (__GNUC__ * 10000 + __GNUC_MINOR__ * 100 + __GNUC_PATCHLEVEL__) <= 40805 # define TOML11_WORKAROUND_GCC_4_8_X_STANDARD_LIBRARY_IMPLEMENTATION # endif #endif #ifdef TOML11_WORKAROUND_GCC_4_8_X_STANDARD_LIBRARY_IMPLEMENTATION iterator insert(iterator p, const std::string& x) { return comments.insert(p, x); } iterator insert(iterator p, std::string&& x) { return comments.insert(p, std::move(x)); } void insert(iterator p, size_type n, const std::string& x) { return comments.insert(p, n, x); } template void insert(iterator p, InputIterator first, InputIterator last) { return comments.insert(p, first, last); } void insert(iterator p, std::initializer_list ini) { return comments.insert(p, ini); } template iterator emplace(iterator p, Ts&& ... args) { return comments.emplace(p, std::forward(args)...); } iterator erase(iterator pos) {return comments.erase(pos);} iterator erase(iterator first, iterator last) { return comments.erase(first, last); } #else iterator insert(const_iterator p, const std::string& x) { return comments.insert(p, x); } iterator insert(const_iterator p, std::string&& x) { return comments.insert(p, std::move(x)); } iterator insert(const_iterator p, size_type n, const std::string& x) { return comments.insert(p, n, x); } template iterator insert(const_iterator p, InputIterator first, InputIterator last) { return comments.insert(p, first, last); } iterator insert(const_iterator p, std::initializer_list ini) { return comments.insert(p, ini); } template iterator emplace(const_iterator p, Ts&& ... args) { return comments.emplace(p, std::forward(args)...); } iterator erase(const_iterator pos) {return comments.erase(pos);} iterator erase(const_iterator first, const_iterator last) { return comments.erase(first, last); } #endif void swap(preserve_comments& other) {comments.swap(other.comments);} void push_back(const std::string& v) {comments.push_back(v);} void push_back(std::string&& v) {comments.push_back(std::move(v));} void pop_back() {comments.pop_back();} template void emplace_back(Ts&& ... args) {comments.emplace_back(std::forward(args)...);} void clear() {comments.clear();} size_type size() const noexcept {return comments.size();} size_type max_size() const noexcept {return comments.max_size();} size_type capacity() const noexcept {return comments.capacity();} bool empty() const noexcept {return comments.empty();} void reserve(size_type n) {comments.reserve(n);} void resize(size_type n) {comments.resize(n);} void resize(size_type n, const std::string& c) {comments.resize(n, c);} void shrink_to_fit() {comments.shrink_to_fit();} reference operator[](const size_type n) noexcept {return comments[n];} const_reference operator[](const size_type n) const noexcept {return comments[n];} reference at(const size_type n) {return comments.at(n);} const_reference at(const size_type n) const {return comments.at(n);} reference front() noexcept {return comments.front();} const_reference front() const noexcept {return comments.front();} reference back() noexcept {return comments.back();} const_reference back() const noexcept {return comments.back();} pointer data() noexcept {return comments.data();} const_pointer data() const noexcept {return comments.data();} iterator begin() noexcept {return comments.begin();} iterator end() noexcept {return comments.end();} const_iterator begin() const noexcept {return comments.begin();} const_iterator end() const noexcept {return comments.end();} const_iterator cbegin() const noexcept {return comments.cbegin();} const_iterator cend() const noexcept {return comments.cend();} reverse_iterator rbegin() noexcept {return comments.rbegin();} reverse_iterator rend() noexcept {return comments.rend();} const_reverse_iterator rbegin() const noexcept {return comments.rbegin();} const_reverse_iterator rend() const noexcept {return comments.rend();} const_reverse_iterator crbegin() const noexcept {return comments.crbegin();} const_reverse_iterator crend() const noexcept {return comments.crend();} friend bool operator==(const preserve_comments&, const preserve_comments&); friend bool operator!=(const preserve_comments&, const preserve_comments&); friend bool operator< (const preserve_comments&, const preserve_comments&); friend bool operator<=(const preserve_comments&, const preserve_comments&); friend bool operator> (const preserve_comments&, const preserve_comments&); friend bool operator>=(const preserve_comments&, const preserve_comments&); friend void swap(preserve_comments&, std::vector&); friend void swap(std::vector&, preserve_comments&); private: container_type comments; }; bool operator==(const preserve_comments& lhs, const preserve_comments& rhs); bool operator!=(const preserve_comments& lhs, const preserve_comments& rhs); bool operator< (const preserve_comments& lhs, const preserve_comments& rhs); bool operator<=(const preserve_comments& lhs, const preserve_comments& rhs); bool operator> (const preserve_comments& lhs, const preserve_comments& rhs); bool operator>=(const preserve_comments& lhs, const preserve_comments& rhs); void swap(preserve_comments& lhs, preserve_comments& rhs); void swap(preserve_comments& lhs, std::vector& rhs); void swap(std::vector& lhs, preserve_comments& rhs); std::ostream& operator<<(std::ostream& os, const preserve_comments& com); namespace detail { // To provide the same interface with `preserve_comments`, `discard_comments` // should have an iterator. But it does not contain anything, so we need to // add an iterator that points nothing. // // It always points null, so DO NOT unwrap this iterator. It always crashes // your program. template struct empty_iterator { using value_type = T; using reference_type = typename std::conditional::type; using pointer_type = typename std::conditional::type; using difference_type = std::ptrdiff_t; using iterator_category = std::random_access_iterator_tag; empty_iterator() = default; ~empty_iterator() = default; empty_iterator(empty_iterator const&) = default; empty_iterator(empty_iterator &&) = default; empty_iterator& operator=(empty_iterator const&) = default; empty_iterator& operator=(empty_iterator &&) = default; // DO NOT call these operators. reference_type operator*() const noexcept {std::terminate();} pointer_type operator->() const noexcept {return nullptr;} reference_type operator[](difference_type) const noexcept {return this->operator*();} // These operators do nothing. empty_iterator& operator++() noexcept {return *this;} empty_iterator operator++(int) noexcept {return *this;} empty_iterator& operator--() noexcept {return *this;} empty_iterator operator--(int) noexcept {return *this;} empty_iterator& operator+=(difference_type) noexcept {return *this;} empty_iterator& operator-=(difference_type) noexcept {return *this;} empty_iterator operator+(difference_type) const noexcept {return *this;} empty_iterator operator-(difference_type) const noexcept {return *this;} }; template bool operator==(const empty_iterator&, const empty_iterator&) noexcept {return true;} template bool operator!=(const empty_iterator&, const empty_iterator&) noexcept {return false;} template bool operator< (const empty_iterator&, const empty_iterator&) noexcept {return false;} template bool operator<=(const empty_iterator&, const empty_iterator&) noexcept {return true;} template bool operator> (const empty_iterator&, const empty_iterator&) noexcept {return false;} template bool operator>=(const empty_iterator&, const empty_iterator&) noexcept {return true;} template typename empty_iterator::difference_type operator-(const empty_iterator&, const empty_iterator&) noexcept {return 0;} template empty_iterator operator+(typename empty_iterator::difference_type, const empty_iterator& rhs) noexcept {return rhs;} template empty_iterator operator+(const empty_iterator& lhs, typename empty_iterator::difference_type) noexcept {return lhs;} } // detail // The default comment type. It discards all the comments. It requires only one // byte to contain, so the memory footprint is smaller than preserve_comments. // // It just ignores `push_back`, `insert`, `erase`, and any other modifications. // IT always returns size() == 0, the iterator taken by `begin()` is always the // same as that of `end()`, and accessing through `operator[]` or iterators // always causes a segmentation fault. DO NOT access to the element of this. // // Why this is chose as the default type is because the last version (2.x.y) // does not contain any comments in a value. To minimize the impact on the // efficiency, this is chosen as a default. // // To reduce the memory footprint, later we can try empty base optimization (EBO). class discard_comments { public: using size_type = std::size_t; using difference_type = std::ptrdiff_t; using value_type = std::string; using reference = std::string&; using const_reference = std::string const&; using pointer = std::string*; using const_pointer = std::string const*; using iterator = detail::empty_iterator; using const_iterator = detail::empty_iterator; using reverse_iterator = detail::empty_iterator; using const_reverse_iterator = detail::empty_iterator; public: discard_comments() = default; ~discard_comments() = default; discard_comments(discard_comments const&) = default; discard_comments(discard_comments &&) = default; discard_comments& operator=(discard_comments const&) = default; discard_comments& operator=(discard_comments &&) = default; explicit discard_comments(const std::vector&) noexcept {} explicit discard_comments(std::vector&&) noexcept {} discard_comments& operator=(const std::vector&) noexcept {return *this;} discard_comments& operator=(std::vector&&) noexcept {return *this;} explicit discard_comments(const preserve_comments&) noexcept {} explicit discard_comments(size_type) noexcept {} discard_comments(size_type, const std::string&) noexcept {} discard_comments(std::initializer_list) noexcept {} template discard_comments(InputIterator, InputIterator) noexcept {} template void assign(InputIterator, InputIterator) noexcept {} void assign(std::initializer_list) noexcept {} void assign(size_type, const std::string&) noexcept {} iterator insert(const_iterator, const std::string&) {return iterator{};} iterator insert(const_iterator, std::string&&) {return iterator{};} iterator insert(const_iterator, size_type, const std::string&) {return iterator{};} template iterator insert(const_iterator, InputIterator, InputIterator) {return iterator{};} iterator insert(const_iterator, std::initializer_list) {return iterator{};} template iterator emplace(const_iterator, Ts&& ...) {return iterator{};} iterator erase(const_iterator) {return iterator{};} iterator erase(const_iterator, const_iterator) {return iterator{};} void swap(discard_comments&) {return;} void push_back(const std::string&) {return;} void push_back(std::string&& ) {return;} void pop_back() {return;} template void emplace_back(Ts&& ...) {return;} void clear() {return;} size_type size() const noexcept {return 0;} size_type max_size() const noexcept {return 0;} size_type capacity() const noexcept {return 0;} bool empty() const noexcept {return true;} void reserve(size_type) {return;} void resize(size_type) {return;} void resize(size_type, const std::string&) {return;} void shrink_to_fit() {return;} // DO NOT access to the element of this container. This container is always // empty, so accessing through operator[], front/back, data causes address // error. reference operator[](const size_type) noexcept {never_call("toml::discard_comment::operator[]");} const_reference operator[](const size_type) const noexcept {never_call("toml::discard_comment::operator[]");} reference at(const size_type) {throw std::out_of_range("toml::discard_comment is always empty.");} const_reference at(const size_type) const {throw std::out_of_range("toml::discard_comment is always empty.");} reference front() noexcept {never_call("toml::discard_comment::front");} const_reference front() const noexcept {never_call("toml::discard_comment::front");} reference back() noexcept {never_call("toml::discard_comment::back");} const_reference back() const noexcept {never_call("toml::discard_comment::back");} pointer data() noexcept {return nullptr;} const_pointer data() const noexcept {return nullptr;} iterator begin() noexcept {return iterator{};} iterator end() noexcept {return iterator{};} const_iterator begin() const noexcept {return const_iterator{};} const_iterator end() const noexcept {return const_iterator{};} const_iterator cbegin() const noexcept {return const_iterator{};} const_iterator cend() const noexcept {return const_iterator{};} reverse_iterator rbegin() noexcept {return iterator{};} reverse_iterator rend() noexcept {return iterator{};} const_reverse_iterator rbegin() const noexcept {return const_iterator{};} const_reverse_iterator rend() const noexcept {return const_iterator{};} const_reverse_iterator crbegin() const noexcept {return const_iterator{};} const_reverse_iterator crend() const noexcept {return const_iterator{};} private: [[noreturn]] static void never_call(const char *const this_function) { #if __has_builtin(__builtin_unreachable) __builtin_unreachable(); #endif throw std::logic_error{this_function}; } }; inline bool operator==(const discard_comments&, const discard_comments&) noexcept {return true;} inline bool operator!=(const discard_comments&, const discard_comments&) noexcept {return false;} inline bool operator< (const discard_comments&, const discard_comments&) noexcept {return false;} inline bool operator<=(const discard_comments&, const discard_comments&) noexcept {return true;} inline bool operator> (const discard_comments&, const discard_comments&) noexcept {return false;} inline bool operator>=(const discard_comments&, const discard_comments&) noexcept {return true;} inline void swap(const discard_comments&, const discard_comments&) noexcept {return;} inline std::ostream& operator<<(std::ostream& os, const discard_comments&) {return os;} } // toml11 #endif // TOML11_COMMENTS_FWD_HPP toml11-4.1.0/include/toml11/fwd/datetime_fwd.hpp000066400000000000000000000221311464712047700213310ustar00rootroot00000000000000#ifndef TOML11_DATETIME_FWD_HPP #define TOML11_DATETIME_FWD_HPP #include #include #include #include #include #include namespace toml { enum class month_t : std::uint8_t { Jan = 0, Feb = 1, Mar = 2, Apr = 3, May = 4, Jun = 5, Jul = 6, Aug = 7, Sep = 8, Oct = 9, Nov = 10, Dec = 11 }; // ---------------------------------------------------------------------------- struct local_date { std::int16_t year{0}; // A.D. (like, 2018) std::uint8_t month{0}; // [0, 11] std::uint8_t day{0}; // [1, 31] local_date(int y, month_t m, int d) : year {static_cast(y)}, month{static_cast(m)}, day {static_cast(d)} {} explicit local_date(const std::tm& t) : year {static_cast(t.tm_year + 1900)}, month{static_cast(t.tm_mon)}, day {static_cast(t.tm_mday)} {} explicit local_date(const std::chrono::system_clock::time_point& tp); explicit local_date(const std::time_t t); operator std::chrono::system_clock::time_point() const; operator std::time_t() const; local_date() = default; ~local_date() = default; local_date(local_date const&) = default; local_date(local_date&&) = default; local_date& operator=(local_date const&) = default; local_date& operator=(local_date&&) = default; }; bool operator==(const local_date& lhs, const local_date& rhs); bool operator!=(const local_date& lhs, const local_date& rhs); bool operator< (const local_date& lhs, const local_date& rhs); bool operator<=(const local_date& lhs, const local_date& rhs); bool operator> (const local_date& lhs, const local_date& rhs); bool operator>=(const local_date& lhs, const local_date& rhs); std::ostream& operator<<(std::ostream& os, const local_date& date); std::string to_string(const local_date& date); // ----------------------------------------------------------------------------- struct local_time { std::uint8_t hour{0}; // [0, 23] std::uint8_t minute{0}; // [0, 59] std::uint8_t second{0}; // [0, 60] std::uint16_t millisecond{0}; // [0, 999] std::uint16_t microsecond{0}; // [0, 999] std::uint16_t nanosecond{0}; // [0, 999] local_time(int h, int m, int s, int ms = 0, int us = 0, int ns = 0) : hour {static_cast(h)}, minute{static_cast(m)}, second{static_cast(s)}, millisecond{static_cast(ms)}, microsecond{static_cast(us)}, nanosecond {static_cast(ns)} {} explicit local_time(const std::tm& t) : hour {static_cast(t.tm_hour)}, minute{static_cast(t.tm_min )}, second{static_cast(t.tm_sec )}, millisecond{0}, microsecond{0}, nanosecond{0} {} template explicit local_time(const std::chrono::duration& t) { const auto h = std::chrono::duration_cast(t); this->hour = static_cast(h.count()); const auto t2 = t - h; const auto m = std::chrono::duration_cast(t2); this->minute = static_cast(m.count()); const auto t3 = t2 - m; const auto s = std::chrono::duration_cast(t3); this->second = static_cast(s.count()); const auto t4 = t3 - s; const auto ms = std::chrono::duration_cast(t4); this->millisecond = static_cast(ms.count()); const auto t5 = t4 - ms; const auto us = std::chrono::duration_cast(t5); this->microsecond = static_cast(us.count()); const auto t6 = t5 - us; const auto ns = std::chrono::duration_cast(t6); this->nanosecond = static_cast(ns.count()); } operator std::chrono::nanoseconds() const; local_time() = default; ~local_time() = default; local_time(local_time const&) = default; local_time(local_time&&) = default; local_time& operator=(local_time const&) = default; local_time& operator=(local_time&&) = default; }; bool operator==(const local_time& lhs, const local_time& rhs); bool operator!=(const local_time& lhs, const local_time& rhs); bool operator< (const local_time& lhs, const local_time& rhs); bool operator<=(const local_time& lhs, const local_time& rhs); bool operator> (const local_time& lhs, const local_time& rhs); bool operator>=(const local_time& lhs, const local_time& rhs); std::ostream& operator<<(std::ostream& os, const local_time& time); std::string to_string(const local_time& time); // ---------------------------------------------------------------------------- struct time_offset { std::int8_t hour{0}; // [-12, 12] std::int8_t minute{0}; // [-59, 59] time_offset(int h, int m) : hour {static_cast(h)}, minute{static_cast(m)} {} operator std::chrono::minutes() const; time_offset() = default; ~time_offset() = default; time_offset(time_offset const&) = default; time_offset(time_offset&&) = default; time_offset& operator=(time_offset const&) = default; time_offset& operator=(time_offset&&) = default; }; bool operator==(const time_offset& lhs, const time_offset& rhs); bool operator!=(const time_offset& lhs, const time_offset& rhs); bool operator< (const time_offset& lhs, const time_offset& rhs); bool operator<=(const time_offset& lhs, const time_offset& rhs); bool operator> (const time_offset& lhs, const time_offset& rhs); bool operator>=(const time_offset& lhs, const time_offset& rhs); std::ostream& operator<<(std::ostream& os, const time_offset& offset); std::string to_string(const time_offset& offset); // ----------------------------------------------------------------------------- struct local_datetime { local_date date{}; local_time time{}; local_datetime(local_date d, local_time t): date{d}, time{t} {} explicit local_datetime(const std::tm& t): date{t}, time{t}{} explicit local_datetime(const std::chrono::system_clock::time_point& tp); explicit local_datetime(const std::time_t t); operator std::chrono::system_clock::time_point() const; operator std::time_t() const; local_datetime() = default; ~local_datetime() = default; local_datetime(local_datetime const&) = default; local_datetime(local_datetime&&) = default; local_datetime& operator=(local_datetime const&) = default; local_datetime& operator=(local_datetime&&) = default; }; bool operator==(const local_datetime& lhs, const local_datetime& rhs); bool operator!=(const local_datetime& lhs, const local_datetime& rhs); bool operator< (const local_datetime& lhs, const local_datetime& rhs); bool operator<=(const local_datetime& lhs, const local_datetime& rhs); bool operator> (const local_datetime& lhs, const local_datetime& rhs); bool operator>=(const local_datetime& lhs, const local_datetime& rhs); std::ostream& operator<<(std::ostream& os, const local_datetime& dt); std::string to_string(const local_datetime& dt); // ----------------------------------------------------------------------------- struct offset_datetime { local_date date{}; local_time time{}; time_offset offset{}; offset_datetime(local_date d, local_time t, time_offset o) : date{d}, time{t}, offset{o} {} offset_datetime(const local_datetime& dt, time_offset o) : date{dt.date}, time{dt.time}, offset{o} {} // use the current local timezone offset explicit offset_datetime(const local_datetime& ld); explicit offset_datetime(const std::chrono::system_clock::time_point& tp); explicit offset_datetime(const std::time_t& t); explicit offset_datetime(const std::tm& t); operator std::chrono::system_clock::time_point() const; operator std::time_t() const; offset_datetime() = default; ~offset_datetime() = default; offset_datetime(offset_datetime const&) = default; offset_datetime(offset_datetime&&) = default; offset_datetime& operator=(offset_datetime const&) = default; offset_datetime& operator=(offset_datetime&&) = default; private: static time_offset get_local_offset(const std::time_t* tp); }; bool operator==(const offset_datetime& lhs, const offset_datetime& rhs); bool operator!=(const offset_datetime& lhs, const offset_datetime& rhs); bool operator< (const offset_datetime& lhs, const offset_datetime& rhs); bool operator<=(const offset_datetime& lhs, const offset_datetime& rhs); bool operator> (const offset_datetime& lhs, const offset_datetime& rhs); bool operator>=(const offset_datetime& lhs, const offset_datetime& rhs); std::ostream& operator<<(std::ostream& os, const offset_datetime& dt); std::string to_string(const offset_datetime& dt); }//toml #endif // TOML11_DATETIME_FWD_HPP toml11-4.1.0/include/toml11/fwd/error_info_fwd.hpp000066400000000000000000000054051464712047700217060ustar00rootroot00000000000000#ifndef TOML11_ERROR_INFO_FWD_HPP #define TOML11_ERROR_INFO_FWD_HPP #include "../source_location.hpp" #include "../utility.hpp" namespace toml { // error info returned from parser. struct error_info { error_info(std::string t, source_location l, std::string m, std::string s = "") : title_(std::move(t)), locations_{std::make_pair(std::move(l), std::move(m))}, suffix_(std::move(s)) {} error_info(std::string t, std::vector> l, std::string s = "") : title_(std::move(t)), locations_(std::move(l)), suffix_(std::move(s)) {} std::string const& title() const noexcept {return title_;} std::string & title() noexcept {return title_;} std::vector> const& locations() const noexcept {return locations_;} void add_locations(source_location loc, std::string msg) noexcept { locations_.emplace_back(std::move(loc), std::move(msg)); } std::string const& suffix() const noexcept {return suffix_;} std::string & suffix() noexcept {return suffix_;} private: std::string title_; std::vector> locations_; std::string suffix_; // hint or something like that }; // forward decl template class basic_value; namespace detail { inline error_info make_error_info_rec(error_info e) { return e; } inline error_info make_error_info_rec(error_info e, std::string s) { e.suffix() = s; return e; } template error_info make_error_info_rec(error_info e, const basic_value& v, std::string msg, Ts&& ... tail); template error_info make_error_info_rec(error_info e, source_location loc, std::string msg, Ts&& ... tail) { e.add_locations(std::move(loc), std::move(msg)); return make_error_info_rec(std::move(e), std::forward(tail)...); } } // detail template error_info make_error_info( std::string title, source_location loc, std::string msg, Ts&& ... tail) { error_info ei(std::move(title), std::move(loc), std::move(msg)); return detail::make_error_info_rec(ei, std::forward(tail) ... ); } std::string format_error(const std::string& errkind, const error_info& err); std::string format_error(const error_info& err); // for custom error message template std::string format_error(std::string title, source_location loc, std::string msg, Ts&& ... tail) { return format_error("", make_error_info(std::move(title), std::move(loc), std::move(msg), std::forward(tail)...)); } std::ostream& operator<<(std::ostream& os, const error_info& e); } // toml #endif // TOML11_ERROR_INFO_FWD_HPP toml11-4.1.0/include/toml11/fwd/format_fwd.hpp000066400000000000000000000164531464712047700210370ustar00rootroot00000000000000#ifndef TOML11_FORMAT_FWD_HPP #define TOML11_FORMAT_FWD_HPP #include #include #include #include #include namespace toml { // toml types with serialization info enum class indent_char : std::uint8_t { space, // use space tab, // use tab none // no indent }; std::ostream& operator<<(std::ostream& os, const indent_char& c); std::string to_string(const indent_char c); // ---------------------------------------------------------------------------- // boolean struct boolean_format_info { // nothing, for now }; inline bool operator==(const boolean_format_info&, const boolean_format_info&) noexcept { return true; } inline bool operator!=(const boolean_format_info&, const boolean_format_info&) noexcept { return false; } // ---------------------------------------------------------------------------- // integer enum class integer_format : std::uint8_t { dec = 0, bin = 1, oct = 2, hex = 3, }; std::ostream& operator<<(std::ostream& os, const integer_format f); std::string to_string(const integer_format); struct integer_format_info { integer_format fmt = integer_format::dec; bool uppercase = true; // hex with uppercase std::size_t width = 0; // minimal width (may exceed) std::size_t spacer = 0; // position of `_` (if 0, no spacer) std::string suffix = ""; // _suffix (library extension) }; bool operator==(const integer_format_info&, const integer_format_info&) noexcept; bool operator!=(const integer_format_info&, const integer_format_info&) noexcept; // ---------------------------------------------------------------------------- // floating enum class floating_format : std::uint8_t { defaultfloat = 0, fixed = 1, // does not include exponential part scientific = 2, // always include exponential part hex = 3 // hexfloat extension }; std::ostream& operator<<(std::ostream& os, const floating_format f); std::string to_string(const floating_format); struct floating_format_info { floating_format fmt = floating_format::defaultfloat; std::size_t prec = 0; // precision (if 0, use the default) std::string suffix = ""; // 1.0e+2_suffix (library extension) }; bool operator==(const floating_format_info&, const floating_format_info&) noexcept; bool operator!=(const floating_format_info&, const floating_format_info&) noexcept; // ---------------------------------------------------------------------------- // string enum class string_format : std::uint8_t { basic = 0, literal = 1, multiline_basic = 2, multiline_literal = 3 }; std::ostream& operator<<(std::ostream& os, const string_format f); std::string to_string(const string_format); struct string_format_info { string_format fmt = string_format::basic; bool start_with_newline = false; }; bool operator==(const string_format_info&, const string_format_info&) noexcept; bool operator!=(const string_format_info&, const string_format_info&) noexcept; // ---------------------------------------------------------------------------- // datetime enum class datetime_delimiter_kind : std::uint8_t { upper_T = 0, lower_t = 1, space = 2, }; std::ostream& operator<<(std::ostream& os, const datetime_delimiter_kind d); std::string to_string(const datetime_delimiter_kind); struct offset_datetime_format_info { datetime_delimiter_kind delimiter = datetime_delimiter_kind::upper_T; bool has_seconds = true; std::size_t subsecond_precision = 6; // [us] }; bool operator==(const offset_datetime_format_info&, const offset_datetime_format_info&) noexcept; bool operator!=(const offset_datetime_format_info&, const offset_datetime_format_info&) noexcept; struct local_datetime_format_info { datetime_delimiter_kind delimiter = datetime_delimiter_kind::upper_T; bool has_seconds = true; std::size_t subsecond_precision = 6; // [us] }; bool operator==(const local_datetime_format_info&, const local_datetime_format_info&) noexcept; bool operator!=(const local_datetime_format_info&, const local_datetime_format_info&) noexcept; struct local_date_format_info { // nothing, for now }; bool operator==(const local_date_format_info&, const local_date_format_info&) noexcept; bool operator!=(const local_date_format_info&, const local_date_format_info&) noexcept; struct local_time_format_info { bool has_seconds = true; std::size_t subsecond_precision = 6; // [us] }; bool operator==(const local_time_format_info&, const local_time_format_info&) noexcept; bool operator!=(const local_time_format_info&, const local_time_format_info&) noexcept; // ---------------------------------------------------------------------------- // array enum class array_format : std::uint8_t { default_format = 0, oneline = 1, multiline = 2, array_of_tables = 3 // [[format.in.this.way]] }; std::ostream& operator<<(std::ostream& os, const array_format f); std::string to_string(const array_format); struct array_format_info { array_format fmt = array_format::default_format; indent_char indent_type = indent_char::space; std::int32_t body_indent = 4; // indent in case of multiline std::int32_t closing_indent = 0; // indent of `]` }; bool operator==(const array_format_info&, const array_format_info&) noexcept; bool operator!=(const array_format_info&, const array_format_info&) noexcept; // ---------------------------------------------------------------------------- // table enum class table_format : std::uint8_t { multiline = 0, // [foo] \n bar = "baz" oneline = 1, // foo = {bar = "baz"} dotted = 2, // foo.bar = "baz" multiline_oneline = 3, // foo = { \n bar = "baz" \n } implicit = 4 // [x] defined by [x.y.z]. skip in serializer. }; std::ostream& operator<<(std::ostream& os, const table_format f); std::string to_string(const table_format); struct table_format_info { table_format fmt = table_format::multiline; indent_char indent_type = indent_char::space; std::int32_t body_indent = 0; // indent of values std::int32_t name_indent = 0; // indent of [table] std::int32_t closing_indent = 0; // in case of {inline-table} }; bool operator==(const table_format_info&, const table_format_info&) noexcept; bool operator!=(const table_format_info&, const table_format_info&) noexcept; // ---------------------------------------------------------------------------- // wrapper namespace detail { template struct value_with_format { using value_type = T; using format_type = F; value_with_format() = default; ~value_with_format() = default; value_with_format(const value_with_format&) = default; value_with_format(value_with_format&&) = default; value_with_format& operator=(const value_with_format&) = default; value_with_format& operator=(value_with_format&&) = default; value_with_format(value_type v, format_type f) : value{std::move(v)}, format{std::move(f)} {} template value_with_format(value_with_format other) : value{std::move(other.value)}, format{std::move(other.format)} {} value_type value; format_type format; }; } // detail } // namespace toml #endif // TOML11_FORMAT_FWD_HPP toml11-4.1.0/include/toml11/fwd/literal_fwd.hpp000066400000000000000000000013221464712047700211700ustar00rootroot00000000000000#ifndef TOML11_LITERAL_FWD_HPP #define TOML11_LITERAL_FWD_HPP #include "../location.hpp" #include "../types.hpp" #include "../version.hpp" // IWYU pragma: keep for TOML11_HAS_CHAR8_T namespace toml { namespace detail { // implementation ::toml::value literal_internal_impl(location loc); } // detail inline namespace literals { inline namespace toml_literals { ::toml::value operator"" _toml(const char* str, std::size_t len); #if defined(TOML11_HAS_CHAR8_T) // value of u8"" literal has been changed from char to char8_t and char8_t is // NOT compatible to char ::toml::value operator"" _toml(const char8_t* str, std::size_t len); #endif } // toml_literals } // literals } // toml #endif // TOML11_LITERAL_FWD_HPP toml11-4.1.0/include/toml11/fwd/location_fwd.hpp000066400000000000000000000074031464712047700213520ustar00rootroot00000000000000#ifndef TOML11_LOCATION_FWD_HPP #define TOML11_LOCATION_FWD_HPP #include "../result.hpp" #include #include #include namespace toml { namespace detail { class region; // fwd decl // // To represent where we are reading in the parse functions. // Since it "points" somewhere in the input stream, the length is always 1. // class location { public: using char_type = unsigned char; // must be unsigned using container_type = std::vector; using difference_type = typename container_type::difference_type; // to suppress sign-conversion warning using source_ptr = std::shared_ptr; public: location(source_ptr src, std::string src_name) : source_(std::move(src)), source_name_(std::move(src_name)), location_(0), line_number_(1) {} location(const location&) = default; location(location&&) = default; location& operator=(const location&) = default; location& operator=(location&&) = default; ~location() = default; void advance(std::size_t n = 1) noexcept; void retrace(std::size_t n = 1) noexcept; bool is_ok() const noexcept { return static_cast(this->source_); } bool eof() const noexcept; char_type current() const; char_type peek(); std::size_t get_location() const noexcept { return this->location_; } void set_location(const std::size_t loc) noexcept; std::size_t line_number() const noexcept { return this->line_number_; } std::string get_line() const; std::size_t column_number() const noexcept; source_ptr const& source() const noexcept {return this->source_;} std::string const& source_name() const noexcept {return this->source_name_;} private: void advance_line_number(const std::size_t n); void retrace_line_number(const std::size_t n); private: friend region; private: source_ptr source_; std::string source_name_; std::size_t location_; // std::vector<>::difference_type is signed std::size_t line_number_; }; bool operator==(const location& lhs, const location& rhs) noexcept; bool operator!=(const location& lhs, const location& rhs); location prev(const location& loc); location next(const location& loc); location make_temporary_location(const std::string& str) noexcept; template result find_if(const location& first, const location& last, const F& func) noexcept { if(first.source() != last.source()) { return err(); } if(first.get_location() >= last.get_location()) { return err(); } auto loc = first; while(loc.get_location() != last.get_location()) { if(func(loc.current())) { return ok(loc); } loc.advance(); } return err(); } template result rfind_if(location first, const location& last, const F& func) { if(first.source() != last.source()) { return err(); } if(first.get_location() >= last.get_location()) { return err(); } auto loc = last; while(loc.get_location() != first.get_location()) { if(func(loc.current())) { return ok(loc); } loc.retrace(); } if(func(first.current())) { return ok(first); } return err(); } result find(const location& first, const location& last, const location::char_type val); result rfind(const location& first, const location& last, const location::char_type val); std::size_t count(const location& first, const location& last, const location::char_type& c); } // detail } // toml #endif // TOML11_LOCATION_FWD_HPP toml11-4.1.0/include/toml11/fwd/region_fwd.hpp000066400000000000000000000053411464712047700210240ustar00rootroot00000000000000#ifndef TOML11_REGION_FWD_HPP #define TOML11_REGION_FWD_HPP #include "../location.hpp" #include #include #include namespace toml { namespace detail { // // To represent where is a toml::value defined, or where does an error occur. // Stored in toml::value. source_location will be constructed based on this. // class region { public: using char_type = location::char_type; using container_type = location::container_type; using difference_type = location::difference_type; using source_ptr = location::source_ptr; using iterator = typename container_type::iterator; using const_iterator = typename container_type::const_iterator; public: // a value that is constructed manually does not have input stream info region() : source_(nullptr), source_name_(""), length_(0), first_line_(0), first_column_(0), last_line_(0), last_column_(0) {} // a value defined in [first, last). // Those source must be the same. Instread, `region` does not make sense. region(const location& first, const location& last); // shorthand of [loc, loc+1) explicit region(const location& loc); ~region() = default; region(const region&) = default; region(region&&) = default; region& operator=(const region&) = default; region& operator=(region&&) = default; bool is_ok() const noexcept { return static_cast(this->source_); } operator bool() const noexcept { return this->is_ok(); } std::size_t length() const noexcept {return this->length_;} std::size_t first_line_number() const noexcept { return this->first_line_; } std::size_t first_column_number() const noexcept { return this->first_column_; } std::size_t last_line_number() const noexcept { return this->last_line_; } std::size_t last_column_number() const noexcept { return this->last_column_; } char_type at(std::size_t i) const; const_iterator begin() const noexcept; const_iterator end() const noexcept; const_iterator cbegin() const noexcept; const_iterator cend() const noexcept; std::string as_string() const; std::vector as_lines() const; source_ptr const& source() const noexcept {return this->source_;} std::string const& source_name() const noexcept {return this->source_name_;} private: source_ptr source_; std::string source_name_; std::size_t length_; std::size_t first_; std::size_t first_line_; std::size_t first_column_; std::size_t last_; std::size_t last_line_; std::size_t last_column_; }; } // namespace detail } // namespace toml #endif // TOML11_REGION_FWD_HPP toml11-4.1.0/include/toml11/fwd/scanner_fwd.hpp000066400000000000000000000225171464712047700211760ustar00rootroot00000000000000#ifndef TOML11_SCANNER_FWD_HPP #define TOML11_SCANNER_FWD_HPP #include "../region.hpp" #include #include #include #include #include #include #include namespace toml { namespace detail { class scanner_base { public: virtual ~scanner_base() = default; virtual region scan(location& loc) const = 0; virtual scanner_base* clone() const = 0; // returns expected character or set of characters or literal. // to show the error location, it changes loc (in `sequence`, especially). virtual std::string expected_chars(location& loc) const = 0; virtual std::string name() const = 0; }; // make `scanner*` copyable struct scanner_storage { template>::value, std::nullptr_t> = nullptr> explicit scanner_storage(Scanner&& s) : scanner_(cxx::make_unique>(std::forward(s))) {} ~scanner_storage() = default; scanner_storage(const scanner_storage& other); scanner_storage& operator=(const scanner_storage& other); scanner_storage(scanner_storage&&) = default; scanner_storage& operator=(scanner_storage&&) = default; bool is_ok() const noexcept {return static_cast(scanner_);} region scan(location& loc) const; std::string expected_chars(location& loc) const; scanner_base& get() const noexcept; std::string name() const; private: std::unique_ptr scanner_; }; // ---------------------------------------------------------------------------- class character final : public scanner_base { public: using char_type = location::char_type; public: explicit character(const char_type c) noexcept : value_(c) {} ~character() override = default; region scan(location& loc) const override; std::string expected_chars(location&) const override; scanner_base* clone() const override; std::string name() const override; private: char_type value_; }; // ---------------------------------------------------------------------------- class character_either final : public scanner_base { public: using char_type = location::char_type; public: explicit character_either(std::initializer_list cs) noexcept : chars_(std::move(cs)) { assert(! this->chars_.empty()); } template explicit character_either(const char (&cs)[N]) noexcept : chars_(N-1, '\0') { static_assert(N >= 1, ""); for(std::size_t i=0; i+1 chars_; }; // ---------------------------------------------------------------------------- class character_in_range final : public scanner_base { public: using char_type = location::char_type; public: explicit character_in_range(const char_type from, const char_type to) noexcept : from_(from), to_(to) {} ~character_in_range() override = default; region scan(location& loc) const override; std::string expected_chars(location&) const override; scanner_base* clone() const override; std::string name() const override; private: char_type from_; char_type to_; }; // ---------------------------------------------------------------------------- class literal final : public scanner_base { public: using char_type = location::char_type; public: template explicit literal(const char (&cs)[N]) noexcept : value_(cs), size_(N-1) // remove null character at the end {} ~literal() override = default; region scan(location& loc) const override; std::string expected_chars(location&) const override; scanner_base* clone() const override; std::string name() const override; private: const char* value_; std::size_t size_; }; // ---------------------------------------------------------------------------- class sequence final: public scanner_base { public: using char_type = location::char_type; public: template explicit sequence(Ts&& ... args) { push_back_all(std::forward(args)...); } sequence(const sequence&) = default; sequence(sequence&&) = default; sequence& operator=(const sequence&) = default; sequence& operator=(sequence&&) = default; ~sequence() override = default; region scan(location& loc) const override; std::string expected_chars(location& loc) const override; scanner_base* clone() const override; template void push_back(Scanner&& other_scanner) { this->others_.emplace_back(std::forward(other_scanner)); } std::string name() const override; private: void push_back_all() { return; } template void push_back_all(T&& head, Ts&& ... args) { others_.emplace_back(std::forward(head)); push_back_all(std::forward(args)...); return; } private: std::vector others_; }; // ---------------------------------------------------------------------------- class either final: public scanner_base { public: using char_type = location::char_type; public: template explicit either(Ts&& ... args) { push_back_all(std::forward(args)...); } either(const either&) = default; either(either&&) = default; either& operator=(const either&) = default; either& operator=(either&&) = default; ~either() override = default; region scan(location& loc) const override; std::string expected_chars(location& loc) const override; scanner_base* clone() const override; template void push_back(Scanner&& other_scanner) { this->others_.emplace_back(std::forward(other_scanner)); } std::string name() const override; private: void push_back_all() { return; } template void push_back_all(T&& head, Ts&& ... args) { others_.emplace_back(std::forward(head)); push_back_all(std::forward(args)...); return; } private: std::vector others_; }; // ---------------------------------------------------------------------------- class repeat_exact final: public scanner_base { public: using char_type = location::char_type; public: template repeat_exact(const std::size_t length, Scanner&& other) : length_(length), other_(std::forward(other)) {} repeat_exact(const repeat_exact&) = default; repeat_exact(repeat_exact&&) = default; repeat_exact& operator=(const repeat_exact&) = default; repeat_exact& operator=(repeat_exact&&) = default; ~repeat_exact() override = default; region scan(location& loc) const override; std::string expected_chars(location& loc) const override; scanner_base* clone() const override; std::string name() const override; private: std::size_t length_; scanner_storage other_; }; // ---------------------------------------------------------------------------- class repeat_at_least final: public scanner_base { public: using char_type = location::char_type; public: template repeat_at_least(const std::size_t length, Scanner&& s) : length_(length), other_(std::forward(s)) {} repeat_at_least(const repeat_at_least&) = default; repeat_at_least(repeat_at_least&&) = default; repeat_at_least& operator=(const repeat_at_least&) = default; repeat_at_least& operator=(repeat_at_least&&) = default; ~repeat_at_least() override = default; region scan(location& loc) const override; std::string expected_chars(location& loc) const override; scanner_base* clone() const override; std::string name() const override; private: std::size_t length_; scanner_storage other_; }; // ---------------------------------------------------------------------------- class maybe final: public scanner_base { public: using char_type = location::char_type; public: template explicit maybe(Scanner&& s) : other_(std::forward(s)) {} maybe(const maybe&) = default; maybe(maybe&&) = default; maybe& operator=(const maybe&) = default; maybe& operator=(maybe&&) = default; ~maybe() override = default; region scan(location& loc) const override; std::string expected_chars(location&) const override; scanner_base* clone() const override; std::string name() const override; private: scanner_storage other_; }; } // detail } // toml #endif // TOML11_SCANNER_FWD_HPP toml11-4.1.0/include/toml11/fwd/source_location_fwd.hpp000066400000000000000000000067161464712047700227400ustar00rootroot00000000000000#ifndef TOML11_SOURCE_LOCATION_FWD_HPP #define TOML11_SOURCE_LOCATION_FWD_HPP #include "../region.hpp" #include #include #include namespace toml { // A struct to contain location in a toml file. struct source_location { public: explicit source_location(const detail::region& r); ~source_location() = default; source_location(source_location const&) = default; source_location(source_location &&) = default; source_location& operator=(source_location const&) = default; source_location& operator=(source_location &&) = default; bool is_ok() const noexcept {return this->is_ok_;} std::size_t length() const noexcept {return this->length_;} std::size_t first_line_number() const noexcept {return this->first_line_;} std::size_t first_column_number() const noexcept {return this->first_column_;} std::size_t last_line_number() const noexcept {return this->last_line_;} std::size_t last_column_number() const noexcept {return this->last_column_;} std::string const& file_name() const noexcept {return this->file_name_;} std::size_t num_lines() const noexcept {return this->line_str_.size();} std::string const& first_line() const; std::string const& last_line() const; std::vector const& lines() const noexcept {return line_str_;} private: bool is_ok_; std::size_t first_line_; std::size_t first_column_; std::size_t last_line_; std::size_t last_column_; std::size_t length_; std::string file_name_; std::vector line_str_; }; namespace detail { std::size_t integer_width_base10(std::size_t i) noexcept; inline std::size_t line_width() noexcept {return 0;} template std::size_t line_width(const source_location& loc, const std::string& /*msg*/, const Ts& ... tail) noexcept { return (std::max)( integer_width_base10(loc.last_line_number()), line_width(tail...)); } std::ostringstream& format_filename(std::ostringstream& oss, const source_location& loc); std::ostringstream& format_empty_line(std::ostringstream& oss, const std::size_t lnw); std::ostringstream& format_line(std::ostringstream& oss, const std::size_t lnw, const std::size_t linenum, const std::string& line); std::ostringstream& format_underline(std::ostringstream& oss, const std::size_t lnw, const std::size_t col, const std::size_t len, const std::string& msg); std::string format_location_impl(const std::size_t lnw, const std::string& prev_fname, const source_location& loc, const std::string& msg); inline std::string format_location_rec(const std::size_t, const std::string&) { return ""; } template std::string format_location_rec(const std::size_t lnw, const std::string& prev_fname, const source_location& loc, const std::string& msg, const Ts& ... tail) { return format_location_impl(lnw, prev_fname, loc, msg) + format_location_rec(lnw, loc.file_name(), tail...); } } // namespace detail // format a location info without title template std::string format_location( const source_location& loc, const std::string& msg, const Ts& ... tail) { const auto lnw = detail::line_width(loc, msg, tail...); const std::string f(""); // at the 1st iteration, no prev_filename is given return detail::format_location_rec(lnw, f, loc, msg, tail...); } } // toml #endif // TOML11_SOURCE_LOCATION_FWD_HPP toml11-4.1.0/include/toml11/fwd/syntax_fwd.hpp000066400000000000000000000160011464712047700210620ustar00rootroot00000000000000#ifndef TOML11_SYNTAX_FWD_HPP #define TOML11_SYNTAX_FWD_HPP #include "../scanner.hpp" #include "../spec.hpp" namespace toml { namespace detail { namespace syntax { using char_type = location::char_type; // =========================================================================== // UTF-8 // avoid redundant representation and out-of-unicode sequence character_in_range utf8_1byte (const spec&); sequence utf8_2bytes(const spec&); sequence utf8_3bytes(const spec&); sequence utf8_4bytes(const spec&); class non_ascii final : public scanner_base { public: using char_type = location::char_type; public: explicit non_ascii(const spec& s) noexcept; ~non_ascii() override = default; region scan(location& loc) const override { return scanner_.scan(loc); } std::string expected_chars(location&) const override { return "non-ascii utf-8 bytes"; } scanner_base* clone() const override { return new non_ascii(*this); } std::string name() const override { return "non_ascii"; } private: either scanner_; }; // =========================================================================== // Whitespace character_either wschar(const spec&); repeat_at_least ws(const spec& s); // =========================================================================== // Newline either newline(const spec&); // =========================================================================== // Comments either allowed_comment_char(const spec& s); // XXX Note that it does not take newline sequence comment(const spec& s); // =========================================================================== // Boolean either boolean(const spec&); // =========================================================================== // Integer class digit final : public scanner_base { public: using char_type = location::char_type; public: explicit digit(const spec&) noexcept; ~digit() override = default; region scan(location& loc) const override { return scanner_.scan(loc); } std::string expected_chars(location&) const override { return "digit [0-9]"; } scanner_base* clone() const override { return new digit(*this); } std::string name() const override { return "digit"; } private: character_in_range scanner_; }; class alpha final : public scanner_base { public: using char_type = location::char_type; public: explicit alpha(const spec&) noexcept; ~alpha() override = default; region scan(location& loc) const override { return scanner_.scan(loc); } std::string expected_chars(location&) const override { return "alpha [a-zA-Z]"; } scanner_base* clone() const override { return new alpha(*this); } std::string name() const override { return "alpha"; } private: either scanner_; }; class hexdig final : public scanner_base { public: using char_type = location::char_type; public: explicit hexdig(const spec& s) noexcept; ~hexdig() override = default; region scan(location& loc) const override { return scanner_.scan(loc); } std::string expected_chars(location&) const override { return "hex [0-9a-fA-F]"; } scanner_base* clone() const override { return new hexdig(*this); } std::string name() const override { return "hexdig"; } private: either scanner_; }; sequence num_suffix(const spec& s); sequence dec_int(const spec& s); sequence hex_int(const spec& s); sequence oct_int(const spec&); sequence bin_int(const spec&); either integer(const spec& s); // =========================================================================== // Floating sequence zero_prefixable_int(const spec& s); sequence fractional_part(const spec& s); sequence exponent_part(const spec& s); sequence hex_floating(const spec& s); either floating(const spec& s); // =========================================================================== // Datetime sequence local_date(const spec& s); sequence local_time(const spec& s); either time_offset(const spec& s); sequence full_time(const spec& s); character_either time_delim(const spec&); sequence local_datetime(const spec& s); sequence offset_datetime(const spec& s); // =========================================================================== // String sequence escaped(const spec& s); either basic_char(const spec& s); sequence basic_string(const spec& s); // --------------------------------------------------------------------------- // multiline string sequence escaped_newline(const spec& s); sequence ml_basic_string(const spec& s); // --------------------------------------------------------------------------- // literal string either literal_char(const spec& s); sequence literal_string(const spec& s); sequence ml_literal_string(const spec& s); either string(const spec& s); // =========================================================================== // Keys // to keep `expected_chars` simple class non_ascii_key_char final : public scanner_base { public: using char_type = location::char_type; private: using in_range = character_in_range; // make definition short public: explicit non_ascii_key_char(const spec& s) noexcept; ~non_ascii_key_char() override = default; region scan(location& loc) const override; std::string expected_chars(location&) const override { return "bare key non-ASCII script"; } scanner_base* clone() const override { return new non_ascii_key_char(*this); } std::string name() const override { return "non-ASCII bare key"; } private: std::uint32_t read_utf8(location& loc) const; }; repeat_at_least unquoted_key(const spec& s); either quoted_key(const spec& s); either simple_key(const spec& s); sequence dot_sep(const spec& s); sequence dotted_key(const spec& s); class key final : public scanner_base { public: using char_type = location::char_type; public: explicit key(const spec& s) noexcept; ~key() override = default; region scan(location& loc) const override { return scanner_.scan(loc); } std::string expected_chars(location&) const override { return "basic key([a-zA-Z0-9_-]) or quoted key(\" or ')"; } scanner_base* clone() const override { return new key(*this); } std::string name() const override { return "key"; } private: either scanner_; }; sequence keyval_sep(const spec& s); // =========================================================================== // Table key sequence std_table(const spec& s); sequence array_table(const spec& s); // =========================================================================== // extension: null literal null_value(const spec&); } // namespace syntax } // namespace detail } // namespace toml #endif // TOML11_SYNTAX_FWD_HPP toml11-4.1.0/include/toml11/fwd/value_t_fwd.hpp000066400000000000000000000135601464712047700212020ustar00rootroot00000000000000#ifndef TOML11_VALUE_T_FWD_HPP #define TOML11_VALUE_T_FWD_HPP #include "../compat.hpp" #include "../format.hpp" #include #include #include #include namespace toml { // forward decl template class basic_value; // ---------------------------------------------------------------------------- // enum representing toml types enum class value_t : std::uint8_t { empty = 0, boolean = 1, integer = 2, floating = 3, string = 4, offset_datetime = 5, local_datetime = 6, local_date = 7, local_time = 8, array = 9, table = 10 }; std::ostream& operator<<(std::ostream& os, value_t t); std::string to_string(value_t t); // ---------------------------------------------------------------------------- // meta functions for internal use namespace detail { template using value_t_constant = std::integral_constant; template struct type_to_enum : value_t_constant {}; template struct type_to_enum : value_t_constant {}; template struct type_to_enum : value_t_constant {}; template struct type_to_enum : value_t_constant {}; template struct type_to_enum : value_t_constant {}; template struct type_to_enum : value_t_constant {}; template struct type_to_enum : value_t_constant {}; template struct type_to_enum : value_t_constant {}; template struct type_to_enum : value_t_constant {}; template struct type_to_enum : value_t_constant {}; template struct type_to_enum : value_t_constant {}; template struct enum_to_type { using type = void; }; template struct enum_to_type { using type = typename V::boolean_type ; }; template struct enum_to_type { using type = typename V::integer_type ; }; template struct enum_to_type { using type = typename V::floating_type ; }; template struct enum_to_type { using type = typename V::string_type ; }; template struct enum_to_type { using type = typename V::offset_datetime_type; }; template struct enum_to_type { using type = typename V::local_datetime_type ; }; template struct enum_to_type { using type = typename V::local_date_type ; }; template struct enum_to_type { using type = typename V::local_time_type ; }; template struct enum_to_type { using type = typename V::array_type ; }; template struct enum_to_type { using type = typename V::table_type ; }; template using enum_to_type_t = typename enum_to_type::type; template struct enum_to_fmt_type { using type = void; }; template<> struct enum_to_fmt_type { using type = boolean_format_info ; }; template<> struct enum_to_fmt_type { using type = integer_format_info ; }; template<> struct enum_to_fmt_type { using type = floating_format_info ; }; template<> struct enum_to_fmt_type { using type = string_format_info ; }; template<> struct enum_to_fmt_type { using type = offset_datetime_format_info; }; template<> struct enum_to_fmt_type { using type = local_datetime_format_info ; }; template<> struct enum_to_fmt_type { using type = local_date_format_info ; }; template<> struct enum_to_fmt_type { using type = local_time_format_info ; }; template<> struct enum_to_fmt_type { using type = array_format_info ; }; template<> struct enum_to_fmt_type { using type = table_format_info ; }; template using enum_to_fmt_type_t = typename enum_to_fmt_type::type; template struct is_exact_toml_type0 : cxx::disjunction< std::is_same, std::is_same, std::is_same, std::is_same, std::is_same, std::is_same, std::is_same, std::is_same, std::is_same, std::is_same >{}; template struct is_exact_toml_type: is_exact_toml_type0, V> {}; template struct is_not_toml_type : cxx::negation> {}; } // namespace detail } // namespace toml #endif // TOML11_VALUE_T_FWD_HPP toml11-4.1.0/include/toml11/get.hpp000066400000000000000000000505601464712047700167030ustar00rootroot00000000000000#ifndef TOML11_GET_HPP #define TOML11_GET_HPP #include #include "from.hpp" #include "types.hpp" #include "value.hpp" #if defined(TOML11_HAS_STRING_VIEW) #include #endif // string_view namespace toml { // ============================================================================ // T is toml::value; identity transformation. template cxx::enable_if_t>::value, T>& get(basic_value& v) { return v; } template cxx::enable_if_t>::value, T> const& get(const basic_value& v) { return v; } template cxx::enable_if_t>::value, T> get(basic_value&& v) { return basic_value(std::move(v)); } // ============================================================================ // exact toml::* type template cxx::enable_if_t>::value, T> & get(basic_value& v) { constexpr auto ty = detail::type_to_enum>::value; return detail::getter::get(v); } template cxx::enable_if_t>::value, T> const& get(const basic_value& v) { constexpr auto ty = detail::type_to_enum>::value; return detail::getter::get(v); } template cxx::enable_if_t>::value, T> get(basic_value&& v) { constexpr auto ty = detail::type_to_enum>::value; return detail::getter::get(std::move(v)); } // ============================================================================ // T is toml::basic_value template cxx::enable_if_t, cxx::negation>> >::value, T> get(basic_value v) { return T(std::move(v)); } // ============================================================================ // integer convertible from toml::value::integer_type template cxx::enable_if_t, cxx::negation>, detail::is_not_toml_type>, cxx::negation>, cxx::negation> >::value, T> get(const basic_value& v) { return static_cast(v.as_integer()); } // ============================================================================ // floating point convertible from toml::value::floating_type template cxx::enable_if_t, detail::is_not_toml_type>, cxx::negation>, cxx::negation> >::value, T> get(const basic_value& v) { return static_cast(v.as_floating()); } // ============================================================================ // std::string with different char/trait/allocator template cxx::enable_if_t>, detail::is_1byte_std_basic_string >::value, T> get(const basic_value& v) { return detail::string_conv>(v.as_string()); } // ============================================================================ // std::string_view #if defined(TOML11_HAS_STRING_VIEW) template cxx::enable_if_t::string_type>::value, T> get(const basic_value& v) { return T(v.as_string()); } #endif // string_view // ============================================================================ // std::chrono::duration from toml::local_time template cxx::enable_if_t::value, T> get(const basic_value& v) { return std::chrono::duration_cast( std::chrono::nanoseconds(v.as_local_time())); } // ============================================================================ // std::chrono::system_clock::time_point from toml::datetime variants template cxx::enable_if_t< std::is_same::value, T> get(const basic_value& v) { switch(v.type()) { case value_t::local_date: { return std::chrono::system_clock::time_point(v.as_local_date()); } case value_t::local_datetime: { return std::chrono::system_clock::time_point(v.as_local_datetime()); } case value_t::offset_datetime: { return std::chrono::system_clock::time_point(v.as_offset_datetime()); } default: { const auto loc = v.location(); throw type_error(format_error("toml::get: " "bad_cast to std::chrono::system_clock::time_point", loc, "the actual type is " + to_string(v.type())), loc); } } } // ============================================================================ // forward declaration to use this recursively. ignore this and go ahead. // array-like (w/ push_back) template cxx::enable_if_t, // T is a container detail::has_push_back_method, // .push_back() works detail::is_not_toml_type>, // but not toml::array cxx::negation>, // but not std::basic_string #if defined(TOML11_HAS_STRING_VIEW) cxx::negation>, // but not std::basic_string_view #endif cxx::negation>, // no T.from_toml() cxx::negation>, // no toml::from cxx::negation&>> >::value, T> get(const basic_value&); // std::array template cxx::enable_if_t::value, T> get(const basic_value&); // std::forward_list template cxx::enable_if_t::value, T> get(const basic_value&); // std::pair template cxx::enable_if_t::value, T> get(const basic_value&); // std::tuple template cxx::enable_if_t::value, T> get(const basic_value&); // std::map (key is convertible from toml::value::key_type) template cxx::enable_if_t, // T is map detail::is_not_toml_type>, // but not toml::table std::is_convertible::key_type, typename T::key_type>, // keys are convertible cxx::negation>, // no T.from_toml() cxx::negation>, // no toml::from cxx::negation&>> >::value, T> get(const basic_value& v); // std::map (key is not convertible from toml::value::key_type, but // is a std::basic_string) template cxx::enable_if_t, // T is map detail::is_not_toml_type>, // but not toml::table cxx::negation::key_type, typename T::key_type>>, // keys are NOT convertible detail::is_1byte_std_basic_string, // is std::basic_string cxx::negation>, // no T.from_toml() cxx::negation>, // no toml::from cxx::negation&>> >::value, T> get(const basic_value& v); // toml::from::from_toml(v) template cxx::enable_if_t::value, T> get(const basic_value&); // has T.from_toml(v) but no from template cxx::enable_if_t, // has T.from_toml() cxx::negation>, // no toml::from std::is_default_constructible // T{} works >::value, T> get(const basic_value&); // T(const toml::value&) and T is not toml::basic_value, // and it does not have `from` nor `from_toml`. template cxx::enable_if_t&>, // has T(const basic_value&) cxx::negation>, // but not basic_value itself cxx::negation>, // no .from_toml() cxx::negation> // no toml::from >::value, T> get(const basic_value&); // ============================================================================ // array-like types; most likely STL container, like std::vector, etc. template cxx::enable_if_t, // T is a container detail::has_push_back_method, // .push_back() works detail::is_not_toml_type>, // but not toml::array cxx::negation>, // but not std::basic_string #if defined(TOML11_HAS_STRING_VIEW) cxx::negation>, // but not std::basic_string_view #endif cxx::negation>, // no T.from_toml() cxx::negation>, // no toml::from cxx::negation&>> >::value, T> get(const basic_value& v) { using value_type = typename T::value_type; const auto& a = v.as_array(); T container; detail::try_reserve(container, a.size()); // if T has .reserve(), call it for(const auto& elem : a) { container.push_back(get(elem)); } return container; } // ============================================================================ // std::array template cxx::enable_if_t::value, T> get(const basic_value& v) { using value_type = typename T::value_type; const auto& a = v.as_array(); T container; if(a.size() != container.size()) { const auto loc = v.location(); throw std::out_of_range(format_error("toml::get: while converting to an array: " " array size is " + std::to_string(container.size()) + " but there are " + std::to_string(a.size()) + " elements in toml array.", loc, "here")); } for(std::size_t i=0; i(a.at(i)); } return container; } // ============================================================================ // std::forward_list template cxx::enable_if_t::value, T> get(const basic_value& v) { using value_type = typename T::value_type; T container; for(const auto& elem : v.as_array()) { container.push_front(get(elem)); } container.reverse(); return container; } // ============================================================================ // std::pair template cxx::enable_if_t::value, T> get(const basic_value& v) { using first_type = typename T::first_type; using second_type = typename T::second_type; const auto& ar = v.as_array(); if(ar.size() != 2) { const auto loc = v.location(); throw std::out_of_range(format_error("toml::get: while converting std::pair: " " but there are " + std::to_string(ar.size()) + " > 2 elements in toml array.", loc, "here")); } return std::make_pair(::toml::get(ar.at(0)), ::toml::get(ar.at(1))); } // ============================================================================ // std::tuple. namespace detail { template T get_tuple_impl(const Array& a, cxx::index_sequence) { return std::make_tuple( ::toml::get::type>(a.at(I))...); } } // detail template cxx::enable_if_t::value, T> get(const basic_value& v) { const auto& ar = v.as_array(); if(ar.size() != std::tuple_size::value) { const auto loc = v.location(); throw std::out_of_range(format_error("toml::get: while converting std::tuple: " " there are " + std::to_string(ar.size()) + " > " + std::to_string(std::tuple_size::value) + " elements in toml array.", loc, "here")); } return detail::get_tuple_impl(ar, cxx::make_index_sequence::value>{}); } // ============================================================================ // map-like types; most likely STL map, like std::map or std::unordered_map. // key is convertible from toml::value::key_type template cxx::enable_if_t, // T is map detail::is_not_toml_type>, // but not toml::table std::is_convertible::key_type, typename T::key_type>, // keys are convertible cxx::negation>, // no T.from_toml() cxx::negation>, // no toml::from cxx::negation&>> >::value, T> get(const basic_value& v) { using key_type = typename T::key_type; using mapped_type = typename T::mapped_type; static_assert( std::is_convertible::key_type, key_type>::value, "toml::get only supports map type of which key_type is " "convertible from toml::basic_value::key_type."); T m; for(const auto& kv : v.as_table()) { m.emplace(key_type(kv.first), get(kv.second)); } return m; } // key is NOT convertible from toml::value::key_type but std::basic_string template cxx::enable_if_t, // T is map detail::is_not_toml_type>, // but not toml::table cxx::negation::key_type, typename T::key_type>>, // keys are NOT convertible detail::is_1byte_std_basic_string, // is std::basic_string cxx::negation>, // no T.from_toml() cxx::negation>, // no toml::from cxx::negation&>> >::value, T> get(const basic_value& v) { using key_type = typename T::key_type; using mapped_type = typename T::mapped_type; T m; for(const auto& kv : v.as_table()) { m.emplace(detail::string_conv(kv.first), get(kv.second)); } return m; } // ============================================================================ // user-defined, but convertible types. // toml::from template cxx::enable_if_t::value, T> get(const basic_value& v) { return ::toml::from::from_toml(v); } // has T.from_toml(v) but no from template cxx::enable_if_t, // has T.from_toml() cxx::negation>, // no toml::from std::is_default_constructible // T{} works >::value, T> get(const basic_value& v) { T ud; ud.from_toml(v); return ud; } // T(const toml::value&) and T is not toml::basic_value, // and it does not have `from` nor `from_toml`. template cxx::enable_if_t&>, // has T(const basic_value&) cxx::negation>, // but not basic_value itself cxx::negation>, // no .from_toml() cxx::negation> // no toml::from >::value, T> get(const basic_value& v) { return T(v); } // ============================================================================ // get_or(value, fallback) template cxx::enable_if_t::value, basic_value> const& get_or(const basic_value& v, const basic_value&) { return v; } template cxx::enable_if_t::value, basic_value>& get_or(basic_value& v, basic_value&) { return v; } template cxx::enable_if_t::value, basic_value> get_or(basic_value&& v, basic_value&&) { return v; } // ---------------------------------------------------------------------------- // specialization for the exact toml types (return type becomes lvalue ref) template cxx::enable_if_t< detail::is_exact_toml_type>::value, T> const& get_or(const basic_value& v, const T& opt) noexcept { try { return get>(v); } catch(...) { return opt; } } template cxx::enable_if_t>, detail::is_exact_toml_type> >::value, T>& get_or(basic_value& v, T& opt) noexcept { try { return get>(v); } catch(...) { return opt; } } template cxx::enable_if_t, basic_value>::value, cxx::remove_cvref_t> get_or(basic_value&& v, T&& opt) noexcept { try { return get>(std::move(v)); } catch(...) { return cxx::remove_cvref_t(std::forward(opt)); } } // ---------------------------------------------------------------------------- // specialization for string literal // template // typename basic_value::string_type // get_or(const basic_value& v, // const typename basic_value::string_type::value_type (&opt)[N]) // { // try // { // return v.as_string(); // } // catch(...) // { // return typename basic_value::string_type(opt); // } // } // // The above only matches to the literal, like `get_or(v, "foo");` but not // ```cpp // const auto opt = "foo"; // const auto str = get_or(v, opt); // ``` // . And the latter causes an error. // To match to both `"foo"` and `const auto opt = "foo"`, we take a pointer to // a character here. template typename basic_value::string_type get_or(const basic_value& v, const typename basic_value::string_type::value_type* opt) { try { return v.as_string(); } catch(...) { return typename basic_value::string_type(opt); } } // ---------------------------------------------------------------------------- // others (require type conversion and return type cannot be lvalue reference) template cxx::enable_if_t>, cxx::negation>>, cxx::negation, typename basic_value::string_type::value_type const*>> >::value, cxx::remove_cvref_t> get_or(const basic_value& v, T&& opt) { try { return get>(v); } catch(...) { return cxx::remove_cvref_t(std::forward(opt)); } } } // toml #endif // TOML11_GET_HPP toml11-4.1.0/include/toml11/impl/000077500000000000000000000000001464712047700163465ustar00rootroot00000000000000toml11-4.1.0/include/toml11/impl/color_impl.hpp000066400000000000000000000034231464712047700212200ustar00rootroot00000000000000#ifndef TOML11_COLOR_IMPL_HPP #define TOML11_COLOR_IMPL_HPP #include "../fwd/color_fwd.hpp" #include "../version.hpp" #include namespace toml { namespace color { // put ANSI escape sequence to ostream inline namespace ansi { TOML11_INLINE std::ostream& reset(std::ostream& os) { if(detail::color_status().should_color()) {os << "\033[00m";} return os; } TOML11_INLINE std::ostream& bold(std::ostream& os) { if(detail::color_status().should_color()) {os << "\033[01m";} return os; } TOML11_INLINE std::ostream& grey(std::ostream& os) { if(detail::color_status().should_color()) {os << "\033[30m";} return os; } TOML11_INLINE std::ostream& gray(std::ostream& os) { if(detail::color_status().should_color()) {os << "\033[30m";} return os; } TOML11_INLINE std::ostream& red(std::ostream& os) { if(detail::color_status().should_color()) {os << "\033[31m";} return os; } TOML11_INLINE std::ostream& green(std::ostream& os) { if(detail::color_status().should_color()) {os << "\033[32m";} return os; } TOML11_INLINE std::ostream& yellow(std::ostream& os) { if(detail::color_status().should_color()) {os << "\033[33m";} return os; } TOML11_INLINE std::ostream& blue(std::ostream& os) { if(detail::color_status().should_color()) {os << "\033[34m";} return os; } TOML11_INLINE std::ostream& magenta(std::ostream& os) { if(detail::color_status().should_color()) {os << "\033[35m";} return os; } TOML11_INLINE std::ostream& cyan (std::ostream& os) { if(detail::color_status().should_color()) {os << "\033[36m";} return os; } TOML11_INLINE std::ostream& white (std::ostream& os) { if(detail::color_status().should_color()) {os << "\033[37m";} return os; } } // ansi } // color } // toml #endif // TOML11_COLOR_IMPL_HPP toml11-4.1.0/include/toml11/impl/comments_impl.hpp000066400000000000000000000030331464712047700217240ustar00rootroot00000000000000#ifndef TOML11_COMMENTS_IMPL_HPP #define TOML11_COMMENTS_IMPL_HPP #include "../fwd/comments_fwd.hpp" // IWYU pragma: keep namespace toml { TOML11_INLINE bool operator==(const preserve_comments& lhs, const preserve_comments& rhs) {return lhs.comments == rhs.comments;} TOML11_INLINE bool operator!=(const preserve_comments& lhs, const preserve_comments& rhs) {return lhs.comments != rhs.comments;} TOML11_INLINE bool operator< (const preserve_comments& lhs, const preserve_comments& rhs) {return lhs.comments < rhs.comments;} TOML11_INLINE bool operator<=(const preserve_comments& lhs, const preserve_comments& rhs) {return lhs.comments <= rhs.comments;} TOML11_INLINE bool operator> (const preserve_comments& lhs, const preserve_comments& rhs) {return lhs.comments > rhs.comments;} TOML11_INLINE bool operator>=(const preserve_comments& lhs, const preserve_comments& rhs) {return lhs.comments >= rhs.comments;} TOML11_INLINE void swap(preserve_comments& lhs, preserve_comments& rhs) { lhs.swap(rhs); return; } TOML11_INLINE void swap(preserve_comments& lhs, std::vector& rhs) { lhs.comments.swap(rhs); return; } TOML11_INLINE void swap(std::vector& lhs, preserve_comments& rhs) { lhs.swap(rhs.comments); return; } TOML11_INLINE std::ostream& operator<<(std::ostream& os, const preserve_comments& com) { for(const auto& c : com) { if(c.front() != '#') { os << '#'; } os << c << '\n'; } return os; } } // toml11 #endif // TOML11_COMMENTS_IMPL_HPP toml11-4.1.0/include/toml11/impl/datetime_impl.hpp000066400000000000000000000427221464712047700217030ustar00rootroot00000000000000#ifndef TOML11_DATETIME_IMPL_HPP #define TOML11_DATETIME_IMPL_HPP #include "../fwd/datetime_fwd.hpp" #include "../version.hpp" #include #include #include #include #include #include #include namespace toml { // To avoid non-threadsafe std::localtime. In C11 (not C++11!), localtime_s is // provided in the absolutely same purpose, but C++11 is actually not compatible // with C11. We need to dispatch the function depending on the OS. namespace detail { // TODO: find more sophisticated way to handle this #if defined(_MSC_VER) TOML11_INLINE std::tm localtime_s(const std::time_t* src) { std::tm dst; const auto result = ::localtime_s(&dst, src); if (result) { throw std::runtime_error("localtime_s failed."); } return dst; } TOML11_INLINE std::tm gmtime_s(const std::time_t* src) { std::tm dst; const auto result = ::gmtime_s(&dst, src); if (result) { throw std::runtime_error("gmtime_s failed."); } return dst; } #elif (defined(_POSIX_C_SOURCE) && _POSIX_C_SOURCE >= 1) || defined(_XOPEN_SOURCE) || defined(_BSD_SOURCE) || defined(_SVID_SOURCE) || defined(_POSIX_SOURCE) TOML11_INLINE std::tm localtime_s(const std::time_t* src) { std::tm dst; const auto result = ::localtime_r(src, &dst); if (!result) { throw std::runtime_error("localtime_r failed."); } return dst; } TOML11_INLINE std::tm gmtime_s(const std::time_t* src) { std::tm dst; const auto result = ::gmtime_r(src, &dst); if (!result) { throw std::runtime_error("gmtime_r failed."); } return dst; } #else // fallback. not threadsafe TOML11_INLINE std::tm localtime_s(const std::time_t* src) { const auto result = std::localtime(src); if (!result) { throw std::runtime_error("localtime failed."); } return *result; } TOML11_INLINE std::tm gmtime_s(const std::time_t* src) { const auto result = std::gmtime(src); if (!result) { throw std::runtime_error("gmtime failed."); } return *result; } #endif } // detail // ---------------------------------------------------------------------------- TOML11_INLINE local_date::local_date(const std::chrono::system_clock::time_point& tp) { const auto t = std::chrono::system_clock::to_time_t(tp); const auto time = detail::localtime_s(&t); *this = local_date(time); } TOML11_INLINE local_date::local_date(const std::time_t t) : local_date{std::chrono::system_clock::from_time_t(t)} {} TOML11_INLINE local_date::operator std::chrono::system_clock::time_point() const { // std::mktime returns date as local time zone. no conversion needed std::tm t; t.tm_sec = 0; t.tm_min = 0; t.tm_hour = 0; t.tm_mday = static_cast(this->day); t.tm_mon = static_cast(this->month); t.tm_year = static_cast(this->year) - 1900; t.tm_wday = 0; // the value will be ignored t.tm_yday = 0; // the value will be ignored t.tm_isdst = -1; return std::chrono::system_clock::from_time_t(std::mktime(&t)); } TOML11_INLINE local_date::operator std::time_t() const { return std::chrono::system_clock::to_time_t( std::chrono::system_clock::time_point(*this)); } TOML11_INLINE bool operator==(const local_date& lhs, const local_date& rhs) { return std::make_tuple(lhs.year, lhs.month, lhs.day) == std::make_tuple(rhs.year, rhs.month, rhs.day); } TOML11_INLINE bool operator!=(const local_date& lhs, const local_date& rhs) { return !(lhs == rhs); } TOML11_INLINE bool operator< (const local_date& lhs, const local_date& rhs) { return std::make_tuple(lhs.year, lhs.month, lhs.day) < std::make_tuple(rhs.year, rhs.month, rhs.day); } TOML11_INLINE bool operator<=(const local_date& lhs, const local_date& rhs) { return (lhs < rhs) || (lhs == rhs); } TOML11_INLINE bool operator> (const local_date& lhs, const local_date& rhs) { return !(lhs <= rhs); } TOML11_INLINE bool operator>=(const local_date& lhs, const local_date& rhs) { return !(lhs < rhs); } TOML11_INLINE std::ostream& operator<<(std::ostream& os, const local_date& date) { os << std::setfill('0') << std::setw(4) << static_cast(date.year ) << '-'; os << std::setfill('0') << std::setw(2) << static_cast(date.month) + 1 << '-'; os << std::setfill('0') << std::setw(2) << static_cast(date.day ) ; return os; } TOML11_INLINE std::string to_string(const local_date& date) { std::ostringstream oss; oss.imbue(std::locale::classic()); oss << date; return oss.str(); } // ----------------------------------------------------------------------------- TOML11_INLINE local_time::operator std::chrono::nanoseconds() const { return std::chrono::nanoseconds (this->nanosecond) + std::chrono::microseconds(this->microsecond) + std::chrono::milliseconds(this->millisecond) + std::chrono::seconds(this->second) + std::chrono::minutes(this->minute) + std::chrono::hours(this->hour); } TOML11_INLINE bool operator==(const local_time& lhs, const local_time& rhs) { return std::make_tuple(lhs.hour, lhs.minute, lhs.second, lhs.millisecond, lhs.microsecond, lhs.nanosecond) == std::make_tuple(rhs.hour, rhs.minute, rhs.second, rhs.millisecond, rhs.microsecond, rhs.nanosecond); } TOML11_INLINE bool operator!=(const local_time& lhs, const local_time& rhs) { return !(lhs == rhs); } TOML11_INLINE bool operator< (const local_time& lhs, const local_time& rhs) { return std::make_tuple(lhs.hour, lhs.minute, lhs.second, lhs.millisecond, lhs.microsecond, lhs.nanosecond) < std::make_tuple(rhs.hour, rhs.minute, rhs.second, rhs.millisecond, rhs.microsecond, rhs.nanosecond); } TOML11_INLINE bool operator<=(const local_time& lhs, const local_time& rhs) { return (lhs < rhs) || (lhs == rhs); } TOML11_INLINE bool operator> (const local_time& lhs, const local_time& rhs) { return !(lhs <= rhs); } TOML11_INLINE bool operator>=(const local_time& lhs, const local_time& rhs) { return !(lhs < rhs); } TOML11_INLINE std::ostream& operator<<(std::ostream& os, const local_time& time) { os << std::setfill('0') << std::setw(2) << static_cast(time.hour ) << ':'; os << std::setfill('0') << std::setw(2) << static_cast(time.minute) << ':'; os << std::setfill('0') << std::setw(2) << static_cast(time.second); if(time.millisecond != 0 || time.microsecond != 0 || time.nanosecond != 0) { os << '.'; os << std::setfill('0') << std::setw(3) << static_cast(time.millisecond); if(time.microsecond != 0 || time.nanosecond != 0) { os << std::setfill('0') << std::setw(3) << static_cast(time.microsecond); if(time.nanosecond != 0) { os << std::setfill('0') << std::setw(3) << static_cast(time.nanosecond); } } } return os; } TOML11_INLINE std::string to_string(const local_time& time) { std::ostringstream oss; oss.imbue(std::locale::classic()); oss << time; return oss.str(); } // ---------------------------------------------------------------------------- TOML11_INLINE time_offset::operator std::chrono::minutes() const { return std::chrono::minutes(this->minute) + std::chrono::hours(this->hour); } TOML11_INLINE bool operator==(const time_offset& lhs, const time_offset& rhs) { return std::make_tuple(lhs.hour, lhs.minute) == std::make_tuple(rhs.hour, rhs.minute); } TOML11_INLINE bool operator!=(const time_offset& lhs, const time_offset& rhs) { return !(lhs == rhs); } TOML11_INLINE bool operator< (const time_offset& lhs, const time_offset& rhs) { return std::make_tuple(lhs.hour, lhs.minute) < std::make_tuple(rhs.hour, rhs.minute); } TOML11_INLINE bool operator<=(const time_offset& lhs, const time_offset& rhs) { return (lhs < rhs) || (lhs == rhs); } TOML11_INLINE bool operator> (const time_offset& lhs, const time_offset& rhs) { return !(lhs <= rhs); } TOML11_INLINE bool operator>=(const time_offset& lhs, const time_offset& rhs) { return !(lhs < rhs); } TOML11_INLINE std::ostream& operator<<(std::ostream& os, const time_offset& offset) { if(offset.hour == 0 && offset.minute == 0) { os << 'Z'; return os; } int minute = static_cast(offset.hour) * 60 + offset.minute; if(minute < 0){os << '-'; minute = std::abs(minute);} else {os << '+';} os << std::setfill('0') << std::setw(2) << minute / 60 << ':'; os << std::setfill('0') << std::setw(2) << minute % 60; return os; } TOML11_INLINE std::string to_string(const time_offset& offset) { std::ostringstream oss; oss.imbue(std::locale::classic()); oss << offset; return oss.str(); } // ----------------------------------------------------------------------------- TOML11_INLINE local_datetime::local_datetime(const std::chrono::system_clock::time_point& tp) { const auto t = std::chrono::system_clock::to_time_t(tp); std::tm ltime = detail::localtime_s(&t); this->date = local_date(ltime); this->time = local_time(ltime); // std::tm lacks subsecond information, so diff between tp and tm // can be used to get millisecond & microsecond information. const auto t_diff = tp - std::chrono::system_clock::from_time_t(std::mktime(<ime)); this->time.millisecond = static_cast( std::chrono::duration_cast(t_diff).count()); this->time.microsecond = static_cast( std::chrono::duration_cast(t_diff).count()); this->time.nanosecond = static_cast( std::chrono::duration_cast(t_diff).count()); } TOML11_INLINE local_datetime::local_datetime(const std::time_t t) : local_datetime{std::chrono::system_clock::from_time_t(t)} {} TOML11_INLINE local_datetime::operator std::chrono::system_clock::time_point() const { using internal_duration = typename std::chrono::system_clock::time_point::duration; // Normally DST begins at A.M. 3 or 4. If we re-use conversion operator // of local_date and local_time independently, the conversion fails if // it is the day when DST begins or ends. Since local_date considers the // time is 00:00 A.M. and local_time does not consider DST because it // does not have any date information. We need to consider both date and // time information at the same time to convert it correctly. std::tm t; t.tm_sec = static_cast(this->time.second); t.tm_min = static_cast(this->time.minute); t.tm_hour = static_cast(this->time.hour); t.tm_mday = static_cast(this->date.day); t.tm_mon = static_cast(this->date.month); t.tm_year = static_cast(this->date.year) - 1900; t.tm_wday = 0; // the value will be ignored t.tm_yday = 0; // the value will be ignored t.tm_isdst = -1; // std::mktime returns date as local time zone. no conversion needed auto dt = std::chrono::system_clock::from_time_t(std::mktime(&t)); dt += std::chrono::duration_cast( std::chrono::milliseconds(this->time.millisecond) + std::chrono::microseconds(this->time.microsecond) + std::chrono::nanoseconds (this->time.nanosecond)); return dt; } TOML11_INLINE local_datetime::operator std::time_t() const { return std::chrono::system_clock::to_time_t( std::chrono::system_clock::time_point(*this)); } TOML11_INLINE bool operator==(const local_datetime& lhs, const local_datetime& rhs) { return std::make_tuple(lhs.date, lhs.time) == std::make_tuple(rhs.date, rhs.time); } TOML11_INLINE bool operator!=(const local_datetime& lhs, const local_datetime& rhs) { return !(lhs == rhs); } TOML11_INLINE bool operator< (const local_datetime& lhs, const local_datetime& rhs) { return std::make_tuple(lhs.date, lhs.time) < std::make_tuple(rhs.date, rhs.time); } TOML11_INLINE bool operator<=(const local_datetime& lhs, const local_datetime& rhs) { return (lhs < rhs) || (lhs == rhs); } TOML11_INLINE bool operator> (const local_datetime& lhs, const local_datetime& rhs) { return !(lhs <= rhs); } TOML11_INLINE bool operator>=(const local_datetime& lhs, const local_datetime& rhs) { return !(lhs < rhs); } TOML11_INLINE std::ostream& operator<<(std::ostream& os, const local_datetime& dt) { os << dt.date << 'T' << dt.time; return os; } TOML11_INLINE std::string to_string(const local_datetime& dt) { std::ostringstream oss; oss.imbue(std::locale::classic()); oss << dt; return oss.str(); } // ----------------------------------------------------------------------------- TOML11_INLINE offset_datetime::offset_datetime(const local_datetime& ld) : date{ld.date}, time{ld.time}, offset{get_local_offset(nullptr)} // use the current local timezone offset {} TOML11_INLINE offset_datetime::offset_datetime(const std::chrono::system_clock::time_point& tp) : offset{0, 0} // use gmtime { const auto timet = std::chrono::system_clock::to_time_t(tp); const auto tm = detail::gmtime_s(&timet); this->date = local_date(tm); this->time = local_time(tm); } TOML11_INLINE offset_datetime::offset_datetime(const std::time_t& t) : offset{0, 0} // use gmtime { const auto tm = detail::gmtime_s(&t); this->date = local_date(tm); this->time = local_time(tm); } TOML11_INLINE offset_datetime::offset_datetime(const std::tm& t) : offset{0, 0} // assume gmtime { this->date = local_date(t); this->time = local_time(t); } TOML11_INLINE offset_datetime::operator std::chrono::system_clock::time_point() const { // get date-time using internal_duration = typename std::chrono::system_clock::time_point::duration; // first, convert it to local date-time information in the same way as // local_datetime does. later we will use time_t to adjust time offset. std::tm t; t.tm_sec = static_cast(this->time.second); t.tm_min = static_cast(this->time.minute); t.tm_hour = static_cast(this->time.hour); t.tm_mday = static_cast(this->date.day); t.tm_mon = static_cast(this->date.month); t.tm_year = static_cast(this->date.year) - 1900; t.tm_wday = 0; // the value will be ignored t.tm_yday = 0; // the value will be ignored t.tm_isdst = -1; const std::time_t tp_loc = std::mktime(std::addressof(t)); auto tp = std::chrono::system_clock::from_time_t(tp_loc); tp += std::chrono::duration_cast( std::chrono::milliseconds(this->time.millisecond) + std::chrono::microseconds(this->time.microsecond) + std::chrono::nanoseconds (this->time.nanosecond)); // Since mktime uses local time zone, it should be corrected. // `12:00:00+09:00` means `03:00:00Z`. So mktime returns `03:00:00Z` if // we are in `+09:00` timezone. To represent `12:00:00Z` there, we need // to add `+09:00` to `03:00:00Z`. // Here, it uses the time_t converted from date-time info to handle // daylight saving time. const auto ofs = get_local_offset(std::addressof(tp_loc)); tp += std::chrono::hours (ofs.hour); tp += std::chrono::minutes(ofs.minute); // We got `12:00:00Z` by correcting local timezone applied by mktime. // Then we will apply the offset. Let's say `12:00:00-08:00` is given. // And now, we have `12:00:00Z`. `12:00:00-08:00` means `20:00:00Z`. // So we need to subtract the offset. tp -= std::chrono::minutes(this->offset); return tp; } TOML11_INLINE offset_datetime::operator std::time_t() const { return std::chrono::system_clock::to_time_t( std::chrono::system_clock::time_point(*this)); } TOML11_INLINE time_offset offset_datetime::get_local_offset(const std::time_t* tp) { // get local timezone with the same date-time information as mktime const auto t = detail::localtime_s(tp); std::array buf; const auto result = std::strftime(buf.data(), 6, "%z", &t); // +hhmm\0 if(result != 5) { throw std::runtime_error("toml::offset_datetime: cannot obtain " "timezone information of current env"); } const int ofs = std::atoi(buf.data()); const int ofs_h = ofs / 100; const int ofs_m = ofs - (ofs_h * 100); return time_offset(ofs_h, ofs_m); } TOML11_INLINE bool operator==(const offset_datetime& lhs, const offset_datetime& rhs) { return std::make_tuple(lhs.date, lhs.time, lhs.offset) == std::make_tuple(rhs.date, rhs.time, rhs.offset); } TOML11_INLINE bool operator!=(const offset_datetime& lhs, const offset_datetime& rhs) { return !(lhs == rhs); } TOML11_INLINE bool operator< (const offset_datetime& lhs, const offset_datetime& rhs) { return std::make_tuple(lhs.date, lhs.time, lhs.offset) < std::make_tuple(rhs.date, rhs.time, rhs.offset); } TOML11_INLINE bool operator<=(const offset_datetime& lhs, const offset_datetime& rhs) { return (lhs < rhs) || (lhs == rhs); } TOML11_INLINE bool operator> (const offset_datetime& lhs, const offset_datetime& rhs) { return !(lhs <= rhs); } TOML11_INLINE bool operator>=(const offset_datetime& lhs, const offset_datetime& rhs) { return !(lhs < rhs); } TOML11_INLINE std::ostream& operator<<(std::ostream& os, const offset_datetime& dt) { os << dt.date << 'T' << dt.time << dt.offset; return os; } TOML11_INLINE std::string to_string(const offset_datetime& dt) { std::ostringstream oss; oss.imbue(std::locale::classic()); oss << dt; return oss.str(); } }//toml #endif // TOML11_DATETIME_IMPL_HPP toml11-4.1.0/include/toml11/impl/error_info_impl.hpp000066400000000000000000000034611464712047700222500ustar00rootroot00000000000000#ifndef TOML11_ERROR_INFO_IMPL_HPP #define TOML11_ERROR_INFO_IMPL_HPP #include "../fwd/error_info_fwd.hpp" #include "../fwd/color_fwd.hpp" #include namespace toml { TOML11_INLINE std::string format_error(const std::string& errkind, const error_info& err) { std::string errmsg; if( ! errkind.empty()) { errmsg = errkind; errmsg += ' '; } errmsg += err.title(); errmsg += '\n'; const auto lnw = [&err]() { std::size_t width = 0; for(const auto& l : err.locations()) { width = (std::max)(detail::integer_width_base10(l.first.last_line_number()), width); } return width; }(); bool first = true; std::string prev_fname; for(const auto& lm : err.locations()) { if( ! first) { std::ostringstream oss; oss << detail::make_string(lnw + 1, ' ') << color::bold << color::blue << " |" << color::reset << color::bold << " ...\n" << color::reset; oss << detail::make_string(lnw + 1, ' ') << color::bold << color::blue << " |\n" << color::reset; errmsg += oss.str(); } const auto& l = lm.first; const auto& m = lm.second; errmsg += detail::format_location_impl(lnw, prev_fname, l, m); prev_fname = l.file_name(); first = false; } errmsg += err.suffix(); return errmsg; } TOML11_INLINE std::string format_error(const error_info& err) { std::ostringstream oss; oss << color::red << color::bold << "[error]" << color::reset; return format_error(oss.str(), err); } TOML11_INLINE std::ostream& operator<<(std::ostream& os, const error_info& e) { os << format_error(e); return os; } } // toml #endif // TOML11_ERROR_INFO_IMPL_HPP toml11-4.1.0/include/toml11/impl/format_impl.hpp000066400000000000000000000222571464712047700214000ustar00rootroot00000000000000#ifndef TOML11_FORMAT_IMPL_HPP #define TOML11_FORMAT_IMPL_HPP #include "../fwd/format_fwd.hpp" #include "../version.hpp" #include #include namespace toml { // toml types with serialization info TOML11_INLINE std::ostream& operator<<(std::ostream& os, const indent_char& c) { switch(c) { case indent_char::space: {os << "space" ; break;} case indent_char::tab: {os << "tab" ; break;} case indent_char::none: {os << "none" ; break;} default: { os << "unknown indent char: " << static_cast(c); } } return os; } TOML11_INLINE std::string to_string(const indent_char c) { std::ostringstream oss; oss << c; return oss.str(); } // ---------------------------------------------------------------------------- // boolean // ---------------------------------------------------------------------------- // integer TOML11_INLINE std::ostream& operator<<(std::ostream& os, const integer_format f) { switch(f) { case integer_format::dec: {os << "dec"; break;} case integer_format::bin: {os << "bin"; break;} case integer_format::oct: {os << "oct"; break;} case integer_format::hex: {os << "hex"; break;} default: { os << "unknown integer_format: " << static_cast(f); break; } } return os; } TOML11_INLINE std::string to_string(const integer_format c) { std::ostringstream oss; oss << c; return oss.str(); } TOML11_INLINE bool operator==(const integer_format_info& lhs, const integer_format_info& rhs) noexcept { return lhs.fmt == rhs.fmt && lhs.uppercase == rhs.uppercase && lhs.width == rhs.width && lhs.spacer == rhs.spacer && lhs.suffix == rhs.suffix ; } TOML11_INLINE bool operator!=(const integer_format_info& lhs, const integer_format_info& rhs) noexcept { return !(lhs == rhs); } // ---------------------------------------------------------------------------- // floating TOML11_INLINE std::ostream& operator<<(std::ostream& os, const floating_format f) { switch(f) { case floating_format::defaultfloat: {os << "defaultfloat"; break;} case floating_format::fixed : {os << "fixed" ; break;} case floating_format::scientific : {os << "scientific" ; break;} case floating_format::hex : {os << "hex" ; break;} default: { os << "unknown floating_format: " << static_cast(f); break; } } return os; } TOML11_INLINE std::string to_string(const floating_format c) { std::ostringstream oss; oss << c; return oss.str(); } TOML11_INLINE bool operator==(const floating_format_info& lhs, const floating_format_info& rhs) noexcept { return lhs.fmt == rhs.fmt && lhs.prec == rhs.prec && lhs.suffix == rhs.suffix ; } TOML11_INLINE bool operator!=(const floating_format_info& lhs, const floating_format_info& rhs) noexcept { return !(lhs == rhs); } // ---------------------------------------------------------------------------- // string TOML11_INLINE std::ostream& operator<<(std::ostream& os, const string_format f) { switch(f) { case string_format::basic : {os << "basic" ; break;} case string_format::literal : {os << "literal" ; break;} case string_format::multiline_basic : {os << "multiline_basic" ; break;} case string_format::multiline_literal: {os << "multiline_literal"; break;} default: { os << "unknown string_format: " << static_cast(f); break; } } return os; } TOML11_INLINE std::string to_string(const string_format c) { std::ostringstream oss; oss << c; return oss.str(); } TOML11_INLINE bool operator==(const string_format_info& lhs, const string_format_info& rhs) noexcept { return lhs.fmt == rhs.fmt && lhs.start_with_newline == rhs.start_with_newline ; } TOML11_INLINE bool operator!=(const string_format_info& lhs, const string_format_info& rhs) noexcept { return !(lhs == rhs); } // ---------------------------------------------------------------------------- // datetime TOML11_INLINE std::ostream& operator<<(std::ostream& os, const datetime_delimiter_kind d) { switch(d) { case datetime_delimiter_kind::upper_T: { os << "upper_T, "; break; } case datetime_delimiter_kind::lower_t: { os << "lower_t, "; break; } case datetime_delimiter_kind::space: { os << "space, "; break; } default: { os << "unknown datetime delimiter: " << static_cast(d); break; } } return os; } TOML11_INLINE std::string to_string(const datetime_delimiter_kind c) { std::ostringstream oss; oss << c; return oss.str(); } TOML11_INLINE bool operator==(const offset_datetime_format_info& lhs, const offset_datetime_format_info& rhs) noexcept { return lhs.delimiter == rhs.delimiter && lhs.has_seconds == rhs.has_seconds && lhs.subsecond_precision == rhs.subsecond_precision ; } TOML11_INLINE bool operator!=(const offset_datetime_format_info& lhs, const offset_datetime_format_info& rhs) noexcept { return !(lhs == rhs); } TOML11_INLINE bool operator==(const local_datetime_format_info& lhs, const local_datetime_format_info& rhs) noexcept { return lhs.delimiter == rhs.delimiter && lhs.has_seconds == rhs.has_seconds && lhs.subsecond_precision == rhs.subsecond_precision ; } TOML11_INLINE bool operator!=(const local_datetime_format_info& lhs, const local_datetime_format_info& rhs) noexcept { return !(lhs == rhs); } TOML11_INLINE bool operator==(const local_date_format_info&, const local_date_format_info&) noexcept { return true; } TOML11_INLINE bool operator!=(const local_date_format_info& lhs, const local_date_format_info& rhs) noexcept { return !(lhs == rhs); } TOML11_INLINE bool operator==(const local_time_format_info& lhs, const local_time_format_info& rhs) noexcept { return lhs.has_seconds == rhs.has_seconds && lhs.subsecond_precision == rhs.subsecond_precision ; } TOML11_INLINE bool operator!=(const local_time_format_info& lhs, const local_time_format_info& rhs) noexcept { return !(lhs == rhs); } // ---------------------------------------------------------------------------- // array TOML11_INLINE std::ostream& operator<<(std::ostream& os, const array_format f) { switch(f) { case array_format::default_format : {os << "default_format" ; break;} case array_format::oneline : {os << "oneline" ; break;} case array_format::multiline : {os << "multiline" ; break;} case array_format::array_of_tables: {os << "array_of_tables"; break;} default: { os << "unknown array_format: " << static_cast(f); break; } } return os; } TOML11_INLINE std::string to_string(const array_format c) { std::ostringstream oss; oss << c; return oss.str(); } TOML11_INLINE bool operator==(const array_format_info& lhs, const array_format_info& rhs) noexcept { return lhs.fmt == rhs.fmt && lhs.indent_type == rhs.indent_type && lhs.body_indent == rhs.body_indent && lhs.closing_indent == rhs.closing_indent ; } TOML11_INLINE bool operator!=(const array_format_info& lhs, const array_format_info& rhs) noexcept { return !(lhs == rhs); } // ---------------------------------------------------------------------------- // table TOML11_INLINE std::ostream& operator<<(std::ostream& os, const table_format f) { switch(f) { case table_format::multiline : {os << "multiline" ; break;} case table_format::oneline : {os << "oneline" ; break;} case table_format::dotted : {os << "dotted" ; break;} case table_format::multiline_oneline: {os << "multiline_oneline"; break;} case table_format::implicit : {os << "implicit" ; break;} default: { os << "unknown array_format: " << static_cast(f); break; } } return os; } TOML11_INLINE std::string to_string(const table_format c) { std::ostringstream oss; oss << c; return oss.str(); } TOML11_INLINE bool operator==(const table_format_info& lhs, const table_format_info& rhs) noexcept { return lhs.fmt == rhs.fmt && lhs.indent_type == rhs.indent_type && lhs.body_indent == rhs.body_indent && lhs.name_indent == rhs.name_indent && lhs.closing_indent == rhs.closing_indent ; } TOML11_INLINE bool operator!=(const table_format_info& lhs, const table_format_info& rhs) noexcept { return !(lhs == rhs); } } // namespace toml #endif // TOML11_FORMAT_IMPL_HPP toml11-4.1.0/include/toml11/impl/literal_impl.hpp000066400000000000000000000115571464712047700215450ustar00rootroot00000000000000#ifndef TOML11_LITERAL_IMPL_HPP #define TOML11_LITERAL_IMPL_HPP #include "../fwd/literal_fwd.hpp" #include "../parser.hpp" #include "../syntax.hpp" namespace toml { namespace detail { // implementation TOML11_INLINE ::toml::value literal_internal_impl(location loc) { const auto s = ::toml::spec::default_version(); context ctx(s); const auto front = loc; // ------------------------------------------------------------------------ // check if it is a raw value. // skip empty lines and comment lines auto sp = skip_multiline_spacer(loc, ctx); if(loc.eof()) { ::toml::value val; if(sp.has_value()) { for(std::size_t i=0; i(str), reinterpret_cast(str + len), c.begin()); if( ! c.empty() && c.back()) { c.push_back('\n'); // to make it easy to parse comment, we add newline } return literal_internal_impl(::toml::detail::location( std::make_shared(std::move(c)), "TOML literal encoded in a C++ code")); } #if defined(__cpp_char8_t) # if __cpp_char8_t >= 201811L # define TOML11_HAS_CHAR8_T 1 # endif #endif #if defined(TOML11_HAS_CHAR8_T) // value of u8"" literal has been changed from char to char8_t and char8_t is // NOT compatible to char TOML11_INLINE ::toml::value operator"" _toml(const char8_t* str, std::size_t len) { if(len == 0) { return ::toml::value{}; } ::toml::detail::location::container_type c(len); std::copy(reinterpret_cast(str), reinterpret_cast(str + len), c.begin()); if( ! c.empty() && c.back()) { c.push_back('\n'); // to make it easy to parse comment, we add newline } return literal_internal_impl(::toml::detail::location( std::make_shared(std::move(c)), "TOML literal encoded in a C++ code")); } #endif } // toml_literals } // literals } // toml #endif // TOML11_LITERAL_IMPL_HPP toml11-4.1.0/include/toml11/impl/location_impl.hpp000066400000000000000000000135361464712047700217200ustar00rootroot00000000000000#ifndef TOML11_LOCATION_IMPL_HPP #define TOML11_LOCATION_IMPL_HPP #include "../fwd/location_fwd.hpp" #include "../utility.hpp" #include "../version.hpp" namespace toml { namespace detail { TOML11_INLINE void location::advance(std::size_t n) noexcept { assert(this->is_ok()); if(this->location_ + n < this->source_->size()) { this->advance_line_number(n); this->location_ += n; } else { this->advance_line_number(this->source_->size() - this->location_); this->location_ = this->source_->size(); } } TOML11_INLINE void location::retrace(std::size_t n) noexcept { assert(this->is_ok()); if(this->location_ < n) { this->location_ = 0; this->line_number_ = 1; } else { this->retrace_line_number(n); this->location_ -= n; } } TOML11_INLINE bool location::eof() const noexcept { assert(this->is_ok()); return this->location_ >= this->source_->size(); } TOML11_INLINE location::char_type location::current() const { assert(this->is_ok()); if(this->eof()) {return '\0';} assert(this->location_ < this->source_->size()); return this->source_->at(this->location_); } TOML11_INLINE location::char_type location::peek() { assert(this->is_ok()); if(this->location_ >= this->source_->size()) { return '\0'; } else { return this->source_->at(this->location_ + 1); } } TOML11_INLINE void location::set_location(const std::size_t loc) noexcept { if(this->location_ == loc) { return ; } if(loc == 0) { this->line_number_ = 1; } else if(this->location_ < loc) { const auto d = loc - this->location_; this->advance_line_number(d); } else { const auto d = this->location_ - loc; this->retrace_line_number(d); } this->location_ = loc; } TOML11_INLINE std::string location::get_line() const { assert(this->is_ok()); const auto iter = std::next(this->source_->cbegin(), static_cast(this->location_)); const auto riter = cxx::make_reverse_iterator(iter); const auto prev = std::find(riter, this->source_->crend(), char_type('\n')); const auto next = std::find(iter, this->source_->cend(), char_type('\n')); return make_string(std::next(prev.base()), next); } TOML11_INLINE std::size_t location::column_number() const noexcept { assert(this->is_ok()); const auto iter = std::next(this->source_->cbegin(), static_cast(this->location_)); const auto riter = cxx::make_reverse_iterator(iter); const auto prev = std::find(riter, this->source_->crend(), char_type('\n')); assert(prev.base() <= iter); return static_cast(std::distance(prev.base(), iter) + 1); // 1-origin } TOML11_INLINE void location::advance_line_number(const std::size_t n) { assert(this->is_ok()); assert(this->location_ + n <= this->source_->size()); const auto iter = this->source_->cbegin(); this->line_number_ += static_cast(std::count( std::next(iter, static_cast(this->location_)), std::next(iter, static_cast(this->location_ + n)), char_type('\n'))); return; } TOML11_INLINE void location::retrace_line_number(const std::size_t n) { assert(this->is_ok()); assert(n <= this->location_); // loc - n >= 0 const auto iter = this->source_->cbegin(); const auto dline_num = static_cast(std::count( std::next(iter, static_cast(this->location_ - n)), std::next(iter, static_cast(this->location_)), char_type('\n'))); if(this->line_number_ <= dline_num) { this->line_number_ = 1; } else { this->line_number_ -= dline_num; } return; } TOML11_INLINE bool operator==(const location& lhs, const location& rhs) noexcept { if( ! lhs.is_ok() || ! rhs.is_ok()) { return (!lhs.is_ok()) && (!rhs.is_ok()); } return lhs.source() == rhs.source() && lhs.source_name() == rhs.source_name() && lhs.get_location() == rhs.get_location(); } TOML11_INLINE bool operator!=(const location& lhs, const location& rhs) { return !(lhs == rhs); } TOML11_INLINE location prev(const location& loc) { location p(loc); p.retrace(1); return p; } TOML11_INLINE location next(const location& loc) { location p(loc); p.advance(1); return p; } TOML11_INLINE location make_temporary_location(const std::string& str) noexcept { location::container_type cont(str.size()); std::transform(str.begin(), str.end(), cont.begin(), [](const std::string::value_type& c) { return cxx::bit_cast(c); }); return location(std::make_shared( std::move(cont)), "internal temporary"); } TOML11_INLINE result find(const location& first, const location& last, const location::char_type val) { return find_if(first, last, [val](const location::char_type c) { return c == val; }); } TOML11_INLINE result rfind(const location& first, const location& last, const location::char_type val) { return rfind_if(first, last, [val](const location::char_type c) { return c == val; }); } TOML11_INLINE std::size_t count(const location& first, const location& last, const location::char_type& c) { if(first.source() != last.source()) { return 0; } if(first.get_location() >= last.get_location()) { return 0; } auto loc = first; std::size_t num = 0; while(loc.get_location() != last.get_location()) { if(loc.current() == c) { num += 1; } loc.advance(); } return num; } } // detail } // toml #endif // TOML11_LOCATION_HPP toml11-4.1.0/include/toml11/impl/region_impl.hpp000066400000000000000000000130441464712047700213650ustar00rootroot00000000000000#ifndef TOML11_REGION_IMPL_HPP #define TOML11_REGION_IMPL_HPP #include "../fwd/region_fwd.hpp" #include "../utility.hpp" #include #include #include #include #include #include namespace toml { namespace detail { // a value defined in [first, last). // Those source must be the same. Instread, `region` does not make sense. TOML11_INLINE region::region(const location& first, const location& last) : source_(first.source()), source_name_(first.source_name()), length_(last.get_location() - first.get_location()), first_(first.get_location()), first_line_(first.line_number()), first_column_(first.column_number()), last_(last.get_location()), last_line_(last.line_number()), last_column_(last.column_number()) { assert(first.source() == last.source()); assert(first.source_name() == last.source_name()); } // shorthand of [loc, loc+1) TOML11_INLINE region::region(const location& loc) : source_(loc.source()), source_name_(loc.source_name()), length_(0), first_line_(0), first_column_(0), last_line_(0), last_column_(0) { // if the file ends with LF, the resulting region points no char. if(loc.eof()) { if(loc.get_location() == 0) { this->length_ = 0; this->first_ = 0; this->first_line_ = 0; this->first_column_ = 0; this->last_ = 0; this->last_line_ = 0; this->last_column_ = 0; } else { const auto first = prev(loc); this->first_ = first.get_location(); this->first_line_ = first.line_number(); this->first_column_ = first.column_number(); this->last_ = loc.get_location(); this->last_line_ = loc.line_number(); this->last_column_ = loc.column_number(); this->length_ = 1; } } else { this->first_ = loc.get_location(); this->first_line_ = loc.line_number(); this->first_column_ = loc.column_number(); this->last_ = loc.get_location() + 1; this->last_line_ = loc.line_number(); this->last_column_ = loc.column_number() + 1; this->length_ = 1; } } TOML11_INLINE region::char_type region::at(std::size_t i) const { if(this->last_ <= this->first_ + i) { throw std::out_of_range("range::at: index " + std::to_string(i) + " exceeds length " + std::to_string(this->length_)); } const auto iter = std::next(this->source_->cbegin(), static_cast(this->first_ + i)); return *iter; } TOML11_INLINE region::const_iterator region::begin() const noexcept { return std::next(this->source_->cbegin(), static_cast(this->first_)); } TOML11_INLINE region::const_iterator region::end() const noexcept { return std::next(this->source_->cbegin(), static_cast(this->last_)); } TOML11_INLINE region::const_iterator region::cbegin() const noexcept { return std::next(this->source_->cbegin(), static_cast(this->first_)); } TOML11_INLINE region::const_iterator region::cend() const noexcept { return std::next(this->source_->cbegin(), static_cast(this->last_)); } TOML11_INLINE std::string region::as_string() const { if(this->is_ok()) { const auto begin = std::next(this->source_->cbegin(), static_cast(this->first_)); const auto end = std::next(this->source_->cbegin(), static_cast(this->last_ )); return ::toml::detail::make_string(begin, end); } else { return std::string(""); } } TOML11_INLINE std::vector region::as_lines() const { assert(this->is_ok()); if(this->length_ == 0) { return std::vector{""}; } // Consider the following toml file // ``` // array = [ // ] # comment // ``` // and the region represnets // ``` // [ // ] // ``` // but we want to show the following. // ``` // array = [ // ] # comment // ``` // So we need to find LFs before `begin` and after `end`. // // But, if region ends with LF, it should not include the next line. // ``` // a = 42 // ^^^- with the last LF // ``` // So we start from `end-1` when looking for LF. const auto begin_idx = static_cast(this->first_); const auto end_idx = static_cast(this->last_) - 1; // length_ != 0, so begin < end. then begin <= end-1 assert(begin_idx <= end_idx); const auto begin = std::next(this->source_->cbegin(), begin_idx); const auto end = std::next(this->source_->cbegin(), end_idx); const auto line_begin = std::find(cxx::make_reverse_iterator(begin), this->source_->crend(), char_type('\n')).base(); const auto line_end = std::find(end, this->source_->cend(), char_type('\n')); const auto reg_lines = make_string(line_begin, line_end); if(reg_lines == "") // the region is an empty line that only contains LF { return std::vector{""}; } std::istringstream iss(reg_lines); std::vector lines; std::string line; while(std::getline(iss, line)) { lines.push_back(line); } return lines; } } // namespace detail } // namespace toml #endif // TOML11_REGION_IMPL_HPP toml11-4.1.0/include/toml11/impl/scanner_impl.hpp000066400000000000000000000240671464712047700215420ustar00rootroot00000000000000#ifndef TOML11_SCANNER_IMPL_HPP #define TOML11_SCANNER_IMPL_HPP #include "../fwd/scanner_fwd.hpp" #include "../utility.hpp" namespace toml { namespace detail { TOML11_INLINE scanner_storage::scanner_storage(const scanner_storage& other) : scanner_(nullptr) { if(other.is_ok()) { scanner_.reset(other.get().clone()); } } TOML11_INLINE scanner_storage& scanner_storage::operator=(const scanner_storage& other) { if(this == std::addressof(other)) {return *this;} if(other.is_ok()) { scanner_.reset(other.get().clone()); } return *this; } TOML11_INLINE region scanner_storage::scan(location& loc) const { assert(this->is_ok()); return this->scanner_->scan(loc); } TOML11_INLINE std::string scanner_storage::expected_chars(location& loc) const { assert(this->is_ok()); return this->scanner_->expected_chars(loc); } TOML11_INLINE scanner_base& scanner_storage::get() const noexcept { assert(this->is_ok()); return *scanner_; } TOML11_INLINE std::string scanner_storage::name() const { assert(this->is_ok()); return this->scanner_->name(); } // ---------------------------------------------------------------------------- TOML11_INLINE region character::scan(location& loc) const { if(loc.eof()) {return region{};} if(loc.current() == this->value_) { const auto first = loc; loc.advance(1); return region(first, loc); } return region{}; } TOML11_INLINE std::string character::expected_chars(location&) const { return show_char(value_); } TOML11_INLINE scanner_base* character::clone() const { return new character(*this); } TOML11_INLINE std::string character::name() const { return "character{" + show_char(value_) + "}"; } // ---------------------------------------------------------------------------- TOML11_INLINE region character_either::scan(location& loc) const { if(loc.eof()) {return region{};} for(const auto c : this->chars_) { if(loc.current() == c) { const auto first = loc; loc.advance(1); return region(first, loc); } } return region{}; } TOML11_INLINE std::string character_either::expected_chars(location&) const { assert( ! chars_.empty()); std::string expected; if(chars_.size() == 1) { expected += show_char(chars_.at(0)); } else if(chars_.size() == 2) { expected += show_char(chars_.at(0)) + " or " + show_char(chars_.at(1)); } else { for(std::size_t i=0; ichars_) { n += show_char(c); n += ", "; } if( ! this->chars_.empty()) { n.pop_back(); n.pop_back(); } n += "}"; return n; } // ---------------------------------------------------------------------------- // character_in_range TOML11_INLINE region character_in_range::scan(location& loc) const { if(loc.eof()) {return region{};} const auto curr = loc.current(); if(this->from_ <= curr && curr <= this->to_) { const auto first = loc; loc.advance(1); return region(first, loc); } return region{}; } TOML11_INLINE std::string character_in_range::expected_chars(location&) const { std::string expected("from `"); expected += show_char(from_); expected += "` to `"; expected += show_char(to_); expected += "`"; return expected; } TOML11_INLINE scanner_base* character_in_range::clone() const { return new character_in_range(*this); } TOML11_INLINE std::string character_in_range::name() const { return "character_in_range{" + show_char(from_) + "," + show_char(to_) + "}"; } // ---------------------------------------------------------------------------- // literal TOML11_INLINE region literal::scan(location& loc) const { const auto first = loc; for(std::size_t i=0; iothers_.empty()) { n.pop_back(); n.pop_back(); } n += "}"; return n; } // ---------------------------------------------------------------------------- // either TOML11_INLINE region either::scan(location& loc) const { for(const auto& other : others_) { const auto reg = other.scan(loc); if(reg.is_ok()) { return reg; } } return region{}; } TOML11_INLINE std::string either::expected_chars(location& loc) const { assert( ! others_.empty()); std::string expected = others_.at(0).expected_chars(loc); if(others_.size() == 2) { expected += " or "; expected += others_.at(1).expected_chars(loc); } else { for(std::size_t i=1; iothers_.empty()) { n.pop_back(); n.pop_back(); } n += "}"; return n; } // ---------------------------------------------------------------------------- // repeat_exact TOML11_INLINE region repeat_exact::scan(location& loc) const { const auto first = loc; for(std::size_t i=0; i #include #include #include #include namespace toml { TOML11_INLINE source_location::source_location(const detail::region& r) : is_ok_(false), first_line_(1), first_column_(1), last_line_(1), last_column_(1), length_(0), file_name_("unknown file") { if(r.is_ok()) { this->is_ok_ = true; this->file_name_ = r.source_name(); this->first_line_ = r.first_line_number(); this->first_column_ = r.first_column_number(); this->last_line_ = r.last_line_number(); this->last_column_ = r.last_column_number(); this->length_ = r.length(); this->line_str_ = r.as_lines(); } } TOML11_INLINE std::string const& source_location::first_line() const { if(this->line_str_.size() == 0) { throw std::out_of_range("toml::source_location::first_line: `lines` is empty"); } return this->line_str_.front(); } TOML11_INLINE std::string const& source_location::last_line() const { if(this->line_str_.size() == 0) { throw std::out_of_range("toml::source_location::first_line: `lines` is empty"); } return this->line_str_.back(); } namespace detail { TOML11_INLINE std::size_t integer_width_base10(std::size_t i) noexcept { std::size_t width = 0; while(i != 0) { i /= 10; width += 1; } return width; } TOML11_INLINE std::ostringstream& format_filename(std::ostringstream& oss, const source_location& loc) { // --> example.toml oss << color::bold << color::blue << " --> " << color::reset << color::bold << loc.file_name() << '\n' << color::reset; return oss; } TOML11_INLINE std::ostringstream& format_empty_line(std::ostringstream& oss, const std::size_t lnw) { // | oss << detail::make_string(lnw + 1, ' ') << color::bold << color::blue << " |\n" << color::reset; return oss; } TOML11_INLINE std::ostringstream& format_line(std::ostringstream& oss, const std::size_t lnw, const std::size_t linenum, const std::string& line) { // 10 | key = "value" oss << ' ' << color::bold << color::blue << std::setw(static_cast(lnw)) << std::right << linenum << " | " << color::reset; for(const char c : line) { if(std::isgraph(c) || c == ' ') { oss << c; } else { oss << show_char(c); } } oss << '\n'; return oss; } TOML11_INLINE std::ostringstream& format_underline(std::ostringstream& oss, const std::size_t lnw, const std::size_t col, const std::size_t len, const std::string& msg) { // | ^^^^^^^-- this part oss << make_string(lnw + 1, ' ') << color::bold << color::blue << " | " << color::reset; oss << make_string(col-1 /*1-origin*/, ' ') << color::bold << color::red << make_string(len, '^') << "-- " << color::reset << msg << '\n'; return oss; } TOML11_INLINE std::string format_location_impl(const std::size_t lnw, const std::string& prev_fname, const source_location& loc, const std::string& msg) { std::ostringstream oss; if(loc.file_name() != prev_fname) { format_filename(oss, loc); if( ! loc.lines().empty()) { format_empty_line(oss, lnw); } } if(loc.lines().size() == 1) { // when column points LF, it exceeds the size of the first line. std::size_t underline_limit = 1; if(loc.first_line().size() < loc.first_column_number()) { underline_limit = 1; } else { underline_limit = loc.first_line().size() - loc.first_column_number() + 1; } const auto underline_len = (std::min)(underline_limit, loc.length()); format_line(oss, lnw, loc.first_line_number(), loc.first_line()); format_underline(oss, lnw, loc.first_column_number(), underline_len, msg); } else if(loc.lines().size() == 2) { const auto first_underline_len = loc.first_line().size() - loc.first_column_number() + 1; format_line(oss, lnw, loc.first_line_number(), loc.first_line()); format_underline(oss, lnw, loc.first_column_number(), first_underline_len, ""); format_line(oss, lnw, loc.last_line_number(), loc.last_line()); format_underline(oss, lnw, 1, loc.last_column_number(), msg); } else if(loc.lines().size() > 2) { const auto first_underline_len = loc.first_line().size() - loc.first_column_number() + 1; format_line(oss, lnw, loc.first_line_number(), loc.first_line()); format_underline(oss, lnw, loc.first_column_number(), first_underline_len, "and"); if(loc.lines().size() == 3) { format_line(oss, lnw, loc.first_line_number()+1, loc.lines().at(1)); format_underline(oss, lnw, 1, loc.lines().at(1).size(), "and"); } else { format_line(oss, lnw, loc.first_line_number()+1, " ..."); format_empty_line(oss, lnw); } format_line(oss, lnw, loc.last_line_number(), loc.last_line()); format_underline(oss, lnw, 1, loc.last_column_number(), msg); } // if loc is empty, do nothing. return oss.str(); } } // namespace detail } // toml #endif // TOML11_SOURCE_LOCATION_IMPL_HPP toml11-4.1.0/include/toml11/impl/syntax_impl.hpp000066400000000000000000000475601464712047700214420ustar00rootroot00000000000000#ifndef TOML11_SYNTAX_IMPL_HPP #define TOML11_SYNTAX_IMPL_HPP #include "../fwd/syntax_fwd.hpp" #include "../scanner.hpp" #include "../spec.hpp" namespace toml { namespace detail { namespace syntax { using char_type = location::char_type; // =========================================================================== // UTF-8 // avoid redundant representation and out-of-unicode sequence TOML11_INLINE character_in_range utf8_1byte(const spec&) { return character_in_range(0x00, 0x7F); } TOML11_INLINE sequence utf8_2bytes(const spec&) { return sequence(character_in_range(0xC2, 0xDF), character_in_range(0x80, 0xBF)); } TOML11_INLINE sequence utf8_3bytes(const spec&) { return sequence(/*1~2 bytes = */either( sequence(character (0xE0), character_in_range(0xA0, 0xBF)), sequence(character_in_range(0xE1, 0xEC), character_in_range(0x80, 0xBF)), sequence(character (0xED), character_in_range(0x80, 0x9F)), sequence(character_in_range(0xEE, 0xEF), character_in_range(0x80, 0xBF)) ), /*3rd byte = */ character_in_range(0x80, 0xBF)); } TOML11_INLINE sequence utf8_4bytes(const spec&) { return sequence(/*1~2 bytes = */either( sequence(character (0xF0), character_in_range(0x90, 0xBF)), sequence(character_in_range(0xF1, 0xF3), character_in_range(0x80, 0xBF)), sequence(character (0xF4), character_in_range(0x80, 0x8F)) ), character_in_range(0x80, 0xBF), character_in_range(0x80, 0xBF)); } TOML11_INLINE non_ascii::non_ascii(const spec& s) noexcept : scanner_(utf8_2bytes(s), utf8_3bytes(s), utf8_4bytes(s)) {} // =========================================================================== // Whitespace TOML11_INLINE character_either wschar(const spec&) { return character_either{char_type(' '), char_type('\t')}; } TOML11_INLINE repeat_at_least ws(const spec& s) { return repeat_at_least(0, wschar(s)); } // =========================================================================== // Newline TOML11_INLINE either newline(const spec&) { return either(character(char_type('\n')), literal("\r\n")); } // =========================================================================== // Comments TOML11_INLINE either allowed_comment_char(const spec& s) { if(s.v1_1_0_allow_control_characters_in_comments) { return either( character_in_range(0x01, 0x09), character_in_range(0x0E, 0x7F), non_ascii(s) ); } else { return either( character(0x09), character_in_range(0x20, 0x7E), non_ascii(s) ); } } // XXX Note that it does not take newline TOML11_INLINE sequence comment(const spec& s) { return sequence(character(char_type('#')), repeat_at_least(0, allowed_comment_char(s))); } // =========================================================================== // Boolean TOML11_INLINE either boolean(const spec&) { return either(literal("true"), literal("false")); } // =========================================================================== // Integer TOML11_INLINE digit::digit(const spec&) noexcept : scanner_(char_type('0'), char_type('9')) {} TOML11_INLINE alpha::alpha(const spec&) noexcept : scanner_( character_in_range(char_type('a'), char_type('z')), character_in_range(char_type('A'), char_type('Z')) ) {} TOML11_INLINE hexdig::hexdig(const spec& s) noexcept : scanner_( digit(s), character_in_range(char_type('a'), char_type('f')), character_in_range(char_type('A'), char_type('F')) ) {} // non-digit-graph = ([a-zA-Z]|unicode mb char) // graph = ([a-zA-Z0-9]|unicode mb char) // suffix = _ non-digit-graph (graph | _graph) TOML11_INLINE sequence num_suffix(const spec& s) { const auto non_digit_graph = [&s]() { return either( alpha(s), non_ascii(s) ); }; const auto graph = [&s]() { return either( alpha(s), digit(s), non_ascii(s) ); }; return sequence( character(char_type('_')), non_digit_graph(), repeat_at_least(0, either( sequence(character(char_type('_')), graph()), graph() ) ) ); } TOML11_INLINE sequence dec_int(const spec& s) { const auto digit19 = []() { return character_in_range(char_type('1'), char_type('9')); }; return sequence( maybe(character_either{char_type('-'), char_type('+')}), either( sequence( digit19(), repeat_at_least(1, either( digit(s), sequence(character(char_type('_')), digit(s)) ) ) ), digit(s) ) ); } TOML11_INLINE sequence hex_int(const spec& s) { return sequence( literal("0x"), hexdig(s), repeat_at_least(0, either( hexdig(s), sequence(character(char_type('_')), hexdig(s)) ) ) ); } TOML11_INLINE sequence oct_int(const spec&) { const auto digit07 = []() { return character_in_range(char_type('0'), char_type('7')); }; return sequence( literal("0o"), digit07(), repeat_at_least(0, either( digit07(), sequence(character(char_type('_')), digit07()) ) ) ); } TOML11_INLINE sequence bin_int(const spec&) { const auto digit01 = []() { return character_either{char_type('0'), char_type('1')}; }; return sequence( literal("0b"), digit01(), repeat_at_least(0, either( digit01(), sequence(character(char_type('_')), digit01()) ) ) ); } TOML11_INLINE either integer(const spec& s) { return either( hex_int(s), oct_int(s), bin_int(s), dec_int(s) ); } // =========================================================================== // Floating TOML11_INLINE sequence zero_prefixable_int(const spec& s) { return sequence( digit(s), repeat_at_least(0, either( digit(s), sequence(character('_'), digit(s)) ) ) ); } TOML11_INLINE sequence fractional_part(const spec& s) { return sequence( character('.'), zero_prefixable_int(s) ); } TOML11_INLINE sequence exponent_part(const spec& s) { return sequence( character_either{char_type('e'), char_type('E')}, maybe(character_either{char_type('+'), char_type('-')}), zero_prefixable_int(s) ); } TOML11_INLINE sequence hex_floating(const spec& s) { // C99 hexfloat (%a) // [+-]? 0x ( [0-9a-fA-F]*\.[0-9a-fA-F]+ | [0-9a-fA-F]+\.? ) [pP] [+-]? [0-9]+ // - 0x(int).(frac)p[+-](int) // - 0x(int).p[+-](int) // - 0x.(frac)p[+-](int) // - 0x(int)p[+-](int) return sequence( maybe(character_either{char_type('+'), char_type('-')}), character('0'), character_either{char_type('x'), char_type('X')}, either( sequence( repeat_at_least(0, hexdig(s)), character('.'), repeat_at_least(1, hexdig(s)) ), sequence( repeat_at_least(1, hexdig(s)), maybe(character('.')) ) ), character_either{char_type('p'), char_type('P')}, maybe(character_either{char_type('+'), char_type('-')}), repeat_at_least(1, character_in_range('0', '9')) ); } TOML11_INLINE either floating(const spec& s) { return either( sequence( dec_int(s), either( exponent_part(s), sequence(fractional_part(s), maybe(exponent_part(s))) ) ), sequence( maybe(character_either{char_type('-'), char_type('+')}), either(literal("inf"), literal("nan")) ) ); } // =========================================================================== // Datetime TOML11_INLINE sequence local_date(const spec& s) { return sequence( repeat_exact(4, digit(s)), character('-'), repeat_exact(2, digit(s)), character('-'), repeat_exact(2, digit(s)) ); } TOML11_INLINE sequence local_time(const spec& s) { auto time = sequence( repeat_exact(2, digit(s)), character(':'), repeat_exact(2, digit(s)) ); if(s.v1_1_0_make_seconds_optional) { time.push_back(maybe(sequence( character(':'), repeat_exact(2, digit(s)), maybe(sequence(character('.'), repeat_at_least(1, digit(s)))) ))); } else { time.push_back(character(':')); time.push_back(repeat_exact(2, digit(s))); time.push_back( maybe(sequence(character('.'), repeat_at_least(1, digit(s)))) ); } return time; } TOML11_INLINE either time_offset(const spec& s) { return either( character_either{'Z', 'z'}, sequence(character_either{'+', '-'}, repeat_exact(2, digit(s)), character(':'), repeat_exact(2, digit(s)) ) ); } TOML11_INLINE sequence full_time(const spec& s) { return sequence(local_time(s), time_offset(s)); } TOML11_INLINE character_either time_delim(const spec&) { return character_either{'T', 't', ' '}; } TOML11_INLINE sequence local_datetime(const spec& s) { return sequence(local_date(s), time_delim(s), local_time(s)); } TOML11_INLINE sequence offset_datetime(const spec& s) { return sequence(local_date(s), time_delim(s), full_time(s)); } // =========================================================================== // String TOML11_INLINE sequence escaped(const spec& s) { character_either escape_char{ '\"','\\', 'b', 'f', 'n', 'r', 't' }; if(s.v1_1_0_add_escape_sequence_e) { escape_char.push_back(char_type('e')); } either escape_seq( std::move(escape_char), sequence(character('u'), repeat_exact(4, hexdig(s))), sequence(character('U'), repeat_exact(8, hexdig(s))) ); if(s.v1_1_0_add_escape_sequence_x) { escape_seq.push_back( sequence(character('x'), repeat_exact(2, hexdig(s))) ); } return sequence( character('\\'), std::move(escape_seq) ); } TOML11_INLINE either basic_char(const spec& s) { const auto basic_unescaped = [&s]() { return either( wschar(s), character(0x21), // 22 is " character_in_range(0x23, 0x5B), // 5C is backslash character_in_range(0x5D, 0x7E), // 7F is DEL non_ascii(s) ); }; return either(basic_unescaped(), escaped(s)); } TOML11_INLINE sequence basic_string(const spec& s) { return sequence( character('"'), repeat_at_least(0, basic_char(s)), character('"') ); } // --------------------------------------------------------------------------- // multiline string TOML11_INLINE sequence escaped_newline(const spec& s) { return sequence( character('\\'), ws(s), newline(s), repeat_at_least(0, either(wschar(s), newline(s))) ); } TOML11_INLINE sequence ml_basic_string(const spec& s) { const auto mlb_content = [&s]() { return either(basic_char(s), newline(s), escaped_newline(s)); }; const auto mlb_quotes = []() { return either(literal("\"\""), character('\"')); }; return sequence( literal("\"\"\""), maybe(newline(s)), repeat_at_least(0, mlb_content()), repeat_at_least(0, sequence( mlb_quotes(), repeat_at_least(1, mlb_content()) ) ), // XXX """ and mlb_quotes are intentionally reordered to avoid // unexpected match of mlb_quotes literal("\"\"\""), maybe(mlb_quotes()) ); } // --------------------------------------------------------------------------- // literal string TOML11_INLINE either literal_char(const spec& s) { return either( character (0x09), character_in_range(0x20, 0x26), character_in_range(0x28, 0x7E), non_ascii(s) ); } TOML11_INLINE sequence literal_string(const spec& s) { return sequence( character('\''), repeat_at_least(0, literal_char(s)), character('\'') ); } TOML11_INLINE sequence ml_literal_string(const spec& s) { const auto mll_quotes = []() { return either(literal("''"), character('\'')); }; const auto mll_content = [&s]() { return either(literal_char(s), newline(s)); }; return sequence( literal("'''"), maybe(newline(s)), repeat_at_least(0, mll_content()), repeat_at_least(0, sequence( mll_quotes(), repeat_at_least(1, mll_content()) ) ), literal("'''"), maybe(mll_quotes()) // XXX ''' and mll_quotes are intentionally reordered to avoid // unexpected match of mll_quotes ); } TOML11_INLINE either string(const spec& s) { return either( ml_basic_string(s), ml_literal_string(s), basic_string(s), literal_string(s) ); } // =========================================================================== // Keys // to keep `expected_chars` simple TOML11_INLINE non_ascii_key_char::non_ascii_key_char(const spec& s) noexcept { assert(s.v1_1_0_allow_non_english_in_bare_keys); (void)s; // for NDEBUG } TOML11_INLINE std::uint32_t non_ascii_key_char::read_utf8(location& loc) const { // U+0000 ... U+0079 ; 0xxx_xxxx // U+0080 ... U+07FF ; 110y_yyyx 10xx_xxxx; // U+0800 ... U+FFFF ; 1110_yyyy 10yx_xxxx 10xx_xxxx // U+010000 ... U+10FFFF; 1111_0yyy 10yy_xxxx 10xx_xxxx 10xx_xxxx const unsigned char b1 = loc.current(); loc.advance(1); if(b1 < 0x80) { return static_cast(b1); } else if((b1 >> 5) == 6) // 0b110 == 6 { const auto b2 = loc.current(); loc.advance(1); const std::uint32_t c1 = b1 & ((1 << 5) - 1); const std::uint32_t c2 = b2 & ((1 << 6) - 1); const std::uint32_t codep = (c1 << 6) + c2; if(codep < 0x80) { return 0xFFFFFFFF; } return codep; } else if((b1 >> 4) == 14) // 0b1110 == 14 { const auto b2 = loc.current(); loc.advance(1); if(loc.eof()) {return 0xFFFFFFFF;} const auto b3 = loc.current(); loc.advance(1); const std::uint32_t c1 = b1 & ((1 << 4) - 1); const std::uint32_t c2 = b2 & ((1 << 6) - 1); const std::uint32_t c3 = b3 & ((1 << 6) - 1); const std::uint32_t codep = (c1 << 12) + (c2 << 6) + c3; if(codep < 0x800) { return 0xFFFFFFFF; } return codep; } else if((b1 >> 3) == 30) // 0b11110 == 30 { const auto b2 = loc.current(); loc.advance(1); if(loc.eof()) {return 0xFFFFFFFF;} const auto b3 = loc.current(); loc.advance(1); if(loc.eof()) {return 0xFFFFFFFF;} const auto b4 = loc.current(); loc.advance(1); const std::uint32_t c1 = b1 & ((1 << 3) - 1); const std::uint32_t c2 = b2 & ((1 << 6) - 1); const std::uint32_t c3 = b3 & ((1 << 6) - 1); const std::uint32_t c4 = b4 & ((1 << 6) - 1); const std::uint32_t codep = (c1 << 18) + (c2 << 12) + (c3 << 6) + c4; if(codep < 0x10000) { return 0xFFFFFFFF; } return codep; } else // not a Unicode codepoint in UTF-8 { return 0xFFFFFFFF; } } TOML11_INLINE region non_ascii_key_char::scan(location& loc) const { if(loc.eof()) {return region{};} const auto first = loc; const auto cp = read_utf8(loc); if(cp == 0xFFFFFFFF) { return region{}; } // ALPHA / DIGIT / %x2D / %x5F ; a-z A-Z 0-9 - _ // / %xB2 / %xB3 / %xB9 / %xBC-BE ; superscript digits, fractions // / %xC0-D6 / %xD8-F6 / %xF8-37D ; non-symbol chars in Latin block // / %x37F-1FFF ; exclude GREEK QUESTION MARK, which is basically a semi-colon // / %x200C-200D / %x203F-2040 ; from General Punctuation Block, include the two tie symbols and ZWNJ, ZWJ // / %x2070-218F / %x2460-24FF ; include super-/subscripts, letterlike/numberlike forms, enclosed alphanumerics // / %x2C00-2FEF / %x3001-D7FF ; skip arrows, math, box drawing etc, skip 2FF0-3000 ideographic up/down markers and spaces // / %xF900-FDCF / %xFDF0-FFFD ; skip D800-DFFF surrogate block, E000-F8FF Private Use area, FDD0-FDEF intended for process-internal use (unicode) // / %x10000-EFFFF ; all chars outside BMP range, excluding Private Use planes (F0000-10FFFF) if(cp == 0xB2 || cp == 0xB3 || cp == 0xB9 || (0xBC <= cp && cp <= 0xBE) || (0xC0 <= cp && cp <= 0xD6 ) || (0xD8 <= cp && cp <= 0xF6) || (0xF8 <= cp && cp <= 0x37D) || (0x37F <= cp && cp <= 0x1FFF) || (0x200C <= cp && cp <= 0x200D) || (0x203F <= cp && cp <= 0x2040) || (0x2070 <= cp && cp <= 0x218F) || (0x2460 <= cp && cp <= 0x24FF) || (0x2C00 <= cp && cp <= 0x2FEF) || (0x3001 <= cp && cp <= 0xD7FF) || (0xF900 <= cp && cp <= 0xFDCF) || (0xFDF0 <= cp && cp <= 0xFFFD) || (0x10000 <= cp && cp <= 0xEFFFF) ) { return region(first, loc); } loc = first; return region{}; } TOML11_INLINE repeat_at_least unquoted_key(const spec& s) { auto keychar = either( alpha(s), digit(s), character{0x2D}, character{0x5F} ); if(s.v1_1_0_allow_non_english_in_bare_keys) { keychar.push_back(non_ascii_key_char(s)); } return repeat_at_least(1, std::move(keychar)); } TOML11_INLINE either quoted_key(const spec& s) { return either(basic_string(s), literal_string(s)); } TOML11_INLINE either simple_key(const spec& s) { return either(unquoted_key(s), quoted_key(s)); } TOML11_INLINE sequence dot_sep(const spec& s) { return sequence(ws(s), character('.'), ws(s)); } TOML11_INLINE sequence dotted_key(const spec& s) { return sequence( simple_key(s), repeat_at_least(1, sequence(dot_sep(s), simple_key(s))) ); } TOML11_INLINE key::key(const spec& s) noexcept : scanner_(dotted_key(s), simple_key(s)) {} TOML11_INLINE sequence keyval_sep(const spec& s) { return sequence(ws(s), character('='), ws(s)); } // =========================================================================== // Table key TOML11_INLINE sequence std_table(const spec& s) { return sequence(character('['), ws(s), key(s), ws(s), character(']')); } TOML11_INLINE sequence array_table(const spec& s) { return sequence(literal("[["), ws(s), key(s), ws(s), literal("]]")); } // =========================================================================== // extension: null TOML11_INLINE literal null_value(const spec&) { return literal("null"); } } // namespace syntax } // namespace detail } // namespace toml #endif // TOML11_SYNTAX_IMPL_HPP toml11-4.1.0/include/toml11/impl/value_t_impl.hpp000066400000000000000000000025141464712047700215410ustar00rootroot00000000000000#ifndef TOML11_VALUE_T_IMPL_HPP #define TOML11_VALUE_T_IMPL_HPP #include "../fwd/value_t_fwd.hpp" #include #include #include namespace toml { TOML11_INLINE std::ostream& operator<<(std::ostream& os, value_t t) { switch(t) { case value_t::boolean : os << "boolean"; return os; case value_t::integer : os << "integer"; return os; case value_t::floating : os << "floating"; return os; case value_t::string : os << "string"; return os; case value_t::offset_datetime : os << "offset_datetime"; return os; case value_t::local_datetime : os << "local_datetime"; return os; case value_t::local_date : os << "local_date"; return os; case value_t::local_time : os << "local_time"; return os; case value_t::array : os << "array"; return os; case value_t::table : os << "table"; return os; case value_t::empty : os << "empty"; return os; default : os << "unknown"; return os; } } TOML11_INLINE std::string to_string(value_t t) { std::ostringstream oss; oss << t; return oss.str(); } } // namespace toml #endif // TOML11_VALUE_T_IMPL_HPP toml11-4.1.0/include/toml11/into.hpp000066400000000000000000000004221464712047700170650ustar00rootroot00000000000000#ifndef TOML11_INTO_HPP #define TOML11_INTO_HPP namespace toml { template struct into; // { // static toml::value into_toml(const T& user_defined_type) // { // // User-defined conversions ... // } // }; } // toml #endif // TOML11_INTO_HPP toml11-4.1.0/include/toml11/literal.hpp000066400000000000000000000003611464712047700175520ustar00rootroot00000000000000#ifndef TOML11_LITERAL_HPP #define TOML11_LITERAL_HPP #include "fwd/literal_fwd.hpp" // IWYU pragma: export #if ! defined(TOML11_COMPILE_SOURCES) #include "impl/literal_impl.hpp" // IWYU pragma: export #endif #endif // TOML11_LITERAL_HPP toml11-4.1.0/include/toml11/location.hpp000066400000000000000000000003661464712047700177330ustar00rootroot00000000000000#ifndef TOML11_LOCATION_HPP #define TOML11_LOCATION_HPP #include "fwd/location_fwd.hpp" // IWYU pragma: export #if ! defined(TOML11_COMPILE_SOURCES) #include "impl/location_impl.hpp" // IWYU pragma: export #endif #endif // TOML11_LOCATION_HPP toml11-4.1.0/include/toml11/ordered_map.hpp000066400000000000000000000176631464712047700204140ustar00rootroot00000000000000#ifndef TOML11_ORDERED_MAP_HPP #define TOML11_ORDERED_MAP_HPP #include #include #include #include namespace toml { namespace detail { template struct ordered_map_ebo_container { Cmp cmp_; // empty base optimization for empty Cmp type }; } // detail template, typename Allocator = std::allocator>> class ordered_map : detail::ordered_map_ebo_container { public: using key_type = Key; using mapped_type = Val; using value_type = std::pair; using key_compare = Cmp; using allocator_type = Allocator; using container_type = std::vector; using reference = typename container_type::reference; using pointer = typename container_type::pointer; using const_reference = typename container_type::const_reference; using const_pointer = typename container_type::const_pointer; using iterator = typename container_type::iterator; using const_iterator = typename container_type::const_iterator; using size_type = typename container_type::size_type; using difference_type = typename container_type::difference_type; private: using ebo_base = detail::ordered_map_ebo_container; public: ordered_map() = default; ~ordered_map() = default; ordered_map(const ordered_map&) = default; ordered_map(ordered_map&&) = default; ordered_map& operator=(const ordered_map&) = default; ordered_map& operator=(ordered_map&&) = default; ordered_map(const ordered_map& other, const Allocator& alloc) : container_(other.container_, alloc) {} ordered_map(ordered_map&& other, const Allocator& alloc) : container_(std::move(other.container_), alloc) {} explicit ordered_map(const Cmp& cmp, const Allocator& alloc = Allocator()) : ebo_base{cmp}, container_(alloc) {} explicit ordered_map(const Allocator& alloc) : container_(alloc) {} template ordered_map(InputIterator first, InputIterator last, const Cmp& cmp = Cmp(), const Allocator& alloc = Allocator()) : ebo_base{cmp}, container_(first, last, alloc) {} template ordered_map(InputIterator first, InputIterator last, const Allocator& alloc) : container_(first, last, alloc) {} ordered_map(std::initializer_list v, const Cmp& cmp = Cmp(), const Allocator& alloc = Allocator()) : ebo_base{cmp}, container_(std::move(v), alloc) {} ordered_map(std::initializer_list v, const Allocator& alloc) : container_(std::move(v), alloc) {} ordered_map& operator=(std::initializer_list v) { this->container_ = std::move(v); return *this; } iterator begin() noexcept {return container_.begin();} iterator end() noexcept {return container_.end();} const_iterator begin() const noexcept {return container_.begin();} const_iterator end() const noexcept {return container_.end();} const_iterator cbegin() const noexcept {return container_.cbegin();} const_iterator cend() const noexcept {return container_.cend();} bool empty() const noexcept {return container_.empty();} std::size_t size() const noexcept {return container_.size();} std::size_t max_size() const noexcept {return container_.max_size();} void clear() {container_.clear();} void push_back(const value_type& v) { if(this->contains(v.first)) { throw std::out_of_range("ordered_map: value already exists"); } container_.push_back(v); } void push_back(value_type&& v) { if(this->contains(v.first)) { throw std::out_of_range("ordered_map: value already exists"); } container_.push_back(std::move(v)); } void emplace_back(key_type k, mapped_type v) { if(this->contains(k)) { throw std::out_of_range("ordered_map: value already exists"); } container_.emplace_back(std::move(k), std::move(v)); } void pop_back() {container_.pop_back();} void insert(value_type kv) { if(this->contains(kv.first)) { throw std::out_of_range("ordered_map: value already exists"); } container_.push_back(std::move(kv)); } void emplace(key_type k, mapped_type v) { if(this->contains(k)) { throw std::out_of_range("ordered_map: value already exists"); } container_.emplace_back(std::move(k), std::move(v)); } std::size_t count(const key_type& key) const { if(this->find(key) != this->end()) { return 1; } else { return 0; } } bool contains(const key_type& key) const { return this->find(key) != this->end(); } iterator find(const key_type& key) noexcept { return std::find_if(this->begin(), this->end(), [&key, this](const value_type& v) {return this->cmp_(v.first, key);}); } const_iterator find(const key_type& key) const noexcept { return std::find_if(this->begin(), this->end(), [&key, this](const value_type& v) {return this->cmp_(v.first, key);}); } mapped_type& at(const key_type& k) { const auto iter = this->find(k); if(iter == this->end()) { throw std::out_of_range("ordered_map: no such element"); } return iter->second; } mapped_type const& at(const key_type& k) const { const auto iter = this->find(k); if(iter == this->end()) { throw std::out_of_range("ordered_map: no such element"); } return iter->second; } mapped_type& operator[](const key_type& k) { const auto iter = this->find(k); if(iter == this->end()) { this->container_.emplace_back(k, mapped_type{}); return this->container_.back().second; } return iter->second; } mapped_type const& operator[](const key_type& k) const { const auto iter = this->find(k); if(iter == this->end()) { throw std::out_of_range("ordered_map: no such element"); } return iter->second; } key_compare key_comp() const {return this->cmp_;} void swap(ordered_map& other) { container_.swap(other.container_); } private: container_type container_; }; template bool operator==(const ordered_map& lhs, const ordered_map& rhs) { return lhs.size() == rhs.size() && std::equal(lhs.begin(), lhs.end(), rhs.begin()); } template bool operator!=(const ordered_map& lhs, const ordered_map& rhs) { return !(lhs == rhs); } template bool operator<(const ordered_map& lhs, const ordered_map& rhs) { return std::lexicographical_compare(lhs.begin(), lhs.end(), rhs.begin(), rhs.end()); } template bool operator>(const ordered_map& lhs, const ordered_map& rhs) { return rhs < lhs; } template bool operator<=(const ordered_map& lhs, const ordered_map& rhs) { return !(lhs > rhs); } template bool operator>=(const ordered_map& lhs, const ordered_map& rhs) { return !(lhs < rhs); } template void swap(ordered_map& lhs, ordered_map& rhs) { lhs.swap(rhs); return; } } // toml #endif // TOML11_ORDERED_MAP_HPP toml11-4.1.0/include/toml11/parser.hpp000066400000000000000000003753561464712047700174350ustar00rootroot00000000000000#ifndef TOML11_PARSER_HPP #define TOML11_PARSER_HPP #include "context.hpp" #include "datetime.hpp" #include "error_info.hpp" #include "region.hpp" #include "result.hpp" #include "scanner.hpp" #include "skip.hpp" #include "syntax.hpp" #include "value.hpp" #include #include #include #include #if defined(TOML11_HAS_FILESYSTEM) && TOML11_HAS_FILESYSTEM #include #endif namespace toml { struct syntax_error final : public ::toml::exception { public: syntax_error(std::string what_arg, std::vector err) : what_(std::move(what_arg)), err_(std::move(err)) {} ~syntax_error() noexcept override = default; const char* what() const noexcept override {return what_.c_str();} std::vector const& errors() const noexcept { return err_; } private: std::string what_; std::vector err_; }; struct file_io_error final : public ::toml::exception { public: file_io_error(const std::string& msg, const std::string& fname) : errno_(cxx::make_nullopt()), what_(msg + " \"" + fname + "\"") {} file_io_error(int errnum, const std::string& msg, const std::string& fname) : errno_(errnum), what_(msg + " \"" + fname + "\": errno=" + std::to_string(errnum)) {} ~file_io_error() noexcept override = default; const char* what() const noexcept override {return what_.c_str();} bool has_errno() const noexcept {return errno_.has_value();} int get_errno() const noexcept {return errno_.value_or(0);} private: cxx::optional errno_; std::string what_; }; namespace detail { /* ============================================================================ * __ ___ _ __ _ __ ___ _ _ * / _/ _ \ ' \| ' \/ _ \ ' \ * \__\___/_|_|_|_|_|_\___/_||_| */ template error_info make_syntax_error(std::string title, const S& scanner, location loc, std::string suffix = "") { auto msg = std::string("expected ") + scanner.expected_chars(loc); auto src = source_location(region(loc)); return make_error_info( std::move(title), std::move(src), std::move(msg), std::move(suffix)); } /* ============================================================================ * _ * __ ___ _ __ _ __ ___ _ _| |_ * / _/ _ \ ' \| ' \/ -_) ' \ _| * \__\___/_|_|_|_|_|_\___|_||_\__| */ template result, error_info> parse_comment_line(location& loc, context& ctx) { const auto& spec = ctx.toml_spec(); const auto first = loc; skip_whitespace(loc, ctx); const auto com_reg = syntax::comment(spec).scan(loc); if(com_reg.is_ok()) { // once comment started, newline must follow (or reach EOF). if( ! loc.eof() && ! syntax::newline(spec).scan(loc).is_ok()) { while( ! loc.eof()) // skip until newline to continue parsing { loc.advance(); if(loc.current() == '\n') { /*skip LF*/ loc.advance(); break; } } return err(make_error_info("toml::parse_comment_line: " "newline (LF / CRLF) or EOF is expected", source_location(region(loc)), "but got this", "Hint: most of the control characters are not allowed in comments")); } return ok(cxx::optional(com_reg.as_string())); } else { loc = first; // rollback whitespace to parse indent return ok(cxx::optional(cxx::make_nullopt())); } } /* ============================================================================ * ___ _ * | _ ) ___ ___| |___ __ _ _ _ * | _ \/ _ \/ _ \ / -_) _` | ' \ * |___/\___/\___/_\___\__,_|_||_| */ template result, error_info> parse_boolean(location& loc, const context& ctx) { const auto& spec = ctx.toml_spec(); // ---------------------------------------------------------------------- // check syntax auto reg = syntax::boolean(spec).scan(loc); if( ! reg.is_ok()) { return err(make_syntax_error("toml::parse_boolean: " "invalid boolean: boolean must be `true` or `false`, in lowercase. " "string must be surrounded by `\"`", syntax::boolean(spec), loc)); } // ---------------------------------------------------------------------- // it matches. gen value const auto str = reg.as_string(); const auto val = [&str]() { if(str == "true") { return true; } else { assert(str == "false"); return false; } }(); // ---------------------------------------------------------------------- // no format info for boolean boolean_format_info fmt; return ok(basic_value(val, std::move(fmt), {}, std::move(reg))); } /* ============================================================================ * ___ _ * |_ _|_ _| |_ ___ __ _ ___ _ _ * | || ' \ _/ -_) _` / -_) '_| * |___|_||_\__\___\__, \___|_| * |___/ */ template result, error_info> parse_bin_integer(location& loc, const context& ctx) { const auto first = loc; const auto& spec = ctx.toml_spec(); auto reg = syntax::bin_int(spec).scan(loc); if( ! reg.is_ok()) { return err(make_syntax_error("toml::parse_bin_integer: " "invalid integer: bin_int must be like: 0b0101, 0b1111_0000", syntax::bin_int(spec), loc)); } auto str = reg.as_string(); integer_format_info fmt; fmt.fmt = integer_format::bin; fmt.width = str.size() - 2 - static_cast(std::count(str.begin(), str.end(), '_')); const auto first_underscore = std::find(str.rbegin(), str.rend(), '_'); if(first_underscore != str.rend()) { fmt.spacer = static_cast(std::distance(str.rbegin(), first_underscore)); } // skip prefix `0b` and zeros and underscores at the MSB str.erase(str.begin(), std::find(std::next(str.begin(), 2), str.end(), '1')); // remove all `_` before calling TC::parse_int str.erase(std::remove(str.begin(), str.end(), '_'), str.end()); // 0b0000_0000 becomes empty. if(str.empty()) { str = "0"; } const auto val = TC::parse_int(str, source_location(region(loc)), 2); if(val.is_ok()) { return ok(basic_value(val.as_ok(), std::move(fmt), {}, std::move(reg))); } else { loc = first; return err(val.as_err()); } } // ---------------------------------------------------------------------------- template result, error_info> parse_oct_integer(location& loc, const context& ctx) { const auto first = loc; const auto& spec = ctx.toml_spec(); auto reg = syntax::oct_int(spec).scan(loc); if( ! reg.is_ok()) { return err(make_syntax_error("toml::parse_oct_integer: " "invalid integer: oct_int must be like: 0o775, 0o04_44", syntax::oct_int(spec), loc)); } auto str = reg.as_string(); integer_format_info fmt; fmt.fmt = integer_format::oct; fmt.width = str.size() - 2 - static_cast(std::count(str.begin(), str.end(), '_')); const auto first_underscore = std::find(str.rbegin(), str.rend(), '_'); if(first_underscore != str.rend()) { fmt.spacer = static_cast(std::distance(str.rbegin(), first_underscore)); } // skip prefix `0o` and zeros and underscores at the MSB str.erase(str.begin(), std::find_if( std::next(str.begin(), 2), str.end(), [](const char c) { return c != '0' && c != '_'; })); // remove all `_` before calling TC::parse_int str.erase(std::remove(str.begin(), str.end(), '_'), str.end()); // 0o0000_0000 becomes empty. if(str.empty()) { str = "0"; } const auto val = TC::parse_int(str, source_location(region(loc)), 8); if(val.is_ok()) { return ok(basic_value(val.as_ok(), std::move(fmt), {}, std::move(reg))); } else { loc = first; return err(val.as_err()); } } template result, error_info> parse_hex_integer(location& loc, const context& ctx) { const auto first = loc; const auto& spec = ctx.toml_spec(); auto reg = syntax::hex_int(spec).scan(loc); if( ! reg.is_ok()) { return err(make_syntax_error("toml::parse_hex_integer: " "invalid integer: hex_int must be like: 0xC0FFEE, 0xdead_beef", syntax::hex_int(spec), loc)); } auto str = reg.as_string(); integer_format_info fmt; fmt.fmt = integer_format::hex; fmt.width = str.size() - 2 - static_cast(std::count(str.begin(), str.end(), '_')); const auto first_underscore = std::find(str.rbegin(), str.rend(), '_'); if(first_underscore != str.rend()) { fmt.spacer = static_cast(std::distance(str.rbegin(), first_underscore)); } // skip prefix `0x` and zeros and underscores at the MSB str.erase(str.begin(), std::find_if( std::next(str.begin(), 2), str.end(), [](const char c) { return c != '0' && c != '_'; })); // remove all `_` before calling TC::parse_int str.erase(std::remove(str.begin(), str.end(), '_'), str.end()); // 0x0000_0000 becomes empty. if(str.empty()) { str = "0"; } // prefix zero and _ is removed. check if it uses upper/lower case. // if both upper and lower case letters are found, set upper=true. const auto lower_not_found = std::find_if(str.begin(), str.end(), [](const char c) { return std::islower(static_cast(c)) != 0; }) == str.end(); const auto upper_found = std::find_if(str.begin(), str.end(), [](const char c) { return std::isupper(static_cast(c)) != 0; }) != str.end(); fmt.uppercase = lower_not_found || upper_found; const auto val = TC::parse_int(str, source_location(region(loc)), 16); if(val.is_ok()) { return ok(basic_value(val.as_ok(), std::move(fmt), {}, std::move(reg))); } else { loc = first; return err(val.as_err()); } } template result, error_info> parse_dec_integer(location& loc, const context& ctx) { const auto first = loc; const auto& spec = ctx.toml_spec(); // ---------------------------------------------------------------------- // check syntax auto reg = syntax::dec_int(spec).scan(loc); if( ! reg.is_ok()) { return err(make_syntax_error("toml::parse_dec_integer: " "invalid integer: dec_int must be like: 42, 123_456_789", syntax::dec_int(spec), loc)); } // ---------------------------------------------------------------------- // it matches. gen value auto str = reg.as_string(); integer_format_info fmt; fmt.fmt = integer_format::dec; fmt.width = str.size() - static_cast(std::count(str.begin(), str.end(), '_')); const auto first_underscore = std::find(str.rbegin(), str.rend(), '_'); if(first_underscore != str.rend()) { fmt.spacer = static_cast(std::distance(str.rbegin(), first_underscore)); } // remove all `_` before calling TC::parse_int str.erase(std::remove(str.begin(), str.end(), '_'), str.end()); auto src = source_location(region(loc)); const auto val = TC::parse_int(str, src, 10); if(val.is_err()) { loc = first; return err(val.as_err()); } // ---------------------------------------------------------------------- // parse suffix (extension) if(spec.ext_num_suffix && loc.current() == '_') { const auto sfx_reg = syntax::num_suffix(spec).scan(loc); if( ! sfx_reg.is_ok()) { loc = first; return err(make_error_info("toml::parse_dec_integer: " "invalid suffix: should be `_ non-digit-graph (graph | _graph)`", source_location(region(loc)), "here")); } auto sfx = sfx_reg.as_string(); assert( ! sfx.empty() && sfx.front() == '_'); sfx.erase(sfx.begin()); // remove the first `_` fmt.suffix = sfx; } return ok(basic_value(val.as_ok(), std::move(fmt), {}, std::move(reg))); } template result, error_info> parse_integer(location& loc, const context& ctx) { const auto first = loc; if( ! loc.eof() && (loc.current() == '+' || loc.current() == '-')) { // skip +/- to diagnose +0xDEADBEEF or -0b0011 (invalid). // without this, +0xDEAD_BEEF will be parsed as a decimal int and // unexpected "xDEAD_BEEF" will appear after integer "+0". loc.advance(); } if( ! loc.eof() && loc.current() == '0') { loc.advance(); if(loc.eof()) { // `[+-]?0`. parse as an decimal integer. loc = first; return parse_dec_integer(loc, ctx); } const auto prefix = loc.current(); auto prefix_src = source_location(region(loc)); loc = first; if(prefix == 'b') {return parse_bin_integer(loc, ctx);} if(prefix == 'o') {return parse_oct_integer(loc, ctx);} if(prefix == 'x') {return parse_hex_integer(loc, ctx);} if(std::isdigit(prefix)) { auto src = source_location(region(loc)); return err(make_error_info("toml::parse_integer: " "leading zero in an decimal integer is not allowed", std::move(src), "leading zero")); } } loc = first; return parse_dec_integer(loc, ctx); } /* ============================================================================ * ___ _ _ _ * | __| |___ __ _| |_(_)_ _ __ _ * | _|| / _ \/ _` | _| | ' \/ _` | * |_| |_\___/\__,_|\__|_|_||_\__, | * |___/ */ template result, error_info> parse_floating(location& loc, const context& ctx) { using floating_type = typename basic_value::floating_type; const auto first = loc; const auto& spec = ctx.toml_spec(); // ---------------------------------------------------------------------- // check syntax bool is_hex = false; std::string str; region reg; if(spec.ext_hex_float && sequence(character('0'), character('x')).scan(loc).is_ok()) { loc = first; is_hex = true; reg = syntax::hex_floating(spec).scan(loc); if( ! reg.is_ok()) { return err(make_syntax_error("toml::parse_floating: " "invalid hex floating: float must be like: 0xABCp-3f", syntax::floating(spec), loc)); } str = reg.as_string(); } else { reg = syntax::floating(spec).scan(loc); if( ! reg.is_ok()) { return err(make_syntax_error("toml::parse_floating: " "invalid floating: float must be like: -3.14159_26535, 6.022e+23, " "inf, or nan (lowercase).", syntax::floating(spec), loc)); } str = reg.as_string(); } // ---------------------------------------------------------------------- // it matches. gen value floating_format_info fmt; if(is_hex) { fmt.fmt = floating_format::hex; } else { // since we already checked that the string conforms the TOML standard. if(std::find(str.begin(), str.end(), 'e') != str.end() || std::find(str.begin(), str.end(), 'E') != str.end()) { fmt.fmt = floating_format::scientific; // use exponent part } else { fmt.fmt = floating_format::fixed; // do not use exponent part } } str.erase(std::remove(str.begin(), str.end(), '_'), str.end()); floating_type val{0}; if(str == "inf" || str == "+inf") { TOML11_CONSTEXPR_IF(std::numeric_limits::has_infinity) { val = std::numeric_limits::infinity(); } else { return err(make_error_info("toml::parse_floating: inf value found" " but the current environment does not support inf. Please" " make sure that the floating-point implementation conforms" " IEEE 754/ISO 60559 international standard.", source_location(region(loc)), "floating_type: inf is not supported")); } } else if(str == "-inf") { TOML11_CONSTEXPR_IF(std::numeric_limits::has_infinity) { val = -std::numeric_limits::infinity(); } else { return err(make_error_info("toml::parse_floating: inf value found" " but the current environment does not support inf. Please" " make sure that the floating-point implementation conforms" " IEEE 754/ISO 60559 international standard.", source_location(region(loc)), "floating_type: inf is not supported")); } } else if(str == "nan" || str == "+nan") { TOML11_CONSTEXPR_IF(std::numeric_limits::has_quiet_NaN) { val = std::numeric_limits::quiet_NaN(); } else TOML11_CONSTEXPR_IF(std::numeric_limits::has_signaling_NaN) { val = std::numeric_limits::signaling_NaN(); } else { return err(make_error_info("toml::parse_floating: NaN value found" " but the current environment does not support NaN. Please" " make sure that the floating-point implementation conforms" " IEEE 754/ISO 60559 international standard.", source_location(region(loc)), "floating_type: NaN is not supported")); } } else if(str == "-nan") { using std::copysign; TOML11_CONSTEXPR_IF(std::numeric_limits::has_quiet_NaN) { val = copysign(std::numeric_limits::quiet_NaN(), floating_type(-1)); } else TOML11_CONSTEXPR_IF(std::numeric_limits::has_signaling_NaN) { val = copysign(std::numeric_limits::signaling_NaN(), floating_type(-1)); } else { return err(make_error_info("toml::parse_floating: NaN value found" " but the current environment does not support NaN. Please" " make sure that the floating-point implementation conforms" " IEEE 754/ISO 60559 international standard.", source_location(region(loc)), "floating_type: NaN is not supported")); } } else { // set precision const auto has_sign = ! str.empty() && (str.front() == '+' || str.front() == '-'); const auto decpoint = std::find(str.begin(), str.end(), '.'); const auto exponent = std::find_if(str.begin(), str.end(), [](const char c) { return c == 'e' || c == 'E'; }); if(decpoint != str.end() && exponent != str.end()) { assert(decpoint < exponent); } if(fmt.fmt == floating_format::scientific) { // total width fmt.prec = static_cast(std::distance(str.begin(), exponent)); if(has_sign) { fmt.prec -= 1; } if(decpoint != str.end()) { fmt.prec -= 1; } } else if(fmt.fmt == floating_format::hex) { fmt.prec = std::numeric_limits::max_digits10; } else { // width after decimal point fmt.prec = static_cast(std::distance(std::next(decpoint), exponent)); } auto src = source_location(region(loc)); const auto res = TC::parse_float(str, src, is_hex); if(res.is_ok()) { val = res.as_ok(); } else { return err(res.as_err()); } } // ---------------------------------------------------------------------- // parse suffix (extension) if(spec.ext_num_suffix && loc.current() == '_') { const auto sfx_reg = syntax::num_suffix(spec).scan(loc); if( ! sfx_reg.is_ok()) { auto src = source_location(region(loc)); loc = first; return err(make_error_info("toml::parse_floating: " "invalid suffix: should be `_ non-digit-graph (graph | _graph)`", std::move(src), "here")); } auto sfx = sfx_reg.as_string(); assert( ! sfx.empty() && sfx.front() == '_'); sfx.erase(sfx.begin()); // remove the first `_` fmt.suffix = sfx; } return ok(basic_value(val, std::move(fmt), {}, std::move(reg))); } /* ============================================================================ * ___ _ _ _ * | \ __ _| |_ ___| |_(_)_ __ ___ * | |) / _` | _/ -_) _| | ' \/ -_) * |___/\__,_|\__\___|\__|_|_|_|_\___| */ // all the offset_datetime, local_datetime, local_date parses date part. template result, error_info> parse_local_date_only(location& loc, const context& ctx) { const auto first = loc; const auto& spec = ctx.toml_spec(); local_date_format_info fmt; // ---------------------------------------------------------------------- // check syntax auto reg = syntax::local_date(spec).scan(loc); if( ! reg.is_ok()) { return err(make_syntax_error("toml::parse_local_date: " "invalid date: date must be like: 1234-05-06, yyyy-mm-dd.", syntax::local_date(spec), loc)); } // ---------------------------------------------------------------------- // it matches. gen value const auto str = reg.as_string(); // 0123456789 // yyyy-mm-dd const auto year_r = from_string(str.substr(0, 4)); const auto month_r = from_string(str.substr(5, 2)); const auto day_r = from_string(str.substr(8, 2)); if(year_r.is_err()) { auto src = source_location(region(first)); return err(make_error_info("toml::parse_local_date: " "failed to read year `" + str.substr(0, 4) + "`", std::move(src), "here")); } if(month_r.is_err()) { auto src = source_location(region(first)); return err(make_error_info("toml::parse_local_date: " "failed to read month `" + str.substr(5, 2) + "`", std::move(src), "here")); } if(day_r.is_err()) { auto src = source_location(region(first)); return err(make_error_info("toml::parse_local_date: " "failed to read day `" + str.substr(8, 2) + "`", std::move(src), "here")); } const auto year = year_r.unwrap(); const auto month = month_r.unwrap(); const auto day = day_r.unwrap(); { // We briefly check whether the input date is valid or not. // Actually, because of the historical reasons, there are several // edge cases, such as 1582/10/5-1582/10/14 (only in several countries). // But here, we do not care about it. // It makes the code complicated and there is only low probability // that such a specific date is needed in practice. If someone need to // validate date accurately, that means that the one need a specialized // library for their purpose in another layer. const bool is_leap = (year % 4 == 0) && ((year % 100 != 0) || (year % 400 == 0)); const auto max_day = [month, is_leap]() { if(month == 2) { return is_leap ? 29 : 28; } if(month == 4 || month == 6 || month == 9 || month == 11) { return 30; } return 31; }(); if((month < 1 || 12 < month) || (day < 1 || max_day < day)) { auto src = source_location(region(first)); return err(make_error_info("toml::parse_local_date: invalid date.", std::move(src), "month must be 01-12, day must be any of " "01-28,29,30,31 depending on the month/year.")); } } return ok(std::make_tuple( local_date(year, static_cast(month - 1), day), std::move(fmt), std::move(reg) )); } template result, error_info> parse_local_date(location& loc, const context& ctx) { auto val_fmt_reg = parse_local_date_only(loc, ctx); if(val_fmt_reg.is_err()) { return err(val_fmt_reg.unwrap_err()); } auto val = std::move(std::get<0>(val_fmt_reg.unwrap())); auto fmt = std::move(std::get<1>(val_fmt_reg.unwrap())); auto reg = std::move(std::get<2>(val_fmt_reg.unwrap())); return ok(basic_value(std::move(val), std::move(fmt), {}, std::move(reg))); } // all the offset_datetime, local_datetime, local_time parses date part. template result, error_info> parse_local_time_only(location& loc, const context& ctx) { const auto first = loc; const auto& spec = ctx.toml_spec(); local_time_format_info fmt; // ---------------------------------------------------------------------- // check syntax auto reg = syntax::local_time(spec).scan(loc); if( ! reg.is_ok()) { if(spec.v1_1_0_make_seconds_optional) { return err(make_syntax_error("toml::parse_local_time: " "invalid time: time must be HH:MM(:SS.sss) (seconds are optional)", syntax::local_time(spec), loc)); } else { return err(make_syntax_error("toml::parse_local_time: " "invalid time: time must be HH:MM:SS(.sss) (subseconds are optional)", syntax::local_time(spec), loc)); } } // ---------------------------------------------------------------------- // it matches. gen value const auto str = reg.as_string(); // at least we have HH:MM. // 01234 // HH:MM const auto hour_r = from_string(str.substr(0, 2)); const auto minute_r = from_string(str.substr(3, 2)); if(hour_r.is_err()) { auto src = source_location(region(first)); return err(make_error_info("toml::parse_local_time: " "failed to read hour `" + str.substr(0, 2) + "`", std::move(src), "here")); } if(minute_r.is_err()) { auto src = source_location(region(first)); return err(make_error_info("toml::parse_local_time: " "failed to read minute `" + str.substr(3, 2) + "`", std::move(src), "here")); } const auto hour = hour_r.unwrap(); const auto minute = minute_r.unwrap(); if((hour < 0 || 24 <= hour) || (minute < 0 || 60 <= minute)) { auto src = source_location(region(first)); return err(make_error_info("toml::parse_local_time: invalid time.", std::move(src), "hour must be 00-23, minute must be 00-59.")); } // ----------------------------------------------------------------------- // we have hour and minute. // Since toml v1.1.0, second and subsecond part becomes optional. // Check the version and return if second does not exist. if(str.size() == 5 && spec.v1_1_0_make_seconds_optional) { fmt.has_seconds = false; fmt.subsecond_precision = 0; return ok(std::make_tuple(local_time(hour, minute, 0), std::move(fmt), std::move(reg))); } assert(str.at(5) == ':'); // we have at least `:SS` part. `.subseconds` are optional. // 0 1 // 012345678901234 // HH:MM:SS.subsec const auto sec_r = from_string(str.substr(6, 2)); if(sec_r.is_err()) { auto src = source_location(region(first)); return err(make_error_info("toml::parse_local_time: " "failed to read second `" + str.substr(6, 2) + "`", std::move(src), "here")); } const auto sec = sec_r.unwrap(); if(sec < 0 || 60 < sec) // :60 is allowed { auto src = source_location(region(first)); return err(make_error_info("toml::parse_local_time: invalid time.", std::move(src), "second must be 00-60.")); } if(str.size() == 8) { fmt.has_seconds = true; fmt.subsecond_precision = 0; return ok(std::make_tuple(local_time(hour, minute, sec), std::move(fmt), std::move(reg))); } assert(str.at(8) == '.'); auto secfrac = str.substr(9, str.size() - 9); fmt.has_seconds = true; fmt.subsecond_precision = secfrac.size(); while(secfrac.size() < 9) { secfrac += '0'; } assert(9 <= secfrac.size()); const auto ms_r = from_string(secfrac.substr(0, 3)); const auto us_r = from_string(secfrac.substr(3, 3)); const auto ns_r = from_string(secfrac.substr(6, 3)); if(ms_r.is_err()) { auto src = source_location(region(first)); return err(make_error_info("toml::parse_local_time: " "failed to read milliseconds `" + secfrac.substr(0, 3) + "`", std::move(src), "here")); } if(us_r.is_err()) { auto src = source_location(region(first)); return err(make_error_info("toml::parse_local_time: " "failed to read microseconds`" + str.substr(3, 3) + "`", std::move(src), "here")); } if(ns_r.is_err()) { auto src = source_location(region(first)); return err(make_error_info("toml::parse_local_time: " "failed to read nanoseconds`" + str.substr(6, 3) + "`", std::move(src), "here")); } const auto ms = ms_r.unwrap(); const auto us = us_r.unwrap(); const auto ns = ns_r.unwrap(); return ok(std::make_tuple(local_time(hour, minute, sec, ms, us, ns), std::move(fmt), std::move(reg))); } template result, error_info> parse_local_time(location& loc, const context& ctx) { const auto first = loc; auto val_fmt_reg = parse_local_time_only(loc, ctx); if(val_fmt_reg.is_err()) { return err(val_fmt_reg.unwrap_err()); } auto val = std::move(std::get<0>(val_fmt_reg.unwrap())); auto fmt = std::move(std::get<1>(val_fmt_reg.unwrap())); auto reg = std::move(std::get<2>(val_fmt_reg.unwrap())); return ok(basic_value(std::move(val), std::move(fmt), {}, std::move(reg))); } template result, error_info> parse_local_datetime(location& loc, const context& ctx) { using char_type = location::char_type; const auto first = loc; local_datetime_format_info fmt; // ---------------------------------------------------------------------- auto date_fmt_reg = parse_local_date_only(loc, ctx); if(date_fmt_reg.is_err()) { return err(date_fmt_reg.unwrap_err()); } if(loc.current() == char_type('T')) { loc.advance(); fmt.delimiter = datetime_delimiter_kind::upper_T; } else if(loc.current() == char_type('t')) { loc.advance(); fmt.delimiter = datetime_delimiter_kind::lower_t; } else if(loc.current() == char_type(' ')) { loc.advance(); fmt.delimiter = datetime_delimiter_kind::space; } else { auto src = source_location(region(loc)); return err(make_error_info("toml::parse_local_datetime: " "expect date-time delimiter `T`, `t` or ` `(space).", std::move(src), "here")); } auto time_fmt_reg = parse_local_time_only(loc, ctx); if(time_fmt_reg.is_err()) { return err(time_fmt_reg.unwrap_err()); } fmt.has_seconds = std::get<1>(time_fmt_reg.unwrap()).has_seconds; fmt.subsecond_precision = std::get<1>(time_fmt_reg.unwrap()).subsecond_precision; // ---------------------------------------------------------------------- region reg(first, loc); local_datetime val(std::get<0>(date_fmt_reg.unwrap()), std::get<0>(time_fmt_reg.unwrap())); return ok(basic_value(val, std::move(fmt), {}, std::move(reg))); } template result, error_info> parse_offset_datetime(location& loc, const context& ctx) { using char_type = location::char_type; const auto first = loc; const auto& spec = ctx.toml_spec(); offset_datetime_format_info fmt; // ---------------------------------------------------------------------- // date part auto date_fmt_reg = parse_local_date_only(loc, ctx); if(date_fmt_reg.is_err()) { return err(date_fmt_reg.unwrap_err()); } // ---------------------------------------------------------------------- // delimiter if(loc.current() == char_type('T')) { loc.advance(); fmt.delimiter = datetime_delimiter_kind::upper_T; } else if(loc.current() == char_type('t')) { loc.advance(); fmt.delimiter = datetime_delimiter_kind::lower_t; } else if(loc.current() == char_type(' ')) { loc.advance(); fmt.delimiter = datetime_delimiter_kind::space; } else { auto src = source_location(region(loc)); return err(make_error_info("toml::parse_offset_datetime: " "expect date-time delimiter `T` or ` `(space).", std::move(src), "here" )); } // ---------------------------------------------------------------------- // time part auto time_fmt_reg = parse_local_time_only(loc, ctx); if(time_fmt_reg.is_err()) { return err(time_fmt_reg.unwrap_err()); } fmt.has_seconds = std::get<1>(time_fmt_reg.unwrap()).has_seconds; fmt.subsecond_precision = std::get<1>(time_fmt_reg.unwrap()).subsecond_precision; // ---------------------------------------------------------------------- // offset part const auto ofs_reg = syntax::time_offset(spec).scan(loc); if( ! ofs_reg.is_ok()) { return err(make_syntax_error("toml::parse_offset_datetime: " "invalid offset: offset must be like: Z, +01:00, or -10:00.", syntax::time_offset(spec), loc)); } const auto ofs_str = ofs_reg.as_string(); time_offset offset(0, 0); assert(ofs_str.size() != 0); if(ofs_str.at(0) == char_type('+') || ofs_str.at(0) == char_type('-')) { const auto hour_r = from_string(ofs_str.substr(1, 2)); const auto minute_r = from_string(ofs_str.substr(4, 2)); if(hour_r.is_err()) { auto src = source_location(region(loc)); return err(make_error_info("toml::parse_offset_datetime: " "Failed to read offset hour part", std::move(src), "here")); } if(minute_r.is_err()) { auto src = source_location(region(loc)); return err(make_error_info("toml::parse_offset_datetime: " "Failed to read offset minute part", std::move(src), "here")); } const auto hour = hour_r.unwrap(); const auto minute = minute_r.unwrap(); if(ofs_str.at(0) == '+') { offset = time_offset(hour, minute); } else { offset = time_offset(-hour, -minute); } } else { assert(ofs_str.at(0) == char_type('Z') || ofs_str.at(0) == char_type('z')); } if (offset.hour < -24 || 24 < offset.hour || offset.minute < -60 || 60 < offset.minute) { return err(make_error_info("toml::parse_offset_datetime: " "too large offset: |hour| <= 24, |minute| <= 60", source_location(region(first, loc)), "here")); } // ---------------------------------------------------------------------- region reg(first, loc); offset_datetime val(local_datetime(std::get<0>(date_fmt_reg.unwrap()), std::get<0>(time_fmt_reg.unwrap())), offset); return ok(basic_value(val, std::move(fmt), {}, std::move(reg))); } /* ============================================================================ * ___ _ _ * / __| |_ _ _(_)_ _ __ _ * \__ \ _| '_| | ' \/ _` | * |___/\__|_| |_|_||_\__, | * |___/ */ template result::string_type, error_info> parse_utf8_codepoint(const region& reg) { using string_type = typename basic_value::string_type; using char_type = typename string_type::value_type; // assert(reg.as_lines().size() == 1); // XXX heavy check const auto str = reg.as_string(); assert( ! str.empty()); assert(str.front() == 'u' || str.front() == 'U' || str.front() == 'x'); std::uint_least32_t codepoint; std::istringstream iss(str.substr(1)); iss >> std::hex >> codepoint; const auto to_char = [](const std::uint_least32_t i) noexcept -> char_type { const auto uc = static_cast(i & 0xFF); return cxx::bit_cast(uc); }; string_type character; if(codepoint < 0x80) // U+0000 ... U+0079 ; just an ASCII. { character += static_cast(codepoint); } else if(codepoint < 0x800) //U+0080 ... U+07FF { // 110yyyyx 10xxxxxx; 0x3f == 0b0011'1111 character += to_char(0xC0|(codepoint >> 6 )); character += to_char(0x80|(codepoint & 0x3F)); } else if(codepoint < 0x10000) // U+0800...U+FFFF { if(0xD800 <= codepoint && codepoint <= 0xDFFF) { auto src = source_location(reg); return err(make_error_info("toml::parse_utf8_codepoint: " "[0xD800, 0xDFFF] is not a valid UTF-8", std::move(src), "here")); } assert(codepoint < 0xD800 || 0xDFFF < codepoint); // 1110yyyy 10yxxxxx 10xxxxxx character += to_char(0xE0| (codepoint >> 12)); character += to_char(0x80|((codepoint >> 6 ) & 0x3F)); character += to_char(0x80|((codepoint ) & 0x3F)); } else if(codepoint < 0x110000) // U+010000 ... U+10FFFF { // 11110yyy 10yyxxxx 10xxxxxx 10xxxxxx character += to_char(0xF0| (codepoint >> 18)); character += to_char(0x80|((codepoint >> 12) & 0x3F)); character += to_char(0x80|((codepoint >> 6 ) & 0x3F)); character += to_char(0x80|((codepoint ) & 0x3F)); } else // out of UTF-8 region { auto src = source_location(reg); return err(make_error_info("toml::parse_utf8_codepoint: " "input codepoint is too large.", std::move(src), "must be in range [0x00, 0x10FFFF]")); } return ok(character); } template result::string_type, error_info> parse_escape_sequence(location& loc, const context& ctx) { using string_type = typename basic_value::string_type; using char_type = typename string_type::value_type; const auto& spec = ctx.toml_spec(); assert( ! loc.eof()); assert(loc.current() == '\\'); loc.advance(); // consume the first backslash string_type retval; if (loc.current() == '\\') { retval += char_type('\\'); loc.advance(); } else if(loc.current() == '"') { retval += char_type('\"'); loc.advance(); } else if(loc.current() == 'b') { retval += char_type('\b'); loc.advance(); } else if(loc.current() == 'f') { retval += char_type('\f'); loc.advance(); } else if(loc.current() == 'n') { retval += char_type('\n'); loc.advance(); } else if(loc.current() == 'r') { retval += char_type('\r'); loc.advance(); } else if(loc.current() == 't') { retval += char_type('\t'); loc.advance(); } else if(spec.v1_1_0_add_escape_sequence_e && loc.current() == 'e') { retval += char_type('\x1b'); loc.advance(); } else if(spec.v1_1_0_add_escape_sequence_x && loc.current() == 'x') { auto scanner = sequence(character('x'), repeat_exact(2, syntax::hexdig(spec))); const auto reg = scanner.scan(loc); if( ! reg.is_ok()) { auto src = source_location(region(loc)); return err(make_error_info("toml::parse_escape_sequence: " "invalid token found in UTF-8 codepoint \\xhh", std::move(src), "here")); } const auto utf8 = parse_utf8_codepoint(reg); if(utf8.is_err()) { return err(utf8.as_err()); } retval += utf8.unwrap(); } else if(loc.current() == 'u') { auto scanner = sequence(character('u'), repeat_exact(4, syntax::hexdig(spec))); const auto reg = scanner.scan(loc); if( ! reg.is_ok()) { auto src = source_location(region(loc)); return err(make_error_info("toml::parse_escape_sequence: " "invalid token found in UTF-8 codepoint \\uhhhh", std::move(src), "here")); } const auto utf8 = parse_utf8_codepoint(reg); if(utf8.is_err()) { return err(utf8.as_err()); } retval += utf8.unwrap(); } else if(loc.current() == 'U') { auto scanner = sequence(character('U'), repeat_exact(8, syntax::hexdig(spec))); const auto reg = scanner.scan(loc); if( ! reg.is_ok()) { auto src = source_location(region(loc)); return err(make_error_info("toml::parse_escape_sequence: " "invalid token found in UTF-8 codepoint \\Uhhhhhhhh", std::move(src), "here")); } const auto utf8 = parse_utf8_codepoint(reg); if(utf8.is_err()) { return err(utf8.as_err()); } retval += utf8.unwrap(); } else { auto src = source_location(region(loc)); std::string escape_seqs = "allowed escape seqs: \\\\, \\\", \\b, \\f, \\n, \\r, \\t"; if(spec.v1_1_0_add_escape_sequence_e) { escape_seqs += ", \\e"; } if(spec.v1_1_0_add_escape_sequence_x) { escape_seqs += ", \\xhh"; } escape_seqs += ", \\uhhhh, or \\Uhhhhhhhh"; return err(make_error_info("toml::parse_escape_sequence: " "unknown escape sequence.", std::move(src), escape_seqs)); } return ok(retval); } template result, error_info> parse_ml_basic_string(location& loc, const context& ctx) { const auto first = loc; const auto& spec = ctx.toml_spec(); string_format_info fmt; fmt.fmt = string_format::multiline_basic; auto reg = syntax::ml_basic_string(spec).scan(loc); if( ! reg.is_ok()) { return err(make_syntax_error("toml::parse_ml_basic_string: " "invalid string format", syntax::ml_basic_string(spec), loc)); } // ---------------------------------------------------------------------- // it matches. gen value auto str = reg.as_string(); // we already checked that it starts with """ and ends with """. assert(str.substr(0, 3) == "\"\"\""); str.erase(0, 3); assert(str.size() >= 3); assert(str.substr(str.size()-3, 3) == "\"\"\""); str.erase(str.size()-3, 3); // the first newline just after """ is trimmed if(str.size() >= 1 && str.at(0) == '\n') { str.erase(0, 1); fmt.start_with_newline = true; } else if(str.size() >= 2 && str.at(0) == '\r' && str.at(1) == '\n') { str.erase(0, 2); fmt.start_with_newline = true; } using string_type = typename basic_value::string_type; string_type val; { auto iter = str.cbegin(); while(iter != str.cend()) { if(*iter == '\\') // remove whitespaces around escaped-newline { // we assume that the string is not too long to copy auto loc2 = make_temporary_location(make_string(iter, str.cend())); if(syntax::escaped_newline(spec).scan(loc2).is_ok()) { std::advance(iter, loc2.get_location()); // skip escaped newline and indent // now iter points non-WS char assert(iter == str.end() || (*iter != ' ' && *iter != '\t')); } else // normal escape seq. { auto esc = parse_escape_sequence(loc2, ctx); // syntax does not check its value. the unicode codepoint may be // invalid, e.g. out-of-bound, [0xD800, 0xDFFF] if(esc.is_err()) { return err(esc.unwrap_err()); } val += esc.unwrap(); std::advance(iter, loc2.get_location()); } } else // we already checked the syntax. we don't need to check it again. { val += static_cast(*iter); ++iter; } } } return ok(basic_value( std::move(val), std::move(fmt), {}, std::move(reg) )); } template result::string_type, region>, error_info> parse_basic_string_only(location& loc, const context& ctx) { const auto first = loc; const auto& spec = ctx.toml_spec(); auto reg = syntax::basic_string(spec).scan(loc); if( ! reg.is_ok()) { return err(make_syntax_error("toml::parse_basic_string: " "invalid string format", syntax::basic_string(spec), loc)); } // ---------------------------------------------------------------------- // it matches. gen value auto str = reg.as_string(); assert(str.back() == '\"'); str.pop_back(); assert(str.at(0) == '\"'); str.erase(0, 1); using string_type = typename basic_value::string_type; using char_type = typename string_type::value_type; string_type val; { auto iter = str.begin(); while(iter != str.end()) { if(*iter == '\\') { auto loc2 = make_temporary_location(make_string(iter, str.end())); auto esc = parse_escape_sequence(loc2, ctx); // syntax does not check its value. the unicode codepoint may be // invalid, e.g. out-of-bound, [0xD800, 0xDFFF] if(esc.is_err()) { return err(esc.unwrap_err()); } val += esc.unwrap(); std::advance(iter, loc2.get_location()); } else { val += char_type(*iter); // we already checked the syntax. ++iter; } } } return ok(std::make_pair(val, reg)); } template result, error_info> parse_basic_string(location& loc, const context& ctx) { const auto first = loc; string_format_info fmt; fmt.fmt = string_format::basic; auto val_res = parse_basic_string_only(loc, ctx); if(val_res.is_err()) { return err(std::move(val_res.unwrap_err())); } auto val = std::move(val_res.unwrap().first ); auto reg = std::move(val_res.unwrap().second); return ok(basic_value(std::move(val), std::move(fmt), {}, std::move(reg))); } template result, error_info> parse_ml_literal_string(location& loc, const context& ctx) { const auto first = loc; const auto& spec = ctx.toml_spec(); string_format_info fmt; fmt.fmt = string_format::multiline_literal; auto reg = syntax::ml_literal_string(spec).scan(loc); if( ! reg.is_ok()) { return err(make_syntax_error("toml::parse_ml_literal_string: " "invalid string format", syntax::ml_literal_string(spec), loc)); } // ---------------------------------------------------------------------- // it matches. gen value auto str = reg.as_string(); assert(str.substr(0, 3) == "'''"); assert(str.substr(str.size()-3, 3) == "'''"); str.erase(0, 3); str.erase(str.size()-3, 3); // the first newline just after """ is trimmed if(str.size() >= 1 && str.at(0) == '\n') { str.erase(0, 1); fmt.start_with_newline = true; } else if(str.size() >= 2 && str.at(0) == '\r' && str.at(1) == '\n') { str.erase(0, 2); fmt.start_with_newline = true; } using string_type = typename basic_value::string_type; string_type val(str.begin(), str.end()); return ok(basic_value( std::move(val), std::move(fmt), {}, std::move(reg) )); } template result::string_type, region>, error_info> parse_literal_string_only(location& loc, const context& ctx) { const auto first = loc; const auto& spec = ctx.toml_spec(); auto reg = syntax::literal_string(spec).scan(loc); if( ! reg.is_ok()) { return err(make_syntax_error("toml::parse_literal_string: " "invalid string format", syntax::literal_string(spec), loc)); } // ---------------------------------------------------------------------- // it matches. gen value auto str = reg.as_string(); assert(str.back() == '\''); str.pop_back(); assert(str.at(0) == '\''); str.erase(0, 1); using string_type = typename basic_value::string_type; string_type val(str.begin(), str.end()); return ok(std::make_pair(std::move(val), std::move(reg))); } template result, error_info> parse_literal_string(location& loc, const context& ctx) { const auto first = loc; string_format_info fmt; fmt.fmt = string_format::literal; auto val_res = parse_literal_string_only(loc, ctx); if(val_res.is_err()) { return err(std::move(val_res.unwrap_err())); } auto val = std::move(val_res.unwrap().first ); auto reg = std::move(val_res.unwrap().second); return ok(basic_value( std::move(val), std::move(fmt), {}, std::move(reg) )); } template result, error_info> parse_string(location& loc, const context& ctx) { const auto first = loc; if( ! loc.eof() && loc.current() == '"') { if(literal("\"\"\"").scan(loc).is_ok()) { loc = first; return parse_ml_basic_string(loc, ctx); } else { loc = first; return parse_basic_string(loc, ctx); } } else if( ! loc.eof() && loc.current() == '\'') { if(literal("'''").scan(loc).is_ok()) { loc = first; return parse_ml_literal_string(loc, ctx); } else { loc = first; return parse_literal_string(loc, ctx); } } else { auto src = source_location(region(loc)); return err(make_error_info("toml::parse_string: " "not a string", std::move(src), "here")); } } template result, error_info> parse_null(location& loc, const context& ctx) { const auto& spec = ctx.toml_spec(); if( ! spec.ext_null_value) { return err(make_error_info("toml::parse_null: " "invalid spec: spec.ext_null_value must be true.", source_location(region(loc)), "here")); } // ---------------------------------------------------------------------- // check syntax auto reg = syntax::null_value(spec).scan(loc); if( ! reg.is_ok()) { return err(make_syntax_error("toml::parse_null: " "invalid null: null must be lowercase. ", syntax::null_value(spec), loc)); } // ---------------------------------------------------------------------- // it matches. gen value // ---------------------------------------------------------------------- // no format info for boolean return ok(basic_value(detail::none_t{}, std::move(reg))); } /* ============================================================================ * _ __ * | |/ /___ _ _ * | ' result::key_type, error_info> parse_simple_key(location& loc, const context& ctx) { using key_type = typename basic_value::key_type; const auto& spec = ctx.toml_spec(); if(loc.current() == '\"') { auto str_res = parse_basic_string_only(loc, ctx); if(str_res.is_ok()) { return ok(std::move(str_res.unwrap().first)); } else { return err(std::move(str_res.unwrap_err())); } } else if(loc.current() == '\'') { auto str_res = parse_literal_string_only(loc, ctx); if(str_res.is_ok()) { return ok(std::move(str_res.unwrap().first)); } else { return err(std::move(str_res.unwrap_err())); } } // bare key. if(const auto bare = syntax::unquoted_key(spec).scan(loc)) { return ok(string_conv(bare.as_string())); } else { std::string postfix; if(spec.v1_1_0_allow_non_english_in_bare_keys) { postfix = "Hint: Not all Unicode characters are allowed as bare key.\n"; } else { postfix = "Hint: non-ASCII scripts are allowed in toml v1.1.0, but not in v1.0.0.\n"; } return err(make_syntax_error("toml::parse_simple_key: " "invalid key: key must be \"quoted\", 'quoted-literal', or bare key.", syntax::unquoted_key(spec), loc, postfix)); } } // dotted key become vector of keys template result::key_type>, region>, error_info> parse_key(location& loc, const context& ctx) { const auto first = loc; const auto& spec = ctx.toml_spec(); using key_type = typename basic_value::key_type; std::vector keys; while( ! loc.eof()) { auto key = parse_simple_key(loc, ctx); if( ! key.is_ok()) { return err(key.unwrap_err()); } keys.push_back(std::move(key.unwrap())); auto reg = syntax::dot_sep(spec).scan(loc); if( ! reg.is_ok()) { break; } } if(keys.empty()) { auto src = source_location(region(first)); return err(make_error_info("toml::parse_key: expected a new key, " "but got nothing", std::move(src), "reached EOF")); } return ok(std::make_pair(std::move(keys), region(first, loc))); } // ============================================================================ // forward-decl to implement parse_array and parse_table template result, error_info> parse_value(location&, context& ctx); template result::key_type>, region>, basic_value >, error_info> parse_key_value_pair(location& loc, context& ctx) { const auto first = loc; const auto& spec = ctx.toml_spec(); auto key_res = parse_key(loc, ctx); if(key_res.is_err()) { loc = first; return err(key_res.unwrap_err()); } if( ! syntax::keyval_sep(spec).scan(loc).is_ok()) { auto e = make_syntax_error("toml::parse_key_value_pair: " "invalid key value separator `=`", syntax::keyval_sep(spec), loc); loc = first; return err(std::move(e)); } auto v_res = parse_value(loc, ctx); if(v_res.is_err()) { // loc = first; return err(v_res.unwrap_err()); } return ok(std::make_pair(std::move(key_res.unwrap()), std::move(v_res.unwrap()))); } /* ============================================================================ * __ _ _ _ _ _ __ _ _ _ * / _` | '_| '_/ _` | || | * \__,_|_| |_| \__,_|\_, | * |__/ */ // array(and multiline inline table with `{` and `}`) has the following format. // `[` // (ws|newline|comment-line)? (value) (ws|newline|comment-line)? `,` // (ws|newline|comment-line)? (value) (ws|newline|comment-line)? `,` // ... // (ws|newline|comment-line)? (value) (ws|newline|comment-line)? (`,`)? // (ws|newline|comment-line)? `]` // it skips (ws|newline|comment-line) and returns the token. template struct multiline_spacer { using comment_type = typename TC::comment_type; bool newline_found; indent_char indent_type; std::int32_t indent; comment_type comments; }; template std::ostream& operator<<(std::ostream& os, const multiline_spacer& sp) { os << "{newline=" << sp.newline_found << ", "; os << "indent_type=" << sp.indent_type << ", "; os << "indent=" << sp.indent << ", "; os << "comments=" << sp.comments.size() << "}"; return os; } template cxx::optional> skip_multiline_spacer(location& loc, context& ctx, const bool newline_found = false) { const auto& spec = ctx.toml_spec(); multiline_spacer spacer; spacer.newline_found = newline_found; spacer.indent_type = indent_char::none; spacer.indent = 0; spacer.comments.clear(); bool spacer_found = false; while( ! loc.eof()) { if(auto comm = sequence(syntax::comment(spec), syntax::newline(spec)).scan(loc)) { spacer.newline_found = true; auto comment = comm.as_string(); if( ! comment.empty() && comment.back() == '\n') { comment.pop_back(); if (!comment.empty() && comment.back() == '\r') { comment.pop_back(); } } spacer.comments.push_back(std::move(comment)); spacer.indent_type = indent_char::none; spacer.indent = 0; spacer_found = true; } else if(auto nl = syntax::newline(spec).scan(loc)) { spacer.newline_found = true; spacer.comments.clear(); spacer.indent_type = indent_char::none; spacer.indent = 0; spacer_found = true; } else if(auto sp = repeat_at_least(1, character(cxx::bit_cast(' '))).scan(loc)) { spacer.indent_type = indent_char::space; spacer.indent = static_cast(sp.length()); spacer_found = true; } else if(auto tabs = repeat_at_least(1, character(cxx::bit_cast('\t'))).scan(loc)) { spacer.indent_type = indent_char::tab; spacer.indent = static_cast(tabs.length()); spacer_found = true; } else { break; // done } } if( ! spacer_found) { return cxx::make_nullopt(); } return spacer; } // not an [[array.of.tables]]. It parses ["this", "type"] template result, error_info> parse_array(location& loc, context& ctx) { const auto num_errors = ctx.errors().size(); const auto first = loc; if(loc.eof() || loc.current() != '[') { auto src = source_location(region(loc)); return err(make_error_info("toml::parse_array: " "The next token is not an array", std::move(src), "here")); } loc.advance(); typename basic_value::array_type val; array_format_info fmt; fmt.fmt = array_format::oneline; fmt.indent_type = indent_char::none; auto spacer = skip_multiline_spacer(loc, ctx); if(spacer.has_value() && spacer.value().newline_found) { fmt.fmt = array_format::multiline; } bool comma_found = true; while( ! loc.eof()) { if(loc.current() == location::char_type(']')) { if(spacer.has_value() && spacer.value().newline_found && spacer.value().indent_type != indent_char::none) { fmt.indent_type = spacer.value().indent_type; fmt.closing_indent = spacer.value().indent; } break; } if( ! comma_found) { auto src = source_location(region(loc)); return err(make_error_info("toml::parse_array: " "expected value-separator `,` or closing `]`", std::move(src), "here")); } if(spacer.has_value() && spacer.value().newline_found && spacer.value().indent_type != indent_char::none) { fmt.indent_type = spacer.value().indent_type; fmt.body_indent = spacer.value().indent; } if(auto elem_res = parse_value(loc, ctx)) { auto elem = std::move(elem_res.unwrap()); if(spacer.has_value()) // copy previous comments to value { elem.comments() = std::move(spacer.value().comments); } // parse spaces between a value and a comma // array = [ // 42 , # the answer // ^^^^ // 3.14 # pi // , 2.71 ^^^^ // ^^ spacer = skip_multiline_spacer(loc, ctx); if(spacer.has_value()) { for(std::size_t i=0; i( std::move(val), std::move(fmt), {}, region(first, loc) )); } /* ============================================================================ * _ _ _ _ _ _ * (_)_ _ | (_)_ _ ___ | |_ __ _| |__| |___ * | | ' \| | | ' \/ -_) | _/ _` | '_ \ / -_) * |_|_||_|_|_|_||_\___| \__\__,_|_.__/_\___| */ // ---------------------------------------------------------------------------- // insert_value is the most complicated part of the toml spec. // // To parse a toml file correctly, we sometimes need to check an exising value // is appendable or not. // // For example while parsing an inline array of tables, // // ```toml // aot = [ // {a = "foo"}, // {a = "bar", b = "baz"}, // ] // ``` // // this `aot` is appendable until parser reaches to `]`. After that, it becomes // non-appendable. // // On the other hand, a normal array of tables, such as // // ```toml // [[aot]] // a = "foo" // // [[aot]] // a = "bar" // b = "baz" // ``` // This `[[aot]]` is appendable until the parser reaches to the EOF. // // // It becomes a bit more difficult in case of dotted keys. // In TOML, it is allowed to append a key-value pair to a table that is // *implicitly* defined by a subtable definitino. // // ```toml // [x.y.z] // w = 123 // // [x] // a = "foo" # OK. x is defined implicitly by `[x.y.z]`. // ``` // // But if the table is defined by a dotted keys, it is not appendable. // // ```toml // [x] // y.z.w = 123 // // [x.y] // # ERROR. x.y is already defined by a dotted table in the previous table. // ``` // // Also, reopening a table using dotted keys is invalid. // // ```toml // [x.y.z] // w = 123 // // [x] // y.z.v = 42 # ERROR. [x.y.z] is already defined. // ``` // // // ```toml // [a] // b.c = "foo" // b.d = "bar" // ``` // // // ```toml // a.b = "foo" // [a] // c = "bar" # ERROR // ``` // // In summary, // - a table must be defined only once. // - assignment to an exising table is possible only when: // - defining a subtable [x.y] to an existing table [x]. // - defining supertable [x] explicitly after [x.y]. // - adding dotted keys in the same table. enum class inserting_value_kind : std::uint8_t { std_table, // insert [standard.table] array_table, // insert [[array.of.tables]] dotted_keys // insert a.b.c = "this" }; template result*, error_info> insert_value(const inserting_value_kind kind, typename basic_value::table_type* current_table_ptr, const std::vector::key_type>& keys, region key_reg, basic_value val) { using value_type = basic_value; using array_type = typename basic_value::array_type; using table_type = typename basic_value::table_type; auto key_loc = source_location(key_reg); assert( ! keys.empty()); // dotted key can insert to dotted key tables defined at the same level. // dotted key can NOT reopen a table even if it is implcitly-defined one. // // [x.y.z] # define x and x.y implicitly. // a = 42 // // [x] # reopening implcitly defined table // r.s.t = 3.14 # VALID r and r.s are new tables. // r.s.u = 2.71 # VALID r and r.s are dotted-key tables. valid. // // y.z.b = "foo" # INVALID x.y.z are multiline table, not a dotted key. // y.c = "bar" # INVALID x.y is implicit multiline table, not a dotted key. // a table cannot reopen dotted-key tables. // // [t1] // t2.t3.v = 0 // [t1.t2] # INVALID t1.t2 is defined as a dotted-key table. for(std::size_t i=0; i{}, key_reg)); assert(current_table.at(key).is_table()); current_table_ptr = std::addressof(current_table.at(key).as_table()); } else if (found->second.is_table()) { const auto fmt = found->second.as_table_fmt().fmt; if(fmt == table_format::oneline || fmt == table_format::multiline_oneline) { // foo = {bar = "baz"} or foo = { \n bar = "baz" \n } return err(make_error_info("toml::insert_value: " "failed to insert a value: inline table is immutable", key_loc, "inserting this", found->second.location(), "to this table")); } // dotted key cannot reopen a table. if(kind ==inserting_value_kind::dotted_keys && fmt != table_format::dotted) { return err(make_error_info("toml::insert_value: " "reopening a table using dotted keys", key_loc, "dotted key cannot reopen a table", found->second.location(), "this table is already closed")); } assert(found->second.is_table()); current_table_ptr = std::addressof(found->second.as_table()); } else if(found->second.is_array_of_tables()) { // aot = [{this = "type", of = "aot"}] # cannot be reopened if(found->second.as_array_fmt().fmt != array_format::array_of_tables) { return err(make_error_info("toml::insert_value:" "inline array of tables are immutable", key_loc, "inserting this", found->second.location(), "inline array of tables")); } // appending to [[aot]] if(kind == inserting_value_kind::dotted_keys) { // [[array.of.tables]] // [array.of] # reopening supertable is okay // tables.x = "foo" # appending `x` to the first table return err(make_error_info("toml::insert_value:" "dotted key cannot reopen an array-of-tables", key_loc, "inserting this", found->second.location(), "to this array-of-tables.")); } // insert_value_by_dotkeys::std_table // [[array.of.tables]] // [array.of.tables.subtable] # appending to the last aot // // insert_value_by_dotkeys::array_table // [[array.of.tables]] // [[array.of.tables.subtable]] # appending to the last aot auto& current_array_table = found->second.as_array().back(); assert(current_array_table.is_table()); current_table_ptr = std::addressof(current_array_table.as_table()); } else { return err(make_error_info("toml::insert_value: " "failed to insert a value, value already exists", key_loc, "while inserting this", found->second.location(), "non-table value already exists")); } } else // this is the last key. insert a new value. { switch(kind) { case inserting_value_kind::dotted_keys: { if(current_table.find(key) != current_table.end()) { return err(make_error_info("toml::insert_value: " "failed to insert a value, value already exists", key_loc, "inserting this", current_table.at(key).location(), "but value already exists")); } current_table.emplace(key, std::move(val)); return ok(std::addressof(current_table.at(key))); } case inserting_value_kind::std_table: { // defining a new table or reopening supertable auto found = current_table.find(key); if(found == current_table.end()) // define a new aot { current_table.emplace(key, std::move(val)); return ok(std::addressof(current_table.at(key))); } else // the table is already defined, reopen it { // assigning a [std.table]. it must be an implicit table. auto& target = found->second; if( ! target.is_table() || // could be an array-of-tables target.as_table_fmt().fmt != table_format::implicit) { return err(make_error_info("toml::insert_value: " "failed to insert a table, table already defined", key_loc, "inserting this", target.location(), "this table is explicitly defined")); } // merge table for(const auto& kv : val.as_table()) { if(target.contains(kv.first)) { // [x.y.z] // w = "foo" // [x] // y = "bar" return err(make_error_info("toml::insert_value: " "failed to insert a table, table keys conflict to each other", key_loc, "inserting this table", kv.second.location(), "having this value", target.at(kv.first).location(), "already defined here")); } else { target[kv.first] = kv.second; } } // change implicit -> explicit target.as_table_fmt().fmt = table_format::multiline; // change definition region change_region_of_value(target, val); return ok(std::addressof(current_table.at(key))); } } case inserting_value_kind::array_table: { auto found = current_table.find(key); if(found == current_table.end()) // define a new aot { array_format_info fmt; fmt.fmt = array_format::array_of_tables; fmt.indent_type = indent_char::none; current_table.emplace(key, value_type( array_type{ std::move(val) }, std::move(fmt), std::vector{}, std::move(key_reg) )); assert( ! current_table.at(key).as_array().empty()); return ok(std::addressof(current_table.at(key).as_array().back())); } else // the array is already defined, append to it { if( ! found->second.is_array_of_tables()) { return err(make_error_info("toml::insert_value: " "failed to insert an array of tables, value already exists", key_loc, "while inserting this", found->second.location(), "non-table value already exists")); } if(found->second.as_array_fmt().fmt != array_format::array_of_tables) { return err(make_error_info("toml::insert_value: " "failed to insert a table, inline array of tables is immutable", key_loc, "while inserting this", found->second.location(), "this is inline array-of-tables")); } found->second.as_array().push_back(std::move(val)); assert( ! current_table.at(key).as_array().empty()); return ok(std::addressof(current_table.at(key).as_array().back())); } } default: {assert(false);} } } } return err(make_error_info("toml::insert_key: no keys found", std::move(key_loc), "here")); } // ---------------------------------------------------------------------------- template result, error_info> parse_inline_table(location& loc, context& ctx) { using table_type = typename basic_value::table_type; const auto num_errors = ctx.errors().size(); const auto first = loc; const auto& spec = ctx.toml_spec(); if(loc.eof() || loc.current() != '{') { auto src = source_location(region(loc)); return err(make_error_info("toml::parse_inline_table: " "The next token is not an inline table", std::move(src), "here")); } loc.advance(); table_type table; table_format_info fmt; fmt.fmt = table_format::oneline; fmt.indent_type = indent_char::none; cxx::optional> spacer(cxx::make_nullopt()); if(spec.v1_1_0_allow_newlines_in_inline_tables) { spacer = skip_multiline_spacer(loc, ctx); if(spacer.has_value() && spacer.value().newline_found) { fmt.fmt = table_format::multiline_oneline; } } else { skip_whitespace(loc, ctx); } bool still_empty = true; bool comma_found = false; while( ! loc.eof()) { // closing! if(loc.current() == '}') { if(comma_found && ! spec.v1_1_0_allow_trailing_comma_in_inline_tables) { auto src = source_location(region(loc)); return err(make_error_info("toml::parse_inline_table: trailing " "comma is not allowed in TOML-v1.0.0)", std::move(src), "here")); } if(spec.v1_1_0_allow_newlines_in_inline_tables) { if(spacer.has_value() && spacer.value().newline_found && spacer.value().indent_type != indent_char::none) { fmt.indent_type = spacer.value().indent_type; fmt.closing_indent = spacer.value().indent; } } break; } // if we already found a value and didn't found `,` nor `}`, error. if( ! comma_found && ! still_empty) { auto src = source_location(region(loc)); return err(make_error_info("toml::parse_inline_table: " "expected value-separator `,` or closing `}`", std::move(src), "here")); } // parse indent. if(spacer.has_value() && spacer.value().newline_found && spacer.value().indent_type != indent_char::none) { fmt.indent_type = spacer.value().indent_type; fmt.body_indent = spacer.value().indent; } still_empty = false; // parsing a value... if(auto kv_res = parse_key_value_pair(loc, ctx)) { auto keys = std::move(kv_res.unwrap().first.first); auto key_reg = std::move(kv_res.unwrap().first.second); auto val = std::move(kv_res.unwrap().second); auto ins_res = insert_value(inserting_value_kind::dotted_keys, std::addressof(table), keys, std::move(key_reg), std::move(val)); if(ins_res.is_err()) { ctx.report_error(std::move(ins_res.unwrap_err())); // we need to skip until the next value (or end of the table) // because we don't have valid kv pair. while( ! loc.eof()) { const auto c = loc.current(); if(c == ',' || c == '\n' || c == '}') { comma_found = (c == ','); break; } loc.advance(); } continue; } // if comment line follows immediately(without newline) after `,`, then // the comment is for the elem. we need to check if comment follows `,`. // // (key) = (val) (ws|newline|comment-line)? `,` (ws)? (comment)? if(spec.v1_1_0_allow_newlines_in_inline_tables) { if(spacer.has_value()) // copy previous comments to value { for(std::size_t i=0; icomments().push_back(spacer.value().comments.at(i)); } } spacer = skip_multiline_spacer(loc, ctx); if(spacer.has_value()) { for(std::size_t i=0; icomments().push_back(spacer.value().comments.at(i)); } if(spacer.value().newline_found) { fmt.fmt = table_format::multiline_oneline; if(spacer.value().indent_type != indent_char::none) { fmt.indent_type = spacer.value().indent_type; fmt.body_indent = spacer.value().indent; } } } } else { skip_whitespace(loc, ctx); } comma_found = character(',').scan(loc).is_ok(); if(spec.v1_1_0_allow_newlines_in_inline_tables) { auto com_res = parse_comment_line(loc, ctx); if(com_res.is_err()) { ctx.report_error(com_res.unwrap_err()); } const bool comment_found = com_res.is_ok() && com_res.unwrap().has_value(); if(comment_found) { fmt.fmt = table_format::multiline_oneline; ins_res.unwrap()->comments().push_back(com_res.unwrap().value()); } if(comma_found) { spacer = skip_multiline_spacer(loc, ctx, comment_found); if(spacer.has_value() && spacer.value().newline_found) { fmt.fmt = table_format::multiline_oneline; } } } else { skip_whitespace(loc, ctx); } } else { ctx.report_error(std::move(kv_res.unwrap_err())); while( ! loc.eof()) { if(loc.current() == '}') { break; } if( ! spec.v1_1_0_allow_newlines_in_inline_tables && loc.current() == '\n') { break; } loc.advance(); } break; } } if(loc.current() != '}') { auto src = source_location(region(loc)); return err(make_error_info("toml::parse_inline_table: " "missing closing bracket `}`", std::move(src), "expected `}`, reached line end")); } else { loc.advance(); // skip } } // any error reported from this function if(num_errors < ctx.errors().size()) { assert(ctx.has_error()); // already reported return err(ctx.pop_last_error()); } basic_value retval( std::move(table), std::move(fmt), {}, region(first, loc)); return ok(std::move(retval)); } /* ============================================================================ * _ * __ ____ _| |_ _ ___ * \ V / _` | | || / -_) * \_/\__,_|_|\_,_\___| */ template result guess_number_type(const location& first, const context& ctx) { const auto& spec = ctx.toml_spec(); location loc = first; if(syntax::offset_datetime(spec).scan(loc).is_ok()) { return ok(value_t::offset_datetime); } loc = first; if(syntax::local_datetime(spec).scan(loc).is_ok()) { const auto curr = loc.current(); // if offset_datetime contains bad offset, it syntax::offset_datetime // fails to scan it. if(curr == '+' || curr == '-') { return err(make_syntax_error("bad offset: must be [+-]HH:MM or Z", syntax::time_offset(spec), loc, std::string( "Hint: valid : +09:00, -05:30\n" "Hint: invalid: +9:00, -5:30\n"))); } return ok(value_t::local_datetime); } loc = first; if(syntax::local_date(spec).scan(loc).is_ok()) { // bad time may appear after this. if( ! loc.eof()) { const auto c = loc.current(); if(c == 'T' || c == 't') { loc.advance(); return err(make_syntax_error("bad time: must be HH:MM:SS.subsec", syntax::local_time(spec), loc, std::string( "Hint: valid : 1979-05-27T07:32:00, 1979-05-27 07:32:00.999999\n" "Hint: invalid: 1979-05-27T7:32:00, 1979-05-27 17:32\n"))); } if(c == ' ') { // A space is allowed as a delimiter between local time. // But there is a case where bad time follows a space. // - invalid: 2019-06-16 7:00:00 // - valid : 2019-06-16 07:00:00 loc.advance(); if( ! loc.eof() && ('0' <= loc.current() && loc.current() <= '9')) { return err(make_syntax_error("bad time: must be HH:MM:SS.subsec", syntax::local_time(spec), loc, std::string( "Hint: valid : 1979-05-27T07:32:00, 1979-05-27 07:32:00.999999\n" "Hint: invalid: 1979-05-27T7:32:00, 1979-05-27 17:32\n"))); } } if('0' <= c && c <= '9') { return err(make_syntax_error("bad datetime: missing T or space", character_either{'T', 't', ' '}, loc, std::string( "Hint: valid : 1979-05-27T07:32:00, 1979-05-27 07:32:00.999999\n" "Hint: invalid: 1979-05-27T7:32:00, 1979-05-27 17:32\n"))); } } return ok(value_t::local_date); } loc = first; if(syntax::local_time(spec).scan(loc).is_ok()) { return ok(value_t::local_time); } loc = first; if(syntax::floating(spec).scan(loc).is_ok()) { if( ! loc.eof() && loc.current() == '_') { if(spec.ext_num_suffix && syntax::num_suffix(spec).scan(loc).is_ok()) { return ok(value_t::floating); } auto src = source_location(region(loc)); return err(make_error_info( "bad float: `_` must be surrounded by digits", std::move(src), "invalid underscore", "Hint: valid : +1.0, -2e-2, 3.141_592_653_589, inf, nan\n" "Hint: invalid: .0, 1., _1.0, 1.0_, 1_.0, 1.0__0\n")); } return ok(value_t::floating); } loc = first; if(spec.ext_hex_float) { if(syntax::hex_floating(spec).scan(loc).is_ok()) { if( ! loc.eof() && loc.current() == '_') { if(spec.ext_num_suffix && syntax::num_suffix(spec).scan(loc).is_ok()) { return ok(value_t::floating); } auto src = source_location(region(loc)); return err(make_error_info( "bad float: `_` must be surrounded by digits", std::move(src), "invalid underscore", "Hint: valid : +1.0, -2e-2, 3.141_592_653_589, inf, nan\n" "Hint: invalid: .0, 1., _1.0, 1.0_, 1_.0, 1.0__0\n")); } return ok(value_t::floating); } loc = first; } if(auto int_reg = syntax::integer(spec).scan(loc)) { if( ! loc.eof()) { const auto c = loc.current(); if(c == '_') { if(spec.ext_num_suffix && syntax::num_suffix(spec).scan(loc).is_ok()) { return ok(value_t::integer); } if(int_reg.length() <= 2 && (int_reg.as_string() == "0" || int_reg.as_string() == "-0" || int_reg.as_string() == "+0")) { auto src = source_location(region(loc)); return err(make_error_info( "bad integer: leading zero is not allowed in decimal int", std::move(src), "leading zero", "Hint: valid : -42, 1_000, 1_2_3_4_5, 0xC0FFEE, 0b0010, 0o755\n" "Hint: invalid: _42, 1__000, 0123\n")); } else { auto src = source_location(region(loc)); return err(make_error_info( "bad integer: `_` must be surrounded by digits", std::move(src), "invalid underscore", "Hint: valid : -42, 1_000, 1_2_3_4_5, 0xC0FFEE, 0b0010, 0o755\n" "Hint: invalid: _42, 1__000, 0123\n")); } } if('0' <= c && c <= '9') { if(loc.current() == '0') { loc.retrace(); return err(make_error_info( "bad integer: leading zero", source_location(region(loc)), "leading zero is not allowed", std::string("Hint: valid : -42, 1_000, 1_2_3_4_5, 0xC0FFEE, 0b0010, 0o755\n" "Hint: invalid: _42, 1__000, 0123\n") )); } else // invalid digits, especially in oct/bin ints. { return err(make_error_info( "bad integer: invalid digit after an integer", source_location(region(loc)), "this digit is not allowed", std::string("Hint: valid : -42, 1_000, 1_2_3_4_5, 0xC0FFEE, 0b0010, 0o755\n" "Hint: invalid: _42, 1__000, 0123\n") )); } } if(c == ':' || c == '-') { auto src = source_location(region(loc)); return err(make_error_info("bad datetime: invalid format", std::move(src), "here", std::string("Hint: valid : 1979-05-27T07:32:00-07:00, 1979-05-27 07:32:00.999999Z\n" "Hint: invalid: 1979-05-27T7:32:00-7:00, 1979-05-27 7:32-00:30") )); } if(c == '.' || c == 'e' || c == 'E') { auto src = source_location(region(loc)); return err(make_error_info("bad float: invalid format", std::move(src), "here", std::string( "Hint: valid : +1.0, -2e-2, 3.141_592_653_589, inf, nan\n" "Hint: invalid: .0, 1., _1.0, 1.0_, 1_.0, 1.0__0\n"))); } } return ok(value_t::integer); } if( ! loc.eof() && loc.current() == '.') { auto src = source_location(region(loc)); return err(make_error_info("bad float: integer part is required before decimal point", std::move(src), "missing integer part", std::string( "Hint: valid : +1.0, -2e-2, 3.141_592_653_589, inf, nan\n" "Hint: invalid: .0, 1., _1.0, 1.0_, 1_.0, 1.0__0\n") )); } if( ! loc.eof() && loc.current() == '_') { auto src = source_location(region(loc)); return err(make_error_info("bad number: `_` must be surrounded by digits", std::move(src), "digits required before `_`", std::string( "Hint: valid : -42, 1_000, 1_2_3_4_5, 0xC0FFEE, 0b0010, 0o755\n" "Hint: invalid: _42, 1__000, 0123\n") )); } auto src = source_location(region(loc)); return err(make_error_info("bad format: unknown value appeared", std::move(src), "here")); } template result guess_value_type(const location& loc, const context& ctx) { const auto& sp = ctx.toml_spec(); location inner(loc); switch(loc.current()) { case '"' : {return ok(value_t::string); } case '\'': {return ok(value_t::string); } case '[' : {return ok(value_t::array); } case '{' : {return ok(value_t::table); } case 't' : { return ok(value_t::boolean); } case 'f' : { return ok(value_t::boolean); } case 'T' : // invalid boolean. { return err(make_syntax_error("toml::parse_value: " "`true` must be in lowercase. " "A string must be surrounded by quotes.", syntax::boolean(sp), inner)); } case 'F' : { return err(make_syntax_error("toml::parse_value: " "`false` must be in lowercase. " "A string must be surrounded by quotes.", syntax::boolean(sp), inner)); } case 'i' : // inf or string without quotes(syntax error). { if(literal("inf").scan(inner).is_ok()) { return ok(value_t::floating); } else { return err(make_syntax_error("toml::parse_value: " "`inf` must be in lowercase. " "A string must be surrounded by quotes.", syntax::floating(sp), inner)); } } case 'I' : // Inf or string without quotes(syntax error). { return err(make_syntax_error("toml::parse_value: " "`inf` must be in lowercase. " "A string must be surrounded by quotes.", syntax::floating(sp), inner)); } case 'n' : // nan or null-extension { if(sp.ext_null_value) { if(literal("nan").scan(inner).is_ok()) { return ok(value_t::floating); } else if(literal("null").scan(inner).is_ok()) { return ok(value_t::empty); } else { return err(make_syntax_error("toml::parse_value: " "Both `nan` and `null` must be in lowercase. " "A string must be surrounded by quotes.", syntax::floating(sp), inner)); } } else // must be nan. { if(literal("nan").scan(inner).is_ok()) { return ok(value_t::floating); } else { return err(make_syntax_error("toml::parse_value: " "`nan` must be in lowercase. " "A string must be surrounded by quotes.", syntax::floating(sp), inner)); } } } case 'N' : // nan or null-extension { if(sp.ext_null_value) { return err(make_syntax_error("toml::parse_value: " "Both `nan` and `null` must be in lowercase. " "A string must be surrounded by quotes.", syntax::floating(sp), inner)); } else { return err(make_syntax_error("toml::parse_value: " "`nan` must be in lowercase. " "A string must be surrounded by quotes.", syntax::floating(sp), inner)); } } default : { return guess_number_type(loc, ctx); } } } template result, error_info> parse_value(location& loc, context& ctx) { const auto ty_res = guess_value_type(loc, ctx); if(ty_res.is_err()) { return err(ty_res.unwrap_err()); } switch(ty_res.unwrap()) { case value_t::empty: { if(ctx.toml_spec().ext_null_value) { return parse_null(loc, ctx); } else { auto src = source_location(region(loc)); return err(make_error_info("toml::parse_value: unknown value appeared", std::move(src), "here")); } } case value_t::boolean : {return parse_boolean (loc, ctx);} case value_t::integer : {return parse_integer (loc, ctx);} case value_t::floating : {return parse_floating (loc, ctx);} case value_t::string : {return parse_string (loc, ctx);} case value_t::offset_datetime: {return parse_offset_datetime(loc, ctx);} case value_t::local_datetime : {return parse_local_datetime (loc, ctx);} case value_t::local_date : {return parse_local_date (loc, ctx);} case value_t::local_time : {return parse_local_time (loc, ctx);} case value_t::array : {return parse_array (loc, ctx);} case value_t::table : {return parse_inline_table (loc, ctx);} default: { auto src = source_location(region(loc)); return err(make_error_info("toml::parse_value: unknown value appeared", std::move(src), "here")); } } } /* ============================================================================ * _____ _ _ * |_ _|_ _| |__| |___ * | |/ _` | '_ \ / -_) * |_|\__,_|_.__/_\___| */ template result::key_type>, region>, error_info> parse_table_key(location& loc, context& ctx) { const auto first = loc; const auto& spec = ctx.toml_spec(); auto reg = syntax::std_table(spec).scan(loc); if(!reg.is_ok()) { return err(make_syntax_error("toml::parse_table_key: invalid table key", syntax::std_table(spec), loc)); } loc = first; loc.advance(); // skip [ skip_whitespace(loc, ctx); auto keys_res = parse_key(loc, ctx); if(keys_res.is_err()) { return err(std::move(keys_res.unwrap_err())); } skip_whitespace(loc, ctx); loc.advance(); // ] return ok(std::make_pair(std::move(keys_res.unwrap().first), std::move(reg))); } template result::key_type>, region>, error_info> parse_array_table_key(location& loc, context& ctx) { const auto first = loc; const auto& spec = ctx.toml_spec(); auto reg = syntax::array_table(spec).scan(loc); if(!reg.is_ok()) { return err(make_syntax_error("toml::parse_array_table_key: invalid array-of-tables key", syntax::array_table(spec), loc)); } loc = first; loc.advance(); // [ loc.advance(); // [ skip_whitespace(loc, ctx); auto keys_res = parse_key(loc, ctx); if(keys_res.is_err()) { return err(std::move(keys_res.unwrap_err())); } skip_whitespace(loc, ctx); loc.advance(); // ] loc.advance(); // ] return ok(std::make_pair(std::move(keys_res.unwrap().first), std::move(reg))); } // called after reading [table.keys] and comments around it. // Since table may already contain a subtable ([x.y.z] can be defined before [x]), // the table that is being parsed is passed as an argument. template result parse_table(location& loc, context& ctx, basic_value& table) { assert(table.is_table()); const auto num_errors = ctx.errors().size(); const auto& spec = ctx.toml_spec(); // clear indent info table.as_table_fmt().indent_type = indent_char::none; bool newline_found = true; while( ! loc.eof()) { const auto start = loc; auto sp = skip_multiline_spacer(loc, ctx, newline_found); // if reached to EOF, the table ends here. return. if(loc.eof()) { break; } // if next table is comming, return. if(sequence(syntax::ws(spec), character('[')).scan(loc).is_ok()) { loc = start; break; } // otherwise, it should be a key-value pair. newline_found = newline_found || (sp.has_value() && sp.value().newline_found); if( ! newline_found) { return err(make_error_info("toml::parse_table: " "newline (LF / CRLF) or EOF is expected", source_location(region(loc)), "here")); } if(sp.has_value() && sp.value().indent_type != indent_char::none) { table.as_table_fmt().indent_type = sp.value().indent_type; table.as_table_fmt().body_indent = sp.value().indent; } newline_found = false; // reset if(auto kv_res = parse_key_value_pair(loc, ctx)) { auto keys = std::move(kv_res.unwrap().first.first); auto key_reg = std::move(kv_res.unwrap().first.second); auto val = std::move(kv_res.unwrap().second); if(sp.has_value()) { for(const auto& com : sp.value().comments) { val.comments().push_back(com); } } if(auto com_res = parse_comment_line(loc, ctx)) { if(auto com_opt = com_res.unwrap()) { val.comments().push_back(com_opt.value()); newline_found = true; // comment includes newline at the end } } else { ctx.report_error(std::move(com_res.unwrap_err())); } auto ins_res = insert_value(inserting_value_kind::dotted_keys, std::addressof(table.as_table()), keys, std::move(key_reg), std::move(val)); if(ins_res.is_err()) { ctx.report_error(std::move(ins_res.unwrap_err())); } } else { ctx.report_error(std::move(kv_res.unwrap_err())); skip_key_value_pair(loc, ctx); } } if(num_errors < ctx.errors().size()) { assert(ctx.has_error()); // already reported return err(ctx.pop_last_error()); } return ok(); } template result, std::vector> parse_file(location& loc, context& ctx) { using value_type = basic_value; using table_type = typename value_type::table_type; const auto first = loc; const auto& spec = ctx.toml_spec(); if(loc.eof()) { return ok(value_type(table_type(), table_format_info{}, {}, region(loc))); } value_type root(table_type(), table_format_info{}, {}, region(loc)); root.as_table_fmt().fmt = table_format::multiline; root.as_table_fmt().indent_type = indent_char::none; // parse top comment. // // ```toml // # this is a comment for the top-level table. // // key = "the first value" // ``` // // ```toml // # this is a comment for "the first value". // key = "the first value" // ``` while( ! loc.eof()) { if(auto com_res = parse_comment_line(loc, ctx)) { if(auto com_opt = com_res.unwrap()) { root.comments().push_back(std::move(com_opt.value())); } else // no comment found. { // if it is not an empty line, clear the root comment. if( ! sequence(syntax::ws(spec), syntax::newline(spec)).scan(loc).is_ok()) { loc = first; root.comments().clear(); } break; } } else { ctx.report_error(std::move(com_res.unwrap_err())); skip_comment_block(loc, ctx); } } // parse root table { const auto res = parse_table(loc, ctx, root); if(res.is_err()) { ctx.report_error(std::move(res.unwrap_err())); skip_until_next_table(loc, ctx); } } // parse tables while( ! loc.eof()) { auto sp = skip_multiline_spacer(loc, ctx, /*newline_found=*/true); if(auto key_res = parse_array_table_key(loc, ctx)) { auto key = std::move(std::get<0>(key_res.unwrap())); auto reg = std::move(std::get<1>(key_res.unwrap())); std::vector com; if(sp.has_value()) { for(std::size_t i=0; i(table_type()); auto res = parse_table(loc, ctx, tmp); if(res.is_err()) { ctx.report_error(res.unwrap_err()); skip_until_next_table(loc, ctx); } continue; } auto tab_ptr = inserted.unwrap(); assert(tab_ptr); const auto tab_res = parse_table(loc, ctx, *tab_ptr); if(tab_res.is_err()) { ctx.report_error(tab_res.unwrap_err()); skip_until_next_table(loc, ctx); } // parse_table first clears `indent_type`. // to keep header indent info, we must store it later. if(sp.has_value() && sp.value().indent_type != indent_char::none) { tab_ptr->as_table_fmt().indent_type = sp.value().indent_type; tab_ptr->as_table_fmt().name_indent = sp.value().indent; } continue; } if(auto key_res = parse_table_key(loc, ctx)) { auto key = std::move(std::get<0>(key_res.unwrap())); auto reg = std::move(std::get<1>(key_res.unwrap())); std::vector com; if(sp.has_value()) { for(std::size_t i=0; i(table_type()); auto res = parse_table(loc, ctx, tmp); if(res.is_err()) { ctx.report_error(res.unwrap_err()); skip_until_next_table(loc, ctx); } continue; } auto tab_ptr = inserted.unwrap(); assert(tab_ptr); const auto tab_res = parse_table(loc, ctx, *tab_ptr); if(tab_res.is_err()) { ctx.report_error(tab_res.unwrap_err()); skip_until_next_table(loc, ctx); } if(sp.has_value() && sp.value().indent_type != indent_char::none) { tab_ptr->as_table_fmt().indent_type = sp.value().indent_type; tab_ptr->as_table_fmt().name_indent = sp.value().indent; } continue; } // does not match array_table nor std_table. report an error. const auto keytop = loc; const auto maybe_array_of_tables = literal("[[").scan(loc).is_ok(); loc = keytop; if(maybe_array_of_tables) { ctx.report_error(make_syntax_error("toml::parse_file: invalid array-table key", syntax::array_table(spec), loc)); } else { ctx.report_error(make_syntax_error("toml::parse_file: invalid table key", syntax::std_table(spec), loc)); } skip_until_next_table(loc, ctx); } if( ! ctx.errors().empty()) { return err(std::move(ctx.errors())); } return ok(std::move(root)); } template result, std::vector> parse_impl(std::vector cs, std::string fname, const spec& s) { using value_type = basic_value; using table_type = typename value_type::table_type; // an empty file is a valid toml file. if(cs.empty()) { auto src = std::make_shared>(std::move(cs)); location loc(std::move(src), std::move(fname)); return ok(value_type(table_type(), table_format_info{}, std::vector{}, region(loc))); } // to simplify parser, add newline at the end if there is no LF. // But, if it has raw CR, the file is invalid (in TOML, CR is not a valid // newline char). if it ends with CR, do not add LF and report it. if(cs.back() != '\n' && cs.back() != '\r') { cs.push_back('\n'); } auto src = std::make_shared>(std::move(cs)); location loc(std::move(src), std::move(fname)); // skip BOM if found if(loc.source()->size() >= 3) { auto first = loc.get_location(); const auto c0 = loc.current(); loc.advance(); const auto c1 = loc.current(); loc.advance(); const auto c2 = loc.current(); loc.advance(); const auto bom_found = (c0 == 0xEF) && (c1 == 0xBB) && (c2 == 0xBF); if( ! bom_found) { loc.set_location(first); } } context ctx(s); return parse_file(loc, ctx); } } // detail // ----------------------------------------------------------------------------- // parse(byte array) template result, std::vector> try_parse(std::vector content, std::string filename, spec s = spec::default_version()) { return detail::parse_impl(std::move(content), std::move(filename), std::move(s)); } template basic_value parse(std::vector content, std::string filename, spec s = spec::default_version()) { auto res = try_parse(std::move(content), std::move(filename), std::move(s)); if(res.is_ok()) { return res.unwrap(); } else { std::string msg; for(const auto& err : res.unwrap_err()) { msg += format_error(err); } throw syntax_error(std::move(msg), std::move(res.unwrap_err())); } } // ----------------------------------------------------------------------------- // parse(istream) template result, std::vector> try_parse(std::istream& is, std::string fname = "unknown file", spec s = spec::default_version()) { const auto beg = is.tellg(); is.seekg(0, std::ios::end); const auto end = is.tellg(); const auto fsize = end - beg; is.seekg(beg); // read whole file as a sequence of char assert(fsize >= 0); std::vector letters(static_cast(fsize), '\0'); is.read(reinterpret_cast(letters.data()), fsize); return detail::parse_impl(std::move(letters), std::move(fname), std::move(s)); } template basic_value parse(std::istream& is, std::string fname = "unknown file", spec s = spec::default_version()) { auto res = try_parse(is, std::move(fname), std::move(s)); if(res.is_ok()) { return res.unwrap(); } else { std::string msg; for(const auto& err : res.unwrap_err()) { msg += format_error(err); } throw syntax_error(std::move(msg), std::move(res.unwrap_err())); } } // ----------------------------------------------------------------------------- // parse(filename) template result, std::vector> try_parse(std::string fname, spec s = spec::default_version()) { std::ifstream ifs(fname, std::ios_base::binary); if(!ifs.good()) { std::vector e; e.push_back(error_info("toml::parse: Error opening file \"" + fname + "\"", {})); return err(std::move(e)); } ifs.exceptions(std::ifstream::failbit | std::ifstream::badbit); return try_parse(ifs, std::move(fname), std::move(s)); } template basic_value parse(std::string fname, spec s = spec::default_version()) { std::ifstream ifs(fname, std::ios_base::binary); if(!ifs.good()) { throw file_io_error("toml::parse: error opening file", fname); } ifs.exceptions(std::ifstream::failbit | std::ifstream::badbit); return parse(ifs, std::move(fname), std::move(s)); } template result, std::vector> try_parse(const char (&fname)[N], spec s = spec::default_version()) { return try_parse(std::string(fname), std::move(s)); } template basic_value parse(const char (&fname)[N], spec s = spec::default_version()) { return parse(std::string(fname), std::move(s)); } // ---------------------------------------------------------------------------- // parse_str template result, std::vector> try_parse_str(std::string content, spec s = spec::default_version(), cxx::source_location loc = cxx::source_location::current()) { std::istringstream iss(std::move(content)); std::string name("internal string" + cxx::to_string(loc)); return try_parse(iss, std::move(name), std::move(s)); } template basic_value parse_str(std::string content, spec s = spec::default_version(), cxx::source_location loc = cxx::source_location::current()) { auto res = try_parse_str(std::move(content), std::move(s), std::move(loc)); if(res.is_ok()) { return res.unwrap(); } else { std::string msg; for(const auto& err : res.unwrap_err()) { msg += format_error(err); } throw syntax_error(std::move(msg), std::move(res.unwrap_err())); } } // ---------------------------------------------------------------------------- // filesystem #if defined(TOML11_HAS_FILESYSTEM) template cxx::enable_if_t::value, result, std::vector>> try_parse(const FSPATH& fpath, spec s = spec::default_version()) { std::ifstream ifs(fpath, std::ios_base::binary); if(!ifs.good()) { std::vector e; e.push_back(error_info("toml::parse: Error opening file \"" + fpath.string() + "\"", {})); return err(std::move(e)); } ifs.exceptions(std::ifstream::failbit | std::ifstream::badbit); return try_parse(ifs, fpath.string(), std::move(s)); } template cxx::enable_if_t::value, basic_value> parse(const FSPATH& fpath, spec s = spec::default_version()) { std::ifstream ifs(fpath, std::ios_base::binary); if(!ifs.good()) { throw file_io_error("toml::parse: error opening file", fpath.string()); } ifs.exceptions(std::ifstream::failbit | std::ifstream::badbit); return parse(ifs, fpath.string(), std::move(s)); } #endif // ----------------------------------------------------------------------------- // FILE* template result, std::vector> try_parse(FILE* fp, std::string filename, spec s = spec::default_version()) { const long beg = std::ftell(fp); if (beg == -1L) { return err(std::vector{error_info( std::string("Failed to access: \"") + filename + "\", errno = " + std::to_string(errno), {} )}); } const int res_seekend = std::fseek(fp, 0, SEEK_END); if (res_seekend != 0) { return err(std::vector{error_info( std::string("Failed to seek: \"") + filename + "\", errno = " + std::to_string(errno), {} )}); } const long end = std::ftell(fp); if (end == -1L) { return err(std::vector{error_info( std::string("Failed to access: \"") + filename + "\", errno = " + std::to_string(errno), {} )}); } const auto fsize = end - beg; const auto res_seekbeg = std::fseek(fp, beg, SEEK_SET); if (res_seekbeg != 0) { return err(std::vector{error_info( std::string("Failed to seek: \"") + filename + "\", errno = " + std::to_string(errno), {} )}); } // read whole file as a sequence of char assert(fsize >= 0); std::vector letters(static_cast(fsize)); const auto actual = std::fread(letters.data(), sizeof(char), static_cast(fsize), fp); if(actual != static_cast(fsize)) { return err(std::vector{error_info( std::string("File size changed: \"") + filename + std::string("\" make sure that FILE* is in binary mode " "to avoid LF <-> CRLF conversion"), {} )}); } return detail::parse_impl(std::move(letters), std::move(filename), std::move(s)); } template basic_value parse(FILE* fp, std::string filename, spec s = spec::default_version()) { const long beg = std::ftell(fp); if (beg == -1L) { throw file_io_error(errno, "Failed to access", filename); } const int res_seekend = std::fseek(fp, 0, SEEK_END); if (res_seekend != 0) { throw file_io_error(errno, "Failed to seek", filename); } const long end = std::ftell(fp); if (end == -1L) { throw file_io_error(errno, "Failed to access", filename); } const auto fsize = end - beg; const auto res_seekbeg = std::fseek(fp, beg, SEEK_SET); if (res_seekbeg != 0) { throw file_io_error(errno, "Failed to seek", filename); } // read whole file as a sequence of char assert(fsize >= 0); std::vector letters(static_cast(fsize)); const auto actual = std::fread(letters.data(), sizeof(char), static_cast(fsize), fp); if(actual != static_cast(fsize)) { throw file_io_error(errno, "File size changed; make sure that " "FILE* is in binary mode to avoid LF <-> CRLF conversion", filename); } auto res = detail::parse_impl(std::move(letters), std::move(filename), std::move(s)); if(res.is_ok()) { return res.unwrap(); } else { std::string msg; for(const auto& err : res.unwrap_err()) { msg += format_error(err); } throw syntax_error(std::move(msg), std::move(res.unwrap_err())); } } } // namespace toml #if defined(TOML11_COMPILE_SOURCES) namespace toml { struct type_config; struct ordered_type_config; extern template result, std::vector> try_parse(std::vector, std::string, spec); extern template result, std::vector> try_parse(std::istream&, std::string, spec); extern template result, std::vector> try_parse(std::string, spec); extern template result, std::vector> try_parse(FILE*, std::string, spec); extern template result, std::vector> try_parse_str(std::string, spec, cxx::source_location); extern template basic_value parse(std::vector, std::string, spec); extern template basic_value parse(std::istream&, std::string, spec); extern template basic_value parse(std::string, spec); extern template basic_value parse(FILE*, std::string, spec); extern template basic_value parse_str(std::string, spec, cxx::source_location); extern template result, std::vector> try_parse(std::vector, std::string, spec); extern template result, std::vector> try_parse(std::istream&, std::string, spec); extern template result, std::vector> try_parse(std::string, spec); extern template result, std::vector> try_parse(FILE*, std::string, spec); extern template result, std::vector> try_parse_str(std::string, spec, cxx::source_location); extern template basic_value parse(std::vector, std::string, spec); extern template basic_value parse(std::istream&, std::string, spec); extern template basic_value parse(std::string, spec); extern template basic_value parse(FILE*, std::string, spec); extern template basic_value parse_str(std::string, spec, cxx::source_location); #if defined(TOML11_HAS_FILESYSTEM) extern template cxx::enable_if_t::value, result, std::vector>> try_parse(const std::filesystem::path&, spec); extern template cxx::enable_if_t::value, result, std::vector>> try_parse(const std::filesystem::path&, spec); extern template cxx::enable_if_t::value, basic_value > parse (const std::filesystem::path&, spec); extern template cxx::enable_if_t::value, basic_value > parse (const std::filesystem::path&, spec); #endif // filesystem } // toml #endif // TOML11_COMPILE_SOURCES #endif // TOML11_PARSER_HPP toml11-4.1.0/include/toml11/region.hpp000066400000000000000000000003541464712047700174030ustar00rootroot00000000000000#ifndef TOML11_REGION_HPP #define TOML11_REGION_HPP #include "fwd/region_fwd.hpp" // IWYU pragma: export #if ! defined(TOML11_COMPILE_SOURCES) #include "impl/region_impl.hpp" // IWYU pragma: export #endif #endif // TOML11_REGION_HPP toml11-4.1.0/include/toml11/result.hpp000066400000000000000000000335221464712047700174410ustar00rootroot00000000000000#ifndef TOML11_RESULT_HPP #define TOML11_RESULT_HPP #include "compat.hpp" #include "exception.hpp" #include #include #include #include #include namespace toml { struct bad_result_access final : public ::toml::exception { public: explicit bad_result_access(std::string what_arg) : what_(std::move(what_arg)) {} ~bad_result_access() noexcept override = default; const char* what() const noexcept override {return what_.c_str();} private: std::string what_; }; // ----------------------------------------------------------------------------- template struct success { static_assert( ! std::is_same::value, ""); using value_type = T; explicit success(value_type v) noexcept(std::is_nothrow_move_constructible::value) : value(std::move(v)) {} template, T>::value, std::nullptr_t> = nullptr> explicit success(U&& v): value(std::forward(v)) {} template explicit success(success v): value(std::move(v.value)) {} ~success() = default; success(const success&) = default; success(success&&) = default; success& operator=(const success&) = default; success& operator=(success&&) = default; value_type& get() noexcept {return value;} value_type const& get() const noexcept {return value;} private: value_type value; }; template struct success> { static_assert( ! std::is_same::value, ""); using value_type = T; explicit success(std::reference_wrapper v) noexcept : value(std::move(v)) {} ~success() = default; success(const success&) = default; success(success&&) = default; success& operator=(const success&) = default; success& operator=(success&&) = default; value_type& get() noexcept {return value.get();} value_type const& get() const noexcept {return value.get();} private: std::reference_wrapper value; }; template success::type> ok(T&& v) { return success::type>(std::forward(v)); } template success ok(const char (&literal)[N]) { return success(std::string(literal)); } // ----------------------------------------------------------------------------- template struct failure { using value_type = T; explicit failure(value_type v) noexcept(std::is_nothrow_move_constructible::value) : value(std::move(v)) {} template, T>::value, std::nullptr_t> = nullptr> explicit failure(U&& v): value(std::forward(v)) {} template explicit failure(failure v): value(std::move(v.value)) {} ~failure() = default; failure(const failure&) = default; failure(failure&&) = default; failure& operator=(const failure&) = default; failure& operator=(failure&&) = default; value_type& get() noexcept {return value;} value_type const& get() const noexcept {return value;} private: value_type value; }; template struct failure> { using value_type = T; explicit failure(std::reference_wrapper v) noexcept : value(std::move(v)) {} ~failure() = default; failure(const failure&) = default; failure(failure&&) = default; failure& operator=(const failure&) = default; failure& operator=(failure&&) = default; value_type& get() noexcept {return value.get();} value_type const& get() const noexcept {return value.get();} private: std::reference_wrapper value; }; template failure::type> err(T&& v) { return failure::type>(std::forward(v)); } template failure err(const char (&literal)[N]) { return failure(std::string(literal)); } /* ============================================================================ * _ _ * _ _ ___ ____ _| | |_ * | '_/ -_|_-< || | | _| * |_| \___/__/\_,_|_|\__| */ template struct result { using success_type = success; using failure_type = failure; using value_type = typename success_type::value_type; using error_type = typename failure_type::value_type; result(success_type s): is_ok_(true), succ_(std::move(s)) {} result(failure_type f): is_ok_(false), fail_(std::move(f)) {} template, value_type>>, std::is_convertible, value_type> >::value, std::nullptr_t> = nullptr> result(success s): is_ok_(true), succ_(std::move(s.value)) {} template, error_type>>, std::is_convertible, error_type> >::value, std::nullptr_t> = nullptr> result(failure f): is_ok_(false), fail_(std::move(f.value)) {} result& operator=(success_type s) { this->cleanup(); this->is_ok_ = true; auto tmp = ::new(std::addressof(this->succ_)) success_type(std::move(s)); assert(tmp == std::addressof(this->succ_)); (void)tmp; return *this; } result& operator=(failure_type f) { this->cleanup(); this->is_ok_ = false; auto tmp = ::new(std::addressof(this->fail_)) failure_type(std::move(f)); assert(tmp == std::addressof(this->fail_)); (void)tmp; return *this; } template result& operator=(success s) { this->cleanup(); this->is_ok_ = true; auto tmp = ::new(std::addressof(this->succ_)) success_type(std::move(s.value)); assert(tmp == std::addressof(this->succ_)); (void)tmp; return *this; } template result& operator=(failure f) { this->cleanup(); this->is_ok_ = false; auto tmp = ::new(std::addressof(this->fail_)) failure_type(std::move(f.value)); assert(tmp == std::addressof(this->fail_)); (void)tmp; return *this; } ~result() noexcept {this->cleanup();} result(const result& other): is_ok_(other.is_ok()) { if(other.is_ok()) { auto tmp = ::new(std::addressof(this->succ_)) success_type(other.succ_); assert(tmp == std::addressof(this->succ_)); (void)tmp; } else { auto tmp = ::new(std::addressof(this->fail_)) failure_type(other.fail_); assert(tmp == std::addressof(this->fail_)); (void)tmp; } } result(result&& other): is_ok_(other.is_ok()) { if(other.is_ok()) { auto tmp = ::new(std::addressof(this->succ_)) success_type(std::move(other.succ_)); assert(tmp == std::addressof(this->succ_)); (void)tmp; } else { auto tmp = ::new(std::addressof(this->fail_)) failure_type(std::move(other.fail_)); assert(tmp == std::addressof(this->fail_)); (void)tmp; } } result& operator=(const result& other) { this->cleanup(); if(other.is_ok()) { auto tmp = ::new(std::addressof(this->succ_)) success_type(other.succ_); assert(tmp == std::addressof(this->succ_)); (void)tmp; } else { auto tmp = ::new(std::addressof(this->fail_)) failure_type(other.fail_); assert(tmp == std::addressof(this->fail_)); (void)tmp; } is_ok_ = other.is_ok(); return *this; } result& operator=(result&& other) { this->cleanup(); if(other.is_ok()) { auto tmp = ::new(std::addressof(this->succ_)) success_type(std::move(other.succ_)); assert(tmp == std::addressof(this->succ_)); (void)tmp; } else { auto tmp = ::new(std::addressof(this->fail_)) failure_type(std::move(other.fail_)); assert(tmp == std::addressof(this->fail_)); (void)tmp; } is_ok_ = other.is_ok(); return *this; } template, value_type>>, cxx::negation, error_type>>, std::is_convertible, value_type>, std::is_convertible, error_type> >::value, std::nullptr_t> = nullptr> result(result other): is_ok_(other.is_ok()) { if(other.is_ok()) { auto tmp = ::new(std::addressof(this->succ_)) success_type(std::move(other.as_ok())); assert(tmp == std::addressof(this->succ_)); (void)tmp; } else { auto tmp = ::new(std::addressof(this->fail_)) failure_type(std::move(other.as_err())); assert(tmp == std::addressof(this->fail_)); (void)tmp; } } template, value_type>>, cxx::negation, error_type>>, std::is_convertible, value_type>, std::is_convertible, error_type> >::value, std::nullptr_t> = nullptr> result& operator=(result other) { this->cleanup(); if(other.is_ok()) { auto tmp = ::new(std::addressof(this->succ_)) success_type(std::move(other.as_ok())); assert(tmp == std::addressof(this->succ_)); (void)tmp; } else { auto tmp = ::new(std::addressof(this->fail_)) failure_type(std::move(other.as_err())); assert(tmp == std::addressof(this->fail_)); (void)tmp; } is_ok_ = other.is_ok(); return *this; } bool is_ok() const noexcept {return is_ok_;} bool is_err() const noexcept {return !is_ok_;} explicit operator bool() const noexcept {return is_ok_;} value_type& unwrap(cxx::source_location loc = cxx::source_location::current()) { if(this->is_err()) { throw bad_result_access("toml::result: bad unwrap" + cxx::to_string(loc)); } return this->succ_.get(); } value_type const& unwrap(cxx::source_location loc = cxx::source_location::current()) const { if(this->is_err()) { throw bad_result_access("toml::result: bad unwrap" + cxx::to_string(loc)); } return this->succ_.get(); } value_type& unwrap_or(value_type& opt) noexcept { if(this->is_err()) {return opt;} return this->succ_.get(); } value_type const& unwrap_or(value_type const& opt) const noexcept { if(this->is_err()) {return opt;} return this->succ_.get(); } error_type& unwrap_err(cxx::source_location loc = cxx::source_location::current()) { if(this->is_ok()) { throw bad_result_access("toml::result: bad unwrap_err" + cxx::to_string(loc)); } return this->fail_.get(); } error_type const& unwrap_err(cxx::source_location loc = cxx::source_location::current()) const { if(this->is_ok()) { throw bad_result_access("toml::result: bad unwrap_err" + cxx::to_string(loc)); } return this->fail_.get(); } value_type& as_ok() noexcept { assert(this->is_ok()); return this->succ_.get(); } value_type const& as_ok() const noexcept { assert(this->is_ok()); return this->succ_.get(); } error_type& as_err() noexcept { assert(this->is_err()); return this->fail_.get(); } error_type const& as_err() const noexcept { assert(this->is_err()); return this->fail_.get(); } private: void cleanup() noexcept { #if defined(__GNUC__) && ! defined(__clang__) #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wduplicated-branches" #endif if(this->is_ok_) {this->succ_.~success_type();} else {this->fail_.~failure_type();} #if defined(__GNUC__) && ! defined(__clang__) #pragma GCC diagnostic pop #endif return; } private: bool is_ok_; union { success_type succ_; failure_type fail_; }; }; // ---------------------------------------------------------------------------- namespace detail { struct none_t {}; inline bool operator==(const none_t&, const none_t&) noexcept {return true;} inline bool operator!=(const none_t&, const none_t&) noexcept {return false;} inline bool operator< (const none_t&, const none_t&) noexcept {return false;} inline bool operator<=(const none_t&, const none_t&) noexcept {return true;} inline bool operator> (const none_t&, const none_t&) noexcept {return false;} inline bool operator>=(const none_t&, const none_t&) noexcept {return true;} inline std::ostream& operator<<(std::ostream& os, const none_t&) { os << "none"; return os; } } // detail inline success ok() noexcept { return success(detail::none_t{}); } inline failure err() noexcept { return failure(detail::none_t{}); } } // toml #endif // TOML11_RESULT_HPP toml11-4.1.0/include/toml11/scanner.hpp000066400000000000000000000003611464712047700175470ustar00rootroot00000000000000#ifndef TOML11_SCANNER_HPP #define TOML11_SCANNER_HPP #include "fwd/scanner_fwd.hpp" // IWYU pragma: export #if ! defined(TOML11_COMPILE_SOURCES) #include "impl/scanner_impl.hpp" // IWYU pragma: export #endif #endif // TOML11_SCANNER_HPP toml11-4.1.0/include/toml11/serializer.hpp000066400000000000000000001330351464712047700202740ustar00rootroot00000000000000#ifndef TOML11_SERIALIZER_HPP #define TOML11_SERIALIZER_HPP #include "comments.hpp" #include "error_info.hpp" #include "exception.hpp" #include "source_location.hpp" #include "spec.hpp" #include "syntax.hpp" #include "types.hpp" #include "utility.hpp" #include "value.hpp" #include #include #include #include #include namespace toml { struct serialization_error final : public ::toml::exception { public: explicit serialization_error(std::string what_arg, source_location loc) : what_(std::move(what_arg)), loc_(std::move(loc)) {} ~serialization_error() noexcept override = default; const char* what() const noexcept override {return what_.c_str();} source_location const& location() const noexcept {return loc_;} private: std::string what_; source_location loc_; }; namespace detail { template class serializer { public: using value_type = basic_value; using key_type = typename value_type::key_type ; using comment_type = typename value_type::comment_type ; using boolean_type = typename value_type::boolean_type ; using integer_type = typename value_type::integer_type ; using floating_type = typename value_type::floating_type ; using string_type = typename value_type::string_type ; using local_time_type = typename value_type::local_time_type ; using local_date_type = typename value_type::local_date_type ; using local_datetime_type = typename value_type::local_datetime_type ; using offset_datetime_type = typename value_type::offset_datetime_type; using array_type = typename value_type::array_type ; using table_type = typename value_type::table_type ; using char_type = typename string_type::value_type; public: explicit serializer(const spec& sp) : spec_(sp), force_inline_(false), current_indent_(0) {} string_type operator()(const std::vector& ks, const value_type& v) { for(const auto& k : ks) { this->keys_.push_back(k); } return (*this)(v); } string_type operator()(const key_type& k, const value_type& v) { this->keys_.push_back(k); return (*this)(v); } string_type operator()(const value_type& v) { switch(v.type()) { case value_t::boolean : {return (*this)(v.as_boolean (), v.as_boolean_fmt (), v.location());} case value_t::integer : {return (*this)(v.as_integer (), v.as_integer_fmt (), v.location());} case value_t::floating : {return (*this)(v.as_floating (), v.as_floating_fmt (), v.location());} case value_t::string : {return (*this)(v.as_string (), v.as_string_fmt (), v.location());} case value_t::offset_datetime: {return (*this)(v.as_offset_datetime(), v.as_offset_datetime_fmt(), v.location());} case value_t::local_datetime : {return (*this)(v.as_local_datetime (), v.as_local_datetime_fmt (), v.location());} case value_t::local_date : {return (*this)(v.as_local_date (), v.as_local_date_fmt (), v.location());} case value_t::local_time : {return (*this)(v.as_local_time (), v.as_local_time_fmt (), v.location());} case value_t::array : { return (*this)(v.as_array(), v.as_array_fmt(), v.comments(), v.location()); } case value_t::table : { string_type retval; if(this->keys_.empty()) // it might be the root table. emit comments here. { retval += format_comments(v.comments(), v.as_table_fmt().indent_type); } if( ! retval.empty()) // we have comment. { retval += char_type('\n'); } retval += (*this)(v.as_table(), v.as_table_fmt(), v.comments(), v.location()); return retval; } case value_t::empty: { if(this->spec_.ext_null_value) { return string_conv("null"); } break; } default: { break; } } throw serialization_error(format_error( "[error] toml::serializer: toml::basic_value " "does not have any valid type.", v.location(), "here"), v.location()); } private: string_type operator()(const boolean_type& b, const boolean_format_info&, const source_location&) // {{{ { if(b) { return string_conv("true"); } else { return string_conv("false"); } } // }}} string_type operator()(const integer_type i, const integer_format_info& fmt, const source_location& loc) // {{{ { std::ostringstream oss; this->set_locale(oss); const auto insert_spacer = [&fmt](std::string s) -> std::string { if(fmt.spacer == 0) {return s;} std::string sign; if( ! s.empty() && (s.at(0) == '+' || s.at(0) == '-')) { sign += s.at(0); s.erase(s.begin()); } std::string spaced; std::size_t counter = 0; for(auto iter = s.rbegin(); iter != s.rend(); ++iter) { if(counter != 0 && counter % fmt.spacer == 0) { spaced += '_'; } spaced += *iter; counter += 1; } if(!spaced.empty() && spaced.back() == '_') {spaced.pop_back();} s.clear(); std::copy(spaced.rbegin(), spaced.rend(), std::back_inserter(s)); return sign + s; }; std::string retval; if(fmt.fmt == integer_format::dec) { oss << std::setw(static_cast(fmt.width)) << std::dec << i; retval = insert_spacer(oss.str()); if(this->spec_.ext_num_suffix && ! fmt.suffix.empty()) { retval += '_'; retval += fmt.suffix; } } else { if(i < 0) { throw serialization_error(format_error("binary, octal, hexadecimal " "integer does not allow negative value", loc, "here"), loc); } switch(fmt.fmt) { case integer_format::hex: { oss << std::noshowbase << std::setw(static_cast(fmt.width)) << std::setfill('0') << std::hex; if(fmt.uppercase) { oss << std::uppercase; } else { oss << std::nouppercase; } oss << i; retval = std::string("0x") + insert_spacer(oss.str()); break; } case integer_format::oct: { oss << std::setw(static_cast(fmt.width)) << std::setfill('0') << std::oct << i; retval = std::string("0o") + insert_spacer(oss.str()); break; } case integer_format::bin: { integer_type x{i}; std::string tmp; std::size_t bits(0); while(x != 0) { if(fmt.spacer != 0) { if(bits != 0 && (bits % fmt.spacer) == 0) {tmp += '_';} } if(x % 2 == 1) { tmp += '1'; } else { tmp += '0'; } x >>= 1; bits += 1; } for(; bits < fmt.width; ++bits) { if(fmt.spacer != 0) { if(bits != 0 && (bits % fmt.spacer) == 0) {tmp += '_';} } tmp += '0'; } for(auto iter = tmp.rbegin(); iter != tmp.rend(); ++iter) { oss << *iter; } retval = std::string("0b") + oss.str(); break; } default: { throw serialization_error(format_error( "none of dec, hex, oct, bin: " + to_string(fmt.fmt), loc, "here"), loc); } } } return string_conv(retval); } // }}} string_type operator()(const floating_type f, const floating_format_info& fmt, const source_location&) // {{{ { using std::isnan; using std::isinf; using std::signbit; std::ostringstream oss; this->set_locale(oss); if(isnan(f)) { if(signbit(f)) { oss << '-'; } oss << "nan"; if(this->spec_.ext_num_suffix && ! fmt.suffix.empty()) { oss << '_'; oss << fmt.suffix; } return string_conv(oss.str()); } if(isinf(f)) { if(signbit(f)) { oss << '-'; } oss << "inf"; if(this->spec_.ext_num_suffix && ! fmt.suffix.empty()) { oss << '_'; oss << fmt.suffix; } return string_conv(oss.str()); } switch(fmt.fmt) { case floating_format::defaultfloat: { if(fmt.prec != 0) { oss << std::setprecision(static_cast(fmt.prec)); } oss << f; // since defaultfloat may omit point, we need to add it std::string s = oss.str(); if (s.find('.') == std::string::npos && s.find('e') == std::string::npos && s.find('E') == std::string::npos ) { s += ".0"; } if(this->spec_.ext_num_suffix && ! fmt.suffix.empty()) { s += '_'; s += fmt.suffix; } return string_conv(s); } case floating_format::fixed: { if(fmt.prec != 0) { oss << std::setprecision(static_cast(fmt.prec)); } oss << std::fixed << f; if(this->spec_.ext_num_suffix && ! fmt.suffix.empty()) { oss << '_' << fmt.suffix; } return string_conv(oss.str()); } case floating_format::scientific: { if(fmt.prec != 0) { oss << std::setprecision(static_cast(fmt.prec)); } oss << std::scientific << f; if(this->spec_.ext_num_suffix && ! fmt.suffix.empty()) { oss << '_' << fmt.suffix; } return string_conv(oss.str()); } case floating_format::hex: { if(this->spec_.ext_hex_float) { oss << std::hexfloat << f; // suffix is only for decimal numbers. return string_conv(oss.str()); } else // no hex allowed. output with max precision. { oss << std::setprecision(std::numeric_limits::max_digits10) << std::scientific << f; // suffix is only for decimal numbers. return string_conv(oss.str()); } } default: { if(this->spec_.ext_num_suffix && ! fmt.suffix.empty()) { oss << '_' << fmt.suffix; } return string_conv(oss.str()); } } } // }}} string_type operator()(string_type s, const string_format_info& fmt, const source_location& loc) // {{{ { string_type retval; switch(fmt.fmt) { case string_format::basic: { retval += char_type('"'); retval += this->escape_basic_string(s); retval += char_type('"'); return retval; } case string_format::literal: { if(std::find(s.begin(), s.end(), char_type('\n')) != s.end()) { throw serialization_error(format_error("toml::serializer: " "(non-multiline) literal string cannot have a newline", loc, "here"), loc); } retval += char_type('\''); retval += s; retval += char_type('\''); return retval; } case string_format::multiline_basic: { retval += string_conv("\"\"\""); if(fmt.start_with_newline) { retval += char_type('\n'); } retval += this->escape_ml_basic_string(s); retval += string_conv("\"\"\""); return retval; } case string_format::multiline_literal: { retval += string_conv("'''"); if(fmt.start_with_newline) { retval += char_type('\n'); } retval += s; retval += string_conv("'''"); return retval; } default: { throw serialization_error(format_error( "[error] toml::serializer::operator()(string): " "invalid string_format value", loc, "here"), loc); } } } // }}} string_type operator()(const local_date_type& d, const local_date_format_info&, const source_location&) // {{{ { std::ostringstream oss; oss << d; return string_conv(oss.str()); } // }}} string_type operator()(const local_time_type& t, const local_time_format_info& fmt, const source_location&) // {{{ { return this->format_local_time(t, fmt.has_seconds, fmt.subsecond_precision); } // }}} string_type operator()(const local_datetime_type& dt, const local_datetime_format_info& fmt, const source_location&) // {{{ { std::ostringstream oss; oss << dt.date; switch(fmt.delimiter) { case datetime_delimiter_kind::upper_T: { oss << 'T'; break; } case datetime_delimiter_kind::lower_t: { oss << 't'; break; } case datetime_delimiter_kind::space: { oss << ' '; break; } default: { oss << 'T'; break; } } return string_conv(oss.str()) + this->format_local_time(dt.time, fmt.has_seconds, fmt.subsecond_precision); } // }}} string_type operator()(const offset_datetime_type& odt, const offset_datetime_format_info& fmt, const source_location&) // {{{ { std::ostringstream oss; oss << odt.date; switch(fmt.delimiter) { case datetime_delimiter_kind::upper_T: { oss << 'T'; break; } case datetime_delimiter_kind::lower_t: { oss << 't'; break; } case datetime_delimiter_kind::space: { oss << ' '; break; } default: { oss << 'T'; break; } } oss << string_conv(this->format_local_time(odt.time, fmt.has_seconds, fmt.subsecond_precision)); oss << odt.offset; return string_conv(oss.str()); } // }}} string_type operator()(const array_type& a, const array_format_info& fmt, const comment_type& com, const source_location& loc) // {{{ { array_format f = fmt.fmt; if(fmt.fmt == array_format::default_format) { // [[in.this.form]], you cannot add a comment to the array itself // (but you can add a comment to each table). // To keep comments, we need to avoid multiline array-of-tables // if array itself has a comment. if( ! this->keys_.empty() && ! a.empty() && com.empty() && std::all_of(a.begin(), a.end(), [](const value_type& e) {return e.is_table();})) { f = array_format::array_of_tables; } else { f = array_format::oneline; // check if it becomes long std::size_t approx_len = 0; for(const auto& e : a) { // have a comment. cannot be inlined if( ! e.comments().empty()) { f = array_format::multiline; break; } // possibly long types ... if(e.is_array() || e.is_table() || e.is_offset_datetime() || e.is_local_datetime()) { f = array_format::multiline; break; } else if(e.is_boolean()) { approx_len += (*this)(e.as_boolean(), e.as_boolean_fmt(), e.location()).size(); } else if(e.is_integer()) { approx_len += (*this)(e.as_integer(), e.as_integer_fmt(), e.location()).size(); } else if(e.is_floating()) { approx_len += (*this)(e.as_floating(), e.as_floating_fmt(), e.location()).size(); } else if(e.is_string()) { if(e.as_string_fmt().fmt == string_format::multiline_basic || e.as_string_fmt().fmt == string_format::multiline_literal) { f = array_format::multiline; break; } approx_len += 2 + (*this)(e.as_string(), e.as_string_fmt(), e.location()).size(); } else if(e.is_local_date()) { approx_len += 10; // 1234-56-78 } else if(e.is_local_time()) { approx_len += 15; // 12:34:56.789012 } if(approx_len > 60) // key, ` = `, `[...]` < 80 { f = array_format::multiline; break; } approx_len += 2; // `, ` } } } if(this->force_inline_ && f == array_format::array_of_tables) { f = array_format::multiline; } if(f == array_format::array_of_tables) { if(this->keys_.empty()) { throw serialization_error("array of table must have its key. " "use format(key, v)", loc); } string_type retval; for(const auto& e : a) { assert(e.is_table()); this->current_indent_ += e.as_table_fmt().name_indent; retval += this->format_comments(e.comments(), e.as_table_fmt().indent_type); retval += this->format_indent(e.as_table_fmt().indent_type); this->current_indent_ -= e.as_table_fmt().name_indent; retval += string_conv("[["); retval += this->format_keys(this->keys_).value(); retval += string_conv("]]\n"); retval += this->format_ml_table(e.as_table(), e.as_table_fmt()); } return retval; } else if(f == array_format::oneline) { // ignore comments. we cannot emit comments string_type retval; retval += char_type('['); for(const auto& e : a) { this->force_inline_ = true; retval += (*this)(e); retval += string_conv(", "); } if( ! a.empty()) { retval.pop_back(); // ` ` retval.pop_back(); // `,` } retval += char_type(']'); this->force_inline_ = false; return retval; } else { assert(f == array_format::multiline); string_type retval; retval += string_conv("[\n"); for(const auto& e : a) { this->current_indent_ += fmt.body_indent; retval += this->format_comments(e.comments(), fmt.indent_type); retval += this->format_indent(fmt.indent_type); this->current_indent_ -= fmt.body_indent; this->force_inline_ = true; retval += (*this)(e); retval += string_conv(",\n"); } this->force_inline_ = false; this->current_indent_ += fmt.closing_indent; retval += this->format_indent(fmt.indent_type); this->current_indent_ -= fmt.closing_indent; retval += char_type(']'); return retval; } } // }}} string_type operator()(const table_type& t, const table_format_info& fmt, const comment_type& com, const source_location& loc) // {{{ { if(this->force_inline_) { if(fmt.fmt == table_format::multiline_oneline) { return this->format_ml_inline_table(t, fmt); } else { return this->format_inline_table(t, fmt); } } else { if(fmt.fmt == table_format::multiline) { string_type retval; // comment is emitted inside format_ml_table if(auto k = this->format_keys(this->keys_)) { this->current_indent_ += fmt.name_indent; retval += this->format_comments(com, fmt.indent_type); retval += this->format_indent(fmt.indent_type); this->current_indent_ -= fmt.name_indent; retval += char_type('['); retval += k.value(); retval += string_conv("]\n"); } // otherwise, its the root. retval += this->format_ml_table(t, fmt); return retval; } else if(fmt.fmt == table_format::oneline) { return this->format_inline_table(t, fmt); } else if(fmt.fmt == table_format::multiline_oneline) { return this->format_ml_inline_table(t, fmt); } else if(fmt.fmt == table_format::dotted) { std::vector keys; if(this->keys_.empty()) { throw serialization_error(format_error("toml::serializer: " "dotted table must have its key. use format(key, v)", loc, "here"), loc); } keys.push_back(this->keys_.back()); const auto retval = this->format_dotted_table(t, fmt, loc, keys); keys.pop_back(); return retval; } else { assert(fmt.fmt == table_format::implicit); string_type retval; for(const auto& kv : t) { const auto& k = kv.first; const auto& v = kv.second; if( ! v.is_table() && ! v.is_array_of_tables()) { throw serialization_error(format_error("toml::serializer: " "an implicit table cannot have non-table value.", v.location(), "here"), v.location()); } if(v.is_table()) { if(v.as_table_fmt().fmt != table_format::multiline && v.as_table_fmt().fmt != table_format::implicit) { throw serialization_error(format_error("toml::serializer: " "an implicit table cannot have non-multiline table", v.location(), "here"), v.location()); } } else { assert(v.is_array()); for(const auto& e : v.as_array()) { if(e.as_table_fmt().fmt != table_format::multiline && v.as_table_fmt().fmt != table_format::implicit) { throw serialization_error(format_error("toml::serializer: " "an implicit table cannot have non-multiline table", e.location(), "here"), e.location()); } } } keys_.push_back(k); retval += (*this)(v); keys_.pop_back(); } return retval; } } } // }}} private: string_type escape_basic_string(const string_type& s) const // {{{ { string_type retval; for(const char_type c : s) { switch(c) { case char_type('\\'): {retval += string_conv("\\\\"); break;} case char_type('\"'): {retval += string_conv("\\\""); break;} case char_type('\b'): {retval += string_conv("\\b" ); break;} case char_type('\t'): {retval += string_conv("\\t" ); break;} case char_type('\f'): {retval += string_conv("\\f" ); break;} case char_type('\n'): {retval += string_conv("\\n" ); break;} case char_type('\r'): {retval += string_conv("\\r" ); break;} default : { if(c == char_type(0x1B) && spec_.v1_1_0_add_escape_sequence_e) { retval += string_conv("\\e"); } else if((char_type(0x00) <= c && c <= char_type(0x08)) || (char_type(0x0A) <= c && c <= char_type(0x1F)) || c == char_type(0x7F)) { if(spec_.v1_1_0_add_escape_sequence_x) { retval += string_conv("\\x"); } else { retval += string_conv("\\u00"); } const auto c1 = c / 16; const auto c2 = c % 16; retval += static_cast('0' + c1); if(c2 < 10) { retval += static_cast('0' + c2); } else // 10 <= c2 { retval += static_cast('A' + (c2 - 10)); } } else { retval += c; } } } } return retval; } // }}} string_type escape_ml_basic_string(const string_type& s) // {{{ { string_type retval; for(const char_type c : s) { switch(c) { case char_type('\\'): {retval += string_conv("\\\\"); break;} case char_type('\b'): {retval += string_conv("\\b" ); break;} case char_type('\t'): {retval += string_conv("\\t" ); break;} case char_type('\f'): {retval += string_conv("\\f" ); break;} case char_type('\n'): {retval += string_conv("\n" ); break;} case char_type('\r'): {retval += string_conv("\\r" ); break;} default : { if(c == char_type(0x1B) && spec_.v1_1_0_add_escape_sequence_e) { retval += string_conv("\\e"); } else if((char_type(0x00) <= c && c <= char_type(0x08)) || (char_type(0x0A) <= c && c <= char_type(0x1F)) || c == char_type(0x7F)) { if(spec_.v1_1_0_add_escape_sequence_x) { retval += string_conv("\\x"); } else { retval += string_conv("\\u00"); } const auto c1 = c / 16; const auto c2 = c % 16; retval += static_cast('0' + c1); if(c2 < 10) { retval += static_cast('0' + c2); } else // 10 <= c2 { retval += static_cast('A' + (c2 - 10)); } } else { retval += c; } } } } // Only 1 or 2 consecutive `"`s are allowed in multiline basic string. // 3 consecutive `"`s are considered as a closing delimiter. // We need to check if there are 3 or more consecutive `"`s and insert // backslash to break them down into several short `"`s like the `str6` // in the following example. // ```toml // str4 = """Here are two quotation marks: "". Simple enough.""" // # str5 = """Here are three quotation marks: """.""" # INVALID // str5 = """Here are three quotation marks: ""\".""" // str6 = """Here are fifteen quotation marks: ""\"""\"""\"""\"""\".""" // ``` auto found_3_quotes = retval.find(string_conv("\"\"\"")); while(found_3_quotes != string_type::npos) { retval.replace(found_3_quotes, 3, string_conv("\"\"\\\"")); found_3_quotes = retval.find(string_conv("\"\"\"")); } return retval; } // }}} string_type format_local_time(const local_time_type& t, const bool has_seconds, const std::size_t subsec_prec) // {{{ { std::ostringstream oss; oss << std::setfill('0') << std::setw(2) << static_cast(t.hour); oss << ':'; oss << std::setfill('0') << std::setw(2) << static_cast(t.minute); if(has_seconds) { oss << ':'; oss << std::setfill('0') << std::setw(2) << static_cast(t.second); if(subsec_prec != 0) { std::ostringstream subsec; subsec << std::setfill('0') << std::setw(3) << static_cast(t.millisecond); subsec << std::setfill('0') << std::setw(3) << static_cast(t.microsecond); subsec << std::setfill('0') << std::setw(3) << static_cast(t.nanosecond); std::string subsec_str = subsec.str(); oss << '.' << subsec_str.substr(0, subsec_prec); } } return string_conv(oss.str()); } // }}} string_type format_ml_table(const table_type& t, const table_format_info& fmt) // {{{ { const auto format_later = [](const value_type& v) -> bool { const bool is_ml_table = v.is_table() && v.as_table_fmt().fmt != table_format::oneline && v.as_table_fmt().fmt != table_format::multiline_oneline && v.as_table_fmt().fmt != table_format::dotted ; const bool is_ml_array_table = v.is_array_of_tables() && v.as_array_fmt().fmt != array_format::oneline && v.as_array_fmt().fmt != array_format::multiline; return is_ml_table || is_ml_array_table; }; string_type retval; this->current_indent_ += fmt.body_indent; for(const auto& kv : t) { const auto& key = kv.first; const auto& val = kv.second; if(format_later(val)) { continue; } this->keys_.push_back(key); retval += format_comments(val.comments(), fmt.indent_type); retval += format_indent(fmt.indent_type); if(val.is_table() && val.as_table_fmt().fmt == table_format::dotted) { retval += (*this)(val); } else { retval += format_key(key); retval += string_conv(" = "); retval += (*this)(val); retval += char_type('\n'); } this->keys_.pop_back(); } this->current_indent_ -= fmt.body_indent; if( ! retval.empty()) { retval += char_type('\n'); // for readability, add empty line between tables } for(const auto& kv : t) { if( ! format_later(kv.second)) { continue; } // must be a [multiline.table] or [[multiline.array.of.tables]]. // comments will be generated inside it. this->keys_.push_back(kv.first); retval += (*this)(kv.second); this->keys_.pop_back(); } return retval; } // }}} string_type format_inline_table(const table_type& t, const table_format_info&) // {{{ { // comments are ignored because we cannot write without newline string_type retval; retval += char_type('{'); for(const auto& kv : t) { this->force_inline_ = true; retval += this->format_key(kv.first); retval += string_conv(" = "); retval += (*this)(kv.second); retval += string_conv(", "); } if( ! t.empty()) { retval.pop_back(); // ' ' retval.pop_back(); // ',' } retval += char_type('}'); this->force_inline_ = false; return retval; } // }}} string_type format_ml_inline_table(const table_type& t, const table_format_info& fmt) // {{{ { string_type retval; retval += string_conv("{\n"); this->current_indent_ += fmt.body_indent; for(const auto& kv : t) { this->force_inline_ = true; retval += format_comments(kv.second.comments(), fmt.indent_type); retval += format_indent(fmt.indent_type); retval += kv.first; retval += string_conv(" = "); this->force_inline_ = true; retval += (*this)(kv.second); retval += string_conv(",\n"); } if( ! t.empty()) { retval.pop_back(); // '\n' retval.pop_back(); // ',' } this->current_indent_ -= fmt.body_indent; this->force_inline_ = false; this->current_indent_ += fmt.closing_indent; retval += format_indent(fmt.indent_type); this->current_indent_ -= fmt.closing_indent; retval += char_type('}'); return retval; } // }}} string_type format_dotted_table(const table_type& t, const table_format_info& fmt, // {{{ const source_location&, std::vector& keys) { // lets say we have: `{"a": {"b": {"c": {"d": "foo", "e": "bar"} } }` // and `a` and `b` are `dotted`. // // - in case if `c` is `oneline`: // ```toml // a.b.c = {d = "foo", e = "bar"} // ``` // // - in case if and `c` is `dotted`: // ```toml // a.b.c.d = "foo" // a.b.c.e = "bar" // ``` string_type retval; for(const auto& kv : t) { const auto& key = kv.first; const auto& val = kv.second; keys.push_back(key); // format recursive dotted table? if (val.is_table() && val.as_table_fmt().fmt != table_format::oneline && val.as_table_fmt().fmt != table_format::multiline_oneline) { retval += this->format_dotted_table(val.as_table(), val.as_table_fmt(), val.location(), keys); } else // non-table or inline tables. format normally { retval += format_comments(val.comments(), fmt.indent_type); retval += format_indent(fmt.indent_type); retval += format_keys(keys).value(); retval += string_conv(" = "); this->force_inline_ = true; // sub-table must be inlined retval += (*this)(val); retval += char_type('\n'); this->force_inline_ = false; } keys.pop_back(); } return retval; } // }}} string_type format_key(const key_type& key) // {{{ { if(key.empty()) { return string_conv("\"\""); } // check the key can be a bare (unquoted) key auto loc = detail::make_temporary_location(string_conv(key)); auto reg = detail::syntax::unquoted_key(this->spec_).scan(loc); if(reg.is_ok() && loc.eof()) { return key; } //if it includes special characters, then format it in a "quoted" key. string_type formatted = string_conv("\""); for(const char_type c : key) { switch(c) { case char_type('\\'): {formatted += string_conv("\\\\"); break;} case char_type('\"'): {formatted += string_conv("\\\""); break;} case char_type('\b'): {formatted += string_conv("\\b" ); break;} case char_type('\t'): {formatted += string_conv("\\t" ); break;} case char_type('\f'): {formatted += string_conv("\\f" ); break;} case char_type('\n'): {formatted += string_conv("\\n" ); break;} case char_type('\r'): {formatted += string_conv("\\r" ); break;} default : { // ASCII ctrl char if( (char_type(0x00) <= c && c <= char_type(0x08)) || (char_type(0x0A) <= c && c <= char_type(0x1F)) || c == char_type(0x7F)) { if(spec_.v1_1_0_add_escape_sequence_x) { formatted += string_conv("\\x"); } else { formatted += string_conv("\\u00"); } const auto c1 = c / 16; const auto c2 = c % 16; formatted += static_cast('0' + c1); if(c2 < 10) { formatted += static_cast('0' + c2); } else // 10 <= c2 { formatted += static_cast('A' + (c2 - 10)); } } else { formatted += c; } break; } } } formatted += string_conv("\""); return formatted; } // }}} cxx::optional format_keys(const std::vector& keys) // {{{ { if(keys.empty()) { return cxx::make_nullopt(); } string_type formatted; for(const auto& ky : keys) { formatted += format_key(ky); formatted += char_type('.'); } formatted.pop_back(); // remove the last dot '.' return formatted; } // }}} string_type format_comments(const discard_comments&, const indent_char) const // {{{ { return string_conv(""); } // }}} string_type format_comments(const preserve_comments& comments, const indent_char indent_type) const // {{{ { string_type retval; for(const auto& c : comments) { if(c.empty()) {continue;} retval += format_indent(indent_type); if(c.front() != '#') {retval += char_type('#');} retval += string_conv(c); if(c.back() != '\n') {retval += char_type('\n');} } return retval; } // }}} string_type format_indent(const indent_char indent_type) const // {{{ { const auto indent = static_cast((std::max)(0, this->current_indent_)); if(indent_type == indent_char::space) { return string_conv(make_string(indent, ' ')); } else if(indent_type == indent_char::tab) { return string_conv(make_string(indent, '\t')); } else { return string_type{}; } } // }}} std::locale set_locale(std::ostream& os) const { return os.imbue(std::locale::classic()); } private: spec spec_; bool force_inline_; // table inside an array without fmt specification std::int32_t current_indent_; std::vector keys_; }; } // detail template typename basic_value::string_type format(const basic_value& v, const spec s = spec::default_version()) { detail::serializer ser(s); return ser(v); } template typename basic_value::string_type format(const typename basic_value::key_type& k, const basic_value& v, const spec s = spec::default_version()) { detail::serializer ser(s); return ser(k, v); } template typename basic_value::string_type format(const std::vector::key_type>& ks, const basic_value& v, const spec s = spec::default_version()) { detail::serializer ser(s); return ser(ks, v); } template std::ostream& operator<<(std::ostream& os, const basic_value& v) { os << format(v); return os; } } // toml #if defined(TOML11_COMPILE_SOURCES) namespace toml { struct type_config; struct ordered_type_config; extern template typename basic_value::string_type format(const basic_value&, const spec); extern template typename basic_value::string_type format(const typename basic_value::key_type& k, const basic_value& v, const spec); extern template typename basic_value::string_type format(const std::vector::key_type>& ks, const basic_value& v, const spec s); extern template typename basic_value::string_type format(const basic_value&, const spec); extern template typename basic_value::string_type format(const typename basic_value::key_type& k, const basic_value& v, const spec); extern template typename basic_value::string_type format(const std::vector::key_type>& ks, const basic_value& v, const spec s); namespace detail { extern template class serializer<::toml::type_config>; extern template class serializer<::toml::ordered_type_config>; } // detail } // toml #endif // TOML11_COMPILE_SOURCES #endif // TOML11_SERIALIZER_HPP toml11-4.1.0/include/toml11/skip.hpp000066400000000000000000000264571464712047700171020ustar00rootroot00000000000000#ifndef TOML11_SKIP_HPP #define TOML11_SKIP_HPP #include "context.hpp" #include "region.hpp" #include "scanner.hpp" #include "syntax.hpp" #include "types.hpp" #include namespace toml { namespace detail { template bool skip_whitespace(location& loc, const context& ctx) { return syntax::ws(ctx.toml_spec()).scan(loc).is_ok(); } template bool skip_empty_lines(location& loc, const context& ctx) { return repeat_at_least(1, sequence( syntax::ws(ctx.toml_spec()), syntax::newline(ctx.toml_spec()) )).scan(loc).is_ok(); } // For error recovery. // // In case if a comment line contains an invalid character, we need to skip it // to advance parsing. template void skip_comment_block(location& loc, const context& ctx) { while( ! loc.eof()) { skip_whitespace(loc, ctx); if(loc.current() == '#') { while( ! loc.eof()) { // both CRLF and LF ends with LF. if(loc.current() == '\n') { loc.advance(); break; } } } else if(syntax::newline(ctx.toml_spec()).scan(loc).is_ok()) { ; // an empty line. skip this also } else { // the next token is neither a comment nor empty line. return ; } } return ; } template void skip_empty_or_comment_lines(location& loc, const context& ctx) { const auto& spec = ctx.toml_spec(); repeat_at_least(0, sequence( syntax::ws(spec), maybe(syntax::comment(spec)), syntax::newline(spec)) ).scan(loc); return ; } // For error recovery. // // Sometimes we need to skip a value and find a delimiter, like `,`, `]`, or `}`. // To find delimiter, we need to skip delimiters in a string. // Since we are skipping invalid value while error recovery, we don't need // to check the syntax. Here we just skip string-like region until closing quote // is found. template void skip_string_like(location& loc, const context&) { // if """ is found, skip until the closing """ is found. if(literal("\"\"\"").scan(loc).is_ok()) { while( ! loc.eof()) { if(literal("\"\"\"").scan(loc).is_ok()) { return; } loc.advance(); } } else if(literal("'''").scan(loc).is_ok()) { while( ! loc.eof()) { if(literal("'''").scan(loc).is_ok()) { return; } loc.advance(); } } // if " is found, skip until the closing " or newline is found. else if(loc.current() == '"') { while( ! loc.eof()) { loc.advance(); if(loc.current() == '"' || loc.current() == '\n') { loc.advance(); return; } } } else if(loc.current() == '\'') { while( ! loc.eof()) { loc.advance(); if(loc.current() == '\'' || loc.current() == '\n') { loc.advance(); return ; } } } return; } template void skip_value(location& loc, const context& ctx); template void skip_array_like(location& loc, const context& ctx); template void skip_inline_table_like(location& loc, const context& ctx); template void skip_key_value_pair(location& loc, const context& ctx); template result guess_value_type(const location& loc, const context& ctx); template void skip_array_like(location& loc, const context& ctx) { const auto& spec = ctx.toml_spec(); assert(loc.current() == '['); loc.advance(); while( ! loc.eof()) { if(loc.current() == '\"' || loc.current() == '\'') { skip_string_like(loc, ctx); } else if(loc.current() == '#') { skip_comment_block(loc, ctx); } else if(loc.current() == '{') { skip_inline_table_like(loc, ctx); } else if(loc.current() == '[') { const auto checkpoint = loc; if(syntax::std_table(spec).scan(loc).is_ok() || syntax::array_table(spec).scan(loc).is_ok()) { loc = checkpoint; break; } // if it is not a table-definition, then it is an array. skip_array_like(loc, ctx); } else if(loc.current() == '=') { // key-value pair cannot be inside the array. // guessing the error is "missing closing bracket `]`". // find the previous key just before `=`. while(loc.get_location() != 0) { loc.retrace(); if(loc.current() == '\n') { loc.advance(); break; } } break; } else if(loc.current() == ']') { break; // found closing bracket } else { loc.advance(); } } return ; } template void skip_inline_table_like(location& loc, const context& ctx) { assert(loc.current() == '{'); loc.advance(); const auto& spec = ctx.toml_spec(); while( ! loc.eof()) { if(loc.current() == '\n' && ! spec.v1_1_0_allow_newlines_in_inline_tables) { break; // missing closing `}`. } else if(loc.current() == '\"' || loc.current() == '\'') { skip_string_like(loc, ctx); } else if(loc.current() == '#') { skip_comment_block(loc, ctx); if( ! spec.v1_1_0_allow_newlines_in_inline_tables) { // comment must end with newline. break; // missing closing `}`. } } else if(loc.current() == '[') { const auto checkpoint = loc; if(syntax::std_table(spec).scan(loc).is_ok() || syntax::array_table(spec).scan(loc).is_ok()) { loc = checkpoint; break; // missing closing `}`. } // if it is not a table-definition, then it is an array. skip_array_like(loc, ctx); } else if(loc.current() == '{') { skip_inline_table_like(loc, ctx); } else if(loc.current() == '}') { // closing brace found. guessing the error is inside the table. break; } else { // skip otherwise. loc.advance(); } } return ; } template void skip_value(location& loc, const context& ctx) { value_t ty = guess_value_type(loc, ctx).unwrap_or(value_t::empty); if(ty == value_t::string) { skip_string_like(loc, ctx); } else if(ty == value_t::array) { skip_array_like(loc, ctx); } else if(ty == value_t::table) { // In case of multiline tables, it may skip key-value pair but not the // whole table. skip_inline_table_like(loc, ctx); } else // others are an "in-line" values. skip until the next line { while( ! loc.eof()) { if(loc.current() == '\n') { break; } else if(loc.current() == ',' || loc.current() == ']' || loc.current() == '}') { break; } loc.advance(); } } return; } template void skip_key_value_pair(location& loc, const context& ctx) { while( ! loc.eof()) { if(loc.current() == '=') { skip_whitespace(loc, ctx); skip_value(loc, ctx); return; } else if(loc.current() == '\n') { // newline is found before finding `=`. assuming "missing `=`". return; } loc.advance(); } return ; } template void skip_until_next_table(location& loc, const context& ctx) { const auto& spec = ctx.toml_spec(); while( ! loc.eof()) { if(loc.current() == '\n') { loc.advance(); const auto line_begin = loc; skip_whitespace(loc, ctx); if(syntax::std_table(spec).scan(loc).is_ok()) { loc = line_begin; return ; } if(syntax::array_table(spec).scan(loc).is_ok()) { loc = line_begin; return ; } } loc.advance(); } } } // namespace detail } // namespace toml #if defined(TOML11_COMPILE_SOURCES) namespace toml { struct type_config; struct ordered_type_config; namespace detail { extern template bool skip_whitespace (location& loc, const context&); extern template bool skip_empty_lines (location& loc, const context&); extern template void skip_comment_block (location& loc, const context&); extern template void skip_empty_or_comment_lines(location& loc, const context&); extern template void skip_string_like (location& loc, const context&); extern template void skip_array_like (location& loc, const context&); extern template void skip_inline_table_like (location& loc, const context&); extern template void skip_value (location& loc, const context&); extern template void skip_key_value_pair (location& loc, const context&); extern template void skip_until_next_table (location& loc, const context&); extern template bool skip_whitespace (location& loc, const context&); extern template bool skip_empty_lines (location& loc, const context&); extern template void skip_comment_block (location& loc, const context&); extern template void skip_empty_or_comment_lines(location& loc, const context&); extern template void skip_string_like (location& loc, const context&); extern template void skip_array_like (location& loc, const context&); extern template void skip_inline_table_like (location& loc, const context&); extern template void skip_value (location& loc, const context&); extern template void skip_key_value_pair (location& loc, const context&); extern template void skip_until_next_table (location& loc, const context&); } // detail } // toml #endif // TOML11_COMPILE_SOURCES #endif // TOML11_SKIP_HPP toml11-4.1.0/include/toml11/source_location.hpp000066400000000000000000000004311464712047700213040ustar00rootroot00000000000000#ifndef TOML11_SOURCE_LOCATION_HPP #define TOML11_SOURCE_LOCATION_HPP #include "fwd/source_location_fwd.hpp" // IWYU pragma: export #if ! defined(TOML11_COMPILE_SOURCES) #include "impl/source_location_impl.hpp" // IWYU pragma: export #endif #endif // TOML11_SOURCE_LOCATION_HPP toml11-4.1.0/include/toml11/spec.hpp000066400000000000000000000070221464712047700170510ustar00rootroot00000000000000#ifndef TOML11_SPEC_HPP #define TOML11_SPEC_HPP #include #include #include namespace toml { struct semantic_version { constexpr semantic_version(std::uint32_t mjr, std::uint32_t mnr, std::uint32_t p) noexcept : major{mjr}, minor{mnr}, patch{p} {} std::uint32_t major; std::uint32_t minor; std::uint32_t patch; }; constexpr inline semantic_version make_semver(std::uint32_t mjr, std::uint32_t mnr, std::uint32_t p) noexcept { return semantic_version(mjr, mnr, p); } constexpr inline bool operator==(const semantic_version& lhs, const semantic_version& rhs) noexcept { return lhs.major == rhs.major && lhs.minor == rhs.minor && lhs.patch == rhs.patch; } constexpr inline bool operator!=(const semantic_version& lhs, const semantic_version& rhs) noexcept { return !(lhs == rhs); } constexpr inline bool operator<(const semantic_version& lhs, const semantic_version& rhs) noexcept { return lhs.major < rhs.major || (lhs.major == rhs.major && lhs.minor < rhs.minor) || (lhs.major == rhs.major && lhs.minor == rhs.minor && lhs.patch < rhs.patch); } constexpr inline bool operator>(const semantic_version& lhs, const semantic_version& rhs) noexcept { return rhs < lhs; } constexpr inline bool operator<=(const semantic_version& lhs, const semantic_version& rhs) noexcept { return !(lhs > rhs); } constexpr inline bool operator>=(const semantic_version& lhs, const semantic_version& rhs) noexcept { return !(lhs < rhs); } inline std::ostream& operator<<(std::ostream& os, const semantic_version& v) { os << v.major << '.' << v.minor << '.' << v.patch; return os; } inline std::string to_string(const semantic_version& v) { std::ostringstream oss; oss << v; return oss.str(); } struct spec { constexpr static spec default_version() noexcept { return spec::v(1, 0, 0); } constexpr static spec v(std::uint32_t mjr, std::uint32_t mnr, std::uint32_t p) noexcept { return spec(make_semver(mjr, mnr, p)); } constexpr explicit spec(const semantic_version& semver) noexcept : version{semver}, v1_1_0_allow_control_characters_in_comments {semantic_version{1, 1, 0} <= semver}, v1_1_0_allow_newlines_in_inline_tables {semantic_version{1, 1, 0} <= semver}, v1_1_0_allow_trailing_comma_in_inline_tables{semantic_version{1, 1, 0} <= semver}, v1_1_0_allow_non_english_in_bare_keys {semantic_version{1, 1, 0} <= semver}, v1_1_0_add_escape_sequence_e {semantic_version{1, 1, 0} <= semver}, v1_1_0_add_escape_sequence_x {semantic_version{1, 1, 0} <= semver}, v1_1_0_make_seconds_optional {semantic_version{1, 1, 0} <= semver}, ext_hex_float {false}, ext_num_suffix{false}, ext_null_value{false} {} semantic_version version; // toml version // diff from v1.0.0 -> v1.1.0 bool v1_1_0_allow_control_characters_in_comments; bool v1_1_0_allow_newlines_in_inline_tables; bool v1_1_0_allow_trailing_comma_in_inline_tables; bool v1_1_0_allow_non_english_in_bare_keys; bool v1_1_0_add_escape_sequence_e; bool v1_1_0_add_escape_sequence_x; bool v1_1_0_make_seconds_optional; // library extensions bool ext_hex_float; // allow hex float (in C++ style) bool ext_num_suffix; // allow number suffix (in C++ style) bool ext_null_value; // allow `null` as a value }; } // namespace toml #endif // TOML11_SPEC_HPP toml11-4.1.0/include/toml11/storage.hpp000066400000000000000000000026151464712047700175660ustar00rootroot00000000000000#ifndef TOML11_STORAGE_HPP #define TOML11_STORAGE_HPP #include "compat.hpp" namespace toml { namespace detail { // It owns a pointer to T. It does deep-copy when copied. // This struct is introduced to implement a recursive type. // // `toml::value` contains `std::vector` to represent a toml array. // But, in the definition of `toml::value`, `toml::value` is still incomplete. // `std::vector` of an incomplete type is not allowed in C++11 (it is allowed // after C++17). To avoid this, we need to use a pointer to `toml::value`, like // `std::vector>`. Although `std::unique_ptr` is // noncopyable, we want to make `toml::value` copyable. `storage` is introduced // to resolve those problems. template struct storage { using value_type = T; explicit storage(value_type v): ptr_(cxx::make_unique(std::move(v))) {} ~storage() = default; storage(const storage& rhs): ptr_(cxx::make_unique(*rhs.ptr_)) {} storage& operator=(const storage& rhs) { this->ptr_ = cxx::make_unique(*rhs.ptr_); return *this; } storage(storage&&) = default; storage& operator=(storage&&) = default; bool is_ok() const noexcept {return static_cast(ptr_);} value_type& get() const noexcept {return *ptr_;} private: std::unique_ptr ptr_; }; } // detail } // toml #endif // TOML11_STORAGE_HPP toml11-4.1.0/include/toml11/syntax.hpp000066400000000000000000000003531464712047700174450ustar00rootroot00000000000000#ifndef TOML11_SYNTAX_HPP #define TOML11_SYNTAX_HPP #include "fwd/syntax_fwd.hpp" // IWYU pragma: export #if ! defined(TOML11_COMPILE_SOURCES) #include "impl/syntax_impl.hpp" // IWYU pragma: export #endif #endif// TOML11_SYNTAX_HPP toml11-4.1.0/include/toml11/traits.hpp000066400000000000000000000206031464712047700174250ustar00rootroot00000000000000#ifndef TOML11_TRAITS_HPP #define TOML11_TRAITS_HPP #include "from.hpp" #include "into.hpp" #include "compat.hpp" #include #include #include #include #include #include #include #if defined(TOML11_HAS_STRING_VIEW) #include #endif namespace toml { template class basic_value; namespace detail { // --------------------------------------------------------------------------- // check whether type T is a kind of container/map class struct has_iterator_impl { template static std::true_type check(typename T::iterator*); template static std::false_type check(...); }; struct has_value_type_impl { template static std::true_type check(typename T::value_type*); template static std::false_type check(...); }; struct has_key_type_impl { template static std::true_type check(typename T::key_type*); template static std::false_type check(...); }; struct has_mapped_type_impl { template static std::true_type check(typename T::mapped_type*); template static std::false_type check(...); }; struct has_reserve_method_impl { template static std::false_type check(...); template static std::true_type check( decltype(std::declval().reserve(std::declval()))*); }; struct has_push_back_method_impl { template static std::false_type check(...); template static std::true_type check( decltype(std::declval().push_back(std::declval()))*); }; struct is_comparable_impl { template static std::false_type check(...); template static std::true_type check( decltype(std::declval() < std::declval())*); }; struct has_from_toml_method_impl { template static std::true_type check( decltype(std::declval().from_toml(std::declval<::toml::basic_value>()))*); template static std::false_type check(...); }; struct has_into_toml_method_impl { template static std::true_type check(decltype(std::declval().into_toml())*); template static std::false_type check(...); }; struct has_template_into_toml_method_impl { template static std::true_type check(decltype(std::declval().template into_toml())*); template static std::false_type check(...); }; struct has_specialized_from_impl { template static std::false_type check(...); template)> static std::true_type check(::toml::from*); }; struct has_specialized_into_impl { template static std::false_type check(...); template)> static std::true_type check(::toml::into*); }; /// Intel C++ compiler can not use decltype in parent class declaration, here /// is a hack to work around it. https://stackoverflow.com/a/23953090/4692076 #ifdef __INTEL_COMPILER #define decltype(...) std::enable_if::type #endif template struct has_iterator: decltype(has_iterator_impl::check(nullptr)){}; template struct has_value_type: decltype(has_value_type_impl::check(nullptr)){}; template struct has_key_type: decltype(has_key_type_impl::check(nullptr)){}; template struct has_mapped_type: decltype(has_mapped_type_impl::check(nullptr)){}; template struct has_reserve_method: decltype(has_reserve_method_impl::check(nullptr)){}; template struct has_push_back_method: decltype(has_push_back_method_impl::check(nullptr)){}; template struct is_comparable: decltype(is_comparable_impl::check(nullptr)){}; template struct has_from_toml_method: decltype(has_from_toml_method_impl::check(nullptr)){}; template struct has_into_toml_method: decltype(has_into_toml_method_impl::check(nullptr)){}; template struct has_template_into_toml_method: decltype(has_template_into_toml_method_impl::check(nullptr)){}; template struct has_specialized_from: decltype(has_specialized_from_impl::check(nullptr)){}; template struct has_specialized_into: decltype(has_specialized_into_impl::check(nullptr)){}; #ifdef __INTEL_COMPILER #undef decltype #endif // --------------------------------------------------------------------------- // type checkers template struct is_std_pair_impl : std::false_type{}; template struct is_std_pair_impl> : std::true_type{}; template using is_std_pair = is_std_pair_impl>; template struct is_std_tuple_impl : std::false_type{}; template struct is_std_tuple_impl> : std::true_type{}; template using is_std_tuple = is_std_tuple_impl>; template struct is_std_array_impl : std::false_type{}; template struct is_std_array_impl> : std::true_type{}; template using is_std_array = is_std_array_impl>; template struct is_std_forward_list_impl : std::false_type{}; template struct is_std_forward_list_impl> : std::true_type{}; template using is_std_forward_list = is_std_forward_list_impl>; template struct is_std_basic_string_impl : std::false_type{}; template struct is_std_basic_string_impl> : std::true_type{}; template using is_std_basic_string = is_std_basic_string_impl>; template struct is_1byte_std_basic_string_impl : std::false_type{}; template struct is_1byte_std_basic_string_impl> : std::integral_constant {}; template using is_1byte_std_basic_string = is_std_basic_string_impl>; #if defined(TOML11_HAS_STRING_VIEW) template struct is_std_basic_string_view_impl : std::false_type{}; template struct is_std_basic_string_view_impl> : std::true_type{}; template using is_std_basic_string_view = is_std_basic_string_view_impl>; template struct is_string_view_of : std::false_type {}; template struct is_string_view_of, std::basic_string> : std::true_type {}; #endif template struct is_chrono_duration_impl: std::false_type{}; template struct is_chrono_duration_impl>: std::true_type{}; template using is_chrono_duration = is_chrono_duration_impl>; template struct is_map_impl : cxx::conjunction< // map satisfies all the following conditions has_iterator, // has T::iterator has_value_type, // has T::value_type has_key_type, // has T::key_type has_mapped_type // has T::mapped_type >{}; template using is_map = is_map_impl>; template struct is_container_impl : cxx::conjunction< cxx::negation>, // not a map cxx::negation>, // not a std::string #ifdef TOML11_HAS_STRING_VIEW cxx::negation>, // not a std::string_view #endif has_iterator, // has T::iterator has_value_type // has T::value_type >{}; template using is_container = is_container_impl>; template struct is_basic_value_impl: std::false_type{}; template struct is_basic_value_impl<::toml::basic_value>: std::true_type{}; template using is_basic_value = is_basic_value_impl>; }// detail }//toml #endif // TOML11_TRAITS_HPP toml11-4.1.0/include/toml11/types.hpp000066400000000000000000000266161464712047700172750ustar00rootroot00000000000000#ifndef TOML11_TYPES_HPP #define TOML11_TYPES_HPP #include "comments.hpp" #include "error_info.hpp" #include "format.hpp" #include "ordered_map.hpp" #include "value.hpp" #include #include #include #include #include #include #include #include namespace toml { // forward decl template class basic_value; // when you use a special integer type as toml::value::integer_type, parse must // be able to read it. So, type_config has static member functions that read the // integer_type as {dec, hex, oct, bin}-integer. But, in most cases, operator<< // is enough. To make config easy, we provide the default read functions. // // Before this functions is called, syntax is checked and prefix(`0x` etc) and // spacer(`_`) are removed. template result read_dec_int(const std::string& str, const source_location src) { constexpr auto max_digits = std::numeric_limits::digits; assert( ! str.empty()); T val{0}; std::istringstream iss(str); iss >> val; if(iss.fail()) { return err(make_error_info("toml::parse_dec_integer: " "too large integer: current max digits = 2^" + std::to_string(max_digits), std::move(src), "must be < 2^" + std::to_string(max_digits))); } return ok(val); } template result read_hex_int(const std::string& str, const source_location src) { constexpr auto max_digits = std::numeric_limits::digits; assert( ! str.empty()); T val{0}; std::istringstream iss(str); iss >> std::hex >> val; if(iss.fail()) { return err(make_error_info("toml::parse_hex_integer: " "too large integer: current max value = 2^" + std::to_string(max_digits), std::move(src), "must be < 2^" + std::to_string(max_digits))); } return ok(val); } template result read_oct_int(const std::string& str, const source_location src) { constexpr auto max_digits = std::numeric_limits::digits; assert( ! str.empty()); T val{0}; std::istringstream iss(str); iss >> std::oct >> val; if(iss.fail()) { return err(make_error_info("toml::parse_oct_integer: " "too large integer: current max value = 2^" + std::to_string(max_digits), std::move(src), "must be < 2^" + std::to_string(max_digits))); } return ok(val); } template result read_bin_int(const std::string& str, const source_location src) { constexpr auto is_bounded = std::numeric_limits::is_bounded; constexpr auto max_digits = std::numeric_limits::digits; const auto max_value = (std::numeric_limits::max)(); T val{0}; T base{1}; for(auto i = str.rbegin(); i != str.rend(); ++i) { const auto c = *i; if(c == '1') { val += base; // prevent `base` from overflow if(is_bounded && max_value / 2 < base && std::next(i) != str.rend()) { base = 0; } else { base *= 2; } } else { assert(c == '0'); if(is_bounded && max_value / 2 < base && std::next(i) != str.rend()) { base = 0; } else { base *= 2; } } } if(base == 0) { return err(make_error_info("toml::parse_bin_integer: " "too large integer: current max value = 2^" + std::to_string(max_digits), std::move(src), "must be < 2^" + std::to_string(max_digits))); } return ok(val); } template result read_int(const std::string& str, const source_location src, const std::uint8_t base) { assert(base == 10 || base == 16 || base == 8 || base == 2); switch(base) { case 2: { return read_bin_int(str, src); } case 8: { return read_oct_int(str, src); } case 16: { return read_hex_int(str, src); } default: { assert(base == 10); return read_dec_int(str, src); } } } inline result read_hex_float(const std::string& str, const source_location src, float val) { #if defined(_MSC_VER) && ! defined(__clang__) const auto res = ::sscanf_s(str.c_str(), "%a", std::addressof(val)); #else const auto res = std::sscanf(str.c_str(), "%a", std::addressof(val)); #endif if(res != 1) { return err(make_error_info("toml::parse_floating: " "failed to read hexadecimal floating point value ", std::move(src), "here")); } return ok(val); } inline result read_hex_float(const std::string& str, const source_location src, double val) { #if defined(_MSC_VER) && ! defined(__clang__) const auto res = ::sscanf_s(str.c_str(), "%la", std::addressof(val)); #else const auto res = std::sscanf(str.c_str(), "%la", std::addressof(val)); #endif if(res != 1) { return err(make_error_info("toml::parse_floating: " "failed to read hexadecimal floating point value ", std::move(src), "here")); } return ok(val); } template cxx::enable_if_t, double>>, cxx::negation, float>> >::value, result> read_hex_float(const std::string&, const source_location src, T) { return err(make_error_info("toml::parse_floating: failed to read " "floating point value because of unknown type in type_config", std::move(src), "here")); } template result read_dec_float(const std::string& str, const source_location src) { T val; std::istringstream iss(str); iss >> val; if(iss.fail()) { return err(make_error_info("toml::parse_floating: " "failed to read floating point value from stream", std::move(src), "here")); } return ok(val); } template result read_float(const std::string& str, const source_location src, const bool is_hex) { if(is_hex) { return read_hex_float(str, src, T{}); } else { return read_dec_float(str, src); } } struct type_config { using comment_type = preserve_comments; using boolean_type = bool; using integer_type = std::int64_t; using floating_type = double; using string_type = std::string; template using array_type = std::vector; template using table_type = std::unordered_map; static result parse_int(const std::string& str, const source_location src, const std::uint8_t base) { return read_int(str, src, base); } static result parse_float(const std::string& str, const source_location src, const bool is_hex) { return read_float(str, src, is_hex); } }; using value = basic_value; using table = typename value::table_type; using array = typename value::array_type; struct ordered_type_config { using comment_type = preserve_comments; using boolean_type = bool; using integer_type = std::int64_t; using floating_type = double; using string_type = std::string; template using array_type = std::vector; template using table_type = ordered_map; static result parse_int(const std::string& str, const source_location src, const std::uint8_t base) { return read_int(str, src, base); } static result parse_float(const std::string& str, const source_location src, const bool is_hex) { return read_float(str, src, is_hex); } }; using ordered_value = basic_value; using ordered_table = typename ordered_value::table_type; using ordered_array = typename ordered_value::array_type; // ---------------------------------------------------------------------------- // meta functions for internal use namespace detail { // ---------------------------------------------------------------------------- // check if type T has all the needed member types struct has_comment_type_impl { template static std::true_type check(typename T::comment_type*); template static std::false_type check(...); }; template using has_comment_type = decltype(has_comment_type_impl::check(nullptr)); struct has_integer_type_impl { template static std::true_type check(typename T::integer_type*); template static std::false_type check(...); }; template using has_integer_type = decltype(has_integer_type_impl::check(nullptr)); struct has_floating_type_impl { template static std::true_type check(typename T::floating_type*); template static std::false_type check(...); }; template using has_floating_type = decltype(has_floating_type_impl::check(nullptr)); struct has_string_type_impl { template static std::true_type check(typename T::string_type*); template static std::false_type check(...); }; template using has_string_type = decltype(has_string_type_impl::check(nullptr)); struct has_array_type_impl { template static std::true_type check(typename T::template array_type*); template static std::false_type check(...); }; template using has_array_type = decltype(has_array_type_impl::check(nullptr)); struct has_table_type_impl { template static std::true_type check(typename T::template table_type*); template static std::false_type check(...); }; template using has_table_type = decltype(has_table_type_impl::check(nullptr)); struct has_parse_int_impl { template static std::true_type check(decltype(std::declval().parse_int( std::declval(), std::declval(), std::declval() ))*); template static std::false_type check(...); }; template using has_parse_int = decltype(has_parse_int_impl::check(nullptr)); struct has_parse_float_impl { template static std::true_type check(decltype(std::declval().parse_float( std::declval(), std::declval(), std::declval() ))*); template static std::false_type check(...); }; template using has_parse_float = decltype(has_parse_float_impl::check(nullptr)); template using is_type_config = cxx::conjunction< has_comment_type, has_integer_type, has_floating_type, has_string_type, has_array_type, has_table_type, has_parse_int, has_parse_float >; } // namespace detail } // namespace toml #if defined(TOML11_COMPILE_SOURCES) namespace toml { extern template class basic_value; extern template class basic_value; } // toml #endif // TOML11_COMPILE_SOURCES #endif // TOML11_TYPES_HPP toml11-4.1.0/include/toml11/utility.hpp000066400000000000000000000120211464712047700176150ustar00rootroot00000000000000#ifndef TOML11_UTILITY_HPP #define TOML11_UTILITY_HPP #include "result.hpp" #include "traits.hpp" #include #include #include #include #include namespace toml { namespace detail { // to output character in an error message. inline std::string show_char(const int c) { using char_type = unsigned char; if(std::isgraph(c)) { return std::string(1, static_cast(c)); } else { std::array buf; buf.fill('\0'); const auto r = std::snprintf(buf.data(), buf.size(), "0x%02x", c & 0xFF); assert(r == static_cast(buf.size()) - 1); (void) r; // Unused variable warning auto in_hex = std::string(buf.data()); switch(c) { case char_type('\0'): {in_hex += "(NUL)"; break;} case char_type(' ') : {in_hex += "(SPACE)"; break;} case char_type('\n'): {in_hex += "(LINE FEED)"; break;} case char_type('\r'): {in_hex += "(CARRIAGE RETURN)"; break;} case char_type('\t'): {in_hex += "(TAB)"; break;} case char_type('\v'): {in_hex += "(VERTICAL TAB)"; break;} case char_type('\f'): {in_hex += "(FORM FEED)"; break;} case char_type('\x1B'): {in_hex += "(ESCAPE)"; break;} default: break; } return in_hex; } } // --------------------------------------------------------------------------- template void try_reserve_impl(Container& container, std::size_t N, std::true_type) { container.reserve(N); return; } template void try_reserve_impl(Container&, std::size_t, std::false_type) noexcept { return; } template void try_reserve(Container& container, std::size_t N) { try_reserve_impl(container, N, has_reserve_method{}); return; } // --------------------------------------------------------------------------- template result from_string(const std::string& str) { T v; std::istringstream iss(str); iss >> v; if(iss.fail()) { return err(); } return ok(v); } // --------------------------------------------------------------------------- // helper function to avoid std::string(0, 'c') or std::string(iter, iter) template std::string make_string(Iterator first, Iterator last) { if(first == last) {return "";} return std::string(first, last); } inline std::string make_string(std::size_t len, char c) { if(len == 0) {return "";} return std::string(len, c); } // --------------------------------------------------------------------------- template struct string_conv_impl { static_assert(sizeof(Char) == sizeof(char), ""); static_assert(sizeof(Char2) == sizeof(char), ""); static std::basic_string invoke(std::basic_string s) { std::basic_string retval; std::transform(s.begin(), s.end(), std::back_inserter(retval), [](const Char2 c) {return static_cast(c);}); return retval; } template static std::basic_string invoke(const Char2 (&s)[N]) { std::basic_string retval; // "string literal" has null-char at the end. to skip it, we use prev. std::transform(std::begin(s), std::prev(std::end(s)), std::back_inserter(retval), [](const Char2 c) {return static_cast(c);}); return retval; } }; template struct string_conv_impl { static_assert(sizeof(Char) == sizeof(char), ""); static std::basic_string invoke(std::basic_string s) { return s; } template static std::basic_string invoke(const Char (&s)[N]) { return std::basic_string(s); } }; template cxx::enable_if_t::value, S> string_conv(std::basic_string s) { using C = typename S::value_type; using T = typename S::traits_type; using A = typename S::allocator_type; return string_conv_impl::invoke(std::move(s)); } template cxx::enable_if_t::value, S> string_conv(const char (&s)[N]) { using C = typename S::value_type; using T = typename S::traits_type; using A = typename S::allocator_type; using C2 = char; using T2 = std::char_traits; using A2 = std::allocator; return string_conv_impl::template invoke(s); } } // namespace detail } // namespace toml #endif // TOML11_UTILITY_HPP toml11-4.1.0/include/toml11/value.hpp000066400000000000000000002644631464712047700172510ustar00rootroot00000000000000#ifndef TOML11_VALUE_HPP #define TOML11_VALUE_HPP #include "color.hpp" #include "datetime.hpp" #include "exception.hpp" #include "error_info.hpp" #include "format.hpp" #include "region.hpp" #include "source_location.hpp" #include "storage.hpp" #include "traits.hpp" #include "value_t.hpp" #include "version.hpp" // IWYU pragma: keep < TOML11_HAS_STRING_VIEW #ifdef TOML11_HAS_STRING_VIEW #include #endif #include namespace toml { template class basic_value; struct type_error final : public ::toml::exception { public: type_error(std::string what_arg, source_location loc) : what_(std::move(what_arg)), loc_(std::move(loc)) {} ~type_error() noexcept override = default; const char* what() const noexcept override {return what_.c_str();} source_location const& location() const noexcept {return loc_;} private: std::string what_; source_location loc_; }; // only for internal use namespace detail { template error_info make_type_error(const basic_value&, const std::string&, const value_t); template error_info make_not_found_error(const basic_value&, const std::string&, const typename basic_value::key_type&); template void change_region_of_value(basic_value&, const basic_value&); template struct getter; } // detail template class basic_value { public: using config_type = TypeConfig; using key_type = typename config_type::string_type; using value_type = basic_value; using boolean_type = typename config_type::boolean_type; using integer_type = typename config_type::integer_type; using floating_type = typename config_type::floating_type; using string_type = typename config_type::string_type; using local_time_type = ::toml::local_time; using local_date_type = ::toml::local_date; using local_datetime_type = ::toml::local_datetime; using offset_datetime_type = ::toml::offset_datetime; using array_type = typename config_type::template array_type; using table_type = typename config_type::template table_type; using comment_type = typename config_type::comment_type; using char_type = typename string_type::value_type; private: using region_type = detail::region; public: basic_value() noexcept : type_(value_t::empty), empty_('\0'), region_{}, comments_{} {} ~basic_value() noexcept {this->cleanup();} // copy/move constructor/assigner ===================================== {{{ basic_value(const basic_value& v) : type_(v.type_), region_(v.region_), comments_(v.comments_) { switch(this->type_) { case value_t::boolean : assigner(boolean_ , v.boolean_ ); break; case value_t::integer : assigner(integer_ , v.integer_ ); break; case value_t::floating : assigner(floating_ , v.floating_ ); break; case value_t::string : assigner(string_ , v.string_ ); break; case value_t::offset_datetime: assigner(offset_datetime_, v.offset_datetime_); break; case value_t::local_datetime : assigner(local_datetime_ , v.local_datetime_ ); break; case value_t::local_date : assigner(local_date_ , v.local_date_ ); break; case value_t::local_time : assigner(local_time_ , v.local_time_ ); break; case value_t::array : assigner(array_ , v.array_ ); break; case value_t::table : assigner(table_ , v.table_ ); break; default : assigner(empty_ , '\0' ); break; } } basic_value(basic_value&& v) : type_(v.type()), region_(std::move(v.region_)), comments_(std::move(v.comments_)) { switch(this->type_) { case value_t::boolean : assigner(boolean_ , std::move(v.boolean_ )); break; case value_t::integer : assigner(integer_ , std::move(v.integer_ )); break; case value_t::floating : assigner(floating_ , std::move(v.floating_ )); break; case value_t::string : assigner(string_ , std::move(v.string_ )); break; case value_t::offset_datetime: assigner(offset_datetime_, std::move(v.offset_datetime_)); break; case value_t::local_datetime : assigner(local_datetime_ , std::move(v.local_datetime_ )); break; case value_t::local_date : assigner(local_date_ , std::move(v.local_date_ )); break; case value_t::local_time : assigner(local_time_ , std::move(v.local_time_ )); break; case value_t::array : assigner(array_ , std::move(v.array_ )); break; case value_t::table : assigner(table_ , std::move(v.table_ )); break; default : assigner(empty_ , '\0' ); break; } } basic_value& operator=(const basic_value& v) { if(this == std::addressof(v)) {return *this;} this->cleanup(); this->type_ = v.type_; this->region_ = v.region_; this->comments_ = v.comments_; switch(this->type_) { case value_t::boolean : assigner(boolean_ , v.boolean_ ); break; case value_t::integer : assigner(integer_ , v.integer_ ); break; case value_t::floating : assigner(floating_ , v.floating_ ); break; case value_t::string : assigner(string_ , v.string_ ); break; case value_t::offset_datetime: assigner(offset_datetime_, v.offset_datetime_); break; case value_t::local_datetime : assigner(local_datetime_ , v.local_datetime_ ); break; case value_t::local_date : assigner(local_date_ , v.local_date_ ); break; case value_t::local_time : assigner(local_time_ , v.local_time_ ); break; case value_t::array : assigner(array_ , v.array_ ); break; case value_t::table : assigner(table_ , v.table_ ); break; default : assigner(empty_ , '\0' ); break; } return *this; } basic_value& operator=(basic_value&& v) { if(this == std::addressof(v)) {return *this;} this->cleanup(); this->type_ = v.type_; this->region_ = std::move(v.region_); this->comments_ = std::move(v.comments_); switch(this->type_) { case value_t::boolean : assigner(boolean_ , std::move(v.boolean_ )); break; case value_t::integer : assigner(integer_ , std::move(v.integer_ )); break; case value_t::floating : assigner(floating_ , std::move(v.floating_ )); break; case value_t::string : assigner(string_ , std::move(v.string_ )); break; case value_t::offset_datetime: assigner(offset_datetime_, std::move(v.offset_datetime_)); break; case value_t::local_datetime : assigner(local_datetime_ , std::move(v.local_datetime_ )); break; case value_t::local_date : assigner(local_date_ , std::move(v.local_date_ )); break; case value_t::local_time : assigner(local_time_ , std::move(v.local_time_ )); break; case value_t::array : assigner(array_ , std::move(v.array_ )); break; case value_t::table : assigner(table_ , std::move(v.table_ )); break; default : assigner(empty_ , '\0' ); break; } return *this; } // }}} // constructor to overwrite commnets ================================== {{{ basic_value(basic_value v, std::vector com) : type_(v.type()), region_(std::move(v.region_)), comments_(std::move(com)) { switch(this->type_) { case value_t::boolean : assigner(boolean_ , std::move(v.boolean_ )); break; case value_t::integer : assigner(integer_ , std::move(v.integer_ )); break; case value_t::floating : assigner(floating_ , std::move(v.floating_ )); break; case value_t::string : assigner(string_ , std::move(v.string_ )); break; case value_t::offset_datetime: assigner(offset_datetime_, std::move(v.offset_datetime_)); break; case value_t::local_datetime : assigner(local_datetime_ , std::move(v.local_datetime_ )); break; case value_t::local_date : assigner(local_date_ , std::move(v.local_date_ )); break; case value_t::local_time : assigner(local_time_ , std::move(v.local_time_ )); break; case value_t::array : assigner(array_ , std::move(v.array_ )); break; case value_t::table : assigner(table_ , std::move(v.table_ )); break; default : assigner(empty_ , '\0' ); break; } } // }}} // conversion between different basic_values ========================== {{{ template basic_value(basic_value other) : type_(other.type_), region_(std::move(other.region_)), comments_(std::move(other.comments_)) { switch(other.type_) { // use auto-convert in constructor case value_t::boolean : assigner(boolean_ , std::move(other.boolean_ )); break; case value_t::integer : assigner(integer_ , std::move(other.integer_ )); break; case value_t::floating : assigner(floating_ , std::move(other.floating_ )); break; case value_t::string : assigner(string_ , std::move(other.string_ )); break; case value_t::offset_datetime: assigner(offset_datetime_, std::move(other.offset_datetime_)); break; case value_t::local_datetime : assigner(local_datetime_ , std::move(other.local_datetime_ )); break; case value_t::local_date : assigner(local_date_ , std::move(other.local_date_ )); break; case value_t::local_time : assigner(local_time_ , std::move(other.local_time_ )); break; // may have different container type case value_t::array : { array_type tmp( std::make_move_iterator(other.array_.value.get().begin()), std::make_move_iterator(other.array_.value.get().end())); assigner(array_, array_storage( detail::storage(std::move(tmp)), other.array_.format )); break; } case value_t::table : { table_type tmp( std::make_move_iterator(other.table_.value.get().begin()), std::make_move_iterator(other.table_.value.get().end())); assigner(table_, table_storage( detail::storage(std::move(tmp)), other.table_.format )); break; } default: break; } } template basic_value(basic_value other, std::vector com) : type_(other.type_), region_(std::move(other.region_)), comments_(std::move(com)) { switch(other.type_) { // use auto-convert in constructor case value_t::boolean : assigner(boolean_ , std::move(other.boolean_ )); break; case value_t::integer : assigner(integer_ , std::move(other.integer_ )); break; case value_t::floating : assigner(floating_ , std::move(other.floating_ )); break; case value_t::string : assigner(string_ , std::move(other.string_ )); break; case value_t::offset_datetime: assigner(offset_datetime_, std::move(other.offset_datetime_)); break; case value_t::local_datetime : assigner(local_datetime_ , std::move(other.local_datetime_ )); break; case value_t::local_date : assigner(local_date_ , std::move(other.local_date_ )); break; case value_t::local_time : assigner(local_time_ , std::move(other.local_time_ )); break; // may have different container type case value_t::array : { array_type tmp( std::make_move_iterator(other.array_.value.get().begin()), std::make_move_iterator(other.array_.value.get().end())); assigner(array_, array_storage( detail::storage(std::move(tmp)), other.array_.format )); break; } case value_t::table : { table_type tmp( std::make_move_iterator(other.table_.value.get().begin()), std::make_move_iterator(other.table_.value.get().end())); assigner(table_, table_storage( detail::storage(std::move(tmp)), other.table_.format )); break; } default: break; } } template basic_value& operator=(basic_value other) { this->cleanup(); this->region_ = other.region_; this->comments_ = comment_type(other.comments_); this->type_ = other.type_; switch(other.type_) { // use auto-convert in constructor case value_t::boolean : assigner(boolean_ , std::move(other.boolean_ )); break; case value_t::integer : assigner(integer_ , std::move(other.integer_ )); break; case value_t::floating : assigner(floating_ , std::move(other.floating_ )); break; case value_t::string : assigner(string_ , std::move(other.string_ )); break; case value_t::offset_datetime: assigner(offset_datetime_, std::move(other.offset_datetime_)); break; case value_t::local_datetime : assigner(local_datetime_ , std::move(other.local_datetime_ )); break; case value_t::local_date : assigner(local_date_ , std::move(other.local_date_ )); break; case value_t::local_time : assigner(local_time_ , std::move(other.local_time_ )); break; // may have different container type case value_t::array : { array_type tmp( std::make_move_iterator(other.array_.value.get().begin()), std::make_move_iterator(other.array_.value.get().end())); assigner(array_, array_storage( detail::storage(std::move(tmp)), other.array_.format )); break; } case value_t::table : { table_type tmp( std::make_move_iterator(other.table_.value.get().begin()), std::make_move_iterator(other.table_.value.get().end())); assigner(table_, table_storage( detail::storage(std::move(tmp)), other.table_.format )); break; } default: break; } return *this; } // }}} // constructor (boolean) ============================================== {{{ basic_value(boolean_type x) : basic_value(x, boolean_format_info{}, std::vector{}, region_type{}) {} basic_value(boolean_type x, boolean_format_info fmt) : basic_value(x, fmt, std::vector{}, region_type{}) {} basic_value(boolean_type x, std::vector com) : basic_value(x, boolean_format_info{}, std::move(com), region_type{}) {} basic_value(boolean_type x, boolean_format_info fmt, std::vector com) : basic_value(x, fmt, std::move(com), region_type{}) {} basic_value(boolean_type x, boolean_format_info fmt, std::vector com, region_type reg) : type_(value_t::boolean), boolean_(boolean_storage(x, fmt)), region_(std::move(reg)), comments_(std::move(com)) {} basic_value& operator=(boolean_type x) { boolean_format_info fmt; if(this->is_boolean()) { fmt = this->as_boolean_fmt(); } this->cleanup(); this->type_ = value_t::boolean; this->region_ = region_type{}; assigner(this->boolean_, boolean_storage(x, fmt)); return *this; } // }}} // constructor (integer) ============================================== {{{ basic_value(integer_type x) : basic_value(std::move(x), integer_format_info{}, std::vector{}, region_type{}) {} basic_value(integer_type x, integer_format_info fmt) : basic_value(std::move(x), std::move(fmt), std::vector{}, region_type{}) {} basic_value(integer_type x, std::vector com) : basic_value(std::move(x), integer_format_info{}, std::move(com), region_type{}) {} basic_value(integer_type x, integer_format_info fmt, std::vector com) : basic_value(std::move(x), std::move(fmt), std::move(com), region_type{}) {} basic_value(integer_type x, integer_format_info fmt, std::vector com, region_type reg) : type_(value_t::integer), integer_(integer_storage(std::move(x), std::move(fmt))), region_(std::move(reg)), comments_(std::move(com)) {} basic_value& operator=(integer_type x) { integer_format_info fmt; if(this->is_integer()) { fmt = this->as_integer_fmt(); } this->cleanup(); this->type_ = value_t::integer; this->region_ = region_type{}; assigner(this->integer_, integer_storage(std::move(x), std::move(fmt))); return *this; } private: template using enable_if_integer_like_t = cxx::enable_if_t, boolean_type>>, cxx::negation, integer_type>>, std::is_integral> >::value, std::nullptr_t>; public: template = nullptr> basic_value(T x) : basic_value(std::move(x), integer_format_info{}, std::vector{}, region_type{}) {} template = nullptr> basic_value(T x, integer_format_info fmt) : basic_value(std::move(x), std::move(fmt), std::vector{}, region_type{}) {} template = nullptr> basic_value(T x, std::vector com) : basic_value(std::move(x), integer_format_info{}, std::move(com), region_type{}) {} template = nullptr> basic_value(T x, integer_format_info fmt, std::vector com) : basic_value(std::move(x), std::move(fmt), std::move(com), region_type{}) {} template = nullptr> basic_value(T x, integer_format_info fmt, std::vector com, region_type reg) : type_(value_t::integer), integer_(integer_storage(std::move(x), std::move(fmt))), region_(std::move(reg)), comments_(std::move(com)) {} template = nullptr> basic_value& operator=(T x) { integer_format_info fmt; if(this->is_integer()) { fmt = this->as_integer_fmt(); } this->cleanup(); this->type_ = value_t::integer; this->region_ = region_type{}; assigner(this->integer_, integer_storage(x, std::move(fmt))); return *this; } // }}} // constructor (floating) ============================================= {{{ basic_value(floating_type x) : basic_value(std::move(x), floating_format_info{}, std::vector{}, region_type{}) {} basic_value(floating_type x, floating_format_info fmt) : basic_value(std::move(x), std::move(fmt), std::vector{}, region_type{}) {} basic_value(floating_type x, std::vector com) : basic_value(std::move(x), floating_format_info{}, std::move(com), region_type{}) {} basic_value(floating_type x, floating_format_info fmt, std::vector com) : basic_value(std::move(x), std::move(fmt), std::move(com), region_type{}) {} basic_value(floating_type x, floating_format_info fmt, std::vector com, region_type reg) : type_(value_t::floating), floating_(floating_storage(std::move(x), std::move(fmt))), region_(std::move(reg)), comments_(std::move(com)) {} basic_value& operator=(floating_type x) { floating_format_info fmt; if(this->is_floating()) { fmt = this->as_floating_fmt(); } this->cleanup(); this->type_ = value_t::floating; this->region_ = region_type{}; assigner(this->floating_, floating_storage(std::move(x), std::move(fmt))); return *this; } private: template using enable_if_floating_like_t = cxx::enable_if_t, floating_type>>, std::is_floating_point> >::value, std::nullptr_t>; public: template = nullptr> basic_value(T x) : basic_value(x, floating_format_info{}, std::vector{}, region_type{}) {} template = nullptr> basic_value(T x, floating_format_info fmt) : basic_value(x, std::move(fmt), std::vector{}, region_type{}) {} template = nullptr> basic_value(T x, std::vector com) : basic_value(x, floating_format_info{}, std::move(com), region_type{}) {} template = nullptr> basic_value(T x, floating_format_info fmt, std::vector com) : basic_value(x, std::move(fmt), std::move(com), region_type{}) {} template = nullptr> basic_value(T x, floating_format_info fmt, std::vector com, region_type reg) : type_(value_t::floating), floating_(floating_storage(x, std::move(fmt))), region_(std::move(reg)), comments_(std::move(com)) {} template = nullptr> basic_value& operator=(T x) { floating_format_info fmt; if(this->is_floating()) { fmt = this->as_floating_fmt(); } this->cleanup(); this->type_ = value_t::floating; this->region_ = region_type{}; assigner(this->floating_, floating_storage(x, std::move(fmt))); return *this; } // }}} // constructor (string) =============================================== {{{ basic_value(string_type x) : basic_value(std::move(x), string_format_info{}, std::vector{}, region_type{}) {} basic_value(string_type x, string_format_info fmt) : basic_value(std::move(x), std::move(fmt), std::vector{}, region_type{}) {} basic_value(string_type x, std::vector com) : basic_value(std::move(x), string_format_info{}, std::move(com), region_type{}) {} basic_value(string_type x, string_format_info fmt, std::vector com) : basic_value(std::move(x), std::move(fmt), std::move(com), region_type{}) {} basic_value(string_type x, string_format_info fmt, std::vector com, region_type reg) : type_(value_t::string), string_(string_storage(std::move(x), std::move(fmt))), region_(std::move(reg)), comments_(std::move(com)) {} basic_value& operator=(string_type x) { string_format_info fmt; if(this->is_string()) { fmt = this->as_string_fmt(); } this->cleanup(); this->type_ = value_t::string; this->region_ = region_type{}; assigner(this->string_, string_storage(x, std::move(fmt))); return *this; } // "string literal" basic_value(const typename string_type::value_type* x) : basic_value(x, string_format_info{}, std::vector{}, region_type{}) {} basic_value(const typename string_type::value_type* x, string_format_info fmt) : basic_value(x, std::move(fmt), std::vector{}, region_type{}) {} basic_value(const typename string_type::value_type* x, std::vector com) : basic_value(x, string_format_info{}, std::move(com), region_type{}) {} basic_value(const typename string_type::value_type* x, string_format_info fmt, std::vector com) : basic_value(x, std::move(fmt), std::move(com), region_type{}) {} basic_value(const typename string_type::value_type* x, string_format_info fmt, std::vector com, region_type reg) : type_(value_t::string), string_(string_storage(string_type(x), std::move(fmt))), region_(std::move(reg)), comments_(std::move(com)) {} basic_value& operator=(const typename string_type::value_type* x) { string_format_info fmt; if(this->is_string()) { fmt = this->as_string_fmt(); } this->cleanup(); this->type_ = value_t::string; this->region_ = region_type{}; assigner(this->string_, string_storage(string_type(x), std::move(fmt))); return *this; } #if defined(TOML11_HAS_STRING_VIEW) using string_view_type = std::basic_string_view< typename string_type::value_type, typename string_type::traits_type>; basic_value(string_view_type x) : basic_value(x, string_format_info{}, std::vector{}, region_type{}) {} basic_value(string_view_type x, string_format_info fmt) : basic_value(x, std::move(fmt), std::vector{}, region_type{}) {} basic_value(string_view_type x, std::vector com) : basic_value(x, string_format_info{}, std::move(com), region_type{}) {} basic_value(string_view_type x, string_format_info fmt, std::vector com) : basic_value(x, std::move(fmt), std::move(com), region_type{}) {} basic_value(string_view_type x, string_format_info fmt, std::vector com, region_type reg) : type_(value_t::string), string_(string_storage(string_type(x), std::move(fmt))), region_(std::move(reg)), comments_(std::move(com)) {} basic_value& operator=(string_view_type x) { string_format_info fmt; if(this->is_string()) { fmt = this->as_string_fmt(); } this->cleanup(); this->type_ = value_t::string; this->region_ = region_type{}; assigner(this->string_, string_storage(string_type(x), std::move(fmt))); return *this; } #endif // TOML11_HAS_STRING_VIEW template, string_type>>, detail::is_1byte_std_basic_string >::value, std::nullptr_t> = nullptr> basic_value(const T& x) : basic_value(x, string_format_info{}, std::vector{}, region_type{}) {} template, string_type>>, detail::is_1byte_std_basic_string >::value, std::nullptr_t> = nullptr> basic_value(const T& x, string_format_info fmt) : basic_value(x, std::move(fmt), std::vector{}, region_type{}) {} template, string_type>>, detail::is_1byte_std_basic_string >::value, std::nullptr_t> = nullptr> basic_value(const T& x, std::vector com) : basic_value(x, string_format_info{}, std::move(com), region_type{}) {} template, string_type>>, detail::is_1byte_std_basic_string >::value, std::nullptr_t> = nullptr> basic_value(const T& x, string_format_info fmt, std::vector com) : basic_value(x, std::move(fmt), std::move(com), region_type{}) {} template, string_type>>, detail::is_1byte_std_basic_string >::value, std::nullptr_t> = nullptr> basic_value(const T& x, string_format_info fmt, std::vector com, region_type reg) : type_(value_t::string), string_(string_storage(detail::string_conv(x), std::move(fmt))), region_(std::move(reg)), comments_(std::move(com)) {} template, string_type>>, detail::is_1byte_std_basic_string >::value, std::nullptr_t> = nullptr> basic_value& operator=(const T& x) { string_format_info fmt; if(this->is_string()) { fmt = this->as_string_fmt(); } this->cleanup(); this->type_ = value_t::string; this->region_ = region_type{}; assigner(this->string_, string_storage(detail::string_conv(x), std::move(fmt))); return *this; } // }}} // constructor (local_date) =========================================== {{{ basic_value(local_date_type x) : basic_value(x, local_date_format_info{}, std::vector{}, region_type{}) {} basic_value(local_date_type x, local_date_format_info fmt) : basic_value(x, fmt, std::vector{}, region_type{}) {} basic_value(local_date_type x, std::vector com) : basic_value(x, local_date_format_info{}, std::move(com), region_type{}) {} basic_value(local_date_type x, local_date_format_info fmt, std::vector com) : basic_value(x, fmt, std::move(com), region_type{}) {} basic_value(local_date_type x, local_date_format_info fmt, std::vector com, region_type reg) : type_(value_t::local_date), local_date_(local_date_storage(x, fmt)), region_(std::move(reg)), comments_(std::move(com)) {} basic_value& operator=(local_date_type x) { local_date_format_info fmt; if(this->is_local_date()) { fmt = this->as_local_date_fmt(); } this->cleanup(); this->type_ = value_t::local_date; this->region_ = region_type{}; assigner(this->local_date_, local_date_storage(x, fmt)); return *this; } // }}} // constructor (local_time) =========================================== {{{ basic_value(local_time_type x) : basic_value(x, local_time_format_info{}, std::vector{}, region_type{}) {} basic_value(local_time_type x, local_time_format_info fmt) : basic_value(x, fmt, std::vector{}, region_type{}) {} basic_value(local_time_type x, std::vector com) : basic_value(x, local_time_format_info{}, std::move(com), region_type{}) {} basic_value(local_time_type x, local_time_format_info fmt, std::vector com) : basic_value(x, fmt, std::move(com), region_type{}) {} basic_value(local_time_type x, local_time_format_info fmt, std::vector com, region_type reg) : type_(value_t::local_time), local_time_(local_time_storage(x, fmt)), region_(std::move(reg)), comments_(std::move(com)) {} basic_value& operator=(local_time_type x) { local_time_format_info fmt; if(this->is_local_time()) { fmt = this->as_local_time_fmt(); } this->cleanup(); this->type_ = value_t::local_time; this->region_ = region_type{}; assigner(this->local_time_, local_time_storage(x, fmt)); return *this; } template basic_value(const std::chrono::duration& x) : basic_value(local_time_type(x), local_time_format_info{}, std::vector{}, region_type{}) {} template basic_value(const std::chrono::duration& x, local_time_format_info fmt) : basic_value(local_time_type(x), std::move(fmt), std::vector{}, region_type{}) {} template basic_value(const std::chrono::duration& x, std::vector com) : basic_value(local_time_type(x), local_time_format_info{}, std::move(com), region_type{}) {} template basic_value(const std::chrono::duration& x, local_time_format_info fmt, std::vector com) : basic_value(local_time_type(x), std::move(fmt), std::move(com), region_type{}) {} template basic_value(const std::chrono::duration& x, local_time_format_info fmt, std::vector com, region_type reg) : basic_value(local_time_type(x), std::move(fmt), std::move(com), std::move(reg)) {} template basic_value& operator=(const std::chrono::duration& x) { local_time_format_info fmt; if(this->is_local_time()) { fmt = this->as_local_time_fmt(); } this->cleanup(); this->type_ = value_t::local_time; this->region_ = region_type{}; assigner(this->local_time_, local_time_storage(local_time_type(x), std::move(fmt))); return *this; } // }}} // constructor (local_datetime) =========================================== {{{ basic_value(local_datetime_type x) : basic_value(x, local_datetime_format_info{}, std::vector{}, region_type{}) {} basic_value(local_datetime_type x, local_datetime_format_info fmt) : basic_value(x, fmt, std::vector{}, region_type{}) {} basic_value(local_datetime_type x, std::vector com) : basic_value(x, local_datetime_format_info{}, std::move(com), region_type{}) {} basic_value(local_datetime_type x, local_datetime_format_info fmt, std::vector com) : basic_value(x, fmt, std::move(com), region_type{}) {} basic_value(local_datetime_type x, local_datetime_format_info fmt, std::vector com, region_type reg) : type_(value_t::local_datetime), local_datetime_(local_datetime_storage(x, fmt)), region_(std::move(reg)), comments_(std::move(com)) {} basic_value& operator=(local_datetime_type x) { local_datetime_format_info fmt; if(this->is_local_datetime()) { fmt = this->as_local_datetime_fmt(); } this->cleanup(); this->type_ = value_t::local_datetime; this->region_ = region_type{}; assigner(this->local_datetime_, local_datetime_storage(x, fmt)); return *this; } // }}} // constructor (offset_datetime) =========================================== {{{ basic_value(offset_datetime_type x) : basic_value(x, offset_datetime_format_info{}, std::vector{}, region_type{}) {} basic_value(offset_datetime_type x, offset_datetime_format_info fmt) : basic_value(x, fmt, std::vector{}, region_type{}) {} basic_value(offset_datetime_type x, std::vector com) : basic_value(x, offset_datetime_format_info{}, std::move(com), region_type{}) {} basic_value(offset_datetime_type x, offset_datetime_format_info fmt, std::vector com) : basic_value(x, fmt, std::move(com), region_type{}) {} basic_value(offset_datetime_type x, offset_datetime_format_info fmt, std::vector com, region_type reg) : type_(value_t::offset_datetime), offset_datetime_(offset_datetime_storage(x, fmt)), region_(std::move(reg)), comments_(std::move(com)) {} basic_value& operator=(offset_datetime_type x) { offset_datetime_format_info fmt; if(this->is_offset_datetime()) { fmt = this->as_offset_datetime_fmt(); } this->cleanup(); this->type_ = value_t::offset_datetime; this->region_ = region_type{}; assigner(this->offset_datetime_, offset_datetime_storage(x, fmt)); return *this; } // system_clock::time_point basic_value(std::chrono::system_clock::time_point x) : basic_value(offset_datetime_type(x), offset_datetime_format_info{}, std::vector{}, region_type{}) {} basic_value(std::chrono::system_clock::time_point x, offset_datetime_format_info fmt) : basic_value(offset_datetime_type(x), fmt, std::vector{}, region_type{}) {} basic_value(std::chrono::system_clock::time_point x, std::vector com) : basic_value(offset_datetime_type(x), offset_datetime_format_info{}, std::move(com), region_type{}) {} basic_value(std::chrono::system_clock::time_point x, offset_datetime_format_info fmt, std::vector com) : basic_value(offset_datetime_type(x), fmt, std::move(com), region_type{}) {} basic_value(std::chrono::system_clock::time_point x, offset_datetime_format_info fmt, std::vector com, region_type reg) : basic_value(offset_datetime_type(x), std::move(fmt), std::move(com), std::move(reg)) {} basic_value& operator=(std::chrono::system_clock::time_point x) { offset_datetime_format_info fmt; if(this->is_offset_datetime()) { fmt = this->as_offset_datetime_fmt(); } this->cleanup(); this->type_ = value_t::offset_datetime; this->region_ = region_type{}; assigner(this->offset_datetime_, offset_datetime_storage(offset_datetime_type(x), fmt)); return *this; } // }}} // constructor (array) ================================================ {{{ basic_value(array_type x) : basic_value(std::move(x), array_format_info{}, std::vector{}, region_type{}) {} basic_value(array_type x, array_format_info fmt) : basic_value(std::move(x), std::move(fmt), std::vector{}, region_type{}) {} basic_value(array_type x, std::vector com) : basic_value(std::move(x), array_format_info{}, std::move(com), region_type{}) {} basic_value(array_type x, array_format_info fmt, std::vector com) : basic_value(std::move(x), fmt, std::move(com), region_type{}) {} basic_value(array_type x, array_format_info fmt, std::vector com, region_type reg) : type_(value_t::array), array_(array_storage( detail::storage(std::move(x)), std::move(fmt) )), region_(std::move(reg)), comments_(std::move(com)) {} basic_value& operator=(array_type x) { array_format_info fmt; if(this->is_array()) { fmt = this->as_array_fmt(); } this->cleanup(); this->type_ = value_t::array; this->region_ = region_type{}; assigner(this->array_, array_storage( detail::storage(std::move(x)), std::move(fmt))); return *this; } private: template using enable_if_array_like_t = cxx::enable_if_t, cxx::negation>, cxx::negation>, #if defined(TOML11_HAS_STRING_VIEW) cxx::negation>, #endif cxx::negation>, cxx::negation> >::value, std::nullptr_t>; public: template = nullptr> basic_value(T x) : basic_value(std::move(x), array_format_info{}, std::vector{}, region_type{}) {} template = nullptr> basic_value(T x, array_format_info fmt) : basic_value(std::move(x), std::move(fmt), std::vector{}, region_type{}) {} template = nullptr> basic_value(T x, std::vector com) : basic_value(std::move(x), array_format_info{}, std::move(com), region_type{}) {} template = nullptr> basic_value(T x, array_format_info fmt, std::vector com) : basic_value(std::move(x), fmt, std::move(com), region_type{}) {} template = nullptr> basic_value(T x, array_format_info fmt, std::vector com, region_type reg) : type_(value_t::array), array_(array_storage( detail::storage(array_type( std::make_move_iterator(x.begin()), std::make_move_iterator(x.end())) ), std::move(fmt) )), region_(std::move(reg)), comments_(std::move(com)) {} template = nullptr> basic_value& operator=(T x) { array_format_info fmt; if(this->is_array()) { fmt = this->as_array_fmt(); } this->cleanup(); this->type_ = value_t::array; this->region_ = region_type{}; array_type a(std::make_move_iterator(x.begin()), std::make_move_iterator(x.end())); assigner(this->array_, array_storage( detail::storage(std::move(a)), std::move(fmt))); return *this; } // }}} // constructor (table) ================================================ {{{ basic_value(table_type x) : basic_value(std::move(x), table_format_info{}, std::vector{}, region_type{}) {} basic_value(table_type x, table_format_info fmt) : basic_value(std::move(x), std::move(fmt), std::vector{}, region_type{}) {} basic_value(table_type x, std::vector com) : basic_value(std::move(x), table_format_info{}, std::move(com), region_type{}) {} basic_value(table_type x, table_format_info fmt, std::vector com) : basic_value(std::move(x), fmt, std::move(com), region_type{}) {} basic_value(table_type x, table_format_info fmt, std::vector com, region_type reg) : type_(value_t::table), table_(table_storage( detail::storage(std::move(x)), std::move(fmt) )), region_(std::move(reg)), comments_(std::move(com)) {} basic_value& operator=(table_type x) { table_format_info fmt; if(this->is_table()) { fmt = this->as_table_fmt(); } this->cleanup(); this->type_ = value_t::table; this->region_ = region_type{}; assigner(this->table_, table_storage( detail::storage(std::move(x)), std::move(fmt))); return *this; } // table-like private: template using enable_if_table_like_t = cxx::enable_if_t>, detail::is_map, cxx::negation>, cxx::negation> >::value, std::nullptr_t>; public: template = nullptr> basic_value(T x) : basic_value(std::move(x), table_format_info{}, std::vector{}, region_type{}) {} template = nullptr> basic_value(T x, table_format_info fmt) : basic_value(std::move(x), std::move(fmt), std::vector{}, region_type{}) {} template = nullptr> basic_value(T x, std::vector com) : basic_value(std::move(x), table_format_info{}, std::move(com), region_type{}) {} template = nullptr> basic_value(T x, table_format_info fmt, std::vector com) : basic_value(std::move(x), fmt, std::move(com), region_type{}) {} template = nullptr> basic_value(T x, table_format_info fmt, std::vector com, region_type reg) : type_(value_t::table), table_(table_storage( detail::storage(table_type( std::make_move_iterator(x.begin()), std::make_move_iterator(x.end()) )), std::move(fmt) )), region_(std::move(reg)), comments_(std::move(com)) {} template = nullptr> basic_value& operator=(T x) { table_format_info fmt; if(this->is_table()) { fmt = this->as_table_fmt(); } this->cleanup(); this->type_ = value_t::table; this->region_ = region_type{}; table_type t(std::make_move_iterator(x.begin()), std::make_move_iterator(x.end())); assigner(this->table_, table_storage( detail::storage(std::move(t)), std::move(fmt))); return *this; } // }}} // constructor (user_defined) ========================================= {{{ template::value, std::nullptr_t> = nullptr> basic_value(const T& ud) : basic_value( into>::template into_toml(ud)) {} template::value, std::nullptr_t> = nullptr> basic_value(const T& ud, std::vector com) : basic_value( into>::template into_toml(ud), std::move(com)) {} template::value, std::nullptr_t> = nullptr> basic_value& operator=(const T& ud) { *this = into>::template into_toml(ud); return *this; } template, cxx::negation> >::value, std::nullptr_t> = nullptr> basic_value(const T& ud): basic_value(ud.into_toml()) {} template, cxx::negation> >::value, std::nullptr_t> = nullptr> basic_value(const T& ud, std::vector com) : basic_value(ud.into_toml(), std::move(com)) {} template, cxx::negation> >::value, std::nullptr_t> = nullptr> basic_value& operator=(const T& ud) { *this = ud.into_toml(); return *this; } template, cxx::negation> >::value, std::nullptr_t> = nullptr> basic_value(const T& ud): basic_value(ud.template into_toml()) {} template, cxx::negation> >::value, std::nullptr_t> = nullptr> basic_value(const T& ud, std::vector com) : basic_value(ud.template into_toml(), std::move(com)) {} template, cxx::negation> >::value, std::nullptr_t> = nullptr> basic_value& operator=(const T& ud) { *this = ud.template into_toml(); return *this; } // }}} // empty value with region info ======================================= {{{ // mainly for `null` extension basic_value(detail::none_t, region_type reg) noexcept : type_(value_t::empty), empty_('\0'), region_(std::move(reg)), comments_{} {} // }}} // type checking ====================================================== {{{ template, value_type>::value, std::nullptr_t> = nullptr> bool is() const noexcept { return detail::type_to_enum::value == this->type_; } bool is(value_t t) const noexcept {return t == this->type_;} bool is_empty() const noexcept {return this->is(value_t::empty );} bool is_boolean() const noexcept {return this->is(value_t::boolean );} bool is_integer() const noexcept {return this->is(value_t::integer );} bool is_floating() const noexcept {return this->is(value_t::floating );} bool is_string() const noexcept {return this->is(value_t::string );} bool is_offset_datetime() const noexcept {return this->is(value_t::offset_datetime);} bool is_local_datetime() const noexcept {return this->is(value_t::local_datetime );} bool is_local_date() const noexcept {return this->is(value_t::local_date );} bool is_local_time() const noexcept {return this->is(value_t::local_time );} bool is_array() const noexcept {return this->is(value_t::array );} bool is_table() const noexcept {return this->is(value_t::table );} bool is_array_of_tables() const noexcept { if( ! this->is_array()) {return false;} const auto& a = this->as_array(std::nothrow); // already checked. // when you define [[array.of.tables]], at least one empty table will be // assigned. In case of array of inline tables, `array_of_tables = []`, // there is no reason to consider this as an array of *tables*. // So empty array is not an array-of-tables. if(a.empty()) {return false;} // since toml v1.0.0 allows array of heterogeneous types, we need to // check all the elements. if any of the elements is not a table, it // is a heterogeneous array and cannot be expressed by `[[aot]]` form. for(const auto& e : a) { if( ! e.is_table()) {return false;} } return true; } value_t type() const noexcept {return type_;} // }}} // as_xxx (noexcept) version ========================================== {{{ template detail::enum_to_type_t> const& as(const std::nothrow_t&) const noexcept { return detail::getter::get_nothrow(*this); } template detail::enum_to_type_t>& as(const std::nothrow_t&) noexcept { return detail::getter::get_nothrow(*this); } boolean_type const& as_boolean (const std::nothrow_t&) const noexcept {return this->boolean_.value;} integer_type const& as_integer (const std::nothrow_t&) const noexcept {return this->integer_.value;} floating_type const& as_floating (const std::nothrow_t&) const noexcept {return this->floating_.value;} string_type const& as_string (const std::nothrow_t&) const noexcept {return this->string_.value;} offset_datetime_type const& as_offset_datetime(const std::nothrow_t&) const noexcept {return this->offset_datetime_.value;} local_datetime_type const& as_local_datetime (const std::nothrow_t&) const noexcept {return this->local_datetime_.value;} local_date_type const& as_local_date (const std::nothrow_t&) const noexcept {return this->local_date_.value;} local_time_type const& as_local_time (const std::nothrow_t&) const noexcept {return this->local_time_.value;} array_type const& as_array (const std::nothrow_t&) const noexcept {return this->array_.value.get();} table_type const& as_table (const std::nothrow_t&) const noexcept {return this->table_.value.get();} boolean_type & as_boolean (const std::nothrow_t&) noexcept {return this->boolean_.value;} integer_type & as_integer (const std::nothrow_t&) noexcept {return this->integer_.value;} floating_type & as_floating (const std::nothrow_t&) noexcept {return this->floating_.value;} string_type & as_string (const std::nothrow_t&) noexcept {return this->string_.value;} offset_datetime_type& as_offset_datetime(const std::nothrow_t&) noexcept {return this->offset_datetime_.value;} local_datetime_type & as_local_datetime (const std::nothrow_t&) noexcept {return this->local_datetime_.value;} local_date_type & as_local_date (const std::nothrow_t&) noexcept {return this->local_date_.value;} local_time_type & as_local_time (const std::nothrow_t&) noexcept {return this->local_time_.value;} array_type & as_array (const std::nothrow_t&) noexcept {return this->array_.value.get();} table_type & as_table (const std::nothrow_t&) noexcept {return this->table_.value.get();} // }}} // as_xxx (throw) ===================================================== {{{ template detail::enum_to_type_t> const& as() const { return detail::getter::get(*this); } template detail::enum_to_type_t>& as() { return detail::getter::get(*this); } boolean_type const& as_boolean() const { if(this->type_ != value_t::boolean) { this->throw_bad_cast("toml::value::as_boolean()", value_t::boolean); } return this->boolean_.value; } integer_type const& as_integer() const { if(this->type_ != value_t::integer) { this->throw_bad_cast("toml::value::as_integer()", value_t::integer); } return this->integer_.value; } floating_type const& as_floating() const { if(this->type_ != value_t::floating) { this->throw_bad_cast("toml::value::as_floating()", value_t::floating); } return this->floating_.value; } string_type const& as_string() const { if(this->type_ != value_t::string) { this->throw_bad_cast("toml::value::as_string()", value_t::string); } return this->string_.value; } offset_datetime_type const& as_offset_datetime() const { if(this->type_ != value_t::offset_datetime) { this->throw_bad_cast("toml::value::as_offset_datetime()", value_t::offset_datetime); } return this->offset_datetime_.value; } local_datetime_type const& as_local_datetime() const { if(this->type_ != value_t::local_datetime) { this->throw_bad_cast("toml::value::as_local_datetime()", value_t::local_datetime); } return this->local_datetime_.value; } local_date_type const& as_local_date() const { if(this->type_ != value_t::local_date) { this->throw_bad_cast("toml::value::as_local_date()", value_t::local_date); } return this->local_date_.value; } local_time_type const& as_local_time() const { if(this->type_ != value_t::local_time) { this->throw_bad_cast("toml::value::as_local_time()", value_t::local_time); } return this->local_time_.value; } array_type const& as_array() const { if(this->type_ != value_t::array) { this->throw_bad_cast("toml::value::as_array()", value_t::array); } return this->array_.value.get(); } table_type const& as_table() const { if(this->type_ != value_t::table) { this->throw_bad_cast("toml::value::as_table()", value_t::table); } return this->table_.value.get(); } // ------------------------------------------------------------------------ // nonconst reference boolean_type& as_boolean() { if(this->type_ != value_t::boolean) { this->throw_bad_cast("toml::value::as_boolean()", value_t::boolean); } return this->boolean_.value; } integer_type& as_integer() { if(this->type_ != value_t::integer) { this->throw_bad_cast("toml::value::as_integer()", value_t::integer); } return this->integer_.value; } floating_type& as_floating() { if(this->type_ != value_t::floating) { this->throw_bad_cast("toml::value::as_floating()", value_t::floating); } return this->floating_.value; } string_type& as_string() { if(this->type_ != value_t::string) { this->throw_bad_cast("toml::value::as_string()", value_t::string); } return this->string_.value; } offset_datetime_type& as_offset_datetime() { if(this->type_ != value_t::offset_datetime) { this->throw_bad_cast("toml::value::as_offset_datetime()", value_t::offset_datetime); } return this->offset_datetime_.value; } local_datetime_type& as_local_datetime() { if(this->type_ != value_t::local_datetime) { this->throw_bad_cast("toml::value::as_local_datetime()", value_t::local_datetime); } return this->local_datetime_.value; } local_date_type& as_local_date() { if(this->type_ != value_t::local_date) { this->throw_bad_cast("toml::value::as_local_date()", value_t::local_date); } return this->local_date_.value; } local_time_type& as_local_time() { if(this->type_ != value_t::local_time) { this->throw_bad_cast("toml::value::as_local_time()", value_t::local_time); } return this->local_time_.value; } array_type& as_array() { if(this->type_ != value_t::array) { this->throw_bad_cast("toml::value::as_array()", value_t::array); } return this->array_.value.get(); } table_type& as_table() { if(this->type_ != value_t::table) { this->throw_bad_cast("toml::value::as_table()", value_t::table); } return this->table_.value.get(); } // }}} // format accessors (noexcept) ======================================== {{{ template detail::enum_to_fmt_type_t const& as_fmt(const std::nothrow_t&) const noexcept { return detail::getter::get_fmt_nothrow(*this); } template detail::enum_to_fmt_type_t& as_fmt(const std::nothrow_t&) noexcept { return detail::getter::get_fmt_nothrow(*this); } boolean_format_info & as_boolean_fmt (const std::nothrow_t&) noexcept {return this->boolean_.format;} integer_format_info & as_integer_fmt (const std::nothrow_t&) noexcept {return this->integer_.format;} floating_format_info & as_floating_fmt (const std::nothrow_t&) noexcept {return this->floating_.format;} string_format_info & as_string_fmt (const std::nothrow_t&) noexcept {return this->string_.format;} offset_datetime_format_info& as_offset_datetime_fmt(const std::nothrow_t&) noexcept {return this->offset_datetime_.format;} local_datetime_format_info & as_local_datetime_fmt (const std::nothrow_t&) noexcept {return this->local_datetime_.format;} local_date_format_info & as_local_date_fmt (const std::nothrow_t&) noexcept {return this->local_date_.format;} local_time_format_info & as_local_time_fmt (const std::nothrow_t&) noexcept {return this->local_time_.format;} array_format_info & as_array_fmt (const std::nothrow_t&) noexcept {return this->array_.format;} table_format_info & as_table_fmt (const std::nothrow_t&) noexcept {return this->table_.format;} boolean_format_info const& as_boolean_fmt (const std::nothrow_t&) const noexcept {return this->boolean_.format;} integer_format_info const& as_integer_fmt (const std::nothrow_t&) const noexcept {return this->integer_.format;} floating_format_info const& as_floating_fmt (const std::nothrow_t&) const noexcept {return this->floating_.format;} string_format_info const& as_string_fmt (const std::nothrow_t&) const noexcept {return this->string_.format;} offset_datetime_format_info const& as_offset_datetime_fmt(const std::nothrow_t&) const noexcept {return this->offset_datetime_.format;} local_datetime_format_info const& as_local_datetime_fmt (const std::nothrow_t&) const noexcept {return this->local_datetime_.format;} local_date_format_info const& as_local_date_fmt (const std::nothrow_t&) const noexcept {return this->local_date_.format;} local_time_format_info const& as_local_time_fmt (const std::nothrow_t&) const noexcept {return this->local_time_.format;} array_format_info const& as_array_fmt (const std::nothrow_t&) const noexcept {return this->array_.format;} table_format_info const& as_table_fmt (const std::nothrow_t&) const noexcept {return this->table_.format;} // }}} // format accessors (throw) =========================================== {{{ template detail::enum_to_fmt_type_t const& as_fmt() const { return detail::getter::get_fmt(*this); } template detail::enum_to_fmt_type_t& as_fmt() { return detail::getter::get_fmt(*this); } boolean_format_info const& as_boolean_fmt() const { if(this->type_ != value_t::boolean) { this->throw_bad_cast("toml::value::as_boolean_fmt()", value_t::boolean); } return this->boolean_.format; } integer_format_info const& as_integer_fmt() const { if(this->type_ != value_t::integer) { this->throw_bad_cast("toml::value::as_integer_fmt()", value_t::integer); } return this->integer_.format; } floating_format_info const& as_floating_fmt() const { if(this->type_ != value_t::floating) { this->throw_bad_cast("toml::value::as_floating_fmt()", value_t::floating); } return this->floating_.format; } string_format_info const& as_string_fmt() const { if(this->type_ != value_t::string) { this->throw_bad_cast("toml::value::as_string_fmt()", value_t::string); } return this->string_.format; } offset_datetime_format_info const& as_offset_datetime_fmt() const { if(this->type_ != value_t::offset_datetime) { this->throw_bad_cast("toml::value::as_offset_datetime_fmt()", value_t::offset_datetime); } return this->offset_datetime_.format; } local_datetime_format_info const& as_local_datetime_fmt() const { if(this->type_ != value_t::local_datetime) { this->throw_bad_cast("toml::value::as_local_datetime_fmt()", value_t::local_datetime); } return this->local_datetime_.format; } local_date_format_info const& as_local_date_fmt() const { if(this->type_ != value_t::local_date) { this->throw_bad_cast("toml::value::as_local_date_fmt()", value_t::local_date); } return this->local_date_.format; } local_time_format_info const& as_local_time_fmt() const { if(this->type_ != value_t::local_time) { this->throw_bad_cast("toml::value::as_local_time_fmt()", value_t::local_time); } return this->local_time_.format; } array_format_info const& as_array_fmt() const { if(this->type_ != value_t::array) { this->throw_bad_cast("toml::value::as_array_fmt()", value_t::array); } return this->array_.format; } table_format_info const& as_table_fmt() const { if(this->type_ != value_t::table) { this->throw_bad_cast("toml::value::as_table_fmt()", value_t::table); } return this->table_.format; } // ------------------------------------------------------------------------ // nonconst reference boolean_format_info& as_boolean_fmt() { if(this->type_ != value_t::boolean) { this->throw_bad_cast("toml::value::as_boolean_fmt()", value_t::boolean); } return this->boolean_.format; } integer_format_info& as_integer_fmt() { if(this->type_ != value_t::integer) { this->throw_bad_cast("toml::value::as_integer_fmt()", value_t::integer); } return this->integer_.format; } floating_format_info& as_floating_fmt() { if(this->type_ != value_t::floating) { this->throw_bad_cast("toml::value::as_floating_fmt()", value_t::floating); } return this->floating_.format; } string_format_info& as_string_fmt() { if(this->type_ != value_t::string) { this->throw_bad_cast("toml::value::as_string_fmt()", value_t::string); } return this->string_.format; } offset_datetime_format_info& as_offset_datetime_fmt() { if(this->type_ != value_t::offset_datetime) { this->throw_bad_cast("toml::value::as_offset_datetime_fmt()", value_t::offset_datetime); } return this->offset_datetime_.format; } local_datetime_format_info& as_local_datetime_fmt() { if(this->type_ != value_t::local_datetime) { this->throw_bad_cast("toml::value::as_local_datetime_fmt()", value_t::local_datetime); } return this->local_datetime_.format; } local_date_format_info& as_local_date_fmt() { if(this->type_ != value_t::local_date) { this->throw_bad_cast("toml::value::as_local_date_fmt()", value_t::local_date); } return this->local_date_.format; } local_time_format_info& as_local_time_fmt() { if(this->type_ != value_t::local_time) { this->throw_bad_cast("toml::value::as_local_time_fmt()", value_t::local_time); } return this->local_time_.format; } array_format_info& as_array_fmt() { if(this->type_ != value_t::array) { this->throw_bad_cast("toml::value::as_array_fmt()", value_t::array); } return this->array_.format; } table_format_info& as_table_fmt() { if(this->type_ != value_t::table) { this->throw_bad_cast("toml::value::as_table_fmt()", value_t::table); } return this->table_.format; } // }}} // table accessors ==================================================== {{{ value_type& at(const key_type& k) { if(!this->is_table()) { this->throw_bad_cast("toml::value::at(key_type)", value_t::table); } auto& table = this->as_table(std::nothrow); const auto found = table.find(k); if(found == table.end()) { this->throw_key_not_found_error("toml::value::at", k); } assert(found->first == k); return found->second; } value_type const& at(const key_type& k) const { if(!this->is_table()) { this->throw_bad_cast("toml::value::at(key_type)", value_t::table); } const auto& table = this->as_table(std::nothrow); const auto found = table.find(k); if(found == table.end()) { this->throw_key_not_found_error("toml::value::at", k); } assert(found->first == k); return found->second; } value_type& operator[](const key_type& k) { if(this->is_empty()) { (*this) = table_type{}; } else if( ! this->is_table()) // initialized, but not a table { this->throw_bad_cast("toml::value::operator[](key_type)", value_t::table); } return (this->as_table(std::nothrow))[k]; } std::size_t count(const key_type& k) const { if(!this->is_table()) { this->throw_bad_cast("toml::value::count(key_type)", value_t::table); } return this->as_table(std::nothrow).count(k); } bool contains(const key_type& k) const { if(!this->is_table()) { this->throw_bad_cast("toml::value::contains(key_type)", value_t::table); } const auto& table = this->as_table(std::nothrow); return table.find(k) != table.end(); } // }}} // array accessors ==================================================== {{{ value_type& at(const std::size_t idx) { if(!this->is_array()) { this->throw_bad_cast("toml::value::at(idx)", value_t::array); } auto& ar = this->as_array(std::nothrow); if(ar.size() <= idx) { std::ostringstream oss; oss << "actual length (" << ar.size() << ") is shorter than the specified index (" << idx << ")."; throw std::out_of_range(format_error( "toml::value::at(idx): no element corresponding to the index", this->location(), oss.str() )); } return ar.at(idx); } value_type const& at(const std::size_t idx) const { if(!this->is_array()) { this->throw_bad_cast("toml::value::at(idx)", value_t::array); } const auto& ar = this->as_array(std::nothrow); if(ar.size() <= idx) { std::ostringstream oss; oss << "actual length (" << ar.size() << ") is shorter than the specified index (" << idx << ")."; throw std::out_of_range(format_error( "toml::value::at(idx): no element corresponding to the index", this->location(), oss.str() )); } return ar.at(idx); } value_type& operator[](const std::size_t idx) noexcept { // no check... return this->as_array(std::nothrow)[idx]; } value_type const& operator[](const std::size_t idx) const noexcept { // no check... return this->as_array(std::nothrow)[idx]; } void push_back(const value_type& x) { if(!this->is_array()) { this->throw_bad_cast("toml::value::push_back(idx)", value_t::array); } this->as_array(std::nothrow).push_back(x); return; } void push_back(value_type&& x) { if(!this->is_array()) { this->throw_bad_cast("toml::value::push_back(idx)", value_t::array); } this->as_array(std::nothrow).push_back(std::move(x)); return; } template value_type& emplace_back(Ts&& ... args) { if(!this->is_array()) { this->throw_bad_cast("toml::value::emplace_back(idx)", value_t::array); } auto& ar = this->as_array(std::nothrow); ar.emplace_back(std::forward(args) ...); return ar.back(); } std::size_t size() const { switch(this->type_) { case value_t::array: { return this->as_array(std::nothrow).size(); } case value_t::table: { return this->as_table(std::nothrow).size(); } case value_t::string: { return this->as_string(std::nothrow).size(); } default: { throw type_error(format_error( "toml::value::size(): bad_cast to container types", this->location(), "the actual type is " + to_string(this->type_) ), this->location()); } } } // }}} source_location location() const { return source_location(this->region_); } comment_type const& comments() const noexcept {return this->comments_;} comment_type& comments() noexcept {return this->comments_;} private: // private helper functions =========================================== {{{ void cleanup() noexcept { switch(this->type_) { case value_t::boolean : { boolean_ .~boolean_storage (); break; } case value_t::integer : { integer_ .~integer_storage (); break; } case value_t::floating : { floating_ .~floating_storage (); break; } case value_t::string : { string_ .~string_storage (); break; } case value_t::offset_datetime : { offset_datetime_.~offset_datetime_storage (); break; } case value_t::local_datetime : { local_datetime_ .~local_datetime_storage (); break; } case value_t::local_date : { local_date_ .~local_date_storage (); break; } case value_t::local_time : { local_time_ .~local_time_storage (); break; } case value_t::array : { array_ .~array_storage (); break; } case value_t::table : { table_ .~table_storage (); break; } default : { break; } } this->type_ = value_t::empty; return; } template static void assigner(T& dst, U&& v) { const auto tmp = ::new(std::addressof(dst)) T(std::forward(v)); assert(tmp == std::addressof(dst)); (void)tmp; } [[noreturn]] void throw_bad_cast(const std::string& funcname, const value_t ty) const { throw type_error(format_error(detail::make_type_error(*this, funcname, ty)), this->location()); } [[noreturn]] void throw_key_not_found_error(const std::string& funcname, const key_type& key) const { throw std::out_of_range(format_error( detail::make_not_found_error(*this, funcname, key))); } template friend void detail::change_region_of_value(basic_value&, const basic_value&); template friend class basic_value; // }}} private: using boolean_storage = detail::value_with_format; using integer_storage = detail::value_with_format; using floating_storage = detail::value_with_format; using string_storage = detail::value_with_format; using offset_datetime_storage = detail::value_with_format; using local_datetime_storage = detail::value_with_format; using local_date_storage = detail::value_with_format; using local_time_storage = detail::value_with_format; using array_storage = detail::value_with_format, array_format_info >; using table_storage = detail::value_with_format, table_format_info >; private: value_t type_; union { char empty_; // the smallest type boolean_storage boolean_; integer_storage integer_; floating_storage floating_; string_storage string_; offset_datetime_storage offset_datetime_; local_datetime_storage local_datetime_; local_date_storage local_date_; local_time_storage local_time_; array_storage array_; table_storage table_; }; region_type region_; comment_type comments_; }; template bool operator==(const basic_value& lhs, const basic_value& rhs) { if(lhs.type() != rhs.type()) {return false;} if(lhs.comments() != rhs.comments()) {return false;} switch(lhs.type()) { case value_t::boolean : { return lhs.as_boolean() == rhs.as_boolean(); } case value_t::integer : { return lhs.as_integer() == rhs.as_integer(); } case value_t::floating : { return lhs.as_floating() == rhs.as_floating(); } case value_t::string : { return lhs.as_string() == rhs.as_string(); } case value_t::offset_datetime: { return lhs.as_offset_datetime() == rhs.as_offset_datetime(); } case value_t::local_datetime: { return lhs.as_local_datetime() == rhs.as_local_datetime(); } case value_t::local_date: { return lhs.as_local_date() == rhs.as_local_date(); } case value_t::local_time: { return lhs.as_local_time() == rhs.as_local_time(); } case value_t::array : { return lhs.as_array() == rhs.as_array(); } case value_t::table : { return lhs.as_table() == rhs.as_table(); } case value_t::empty : {return true; } default: {return false;} } } template bool operator!=(const basic_value& lhs, const basic_value& rhs) { return !(lhs == rhs); } template cxx::enable_if_t::array_type>, detail::is_comparable::table_type> >::value, bool> operator<(const basic_value& lhs, const basic_value& rhs) { if(lhs.type() != rhs.type()) { return (lhs.type() < rhs.type()); } switch(lhs.type()) { case value_t::boolean : { return lhs.as_boolean() < rhs.as_boolean() || (lhs.as_boolean() == rhs.as_boolean() && lhs.comments() < rhs.comments()); } case value_t::integer : { return lhs.as_integer() < rhs.as_integer() || (lhs.as_integer() == rhs.as_integer() && lhs.comments() < rhs.comments()); } case value_t::floating : { return lhs.as_floating() < rhs.as_floating() || (lhs.as_floating() == rhs.as_floating() && lhs.comments() < rhs.comments()); } case value_t::string : { return lhs.as_string() < rhs.as_string() || (lhs.as_string() == rhs.as_string() && lhs.comments() < rhs.comments()); } case value_t::offset_datetime: { return lhs.as_offset_datetime() < rhs.as_offset_datetime() || (lhs.as_offset_datetime() == rhs.as_offset_datetime() && lhs.comments() < rhs.comments()); } case value_t::local_datetime: { return lhs.as_local_datetime() < rhs.as_local_datetime() || (lhs.as_local_datetime() == rhs.as_local_datetime() && lhs.comments() < rhs.comments()); } case value_t::local_date: { return lhs.as_local_date() < rhs.as_local_date() || (lhs.as_local_date() == rhs.as_local_date() && lhs.comments() < rhs.comments()); } case value_t::local_time: { return lhs.as_local_time() < rhs.as_local_time() || (lhs.as_local_time() == rhs.as_local_time() && lhs.comments() < rhs.comments()); } case value_t::array : { return lhs.as_array() < rhs.as_array() || (lhs.as_array() == rhs.as_array() && lhs.comments() < rhs.comments()); } case value_t::table : { return lhs.as_table() < rhs.as_table() || (lhs.as_table() == rhs.as_table() && lhs.comments() < rhs.comments()); } case value_t::empty : { return lhs.comments() < rhs.comments(); } default: { return lhs.comments() < rhs.comments(); } } } template cxx::enable_if_t::array_type>, detail::is_comparable::table_type> >::value, bool> operator<=(const basic_value& lhs, const basic_value& rhs) { return (lhs < rhs) || (lhs == rhs); } template cxx::enable_if_t::array_type>, detail::is_comparable::table_type> >::value, bool> operator>(const basic_value& lhs, const basic_value& rhs) { return !(lhs <= rhs); } template cxx::enable_if_t::array_type>, detail::is_comparable::table_type> >::value, bool> operator>=(const basic_value& lhs, const basic_value& rhs) { return !(lhs < rhs); } // error_info helper namespace detail { template error_info make_error_info_rec(error_info e, const basic_value& v, std::string msg, Ts&& ... tail) { return make_error_info_rec(std::move(e), v.location(), std::move(msg), std::forward(tail)...); } } // detail template error_info make_error_info( std::string title, const basic_value& v, std::string msg, Ts&& ... tail) { return make_error_info(std::move(title), v.location(), std::move(msg), std::forward(tail)...); } template std::string format_error(std::string title, const basic_value& v, std::string msg, Ts&& ... tail) { return format_error(std::move(title), v.location(), std::move(msg), std::forward(tail)...); } namespace detail { template error_info make_type_error(const basic_value& v, const std::string& fname, const value_t ty) { return make_error_info(fname + ": bad_cast to " + to_string(ty), v.location(), "the actual type is " + to_string(v.type())); } template error_info make_not_found_error(const basic_value& v, const std::string& fname, const typename basic_value::key_type& key) { const auto loc = v.location(); const std::string title = fname + ": key \"" + string_conv(key) + "\" not found"; std::vector> locs; if( ! loc.is_ok()) { return error_info(title, locs); } if(loc.first_line_number() == 1 && loc.first_column_number() == 1 && loc.length() == 1) { // The top-level table has its region at the 0th character of the file. // That means that, in the case when a key is not found in the top-level // table, the error message points to the first character. If the file has // the first table at the first line, the error message would be like this. // ```console // [error] key "a" not found // --> example.toml // | // 1 | [table] // | ^------ in this table // ``` // It actually points to the top-level table at the first character, not // `[table]`. But it is too confusing. To avoid the confusion, the error // message should explicitly say "key not found in the top-level table". locs.emplace_back(v.location(), "at the top-level table"); } else { locs.emplace_back(v.location(), "in this table"); } return error_info(title, locs); } #define TOML11_DETAIL_GENERATE_COMPTIME_GETTER(ty) \ template \ struct getter \ { \ using value_type = basic_value; \ using result_type = enum_to_type_t; \ using format_type = enum_to_fmt_type_t; \ \ static result_type& get(value_type& v) \ { \ return v.as_ ## ty(); \ } \ static result_type const& get(const value_type& v) \ { \ return v.as_ ## ty(); \ } \ \ static result_type& get_nothrow(value_type& v) noexcept \ { \ return v.as_ ## ty(std::nothrow); \ } \ static result_type const& get_nothrow(const value_type& v) noexcept \ { \ return v.as_ ## ty(std::nothrow); \ } \ \ static format_type& get_fmt(value_type& v) \ { \ return v.as_ ## ty ## _fmt(); \ } \ static format_type const& get_fmt(const value_type& v) \ { \ return v.as_ ## ty ## _fmt(); \ } \ \ static format_type& get_fmt_nothrow(value_type& v) noexcept \ { \ return v.as_ ## ty ## _fmt(std::nothrow); \ } \ static format_type const& get_fmt_nothrow(const value_type& v) noexcept \ { \ return v.as_ ## ty ## _fmt(std::nothrow); \ } \ }; TOML11_DETAIL_GENERATE_COMPTIME_GETTER(boolean ) TOML11_DETAIL_GENERATE_COMPTIME_GETTER(integer ) TOML11_DETAIL_GENERATE_COMPTIME_GETTER(floating ) TOML11_DETAIL_GENERATE_COMPTIME_GETTER(string ) TOML11_DETAIL_GENERATE_COMPTIME_GETTER(offset_datetime) TOML11_DETAIL_GENERATE_COMPTIME_GETTER(local_datetime ) TOML11_DETAIL_GENERATE_COMPTIME_GETTER(local_date ) TOML11_DETAIL_GENERATE_COMPTIME_GETTER(local_time ) TOML11_DETAIL_GENERATE_COMPTIME_GETTER(array ) TOML11_DETAIL_GENERATE_COMPTIME_GETTER(table ) #undef TOML11_DETAIL_GENERATE_COMPTIME_GETTER template void change_region_of_value(basic_value& dst, const basic_value& src) { dst.region_ = std::move(src.region_); return; } } // namespace detail } // namespace toml #endif // TOML11_VALUE_HPP toml11-4.1.0/include/toml11/value_t.hpp000066400000000000000000000003611464712047700175550ustar00rootroot00000000000000#ifndef TOML11_VALUE_T_HPP #define TOML11_VALUE_T_HPP #include "fwd/value_t_fwd.hpp" // IWYU pragma: export #if ! defined(TOML11_COMPILE_SOURCES) #include "impl/value_t_impl.hpp" // IWYU pragma: export #endif #endif // TOML11_VALUE_T_HPP toml11-4.1.0/include/toml11/version.hpp000066400000000000000000000065511464712047700176120ustar00rootroot00000000000000#ifndef TOML11_VERSION_HPP #define TOML11_VERSION_HPP #define TOML11_VERSION_MAJOR 4 #define TOML11_VERSION_MINOR 1 #define TOML11_VERSION_PATCH 0 #ifndef __cplusplus # error "__cplusplus is not defined" #endif // Since MSVC does not define `__cplusplus` correctly unless you pass // `/Zc:__cplusplus` when compiling, the workaround macros are added. // // The value of `__cplusplus` macro is defined in the C++ standard spec, but // MSVC ignores the value, maybe because of backward compatibility. Instead, // MSVC defines _MSVC_LANG that has the same value as __cplusplus defined in // the C++ standard. So we check if _MSVC_LANG is defined before using `__cplusplus`. // // FYI: https://docs.microsoft.com/en-us/cpp/build/reference/zc-cplusplus?view=msvc-170 // https://docs.microsoft.com/en-us/cpp/preprocessor/predefined-macros?view=msvc-170 // #if defined(_MSVC_LANG) && defined(_MSC_VER) && 190024210 <= _MSC_FULL_VER # define TOML11_CPLUSPLUS_STANDARD_VERSION _MSVC_LANG #else # define TOML11_CPLUSPLUS_STANDARD_VERSION __cplusplus #endif #if TOML11_CPLUSPLUS_STANDARD_VERSION < 201103L # error "toml11 requires C++11 or later." #endif #if ! defined(__has_include) # define __has_include(x) 0 #endif #if ! defined(__has_cpp_attribute) # define __has_cpp_attribute(x) 0 #endif #if ! defined(__has_builtin) # define __has_builtin(x) 0 #endif // hard to remember #ifndef TOML11_CXX14_VALUE #define TOML11_CXX14_VALUE 201402L #endif//TOML11_CXX14_VALUE #ifndef TOML11_CXX17_VALUE #define TOML11_CXX17_VALUE 201703L #endif//TOML11_CXX17_VALUE #ifndef TOML11_CXX20_VALUE #define TOML11_CXX20_VALUE 202002L #endif//TOML11_CXX20_VALUE #if defined(__cpp_char8_t) # if __cpp_char8_t >= 201811L # define TOML11_HAS_CHAR8_T 1 # endif #endif #if TOML11_CPLUSPLUS_STANDARD_VERSION >= TOML11_CXX17_VALUE # if __has_include() # define TOML11_HAS_STRING_VIEW 1 # endif #endif #ifndef TOML11_DISABLE_STD_FILESYSTEM # if TOML11_CPLUSPLUS_STANDARD_VERSION >= TOML11_CXX17_VALUE # if __has_include() # define TOML11_HAS_FILESYSTEM 1 # endif # endif #endif #if defined(TOML11_COMPILE_SOURCES) # define TOML11_INLINE #else # define TOML11_INLINE inline #endif namespace toml { inline const char* license_notice() noexcept { return R"(The MIT License (MIT) Copyright (c) 2017-now Toru Niina Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.)"; } } // toml #endif // TOML11_VERSION_HPP toml11-4.1.0/include/toml11/visit.hpp000066400000000000000000000076411464712047700172640ustar00rootroot00000000000000#ifndef TOML11_VISIT_HPP #define TOML11_VISIT_HPP #include "exception.hpp" #include "traits.hpp" #include "value.hpp" namespace toml { template cxx::return_type_of_t::boolean_type&> visit(Visitor&& visitor, const basic_value& v) { switch(v.type()) { case value_t::boolean : {return visitor(v.as_boolean ());} case value_t::integer : {return visitor(v.as_integer ());} case value_t::floating : {return visitor(v.as_floating ());} case value_t::string : {return visitor(v.as_string ());} case value_t::offset_datetime: {return visitor(v.as_offset_datetime());} case value_t::local_datetime : {return visitor(v.as_local_datetime ());} case value_t::local_date : {return visitor(v.as_local_date ());} case value_t::local_time : {return visitor(v.as_local_time ());} case value_t::array : {return visitor(v.as_array ());} case value_t::table : {return visitor(v.as_table ());} case value_t::empty : break; default: break; } throw type_error(format_error("[error] toml::visit: toml::basic_value " "does not have any valid type.", v.location(), "here"), v.location()); } template cxx::return_type_of_t::boolean_type&> visit(Visitor&& visitor, basic_value& v) { switch(v.type()) { case value_t::boolean : {return visitor(v.as_boolean ());} case value_t::integer : {return visitor(v.as_integer ());} case value_t::floating : {return visitor(v.as_floating ());} case value_t::string : {return visitor(v.as_string ());} case value_t::offset_datetime: {return visitor(v.as_offset_datetime());} case value_t::local_datetime : {return visitor(v.as_local_datetime ());} case value_t::local_date : {return visitor(v.as_local_date ());} case value_t::local_time : {return visitor(v.as_local_time ());} case value_t::array : {return visitor(v.as_array ());} case value_t::table : {return visitor(v.as_table ());} case value_t::empty : break; default: break; } throw type_error(format_error("[error] toml::visit: toml::basic_value " "does not have any valid type.", v.location(), "here"), v.location()); } template cxx::return_type_of_t::boolean_type&&> visit(Visitor&& visitor, basic_value&& v) { switch(v.type()) { case value_t::boolean : {return visitor(std::move(v.as_boolean ()));} case value_t::integer : {return visitor(std::move(v.as_integer ()));} case value_t::floating : {return visitor(std::move(v.as_floating ()));} case value_t::string : {return visitor(std::move(v.as_string ()));} case value_t::offset_datetime: {return visitor(std::move(v.as_offset_datetime()));} case value_t::local_datetime : {return visitor(std::move(v.as_local_datetime ()));} case value_t::local_date : {return visitor(std::move(v.as_local_date ()));} case value_t::local_time : {return visitor(std::move(v.as_local_time ()));} case value_t::array : {return visitor(std::move(v.as_array ()));} case value_t::table : {return visitor(std::move(v.as_table ()));} case value_t::empty : break; default: break; } throw type_error(format_error("[error] toml::visit: toml::basic_value " "does not have any valid type.", v.location(), "here"), v.location()); } } // toml #endif // TOML11_VISIT_HPP toml11-4.1.0/include/toml_fwd.hpp000066400000000000000000000035531464712047700166220ustar00rootroot00000000000000#ifndef TOML11_TOML_FWD_HPP #define TOML11_TOML_FWD_HPP // IWYU pragma: begin_exports #include "toml11/version.hpp" #include "toml11/compat.hpp" #include "toml11/conversion.hpp" #include "toml11/from.hpp" #include "toml11/into.hpp" // IWYU pragma: end_exports #include #ifdef TOML11_COLORIZE_ERROR_MESSAGE #define TOML11_ERROR_MESSAGE_COLORIZED true #else #define TOML11_ERROR_MESSAGE_COLORIZED false #endif namespace toml { class discard_comments; class preserve_comments; enum class month_t : std::uint8_t; struct local_date; struct local_time; struct time_offset; struct local_datetime; struct offset_datetime; struct error_info; struct exception; enum class indent_char : std::uint8_t; enum class integer_format : std::uint8_t; enum class floating_format : std::uint8_t; enum class string_format : std::uint8_t; enum class datetime_delimiter_kind : std::uint8_t; enum class array_format : std::uint8_t; enum class table_format : std::uint8_t; struct boolean_format_info; struct integer_format_info; struct floating_format_info; struct string_format_info; struct offset_datetime_format_info; struct local_datetime_format_info; struct local_date_format_info; struct local_time_format_info; struct array_format_info; struct table_format_info; template class ordered_map; struct syntax_error; struct file_io_error; struct bad_result_access; template struct success; template struct failure; template struct result; struct source_location; struct semantic_version; struct spec; template class basic_value; struct type_error; struct type_config; using value = basic_value; struct ordered_type_config; using ordered_value = basic_value; enum class value_t : std::uint8_t; } // toml #endif// TOML11_TOML_HPP toml11-4.1.0/single_include/000077500000000000000000000000001464712047700156315ustar00rootroot00000000000000toml11-4.1.0/single_include/toml.hpp000066400000000000000000021137651464712047700173340ustar00rootroot00000000000000#ifndef TOML11_VERSION_HPP #define TOML11_VERSION_HPP #define TOML11_VERSION_MAJOR 4 #define TOML11_VERSION_MINOR 1 #define TOML11_VERSION_PATCH 0 #ifndef __cplusplus # error "__cplusplus is not defined" #endif // Since MSVC does not define `__cplusplus` correctly unless you pass // `/Zc:__cplusplus` when compiling, the workaround macros are added. // // The value of `__cplusplus` macro is defined in the C++ standard spec, but // MSVC ignores the value, maybe because of backward compatibility. Instead, // MSVC defines _MSVC_LANG that has the same value as __cplusplus defined in // the C++ standard. So we check if _MSVC_LANG is defined before using `__cplusplus`. // // FYI: https://docs.microsoft.com/en-us/cpp/build/reference/zc-cplusplus?view=msvc-170 // https://docs.microsoft.com/en-us/cpp/preprocessor/predefined-macros?view=msvc-170 // #if defined(_MSVC_LANG) && defined(_MSC_VER) && 190024210 <= _MSC_FULL_VER # define TOML11_CPLUSPLUS_STANDARD_VERSION _MSVC_LANG #else # define TOML11_CPLUSPLUS_STANDARD_VERSION __cplusplus #endif #if TOML11_CPLUSPLUS_STANDARD_VERSION < 201103L # error "toml11 requires C++11 or later." #endif #if ! defined(__has_include) # define __has_include(x) 0 #endif #if ! defined(__has_cpp_attribute) # define __has_cpp_attribute(x) 0 #endif #if ! defined(__has_builtin) # define __has_builtin(x) 0 #endif // hard to remember #ifndef TOML11_CXX14_VALUE #define TOML11_CXX14_VALUE 201402L #endif//TOML11_CXX14_VALUE #ifndef TOML11_CXX17_VALUE #define TOML11_CXX17_VALUE 201703L #endif//TOML11_CXX17_VALUE #ifndef TOML11_CXX20_VALUE #define TOML11_CXX20_VALUE 202002L #endif//TOML11_CXX20_VALUE #if defined(__cpp_char8_t) # if __cpp_char8_t >= 201811L # define TOML11_HAS_CHAR8_T 1 # endif #endif #if TOML11_CPLUSPLUS_STANDARD_VERSION >= TOML11_CXX17_VALUE # if __has_include() # define TOML11_HAS_STRING_VIEW 1 # endif #endif #ifndef TOML11_DISABLE_STD_FILESYSTEM # if TOML11_CPLUSPLUS_STANDARD_VERSION >= TOML11_CXX17_VALUE # if __has_include() # define TOML11_HAS_FILESYSTEM 1 # endif # endif #endif #if defined(TOML11_COMPILE_SOURCES) # define TOML11_INLINE #else # define TOML11_INLINE inline #endif namespace toml { inline const char* license_notice() noexcept { return R"(The MIT License (MIT) Copyright (c) 2017-now Toru Niina Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.)"; } } // toml #endif // TOML11_VERSION_HPP #ifndef TOML11_FORMAT_HPP #define TOML11_FORMAT_HPP #ifndef TOML11_FORMAT_FWD_HPP #define TOML11_FORMAT_FWD_HPP #include #include #include #include #include namespace toml { // toml types with serialization info enum class indent_char : std::uint8_t { space, // use space tab, // use tab none // no indent }; std::ostream& operator<<(std::ostream& os, const indent_char& c); std::string to_string(const indent_char c); // ---------------------------------------------------------------------------- // boolean struct boolean_format_info { // nothing, for now }; inline bool operator==(const boolean_format_info&, const boolean_format_info&) noexcept { return true; } inline bool operator!=(const boolean_format_info&, const boolean_format_info&) noexcept { return false; } // ---------------------------------------------------------------------------- // integer enum class integer_format : std::uint8_t { dec = 0, bin = 1, oct = 2, hex = 3, }; std::ostream& operator<<(std::ostream& os, const integer_format f); std::string to_string(const integer_format); struct integer_format_info { integer_format fmt = integer_format::dec; bool uppercase = true; // hex with uppercase std::size_t width = 0; // minimal width (may exceed) std::size_t spacer = 0; // position of `_` (if 0, no spacer) std::string suffix = ""; // _suffix (library extension) }; bool operator==(const integer_format_info&, const integer_format_info&) noexcept; bool operator!=(const integer_format_info&, const integer_format_info&) noexcept; // ---------------------------------------------------------------------------- // floating enum class floating_format : std::uint8_t { defaultfloat = 0, fixed = 1, // does not include exponential part scientific = 2, // always include exponential part hex = 3 // hexfloat extension }; std::ostream& operator<<(std::ostream& os, const floating_format f); std::string to_string(const floating_format); struct floating_format_info { floating_format fmt = floating_format::defaultfloat; std::size_t prec = 0; // precision (if 0, use the default) std::string suffix = ""; // 1.0e+2_suffix (library extension) }; bool operator==(const floating_format_info&, const floating_format_info&) noexcept; bool operator!=(const floating_format_info&, const floating_format_info&) noexcept; // ---------------------------------------------------------------------------- // string enum class string_format : std::uint8_t { basic = 0, literal = 1, multiline_basic = 2, multiline_literal = 3 }; std::ostream& operator<<(std::ostream& os, const string_format f); std::string to_string(const string_format); struct string_format_info { string_format fmt = string_format::basic; bool start_with_newline = false; }; bool operator==(const string_format_info&, const string_format_info&) noexcept; bool operator!=(const string_format_info&, const string_format_info&) noexcept; // ---------------------------------------------------------------------------- // datetime enum class datetime_delimiter_kind : std::uint8_t { upper_T = 0, lower_t = 1, space = 2, }; std::ostream& operator<<(std::ostream& os, const datetime_delimiter_kind d); std::string to_string(const datetime_delimiter_kind); struct offset_datetime_format_info { datetime_delimiter_kind delimiter = datetime_delimiter_kind::upper_T; bool has_seconds = true; std::size_t subsecond_precision = 6; // [us] }; bool operator==(const offset_datetime_format_info&, const offset_datetime_format_info&) noexcept; bool operator!=(const offset_datetime_format_info&, const offset_datetime_format_info&) noexcept; struct local_datetime_format_info { datetime_delimiter_kind delimiter = datetime_delimiter_kind::upper_T; bool has_seconds = true; std::size_t subsecond_precision = 6; // [us] }; bool operator==(const local_datetime_format_info&, const local_datetime_format_info&) noexcept; bool operator!=(const local_datetime_format_info&, const local_datetime_format_info&) noexcept; struct local_date_format_info { // nothing, for now }; bool operator==(const local_date_format_info&, const local_date_format_info&) noexcept; bool operator!=(const local_date_format_info&, const local_date_format_info&) noexcept; struct local_time_format_info { bool has_seconds = true; std::size_t subsecond_precision = 6; // [us] }; bool operator==(const local_time_format_info&, const local_time_format_info&) noexcept; bool operator!=(const local_time_format_info&, const local_time_format_info&) noexcept; // ---------------------------------------------------------------------------- // array enum class array_format : std::uint8_t { default_format = 0, oneline = 1, multiline = 2, array_of_tables = 3 // [[format.in.this.way]] }; std::ostream& operator<<(std::ostream& os, const array_format f); std::string to_string(const array_format); struct array_format_info { array_format fmt = array_format::default_format; indent_char indent_type = indent_char::space; std::int32_t body_indent = 4; // indent in case of multiline std::int32_t closing_indent = 0; // indent of `]` }; bool operator==(const array_format_info&, const array_format_info&) noexcept; bool operator!=(const array_format_info&, const array_format_info&) noexcept; // ---------------------------------------------------------------------------- // table enum class table_format : std::uint8_t { multiline = 0, // [foo] \n bar = "baz" oneline = 1, // foo = {bar = "baz"} dotted = 2, // foo.bar = "baz" multiline_oneline = 3, // foo = { \n bar = "baz" \n } implicit = 4 // [x] defined by [x.y.z]. skip in serializer. }; std::ostream& operator<<(std::ostream& os, const table_format f); std::string to_string(const table_format); struct table_format_info { table_format fmt = table_format::multiline; indent_char indent_type = indent_char::space; std::int32_t body_indent = 0; // indent of values std::int32_t name_indent = 0; // indent of [table] std::int32_t closing_indent = 0; // in case of {inline-table} }; bool operator==(const table_format_info&, const table_format_info&) noexcept; bool operator!=(const table_format_info&, const table_format_info&) noexcept; // ---------------------------------------------------------------------------- // wrapper namespace detail { template struct value_with_format { using value_type = T; using format_type = F; value_with_format() = default; ~value_with_format() = default; value_with_format(const value_with_format&) = default; value_with_format(value_with_format&&) = default; value_with_format& operator=(const value_with_format&) = default; value_with_format& operator=(value_with_format&&) = default; value_with_format(value_type v, format_type f) : value{std::move(v)}, format{std::move(f)} {} template value_with_format(value_with_format other) : value{std::move(other.value)}, format{std::move(other.format)} {} value_type value; format_type format; }; } // detail } // namespace toml #endif // TOML11_FORMAT_FWD_HPP #if ! defined(TOML11_COMPILE_SOURCES) #ifndef TOML11_FORMAT_IMPL_HPP #define TOML11_FORMAT_IMPL_HPP #include #include namespace toml { // toml types with serialization info TOML11_INLINE std::ostream& operator<<(std::ostream& os, const indent_char& c) { switch(c) { case indent_char::space: {os << "space" ; break;} case indent_char::tab: {os << "tab" ; break;} case indent_char::none: {os << "none" ; break;} default: { os << "unknown indent char: " << static_cast(c); } } return os; } TOML11_INLINE std::string to_string(const indent_char c) { std::ostringstream oss; oss << c; return oss.str(); } // ---------------------------------------------------------------------------- // boolean // ---------------------------------------------------------------------------- // integer TOML11_INLINE std::ostream& operator<<(std::ostream& os, const integer_format f) { switch(f) { case integer_format::dec: {os << "dec"; break;} case integer_format::bin: {os << "bin"; break;} case integer_format::oct: {os << "oct"; break;} case integer_format::hex: {os << "hex"; break;} default: { os << "unknown integer_format: " << static_cast(f); break; } } return os; } TOML11_INLINE std::string to_string(const integer_format c) { std::ostringstream oss; oss << c; return oss.str(); } TOML11_INLINE bool operator==(const integer_format_info& lhs, const integer_format_info& rhs) noexcept { return lhs.fmt == rhs.fmt && lhs.uppercase == rhs.uppercase && lhs.width == rhs.width && lhs.spacer == rhs.spacer && lhs.suffix == rhs.suffix ; } TOML11_INLINE bool operator!=(const integer_format_info& lhs, const integer_format_info& rhs) noexcept { return !(lhs == rhs); } // ---------------------------------------------------------------------------- // floating TOML11_INLINE std::ostream& operator<<(std::ostream& os, const floating_format f) { switch(f) { case floating_format::defaultfloat: {os << "defaultfloat"; break;} case floating_format::fixed : {os << "fixed" ; break;} case floating_format::scientific : {os << "scientific" ; break;} case floating_format::hex : {os << "hex" ; break;} default: { os << "unknown floating_format: " << static_cast(f); break; } } return os; } TOML11_INLINE std::string to_string(const floating_format c) { std::ostringstream oss; oss << c; return oss.str(); } TOML11_INLINE bool operator==(const floating_format_info& lhs, const floating_format_info& rhs) noexcept { return lhs.fmt == rhs.fmt && lhs.prec == rhs.prec && lhs.suffix == rhs.suffix ; } TOML11_INLINE bool operator!=(const floating_format_info& lhs, const floating_format_info& rhs) noexcept { return !(lhs == rhs); } // ---------------------------------------------------------------------------- // string TOML11_INLINE std::ostream& operator<<(std::ostream& os, const string_format f) { switch(f) { case string_format::basic : {os << "basic" ; break;} case string_format::literal : {os << "literal" ; break;} case string_format::multiline_basic : {os << "multiline_basic" ; break;} case string_format::multiline_literal: {os << "multiline_literal"; break;} default: { os << "unknown string_format: " << static_cast(f); break; } } return os; } TOML11_INLINE std::string to_string(const string_format c) { std::ostringstream oss; oss << c; return oss.str(); } TOML11_INLINE bool operator==(const string_format_info& lhs, const string_format_info& rhs) noexcept { return lhs.fmt == rhs.fmt && lhs.start_with_newline == rhs.start_with_newline ; } TOML11_INLINE bool operator!=(const string_format_info& lhs, const string_format_info& rhs) noexcept { return !(lhs == rhs); } // ---------------------------------------------------------------------------- // datetime TOML11_INLINE std::ostream& operator<<(std::ostream& os, const datetime_delimiter_kind d) { switch(d) { case datetime_delimiter_kind::upper_T: { os << "upper_T, "; break; } case datetime_delimiter_kind::lower_t: { os << "lower_t, "; break; } case datetime_delimiter_kind::space: { os << "space, "; break; } default: { os << "unknown datetime delimiter: " << static_cast(d); break; } } return os; } TOML11_INLINE std::string to_string(const datetime_delimiter_kind c) { std::ostringstream oss; oss << c; return oss.str(); } TOML11_INLINE bool operator==(const offset_datetime_format_info& lhs, const offset_datetime_format_info& rhs) noexcept { return lhs.delimiter == rhs.delimiter && lhs.has_seconds == rhs.has_seconds && lhs.subsecond_precision == rhs.subsecond_precision ; } TOML11_INLINE bool operator!=(const offset_datetime_format_info& lhs, const offset_datetime_format_info& rhs) noexcept { return !(lhs == rhs); } TOML11_INLINE bool operator==(const local_datetime_format_info& lhs, const local_datetime_format_info& rhs) noexcept { return lhs.delimiter == rhs.delimiter && lhs.has_seconds == rhs.has_seconds && lhs.subsecond_precision == rhs.subsecond_precision ; } TOML11_INLINE bool operator!=(const local_datetime_format_info& lhs, const local_datetime_format_info& rhs) noexcept { return !(lhs == rhs); } TOML11_INLINE bool operator==(const local_date_format_info&, const local_date_format_info&) noexcept { return true; } TOML11_INLINE bool operator!=(const local_date_format_info& lhs, const local_date_format_info& rhs) noexcept { return !(lhs == rhs); } TOML11_INLINE bool operator==(const local_time_format_info& lhs, const local_time_format_info& rhs) noexcept { return lhs.has_seconds == rhs.has_seconds && lhs.subsecond_precision == rhs.subsecond_precision ; } TOML11_INLINE bool operator!=(const local_time_format_info& lhs, const local_time_format_info& rhs) noexcept { return !(lhs == rhs); } // ---------------------------------------------------------------------------- // array TOML11_INLINE std::ostream& operator<<(std::ostream& os, const array_format f) { switch(f) { case array_format::default_format : {os << "default_format" ; break;} case array_format::oneline : {os << "oneline" ; break;} case array_format::multiline : {os << "multiline" ; break;} case array_format::array_of_tables: {os << "array_of_tables"; break;} default: { os << "unknown array_format: " << static_cast(f); break; } } return os; } TOML11_INLINE std::string to_string(const array_format c) { std::ostringstream oss; oss << c; return oss.str(); } TOML11_INLINE bool operator==(const array_format_info& lhs, const array_format_info& rhs) noexcept { return lhs.fmt == rhs.fmt && lhs.indent_type == rhs.indent_type && lhs.body_indent == rhs.body_indent && lhs.closing_indent == rhs.closing_indent ; } TOML11_INLINE bool operator!=(const array_format_info& lhs, const array_format_info& rhs) noexcept { return !(lhs == rhs); } // ---------------------------------------------------------------------------- // table TOML11_INLINE std::ostream& operator<<(std::ostream& os, const table_format f) { switch(f) { case table_format::multiline : {os << "multiline" ; break;} case table_format::oneline : {os << "oneline" ; break;} case table_format::dotted : {os << "dotted" ; break;} case table_format::multiline_oneline: {os << "multiline_oneline"; break;} case table_format::implicit : {os << "implicit" ; break;} default: { os << "unknown array_format: " << static_cast(f); break; } } return os; } TOML11_INLINE std::string to_string(const table_format c) { std::ostringstream oss; oss << c; return oss.str(); } TOML11_INLINE bool operator==(const table_format_info& lhs, const table_format_info& rhs) noexcept { return lhs.fmt == rhs.fmt && lhs.indent_type == rhs.indent_type && lhs.body_indent == rhs.body_indent && lhs.name_indent == rhs.name_indent && lhs.closing_indent == rhs.closing_indent ; } TOML11_INLINE bool operator!=(const table_format_info& lhs, const table_format_info& rhs) noexcept { return !(lhs == rhs); } } // namespace toml #endif // TOML11_FORMAT_IMPL_HPP #endif #endif// TOML11_FORMAT_HPP #ifndef TOML11_DATETIME_HPP #define TOML11_DATETIME_HPP #ifndef TOML11_DATETIME_FWD_HPP #define TOML11_DATETIME_FWD_HPP #include #include #include #include #include #include namespace toml { enum class month_t : std::uint8_t { Jan = 0, Feb = 1, Mar = 2, Apr = 3, May = 4, Jun = 5, Jul = 6, Aug = 7, Sep = 8, Oct = 9, Nov = 10, Dec = 11 }; // ---------------------------------------------------------------------------- struct local_date { std::int16_t year{0}; // A.D. (like, 2018) std::uint8_t month{0}; // [0, 11] std::uint8_t day{0}; // [1, 31] local_date(int y, month_t m, int d) : year {static_cast(y)}, month{static_cast(m)}, day {static_cast(d)} {} explicit local_date(const std::tm& t) : year {static_cast(t.tm_year + 1900)}, month{static_cast(t.tm_mon)}, day {static_cast(t.tm_mday)} {} explicit local_date(const std::chrono::system_clock::time_point& tp); explicit local_date(const std::time_t t); operator std::chrono::system_clock::time_point() const; operator std::time_t() const; local_date() = default; ~local_date() = default; local_date(local_date const&) = default; local_date(local_date&&) = default; local_date& operator=(local_date const&) = default; local_date& operator=(local_date&&) = default; }; bool operator==(const local_date& lhs, const local_date& rhs); bool operator!=(const local_date& lhs, const local_date& rhs); bool operator< (const local_date& lhs, const local_date& rhs); bool operator<=(const local_date& lhs, const local_date& rhs); bool operator> (const local_date& lhs, const local_date& rhs); bool operator>=(const local_date& lhs, const local_date& rhs); std::ostream& operator<<(std::ostream& os, const local_date& date); std::string to_string(const local_date& date); // ----------------------------------------------------------------------------- struct local_time { std::uint8_t hour{0}; // [0, 23] std::uint8_t minute{0}; // [0, 59] std::uint8_t second{0}; // [0, 60] std::uint16_t millisecond{0}; // [0, 999] std::uint16_t microsecond{0}; // [0, 999] std::uint16_t nanosecond{0}; // [0, 999] local_time(int h, int m, int s, int ms = 0, int us = 0, int ns = 0) : hour {static_cast(h)}, minute{static_cast(m)}, second{static_cast(s)}, millisecond{static_cast(ms)}, microsecond{static_cast(us)}, nanosecond {static_cast(ns)} {} explicit local_time(const std::tm& t) : hour {static_cast(t.tm_hour)}, minute{static_cast(t.tm_min )}, second{static_cast(t.tm_sec )}, millisecond{0}, microsecond{0}, nanosecond{0} {} template explicit local_time(const std::chrono::duration& t) { const auto h = std::chrono::duration_cast(t); this->hour = static_cast(h.count()); const auto t2 = t - h; const auto m = std::chrono::duration_cast(t2); this->minute = static_cast(m.count()); const auto t3 = t2 - m; const auto s = std::chrono::duration_cast(t3); this->second = static_cast(s.count()); const auto t4 = t3 - s; const auto ms = std::chrono::duration_cast(t4); this->millisecond = static_cast(ms.count()); const auto t5 = t4 - ms; const auto us = std::chrono::duration_cast(t5); this->microsecond = static_cast(us.count()); const auto t6 = t5 - us; const auto ns = std::chrono::duration_cast(t6); this->nanosecond = static_cast(ns.count()); } operator std::chrono::nanoseconds() const; local_time() = default; ~local_time() = default; local_time(local_time const&) = default; local_time(local_time&&) = default; local_time& operator=(local_time const&) = default; local_time& operator=(local_time&&) = default; }; bool operator==(const local_time& lhs, const local_time& rhs); bool operator!=(const local_time& lhs, const local_time& rhs); bool operator< (const local_time& lhs, const local_time& rhs); bool operator<=(const local_time& lhs, const local_time& rhs); bool operator> (const local_time& lhs, const local_time& rhs); bool operator>=(const local_time& lhs, const local_time& rhs); std::ostream& operator<<(std::ostream& os, const local_time& time); std::string to_string(const local_time& time); // ---------------------------------------------------------------------------- struct time_offset { std::int8_t hour{0}; // [-12, 12] std::int8_t minute{0}; // [-59, 59] time_offset(int h, int m) : hour {static_cast(h)}, minute{static_cast(m)} {} operator std::chrono::minutes() const; time_offset() = default; ~time_offset() = default; time_offset(time_offset const&) = default; time_offset(time_offset&&) = default; time_offset& operator=(time_offset const&) = default; time_offset& operator=(time_offset&&) = default; }; bool operator==(const time_offset& lhs, const time_offset& rhs); bool operator!=(const time_offset& lhs, const time_offset& rhs); bool operator< (const time_offset& lhs, const time_offset& rhs); bool operator<=(const time_offset& lhs, const time_offset& rhs); bool operator> (const time_offset& lhs, const time_offset& rhs); bool operator>=(const time_offset& lhs, const time_offset& rhs); std::ostream& operator<<(std::ostream& os, const time_offset& offset); std::string to_string(const time_offset& offset); // ----------------------------------------------------------------------------- struct local_datetime { local_date date{}; local_time time{}; local_datetime(local_date d, local_time t): date{d}, time{t} {} explicit local_datetime(const std::tm& t): date{t}, time{t}{} explicit local_datetime(const std::chrono::system_clock::time_point& tp); explicit local_datetime(const std::time_t t); operator std::chrono::system_clock::time_point() const; operator std::time_t() const; local_datetime() = default; ~local_datetime() = default; local_datetime(local_datetime const&) = default; local_datetime(local_datetime&&) = default; local_datetime& operator=(local_datetime const&) = default; local_datetime& operator=(local_datetime&&) = default; }; bool operator==(const local_datetime& lhs, const local_datetime& rhs); bool operator!=(const local_datetime& lhs, const local_datetime& rhs); bool operator< (const local_datetime& lhs, const local_datetime& rhs); bool operator<=(const local_datetime& lhs, const local_datetime& rhs); bool operator> (const local_datetime& lhs, const local_datetime& rhs); bool operator>=(const local_datetime& lhs, const local_datetime& rhs); std::ostream& operator<<(std::ostream& os, const local_datetime& dt); std::string to_string(const local_datetime& dt); // ----------------------------------------------------------------------------- struct offset_datetime { local_date date{}; local_time time{}; time_offset offset{}; offset_datetime(local_date d, local_time t, time_offset o) : date{d}, time{t}, offset{o} {} offset_datetime(const local_datetime& dt, time_offset o) : date{dt.date}, time{dt.time}, offset{o} {} // use the current local timezone offset explicit offset_datetime(const local_datetime& ld); explicit offset_datetime(const std::chrono::system_clock::time_point& tp); explicit offset_datetime(const std::time_t& t); explicit offset_datetime(const std::tm& t); operator std::chrono::system_clock::time_point() const; operator std::time_t() const; offset_datetime() = default; ~offset_datetime() = default; offset_datetime(offset_datetime const&) = default; offset_datetime(offset_datetime&&) = default; offset_datetime& operator=(offset_datetime const&) = default; offset_datetime& operator=(offset_datetime&&) = default; private: static time_offset get_local_offset(const std::time_t* tp); }; bool operator==(const offset_datetime& lhs, const offset_datetime& rhs); bool operator!=(const offset_datetime& lhs, const offset_datetime& rhs); bool operator< (const offset_datetime& lhs, const offset_datetime& rhs); bool operator<=(const offset_datetime& lhs, const offset_datetime& rhs); bool operator> (const offset_datetime& lhs, const offset_datetime& rhs); bool operator>=(const offset_datetime& lhs, const offset_datetime& rhs); std::ostream& operator<<(std::ostream& os, const offset_datetime& dt); std::string to_string(const offset_datetime& dt); }//toml #endif // TOML11_DATETIME_FWD_HPP #if ! defined(TOML11_COMPILE_SOURCES) #ifndef TOML11_DATETIME_IMPL_HPP #define TOML11_DATETIME_IMPL_HPP #include #include #include #include #include #include #include namespace toml { // To avoid non-threadsafe std::localtime. In C11 (not C++11!), localtime_s is // provided in the absolutely same purpose, but C++11 is actually not compatible // with C11. We need to dispatch the function depending on the OS. namespace detail { // TODO: find more sophisticated way to handle this #if defined(_MSC_VER) TOML11_INLINE std::tm localtime_s(const std::time_t* src) { std::tm dst; const auto result = ::localtime_s(&dst, src); if (result) { throw std::runtime_error("localtime_s failed."); } return dst; } TOML11_INLINE std::tm gmtime_s(const std::time_t* src) { std::tm dst; const auto result = ::gmtime_s(&dst, src); if (result) { throw std::runtime_error("gmtime_s failed."); } return dst; } #elif (defined(_POSIX_C_SOURCE) && _POSIX_C_SOURCE >= 1) || defined(_XOPEN_SOURCE) || defined(_BSD_SOURCE) || defined(_SVID_SOURCE) || defined(_POSIX_SOURCE) TOML11_INLINE std::tm localtime_s(const std::time_t* src) { std::tm dst; const auto result = ::localtime_r(src, &dst); if (!result) { throw std::runtime_error("localtime_r failed."); } return dst; } TOML11_INLINE std::tm gmtime_s(const std::time_t* src) { std::tm dst; const auto result = ::gmtime_r(src, &dst); if (!result) { throw std::runtime_error("gmtime_r failed."); } return dst; } #else // fallback. not threadsafe TOML11_INLINE std::tm localtime_s(const std::time_t* src) { const auto result = std::localtime(src); if (!result) { throw std::runtime_error("localtime failed."); } return *result; } TOML11_INLINE std::tm gmtime_s(const std::time_t* src) { const auto result = std::gmtime(src); if (!result) { throw std::runtime_error("gmtime failed."); } return *result; } #endif } // detail // ---------------------------------------------------------------------------- TOML11_INLINE local_date::local_date(const std::chrono::system_clock::time_point& tp) { const auto t = std::chrono::system_clock::to_time_t(tp); const auto time = detail::localtime_s(&t); *this = local_date(time); } TOML11_INLINE local_date::local_date(const std::time_t t) : local_date{std::chrono::system_clock::from_time_t(t)} {} TOML11_INLINE local_date::operator std::chrono::system_clock::time_point() const { // std::mktime returns date as local time zone. no conversion needed std::tm t; t.tm_sec = 0; t.tm_min = 0; t.tm_hour = 0; t.tm_mday = static_cast(this->day); t.tm_mon = static_cast(this->month); t.tm_year = static_cast(this->year) - 1900; t.tm_wday = 0; // the value will be ignored t.tm_yday = 0; // the value will be ignored t.tm_isdst = -1; return std::chrono::system_clock::from_time_t(std::mktime(&t)); } TOML11_INLINE local_date::operator std::time_t() const { return std::chrono::system_clock::to_time_t( std::chrono::system_clock::time_point(*this)); } TOML11_INLINE bool operator==(const local_date& lhs, const local_date& rhs) { return std::make_tuple(lhs.year, lhs.month, lhs.day) == std::make_tuple(rhs.year, rhs.month, rhs.day); } TOML11_INLINE bool operator!=(const local_date& lhs, const local_date& rhs) { return !(lhs == rhs); } TOML11_INLINE bool operator< (const local_date& lhs, const local_date& rhs) { return std::make_tuple(lhs.year, lhs.month, lhs.day) < std::make_tuple(rhs.year, rhs.month, rhs.day); } TOML11_INLINE bool operator<=(const local_date& lhs, const local_date& rhs) { return (lhs < rhs) || (lhs == rhs); } TOML11_INLINE bool operator> (const local_date& lhs, const local_date& rhs) { return !(lhs <= rhs); } TOML11_INLINE bool operator>=(const local_date& lhs, const local_date& rhs) { return !(lhs < rhs); } TOML11_INLINE std::ostream& operator<<(std::ostream& os, const local_date& date) { os << std::setfill('0') << std::setw(4) << static_cast(date.year ) << '-'; os << std::setfill('0') << std::setw(2) << static_cast(date.month) + 1 << '-'; os << std::setfill('0') << std::setw(2) << static_cast(date.day ) ; return os; } TOML11_INLINE std::string to_string(const local_date& date) { std::ostringstream oss; oss.imbue(std::locale::classic()); oss << date; return oss.str(); } // ----------------------------------------------------------------------------- TOML11_INLINE local_time::operator std::chrono::nanoseconds() const { return std::chrono::nanoseconds (this->nanosecond) + std::chrono::microseconds(this->microsecond) + std::chrono::milliseconds(this->millisecond) + std::chrono::seconds(this->second) + std::chrono::minutes(this->minute) + std::chrono::hours(this->hour); } TOML11_INLINE bool operator==(const local_time& lhs, const local_time& rhs) { return std::make_tuple(lhs.hour, lhs.minute, lhs.second, lhs.millisecond, lhs.microsecond, lhs.nanosecond) == std::make_tuple(rhs.hour, rhs.minute, rhs.second, rhs.millisecond, rhs.microsecond, rhs.nanosecond); } TOML11_INLINE bool operator!=(const local_time& lhs, const local_time& rhs) { return !(lhs == rhs); } TOML11_INLINE bool operator< (const local_time& lhs, const local_time& rhs) { return std::make_tuple(lhs.hour, lhs.minute, lhs.second, lhs.millisecond, lhs.microsecond, lhs.nanosecond) < std::make_tuple(rhs.hour, rhs.minute, rhs.second, rhs.millisecond, rhs.microsecond, rhs.nanosecond); } TOML11_INLINE bool operator<=(const local_time& lhs, const local_time& rhs) { return (lhs < rhs) || (lhs == rhs); } TOML11_INLINE bool operator> (const local_time& lhs, const local_time& rhs) { return !(lhs <= rhs); } TOML11_INLINE bool operator>=(const local_time& lhs, const local_time& rhs) { return !(lhs < rhs); } TOML11_INLINE std::ostream& operator<<(std::ostream& os, const local_time& time) { os << std::setfill('0') << std::setw(2) << static_cast(time.hour ) << ':'; os << std::setfill('0') << std::setw(2) << static_cast(time.minute) << ':'; os << std::setfill('0') << std::setw(2) << static_cast(time.second); if(time.millisecond != 0 || time.microsecond != 0 || time.nanosecond != 0) { os << '.'; os << std::setfill('0') << std::setw(3) << static_cast(time.millisecond); if(time.microsecond != 0 || time.nanosecond != 0) { os << std::setfill('0') << std::setw(3) << static_cast(time.microsecond); if(time.nanosecond != 0) { os << std::setfill('0') << std::setw(3) << static_cast(time.nanosecond); } } } return os; } TOML11_INLINE std::string to_string(const local_time& time) { std::ostringstream oss; oss.imbue(std::locale::classic()); oss << time; return oss.str(); } // ---------------------------------------------------------------------------- TOML11_INLINE time_offset::operator std::chrono::minutes() const { return std::chrono::minutes(this->minute) + std::chrono::hours(this->hour); } TOML11_INLINE bool operator==(const time_offset& lhs, const time_offset& rhs) { return std::make_tuple(lhs.hour, lhs.minute) == std::make_tuple(rhs.hour, rhs.minute); } TOML11_INLINE bool operator!=(const time_offset& lhs, const time_offset& rhs) { return !(lhs == rhs); } TOML11_INLINE bool operator< (const time_offset& lhs, const time_offset& rhs) { return std::make_tuple(lhs.hour, lhs.minute) < std::make_tuple(rhs.hour, rhs.minute); } TOML11_INLINE bool operator<=(const time_offset& lhs, const time_offset& rhs) { return (lhs < rhs) || (lhs == rhs); } TOML11_INLINE bool operator> (const time_offset& lhs, const time_offset& rhs) { return !(lhs <= rhs); } TOML11_INLINE bool operator>=(const time_offset& lhs, const time_offset& rhs) { return !(lhs < rhs); } TOML11_INLINE std::ostream& operator<<(std::ostream& os, const time_offset& offset) { if(offset.hour == 0 && offset.minute == 0) { os << 'Z'; return os; } int minute = static_cast(offset.hour) * 60 + offset.minute; if(minute < 0){os << '-'; minute = std::abs(minute);} else {os << '+';} os << std::setfill('0') << std::setw(2) << minute / 60 << ':'; os << std::setfill('0') << std::setw(2) << minute % 60; return os; } TOML11_INLINE std::string to_string(const time_offset& offset) { std::ostringstream oss; oss.imbue(std::locale::classic()); oss << offset; return oss.str(); } // ----------------------------------------------------------------------------- TOML11_INLINE local_datetime::local_datetime(const std::chrono::system_clock::time_point& tp) { const auto t = std::chrono::system_clock::to_time_t(tp); std::tm ltime = detail::localtime_s(&t); this->date = local_date(ltime); this->time = local_time(ltime); // std::tm lacks subsecond information, so diff between tp and tm // can be used to get millisecond & microsecond information. const auto t_diff = tp - std::chrono::system_clock::from_time_t(std::mktime(<ime)); this->time.millisecond = static_cast( std::chrono::duration_cast(t_diff).count()); this->time.microsecond = static_cast( std::chrono::duration_cast(t_diff).count()); this->time.nanosecond = static_cast( std::chrono::duration_cast(t_diff).count()); } TOML11_INLINE local_datetime::local_datetime(const std::time_t t) : local_datetime{std::chrono::system_clock::from_time_t(t)} {} TOML11_INLINE local_datetime::operator std::chrono::system_clock::time_point() const { using internal_duration = typename std::chrono::system_clock::time_point::duration; // Normally DST begins at A.M. 3 or 4. If we re-use conversion operator // of local_date and local_time independently, the conversion fails if // it is the day when DST begins or ends. Since local_date considers the // time is 00:00 A.M. and local_time does not consider DST because it // does not have any date information. We need to consider both date and // time information at the same time to convert it correctly. std::tm t; t.tm_sec = static_cast(this->time.second); t.tm_min = static_cast(this->time.minute); t.tm_hour = static_cast(this->time.hour); t.tm_mday = static_cast(this->date.day); t.tm_mon = static_cast(this->date.month); t.tm_year = static_cast(this->date.year) - 1900; t.tm_wday = 0; // the value will be ignored t.tm_yday = 0; // the value will be ignored t.tm_isdst = -1; // std::mktime returns date as local time zone. no conversion needed auto dt = std::chrono::system_clock::from_time_t(std::mktime(&t)); dt += std::chrono::duration_cast( std::chrono::milliseconds(this->time.millisecond) + std::chrono::microseconds(this->time.microsecond) + std::chrono::nanoseconds (this->time.nanosecond)); return dt; } TOML11_INLINE local_datetime::operator std::time_t() const { return std::chrono::system_clock::to_time_t( std::chrono::system_clock::time_point(*this)); } TOML11_INLINE bool operator==(const local_datetime& lhs, const local_datetime& rhs) { return std::make_tuple(lhs.date, lhs.time) == std::make_tuple(rhs.date, rhs.time); } TOML11_INLINE bool operator!=(const local_datetime& lhs, const local_datetime& rhs) { return !(lhs == rhs); } TOML11_INLINE bool operator< (const local_datetime& lhs, const local_datetime& rhs) { return std::make_tuple(lhs.date, lhs.time) < std::make_tuple(rhs.date, rhs.time); } TOML11_INLINE bool operator<=(const local_datetime& lhs, const local_datetime& rhs) { return (lhs < rhs) || (lhs == rhs); } TOML11_INLINE bool operator> (const local_datetime& lhs, const local_datetime& rhs) { return !(lhs <= rhs); } TOML11_INLINE bool operator>=(const local_datetime& lhs, const local_datetime& rhs) { return !(lhs < rhs); } TOML11_INLINE std::ostream& operator<<(std::ostream& os, const local_datetime& dt) { os << dt.date << 'T' << dt.time; return os; } TOML11_INLINE std::string to_string(const local_datetime& dt) { std::ostringstream oss; oss.imbue(std::locale::classic()); oss << dt; return oss.str(); } // ----------------------------------------------------------------------------- TOML11_INLINE offset_datetime::offset_datetime(const local_datetime& ld) : date{ld.date}, time{ld.time}, offset{get_local_offset(nullptr)} // use the current local timezone offset {} TOML11_INLINE offset_datetime::offset_datetime(const std::chrono::system_clock::time_point& tp) : offset{0, 0} // use gmtime { const auto timet = std::chrono::system_clock::to_time_t(tp); const auto tm = detail::gmtime_s(&timet); this->date = local_date(tm); this->time = local_time(tm); } TOML11_INLINE offset_datetime::offset_datetime(const std::time_t& t) : offset{0, 0} // use gmtime { const auto tm = detail::gmtime_s(&t); this->date = local_date(tm); this->time = local_time(tm); } TOML11_INLINE offset_datetime::offset_datetime(const std::tm& t) : offset{0, 0} // assume gmtime { this->date = local_date(t); this->time = local_time(t); } TOML11_INLINE offset_datetime::operator std::chrono::system_clock::time_point() const { // get date-time using internal_duration = typename std::chrono::system_clock::time_point::duration; // first, convert it to local date-time information in the same way as // local_datetime does. later we will use time_t to adjust time offset. std::tm t; t.tm_sec = static_cast(this->time.second); t.tm_min = static_cast(this->time.minute); t.tm_hour = static_cast(this->time.hour); t.tm_mday = static_cast(this->date.day); t.tm_mon = static_cast(this->date.month); t.tm_year = static_cast(this->date.year) - 1900; t.tm_wday = 0; // the value will be ignored t.tm_yday = 0; // the value will be ignored t.tm_isdst = -1; const std::time_t tp_loc = std::mktime(std::addressof(t)); auto tp = std::chrono::system_clock::from_time_t(tp_loc); tp += std::chrono::duration_cast( std::chrono::milliseconds(this->time.millisecond) + std::chrono::microseconds(this->time.microsecond) + std::chrono::nanoseconds (this->time.nanosecond)); // Since mktime uses local time zone, it should be corrected. // `12:00:00+09:00` means `03:00:00Z`. So mktime returns `03:00:00Z` if // we are in `+09:00` timezone. To represent `12:00:00Z` there, we need // to add `+09:00` to `03:00:00Z`. // Here, it uses the time_t converted from date-time info to handle // daylight saving time. const auto ofs = get_local_offset(std::addressof(tp_loc)); tp += std::chrono::hours (ofs.hour); tp += std::chrono::minutes(ofs.minute); // We got `12:00:00Z` by correcting local timezone applied by mktime. // Then we will apply the offset. Let's say `12:00:00-08:00` is given. // And now, we have `12:00:00Z`. `12:00:00-08:00` means `20:00:00Z`. // So we need to subtract the offset. tp -= std::chrono::minutes(this->offset); return tp; } TOML11_INLINE offset_datetime::operator std::time_t() const { return std::chrono::system_clock::to_time_t( std::chrono::system_clock::time_point(*this)); } TOML11_INLINE time_offset offset_datetime::get_local_offset(const std::time_t* tp) { // get local timezone with the same date-time information as mktime const auto t = detail::localtime_s(tp); std::array buf; const auto result = std::strftime(buf.data(), 6, "%z", &t); // +hhmm\0 if(result != 5) { throw std::runtime_error("toml::offset_datetime: cannot obtain " "timezone information of current env"); } const int ofs = std::atoi(buf.data()); const int ofs_h = ofs / 100; const int ofs_m = ofs - (ofs_h * 100); return time_offset(ofs_h, ofs_m); } TOML11_INLINE bool operator==(const offset_datetime& lhs, const offset_datetime& rhs) { return std::make_tuple(lhs.date, lhs.time, lhs.offset) == std::make_tuple(rhs.date, rhs.time, rhs.offset); } TOML11_INLINE bool operator!=(const offset_datetime& lhs, const offset_datetime& rhs) { return !(lhs == rhs); } TOML11_INLINE bool operator< (const offset_datetime& lhs, const offset_datetime& rhs) { return std::make_tuple(lhs.date, lhs.time, lhs.offset) < std::make_tuple(rhs.date, rhs.time, rhs.offset); } TOML11_INLINE bool operator<=(const offset_datetime& lhs, const offset_datetime& rhs) { return (lhs < rhs) || (lhs == rhs); } TOML11_INLINE bool operator> (const offset_datetime& lhs, const offset_datetime& rhs) { return !(lhs <= rhs); } TOML11_INLINE bool operator>=(const offset_datetime& lhs, const offset_datetime& rhs) { return !(lhs < rhs); } TOML11_INLINE std::ostream& operator<<(std::ostream& os, const offset_datetime& dt) { os << dt.date << 'T' << dt.time << dt.offset; return os; } TOML11_INLINE std::string to_string(const offset_datetime& dt) { std::ostringstream oss; oss.imbue(std::locale::classic()); oss << dt; return oss.str(); } }//toml #endif // TOML11_DATETIME_IMPL_HPP #endif #endif // TOML11_DATETIME_HPP #ifndef TOML11_COMPAT_HPP #define TOML11_COMPAT_HPP #include #include #include #include #include #include #if TOML11_CPLUSPLUS_STANDARD_VERSION >= TOML11_CXX20_VALUE # if __has_include() # include # endif #endif #include // ---------------------------------------------------------------------------- #if TOML11_CPLUSPLUS_STANDARD_VERSION >= TOML11_CXX14_VALUE # if __has_cpp_attribute(deprecated) # define TOML11_HAS_ATTR_DEPRECATED 1 # endif #endif #if defined(TOML11_HAS_ATTR_DEPRECATED) # define TOML11_DEPRECATED(msg) [[deprecated(msg)]] #elif defined(__GNUC__) # define TOML11_DEPRECATED(msg) __attribute__((deprecated(msg))) #elif defined(_MSC_VER) # define TOML11_DEPRECATED(msg) __declspec(deprecated(msg)) #else # define TOML11_DEPRECATED(msg) #endif // ---------------------------------------------------------------------------- #if defined(__cpp_if_constexpr) # if __cpp_if_constexpr >= 201606L # define TOML11_HAS_CONSTEXPR_IF 1 # endif #endif #if defined(TOML11_HAS_CONSTEXPR_IF) # define TOML11_CONSTEXPR_IF if constexpr #else # define TOML11_CONSTEXPR_IF if #endif // ---------------------------------------------------------------------------- #if TOML11_CPLUSPLUS_STANDARD_VERSION >= TOML11_CXX14_VALUE # if defined(__cpp_lib_make_unique) # if __cpp_lib_make_unique >= 201304L # define TOML11_HAS_STD_MAKE_UNIQUE 1 # endif # endif #endif namespace toml { namespace cxx { #if defined(TOML11_HAS_STD_MAKE_UNIQUE) using std::make_unique; #else template std::unique_ptr make_unique(Ts&& ... args) { return std::unique_ptr(new T(std::forward(args)...)); } #endif // TOML11_HAS_STD_MAKE_UNIQUE } // cxx } // toml // --------------------------------------------------------------------------- #if TOML11_CPLUSPLUS_STANDARD_VERSION >= TOML11_CXX14_VALUE # if defined(__cpp_lib_make_reverse_iterator) # if __cpp_lib_make_reverse_iterator >= 201402L # define TOML11_HAS_STD_MAKE_REVERSE_ITERATOR 1 # endif # endif #endif namespace toml { namespace cxx { # if defined(TOML11_HAS_STD_MAKE_REVERSE_ITERATOR) using std::make_reverse_iterator; #else template std::reverse_iterator make_reverse_iterator(Iterator iter) { return std::reverse_iterator(iter); } #endif // TOML11_HAS_STD_MAKE_REVERSE_ITERATOR } // cxx } // toml // --------------------------------------------------------------------------- #if TOML11_CPLUSPLUS_STANDARD_VERSION >= TOML11_CXX20_VALUE # if defined(__cpp_lib_clamp) # if __cpp_lib_clamp >= 201603L # define TOML11_HAS_STD_CLAMP 1 # endif # endif #endif namespace toml { namespace cxx { #if defined(TOML11_HAS_STD_CLAMP) using std::clamp; #else template T clamp(const T& x, const T& low, const T& high) noexcept { assert(low <= high); return (std::min)((std::max)(x, low), high); } #endif // TOML11_HAS_STD_CLAMP } // cxx } // toml // --------------------------------------------------------------------------- #if TOML11_CPLUSPLUS_STANDARD_VERSION >= TOML11_CXX20_VALUE # if defined(__cpp_lib_bit_cast) # if __cpp_lib_bit_cast >= 201806L # define TOML11_HAS_STD_BIT_CAST 1 # endif # endif #endif namespace toml { namespace cxx { #if defined(TOML11_HAS_STD_BIT_CAST) using std::bit_cast; #else template U bit_cast(const T& x) noexcept { static_assert(sizeof(T) == sizeof(U), ""); static_assert(std::is_default_constructible::value, ""); U z; std::memcpy(reinterpret_cast(std::addressof(z)), reinterpret_cast(std::addressof(x)), sizeof(T)); return z; } #endif // TOML11_HAS_STD_BIT_CAST } // cxx } // toml // --------------------------------------------------------------------------- // C++20 remove_cvref_t #if TOML11_CPLUSPLUS_STANDARD_VERSION >= TOML11_CXX20_VALUE # if defined(__cpp_lib_remove_cvref) # if __cpp_lib_remove_cvref >= 201711L # define TOML11_HAS_STD_REMOVE_CVREF 1 # endif # endif #endif namespace toml { namespace cxx { #if defined(TOML11_HAS_STD_REMOVE_CVREF) using std::remove_cvref; using std::remove_cvref_t; #else template struct remove_cvref { using type = typename std::remove_cv< typename std::remove_reference::type>::type; }; template using remove_cvref_t = typename remove_cvref::type; #endif // TOML11_HAS_STD_REMOVE_CVREF } // cxx } // toml // --------------------------------------------------------------------------- // C++17 and/or/not #if TOML11_CPLUSPLUS_STANDARD_VERSION >= TOML11_CXX17_VALUE # if defined(__cpp_lib_logical_traits) # if __cpp_lib_logical_traits >= 201510L # define TOML11_HAS_STD_CONJUNCTION 1 # endif # endif #endif namespace toml { namespace cxx { #if defined(TOML11_HAS_STD_CONJUNCTION) using std::conjunction; using std::disjunction; using std::negation; #else template struct conjunction : std::true_type{}; template struct conjunction : T{}; template struct conjunction : std::conditional(T::value), conjunction, T>::type {}; template struct disjunction : std::false_type{}; template struct disjunction : T {}; template struct disjunction : std::conditional(T::value), T, disjunction>::type {}; template struct negation : std::integral_constant(T::value)>{}; #endif // TOML11_HAS_STD_CONJUNCTION } // cxx } // toml // --------------------------------------------------------------------------- // C++14 index_sequence #if TOML11_CPLUSPLUS_STANDARD_VERSION >= TOML11_CXX14_VALUE # if defined(__cpp_lib_integer_sequence) # if __cpp_lib_integer_sequence >= 201304L # define TOML11_HAS_STD_INTEGER_SEQUENCE 1 # endif # endif #endif namespace toml { namespace cxx { #if defined(TOML11_HAS_STD_INTEGER_SEQUENCE) using std::index_sequence; using std::make_index_sequence; #else template struct index_sequence{}; template struct double_index_sequence; template struct double_index_sequence> { using type = index_sequence; }; template struct double_index_sequence> { using type = index_sequence; }; template struct index_sequence_maker { using type = typename double_index_sequence< N % 2 == 1, N/2, typename index_sequence_maker::type >::type; }; template<> struct index_sequence_maker<0> { using type = index_sequence<>; }; template using make_index_sequence = typename index_sequence_maker::type; #endif // TOML11_HAS_STD_INTEGER_SEQUENCE } // cxx } // toml // --------------------------------------------------------------------------- // C++14 enable_if_t #if TOML11_CPLUSPLUS_STANDARD_VERSION >= TOML11_CXX14_VALUE # if defined(__cpp_lib_transformation_trait_aliases) # if __cpp_lib_transformation_trait_aliases >= 201304L # define TOML11_HAS_STD_ENABLE_IF_T 1 # endif # endif #endif namespace toml { namespace cxx { #if defined(TOML11_HAS_STD_ENABLE_IF_T) using std::enable_if_t; #else template using enable_if_t = typename std::enable_if::type; #endif // TOML11_HAS_STD_ENABLE_IF_T } // cxx } // toml // --------------------------------------------------------------------------- // return_type_of_t #if TOML11_CPLUSPLUS_STANDARD_VERSION >= TOML11_CXX17_VALUE # if defined(__cpp_lib_is_invocable) # if __cpp_lib_is_invocable >= 201703 # define TOML11_HAS_STD_INVOKE_RESULT 1 # endif # endif #endif namespace toml { namespace cxx { #if defined(TOML11_HAS_STD_INVOKE_RESULT) template using return_type_of_t = std::invoke_result_t; #else // result_of is deprecated after C++17 template using return_type_of_t = typename std::result_of::type; #endif // TOML11_HAS_STD_INVOKE_RESULT } // cxx } // toml // ---------------------------------------------------------------------------- // (subset of) source_location #if TOML11_CPLUSPLUS_STANDARD_VERSION >= 202002L # if __has_include() # define TOML11_HAS_STD_SOURCE_LOCATION # endif // has_include #endif // c++20 #if ! defined(TOML11_HAS_STD_SOURCE_LOCATION) # if defined(__GNUC__) && ! defined(__clang__) # if TOML11_CPLUSPLUS_STANDARD_VERSION >= TOML11_CXX14_VALUE # if __has_include() # define TOML11_HAS_EXPERIMENTAL_SOURCE_LOCATION # endif # endif # endif // GNU g++ #endif // not TOML11_HAS_STD_SOURCE_LOCATION #if ! defined(TOML11_HAS_STD_SOURCE_LOCATION) && ! defined(TOML11_HAS_EXPERIMENTAL_SOURCE_LOCATION) # if defined(__GNUC__) && ! defined(__clang__) # if (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 9)) # define TOML11_HAS_BUILTIN_FILE_LINE 1 # define TOML11_BUILTIN_LINE_TYPE int # endif # elif defined(__clang__) // clang 9.0.0 implements builtin_FILE/LINE # if __has_builtin(__builtin_FILE) && __has_builtin(__builtin_LINE) # define TOML11_HAS_BUILTIN_FILE_LINE 1 # define TOML11_BUILTIN_LINE_TYPE unsigned int # endif # elif defined(_MSVC_LANG) && defined(_MSC_VER) # if _MSC_VER > 1926 # define TOML11_HAS_BUILTIN_FILE_LINE 1 # define TOML11_BUILTIN_LINE_TYPE int # endif # endif #endif #if defined(TOML11_HAS_STD_SOURCE_LOCATION) #include namespace toml { namespace cxx { using source_location = std::source_location; inline std::string to_string(const source_location& loc) { return std::string(" at line ") + std::to_string(loc.line()) + std::string(" in file ") + std::string(loc.file_name()); } } // cxx } // toml #elif defined(TOML11_HAS_EXPERIMENTAL_SOURCE_LOCATION) #include namespace toml { namespace cxx { using source_location = std::experimental::source_location; inline std::string to_string(const source_location& loc) { return std::string(" at line ") + std::to_string(loc.line()) + std::string(" in file ") + std::string(loc.file_name()); } } // cxx } // toml #elif defined(TOML11_HAS_BUILTIN_FILE_LINE) namespace toml { namespace cxx { struct source_location { using line_type = TOML11_BUILTIN_LINE_TYPE; static source_location current(const line_type line = __builtin_LINE(), const char* file = __builtin_FILE()) { return source_location(line, file); } source_location(const line_type line, const char* file) : line_(line), file_name_(file) {} line_type line() const noexcept {return line_;} const char* file_name() const noexcept {return file_name_;} private: line_type line_; const char* file_name_; }; inline std::string to_string(const source_location& loc) { return std::string(" at line ") + std::to_string(loc.line()) + std::string(" in file ") + std::string(loc.file_name()); } } // cxx } // toml #else // no builtin namespace toml { namespace cxx { struct source_location { static source_location current() { return source_location{}; } }; inline std::string to_string(const source_location&) { return std::string(""); } } // cxx } // toml #endif // TOML11_HAS_STD_SOURCE_LOCATION // ---------------------------------------------------------------------------- // (subset of) optional #if TOML11_CPLUSPLUS_STANDARD_VERSION >= TOML11_CXX17_VALUE # if __has_include() # include # endif // has_include(optional) #endif // C++17 #if TOML11_CPLUSPLUS_STANDARD_VERSION >= TOML11_CXX17_VALUE # if defined(__cpp_lib_optional) # if __cpp_lib_optional >= 201606L # define TOML11_HAS_STD_OPTIONAL 1 # endif # endif #endif #if defined(TOML11_HAS_STD_OPTIONAL) namespace toml { namespace cxx { using std::optional; inline std::nullopt_t make_nullopt() {return std::nullopt;} template std::basic_ostream& operator<<(std::basic_ostream& os, const std::nullopt_t&) { os << "nullopt"; return os; } } // cxx } // toml #else // TOML11_HAS_STD_OPTIONAL namespace toml { namespace cxx { struct nullopt_t{}; inline nullopt_t make_nullopt() {return nullopt_t{};} inline bool operator==(const nullopt_t&, const nullopt_t&) noexcept {return true;} inline bool operator!=(const nullopt_t&, const nullopt_t&) noexcept {return false;} inline bool operator< (const nullopt_t&, const nullopt_t&) noexcept {return false;} inline bool operator<=(const nullopt_t&, const nullopt_t&) noexcept {return true;} inline bool operator> (const nullopt_t&, const nullopt_t&) noexcept {return false;} inline bool operator>=(const nullopt_t&, const nullopt_t&) noexcept {return true;} template std::basic_ostream& operator<<(std::basic_ostream& os, const nullopt_t&) { os << "nullopt"; return os; } template class optional { public: using value_type = T; public: optional() noexcept : has_value_(false), null_('\0') {} optional(nullopt_t) noexcept : has_value_(false), null_('\0') {} optional(const T& x): has_value_(true), value_(x) {} optional(T&& x): has_value_(true), value_(std::move(x)) {} template::value, std::nullptr_t> = nullptr> explicit optional(U&& x): has_value_(true), value_(std::forward(x)) {} optional(const optional& rhs): has_value_(rhs.has_value_) { if(rhs.has_value_) { this->assigner(rhs.value_); } } optional(optional&& rhs): has_value_(rhs.has_value_) { if(this->has_value_) { this->assigner(std::move(rhs.value_)); } } optional& operator=(const optional& rhs) { if(this == std::addressof(rhs)) {return *this;} this->cleanup(); this->has_value_ = rhs.has_value_; if(this->has_value_) { this->assigner(rhs.value_); } return *this; } optional& operator=(optional&& rhs) { if(this == std::addressof(rhs)) {return *this;} this->cleanup(); this->has_value_ = rhs.has_value_; if(this->has_value_) { this->assigner(std::move(rhs.value_)); } return *this; } template>, std::is_constructible >::value, std::nullptr_t> = nullptr> explicit optional(const optional& rhs): has_value_(rhs.has_value_), null_('\0') { if(rhs.has_value_) { this->assigner(rhs.value_); } } template>, std::is_constructible >::value, std::nullptr_t> = nullptr> explicit optional(optional&& rhs): has_value_(rhs.has_value_), null_('\0') { if(this->has_value_) { this->assigner(std::move(rhs.value_)); } } template>, std::is_constructible >::value, std::nullptr_t> = nullptr> optional& operator=(const optional& rhs) { if(this == std::addressof(rhs)) {return *this;} this->cleanup(); this->has_value_ = rhs.has_value_; if(this->has_value_) { this->assigner(rhs.value_); } return *this; } template>, std::is_constructible >::value, std::nullptr_t> = nullptr> optional& operator=(optional&& rhs) { if(this == std::addressof(rhs)) {return *this;} this->cleanup(); this->has_value_ = rhs.has_value_; if(this->has_value_) { this->assigner(std::move(rhs.value_)); } return *this; } ~optional() noexcept { this->cleanup(); } explicit operator bool() const noexcept { return has_value_; } bool has_value() const noexcept {return has_value_;} value_type const& value(source_location loc = source_location::current()) const { if( ! this->has_value_) { throw std::runtime_error("optional::value(): bad_unwrap" + to_string(loc)); } return this->value_; } value_type& value(source_location loc = source_location::current()) { if( ! this->has_value_) { throw std::runtime_error("optional::value(): bad_unwrap" + to_string(loc)); } return this->value_; } value_type const& value_or(const value_type& opt) const { if(this->has_value_) {return this->value_;} else {return opt;} } value_type& value_or(value_type& opt) { if(this->has_value_) {return this->value_;} else {return opt;} } private: void cleanup() noexcept { if(this->has_value_) { value_.~T(); } } template void assigner(U&& x) { const auto tmp = ::new(std::addressof(this->value_)) value_type(std::forward(x)); assert(tmp == std::addressof(this->value_)); (void)tmp; } private: bool has_value_; union { char null_; T value_; }; }; } // cxx } // toml #endif // TOML11_HAS_STD_OPTIONAL #endif // TOML11_COMPAT_HPP #ifndef TOML11_VALUE_T_HPP #define TOML11_VALUE_T_HPP #ifndef TOML11_VALUE_T_FWD_HPP #define TOML11_VALUE_T_FWD_HPP #include #include #include #include namespace toml { // forward decl template class basic_value; // ---------------------------------------------------------------------------- // enum representing toml types enum class value_t : std::uint8_t { empty = 0, boolean = 1, integer = 2, floating = 3, string = 4, offset_datetime = 5, local_datetime = 6, local_date = 7, local_time = 8, array = 9, table = 10 }; std::ostream& operator<<(std::ostream& os, value_t t); std::string to_string(value_t t); // ---------------------------------------------------------------------------- // meta functions for internal use namespace detail { template using value_t_constant = std::integral_constant; template struct type_to_enum : value_t_constant {}; template struct type_to_enum : value_t_constant {}; template struct type_to_enum : value_t_constant {}; template struct type_to_enum : value_t_constant {}; template struct type_to_enum : value_t_constant {}; template struct type_to_enum : value_t_constant {}; template struct type_to_enum : value_t_constant {}; template struct type_to_enum : value_t_constant {}; template struct type_to_enum : value_t_constant {}; template struct type_to_enum : value_t_constant {}; template struct type_to_enum : value_t_constant {}; template struct enum_to_type { using type = void; }; template struct enum_to_type { using type = typename V::boolean_type ; }; template struct enum_to_type { using type = typename V::integer_type ; }; template struct enum_to_type { using type = typename V::floating_type ; }; template struct enum_to_type { using type = typename V::string_type ; }; template struct enum_to_type { using type = typename V::offset_datetime_type; }; template struct enum_to_type { using type = typename V::local_datetime_type ; }; template struct enum_to_type { using type = typename V::local_date_type ; }; template struct enum_to_type { using type = typename V::local_time_type ; }; template struct enum_to_type { using type = typename V::array_type ; }; template struct enum_to_type { using type = typename V::table_type ; }; template using enum_to_type_t = typename enum_to_type::type; template struct enum_to_fmt_type { using type = void; }; template<> struct enum_to_fmt_type { using type = boolean_format_info ; }; template<> struct enum_to_fmt_type { using type = integer_format_info ; }; template<> struct enum_to_fmt_type { using type = floating_format_info ; }; template<> struct enum_to_fmt_type { using type = string_format_info ; }; template<> struct enum_to_fmt_type { using type = offset_datetime_format_info; }; template<> struct enum_to_fmt_type { using type = local_datetime_format_info ; }; template<> struct enum_to_fmt_type { using type = local_date_format_info ; }; template<> struct enum_to_fmt_type { using type = local_time_format_info ; }; template<> struct enum_to_fmt_type { using type = array_format_info ; }; template<> struct enum_to_fmt_type { using type = table_format_info ; }; template using enum_to_fmt_type_t = typename enum_to_fmt_type::type; template struct is_exact_toml_type0 : cxx::disjunction< std::is_same, std::is_same, std::is_same, std::is_same, std::is_same, std::is_same, std::is_same, std::is_same, std::is_same, std::is_same >{}; template struct is_exact_toml_type: is_exact_toml_type0, V> {}; template struct is_not_toml_type : cxx::negation> {}; } // namespace detail } // namespace toml #endif // TOML11_VALUE_T_FWD_HPP #if ! defined(TOML11_COMPILE_SOURCES) #ifndef TOML11_VALUE_T_IMPL_HPP #define TOML11_VALUE_T_IMPL_HPP #include #include #include namespace toml { TOML11_INLINE std::ostream& operator<<(std::ostream& os, value_t t) { switch(t) { case value_t::boolean : os << "boolean"; return os; case value_t::integer : os << "integer"; return os; case value_t::floating : os << "floating"; return os; case value_t::string : os << "string"; return os; case value_t::offset_datetime : os << "offset_datetime"; return os; case value_t::local_datetime : os << "local_datetime"; return os; case value_t::local_date : os << "local_date"; return os; case value_t::local_time : os << "local_time"; return os; case value_t::array : os << "array"; return os; case value_t::table : os << "table"; return os; case value_t::empty : os << "empty"; return os; default : os << "unknown"; return os; } } TOML11_INLINE std::string to_string(value_t t) { std::ostringstream oss; oss << t; return oss.str(); } } // namespace toml #endif // TOML11_VALUE_T_IMPL_HPP #endif #endif // TOML11_VALUE_T_HPP #ifndef TOML11_STORAGE_HPP #define TOML11_STORAGE_HPP namespace toml { namespace detail { // It owns a pointer to T. It does deep-copy when copied. // This struct is introduced to implement a recursive type. // // `toml::value` contains `std::vector` to represent a toml array. // But, in the definition of `toml::value`, `toml::value` is still incomplete. // `std::vector` of an incomplete type is not allowed in C++11 (it is allowed // after C++17). To avoid this, we need to use a pointer to `toml::value`, like // `std::vector>`. Although `std::unique_ptr` is // noncopyable, we want to make `toml::value` copyable. `storage` is introduced // to resolve those problems. template struct storage { using value_type = T; explicit storage(value_type v): ptr_(cxx::make_unique(std::move(v))) {} ~storage() = default; storage(const storage& rhs): ptr_(cxx::make_unique(*rhs.ptr_)) {} storage& operator=(const storage& rhs) { this->ptr_ = cxx::make_unique(*rhs.ptr_); return *this; } storage(storage&&) = default; storage& operator=(storage&&) = default; bool is_ok() const noexcept {return static_cast(ptr_);} value_type& get() const noexcept {return *ptr_;} private: std::unique_ptr ptr_; }; } // detail } // toml #endif // TOML11_STORAGE_HPP #ifndef TOML11_COMMENTS_HPP #define TOML11_COMMENTS_HPP #ifndef TOML11_COMMENTS_FWD_HPP #define TOML11_COMMENTS_FWD_HPP // to use __has_builtin #include #include #include #include #include #include #include #include // This file provides mainly two classes, `preserve_comments` and `discard_comments`. // Those two are a container that have the same interface as `std::vector` // but bahaves in the opposite way. `preserve_comments` is just the same as // `std::vector` and each `std::string` corresponds to a comment line. // Conversely, `discard_comments` discards all the strings and ignores everything // assigned in it. `discard_comments` is always empty and you will encounter an // error whenever you access to the element. namespace toml { class discard_comments; // forward decl class preserve_comments { public: // `container_type` is not provided in discard_comments. // do not use this inner-type in a generic code. using container_type = std::vector; using size_type = container_type::size_type; using difference_type = container_type::difference_type; using value_type = container_type::value_type; using reference = container_type::reference; using const_reference = container_type::const_reference; using pointer = container_type::pointer; using const_pointer = container_type::const_pointer; using iterator = container_type::iterator; using const_iterator = container_type::const_iterator; using reverse_iterator = container_type::reverse_iterator; using const_reverse_iterator = container_type::const_reverse_iterator; public: preserve_comments() = default; ~preserve_comments() = default; preserve_comments(preserve_comments const&) = default; preserve_comments(preserve_comments &&) = default; preserve_comments& operator=(preserve_comments const&) = default; preserve_comments& operator=(preserve_comments &&) = default; explicit preserve_comments(const std::vector& c): comments(c){} explicit preserve_comments(std::vector&& c) : comments(std::move(c)) {} preserve_comments& operator=(const std::vector& c) { comments = c; return *this; } preserve_comments& operator=(std::vector&& c) { comments = std::move(c); return *this; } explicit preserve_comments(const discard_comments&) {} explicit preserve_comments(size_type n): comments(n) {} preserve_comments(size_type n, const std::string& x): comments(n, x) {} preserve_comments(std::initializer_list x): comments(x) {} template preserve_comments(InputIterator first, InputIterator last) : comments(first, last) {} template void assign(InputIterator first, InputIterator last) {comments.assign(first, last);} void assign(std::initializer_list ini) {comments.assign(ini);} void assign(size_type n, const std::string& val) {comments.assign(n, val);} // Related to the issue #97. // // `std::vector::insert` and `std::vector::erase` in the STL implementation // included in GCC 4.8.5 takes `std::vector::iterator` instead of // `std::vector::const_iterator`. It causes compilation error in GCC 4.8.5. #if defined(__GNUC__) && defined(__GNUC_MINOR__) && defined(__GNUC_PATCHLEVEL__) && !defined(__clang__) # if (__GNUC__ * 10000 + __GNUC_MINOR__ * 100 + __GNUC_PATCHLEVEL__) <= 40805 # define TOML11_WORKAROUND_GCC_4_8_X_STANDARD_LIBRARY_IMPLEMENTATION # endif #endif #ifdef TOML11_WORKAROUND_GCC_4_8_X_STANDARD_LIBRARY_IMPLEMENTATION iterator insert(iterator p, const std::string& x) { return comments.insert(p, x); } iterator insert(iterator p, std::string&& x) { return comments.insert(p, std::move(x)); } void insert(iterator p, size_type n, const std::string& x) { return comments.insert(p, n, x); } template void insert(iterator p, InputIterator first, InputIterator last) { return comments.insert(p, first, last); } void insert(iterator p, std::initializer_list ini) { return comments.insert(p, ini); } template iterator emplace(iterator p, Ts&& ... args) { return comments.emplace(p, std::forward(args)...); } iterator erase(iterator pos) {return comments.erase(pos);} iterator erase(iterator first, iterator last) { return comments.erase(first, last); } #else iterator insert(const_iterator p, const std::string& x) { return comments.insert(p, x); } iterator insert(const_iterator p, std::string&& x) { return comments.insert(p, std::move(x)); } iterator insert(const_iterator p, size_type n, const std::string& x) { return comments.insert(p, n, x); } template iterator insert(const_iterator p, InputIterator first, InputIterator last) { return comments.insert(p, first, last); } iterator insert(const_iterator p, std::initializer_list ini) { return comments.insert(p, ini); } template iterator emplace(const_iterator p, Ts&& ... args) { return comments.emplace(p, std::forward(args)...); } iterator erase(const_iterator pos) {return comments.erase(pos);} iterator erase(const_iterator first, const_iterator last) { return comments.erase(first, last); } #endif void swap(preserve_comments& other) {comments.swap(other.comments);} void push_back(const std::string& v) {comments.push_back(v);} void push_back(std::string&& v) {comments.push_back(std::move(v));} void pop_back() {comments.pop_back();} template void emplace_back(Ts&& ... args) {comments.emplace_back(std::forward(args)...);} void clear() {comments.clear();} size_type size() const noexcept {return comments.size();} size_type max_size() const noexcept {return comments.max_size();} size_type capacity() const noexcept {return comments.capacity();} bool empty() const noexcept {return comments.empty();} void reserve(size_type n) {comments.reserve(n);} void resize(size_type n) {comments.resize(n);} void resize(size_type n, const std::string& c) {comments.resize(n, c);} void shrink_to_fit() {comments.shrink_to_fit();} reference operator[](const size_type n) noexcept {return comments[n];} const_reference operator[](const size_type n) const noexcept {return comments[n];} reference at(const size_type n) {return comments.at(n);} const_reference at(const size_type n) const {return comments.at(n);} reference front() noexcept {return comments.front();} const_reference front() const noexcept {return comments.front();} reference back() noexcept {return comments.back();} const_reference back() const noexcept {return comments.back();} pointer data() noexcept {return comments.data();} const_pointer data() const noexcept {return comments.data();} iterator begin() noexcept {return comments.begin();} iterator end() noexcept {return comments.end();} const_iterator begin() const noexcept {return comments.begin();} const_iterator end() const noexcept {return comments.end();} const_iterator cbegin() const noexcept {return comments.cbegin();} const_iterator cend() const noexcept {return comments.cend();} reverse_iterator rbegin() noexcept {return comments.rbegin();} reverse_iterator rend() noexcept {return comments.rend();} const_reverse_iterator rbegin() const noexcept {return comments.rbegin();} const_reverse_iterator rend() const noexcept {return comments.rend();} const_reverse_iterator crbegin() const noexcept {return comments.crbegin();} const_reverse_iterator crend() const noexcept {return comments.crend();} friend bool operator==(const preserve_comments&, const preserve_comments&); friend bool operator!=(const preserve_comments&, const preserve_comments&); friend bool operator< (const preserve_comments&, const preserve_comments&); friend bool operator<=(const preserve_comments&, const preserve_comments&); friend bool operator> (const preserve_comments&, const preserve_comments&); friend bool operator>=(const preserve_comments&, const preserve_comments&); friend void swap(preserve_comments&, std::vector&); friend void swap(std::vector&, preserve_comments&); private: container_type comments; }; bool operator==(const preserve_comments& lhs, const preserve_comments& rhs); bool operator!=(const preserve_comments& lhs, const preserve_comments& rhs); bool operator< (const preserve_comments& lhs, const preserve_comments& rhs); bool operator<=(const preserve_comments& lhs, const preserve_comments& rhs); bool operator> (const preserve_comments& lhs, const preserve_comments& rhs); bool operator>=(const preserve_comments& lhs, const preserve_comments& rhs); void swap(preserve_comments& lhs, preserve_comments& rhs); void swap(preserve_comments& lhs, std::vector& rhs); void swap(std::vector& lhs, preserve_comments& rhs); std::ostream& operator<<(std::ostream& os, const preserve_comments& com); namespace detail { // To provide the same interface with `preserve_comments`, `discard_comments` // should have an iterator. But it does not contain anything, so we need to // add an iterator that points nothing. // // It always points null, so DO NOT unwrap this iterator. It always crashes // your program. template struct empty_iterator { using value_type = T; using reference_type = typename std::conditional::type; using pointer_type = typename std::conditional::type; using difference_type = std::ptrdiff_t; using iterator_category = std::random_access_iterator_tag; empty_iterator() = default; ~empty_iterator() = default; empty_iterator(empty_iterator const&) = default; empty_iterator(empty_iterator &&) = default; empty_iterator& operator=(empty_iterator const&) = default; empty_iterator& operator=(empty_iterator &&) = default; // DO NOT call these operators. reference_type operator*() const noexcept {std::terminate();} pointer_type operator->() const noexcept {return nullptr;} reference_type operator[](difference_type) const noexcept {return this->operator*();} // These operators do nothing. empty_iterator& operator++() noexcept {return *this;} empty_iterator operator++(int) noexcept {return *this;} empty_iterator& operator--() noexcept {return *this;} empty_iterator operator--(int) noexcept {return *this;} empty_iterator& operator+=(difference_type) noexcept {return *this;} empty_iterator& operator-=(difference_type) noexcept {return *this;} empty_iterator operator+(difference_type) const noexcept {return *this;} empty_iterator operator-(difference_type) const noexcept {return *this;} }; template bool operator==(const empty_iterator&, const empty_iterator&) noexcept {return true;} template bool operator!=(const empty_iterator&, const empty_iterator&) noexcept {return false;} template bool operator< (const empty_iterator&, const empty_iterator&) noexcept {return false;} template bool operator<=(const empty_iterator&, const empty_iterator&) noexcept {return true;} template bool operator> (const empty_iterator&, const empty_iterator&) noexcept {return false;} template bool operator>=(const empty_iterator&, const empty_iterator&) noexcept {return true;} template typename empty_iterator::difference_type operator-(const empty_iterator&, const empty_iterator&) noexcept {return 0;} template empty_iterator operator+(typename empty_iterator::difference_type, const empty_iterator& rhs) noexcept {return rhs;} template empty_iterator operator+(const empty_iterator& lhs, typename empty_iterator::difference_type) noexcept {return lhs;} } // detail // The default comment type. It discards all the comments. It requires only one // byte to contain, so the memory footprint is smaller than preserve_comments. // // It just ignores `push_back`, `insert`, `erase`, and any other modifications. // IT always returns size() == 0, the iterator taken by `begin()` is always the // same as that of `end()`, and accessing through `operator[]` or iterators // always causes a segmentation fault. DO NOT access to the element of this. // // Why this is chose as the default type is because the last version (2.x.y) // does not contain any comments in a value. To minimize the impact on the // efficiency, this is chosen as a default. // // To reduce the memory footprint, later we can try empty base optimization (EBO). class discard_comments { public: using size_type = std::size_t; using difference_type = std::ptrdiff_t; using value_type = std::string; using reference = std::string&; using const_reference = std::string const&; using pointer = std::string*; using const_pointer = std::string const*; using iterator = detail::empty_iterator; using const_iterator = detail::empty_iterator; using reverse_iterator = detail::empty_iterator; using const_reverse_iterator = detail::empty_iterator; public: discard_comments() = default; ~discard_comments() = default; discard_comments(discard_comments const&) = default; discard_comments(discard_comments &&) = default; discard_comments& operator=(discard_comments const&) = default; discard_comments& operator=(discard_comments &&) = default; explicit discard_comments(const std::vector&) noexcept {} explicit discard_comments(std::vector&&) noexcept {} discard_comments& operator=(const std::vector&) noexcept {return *this;} discard_comments& operator=(std::vector&&) noexcept {return *this;} explicit discard_comments(const preserve_comments&) noexcept {} explicit discard_comments(size_type) noexcept {} discard_comments(size_type, const std::string&) noexcept {} discard_comments(std::initializer_list) noexcept {} template discard_comments(InputIterator, InputIterator) noexcept {} template void assign(InputIterator, InputIterator) noexcept {} void assign(std::initializer_list) noexcept {} void assign(size_type, const std::string&) noexcept {} iterator insert(const_iterator, const std::string&) {return iterator{};} iterator insert(const_iterator, std::string&&) {return iterator{};} iterator insert(const_iterator, size_type, const std::string&) {return iterator{};} template iterator insert(const_iterator, InputIterator, InputIterator) {return iterator{};} iterator insert(const_iterator, std::initializer_list) {return iterator{};} template iterator emplace(const_iterator, Ts&& ...) {return iterator{};} iterator erase(const_iterator) {return iterator{};} iterator erase(const_iterator, const_iterator) {return iterator{};} void swap(discard_comments&) {return;} void push_back(const std::string&) {return;} void push_back(std::string&& ) {return;} void pop_back() {return;} template void emplace_back(Ts&& ...) {return;} void clear() {return;} size_type size() const noexcept {return 0;} size_type max_size() const noexcept {return 0;} size_type capacity() const noexcept {return 0;} bool empty() const noexcept {return true;} void reserve(size_type) {return;} void resize(size_type) {return;} void resize(size_type, const std::string&) {return;} void shrink_to_fit() {return;} // DO NOT access to the element of this container. This container is always // empty, so accessing through operator[], front/back, data causes address // error. reference operator[](const size_type) noexcept {never_call("toml::discard_comment::operator[]");} const_reference operator[](const size_type) const noexcept {never_call("toml::discard_comment::operator[]");} reference at(const size_type) {throw std::out_of_range("toml::discard_comment is always empty.");} const_reference at(const size_type) const {throw std::out_of_range("toml::discard_comment is always empty.");} reference front() noexcept {never_call("toml::discard_comment::front");} const_reference front() const noexcept {never_call("toml::discard_comment::front");} reference back() noexcept {never_call("toml::discard_comment::back");} const_reference back() const noexcept {never_call("toml::discard_comment::back");} pointer data() noexcept {return nullptr;} const_pointer data() const noexcept {return nullptr;} iterator begin() noexcept {return iterator{};} iterator end() noexcept {return iterator{};} const_iterator begin() const noexcept {return const_iterator{};} const_iterator end() const noexcept {return const_iterator{};} const_iterator cbegin() const noexcept {return const_iterator{};} const_iterator cend() const noexcept {return const_iterator{};} reverse_iterator rbegin() noexcept {return iterator{};} reverse_iterator rend() noexcept {return iterator{};} const_reverse_iterator rbegin() const noexcept {return const_iterator{};} const_reverse_iterator rend() const noexcept {return const_iterator{};} const_reverse_iterator crbegin() const noexcept {return const_iterator{};} const_reverse_iterator crend() const noexcept {return const_iterator{};} private: [[noreturn]] static void never_call(const char *const this_function) { #if __has_builtin(__builtin_unreachable) __builtin_unreachable(); #endif throw std::logic_error{this_function}; } }; inline bool operator==(const discard_comments&, const discard_comments&) noexcept {return true;} inline bool operator!=(const discard_comments&, const discard_comments&) noexcept {return false;} inline bool operator< (const discard_comments&, const discard_comments&) noexcept {return false;} inline bool operator<=(const discard_comments&, const discard_comments&) noexcept {return true;} inline bool operator> (const discard_comments&, const discard_comments&) noexcept {return false;} inline bool operator>=(const discard_comments&, const discard_comments&) noexcept {return true;} inline void swap(const discard_comments&, const discard_comments&) noexcept {return;} inline std::ostream& operator<<(std::ostream& os, const discard_comments&) {return os;} } // toml11 #endif // TOML11_COMMENTS_FWD_HPP #if ! defined(TOML11_COMPILE_SOURCES) #ifndef TOML11_COMMENTS_IMPL_HPP #define TOML11_COMMENTS_IMPL_HPP namespace toml { TOML11_INLINE bool operator==(const preserve_comments& lhs, const preserve_comments& rhs) {return lhs.comments == rhs.comments;} TOML11_INLINE bool operator!=(const preserve_comments& lhs, const preserve_comments& rhs) {return lhs.comments != rhs.comments;} TOML11_INLINE bool operator< (const preserve_comments& lhs, const preserve_comments& rhs) {return lhs.comments < rhs.comments;} TOML11_INLINE bool operator<=(const preserve_comments& lhs, const preserve_comments& rhs) {return lhs.comments <= rhs.comments;} TOML11_INLINE bool operator> (const preserve_comments& lhs, const preserve_comments& rhs) {return lhs.comments > rhs.comments;} TOML11_INLINE bool operator>=(const preserve_comments& lhs, const preserve_comments& rhs) {return lhs.comments >= rhs.comments;} TOML11_INLINE void swap(preserve_comments& lhs, preserve_comments& rhs) { lhs.swap(rhs); return; } TOML11_INLINE void swap(preserve_comments& lhs, std::vector& rhs) { lhs.comments.swap(rhs); return; } TOML11_INLINE void swap(std::vector& lhs, preserve_comments& rhs) { lhs.swap(rhs.comments); return; } TOML11_INLINE std::ostream& operator<<(std::ostream& os, const preserve_comments& com) { for(const auto& c : com) { if(c.front() != '#') { os << '#'; } os << c << '\n'; } return os; } } // toml11 #endif // TOML11_COMMENTS_IMPL_HPP #endif #endif // TOML11_COMMENTS_HPP #ifndef TOML11_COLOR_HPP #define TOML11_COLOR_HPP #ifndef TOML11_COLOR_FWD_HPP #define TOML11_COLOR_FWD_HPP #include #ifdef TOML11_COLORIZE_ERROR_MESSAGE #define TOML11_ERROR_MESSAGE_COLORIZED true #else #define TOML11_ERROR_MESSAGE_COLORIZED false #endif namespace toml { namespace color { // put ANSI escape sequence to ostream inline namespace ansi { namespace detail { // Control color mode globally class color_mode { public: void enable() noexcept { should_color_ = true; } void disable() noexcept { should_color_ = false; } bool should_color() const noexcept { return should_color_; } private: bool should_color_ = TOML11_ERROR_MESSAGE_COLORIZED; }; inline color_mode& color_status() noexcept { static thread_local color_mode status; return status; } } // detail std::ostream& reset (std::ostream& os); std::ostream& bold (std::ostream& os); std::ostream& grey (std::ostream& os); std::ostream& gray (std::ostream& os); std::ostream& red (std::ostream& os); std::ostream& green (std::ostream& os); std::ostream& yellow (std::ostream& os); std::ostream& blue (std::ostream& os); std::ostream& magenta(std::ostream& os); std::ostream& cyan (std::ostream& os); std::ostream& white (std::ostream& os); } // ansi inline void enable() { return detail::color_status().enable(); } inline void disable() { return detail::color_status().disable(); } inline bool should_color() { return detail::color_status().should_color(); } } // color } // toml #endif // TOML11_COLOR_FWD_HPP #if ! defined(TOML11_COMPILE_SOURCES) #ifndef TOML11_COLOR_IMPL_HPP #define TOML11_COLOR_IMPL_HPP #include namespace toml { namespace color { // put ANSI escape sequence to ostream inline namespace ansi { TOML11_INLINE std::ostream& reset(std::ostream& os) { if(detail::color_status().should_color()) {os << "\033[00m";} return os; } TOML11_INLINE std::ostream& bold(std::ostream& os) { if(detail::color_status().should_color()) {os << "\033[01m";} return os; } TOML11_INLINE std::ostream& grey(std::ostream& os) { if(detail::color_status().should_color()) {os << "\033[30m";} return os; } TOML11_INLINE std::ostream& gray(std::ostream& os) { if(detail::color_status().should_color()) {os << "\033[30m";} return os; } TOML11_INLINE std::ostream& red(std::ostream& os) { if(detail::color_status().should_color()) {os << "\033[31m";} return os; } TOML11_INLINE std::ostream& green(std::ostream& os) { if(detail::color_status().should_color()) {os << "\033[32m";} return os; } TOML11_INLINE std::ostream& yellow(std::ostream& os) { if(detail::color_status().should_color()) {os << "\033[33m";} return os; } TOML11_INLINE std::ostream& blue(std::ostream& os) { if(detail::color_status().should_color()) {os << "\033[34m";} return os; } TOML11_INLINE std::ostream& magenta(std::ostream& os) { if(detail::color_status().should_color()) {os << "\033[35m";} return os; } TOML11_INLINE std::ostream& cyan (std::ostream& os) { if(detail::color_status().should_color()) {os << "\033[36m";} return os; } TOML11_INLINE std::ostream& white (std::ostream& os) { if(detail::color_status().should_color()) {os << "\033[37m";} return os; } } // ansi } // color } // toml #endif // TOML11_COLOR_IMPL_HPP #endif #endif // TOML11_COLOR_HPP #ifndef TOML11_SPEC_HPP #define TOML11_SPEC_HPP #include #include #include namespace toml { struct semantic_version { constexpr semantic_version(std::uint32_t mjr, std::uint32_t mnr, std::uint32_t p) noexcept : major{mjr}, minor{mnr}, patch{p} {} std::uint32_t major; std::uint32_t minor; std::uint32_t patch; }; constexpr inline semantic_version make_semver(std::uint32_t mjr, std::uint32_t mnr, std::uint32_t p) noexcept { return semantic_version(mjr, mnr, p); } constexpr inline bool operator==(const semantic_version& lhs, const semantic_version& rhs) noexcept { return lhs.major == rhs.major && lhs.minor == rhs.minor && lhs.patch == rhs.patch; } constexpr inline bool operator!=(const semantic_version& lhs, const semantic_version& rhs) noexcept { return !(lhs == rhs); } constexpr inline bool operator<(const semantic_version& lhs, const semantic_version& rhs) noexcept { return lhs.major < rhs.major || (lhs.major == rhs.major && lhs.minor < rhs.minor) || (lhs.major == rhs.major && lhs.minor == rhs.minor && lhs.patch < rhs.patch); } constexpr inline bool operator>(const semantic_version& lhs, const semantic_version& rhs) noexcept { return rhs < lhs; } constexpr inline bool operator<=(const semantic_version& lhs, const semantic_version& rhs) noexcept { return !(lhs > rhs); } constexpr inline bool operator>=(const semantic_version& lhs, const semantic_version& rhs) noexcept { return !(lhs < rhs); } inline std::ostream& operator<<(std::ostream& os, const semantic_version& v) { os << v.major << '.' << v.minor << '.' << v.patch; return os; } inline std::string to_string(const semantic_version& v) { std::ostringstream oss; oss << v; return oss.str(); } struct spec { constexpr static spec default_version() noexcept { return spec::v(1, 0, 0); } constexpr static spec v(std::uint32_t mjr, std::uint32_t mnr, std::uint32_t p) noexcept { return spec(make_semver(mjr, mnr, p)); } constexpr explicit spec(const semantic_version& semver) noexcept : version{semver}, v1_1_0_allow_control_characters_in_comments {semantic_version{1, 1, 0} <= semver}, v1_1_0_allow_newlines_in_inline_tables {semantic_version{1, 1, 0} <= semver}, v1_1_0_allow_trailing_comma_in_inline_tables{semantic_version{1, 1, 0} <= semver}, v1_1_0_allow_non_english_in_bare_keys {semantic_version{1, 1, 0} <= semver}, v1_1_0_add_escape_sequence_e {semantic_version{1, 1, 0} <= semver}, v1_1_0_add_escape_sequence_x {semantic_version{1, 1, 0} <= semver}, v1_1_0_make_seconds_optional {semantic_version{1, 1, 0} <= semver}, ext_hex_float {false}, ext_num_suffix{false}, ext_null_value{false} {} semantic_version version; // toml version // diff from v1.0.0 -> v1.1.0 bool v1_1_0_allow_control_characters_in_comments; bool v1_1_0_allow_newlines_in_inline_tables; bool v1_1_0_allow_trailing_comma_in_inline_tables; bool v1_1_0_allow_non_english_in_bare_keys; bool v1_1_0_add_escape_sequence_e; bool v1_1_0_add_escape_sequence_x; bool v1_1_0_make_seconds_optional; // library extensions bool ext_hex_float; // allow hex float (in C++ style) bool ext_num_suffix; // allow number suffix (in C++ style) bool ext_null_value; // allow `null` as a value }; } // namespace toml #endif // TOML11_SPEC_HPP #ifndef TOML11_ORDERED_MAP_HPP #define TOML11_ORDERED_MAP_HPP #include #include #include #include namespace toml { namespace detail { template struct ordered_map_ebo_container { Cmp cmp_; // empty base optimization for empty Cmp type }; } // detail template, typename Allocator = std::allocator>> class ordered_map : detail::ordered_map_ebo_container { public: using key_type = Key; using mapped_type = Val; using value_type = std::pair; using key_compare = Cmp; using allocator_type = Allocator; using container_type = std::vector; using reference = typename container_type::reference; using pointer = typename container_type::pointer; using const_reference = typename container_type::const_reference; using const_pointer = typename container_type::const_pointer; using iterator = typename container_type::iterator; using const_iterator = typename container_type::const_iterator; using size_type = typename container_type::size_type; using difference_type = typename container_type::difference_type; private: using ebo_base = detail::ordered_map_ebo_container; public: ordered_map() = default; ~ordered_map() = default; ordered_map(const ordered_map&) = default; ordered_map(ordered_map&&) = default; ordered_map& operator=(const ordered_map&) = default; ordered_map& operator=(ordered_map&&) = default; ordered_map(const ordered_map& other, const Allocator& alloc) : container_(other.container_, alloc) {} ordered_map(ordered_map&& other, const Allocator& alloc) : container_(std::move(other.container_), alloc) {} explicit ordered_map(const Cmp& cmp, const Allocator& alloc = Allocator()) : ebo_base{cmp}, container_(alloc) {} explicit ordered_map(const Allocator& alloc) : container_(alloc) {} template ordered_map(InputIterator first, InputIterator last, const Cmp& cmp = Cmp(), const Allocator& alloc = Allocator()) : ebo_base{cmp}, container_(first, last, alloc) {} template ordered_map(InputIterator first, InputIterator last, const Allocator& alloc) : container_(first, last, alloc) {} ordered_map(std::initializer_list v, const Cmp& cmp = Cmp(), const Allocator& alloc = Allocator()) : ebo_base{cmp}, container_(std::move(v), alloc) {} ordered_map(std::initializer_list v, const Allocator& alloc) : container_(std::move(v), alloc) {} ordered_map& operator=(std::initializer_list v) { this->container_ = std::move(v); return *this; } iterator begin() noexcept {return container_.begin();} iterator end() noexcept {return container_.end();} const_iterator begin() const noexcept {return container_.begin();} const_iterator end() const noexcept {return container_.end();} const_iterator cbegin() const noexcept {return container_.cbegin();} const_iterator cend() const noexcept {return container_.cend();} bool empty() const noexcept {return container_.empty();} std::size_t size() const noexcept {return container_.size();} std::size_t max_size() const noexcept {return container_.max_size();} void clear() {container_.clear();} void push_back(const value_type& v) { if(this->contains(v.first)) { throw std::out_of_range("ordered_map: value already exists"); } container_.push_back(v); } void push_back(value_type&& v) { if(this->contains(v.first)) { throw std::out_of_range("ordered_map: value already exists"); } container_.push_back(std::move(v)); } void emplace_back(key_type k, mapped_type v) { if(this->contains(k)) { throw std::out_of_range("ordered_map: value already exists"); } container_.emplace_back(std::move(k), std::move(v)); } void pop_back() {container_.pop_back();} void insert(value_type kv) { if(this->contains(kv.first)) { throw std::out_of_range("ordered_map: value already exists"); } container_.push_back(std::move(kv)); } void emplace(key_type k, mapped_type v) { if(this->contains(k)) { throw std::out_of_range("ordered_map: value already exists"); } container_.emplace_back(std::move(k), std::move(v)); } std::size_t count(const key_type& key) const { if(this->find(key) != this->end()) { return 1; } else { return 0; } } bool contains(const key_type& key) const { return this->find(key) != this->end(); } iterator find(const key_type& key) noexcept { return std::find_if(this->begin(), this->end(), [&key, this](const value_type& v) {return this->cmp_(v.first, key);}); } const_iterator find(const key_type& key) const noexcept { return std::find_if(this->begin(), this->end(), [&key, this](const value_type& v) {return this->cmp_(v.first, key);}); } mapped_type& at(const key_type& k) { const auto iter = this->find(k); if(iter == this->end()) { throw std::out_of_range("ordered_map: no such element"); } return iter->second; } mapped_type const& at(const key_type& k) const { const auto iter = this->find(k); if(iter == this->end()) { throw std::out_of_range("ordered_map: no such element"); } return iter->second; } mapped_type& operator[](const key_type& k) { const auto iter = this->find(k); if(iter == this->end()) { this->container_.emplace_back(k, mapped_type{}); return this->container_.back().second; } return iter->second; } mapped_type const& operator[](const key_type& k) const { const auto iter = this->find(k); if(iter == this->end()) { throw std::out_of_range("ordered_map: no such element"); } return iter->second; } key_compare key_comp() const {return this->cmp_;} void swap(ordered_map& other) { container_.swap(other.container_); } private: container_type container_; }; template bool operator==(const ordered_map& lhs, const ordered_map& rhs) { return lhs.size() == rhs.size() && std::equal(lhs.begin(), lhs.end(), rhs.begin()); } template bool operator!=(const ordered_map& lhs, const ordered_map& rhs) { return !(lhs == rhs); } template bool operator<(const ordered_map& lhs, const ordered_map& rhs) { return std::lexicographical_compare(lhs.begin(), lhs.end(), rhs.begin(), rhs.end()); } template bool operator>(const ordered_map& lhs, const ordered_map& rhs) { return rhs < lhs; } template bool operator<=(const ordered_map& lhs, const ordered_map& rhs) { return !(lhs > rhs); } template bool operator>=(const ordered_map& lhs, const ordered_map& rhs) { return !(lhs < rhs); } template void swap(ordered_map& lhs, ordered_map& rhs) { lhs.swap(rhs); return; } } // toml #endif // TOML11_ORDERED_MAP_HPP #ifndef TOML11_INTO_HPP #define TOML11_INTO_HPP namespace toml { template struct into; // { // static toml::value into_toml(const T& user_defined_type) // { // // User-defined conversions ... // } // }; } // toml #endif // TOML11_INTO_HPP #ifndef TOML11_FROM_HPP #define TOML11_FROM_HPP namespace toml { template struct from; // { // static T from_toml(const toml::value& v) // { // // User-defined conversions ... // } // }; } // toml #endif // TOML11_FROM_HPP #ifndef TOML11_TRAITS_HPP #define TOML11_TRAITS_HPP #include #include #include #include #include #include #include #if defined(TOML11_HAS_STRING_VIEW) #include #endif namespace toml { template class basic_value; namespace detail { // --------------------------------------------------------------------------- // check whether type T is a kind of container/map class struct has_iterator_impl { template static std::true_type check(typename T::iterator*); template static std::false_type check(...); }; struct has_value_type_impl { template static std::true_type check(typename T::value_type*); template static std::false_type check(...); }; struct has_key_type_impl { template static std::true_type check(typename T::key_type*); template static std::false_type check(...); }; struct has_mapped_type_impl { template static std::true_type check(typename T::mapped_type*); template static std::false_type check(...); }; struct has_reserve_method_impl { template static std::false_type check(...); template static std::true_type check( decltype(std::declval().reserve(std::declval()))*); }; struct has_push_back_method_impl { template static std::false_type check(...); template static std::true_type check( decltype(std::declval().push_back(std::declval()))*); }; struct is_comparable_impl { template static std::false_type check(...); template static std::true_type check( decltype(std::declval() < std::declval())*); }; struct has_from_toml_method_impl { template static std::true_type check( decltype(std::declval().from_toml(std::declval<::toml::basic_value>()))*); template static std::false_type check(...); }; struct has_into_toml_method_impl { template static std::true_type check(decltype(std::declval().into_toml())*); template static std::false_type check(...); }; struct has_template_into_toml_method_impl { template static std::true_type check(decltype(std::declval().template into_toml())*); template static std::false_type check(...); }; struct has_specialized_from_impl { template static std::false_type check(...); template)> static std::true_type check(::toml::from*); }; struct has_specialized_into_impl { template static std::false_type check(...); template)> static std::true_type check(::toml::into*); }; /// Intel C++ compiler can not use decltype in parent class declaration, here /// is a hack to work around it. https://stackoverflow.com/a/23953090/4692076 #ifdef __INTEL_COMPILER #define decltype(...) std::enable_if::type #endif template struct has_iterator: decltype(has_iterator_impl::check(nullptr)){}; template struct has_value_type: decltype(has_value_type_impl::check(nullptr)){}; template struct has_key_type: decltype(has_key_type_impl::check(nullptr)){}; template struct has_mapped_type: decltype(has_mapped_type_impl::check(nullptr)){}; template struct has_reserve_method: decltype(has_reserve_method_impl::check(nullptr)){}; template struct has_push_back_method: decltype(has_push_back_method_impl::check(nullptr)){}; template struct is_comparable: decltype(is_comparable_impl::check(nullptr)){}; template struct has_from_toml_method: decltype(has_from_toml_method_impl::check(nullptr)){}; template struct has_into_toml_method: decltype(has_into_toml_method_impl::check(nullptr)){}; template struct has_template_into_toml_method: decltype(has_template_into_toml_method_impl::check(nullptr)){}; template struct has_specialized_from: decltype(has_specialized_from_impl::check(nullptr)){}; template struct has_specialized_into: decltype(has_specialized_into_impl::check(nullptr)){}; #ifdef __INTEL_COMPILER #undef decltype #endif // --------------------------------------------------------------------------- // type checkers template struct is_std_pair_impl : std::false_type{}; template struct is_std_pair_impl> : std::true_type{}; template using is_std_pair = is_std_pair_impl>; template struct is_std_tuple_impl : std::false_type{}; template struct is_std_tuple_impl> : std::true_type{}; template using is_std_tuple = is_std_tuple_impl>; template struct is_std_array_impl : std::false_type{}; template struct is_std_array_impl> : std::true_type{}; template using is_std_array = is_std_array_impl>; template struct is_std_forward_list_impl : std::false_type{}; template struct is_std_forward_list_impl> : std::true_type{}; template using is_std_forward_list = is_std_forward_list_impl>; template struct is_std_basic_string_impl : std::false_type{}; template struct is_std_basic_string_impl> : std::true_type{}; template using is_std_basic_string = is_std_basic_string_impl>; template struct is_1byte_std_basic_string_impl : std::false_type{}; template struct is_1byte_std_basic_string_impl> : std::integral_constant {}; template using is_1byte_std_basic_string = is_std_basic_string_impl>; #if defined(TOML11_HAS_STRING_VIEW) template struct is_std_basic_string_view_impl : std::false_type{}; template struct is_std_basic_string_view_impl> : std::true_type{}; template using is_std_basic_string_view = is_std_basic_string_view_impl>; template struct is_string_view_of : std::false_type {}; template struct is_string_view_of, std::basic_string> : std::true_type {}; #endif template struct is_chrono_duration_impl: std::false_type{}; template struct is_chrono_duration_impl>: std::true_type{}; template using is_chrono_duration = is_chrono_duration_impl>; template struct is_map_impl : cxx::conjunction< // map satisfies all the following conditions has_iterator, // has T::iterator has_value_type, // has T::value_type has_key_type, // has T::key_type has_mapped_type // has T::mapped_type >{}; template using is_map = is_map_impl>; template struct is_container_impl : cxx::conjunction< cxx::negation>, // not a map cxx::negation>, // not a std::string #ifdef TOML11_HAS_STRING_VIEW cxx::negation>, // not a std::string_view #endif has_iterator, // has T::iterator has_value_type // has T::value_type >{}; template using is_container = is_container_impl>; template struct is_basic_value_impl: std::false_type{}; template struct is_basic_value_impl<::toml::basic_value>: std::true_type{}; template using is_basic_value = is_basic_value_impl>; }// detail }//toml #endif // TOML11_TRAITS_HPP #ifndef TOML11_CONVERSION_HPP #define TOML11_CONVERSION_HPP // use it in the following way. // ```cpp // namespace foo // { // struct Foo // { // std::string s; // double d; // int i; // }; // } // foo // // TOML11_DEFINE_CONVERSION_NON_INTRUSIVE(foo::Foo, s, d, i) // ``` // // And then you can use `toml::get(v)` and `toml::find(file, "foo");` // #define TOML11_STRINGIZE_AUX(x) #x #define TOML11_STRINGIZE(x) TOML11_STRINGIZE_AUX(x) #define TOML11_CONCATENATE_AUX(x, y) x##y #define TOML11_CONCATENATE(x, y) TOML11_CONCATENATE_AUX(x, y) // ============================================================================ // TOML11_DEFINE_CONVERSION_NON_INTRUSIVE #ifndef TOML11_WITHOUT_DEFINE_NON_INTRUSIVE // ---------------------------------------------------------------------------- // TOML11_ARGS_SIZE #define TOML11_INDEX_RSEQ() \ 32, 31, 30, 29, 28, 27, 26, 25, 24, 23, 22, 21, 20, 19, 18, 17, \ 16, 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0 #define TOML11_ARGS_SIZE_IMPL(\ ARG1, ARG2, ARG3, ARG4, ARG5, ARG6, ARG7, ARG8, ARG9, ARG10, \ ARG11, ARG12, ARG13, ARG14, ARG15, ARG16, ARG17, ARG18, ARG19, ARG20, \ ARG21, ARG22, ARG23, ARG24, ARG25, ARG26, ARG27, ARG28, ARG29, ARG30, \ ARG31, ARG32, N, ...) N #define TOML11_ARGS_SIZE_AUX(...) TOML11_ARGS_SIZE_IMPL(__VA_ARGS__) #define TOML11_ARGS_SIZE(...) TOML11_ARGS_SIZE_AUX(__VA_ARGS__, TOML11_INDEX_RSEQ()) // ---------------------------------------------------------------------------- // TOML11_FOR_EACH_VA_ARGS #define TOML11_FOR_EACH_VA_ARGS_AUX_1( FUNCTOR, ARG1 ) FUNCTOR(ARG1) #define TOML11_FOR_EACH_VA_ARGS_AUX_2( FUNCTOR, ARG1, ...) FUNCTOR(ARG1) TOML11_FOR_EACH_VA_ARGS_AUX_1( FUNCTOR, __VA_ARGS__) #define TOML11_FOR_EACH_VA_ARGS_AUX_3( FUNCTOR, ARG1, ...) FUNCTOR(ARG1) TOML11_FOR_EACH_VA_ARGS_AUX_2( FUNCTOR, __VA_ARGS__) #define TOML11_FOR_EACH_VA_ARGS_AUX_4( FUNCTOR, ARG1, ...) FUNCTOR(ARG1) TOML11_FOR_EACH_VA_ARGS_AUX_3( FUNCTOR, __VA_ARGS__) #define TOML11_FOR_EACH_VA_ARGS_AUX_5( FUNCTOR, ARG1, ...) FUNCTOR(ARG1) TOML11_FOR_EACH_VA_ARGS_AUX_4( FUNCTOR, __VA_ARGS__) #define TOML11_FOR_EACH_VA_ARGS_AUX_6( FUNCTOR, ARG1, ...) FUNCTOR(ARG1) TOML11_FOR_EACH_VA_ARGS_AUX_5( FUNCTOR, __VA_ARGS__) #define TOML11_FOR_EACH_VA_ARGS_AUX_7( FUNCTOR, ARG1, ...) FUNCTOR(ARG1) TOML11_FOR_EACH_VA_ARGS_AUX_6( FUNCTOR, __VA_ARGS__) #define TOML11_FOR_EACH_VA_ARGS_AUX_8( FUNCTOR, ARG1, ...) FUNCTOR(ARG1) TOML11_FOR_EACH_VA_ARGS_AUX_7( FUNCTOR, __VA_ARGS__) #define TOML11_FOR_EACH_VA_ARGS_AUX_9( FUNCTOR, ARG1, ...) FUNCTOR(ARG1) TOML11_FOR_EACH_VA_ARGS_AUX_8( FUNCTOR, __VA_ARGS__) #define TOML11_FOR_EACH_VA_ARGS_AUX_10(FUNCTOR, ARG1, ...) FUNCTOR(ARG1) TOML11_FOR_EACH_VA_ARGS_AUX_9( FUNCTOR, __VA_ARGS__) #define TOML11_FOR_EACH_VA_ARGS_AUX_11(FUNCTOR, ARG1, ...) FUNCTOR(ARG1) TOML11_FOR_EACH_VA_ARGS_AUX_10(FUNCTOR, __VA_ARGS__) #define TOML11_FOR_EACH_VA_ARGS_AUX_12(FUNCTOR, ARG1, ...) FUNCTOR(ARG1) TOML11_FOR_EACH_VA_ARGS_AUX_11(FUNCTOR, __VA_ARGS__) #define TOML11_FOR_EACH_VA_ARGS_AUX_13(FUNCTOR, ARG1, ...) FUNCTOR(ARG1) TOML11_FOR_EACH_VA_ARGS_AUX_12(FUNCTOR, __VA_ARGS__) #define TOML11_FOR_EACH_VA_ARGS_AUX_14(FUNCTOR, ARG1, ...) FUNCTOR(ARG1) TOML11_FOR_EACH_VA_ARGS_AUX_13(FUNCTOR, __VA_ARGS__) #define TOML11_FOR_EACH_VA_ARGS_AUX_15(FUNCTOR, ARG1, ...) FUNCTOR(ARG1) TOML11_FOR_EACH_VA_ARGS_AUX_14(FUNCTOR, __VA_ARGS__) #define TOML11_FOR_EACH_VA_ARGS_AUX_16(FUNCTOR, ARG1, ...) FUNCTOR(ARG1) TOML11_FOR_EACH_VA_ARGS_AUX_15(FUNCTOR, __VA_ARGS__) #define TOML11_FOR_EACH_VA_ARGS_AUX_17(FUNCTOR, ARG1, ...) FUNCTOR(ARG1) TOML11_FOR_EACH_VA_ARGS_AUX_16(FUNCTOR, __VA_ARGS__) #define TOML11_FOR_EACH_VA_ARGS_AUX_18(FUNCTOR, ARG1, ...) FUNCTOR(ARG1) TOML11_FOR_EACH_VA_ARGS_AUX_17(FUNCTOR, __VA_ARGS__) #define TOML11_FOR_EACH_VA_ARGS_AUX_19(FUNCTOR, ARG1, ...) FUNCTOR(ARG1) TOML11_FOR_EACH_VA_ARGS_AUX_18(FUNCTOR, __VA_ARGS__) #define TOML11_FOR_EACH_VA_ARGS_AUX_20(FUNCTOR, ARG1, ...) FUNCTOR(ARG1) TOML11_FOR_EACH_VA_ARGS_AUX_19(FUNCTOR, __VA_ARGS__) #define TOML11_FOR_EACH_VA_ARGS_AUX_21(FUNCTOR, ARG1, ...) FUNCTOR(ARG1) TOML11_FOR_EACH_VA_ARGS_AUX_20(FUNCTOR, __VA_ARGS__) #define TOML11_FOR_EACH_VA_ARGS_AUX_22(FUNCTOR, ARG1, ...) FUNCTOR(ARG1) TOML11_FOR_EACH_VA_ARGS_AUX_21(FUNCTOR, __VA_ARGS__) #define TOML11_FOR_EACH_VA_ARGS_AUX_23(FUNCTOR, ARG1, ...) FUNCTOR(ARG1) TOML11_FOR_EACH_VA_ARGS_AUX_22(FUNCTOR, __VA_ARGS__) #define TOML11_FOR_EACH_VA_ARGS_AUX_24(FUNCTOR, ARG1, ...) FUNCTOR(ARG1) TOML11_FOR_EACH_VA_ARGS_AUX_23(FUNCTOR, __VA_ARGS__) #define TOML11_FOR_EACH_VA_ARGS_AUX_25(FUNCTOR, ARG1, ...) FUNCTOR(ARG1) TOML11_FOR_EACH_VA_ARGS_AUX_24(FUNCTOR, __VA_ARGS__) #define TOML11_FOR_EACH_VA_ARGS_AUX_26(FUNCTOR, ARG1, ...) FUNCTOR(ARG1) TOML11_FOR_EACH_VA_ARGS_AUX_25(FUNCTOR, __VA_ARGS__) #define TOML11_FOR_EACH_VA_ARGS_AUX_27(FUNCTOR, ARG1, ...) FUNCTOR(ARG1) TOML11_FOR_EACH_VA_ARGS_AUX_26(FUNCTOR, __VA_ARGS__) #define TOML11_FOR_EACH_VA_ARGS_AUX_28(FUNCTOR, ARG1, ...) FUNCTOR(ARG1) TOML11_FOR_EACH_VA_ARGS_AUX_27(FUNCTOR, __VA_ARGS__) #define TOML11_FOR_EACH_VA_ARGS_AUX_29(FUNCTOR, ARG1, ...) FUNCTOR(ARG1) TOML11_FOR_EACH_VA_ARGS_AUX_28(FUNCTOR, __VA_ARGS__) #define TOML11_FOR_EACH_VA_ARGS_AUX_30(FUNCTOR, ARG1, ...) FUNCTOR(ARG1) TOML11_FOR_EACH_VA_ARGS_AUX_29(FUNCTOR, __VA_ARGS__) #define TOML11_FOR_EACH_VA_ARGS_AUX_31(FUNCTOR, ARG1, ...) FUNCTOR(ARG1) TOML11_FOR_EACH_VA_ARGS_AUX_30(FUNCTOR, __VA_ARGS__) #define TOML11_FOR_EACH_VA_ARGS_AUX_32(FUNCTOR, ARG1, ...) FUNCTOR(ARG1) TOML11_FOR_EACH_VA_ARGS_AUX_31(FUNCTOR, __VA_ARGS__) #define TOML11_FOR_EACH_VA_ARGS(FUNCTOR, ...)\ TOML11_CONCATENATE(TOML11_FOR_EACH_VA_ARGS_AUX_, TOML11_ARGS_SIZE(__VA_ARGS__))(FUNCTOR, __VA_ARGS__) #define TOML11_FIND_MEMBER_VARIABLE_FROM_VALUE(VAR_NAME)\ obj.VAR_NAME = toml::find(v, TOML11_STRINGIZE(VAR_NAME)); #define TOML11_ASSIGN_MEMBER_VARIABLE_TO_VALUE(VAR_NAME)\ v[TOML11_STRINGIZE(VAR_NAME)] = obj.VAR_NAME; #define TOML11_DEFINE_CONVERSION_NON_INTRUSIVE(NAME, ...)\ namespace toml { \ template<> \ struct from \ { \ template \ static NAME from_toml(const basic_value& v) \ { \ NAME obj; \ TOML11_FOR_EACH_VA_ARGS(TOML11_FIND_MEMBER_VARIABLE_FROM_VALUE, __VA_ARGS__) \ return obj; \ } \ }; \ template<> \ struct into \ { \ template \ static basic_value into_toml(const NAME& obj) \ { \ ::toml::basic_value v = typename ::toml::basic_value::table_type{}; \ TOML11_FOR_EACH_VA_ARGS(TOML11_ASSIGN_MEMBER_VARIABLE_TO_VALUE, __VA_ARGS__) \ return v; \ } \ }; \ } /* toml */ #endif// TOML11_WITHOUT_DEFINE_NON_INTRUSIVE #endif // TOML11_CONVERSION_HPP #ifndef TOML11_EXCEPTION_HPP #define TOML11_EXCEPTION_HPP #include namespace toml { struct exception : public std::exception { public: virtual ~exception() noexcept override = default; virtual const char* what() const noexcept override {return "";} }; } // toml #endif // TOMl11_EXCEPTION_HPP #ifndef TOML11_RESULT_HPP #define TOML11_RESULT_HPP #include #include #include #include #include namespace toml { struct bad_result_access final : public ::toml::exception { public: explicit bad_result_access(std::string what_arg) : what_(std::move(what_arg)) {} ~bad_result_access() noexcept override = default; const char* what() const noexcept override {return what_.c_str();} private: std::string what_; }; // ----------------------------------------------------------------------------- template struct success { static_assert( ! std::is_same::value, ""); using value_type = T; explicit success(value_type v) noexcept(std::is_nothrow_move_constructible::value) : value(std::move(v)) {} template, T>::value, std::nullptr_t> = nullptr> explicit success(U&& v): value(std::forward(v)) {} template explicit success(success v): value(std::move(v.value)) {} ~success() = default; success(const success&) = default; success(success&&) = default; success& operator=(const success&) = default; success& operator=(success&&) = default; value_type& get() noexcept {return value;} value_type const& get() const noexcept {return value;} private: value_type value; }; template struct success> { static_assert( ! std::is_same::value, ""); using value_type = T; explicit success(std::reference_wrapper v) noexcept : value(std::move(v)) {} ~success() = default; success(const success&) = default; success(success&&) = default; success& operator=(const success&) = default; success& operator=(success&&) = default; value_type& get() noexcept {return value.get();} value_type const& get() const noexcept {return value.get();} private: std::reference_wrapper value; }; template success::type> ok(T&& v) { return success::type>(std::forward(v)); } template success ok(const char (&literal)[N]) { return success(std::string(literal)); } // ----------------------------------------------------------------------------- template struct failure { using value_type = T; explicit failure(value_type v) noexcept(std::is_nothrow_move_constructible::value) : value(std::move(v)) {} template, T>::value, std::nullptr_t> = nullptr> explicit failure(U&& v): value(std::forward(v)) {} template explicit failure(failure v): value(std::move(v.value)) {} ~failure() = default; failure(const failure&) = default; failure(failure&&) = default; failure& operator=(const failure&) = default; failure& operator=(failure&&) = default; value_type& get() noexcept {return value;} value_type const& get() const noexcept {return value;} private: value_type value; }; template struct failure> { using value_type = T; explicit failure(std::reference_wrapper v) noexcept : value(std::move(v)) {} ~failure() = default; failure(const failure&) = default; failure(failure&&) = default; failure& operator=(const failure&) = default; failure& operator=(failure&&) = default; value_type& get() noexcept {return value.get();} value_type const& get() const noexcept {return value.get();} private: std::reference_wrapper value; }; template failure::type> err(T&& v) { return failure::type>(std::forward(v)); } template failure err(const char (&literal)[N]) { return failure(std::string(literal)); } /* ============================================================================ * _ _ * _ _ ___ ____ _| | |_ * | '_/ -_|_-< || | | _| * |_| \___/__/\_,_|_|\__| */ template struct result { using success_type = success; using failure_type = failure; using value_type = typename success_type::value_type; using error_type = typename failure_type::value_type; result(success_type s): is_ok_(true), succ_(std::move(s)) {} result(failure_type f): is_ok_(false), fail_(std::move(f)) {} template, value_type>>, std::is_convertible, value_type> >::value, std::nullptr_t> = nullptr> result(success s): is_ok_(true), succ_(std::move(s.value)) {} template, error_type>>, std::is_convertible, error_type> >::value, std::nullptr_t> = nullptr> result(failure f): is_ok_(false), fail_(std::move(f.value)) {} result& operator=(success_type s) { this->cleanup(); this->is_ok_ = true; auto tmp = ::new(std::addressof(this->succ_)) success_type(std::move(s)); assert(tmp == std::addressof(this->succ_)); (void)tmp; return *this; } result& operator=(failure_type f) { this->cleanup(); this->is_ok_ = false; auto tmp = ::new(std::addressof(this->fail_)) failure_type(std::move(f)); assert(tmp == std::addressof(this->fail_)); (void)tmp; return *this; } template result& operator=(success s) { this->cleanup(); this->is_ok_ = true; auto tmp = ::new(std::addressof(this->succ_)) success_type(std::move(s.value)); assert(tmp == std::addressof(this->succ_)); (void)tmp; return *this; } template result& operator=(failure f) { this->cleanup(); this->is_ok_ = false; auto tmp = ::new(std::addressof(this->fail_)) failure_type(std::move(f.value)); assert(tmp == std::addressof(this->fail_)); (void)tmp; return *this; } ~result() noexcept {this->cleanup();} result(const result& other): is_ok_(other.is_ok()) { if(other.is_ok()) { auto tmp = ::new(std::addressof(this->succ_)) success_type(other.succ_); assert(tmp == std::addressof(this->succ_)); (void)tmp; } else { auto tmp = ::new(std::addressof(this->fail_)) failure_type(other.fail_); assert(tmp == std::addressof(this->fail_)); (void)tmp; } } result(result&& other): is_ok_(other.is_ok()) { if(other.is_ok()) { auto tmp = ::new(std::addressof(this->succ_)) success_type(std::move(other.succ_)); assert(tmp == std::addressof(this->succ_)); (void)tmp; } else { auto tmp = ::new(std::addressof(this->fail_)) failure_type(std::move(other.fail_)); assert(tmp == std::addressof(this->fail_)); (void)tmp; } } result& operator=(const result& other) { this->cleanup(); if(other.is_ok()) { auto tmp = ::new(std::addressof(this->succ_)) success_type(other.succ_); assert(tmp == std::addressof(this->succ_)); (void)tmp; } else { auto tmp = ::new(std::addressof(this->fail_)) failure_type(other.fail_); assert(tmp == std::addressof(this->fail_)); (void)tmp; } is_ok_ = other.is_ok(); return *this; } result& operator=(result&& other) { this->cleanup(); if(other.is_ok()) { auto tmp = ::new(std::addressof(this->succ_)) success_type(std::move(other.succ_)); assert(tmp == std::addressof(this->succ_)); (void)tmp; } else { auto tmp = ::new(std::addressof(this->fail_)) failure_type(std::move(other.fail_)); assert(tmp == std::addressof(this->fail_)); (void)tmp; } is_ok_ = other.is_ok(); return *this; } template, value_type>>, cxx::negation, error_type>>, std::is_convertible, value_type>, std::is_convertible, error_type> >::value, std::nullptr_t> = nullptr> result(result other): is_ok_(other.is_ok()) { if(other.is_ok()) { auto tmp = ::new(std::addressof(this->succ_)) success_type(std::move(other.as_ok())); assert(tmp == std::addressof(this->succ_)); (void)tmp; } else { auto tmp = ::new(std::addressof(this->fail_)) failure_type(std::move(other.as_err())); assert(tmp == std::addressof(this->fail_)); (void)tmp; } } template, value_type>>, cxx::negation, error_type>>, std::is_convertible, value_type>, std::is_convertible, error_type> >::value, std::nullptr_t> = nullptr> result& operator=(result other) { this->cleanup(); if(other.is_ok()) { auto tmp = ::new(std::addressof(this->succ_)) success_type(std::move(other.as_ok())); assert(tmp == std::addressof(this->succ_)); (void)tmp; } else { auto tmp = ::new(std::addressof(this->fail_)) failure_type(std::move(other.as_err())); assert(tmp == std::addressof(this->fail_)); (void)tmp; } is_ok_ = other.is_ok(); return *this; } bool is_ok() const noexcept {return is_ok_;} bool is_err() const noexcept {return !is_ok_;} explicit operator bool() const noexcept {return is_ok_;} value_type& unwrap(cxx::source_location loc = cxx::source_location::current()) { if(this->is_err()) { throw bad_result_access("toml::result: bad unwrap" + cxx::to_string(loc)); } return this->succ_.get(); } value_type const& unwrap(cxx::source_location loc = cxx::source_location::current()) const { if(this->is_err()) { throw bad_result_access("toml::result: bad unwrap" + cxx::to_string(loc)); } return this->succ_.get(); } value_type& unwrap_or(value_type& opt) noexcept { if(this->is_err()) {return opt;} return this->succ_.get(); } value_type const& unwrap_or(value_type const& opt) const noexcept { if(this->is_err()) {return opt;} return this->succ_.get(); } error_type& unwrap_err(cxx::source_location loc = cxx::source_location::current()) { if(this->is_ok()) { throw bad_result_access("toml::result: bad unwrap_err" + cxx::to_string(loc)); } return this->fail_.get(); } error_type const& unwrap_err(cxx::source_location loc = cxx::source_location::current()) const { if(this->is_ok()) { throw bad_result_access("toml::result: bad unwrap_err" + cxx::to_string(loc)); } return this->fail_.get(); } value_type& as_ok() noexcept { assert(this->is_ok()); return this->succ_.get(); } value_type const& as_ok() const noexcept { assert(this->is_ok()); return this->succ_.get(); } error_type& as_err() noexcept { assert(this->is_err()); return this->fail_.get(); } error_type const& as_err() const noexcept { assert(this->is_err()); return this->fail_.get(); } private: void cleanup() noexcept { #if defined(__GNUC__) && ! defined(__clang__) #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wduplicated-branches" #endif if(this->is_ok_) {this->succ_.~success_type();} else {this->fail_.~failure_type();} #if defined(__GNUC__) && ! defined(__clang__) #pragma GCC diagnostic pop #endif return; } private: bool is_ok_; union { success_type succ_; failure_type fail_; }; }; // ---------------------------------------------------------------------------- namespace detail { struct none_t {}; inline bool operator==(const none_t&, const none_t&) noexcept {return true;} inline bool operator!=(const none_t&, const none_t&) noexcept {return false;} inline bool operator< (const none_t&, const none_t&) noexcept {return false;} inline bool operator<=(const none_t&, const none_t&) noexcept {return true;} inline bool operator> (const none_t&, const none_t&) noexcept {return false;} inline bool operator>=(const none_t&, const none_t&) noexcept {return true;} inline std::ostream& operator<<(std::ostream& os, const none_t&) { os << "none"; return os; } } // detail inline success ok() noexcept { return success(detail::none_t{}); } inline failure err() noexcept { return failure(detail::none_t{}); } } // toml #endif // TOML11_RESULT_HPP #ifndef TOML11_UTILITY_HPP #define TOML11_UTILITY_HPP #include #include #include #include #include namespace toml { namespace detail { // to output character in an error message. inline std::string show_char(const int c) { using char_type = unsigned char; if(std::isgraph(c)) { return std::string(1, static_cast(c)); } else { std::array buf; buf.fill('\0'); const auto r = std::snprintf(buf.data(), buf.size(), "0x%02x", c & 0xFF); assert(r == static_cast(buf.size()) - 1); (void) r; // Unused variable warning auto in_hex = std::string(buf.data()); switch(c) { case char_type('\0'): {in_hex += "(NUL)"; break;} case char_type(' ') : {in_hex += "(SPACE)"; break;} case char_type('\n'): {in_hex += "(LINE FEED)"; break;} case char_type('\r'): {in_hex += "(CARRIAGE RETURN)"; break;} case char_type('\t'): {in_hex += "(TAB)"; break;} case char_type('\v'): {in_hex += "(VERTICAL TAB)"; break;} case char_type('\f'): {in_hex += "(FORM FEED)"; break;} case char_type('\x1B'): {in_hex += "(ESCAPE)"; break;} default: break; } return in_hex; } } // --------------------------------------------------------------------------- template void try_reserve_impl(Container& container, std::size_t N, std::true_type) { container.reserve(N); return; } template void try_reserve_impl(Container&, std::size_t, std::false_type) noexcept { return; } template void try_reserve(Container& container, std::size_t N) { try_reserve_impl(container, N, has_reserve_method{}); return; } // --------------------------------------------------------------------------- template result from_string(const std::string& str) { T v; std::istringstream iss(str); iss >> v; if(iss.fail()) { return err(); } return ok(v); } // --------------------------------------------------------------------------- // helper function to avoid std::string(0, 'c') or std::string(iter, iter) template std::string make_string(Iterator first, Iterator last) { if(first == last) {return "";} return std::string(first, last); } inline std::string make_string(std::size_t len, char c) { if(len == 0) {return "";} return std::string(len, c); } // --------------------------------------------------------------------------- template struct string_conv_impl { static_assert(sizeof(Char) == sizeof(char), ""); static_assert(sizeof(Char2) == sizeof(char), ""); static std::basic_string invoke(std::basic_string s) { std::basic_string retval; std::transform(s.begin(), s.end(), std::back_inserter(retval), [](const Char2 c) {return static_cast(c);}); return retval; } template static std::basic_string invoke(const Char2 (&s)[N]) { std::basic_string retval; // "string literal" has null-char at the end. to skip it, we use prev. std::transform(std::begin(s), std::prev(std::end(s)), std::back_inserter(retval), [](const Char2 c) {return static_cast(c);}); return retval; } }; template struct string_conv_impl { static_assert(sizeof(Char) == sizeof(char), ""); static std::basic_string invoke(std::basic_string s) { return s; } template static std::basic_string invoke(const Char (&s)[N]) { return std::basic_string(s); } }; template cxx::enable_if_t::value, S> string_conv(std::basic_string s) { using C = typename S::value_type; using T = typename S::traits_type; using A = typename S::allocator_type; return string_conv_impl::invoke(std::move(s)); } template cxx::enable_if_t::value, S> string_conv(const char (&s)[N]) { using C = typename S::value_type; using T = typename S::traits_type; using A = typename S::allocator_type; using C2 = char; using T2 = std::char_traits; using A2 = std::allocator; return string_conv_impl::template invoke(s); } } // namespace detail } // namespace toml #endif // TOML11_UTILITY_HPP #ifndef TOML11_LOCATION_HPP #define TOML11_LOCATION_HPP #ifndef TOML11_LOCATION_FWD_HPP #define TOML11_LOCATION_FWD_HPP #include #include #include namespace toml { namespace detail { class region; // fwd decl // // To represent where we are reading in the parse functions. // Since it "points" somewhere in the input stream, the length is always 1. // class location { public: using char_type = unsigned char; // must be unsigned using container_type = std::vector; using difference_type = typename container_type::difference_type; // to suppress sign-conversion warning using source_ptr = std::shared_ptr; public: location(source_ptr src, std::string src_name) : source_(std::move(src)), source_name_(std::move(src_name)), location_(0), line_number_(1) {} location(const location&) = default; location(location&&) = default; location& operator=(const location&) = default; location& operator=(location&&) = default; ~location() = default; void advance(std::size_t n = 1) noexcept; void retrace(std::size_t n = 1) noexcept; bool is_ok() const noexcept { return static_cast(this->source_); } bool eof() const noexcept; char_type current() const; char_type peek(); std::size_t get_location() const noexcept { return this->location_; } void set_location(const std::size_t loc) noexcept; std::size_t line_number() const noexcept { return this->line_number_; } std::string get_line() const; std::size_t column_number() const noexcept; source_ptr const& source() const noexcept {return this->source_;} std::string const& source_name() const noexcept {return this->source_name_;} private: void advance_line_number(const std::size_t n); void retrace_line_number(const std::size_t n); private: friend region; private: source_ptr source_; std::string source_name_; std::size_t location_; // std::vector<>::difference_type is signed std::size_t line_number_; }; bool operator==(const location& lhs, const location& rhs) noexcept; bool operator!=(const location& lhs, const location& rhs); location prev(const location& loc); location next(const location& loc); location make_temporary_location(const std::string& str) noexcept; template result find_if(const location& first, const location& last, const F& func) noexcept { if(first.source() != last.source()) { return err(); } if(first.get_location() >= last.get_location()) { return err(); } auto loc = first; while(loc.get_location() != last.get_location()) { if(func(loc.current())) { return ok(loc); } loc.advance(); } return err(); } template result rfind_if(location first, const location& last, const F& func) { if(first.source() != last.source()) { return err(); } if(first.get_location() >= last.get_location()) { return err(); } auto loc = last; while(loc.get_location() != first.get_location()) { if(func(loc.current())) { return ok(loc); } loc.retrace(); } if(func(first.current())) { return ok(first); } return err(); } result find(const location& first, const location& last, const location::char_type val); result rfind(const location& first, const location& last, const location::char_type val); std::size_t count(const location& first, const location& last, const location::char_type& c); } // detail } // toml #endif // TOML11_LOCATION_FWD_HPP #if ! defined(TOML11_COMPILE_SOURCES) #ifndef TOML11_LOCATION_IMPL_HPP #define TOML11_LOCATION_IMPL_HPP namespace toml { namespace detail { TOML11_INLINE void location::advance(std::size_t n) noexcept { assert(this->is_ok()); if(this->location_ + n < this->source_->size()) { this->advance_line_number(n); this->location_ += n; } else { this->advance_line_number(this->source_->size() - this->location_); this->location_ = this->source_->size(); } } TOML11_INLINE void location::retrace(std::size_t n) noexcept { assert(this->is_ok()); if(this->location_ < n) { this->location_ = 0; this->line_number_ = 1; } else { this->retrace_line_number(n); this->location_ -= n; } } TOML11_INLINE bool location::eof() const noexcept { assert(this->is_ok()); return this->location_ >= this->source_->size(); } TOML11_INLINE location::char_type location::current() const { assert(this->is_ok()); if(this->eof()) {return '\0';} assert(this->location_ < this->source_->size()); return this->source_->at(this->location_); } TOML11_INLINE location::char_type location::peek() { assert(this->is_ok()); if(this->location_ >= this->source_->size()) { return '\0'; } else { return this->source_->at(this->location_ + 1); } } TOML11_INLINE void location::set_location(const std::size_t loc) noexcept { if(this->location_ == loc) { return ; } if(loc == 0) { this->line_number_ = 1; } else if(this->location_ < loc) { const auto d = loc - this->location_; this->advance_line_number(d); } else { const auto d = this->location_ - loc; this->retrace_line_number(d); } this->location_ = loc; } TOML11_INLINE std::string location::get_line() const { assert(this->is_ok()); const auto iter = std::next(this->source_->cbegin(), static_cast(this->location_)); const auto riter = cxx::make_reverse_iterator(iter); const auto prev = std::find(riter, this->source_->crend(), char_type('\n')); const auto next = std::find(iter, this->source_->cend(), char_type('\n')); return make_string(std::next(prev.base()), next); } TOML11_INLINE std::size_t location::column_number() const noexcept { assert(this->is_ok()); const auto iter = std::next(this->source_->cbegin(), static_cast(this->location_)); const auto riter = cxx::make_reverse_iterator(iter); const auto prev = std::find(riter, this->source_->crend(), char_type('\n')); assert(prev.base() <= iter); return static_cast(std::distance(prev.base(), iter) + 1); // 1-origin } TOML11_INLINE void location::advance_line_number(const std::size_t n) { assert(this->is_ok()); assert(this->location_ + n <= this->source_->size()); const auto iter = this->source_->cbegin(); this->line_number_ += static_cast(std::count( std::next(iter, static_cast(this->location_)), std::next(iter, static_cast(this->location_ + n)), char_type('\n'))); return; } TOML11_INLINE void location::retrace_line_number(const std::size_t n) { assert(this->is_ok()); assert(n <= this->location_); // loc - n >= 0 const auto iter = this->source_->cbegin(); const auto dline_num = static_cast(std::count( std::next(iter, static_cast(this->location_ - n)), std::next(iter, static_cast(this->location_)), char_type('\n'))); if(this->line_number_ <= dline_num) { this->line_number_ = 1; } else { this->line_number_ -= dline_num; } return; } TOML11_INLINE bool operator==(const location& lhs, const location& rhs) noexcept { if( ! lhs.is_ok() || ! rhs.is_ok()) { return (!lhs.is_ok()) && (!rhs.is_ok()); } return lhs.source() == rhs.source() && lhs.source_name() == rhs.source_name() && lhs.get_location() == rhs.get_location(); } TOML11_INLINE bool operator!=(const location& lhs, const location& rhs) { return !(lhs == rhs); } TOML11_INLINE location prev(const location& loc) { location p(loc); p.retrace(1); return p; } TOML11_INLINE location next(const location& loc) { location p(loc); p.advance(1); return p; } TOML11_INLINE location make_temporary_location(const std::string& str) noexcept { location::container_type cont(str.size()); std::transform(str.begin(), str.end(), cont.begin(), [](const std::string::value_type& c) { return cxx::bit_cast(c); }); return location(std::make_shared( std::move(cont)), "internal temporary"); } TOML11_INLINE result find(const location& first, const location& last, const location::char_type val) { return find_if(first, last, [val](const location::char_type c) { return c == val; }); } TOML11_INLINE result rfind(const location& first, const location& last, const location::char_type val) { return rfind_if(first, last, [val](const location::char_type c) { return c == val; }); } TOML11_INLINE std::size_t count(const location& first, const location& last, const location::char_type& c) { if(first.source() != last.source()) { return 0; } if(first.get_location() >= last.get_location()) { return 0; } auto loc = first; std::size_t num = 0; while(loc.get_location() != last.get_location()) { if(loc.current() == c) { num += 1; } loc.advance(); } return num; } } // detail } // toml #endif // TOML11_LOCATION_HPP #endif #endif // TOML11_LOCATION_HPP #ifndef TOML11_REGION_HPP #define TOML11_REGION_HPP #ifndef TOML11_REGION_FWD_HPP #define TOML11_REGION_FWD_HPP #include #include #include namespace toml { namespace detail { // // To represent where is a toml::value defined, or where does an error occur. // Stored in toml::value. source_location will be constructed based on this. // class region { public: using char_type = location::char_type; using container_type = location::container_type; using difference_type = location::difference_type; using source_ptr = location::source_ptr; using iterator = typename container_type::iterator; using const_iterator = typename container_type::const_iterator; public: // a value that is constructed manually does not have input stream info region() : source_(nullptr), source_name_(""), length_(0), first_line_(0), first_column_(0), last_line_(0), last_column_(0) {} // a value defined in [first, last). // Those source must be the same. Instread, `region` does not make sense. region(const location& first, const location& last); // shorthand of [loc, loc+1) explicit region(const location& loc); ~region() = default; region(const region&) = default; region(region&&) = default; region& operator=(const region&) = default; region& operator=(region&&) = default; bool is_ok() const noexcept { return static_cast(this->source_); } operator bool() const noexcept { return this->is_ok(); } std::size_t length() const noexcept {return this->length_;} std::size_t first_line_number() const noexcept { return this->first_line_; } std::size_t first_column_number() const noexcept { return this->first_column_; } std::size_t last_line_number() const noexcept { return this->last_line_; } std::size_t last_column_number() const noexcept { return this->last_column_; } char_type at(std::size_t i) const; const_iterator begin() const noexcept; const_iterator end() const noexcept; const_iterator cbegin() const noexcept; const_iterator cend() const noexcept; std::string as_string() const; std::vector as_lines() const; source_ptr const& source() const noexcept {return this->source_;} std::string const& source_name() const noexcept {return this->source_name_;} private: source_ptr source_; std::string source_name_; std::size_t length_; std::size_t first_; std::size_t first_line_; std::size_t first_column_; std::size_t last_; std::size_t last_line_; std::size_t last_column_; }; } // namespace detail } // namespace toml #endif // TOML11_REGION_FWD_HPP #if ! defined(TOML11_COMPILE_SOURCES) #ifndef TOML11_REGION_IMPL_HPP #define TOML11_REGION_IMPL_HPP #include #include #include #include #include #include namespace toml { namespace detail { // a value defined in [first, last). // Those source must be the same. Instread, `region` does not make sense. TOML11_INLINE region::region(const location& first, const location& last) : source_(first.source()), source_name_(first.source_name()), length_(last.get_location() - first.get_location()), first_(first.get_location()), first_line_(first.line_number()), first_column_(first.column_number()), last_(last.get_location()), last_line_(last.line_number()), last_column_(last.column_number()) { assert(first.source() == last.source()); assert(first.source_name() == last.source_name()); } // shorthand of [loc, loc+1) TOML11_INLINE region::region(const location& loc) : source_(loc.source()), source_name_(loc.source_name()), length_(0), first_line_(0), first_column_(0), last_line_(0), last_column_(0) { // if the file ends with LF, the resulting region points no char. if(loc.eof()) { if(loc.get_location() == 0) { this->length_ = 0; this->first_ = 0; this->first_line_ = 0; this->first_column_ = 0; this->last_ = 0; this->last_line_ = 0; this->last_column_ = 0; } else { const auto first = prev(loc); this->first_ = first.get_location(); this->first_line_ = first.line_number(); this->first_column_ = first.column_number(); this->last_ = loc.get_location(); this->last_line_ = loc.line_number(); this->last_column_ = loc.column_number(); this->length_ = 1; } } else { this->first_ = loc.get_location(); this->first_line_ = loc.line_number(); this->first_column_ = loc.column_number(); this->last_ = loc.get_location() + 1; this->last_line_ = loc.line_number(); this->last_column_ = loc.column_number() + 1; this->length_ = 1; } } TOML11_INLINE region::char_type region::at(std::size_t i) const { if(this->last_ <= this->first_ + i) { throw std::out_of_range("range::at: index " + std::to_string(i) + " exceeds length " + std::to_string(this->length_)); } const auto iter = std::next(this->source_->cbegin(), static_cast(this->first_ + i)); return *iter; } TOML11_INLINE region::const_iterator region::begin() const noexcept { return std::next(this->source_->cbegin(), static_cast(this->first_)); } TOML11_INLINE region::const_iterator region::end() const noexcept { return std::next(this->source_->cbegin(), static_cast(this->last_)); } TOML11_INLINE region::const_iterator region::cbegin() const noexcept { return std::next(this->source_->cbegin(), static_cast(this->first_)); } TOML11_INLINE region::const_iterator region::cend() const noexcept { return std::next(this->source_->cbegin(), static_cast(this->last_)); } TOML11_INLINE std::string region::as_string() const { if(this->is_ok()) { const auto begin = std::next(this->source_->cbegin(), static_cast(this->first_)); const auto end = std::next(this->source_->cbegin(), static_cast(this->last_ )); return ::toml::detail::make_string(begin, end); } else { return std::string(""); } } TOML11_INLINE std::vector region::as_lines() const { assert(this->is_ok()); if(this->length_ == 0) { return std::vector{""}; } // Consider the following toml file // ``` // array = [ // ] # comment // ``` // and the region represnets // ``` // [ // ] // ``` // but we want to show the following. // ``` // array = [ // ] # comment // ``` // So we need to find LFs before `begin` and after `end`. // // But, if region ends with LF, it should not include the next line. // ``` // a = 42 // ^^^- with the last LF // ``` // So we start from `end-1` when looking for LF. const auto begin_idx = static_cast(this->first_); const auto end_idx = static_cast(this->last_) - 1; // length_ != 0, so begin < end. then begin <= end-1 assert(begin_idx <= end_idx); const auto begin = std::next(this->source_->cbegin(), begin_idx); const auto end = std::next(this->source_->cbegin(), end_idx); const auto line_begin = std::find(cxx::make_reverse_iterator(begin), this->source_->crend(), char_type('\n')).base(); const auto line_end = std::find(end, this->source_->cend(), char_type('\n')); const auto reg_lines = make_string(line_begin, line_end); if(reg_lines == "") // the region is an empty line that only contains LF { return std::vector{""}; } std::istringstream iss(reg_lines); std::vector lines; std::string line; while(std::getline(iss, line)) { lines.push_back(line); } return lines; } } // namespace detail } // namespace toml #endif // TOML11_REGION_IMPL_HPP #endif #endif // TOML11_REGION_HPP #ifndef TOML11_SOURCE_LOCATION_HPP #define TOML11_SOURCE_LOCATION_HPP #ifndef TOML11_SOURCE_LOCATION_FWD_HPP #define TOML11_SOURCE_LOCATION_FWD_HPP #include #include #include namespace toml { // A struct to contain location in a toml file. struct source_location { public: explicit source_location(const detail::region& r); ~source_location() = default; source_location(source_location const&) = default; source_location(source_location &&) = default; source_location& operator=(source_location const&) = default; source_location& operator=(source_location &&) = default; bool is_ok() const noexcept {return this->is_ok_;} std::size_t length() const noexcept {return this->length_;} std::size_t first_line_number() const noexcept {return this->first_line_;} std::size_t first_column_number() const noexcept {return this->first_column_;} std::size_t last_line_number() const noexcept {return this->last_line_;} std::size_t last_column_number() const noexcept {return this->last_column_;} std::string const& file_name() const noexcept {return this->file_name_;} std::size_t num_lines() const noexcept {return this->line_str_.size();} std::string const& first_line() const; std::string const& last_line() const; std::vector const& lines() const noexcept {return line_str_;} private: bool is_ok_; std::size_t first_line_; std::size_t first_column_; std::size_t last_line_; std::size_t last_column_; std::size_t length_; std::string file_name_; std::vector line_str_; }; namespace detail { std::size_t integer_width_base10(std::size_t i) noexcept; inline std::size_t line_width() noexcept {return 0;} template std::size_t line_width(const source_location& loc, const std::string& /*msg*/, const Ts& ... tail) noexcept { return (std::max)( integer_width_base10(loc.last_line_number()), line_width(tail...)); } std::ostringstream& format_filename(std::ostringstream& oss, const source_location& loc); std::ostringstream& format_empty_line(std::ostringstream& oss, const std::size_t lnw); std::ostringstream& format_line(std::ostringstream& oss, const std::size_t lnw, const std::size_t linenum, const std::string& line); std::ostringstream& format_underline(std::ostringstream& oss, const std::size_t lnw, const std::size_t col, const std::size_t len, const std::string& msg); std::string format_location_impl(const std::size_t lnw, const std::string& prev_fname, const source_location& loc, const std::string& msg); inline std::string format_location_rec(const std::size_t, const std::string&) { return ""; } template std::string format_location_rec(const std::size_t lnw, const std::string& prev_fname, const source_location& loc, const std::string& msg, const Ts& ... tail) { return format_location_impl(lnw, prev_fname, loc, msg) + format_location_rec(lnw, loc.file_name(), tail...); } } // namespace detail // format a location info without title template std::string format_location( const source_location& loc, const std::string& msg, const Ts& ... tail) { const auto lnw = detail::line_width(loc, msg, tail...); const std::string f(""); // at the 1st iteration, no prev_filename is given return detail::format_location_rec(lnw, f, loc, msg, tail...); } } // toml #endif // TOML11_SOURCE_LOCATION_FWD_HPP #if ! defined(TOML11_COMPILE_SOURCES) #ifndef TOML11_SOURCE_LOCATION_IMPL_HPP #define TOML11_SOURCE_LOCATION_IMPL_HPP #include #include #include #include #include namespace toml { TOML11_INLINE source_location::source_location(const detail::region& r) : is_ok_(false), first_line_(1), first_column_(1), last_line_(1), last_column_(1), length_(0), file_name_("unknown file") { if(r.is_ok()) { this->is_ok_ = true; this->file_name_ = r.source_name(); this->first_line_ = r.first_line_number(); this->first_column_ = r.first_column_number(); this->last_line_ = r.last_line_number(); this->last_column_ = r.last_column_number(); this->length_ = r.length(); this->line_str_ = r.as_lines(); } } TOML11_INLINE std::string const& source_location::first_line() const { if(this->line_str_.size() == 0) { throw std::out_of_range("toml::source_location::first_line: `lines` is empty"); } return this->line_str_.front(); } TOML11_INLINE std::string const& source_location::last_line() const { if(this->line_str_.size() == 0) { throw std::out_of_range("toml::source_location::first_line: `lines` is empty"); } return this->line_str_.back(); } namespace detail { TOML11_INLINE std::size_t integer_width_base10(std::size_t i) noexcept { std::size_t width = 0; while(i != 0) { i /= 10; width += 1; } return width; } TOML11_INLINE std::ostringstream& format_filename(std::ostringstream& oss, const source_location& loc) { // --> example.toml oss << color::bold << color::blue << " --> " << color::reset << color::bold << loc.file_name() << '\n' << color::reset; return oss; } TOML11_INLINE std::ostringstream& format_empty_line(std::ostringstream& oss, const std::size_t lnw) { // | oss << detail::make_string(lnw + 1, ' ') << color::bold << color::blue << " |\n" << color::reset; return oss; } TOML11_INLINE std::ostringstream& format_line(std::ostringstream& oss, const std::size_t lnw, const std::size_t linenum, const std::string& line) { // 10 | key = "value" oss << ' ' << color::bold << color::blue << std::setw(static_cast(lnw)) << std::right << linenum << " | " << color::reset; for(const char c : line) { if(std::isgraph(c) || c == ' ') { oss << c; } else { oss << show_char(c); } } oss << '\n'; return oss; } TOML11_INLINE std::ostringstream& format_underline(std::ostringstream& oss, const std::size_t lnw, const std::size_t col, const std::size_t len, const std::string& msg) { // | ^^^^^^^-- this part oss << make_string(lnw + 1, ' ') << color::bold << color::blue << " | " << color::reset; oss << make_string(col-1 /*1-origin*/, ' ') << color::bold << color::red << make_string(len, '^') << "-- " << color::reset << msg << '\n'; return oss; } TOML11_INLINE std::string format_location_impl(const std::size_t lnw, const std::string& prev_fname, const source_location& loc, const std::string& msg) { std::ostringstream oss; if(loc.file_name() != prev_fname) { format_filename(oss, loc); if( ! loc.lines().empty()) { format_empty_line(oss, lnw); } } if(loc.lines().size() == 1) { // when column points LF, it exceeds the size of the first line. std::size_t underline_limit = 1; if(loc.first_line().size() < loc.first_column_number()) { underline_limit = 1; } else { underline_limit = loc.first_line().size() - loc.first_column_number() + 1; } const auto underline_len = (std::min)(underline_limit, loc.length()); format_line(oss, lnw, loc.first_line_number(), loc.first_line()); format_underline(oss, lnw, loc.first_column_number(), underline_len, msg); } else if(loc.lines().size() == 2) { const auto first_underline_len = loc.first_line().size() - loc.first_column_number() + 1; format_line(oss, lnw, loc.first_line_number(), loc.first_line()); format_underline(oss, lnw, loc.first_column_number(), first_underline_len, ""); format_line(oss, lnw, loc.last_line_number(), loc.last_line()); format_underline(oss, lnw, 1, loc.last_column_number(), msg); } else if(loc.lines().size() > 2) { const auto first_underline_len = loc.first_line().size() - loc.first_column_number() + 1; format_line(oss, lnw, loc.first_line_number(), loc.first_line()); format_underline(oss, lnw, loc.first_column_number(), first_underline_len, "and"); if(loc.lines().size() == 3) { format_line(oss, lnw, loc.first_line_number()+1, loc.lines().at(1)); format_underline(oss, lnw, 1, loc.lines().at(1).size(), "and"); } else { format_line(oss, lnw, loc.first_line_number()+1, " ..."); format_empty_line(oss, lnw); } format_line(oss, lnw, loc.last_line_number(), loc.last_line()); format_underline(oss, lnw, 1, loc.last_column_number(), msg); } // if loc is empty, do nothing. return oss.str(); } } // namespace detail } // toml #endif // TOML11_SOURCE_LOCATION_IMPL_HPP #endif #endif // TOML11_SOURCE_LOCATION_HPP #ifndef TOML11_ERROR_INFO_HPP #define TOML11_ERROR_INFO_HPP #ifndef TOML11_ERROR_INFO_FWD_HPP #define TOML11_ERROR_INFO_FWD_HPP namespace toml { // error info returned from parser. struct error_info { error_info(std::string t, source_location l, std::string m, std::string s = "") : title_(std::move(t)), locations_{std::make_pair(std::move(l), std::move(m))}, suffix_(std::move(s)) {} error_info(std::string t, std::vector> l, std::string s = "") : title_(std::move(t)), locations_(std::move(l)), suffix_(std::move(s)) {} std::string const& title() const noexcept {return title_;} std::string & title() noexcept {return title_;} std::vector> const& locations() const noexcept {return locations_;} void add_locations(source_location loc, std::string msg) noexcept { locations_.emplace_back(std::move(loc), std::move(msg)); } std::string const& suffix() const noexcept {return suffix_;} std::string & suffix() noexcept {return suffix_;} private: std::string title_; std::vector> locations_; std::string suffix_; // hint or something like that }; // forward decl template class basic_value; namespace detail { inline error_info make_error_info_rec(error_info e) { return e; } inline error_info make_error_info_rec(error_info e, std::string s) { e.suffix() = s; return e; } template error_info make_error_info_rec(error_info e, const basic_value& v, std::string msg, Ts&& ... tail); template error_info make_error_info_rec(error_info e, source_location loc, std::string msg, Ts&& ... tail) { e.add_locations(std::move(loc), std::move(msg)); return make_error_info_rec(std::move(e), std::forward(tail)...); } } // detail template error_info make_error_info( std::string title, source_location loc, std::string msg, Ts&& ... tail) { error_info ei(std::move(title), std::move(loc), std::move(msg)); return detail::make_error_info_rec(ei, std::forward(tail) ... ); } std::string format_error(const std::string& errkind, const error_info& err); std::string format_error(const error_info& err); // for custom error message template std::string format_error(std::string title, source_location loc, std::string msg, Ts&& ... tail) { return format_error("", make_error_info(std::move(title), std::move(loc), std::move(msg), std::forward(tail)...)); } std::ostream& operator<<(std::ostream& os, const error_info& e); } // toml #endif // TOML11_ERROR_INFO_FWD_HPP #if ! defined(TOML11_COMPILE_SOURCES) #ifndef TOML11_ERROR_INFO_IMPL_HPP #define TOML11_ERROR_INFO_IMPL_HPP #include namespace toml { TOML11_INLINE std::string format_error(const std::string& errkind, const error_info& err) { std::string errmsg; if( ! errkind.empty()) { errmsg = errkind; errmsg += ' '; } errmsg += err.title(); errmsg += '\n'; const auto lnw = [&err]() { std::size_t width = 0; for(const auto& l : err.locations()) { width = (std::max)(detail::integer_width_base10(l.first.last_line_number()), width); } return width; }(); bool first = true; std::string prev_fname; for(const auto& lm : err.locations()) { if( ! first) { std::ostringstream oss; oss << detail::make_string(lnw + 1, ' ') << color::bold << color::blue << " |" << color::reset << color::bold << " ...\n" << color::reset; oss << detail::make_string(lnw + 1, ' ') << color::bold << color::blue << " |\n" << color::reset; errmsg += oss.str(); } const auto& l = lm.first; const auto& m = lm.second; errmsg += detail::format_location_impl(lnw, prev_fname, l, m); prev_fname = l.file_name(); first = false; } errmsg += err.suffix(); return errmsg; } TOML11_INLINE std::string format_error(const error_info& err) { std::ostringstream oss; oss << color::red << color::bold << "[error]" << color::reset; return format_error(oss.str(), err); } TOML11_INLINE std::ostream& operator<<(std::ostream& os, const error_info& e) { os << format_error(e); return os; } } // toml #endif // TOML11_ERROR_INFO_IMPL_HPP #endif #endif // TOML11_ERROR_INFO_HPP #ifndef TOML11_VALUE_HPP #define TOML11_VALUE_HPP #ifdef TOML11_HAS_STRING_VIEW #include #endif #include namespace toml { template class basic_value; struct type_error final : public ::toml::exception { public: type_error(std::string what_arg, source_location loc) : what_(std::move(what_arg)), loc_(std::move(loc)) {} ~type_error() noexcept override = default; const char* what() const noexcept override {return what_.c_str();} source_location const& location() const noexcept {return loc_;} private: std::string what_; source_location loc_; }; // only for internal use namespace detail { template error_info make_type_error(const basic_value&, const std::string&, const value_t); template error_info make_not_found_error(const basic_value&, const std::string&, const typename basic_value::key_type&); template void change_region_of_value(basic_value&, const basic_value&); template struct getter; } // detail template class basic_value { public: using config_type = TypeConfig; using key_type = typename config_type::string_type; using value_type = basic_value; using boolean_type = typename config_type::boolean_type; using integer_type = typename config_type::integer_type; using floating_type = typename config_type::floating_type; using string_type = typename config_type::string_type; using local_time_type = ::toml::local_time; using local_date_type = ::toml::local_date; using local_datetime_type = ::toml::local_datetime; using offset_datetime_type = ::toml::offset_datetime; using array_type = typename config_type::template array_type; using table_type = typename config_type::template table_type; using comment_type = typename config_type::comment_type; using char_type = typename string_type::value_type; private: using region_type = detail::region; public: basic_value() noexcept : type_(value_t::empty), empty_('\0'), region_{}, comments_{} {} ~basic_value() noexcept {this->cleanup();} // copy/move constructor/assigner ===================================== {{{ basic_value(const basic_value& v) : type_(v.type_), region_(v.region_), comments_(v.comments_) { switch(this->type_) { case value_t::boolean : assigner(boolean_ , v.boolean_ ); break; case value_t::integer : assigner(integer_ , v.integer_ ); break; case value_t::floating : assigner(floating_ , v.floating_ ); break; case value_t::string : assigner(string_ , v.string_ ); break; case value_t::offset_datetime: assigner(offset_datetime_, v.offset_datetime_); break; case value_t::local_datetime : assigner(local_datetime_ , v.local_datetime_ ); break; case value_t::local_date : assigner(local_date_ , v.local_date_ ); break; case value_t::local_time : assigner(local_time_ , v.local_time_ ); break; case value_t::array : assigner(array_ , v.array_ ); break; case value_t::table : assigner(table_ , v.table_ ); break; default : assigner(empty_ , '\0' ); break; } } basic_value(basic_value&& v) : type_(v.type()), region_(std::move(v.region_)), comments_(std::move(v.comments_)) { switch(this->type_) { case value_t::boolean : assigner(boolean_ , std::move(v.boolean_ )); break; case value_t::integer : assigner(integer_ , std::move(v.integer_ )); break; case value_t::floating : assigner(floating_ , std::move(v.floating_ )); break; case value_t::string : assigner(string_ , std::move(v.string_ )); break; case value_t::offset_datetime: assigner(offset_datetime_, std::move(v.offset_datetime_)); break; case value_t::local_datetime : assigner(local_datetime_ , std::move(v.local_datetime_ )); break; case value_t::local_date : assigner(local_date_ , std::move(v.local_date_ )); break; case value_t::local_time : assigner(local_time_ , std::move(v.local_time_ )); break; case value_t::array : assigner(array_ , std::move(v.array_ )); break; case value_t::table : assigner(table_ , std::move(v.table_ )); break; default : assigner(empty_ , '\0' ); break; } } basic_value& operator=(const basic_value& v) { if(this == std::addressof(v)) {return *this;} this->cleanup(); this->type_ = v.type_; this->region_ = v.region_; this->comments_ = v.comments_; switch(this->type_) { case value_t::boolean : assigner(boolean_ , v.boolean_ ); break; case value_t::integer : assigner(integer_ , v.integer_ ); break; case value_t::floating : assigner(floating_ , v.floating_ ); break; case value_t::string : assigner(string_ , v.string_ ); break; case value_t::offset_datetime: assigner(offset_datetime_, v.offset_datetime_); break; case value_t::local_datetime : assigner(local_datetime_ , v.local_datetime_ ); break; case value_t::local_date : assigner(local_date_ , v.local_date_ ); break; case value_t::local_time : assigner(local_time_ , v.local_time_ ); break; case value_t::array : assigner(array_ , v.array_ ); break; case value_t::table : assigner(table_ , v.table_ ); break; default : assigner(empty_ , '\0' ); break; } return *this; } basic_value& operator=(basic_value&& v) { if(this == std::addressof(v)) {return *this;} this->cleanup(); this->type_ = v.type_; this->region_ = std::move(v.region_); this->comments_ = std::move(v.comments_); switch(this->type_) { case value_t::boolean : assigner(boolean_ , std::move(v.boolean_ )); break; case value_t::integer : assigner(integer_ , std::move(v.integer_ )); break; case value_t::floating : assigner(floating_ , std::move(v.floating_ )); break; case value_t::string : assigner(string_ , std::move(v.string_ )); break; case value_t::offset_datetime: assigner(offset_datetime_, std::move(v.offset_datetime_)); break; case value_t::local_datetime : assigner(local_datetime_ , std::move(v.local_datetime_ )); break; case value_t::local_date : assigner(local_date_ , std::move(v.local_date_ )); break; case value_t::local_time : assigner(local_time_ , std::move(v.local_time_ )); break; case value_t::array : assigner(array_ , std::move(v.array_ )); break; case value_t::table : assigner(table_ , std::move(v.table_ )); break; default : assigner(empty_ , '\0' ); break; } return *this; } // }}} // constructor to overwrite commnets ================================== {{{ basic_value(basic_value v, std::vector com) : type_(v.type()), region_(std::move(v.region_)), comments_(std::move(com)) { switch(this->type_) { case value_t::boolean : assigner(boolean_ , std::move(v.boolean_ )); break; case value_t::integer : assigner(integer_ , std::move(v.integer_ )); break; case value_t::floating : assigner(floating_ , std::move(v.floating_ )); break; case value_t::string : assigner(string_ , std::move(v.string_ )); break; case value_t::offset_datetime: assigner(offset_datetime_, std::move(v.offset_datetime_)); break; case value_t::local_datetime : assigner(local_datetime_ , std::move(v.local_datetime_ )); break; case value_t::local_date : assigner(local_date_ , std::move(v.local_date_ )); break; case value_t::local_time : assigner(local_time_ , std::move(v.local_time_ )); break; case value_t::array : assigner(array_ , std::move(v.array_ )); break; case value_t::table : assigner(table_ , std::move(v.table_ )); break; default : assigner(empty_ , '\0' ); break; } } // }}} // conversion between different basic_values ========================== {{{ template basic_value(basic_value other) : type_(other.type_), region_(std::move(other.region_)), comments_(std::move(other.comments_)) { switch(other.type_) { // use auto-convert in constructor case value_t::boolean : assigner(boolean_ , std::move(other.boolean_ )); break; case value_t::integer : assigner(integer_ , std::move(other.integer_ )); break; case value_t::floating : assigner(floating_ , std::move(other.floating_ )); break; case value_t::string : assigner(string_ , std::move(other.string_ )); break; case value_t::offset_datetime: assigner(offset_datetime_, std::move(other.offset_datetime_)); break; case value_t::local_datetime : assigner(local_datetime_ , std::move(other.local_datetime_ )); break; case value_t::local_date : assigner(local_date_ , std::move(other.local_date_ )); break; case value_t::local_time : assigner(local_time_ , std::move(other.local_time_ )); break; // may have different container type case value_t::array : { array_type tmp( std::make_move_iterator(other.array_.value.get().begin()), std::make_move_iterator(other.array_.value.get().end())); assigner(array_, array_storage( detail::storage(std::move(tmp)), other.array_.format )); break; } case value_t::table : { table_type tmp( std::make_move_iterator(other.table_.value.get().begin()), std::make_move_iterator(other.table_.value.get().end())); assigner(table_, table_storage( detail::storage(std::move(tmp)), other.table_.format )); break; } default: break; } } template basic_value(basic_value other, std::vector com) : type_(other.type_), region_(std::move(other.region_)), comments_(std::move(com)) { switch(other.type_) { // use auto-convert in constructor case value_t::boolean : assigner(boolean_ , std::move(other.boolean_ )); break; case value_t::integer : assigner(integer_ , std::move(other.integer_ )); break; case value_t::floating : assigner(floating_ , std::move(other.floating_ )); break; case value_t::string : assigner(string_ , std::move(other.string_ )); break; case value_t::offset_datetime: assigner(offset_datetime_, std::move(other.offset_datetime_)); break; case value_t::local_datetime : assigner(local_datetime_ , std::move(other.local_datetime_ )); break; case value_t::local_date : assigner(local_date_ , std::move(other.local_date_ )); break; case value_t::local_time : assigner(local_time_ , std::move(other.local_time_ )); break; // may have different container type case value_t::array : { array_type tmp( std::make_move_iterator(other.array_.value.get().begin()), std::make_move_iterator(other.array_.value.get().end())); assigner(array_, array_storage( detail::storage(std::move(tmp)), other.array_.format )); break; } case value_t::table : { table_type tmp( std::make_move_iterator(other.table_.value.get().begin()), std::make_move_iterator(other.table_.value.get().end())); assigner(table_, table_storage( detail::storage(std::move(tmp)), other.table_.format )); break; } default: break; } } template basic_value& operator=(basic_value other) { this->cleanup(); this->region_ = other.region_; this->comments_ = comment_type(other.comments_); this->type_ = other.type_; switch(other.type_) { // use auto-convert in constructor case value_t::boolean : assigner(boolean_ , std::move(other.boolean_ )); break; case value_t::integer : assigner(integer_ , std::move(other.integer_ )); break; case value_t::floating : assigner(floating_ , std::move(other.floating_ )); break; case value_t::string : assigner(string_ , std::move(other.string_ )); break; case value_t::offset_datetime: assigner(offset_datetime_, std::move(other.offset_datetime_)); break; case value_t::local_datetime : assigner(local_datetime_ , std::move(other.local_datetime_ )); break; case value_t::local_date : assigner(local_date_ , std::move(other.local_date_ )); break; case value_t::local_time : assigner(local_time_ , std::move(other.local_time_ )); break; // may have different container type case value_t::array : { array_type tmp( std::make_move_iterator(other.array_.value.get().begin()), std::make_move_iterator(other.array_.value.get().end())); assigner(array_, array_storage( detail::storage(std::move(tmp)), other.array_.format )); break; } case value_t::table : { table_type tmp( std::make_move_iterator(other.table_.value.get().begin()), std::make_move_iterator(other.table_.value.get().end())); assigner(table_, table_storage( detail::storage(std::move(tmp)), other.table_.format )); break; } default: break; } return *this; } // }}} // constructor (boolean) ============================================== {{{ basic_value(boolean_type x) : basic_value(x, boolean_format_info{}, std::vector{}, region_type{}) {} basic_value(boolean_type x, boolean_format_info fmt) : basic_value(x, fmt, std::vector{}, region_type{}) {} basic_value(boolean_type x, std::vector com) : basic_value(x, boolean_format_info{}, std::move(com), region_type{}) {} basic_value(boolean_type x, boolean_format_info fmt, std::vector com) : basic_value(x, fmt, std::move(com), region_type{}) {} basic_value(boolean_type x, boolean_format_info fmt, std::vector com, region_type reg) : type_(value_t::boolean), boolean_(boolean_storage(x, fmt)), region_(std::move(reg)), comments_(std::move(com)) {} basic_value& operator=(boolean_type x) { boolean_format_info fmt; if(this->is_boolean()) { fmt = this->as_boolean_fmt(); } this->cleanup(); this->type_ = value_t::boolean; this->region_ = region_type{}; assigner(this->boolean_, boolean_storage(x, fmt)); return *this; } // }}} // constructor (integer) ============================================== {{{ basic_value(integer_type x) : basic_value(std::move(x), integer_format_info{}, std::vector{}, region_type{}) {} basic_value(integer_type x, integer_format_info fmt) : basic_value(std::move(x), std::move(fmt), std::vector{}, region_type{}) {} basic_value(integer_type x, std::vector com) : basic_value(std::move(x), integer_format_info{}, std::move(com), region_type{}) {} basic_value(integer_type x, integer_format_info fmt, std::vector com) : basic_value(std::move(x), std::move(fmt), std::move(com), region_type{}) {} basic_value(integer_type x, integer_format_info fmt, std::vector com, region_type reg) : type_(value_t::integer), integer_(integer_storage(std::move(x), std::move(fmt))), region_(std::move(reg)), comments_(std::move(com)) {} basic_value& operator=(integer_type x) { integer_format_info fmt; if(this->is_integer()) { fmt = this->as_integer_fmt(); } this->cleanup(); this->type_ = value_t::integer; this->region_ = region_type{}; assigner(this->integer_, integer_storage(std::move(x), std::move(fmt))); return *this; } private: template using enable_if_integer_like_t = cxx::enable_if_t, boolean_type>>, cxx::negation, integer_type>>, std::is_integral> >::value, std::nullptr_t>; public: template = nullptr> basic_value(T x) : basic_value(std::move(x), integer_format_info{}, std::vector{}, region_type{}) {} template = nullptr> basic_value(T x, integer_format_info fmt) : basic_value(std::move(x), std::move(fmt), std::vector{}, region_type{}) {} template = nullptr> basic_value(T x, std::vector com) : basic_value(std::move(x), integer_format_info{}, std::move(com), region_type{}) {} template = nullptr> basic_value(T x, integer_format_info fmt, std::vector com) : basic_value(std::move(x), std::move(fmt), std::move(com), region_type{}) {} template = nullptr> basic_value(T x, integer_format_info fmt, std::vector com, region_type reg) : type_(value_t::integer), integer_(integer_storage(std::move(x), std::move(fmt))), region_(std::move(reg)), comments_(std::move(com)) {} template = nullptr> basic_value& operator=(T x) { integer_format_info fmt; if(this->is_integer()) { fmt = this->as_integer_fmt(); } this->cleanup(); this->type_ = value_t::integer; this->region_ = region_type{}; assigner(this->integer_, integer_storage(x, std::move(fmt))); return *this; } // }}} // constructor (floating) ============================================= {{{ basic_value(floating_type x) : basic_value(std::move(x), floating_format_info{}, std::vector{}, region_type{}) {} basic_value(floating_type x, floating_format_info fmt) : basic_value(std::move(x), std::move(fmt), std::vector{}, region_type{}) {} basic_value(floating_type x, std::vector com) : basic_value(std::move(x), floating_format_info{}, std::move(com), region_type{}) {} basic_value(floating_type x, floating_format_info fmt, std::vector com) : basic_value(std::move(x), std::move(fmt), std::move(com), region_type{}) {} basic_value(floating_type x, floating_format_info fmt, std::vector com, region_type reg) : type_(value_t::floating), floating_(floating_storage(std::move(x), std::move(fmt))), region_(std::move(reg)), comments_(std::move(com)) {} basic_value& operator=(floating_type x) { floating_format_info fmt; if(this->is_floating()) { fmt = this->as_floating_fmt(); } this->cleanup(); this->type_ = value_t::floating; this->region_ = region_type{}; assigner(this->floating_, floating_storage(std::move(x), std::move(fmt))); return *this; } private: template using enable_if_floating_like_t = cxx::enable_if_t, floating_type>>, std::is_floating_point> >::value, std::nullptr_t>; public: template = nullptr> basic_value(T x) : basic_value(x, floating_format_info{}, std::vector{}, region_type{}) {} template = nullptr> basic_value(T x, floating_format_info fmt) : basic_value(x, std::move(fmt), std::vector{}, region_type{}) {} template = nullptr> basic_value(T x, std::vector com) : basic_value(x, floating_format_info{}, std::move(com), region_type{}) {} template = nullptr> basic_value(T x, floating_format_info fmt, std::vector com) : basic_value(x, std::move(fmt), std::move(com), region_type{}) {} template = nullptr> basic_value(T x, floating_format_info fmt, std::vector com, region_type reg) : type_(value_t::floating), floating_(floating_storage(x, std::move(fmt))), region_(std::move(reg)), comments_(std::move(com)) {} template = nullptr> basic_value& operator=(T x) { floating_format_info fmt; if(this->is_floating()) { fmt = this->as_floating_fmt(); } this->cleanup(); this->type_ = value_t::floating; this->region_ = region_type{}; assigner(this->floating_, floating_storage(x, std::move(fmt))); return *this; } // }}} // constructor (string) =============================================== {{{ basic_value(string_type x) : basic_value(std::move(x), string_format_info{}, std::vector{}, region_type{}) {} basic_value(string_type x, string_format_info fmt) : basic_value(std::move(x), std::move(fmt), std::vector{}, region_type{}) {} basic_value(string_type x, std::vector com) : basic_value(std::move(x), string_format_info{}, std::move(com), region_type{}) {} basic_value(string_type x, string_format_info fmt, std::vector com) : basic_value(std::move(x), std::move(fmt), std::move(com), region_type{}) {} basic_value(string_type x, string_format_info fmt, std::vector com, region_type reg) : type_(value_t::string), string_(string_storage(std::move(x), std::move(fmt))), region_(std::move(reg)), comments_(std::move(com)) {} basic_value& operator=(string_type x) { string_format_info fmt; if(this->is_string()) { fmt = this->as_string_fmt(); } this->cleanup(); this->type_ = value_t::string; this->region_ = region_type{}; assigner(this->string_, string_storage(x, std::move(fmt))); return *this; } // "string literal" basic_value(const typename string_type::value_type* x) : basic_value(x, string_format_info{}, std::vector{}, region_type{}) {} basic_value(const typename string_type::value_type* x, string_format_info fmt) : basic_value(x, std::move(fmt), std::vector{}, region_type{}) {} basic_value(const typename string_type::value_type* x, std::vector com) : basic_value(x, string_format_info{}, std::move(com), region_type{}) {} basic_value(const typename string_type::value_type* x, string_format_info fmt, std::vector com) : basic_value(x, std::move(fmt), std::move(com), region_type{}) {} basic_value(const typename string_type::value_type* x, string_format_info fmt, std::vector com, region_type reg) : type_(value_t::string), string_(string_storage(string_type(x), std::move(fmt))), region_(std::move(reg)), comments_(std::move(com)) {} basic_value& operator=(const typename string_type::value_type* x) { string_format_info fmt; if(this->is_string()) { fmt = this->as_string_fmt(); } this->cleanup(); this->type_ = value_t::string; this->region_ = region_type{}; assigner(this->string_, string_storage(string_type(x), std::move(fmt))); return *this; } #if defined(TOML11_HAS_STRING_VIEW) using string_view_type = std::basic_string_view< typename string_type::value_type, typename string_type::traits_type>; basic_value(string_view_type x) : basic_value(x, string_format_info{}, std::vector{}, region_type{}) {} basic_value(string_view_type x, string_format_info fmt) : basic_value(x, std::move(fmt), std::vector{}, region_type{}) {} basic_value(string_view_type x, std::vector com) : basic_value(x, string_format_info{}, std::move(com), region_type{}) {} basic_value(string_view_type x, string_format_info fmt, std::vector com) : basic_value(x, std::move(fmt), std::move(com), region_type{}) {} basic_value(string_view_type x, string_format_info fmt, std::vector com, region_type reg) : type_(value_t::string), string_(string_storage(string_type(x), std::move(fmt))), region_(std::move(reg)), comments_(std::move(com)) {} basic_value& operator=(string_view_type x) { string_format_info fmt; if(this->is_string()) { fmt = this->as_string_fmt(); } this->cleanup(); this->type_ = value_t::string; this->region_ = region_type{}; assigner(this->string_, string_storage(string_type(x), std::move(fmt))); return *this; } #endif // TOML11_HAS_STRING_VIEW template, string_type>>, detail::is_1byte_std_basic_string >::value, std::nullptr_t> = nullptr> basic_value(const T& x) : basic_value(x, string_format_info{}, std::vector{}, region_type{}) {} template, string_type>>, detail::is_1byte_std_basic_string >::value, std::nullptr_t> = nullptr> basic_value(const T& x, string_format_info fmt) : basic_value(x, std::move(fmt), std::vector{}, region_type{}) {} template, string_type>>, detail::is_1byte_std_basic_string >::value, std::nullptr_t> = nullptr> basic_value(const T& x, std::vector com) : basic_value(x, string_format_info{}, std::move(com), region_type{}) {} template, string_type>>, detail::is_1byte_std_basic_string >::value, std::nullptr_t> = nullptr> basic_value(const T& x, string_format_info fmt, std::vector com) : basic_value(x, std::move(fmt), std::move(com), region_type{}) {} template, string_type>>, detail::is_1byte_std_basic_string >::value, std::nullptr_t> = nullptr> basic_value(const T& x, string_format_info fmt, std::vector com, region_type reg) : type_(value_t::string), string_(string_storage(detail::string_conv(x), std::move(fmt))), region_(std::move(reg)), comments_(std::move(com)) {} template, string_type>>, detail::is_1byte_std_basic_string >::value, std::nullptr_t> = nullptr> basic_value& operator=(const T& x) { string_format_info fmt; if(this->is_string()) { fmt = this->as_string_fmt(); } this->cleanup(); this->type_ = value_t::string; this->region_ = region_type{}; assigner(this->string_, string_storage(detail::string_conv(x), std::move(fmt))); return *this; } // }}} // constructor (local_date) =========================================== {{{ basic_value(local_date_type x) : basic_value(x, local_date_format_info{}, std::vector{}, region_type{}) {} basic_value(local_date_type x, local_date_format_info fmt) : basic_value(x, fmt, std::vector{}, region_type{}) {} basic_value(local_date_type x, std::vector com) : basic_value(x, local_date_format_info{}, std::move(com), region_type{}) {} basic_value(local_date_type x, local_date_format_info fmt, std::vector com) : basic_value(x, fmt, std::move(com), region_type{}) {} basic_value(local_date_type x, local_date_format_info fmt, std::vector com, region_type reg) : type_(value_t::local_date), local_date_(local_date_storage(x, fmt)), region_(std::move(reg)), comments_(std::move(com)) {} basic_value& operator=(local_date_type x) { local_date_format_info fmt; if(this->is_local_date()) { fmt = this->as_local_date_fmt(); } this->cleanup(); this->type_ = value_t::local_date; this->region_ = region_type{}; assigner(this->local_date_, local_date_storage(x, fmt)); return *this; } // }}} // constructor (local_time) =========================================== {{{ basic_value(local_time_type x) : basic_value(x, local_time_format_info{}, std::vector{}, region_type{}) {} basic_value(local_time_type x, local_time_format_info fmt) : basic_value(x, fmt, std::vector{}, region_type{}) {} basic_value(local_time_type x, std::vector com) : basic_value(x, local_time_format_info{}, std::move(com), region_type{}) {} basic_value(local_time_type x, local_time_format_info fmt, std::vector com) : basic_value(x, fmt, std::move(com), region_type{}) {} basic_value(local_time_type x, local_time_format_info fmt, std::vector com, region_type reg) : type_(value_t::local_time), local_time_(local_time_storage(x, fmt)), region_(std::move(reg)), comments_(std::move(com)) {} basic_value& operator=(local_time_type x) { local_time_format_info fmt; if(this->is_local_time()) { fmt = this->as_local_time_fmt(); } this->cleanup(); this->type_ = value_t::local_time; this->region_ = region_type{}; assigner(this->local_time_, local_time_storage(x, fmt)); return *this; } template basic_value(const std::chrono::duration& x) : basic_value(local_time_type(x), local_time_format_info{}, std::vector{}, region_type{}) {} template basic_value(const std::chrono::duration& x, local_time_format_info fmt) : basic_value(local_time_type(x), std::move(fmt), std::vector{}, region_type{}) {} template basic_value(const std::chrono::duration& x, std::vector com) : basic_value(local_time_type(x), local_time_format_info{}, std::move(com), region_type{}) {} template basic_value(const std::chrono::duration& x, local_time_format_info fmt, std::vector com) : basic_value(local_time_type(x), std::move(fmt), std::move(com), region_type{}) {} template basic_value(const std::chrono::duration& x, local_time_format_info fmt, std::vector com, region_type reg) : basic_value(local_time_type(x), std::move(fmt), std::move(com), std::move(reg)) {} template basic_value& operator=(const std::chrono::duration& x) { local_time_format_info fmt; if(this->is_local_time()) { fmt = this->as_local_time_fmt(); } this->cleanup(); this->type_ = value_t::local_time; this->region_ = region_type{}; assigner(this->local_time_, local_time_storage(local_time_type(x), std::move(fmt))); return *this; } // }}} // constructor (local_datetime) =========================================== {{{ basic_value(local_datetime_type x) : basic_value(x, local_datetime_format_info{}, std::vector{}, region_type{}) {} basic_value(local_datetime_type x, local_datetime_format_info fmt) : basic_value(x, fmt, std::vector{}, region_type{}) {} basic_value(local_datetime_type x, std::vector com) : basic_value(x, local_datetime_format_info{}, std::move(com), region_type{}) {} basic_value(local_datetime_type x, local_datetime_format_info fmt, std::vector com) : basic_value(x, fmt, std::move(com), region_type{}) {} basic_value(local_datetime_type x, local_datetime_format_info fmt, std::vector com, region_type reg) : type_(value_t::local_datetime), local_datetime_(local_datetime_storage(x, fmt)), region_(std::move(reg)), comments_(std::move(com)) {} basic_value& operator=(local_datetime_type x) { local_datetime_format_info fmt; if(this->is_local_datetime()) { fmt = this->as_local_datetime_fmt(); } this->cleanup(); this->type_ = value_t::local_datetime; this->region_ = region_type{}; assigner(this->local_datetime_, local_datetime_storage(x, fmt)); return *this; } // }}} // constructor (offset_datetime) =========================================== {{{ basic_value(offset_datetime_type x) : basic_value(x, offset_datetime_format_info{}, std::vector{}, region_type{}) {} basic_value(offset_datetime_type x, offset_datetime_format_info fmt) : basic_value(x, fmt, std::vector{}, region_type{}) {} basic_value(offset_datetime_type x, std::vector com) : basic_value(x, offset_datetime_format_info{}, std::move(com), region_type{}) {} basic_value(offset_datetime_type x, offset_datetime_format_info fmt, std::vector com) : basic_value(x, fmt, std::move(com), region_type{}) {} basic_value(offset_datetime_type x, offset_datetime_format_info fmt, std::vector com, region_type reg) : type_(value_t::offset_datetime), offset_datetime_(offset_datetime_storage(x, fmt)), region_(std::move(reg)), comments_(std::move(com)) {} basic_value& operator=(offset_datetime_type x) { offset_datetime_format_info fmt; if(this->is_offset_datetime()) { fmt = this->as_offset_datetime_fmt(); } this->cleanup(); this->type_ = value_t::offset_datetime; this->region_ = region_type{}; assigner(this->offset_datetime_, offset_datetime_storage(x, fmt)); return *this; } // system_clock::time_point basic_value(std::chrono::system_clock::time_point x) : basic_value(offset_datetime_type(x), offset_datetime_format_info{}, std::vector{}, region_type{}) {} basic_value(std::chrono::system_clock::time_point x, offset_datetime_format_info fmt) : basic_value(offset_datetime_type(x), fmt, std::vector{}, region_type{}) {} basic_value(std::chrono::system_clock::time_point x, std::vector com) : basic_value(offset_datetime_type(x), offset_datetime_format_info{}, std::move(com), region_type{}) {} basic_value(std::chrono::system_clock::time_point x, offset_datetime_format_info fmt, std::vector com) : basic_value(offset_datetime_type(x), fmt, std::move(com), region_type{}) {} basic_value(std::chrono::system_clock::time_point x, offset_datetime_format_info fmt, std::vector com, region_type reg) : basic_value(offset_datetime_type(x), std::move(fmt), std::move(com), std::move(reg)) {} basic_value& operator=(std::chrono::system_clock::time_point x) { offset_datetime_format_info fmt; if(this->is_offset_datetime()) { fmt = this->as_offset_datetime_fmt(); } this->cleanup(); this->type_ = value_t::offset_datetime; this->region_ = region_type{}; assigner(this->offset_datetime_, offset_datetime_storage(offset_datetime_type(x), fmt)); return *this; } // }}} // constructor (array) ================================================ {{{ basic_value(array_type x) : basic_value(std::move(x), array_format_info{}, std::vector{}, region_type{}) {} basic_value(array_type x, array_format_info fmt) : basic_value(std::move(x), std::move(fmt), std::vector{}, region_type{}) {} basic_value(array_type x, std::vector com) : basic_value(std::move(x), array_format_info{}, std::move(com), region_type{}) {} basic_value(array_type x, array_format_info fmt, std::vector com) : basic_value(std::move(x), fmt, std::move(com), region_type{}) {} basic_value(array_type x, array_format_info fmt, std::vector com, region_type reg) : type_(value_t::array), array_(array_storage( detail::storage(std::move(x)), std::move(fmt) )), region_(std::move(reg)), comments_(std::move(com)) {} basic_value& operator=(array_type x) { array_format_info fmt; if(this->is_array()) { fmt = this->as_array_fmt(); } this->cleanup(); this->type_ = value_t::array; this->region_ = region_type{}; assigner(this->array_, array_storage( detail::storage(std::move(x)), std::move(fmt))); return *this; } private: template using enable_if_array_like_t = cxx::enable_if_t, cxx::negation>, cxx::negation>, #if defined(TOML11_HAS_STRING_VIEW) cxx::negation>, #endif cxx::negation>, cxx::negation> >::value, std::nullptr_t>; public: template = nullptr> basic_value(T x) : basic_value(std::move(x), array_format_info{}, std::vector{}, region_type{}) {} template = nullptr> basic_value(T x, array_format_info fmt) : basic_value(std::move(x), std::move(fmt), std::vector{}, region_type{}) {} template = nullptr> basic_value(T x, std::vector com) : basic_value(std::move(x), array_format_info{}, std::move(com), region_type{}) {} template = nullptr> basic_value(T x, array_format_info fmt, std::vector com) : basic_value(std::move(x), fmt, std::move(com), region_type{}) {} template = nullptr> basic_value(T x, array_format_info fmt, std::vector com, region_type reg) : type_(value_t::array), array_(array_storage( detail::storage(array_type( std::make_move_iterator(x.begin()), std::make_move_iterator(x.end())) ), std::move(fmt) )), region_(std::move(reg)), comments_(std::move(com)) {} template = nullptr> basic_value& operator=(T x) { array_format_info fmt; if(this->is_array()) { fmt = this->as_array_fmt(); } this->cleanup(); this->type_ = value_t::array; this->region_ = region_type{}; array_type a(std::make_move_iterator(x.begin()), std::make_move_iterator(x.end())); assigner(this->array_, array_storage( detail::storage(std::move(a)), std::move(fmt))); return *this; } // }}} // constructor (table) ================================================ {{{ basic_value(table_type x) : basic_value(std::move(x), table_format_info{}, std::vector{}, region_type{}) {} basic_value(table_type x, table_format_info fmt) : basic_value(std::move(x), std::move(fmt), std::vector{}, region_type{}) {} basic_value(table_type x, std::vector com) : basic_value(std::move(x), table_format_info{}, std::move(com), region_type{}) {} basic_value(table_type x, table_format_info fmt, std::vector com) : basic_value(std::move(x), fmt, std::move(com), region_type{}) {} basic_value(table_type x, table_format_info fmt, std::vector com, region_type reg) : type_(value_t::table), table_(table_storage( detail::storage(std::move(x)), std::move(fmt) )), region_(std::move(reg)), comments_(std::move(com)) {} basic_value& operator=(table_type x) { table_format_info fmt; if(this->is_table()) { fmt = this->as_table_fmt(); } this->cleanup(); this->type_ = value_t::table; this->region_ = region_type{}; assigner(this->table_, table_storage( detail::storage(std::move(x)), std::move(fmt))); return *this; } // table-like private: template using enable_if_table_like_t = cxx::enable_if_t>, detail::is_map, cxx::negation>, cxx::negation> >::value, std::nullptr_t>; public: template = nullptr> basic_value(T x) : basic_value(std::move(x), table_format_info{}, std::vector{}, region_type{}) {} template = nullptr> basic_value(T x, table_format_info fmt) : basic_value(std::move(x), std::move(fmt), std::vector{}, region_type{}) {} template = nullptr> basic_value(T x, std::vector com) : basic_value(std::move(x), table_format_info{}, std::move(com), region_type{}) {} template = nullptr> basic_value(T x, table_format_info fmt, std::vector com) : basic_value(std::move(x), fmt, std::move(com), region_type{}) {} template = nullptr> basic_value(T x, table_format_info fmt, std::vector com, region_type reg) : type_(value_t::table), table_(table_storage( detail::storage(table_type( std::make_move_iterator(x.begin()), std::make_move_iterator(x.end()) )), std::move(fmt) )), region_(std::move(reg)), comments_(std::move(com)) {} template = nullptr> basic_value& operator=(T x) { table_format_info fmt; if(this->is_table()) { fmt = this->as_table_fmt(); } this->cleanup(); this->type_ = value_t::table; this->region_ = region_type{}; table_type t(std::make_move_iterator(x.begin()), std::make_move_iterator(x.end())); assigner(this->table_, table_storage( detail::storage(std::move(t)), std::move(fmt))); return *this; } // }}} // constructor (user_defined) ========================================= {{{ template::value, std::nullptr_t> = nullptr> basic_value(const T& ud) : basic_value( into>::template into_toml(ud)) {} template::value, std::nullptr_t> = nullptr> basic_value(const T& ud, std::vector com) : basic_value( into>::template into_toml(ud), std::move(com)) {} template::value, std::nullptr_t> = nullptr> basic_value& operator=(const T& ud) { *this = into>::template into_toml(ud); return *this; } template, cxx::negation> >::value, std::nullptr_t> = nullptr> basic_value(const T& ud): basic_value(ud.into_toml()) {} template, cxx::negation> >::value, std::nullptr_t> = nullptr> basic_value(const T& ud, std::vector com) : basic_value(ud.into_toml(), std::move(com)) {} template, cxx::negation> >::value, std::nullptr_t> = nullptr> basic_value& operator=(const T& ud) { *this = ud.into_toml(); return *this; } template, cxx::negation> >::value, std::nullptr_t> = nullptr> basic_value(const T& ud): basic_value(ud.template into_toml()) {} template, cxx::negation> >::value, std::nullptr_t> = nullptr> basic_value(const T& ud, std::vector com) : basic_value(ud.template into_toml(), std::move(com)) {} template, cxx::negation> >::value, std::nullptr_t> = nullptr> basic_value& operator=(const T& ud) { *this = ud.template into_toml(); return *this; } // }}} // empty value with region info ======================================= {{{ // mainly for `null` extension basic_value(detail::none_t, region_type reg) noexcept : type_(value_t::empty), empty_('\0'), region_(std::move(reg)), comments_{} {} // }}} // type checking ====================================================== {{{ template, value_type>::value, std::nullptr_t> = nullptr> bool is() const noexcept { return detail::type_to_enum::value == this->type_; } bool is(value_t t) const noexcept {return t == this->type_;} bool is_empty() const noexcept {return this->is(value_t::empty );} bool is_boolean() const noexcept {return this->is(value_t::boolean );} bool is_integer() const noexcept {return this->is(value_t::integer );} bool is_floating() const noexcept {return this->is(value_t::floating );} bool is_string() const noexcept {return this->is(value_t::string );} bool is_offset_datetime() const noexcept {return this->is(value_t::offset_datetime);} bool is_local_datetime() const noexcept {return this->is(value_t::local_datetime );} bool is_local_date() const noexcept {return this->is(value_t::local_date );} bool is_local_time() const noexcept {return this->is(value_t::local_time );} bool is_array() const noexcept {return this->is(value_t::array );} bool is_table() const noexcept {return this->is(value_t::table );} bool is_array_of_tables() const noexcept { if( ! this->is_array()) {return false;} const auto& a = this->as_array(std::nothrow); // already checked. // when you define [[array.of.tables]], at least one empty table will be // assigned. In case of array of inline tables, `array_of_tables = []`, // there is no reason to consider this as an array of *tables*. // So empty array is not an array-of-tables. if(a.empty()) {return false;} // since toml v1.0.0 allows array of heterogeneous types, we need to // check all the elements. if any of the elements is not a table, it // is a heterogeneous array and cannot be expressed by `[[aot]]` form. for(const auto& e : a) { if( ! e.is_table()) {return false;} } return true; } value_t type() const noexcept {return type_;} // }}} // as_xxx (noexcept) version ========================================== {{{ template detail::enum_to_type_t> const& as(const std::nothrow_t&) const noexcept { return detail::getter::get_nothrow(*this); } template detail::enum_to_type_t>& as(const std::nothrow_t&) noexcept { return detail::getter::get_nothrow(*this); } boolean_type const& as_boolean (const std::nothrow_t&) const noexcept {return this->boolean_.value;} integer_type const& as_integer (const std::nothrow_t&) const noexcept {return this->integer_.value;} floating_type const& as_floating (const std::nothrow_t&) const noexcept {return this->floating_.value;} string_type const& as_string (const std::nothrow_t&) const noexcept {return this->string_.value;} offset_datetime_type const& as_offset_datetime(const std::nothrow_t&) const noexcept {return this->offset_datetime_.value;} local_datetime_type const& as_local_datetime (const std::nothrow_t&) const noexcept {return this->local_datetime_.value;} local_date_type const& as_local_date (const std::nothrow_t&) const noexcept {return this->local_date_.value;} local_time_type const& as_local_time (const std::nothrow_t&) const noexcept {return this->local_time_.value;} array_type const& as_array (const std::nothrow_t&) const noexcept {return this->array_.value.get();} table_type const& as_table (const std::nothrow_t&) const noexcept {return this->table_.value.get();} boolean_type & as_boolean (const std::nothrow_t&) noexcept {return this->boolean_.value;} integer_type & as_integer (const std::nothrow_t&) noexcept {return this->integer_.value;} floating_type & as_floating (const std::nothrow_t&) noexcept {return this->floating_.value;} string_type & as_string (const std::nothrow_t&) noexcept {return this->string_.value;} offset_datetime_type& as_offset_datetime(const std::nothrow_t&) noexcept {return this->offset_datetime_.value;} local_datetime_type & as_local_datetime (const std::nothrow_t&) noexcept {return this->local_datetime_.value;} local_date_type & as_local_date (const std::nothrow_t&) noexcept {return this->local_date_.value;} local_time_type & as_local_time (const std::nothrow_t&) noexcept {return this->local_time_.value;} array_type & as_array (const std::nothrow_t&) noexcept {return this->array_.value.get();} table_type & as_table (const std::nothrow_t&) noexcept {return this->table_.value.get();} // }}} // as_xxx (throw) ===================================================== {{{ template detail::enum_to_type_t> const& as() const { return detail::getter::get(*this); } template detail::enum_to_type_t>& as() { return detail::getter::get(*this); } boolean_type const& as_boolean() const { if(this->type_ != value_t::boolean) { this->throw_bad_cast("toml::value::as_boolean()", value_t::boolean); } return this->boolean_.value; } integer_type const& as_integer() const { if(this->type_ != value_t::integer) { this->throw_bad_cast("toml::value::as_integer()", value_t::integer); } return this->integer_.value; } floating_type const& as_floating() const { if(this->type_ != value_t::floating) { this->throw_bad_cast("toml::value::as_floating()", value_t::floating); } return this->floating_.value; } string_type const& as_string() const { if(this->type_ != value_t::string) { this->throw_bad_cast("toml::value::as_string()", value_t::string); } return this->string_.value; } offset_datetime_type const& as_offset_datetime() const { if(this->type_ != value_t::offset_datetime) { this->throw_bad_cast("toml::value::as_offset_datetime()", value_t::offset_datetime); } return this->offset_datetime_.value; } local_datetime_type const& as_local_datetime() const { if(this->type_ != value_t::local_datetime) { this->throw_bad_cast("toml::value::as_local_datetime()", value_t::local_datetime); } return this->local_datetime_.value; } local_date_type const& as_local_date() const { if(this->type_ != value_t::local_date) { this->throw_bad_cast("toml::value::as_local_date()", value_t::local_date); } return this->local_date_.value; } local_time_type const& as_local_time() const { if(this->type_ != value_t::local_time) { this->throw_bad_cast("toml::value::as_local_time()", value_t::local_time); } return this->local_time_.value; } array_type const& as_array() const { if(this->type_ != value_t::array) { this->throw_bad_cast("toml::value::as_array()", value_t::array); } return this->array_.value.get(); } table_type const& as_table() const { if(this->type_ != value_t::table) { this->throw_bad_cast("toml::value::as_table()", value_t::table); } return this->table_.value.get(); } // ------------------------------------------------------------------------ // nonconst reference boolean_type& as_boolean() { if(this->type_ != value_t::boolean) { this->throw_bad_cast("toml::value::as_boolean()", value_t::boolean); } return this->boolean_.value; } integer_type& as_integer() { if(this->type_ != value_t::integer) { this->throw_bad_cast("toml::value::as_integer()", value_t::integer); } return this->integer_.value; } floating_type& as_floating() { if(this->type_ != value_t::floating) { this->throw_bad_cast("toml::value::as_floating()", value_t::floating); } return this->floating_.value; } string_type& as_string() { if(this->type_ != value_t::string) { this->throw_bad_cast("toml::value::as_string()", value_t::string); } return this->string_.value; } offset_datetime_type& as_offset_datetime() { if(this->type_ != value_t::offset_datetime) { this->throw_bad_cast("toml::value::as_offset_datetime()", value_t::offset_datetime); } return this->offset_datetime_.value; } local_datetime_type& as_local_datetime() { if(this->type_ != value_t::local_datetime) { this->throw_bad_cast("toml::value::as_local_datetime()", value_t::local_datetime); } return this->local_datetime_.value; } local_date_type& as_local_date() { if(this->type_ != value_t::local_date) { this->throw_bad_cast("toml::value::as_local_date()", value_t::local_date); } return this->local_date_.value; } local_time_type& as_local_time() { if(this->type_ != value_t::local_time) { this->throw_bad_cast("toml::value::as_local_time()", value_t::local_time); } return this->local_time_.value; } array_type& as_array() { if(this->type_ != value_t::array) { this->throw_bad_cast("toml::value::as_array()", value_t::array); } return this->array_.value.get(); } table_type& as_table() { if(this->type_ != value_t::table) { this->throw_bad_cast("toml::value::as_table()", value_t::table); } return this->table_.value.get(); } // }}} // format accessors (noexcept) ======================================== {{{ template detail::enum_to_fmt_type_t const& as_fmt(const std::nothrow_t&) const noexcept { return detail::getter::get_fmt_nothrow(*this); } template detail::enum_to_fmt_type_t& as_fmt(const std::nothrow_t&) noexcept { return detail::getter::get_fmt_nothrow(*this); } boolean_format_info & as_boolean_fmt (const std::nothrow_t&) noexcept {return this->boolean_.format;} integer_format_info & as_integer_fmt (const std::nothrow_t&) noexcept {return this->integer_.format;} floating_format_info & as_floating_fmt (const std::nothrow_t&) noexcept {return this->floating_.format;} string_format_info & as_string_fmt (const std::nothrow_t&) noexcept {return this->string_.format;} offset_datetime_format_info& as_offset_datetime_fmt(const std::nothrow_t&) noexcept {return this->offset_datetime_.format;} local_datetime_format_info & as_local_datetime_fmt (const std::nothrow_t&) noexcept {return this->local_datetime_.format;} local_date_format_info & as_local_date_fmt (const std::nothrow_t&) noexcept {return this->local_date_.format;} local_time_format_info & as_local_time_fmt (const std::nothrow_t&) noexcept {return this->local_time_.format;} array_format_info & as_array_fmt (const std::nothrow_t&) noexcept {return this->array_.format;} table_format_info & as_table_fmt (const std::nothrow_t&) noexcept {return this->table_.format;} boolean_format_info const& as_boolean_fmt (const std::nothrow_t&) const noexcept {return this->boolean_.format;} integer_format_info const& as_integer_fmt (const std::nothrow_t&) const noexcept {return this->integer_.format;} floating_format_info const& as_floating_fmt (const std::nothrow_t&) const noexcept {return this->floating_.format;} string_format_info const& as_string_fmt (const std::nothrow_t&) const noexcept {return this->string_.format;} offset_datetime_format_info const& as_offset_datetime_fmt(const std::nothrow_t&) const noexcept {return this->offset_datetime_.format;} local_datetime_format_info const& as_local_datetime_fmt (const std::nothrow_t&) const noexcept {return this->local_datetime_.format;} local_date_format_info const& as_local_date_fmt (const std::nothrow_t&) const noexcept {return this->local_date_.format;} local_time_format_info const& as_local_time_fmt (const std::nothrow_t&) const noexcept {return this->local_time_.format;} array_format_info const& as_array_fmt (const std::nothrow_t&) const noexcept {return this->array_.format;} table_format_info const& as_table_fmt (const std::nothrow_t&) const noexcept {return this->table_.format;} // }}} // format accessors (throw) =========================================== {{{ template detail::enum_to_fmt_type_t const& as_fmt() const { return detail::getter::get_fmt(*this); } template detail::enum_to_fmt_type_t& as_fmt() { return detail::getter::get_fmt(*this); } boolean_format_info const& as_boolean_fmt() const { if(this->type_ != value_t::boolean) { this->throw_bad_cast("toml::value::as_boolean_fmt()", value_t::boolean); } return this->boolean_.format; } integer_format_info const& as_integer_fmt() const { if(this->type_ != value_t::integer) { this->throw_bad_cast("toml::value::as_integer_fmt()", value_t::integer); } return this->integer_.format; } floating_format_info const& as_floating_fmt() const { if(this->type_ != value_t::floating) { this->throw_bad_cast("toml::value::as_floating_fmt()", value_t::floating); } return this->floating_.format; } string_format_info const& as_string_fmt() const { if(this->type_ != value_t::string) { this->throw_bad_cast("toml::value::as_string_fmt()", value_t::string); } return this->string_.format; } offset_datetime_format_info const& as_offset_datetime_fmt() const { if(this->type_ != value_t::offset_datetime) { this->throw_bad_cast("toml::value::as_offset_datetime_fmt()", value_t::offset_datetime); } return this->offset_datetime_.format; } local_datetime_format_info const& as_local_datetime_fmt() const { if(this->type_ != value_t::local_datetime) { this->throw_bad_cast("toml::value::as_local_datetime_fmt()", value_t::local_datetime); } return this->local_datetime_.format; } local_date_format_info const& as_local_date_fmt() const { if(this->type_ != value_t::local_date) { this->throw_bad_cast("toml::value::as_local_date_fmt()", value_t::local_date); } return this->local_date_.format; } local_time_format_info const& as_local_time_fmt() const { if(this->type_ != value_t::local_time) { this->throw_bad_cast("toml::value::as_local_time_fmt()", value_t::local_time); } return this->local_time_.format; } array_format_info const& as_array_fmt() const { if(this->type_ != value_t::array) { this->throw_bad_cast("toml::value::as_array_fmt()", value_t::array); } return this->array_.format; } table_format_info const& as_table_fmt() const { if(this->type_ != value_t::table) { this->throw_bad_cast("toml::value::as_table_fmt()", value_t::table); } return this->table_.format; } // ------------------------------------------------------------------------ // nonconst reference boolean_format_info& as_boolean_fmt() { if(this->type_ != value_t::boolean) { this->throw_bad_cast("toml::value::as_boolean_fmt()", value_t::boolean); } return this->boolean_.format; } integer_format_info& as_integer_fmt() { if(this->type_ != value_t::integer) { this->throw_bad_cast("toml::value::as_integer_fmt()", value_t::integer); } return this->integer_.format; } floating_format_info& as_floating_fmt() { if(this->type_ != value_t::floating) { this->throw_bad_cast("toml::value::as_floating_fmt()", value_t::floating); } return this->floating_.format; } string_format_info& as_string_fmt() { if(this->type_ != value_t::string) { this->throw_bad_cast("toml::value::as_string_fmt()", value_t::string); } return this->string_.format; } offset_datetime_format_info& as_offset_datetime_fmt() { if(this->type_ != value_t::offset_datetime) { this->throw_bad_cast("toml::value::as_offset_datetime_fmt()", value_t::offset_datetime); } return this->offset_datetime_.format; } local_datetime_format_info& as_local_datetime_fmt() { if(this->type_ != value_t::local_datetime) { this->throw_bad_cast("toml::value::as_local_datetime_fmt()", value_t::local_datetime); } return this->local_datetime_.format; } local_date_format_info& as_local_date_fmt() { if(this->type_ != value_t::local_date) { this->throw_bad_cast("toml::value::as_local_date_fmt()", value_t::local_date); } return this->local_date_.format; } local_time_format_info& as_local_time_fmt() { if(this->type_ != value_t::local_time) { this->throw_bad_cast("toml::value::as_local_time_fmt()", value_t::local_time); } return this->local_time_.format; } array_format_info& as_array_fmt() { if(this->type_ != value_t::array) { this->throw_bad_cast("toml::value::as_array_fmt()", value_t::array); } return this->array_.format; } table_format_info& as_table_fmt() { if(this->type_ != value_t::table) { this->throw_bad_cast("toml::value::as_table_fmt()", value_t::table); } return this->table_.format; } // }}} // table accessors ==================================================== {{{ value_type& at(const key_type& k) { if(!this->is_table()) { this->throw_bad_cast("toml::value::at(key_type)", value_t::table); } auto& table = this->as_table(std::nothrow); const auto found = table.find(k); if(found == table.end()) { this->throw_key_not_found_error("toml::value::at", k); } assert(found->first == k); return found->second; } value_type const& at(const key_type& k) const { if(!this->is_table()) { this->throw_bad_cast("toml::value::at(key_type)", value_t::table); } const auto& table = this->as_table(std::nothrow); const auto found = table.find(k); if(found == table.end()) { this->throw_key_not_found_error("toml::value::at", k); } assert(found->first == k); return found->second; } value_type& operator[](const key_type& k) { if(this->is_empty()) { (*this) = table_type{}; } else if( ! this->is_table()) // initialized, but not a table { this->throw_bad_cast("toml::value::operator[](key_type)", value_t::table); } return (this->as_table(std::nothrow))[k]; } std::size_t count(const key_type& k) const { if(!this->is_table()) { this->throw_bad_cast("toml::value::count(key_type)", value_t::table); } return this->as_table(std::nothrow).count(k); } bool contains(const key_type& k) const { if(!this->is_table()) { this->throw_bad_cast("toml::value::contains(key_type)", value_t::table); } const auto& table = this->as_table(std::nothrow); return table.find(k) != table.end(); } // }}} // array accessors ==================================================== {{{ value_type& at(const std::size_t idx) { if(!this->is_array()) { this->throw_bad_cast("toml::value::at(idx)", value_t::array); } auto& ar = this->as_array(std::nothrow); if(ar.size() <= idx) { std::ostringstream oss; oss << "actual length (" << ar.size() << ") is shorter than the specified index (" << idx << ")."; throw std::out_of_range(format_error( "toml::value::at(idx): no element corresponding to the index", this->location(), oss.str() )); } return ar.at(idx); } value_type const& at(const std::size_t idx) const { if(!this->is_array()) { this->throw_bad_cast("toml::value::at(idx)", value_t::array); } const auto& ar = this->as_array(std::nothrow); if(ar.size() <= idx) { std::ostringstream oss; oss << "actual length (" << ar.size() << ") is shorter than the specified index (" << idx << ")."; throw std::out_of_range(format_error( "toml::value::at(idx): no element corresponding to the index", this->location(), oss.str() )); } return ar.at(idx); } value_type& operator[](const std::size_t idx) noexcept { // no check... return this->as_array(std::nothrow)[idx]; } value_type const& operator[](const std::size_t idx) const noexcept { // no check... return this->as_array(std::nothrow)[idx]; } void push_back(const value_type& x) { if(!this->is_array()) { this->throw_bad_cast("toml::value::push_back(idx)", value_t::array); } this->as_array(std::nothrow).push_back(x); return; } void push_back(value_type&& x) { if(!this->is_array()) { this->throw_bad_cast("toml::value::push_back(idx)", value_t::array); } this->as_array(std::nothrow).push_back(std::move(x)); return; } template value_type& emplace_back(Ts&& ... args) { if(!this->is_array()) { this->throw_bad_cast("toml::value::emplace_back(idx)", value_t::array); } auto& ar = this->as_array(std::nothrow); ar.emplace_back(std::forward(args) ...); return ar.back(); } std::size_t size() const { switch(this->type_) { case value_t::array: { return this->as_array(std::nothrow).size(); } case value_t::table: { return this->as_table(std::nothrow).size(); } case value_t::string: { return this->as_string(std::nothrow).size(); } default: { throw type_error(format_error( "toml::value::size(): bad_cast to container types", this->location(), "the actual type is " + to_string(this->type_) ), this->location()); } } } // }}} source_location location() const { return source_location(this->region_); } comment_type const& comments() const noexcept {return this->comments_;} comment_type& comments() noexcept {return this->comments_;} private: // private helper functions =========================================== {{{ void cleanup() noexcept { switch(this->type_) { case value_t::boolean : { boolean_ .~boolean_storage (); break; } case value_t::integer : { integer_ .~integer_storage (); break; } case value_t::floating : { floating_ .~floating_storage (); break; } case value_t::string : { string_ .~string_storage (); break; } case value_t::offset_datetime : { offset_datetime_.~offset_datetime_storage (); break; } case value_t::local_datetime : { local_datetime_ .~local_datetime_storage (); break; } case value_t::local_date : { local_date_ .~local_date_storage (); break; } case value_t::local_time : { local_time_ .~local_time_storage (); break; } case value_t::array : { array_ .~array_storage (); break; } case value_t::table : { table_ .~table_storage (); break; } default : { break; } } this->type_ = value_t::empty; return; } template static void assigner(T& dst, U&& v) { const auto tmp = ::new(std::addressof(dst)) T(std::forward(v)); assert(tmp == std::addressof(dst)); (void)tmp; } [[noreturn]] void throw_bad_cast(const std::string& funcname, const value_t ty) const { throw type_error(format_error(detail::make_type_error(*this, funcname, ty)), this->location()); } [[noreturn]] void throw_key_not_found_error(const std::string& funcname, const key_type& key) const { throw std::out_of_range(format_error( detail::make_not_found_error(*this, funcname, key))); } template friend void detail::change_region_of_value(basic_value&, const basic_value&); template friend class basic_value; // }}} private: using boolean_storage = detail::value_with_format; using integer_storage = detail::value_with_format; using floating_storage = detail::value_with_format; using string_storage = detail::value_with_format; using offset_datetime_storage = detail::value_with_format; using local_datetime_storage = detail::value_with_format; using local_date_storage = detail::value_with_format; using local_time_storage = detail::value_with_format; using array_storage = detail::value_with_format, array_format_info >; using table_storage = detail::value_with_format, table_format_info >; private: value_t type_; union { char empty_; // the smallest type boolean_storage boolean_; integer_storage integer_; floating_storage floating_; string_storage string_; offset_datetime_storage offset_datetime_; local_datetime_storage local_datetime_; local_date_storage local_date_; local_time_storage local_time_; array_storage array_; table_storage table_; }; region_type region_; comment_type comments_; }; template bool operator==(const basic_value& lhs, const basic_value& rhs) { if(lhs.type() != rhs.type()) {return false;} if(lhs.comments() != rhs.comments()) {return false;} switch(lhs.type()) { case value_t::boolean : { return lhs.as_boolean() == rhs.as_boolean(); } case value_t::integer : { return lhs.as_integer() == rhs.as_integer(); } case value_t::floating : { return lhs.as_floating() == rhs.as_floating(); } case value_t::string : { return lhs.as_string() == rhs.as_string(); } case value_t::offset_datetime: { return lhs.as_offset_datetime() == rhs.as_offset_datetime(); } case value_t::local_datetime: { return lhs.as_local_datetime() == rhs.as_local_datetime(); } case value_t::local_date: { return lhs.as_local_date() == rhs.as_local_date(); } case value_t::local_time: { return lhs.as_local_time() == rhs.as_local_time(); } case value_t::array : { return lhs.as_array() == rhs.as_array(); } case value_t::table : { return lhs.as_table() == rhs.as_table(); } case value_t::empty : {return true; } default: {return false;} } } template bool operator!=(const basic_value& lhs, const basic_value& rhs) { return !(lhs == rhs); } template cxx::enable_if_t::array_type>, detail::is_comparable::table_type> >::value, bool> operator<(const basic_value& lhs, const basic_value& rhs) { if(lhs.type() != rhs.type()) { return (lhs.type() < rhs.type()); } switch(lhs.type()) { case value_t::boolean : { return lhs.as_boolean() < rhs.as_boolean() || (lhs.as_boolean() == rhs.as_boolean() && lhs.comments() < rhs.comments()); } case value_t::integer : { return lhs.as_integer() < rhs.as_integer() || (lhs.as_integer() == rhs.as_integer() && lhs.comments() < rhs.comments()); } case value_t::floating : { return lhs.as_floating() < rhs.as_floating() || (lhs.as_floating() == rhs.as_floating() && lhs.comments() < rhs.comments()); } case value_t::string : { return lhs.as_string() < rhs.as_string() || (lhs.as_string() == rhs.as_string() && lhs.comments() < rhs.comments()); } case value_t::offset_datetime: { return lhs.as_offset_datetime() < rhs.as_offset_datetime() || (lhs.as_offset_datetime() == rhs.as_offset_datetime() && lhs.comments() < rhs.comments()); } case value_t::local_datetime: { return lhs.as_local_datetime() < rhs.as_local_datetime() || (lhs.as_local_datetime() == rhs.as_local_datetime() && lhs.comments() < rhs.comments()); } case value_t::local_date: { return lhs.as_local_date() < rhs.as_local_date() || (lhs.as_local_date() == rhs.as_local_date() && lhs.comments() < rhs.comments()); } case value_t::local_time: { return lhs.as_local_time() < rhs.as_local_time() || (lhs.as_local_time() == rhs.as_local_time() && lhs.comments() < rhs.comments()); } case value_t::array : { return lhs.as_array() < rhs.as_array() || (lhs.as_array() == rhs.as_array() && lhs.comments() < rhs.comments()); } case value_t::table : { return lhs.as_table() < rhs.as_table() || (lhs.as_table() == rhs.as_table() && lhs.comments() < rhs.comments()); } case value_t::empty : { return lhs.comments() < rhs.comments(); } default: { return lhs.comments() < rhs.comments(); } } } template cxx::enable_if_t::array_type>, detail::is_comparable::table_type> >::value, bool> operator<=(const basic_value& lhs, const basic_value& rhs) { return (lhs < rhs) || (lhs == rhs); } template cxx::enable_if_t::array_type>, detail::is_comparable::table_type> >::value, bool> operator>(const basic_value& lhs, const basic_value& rhs) { return !(lhs <= rhs); } template cxx::enable_if_t::array_type>, detail::is_comparable::table_type> >::value, bool> operator>=(const basic_value& lhs, const basic_value& rhs) { return !(lhs < rhs); } // error_info helper namespace detail { template error_info make_error_info_rec(error_info e, const basic_value& v, std::string msg, Ts&& ... tail) { return make_error_info_rec(std::move(e), v.location(), std::move(msg), std::forward(tail)...); } } // detail template error_info make_error_info( std::string title, const basic_value& v, std::string msg, Ts&& ... tail) { return make_error_info(std::move(title), v.location(), std::move(msg), std::forward(tail)...); } template std::string format_error(std::string title, const basic_value& v, std::string msg, Ts&& ... tail) { return format_error(std::move(title), v.location(), std::move(msg), std::forward(tail)...); } namespace detail { template error_info make_type_error(const basic_value& v, const std::string& fname, const value_t ty) { return make_error_info(fname + ": bad_cast to " + to_string(ty), v.location(), "the actual type is " + to_string(v.type())); } template error_info make_not_found_error(const basic_value& v, const std::string& fname, const typename basic_value::key_type& key) { const auto loc = v.location(); const std::string title = fname + ": key \"" + string_conv(key) + "\" not found"; std::vector> locs; if( ! loc.is_ok()) { return error_info(title, locs); } if(loc.first_line_number() == 1 && loc.first_column_number() == 1 && loc.length() == 1) { // The top-level table has its region at the 0th character of the file. // That means that, in the case when a key is not found in the top-level // table, the error message points to the first character. If the file has // the first table at the first line, the error message would be like this. // ```console // [error] key "a" not found // --> example.toml // | // 1 | [table] // | ^------ in this table // ``` // It actually points to the top-level table at the first character, not // `[table]`. But it is too confusing. To avoid the confusion, the error // message should explicitly say "key not found in the top-level table". locs.emplace_back(v.location(), "at the top-level table"); } else { locs.emplace_back(v.location(), "in this table"); } return error_info(title, locs); } #define TOML11_DETAIL_GENERATE_COMPTIME_GETTER(ty) \ template \ struct getter \ { \ using value_type = basic_value; \ using result_type = enum_to_type_t; \ using format_type = enum_to_fmt_type_t; \ \ static result_type& get(value_type& v) \ { \ return v.as_ ## ty(); \ } \ static result_type const& get(const value_type& v) \ { \ return v.as_ ## ty(); \ } \ \ static result_type& get_nothrow(value_type& v) noexcept \ { \ return v.as_ ## ty(std::nothrow); \ } \ static result_type const& get_nothrow(const value_type& v) noexcept \ { \ return v.as_ ## ty(std::nothrow); \ } \ \ static format_type& get_fmt(value_type& v) \ { \ return v.as_ ## ty ## _fmt(); \ } \ static format_type const& get_fmt(const value_type& v) \ { \ return v.as_ ## ty ## _fmt(); \ } \ \ static format_type& get_fmt_nothrow(value_type& v) noexcept \ { \ return v.as_ ## ty ## _fmt(std::nothrow); \ } \ static format_type const& get_fmt_nothrow(const value_type& v) noexcept \ { \ return v.as_ ## ty ## _fmt(std::nothrow); \ } \ }; TOML11_DETAIL_GENERATE_COMPTIME_GETTER(boolean ) TOML11_DETAIL_GENERATE_COMPTIME_GETTER(integer ) TOML11_DETAIL_GENERATE_COMPTIME_GETTER(floating ) TOML11_DETAIL_GENERATE_COMPTIME_GETTER(string ) TOML11_DETAIL_GENERATE_COMPTIME_GETTER(offset_datetime) TOML11_DETAIL_GENERATE_COMPTIME_GETTER(local_datetime ) TOML11_DETAIL_GENERATE_COMPTIME_GETTER(local_date ) TOML11_DETAIL_GENERATE_COMPTIME_GETTER(local_time ) TOML11_DETAIL_GENERATE_COMPTIME_GETTER(array ) TOML11_DETAIL_GENERATE_COMPTIME_GETTER(table ) #undef TOML11_DETAIL_GENERATE_COMPTIME_GETTER template void change_region_of_value(basic_value& dst, const basic_value& src) { dst.region_ = std::move(src.region_); return; } } // namespace detail } // namespace toml #endif // TOML11_VALUE_HPP #ifndef TOML11_VISIT_HPP #define TOML11_VISIT_HPP namespace toml { template cxx::return_type_of_t::boolean_type&> visit(Visitor&& visitor, const basic_value& v) { switch(v.type()) { case value_t::boolean : {return visitor(v.as_boolean ());} case value_t::integer : {return visitor(v.as_integer ());} case value_t::floating : {return visitor(v.as_floating ());} case value_t::string : {return visitor(v.as_string ());} case value_t::offset_datetime: {return visitor(v.as_offset_datetime());} case value_t::local_datetime : {return visitor(v.as_local_datetime ());} case value_t::local_date : {return visitor(v.as_local_date ());} case value_t::local_time : {return visitor(v.as_local_time ());} case value_t::array : {return visitor(v.as_array ());} case value_t::table : {return visitor(v.as_table ());} case value_t::empty : break; default: break; } throw type_error(format_error("[error] toml::visit: toml::basic_value " "does not have any valid type.", v.location(), "here"), v.location()); } template cxx::return_type_of_t::boolean_type&> visit(Visitor&& visitor, basic_value& v) { switch(v.type()) { case value_t::boolean : {return visitor(v.as_boolean ());} case value_t::integer : {return visitor(v.as_integer ());} case value_t::floating : {return visitor(v.as_floating ());} case value_t::string : {return visitor(v.as_string ());} case value_t::offset_datetime: {return visitor(v.as_offset_datetime());} case value_t::local_datetime : {return visitor(v.as_local_datetime ());} case value_t::local_date : {return visitor(v.as_local_date ());} case value_t::local_time : {return visitor(v.as_local_time ());} case value_t::array : {return visitor(v.as_array ());} case value_t::table : {return visitor(v.as_table ());} case value_t::empty : break; default: break; } throw type_error(format_error("[error] toml::visit: toml::basic_value " "does not have any valid type.", v.location(), "here"), v.location()); } template cxx::return_type_of_t::boolean_type&&> visit(Visitor&& visitor, basic_value&& v) { switch(v.type()) { case value_t::boolean : {return visitor(std::move(v.as_boolean ()));} case value_t::integer : {return visitor(std::move(v.as_integer ()));} case value_t::floating : {return visitor(std::move(v.as_floating ()));} case value_t::string : {return visitor(std::move(v.as_string ()));} case value_t::offset_datetime: {return visitor(std::move(v.as_offset_datetime()));} case value_t::local_datetime : {return visitor(std::move(v.as_local_datetime ()));} case value_t::local_date : {return visitor(std::move(v.as_local_date ()));} case value_t::local_time : {return visitor(std::move(v.as_local_time ()));} case value_t::array : {return visitor(std::move(v.as_array ()));} case value_t::table : {return visitor(std::move(v.as_table ()));} case value_t::empty : break; default: break; } throw type_error(format_error("[error] toml::visit: toml::basic_value " "does not have any valid type.", v.location(), "here"), v.location()); } } // toml #endif // TOML11_VISIT_HPP #ifndef TOML11_TYPES_HPP #define TOML11_TYPES_HPP #include #include #include #include #include #include #include #include namespace toml { // forward decl template class basic_value; // when you use a special integer type as toml::value::integer_type, parse must // be able to read it. So, type_config has static member functions that read the // integer_type as {dec, hex, oct, bin}-integer. But, in most cases, operator<< // is enough. To make config easy, we provide the default read functions. // // Before this functions is called, syntax is checked and prefix(`0x` etc) and // spacer(`_`) are removed. template result read_dec_int(const std::string& str, const source_location src) { constexpr auto max_digits = std::numeric_limits::digits; assert( ! str.empty()); T val{0}; std::istringstream iss(str); iss >> val; if(iss.fail()) { return err(make_error_info("toml::parse_dec_integer: " "too large integer: current max digits = 2^" + std::to_string(max_digits), std::move(src), "must be < 2^" + std::to_string(max_digits))); } return ok(val); } template result read_hex_int(const std::string& str, const source_location src) { constexpr auto max_digits = std::numeric_limits::digits; assert( ! str.empty()); T val{0}; std::istringstream iss(str); iss >> std::hex >> val; if(iss.fail()) { return err(make_error_info("toml::parse_hex_integer: " "too large integer: current max value = 2^" + std::to_string(max_digits), std::move(src), "must be < 2^" + std::to_string(max_digits))); } return ok(val); } template result read_oct_int(const std::string& str, const source_location src) { constexpr auto max_digits = std::numeric_limits::digits; assert( ! str.empty()); T val{0}; std::istringstream iss(str); iss >> std::oct >> val; if(iss.fail()) { return err(make_error_info("toml::parse_oct_integer: " "too large integer: current max value = 2^" + std::to_string(max_digits), std::move(src), "must be < 2^" + std::to_string(max_digits))); } return ok(val); } template result read_bin_int(const std::string& str, const source_location src) { constexpr auto is_bounded = std::numeric_limits::is_bounded; constexpr auto max_digits = std::numeric_limits::digits; const auto max_value = (std::numeric_limits::max)(); T val{0}; T base{1}; for(auto i = str.rbegin(); i != str.rend(); ++i) { const auto c = *i; if(c == '1') { val += base; // prevent `base` from overflow if(is_bounded && max_value / 2 < base && std::next(i) != str.rend()) { base = 0; } else { base *= 2; } } else { assert(c == '0'); if(is_bounded && max_value / 2 < base && std::next(i) != str.rend()) { base = 0; } else { base *= 2; } } } if(base == 0) { return err(make_error_info("toml::parse_bin_integer: " "too large integer: current max value = 2^" + std::to_string(max_digits), std::move(src), "must be < 2^" + std::to_string(max_digits))); } return ok(val); } template result read_int(const std::string& str, const source_location src, const std::uint8_t base) { assert(base == 10 || base == 16 || base == 8 || base == 2); switch(base) { case 2: { return read_bin_int(str, src); } case 8: { return read_oct_int(str, src); } case 16: { return read_hex_int(str, src); } default: { assert(base == 10); return read_dec_int(str, src); } } } inline result read_hex_float(const std::string& str, const source_location src, float val) { #if defined(_MSC_VER) && ! defined(__clang__) const auto res = ::sscanf_s(str.c_str(), "%a", std::addressof(val)); #else const auto res = std::sscanf(str.c_str(), "%a", std::addressof(val)); #endif if(res != 1) { return err(make_error_info("toml::parse_floating: " "failed to read hexadecimal floating point value ", std::move(src), "here")); } return ok(val); } inline result read_hex_float(const std::string& str, const source_location src, double val) { #if defined(_MSC_VER) && ! defined(__clang__) const auto res = ::sscanf_s(str.c_str(), "%la", std::addressof(val)); #else const auto res = std::sscanf(str.c_str(), "%la", std::addressof(val)); #endif if(res != 1) { return err(make_error_info("toml::parse_floating: " "failed to read hexadecimal floating point value ", std::move(src), "here")); } return ok(val); } template cxx::enable_if_t, double>>, cxx::negation, float>> >::value, result> read_hex_float(const std::string&, const source_location src, T) { return err(make_error_info("toml::parse_floating: failed to read " "floating point value because of unknown type in type_config", std::move(src), "here")); } template result read_dec_float(const std::string& str, const source_location src) { T val; std::istringstream iss(str); iss >> val; if(iss.fail()) { return err(make_error_info("toml::parse_floating: " "failed to read floating point value from stream", std::move(src), "here")); } return ok(val); } template result read_float(const std::string& str, const source_location src, const bool is_hex) { if(is_hex) { return read_hex_float(str, src, T{}); } else { return read_dec_float(str, src); } } struct type_config { using comment_type = preserve_comments; using boolean_type = bool; using integer_type = std::int64_t; using floating_type = double; using string_type = std::string; template using array_type = std::vector; template using table_type = std::unordered_map; static result parse_int(const std::string& str, const source_location src, const std::uint8_t base) { return read_int(str, src, base); } static result parse_float(const std::string& str, const source_location src, const bool is_hex) { return read_float(str, src, is_hex); } }; using value = basic_value; using table = typename value::table_type; using array = typename value::array_type; struct ordered_type_config { using comment_type = preserve_comments; using boolean_type = bool; using integer_type = std::int64_t; using floating_type = double; using string_type = std::string; template using array_type = std::vector; template using table_type = ordered_map; static result parse_int(const std::string& str, const source_location src, const std::uint8_t base) { return read_int(str, src, base); } static result parse_float(const std::string& str, const source_location src, const bool is_hex) { return read_float(str, src, is_hex); } }; using ordered_value = basic_value; using ordered_table = typename ordered_value::table_type; using ordered_array = typename ordered_value::array_type; // ---------------------------------------------------------------------------- // meta functions for internal use namespace detail { // ---------------------------------------------------------------------------- // check if type T has all the needed member types struct has_comment_type_impl { template static std::true_type check(typename T::comment_type*); template static std::false_type check(...); }; template using has_comment_type = decltype(has_comment_type_impl::check(nullptr)); struct has_integer_type_impl { template static std::true_type check(typename T::integer_type*); template static std::false_type check(...); }; template using has_integer_type = decltype(has_integer_type_impl::check(nullptr)); struct has_floating_type_impl { template static std::true_type check(typename T::floating_type*); template static std::false_type check(...); }; template using has_floating_type = decltype(has_floating_type_impl::check(nullptr)); struct has_string_type_impl { template static std::true_type check(typename T::string_type*); template static std::false_type check(...); }; template using has_string_type = decltype(has_string_type_impl::check(nullptr)); struct has_array_type_impl { template static std::true_type check(typename T::template array_type*); template static std::false_type check(...); }; template using has_array_type = decltype(has_array_type_impl::check(nullptr)); struct has_table_type_impl { template static std::true_type check(typename T::template table_type*); template static std::false_type check(...); }; template using has_table_type = decltype(has_table_type_impl::check(nullptr)); struct has_parse_int_impl { template static std::true_type check(decltype(std::declval().parse_int( std::declval(), std::declval(), std::declval() ))*); template static std::false_type check(...); }; template using has_parse_int = decltype(has_parse_int_impl::check(nullptr)); struct has_parse_float_impl { template static std::true_type check(decltype(std::declval().parse_float( std::declval(), std::declval(), std::declval() ))*); template static std::false_type check(...); }; template using has_parse_float = decltype(has_parse_float_impl::check(nullptr)); template using is_type_config = cxx::conjunction< has_comment_type, has_integer_type, has_floating_type, has_string_type, has_array_type, has_table_type, has_parse_int, has_parse_float >; } // namespace detail } // namespace toml #if defined(TOML11_COMPILE_SOURCES) namespace toml { extern template class basic_value; extern template class basic_value; } // toml #endif // TOML11_COMPILE_SOURCES #endif // TOML11_TYPES_HPP #ifndef TOML11_GET_HPP #define TOML11_GET_HPP #include #if defined(TOML11_HAS_STRING_VIEW) #include #endif // string_view namespace toml { // ============================================================================ // T is toml::value; identity transformation. template cxx::enable_if_t>::value, T>& get(basic_value& v) { return v; } template cxx::enable_if_t>::value, T> const& get(const basic_value& v) { return v; } template cxx::enable_if_t>::value, T> get(basic_value&& v) { return basic_value(std::move(v)); } // ============================================================================ // exact toml::* type template cxx::enable_if_t>::value, T> & get(basic_value& v) { constexpr auto ty = detail::type_to_enum>::value; return detail::getter::get(v); } template cxx::enable_if_t>::value, T> const& get(const basic_value& v) { constexpr auto ty = detail::type_to_enum>::value; return detail::getter::get(v); } template cxx::enable_if_t>::value, T> get(basic_value&& v) { constexpr auto ty = detail::type_to_enum>::value; return detail::getter::get(std::move(v)); } // ============================================================================ // T is toml::basic_value template cxx::enable_if_t, cxx::negation>> >::value, T> get(basic_value v) { return T(std::move(v)); } // ============================================================================ // integer convertible from toml::value::integer_type template cxx::enable_if_t, cxx::negation>, detail::is_not_toml_type>, cxx::negation>, cxx::negation> >::value, T> get(const basic_value& v) { return static_cast(v.as_integer()); } // ============================================================================ // floating point convertible from toml::value::floating_type template cxx::enable_if_t, detail::is_not_toml_type>, cxx::negation>, cxx::negation> >::value, T> get(const basic_value& v) { return static_cast(v.as_floating()); } // ============================================================================ // std::string with different char/trait/allocator template cxx::enable_if_t>, detail::is_1byte_std_basic_string >::value, T> get(const basic_value& v) { return detail::string_conv>(v.as_string()); } // ============================================================================ // std::string_view #if defined(TOML11_HAS_STRING_VIEW) template cxx::enable_if_t::string_type>::value, T> get(const basic_value& v) { return T(v.as_string()); } #endif // string_view // ============================================================================ // std::chrono::duration from toml::local_time template cxx::enable_if_t::value, T> get(const basic_value& v) { return std::chrono::duration_cast( std::chrono::nanoseconds(v.as_local_time())); } // ============================================================================ // std::chrono::system_clock::time_point from toml::datetime variants template cxx::enable_if_t< std::is_same::value, T> get(const basic_value& v) { switch(v.type()) { case value_t::local_date: { return std::chrono::system_clock::time_point(v.as_local_date()); } case value_t::local_datetime: { return std::chrono::system_clock::time_point(v.as_local_datetime()); } case value_t::offset_datetime: { return std::chrono::system_clock::time_point(v.as_offset_datetime()); } default: { const auto loc = v.location(); throw type_error(format_error("toml::get: " "bad_cast to std::chrono::system_clock::time_point", loc, "the actual type is " + to_string(v.type())), loc); } } } // ============================================================================ // forward declaration to use this recursively. ignore this and go ahead. // array-like (w/ push_back) template cxx::enable_if_t, // T is a container detail::has_push_back_method, // .push_back() works detail::is_not_toml_type>, // but not toml::array cxx::negation>, // but not std::basic_string #if defined(TOML11_HAS_STRING_VIEW) cxx::negation>, // but not std::basic_string_view #endif cxx::negation>, // no T.from_toml() cxx::negation>, // no toml::from cxx::negation&>> >::value, T> get(const basic_value&); // std::array template cxx::enable_if_t::value, T> get(const basic_value&); // std::forward_list template cxx::enable_if_t::value, T> get(const basic_value&); // std::pair template cxx::enable_if_t::value, T> get(const basic_value&); // std::tuple template cxx::enable_if_t::value, T> get(const basic_value&); // std::map (key is convertible from toml::value::key_type) template cxx::enable_if_t, // T is map detail::is_not_toml_type>, // but not toml::table std::is_convertible::key_type, typename T::key_type>, // keys are convertible cxx::negation>, // no T.from_toml() cxx::negation>, // no toml::from cxx::negation&>> >::value, T> get(const basic_value& v); // std::map (key is not convertible from toml::value::key_type, but // is a std::basic_string) template cxx::enable_if_t, // T is map detail::is_not_toml_type>, // but not toml::table cxx::negation::key_type, typename T::key_type>>, // keys are NOT convertible detail::is_1byte_std_basic_string, // is std::basic_string cxx::negation>, // no T.from_toml() cxx::negation>, // no toml::from cxx::negation&>> >::value, T> get(const basic_value& v); // toml::from::from_toml(v) template cxx::enable_if_t::value, T> get(const basic_value&); // has T.from_toml(v) but no from template cxx::enable_if_t, // has T.from_toml() cxx::negation>, // no toml::from std::is_default_constructible // T{} works >::value, T> get(const basic_value&); // T(const toml::value&) and T is not toml::basic_value, // and it does not have `from` nor `from_toml`. template cxx::enable_if_t&>, // has T(const basic_value&) cxx::negation>, // but not basic_value itself cxx::negation>, // no .from_toml() cxx::negation> // no toml::from >::value, T> get(const basic_value&); // ============================================================================ // array-like types; most likely STL container, like std::vector, etc. template cxx::enable_if_t, // T is a container detail::has_push_back_method, // .push_back() works detail::is_not_toml_type>, // but not toml::array cxx::negation>, // but not std::basic_string #if defined(TOML11_HAS_STRING_VIEW) cxx::negation>, // but not std::basic_string_view #endif cxx::negation>, // no T.from_toml() cxx::negation>, // no toml::from cxx::negation&>> >::value, T> get(const basic_value& v) { using value_type = typename T::value_type; const auto& a = v.as_array(); T container; detail::try_reserve(container, a.size()); // if T has .reserve(), call it for(const auto& elem : a) { container.push_back(get(elem)); } return container; } // ============================================================================ // std::array template cxx::enable_if_t::value, T> get(const basic_value& v) { using value_type = typename T::value_type; const auto& a = v.as_array(); T container; if(a.size() != container.size()) { const auto loc = v.location(); throw std::out_of_range(format_error("toml::get: while converting to an array: " " array size is " + std::to_string(container.size()) + " but there are " + std::to_string(a.size()) + " elements in toml array.", loc, "here")); } for(std::size_t i=0; i(a.at(i)); } return container; } // ============================================================================ // std::forward_list template cxx::enable_if_t::value, T> get(const basic_value& v) { using value_type = typename T::value_type; T container; for(const auto& elem : v.as_array()) { container.push_front(get(elem)); } container.reverse(); return container; } // ============================================================================ // std::pair template cxx::enable_if_t::value, T> get(const basic_value& v) { using first_type = typename T::first_type; using second_type = typename T::second_type; const auto& ar = v.as_array(); if(ar.size() != 2) { const auto loc = v.location(); throw std::out_of_range(format_error("toml::get: while converting std::pair: " " but there are " + std::to_string(ar.size()) + " > 2 elements in toml array.", loc, "here")); } return std::make_pair(::toml::get(ar.at(0)), ::toml::get(ar.at(1))); } // ============================================================================ // std::tuple. namespace detail { template T get_tuple_impl(const Array& a, cxx::index_sequence) { return std::make_tuple( ::toml::get::type>(a.at(I))...); } } // detail template cxx::enable_if_t::value, T> get(const basic_value& v) { const auto& ar = v.as_array(); if(ar.size() != std::tuple_size::value) { const auto loc = v.location(); throw std::out_of_range(format_error("toml::get: while converting std::tuple: " " there are " + std::to_string(ar.size()) + " > " + std::to_string(std::tuple_size::value) + " elements in toml array.", loc, "here")); } return detail::get_tuple_impl(ar, cxx::make_index_sequence::value>{}); } // ============================================================================ // map-like types; most likely STL map, like std::map or std::unordered_map. // key is convertible from toml::value::key_type template cxx::enable_if_t, // T is map detail::is_not_toml_type>, // but not toml::table std::is_convertible::key_type, typename T::key_type>, // keys are convertible cxx::negation>, // no T.from_toml() cxx::negation>, // no toml::from cxx::negation&>> >::value, T> get(const basic_value& v) { using key_type = typename T::key_type; using mapped_type = typename T::mapped_type; static_assert( std::is_convertible::key_type, key_type>::value, "toml::get only supports map type of which key_type is " "convertible from toml::basic_value::key_type."); T m; for(const auto& kv : v.as_table()) { m.emplace(key_type(kv.first), get(kv.second)); } return m; } // key is NOT convertible from toml::value::key_type but std::basic_string template cxx::enable_if_t, // T is map detail::is_not_toml_type>, // but not toml::table cxx::negation::key_type, typename T::key_type>>, // keys are NOT convertible detail::is_1byte_std_basic_string, // is std::basic_string cxx::negation>, // no T.from_toml() cxx::negation>, // no toml::from cxx::negation&>> >::value, T> get(const basic_value& v) { using key_type = typename T::key_type; using mapped_type = typename T::mapped_type; T m; for(const auto& kv : v.as_table()) { m.emplace(detail::string_conv(kv.first), get(kv.second)); } return m; } // ============================================================================ // user-defined, but convertible types. // toml::from template cxx::enable_if_t::value, T> get(const basic_value& v) { return ::toml::from::from_toml(v); } // has T.from_toml(v) but no from template cxx::enable_if_t, // has T.from_toml() cxx::negation>, // no toml::from std::is_default_constructible // T{} works >::value, T> get(const basic_value& v) { T ud; ud.from_toml(v); return ud; } // T(const toml::value&) and T is not toml::basic_value, // and it does not have `from` nor `from_toml`. template cxx::enable_if_t&>, // has T(const basic_value&) cxx::negation>, // but not basic_value itself cxx::negation>, // no .from_toml() cxx::negation> // no toml::from >::value, T> get(const basic_value& v) { return T(v); } // ============================================================================ // get_or(value, fallback) template cxx::enable_if_t::value, basic_value> const& get_or(const basic_value& v, const basic_value&) { return v; } template cxx::enable_if_t::value, basic_value>& get_or(basic_value& v, basic_value&) { return v; } template cxx::enable_if_t::value, basic_value> get_or(basic_value&& v, basic_value&&) { return v; } // ---------------------------------------------------------------------------- // specialization for the exact toml types (return type becomes lvalue ref) template cxx::enable_if_t< detail::is_exact_toml_type>::value, T> const& get_or(const basic_value& v, const T& opt) noexcept { try { return get>(v); } catch(...) { return opt; } } template cxx::enable_if_t>, detail::is_exact_toml_type> >::value, T>& get_or(basic_value& v, T& opt) noexcept { try { return get>(v); } catch(...) { return opt; } } template cxx::enable_if_t, basic_value>::value, cxx::remove_cvref_t> get_or(basic_value&& v, T&& opt) noexcept { try { return get>(std::move(v)); } catch(...) { return cxx::remove_cvref_t(std::forward(opt)); } } // ---------------------------------------------------------------------------- // specialization for string literal // template // typename basic_value::string_type // get_or(const basic_value& v, // const typename basic_value::string_type::value_type (&opt)[N]) // { // try // { // return v.as_string(); // } // catch(...) // { // return typename basic_value::string_type(opt); // } // } // // The above only matches to the literal, like `get_or(v, "foo");` but not // ```cpp // const auto opt = "foo"; // const auto str = get_or(v, opt); // ``` // . And the latter causes an error. // To match to both `"foo"` and `const auto opt = "foo"`, we take a pointer to // a character here. template typename basic_value::string_type get_or(const basic_value& v, const typename basic_value::string_type::value_type* opt) { try { return v.as_string(); } catch(...) { return typename basic_value::string_type(opt); } } // ---------------------------------------------------------------------------- // others (require type conversion and return type cannot be lvalue reference) template cxx::enable_if_t>, cxx::negation>>, cxx::negation, typename basic_value::string_type::value_type const*>> >::value, cxx::remove_cvref_t> get_or(const basic_value& v, T&& opt) { try { return get>(v); } catch(...) { return cxx::remove_cvref_t(std::forward(opt)); } } } // toml #endif // TOML11_GET_HPP #ifndef TOML11_FIND_HPP #define TOML11_FIND_HPP #include #if defined(TOML11_HAS_STRING_VIEW) #include #endif namespace toml { // ---------------------------------------------------------------------------- // find(value, key); template decltype(::toml::get(std::declval const&>())) find(const basic_value& v, const typename basic_value::key_type& ky) { return ::toml::get(v.at(ky)); } template decltype(::toml::get(std::declval&>())) find(basic_value& v, const typename basic_value::key_type& ky) { return ::toml::get(v.at(ky)); } template decltype(::toml::get(std::declval&&>())) find(basic_value&& v, const typename basic_value::key_type& ky) { return ::toml::get(std::move(v.at(ky))); } // ---------------------------------------------------------------------------- // find(value, idx) template decltype(::toml::get(std::declval const&>())) find(const basic_value& v, const std::size_t idx) { return ::toml::get(v.at(idx)); } template decltype(::toml::get(std::declval&>())) find(basic_value& v, const std::size_t idx) { return ::toml::get(v.at(idx)); } template decltype(::toml::get(std::declval&&>())) find(basic_value&& v, const std::size_t idx) { return ::toml::get(std::move(v.at(idx))); } // ---------------------------------------------------------------------------- // find(value, key/idx), w/o conversion template cxx::enable_if_t::value, basic_value>& find(basic_value& v, const typename basic_value::key_type& ky) { return v.at(ky); } template cxx::enable_if_t::value, basic_value> const& find(basic_value const& v, const typename basic_value::key_type& ky) { return v.at(ky); } template cxx::enable_if_t::value, basic_value> find(basic_value&& v, const typename basic_value::key_type& ky) { return basic_value(std::move(v.at(ky))); } template cxx::enable_if_t::value, basic_value>& find(basic_value& v, const std::size_t idx) { return v.at(idx); } template cxx::enable_if_t::value, basic_value> const& find(basic_value const& v, const std::size_t idx) { return v.at(idx); } template cxx::enable_if_t::value, basic_value> find(basic_value&& v, const std::size_t idx) { return basic_value(std::move(v.at(idx))); } // -------------------------------------------------------------------------- // toml::find(toml::value, toml::key, Ts&& ... keys) namespace detail { // It suppresses warnings by -Wsign-conversion when we pass integer literal // to toml::find. integer literal `0` is deduced as an int, and will be // converted to std::size_t. This causes sign-conversion. template std::size_t key_cast(const std::size_t& v) noexcept { return v; } template cxx::enable_if_t>::value, std::size_t> key_cast(const T& v) noexcept { return static_cast(v); } // for string-like (string, string literal, string_view) template typename basic_value::key_type const& key_cast(const typename basic_value::key_type& v) noexcept { return v; } template typename basic_value::key_type key_cast(const typename basic_value::key_type::value_type* v) { return typename basic_value::key_type(v); } #if defined(TOML11_HAS_STRING_VIEW) template typename basic_value::key_type key_cast(const std::string_view v) { return typename basic_value::key_type(v); } #endif // string_view } // detail // ---------------------------------------------------------------------------- // find(v, keys...) template cxx::enable_if_t::value, basic_value> const& find(const basic_value& v, const K1& k1, const K2& k2, const Ks& ... ks) { return find(v.at(detail::key_cast(k1)), detail::key_cast(k2), ks...); } template cxx::enable_if_t::value, basic_value>& find(basic_value& v, const K1& k1, const K2& k2, const Ks& ... ks) { return find(v.at(detail::key_cast(k1)), detail::key_cast(k2), ks...); } template cxx::enable_if_t::value, basic_value> find(basic_value&& v, const K1& k1, const K2& k2, const Ks& ... ks) { return find(std::move(v.at(detail::key_cast(k1))), detail::key_cast(k2), ks...); } // ---------------------------------------------------------------------------- // find(v, keys...) template decltype(::toml::get(std::declval&>())) find(const basic_value& v, const K1& k1, const K2& k2, const Ks& ... ks) { return find(v.at(detail::key_cast(k1)), detail::key_cast(k2), ks...); } template decltype(::toml::get(std::declval&>())) find(basic_value& v, const K1& k1, const K2& k2, const Ks& ... ks) { return find(v.at(detail::key_cast(k1)), detail::key_cast(k2), ks...); } template decltype(::toml::get(std::declval&&>())) find(basic_value&& v, const K1& k1, const K2& k2, const Ks& ... ks) { return find(std::move(v.at(detail::key_cast(k1))), detail::key_cast(k2), ks...); } // =========================================================================== // find_or(value, key, fallback) // --------------------------------------------------------------------------- // find_or(v, key, other_v) template cxx::enable_if_t::value, basic_value>& find_or(basic_value& v, const K& k, basic_value& opt) noexcept { try { return ::toml::find(v, detail::key_cast(k)); } catch(...) { return opt; } } template cxx::enable_if_t::value, basic_value> const& find_or(const basic_value& v, const K& k, const basic_value& opt) noexcept { try { return ::toml::find(v, detail::key_cast(k)); } catch(...) { return opt; } } template cxx::enable_if_t::value, basic_value> find_or(basic_value&& v, const K& k, basic_value&& opt) noexcept { try { return ::toml::find(v, detail::key_cast(k)); } catch(...) { return opt; } } // --------------------------------------------------------------------------- // toml types (return type can be a reference) template cxx::enable_if_t>::value, cxx::remove_cvref_t const&> find_or(const basic_value& v, const K& k, const T& opt) { try { return ::toml::get(v.at(detail::key_cast(k))); } catch(...) { return opt; } } template cxx::enable_if_t>, detail::is_exact_toml_type> >::value, cxx::remove_cvref_t&> find_or(basic_value& v, const K& k, T& opt) { try { return ::toml::get(v.at(detail::key_cast(k))); } catch(...) { return opt; } } template cxx::enable_if_t>::value, cxx::remove_cvref_t> find_or(basic_value&& v, const K& k, T opt) { try { return ::toml::get(std::move(v.at(detail::key_cast(k)))); } catch(...) { return T(std::move(opt)); } } // --------------------------------------------------------------------------- // string literal (deduced as std::string) // XXX to avoid confusion when T is explicitly specified in find_or(), // we restrict the string type as std::string. template cxx::enable_if_t::value, std::string> find_or(const basic_value& v, const K& k, const char* opt) { try { return ::toml::get(v.at(detail::key_cast(k))); } catch(...) { return std::string(opt); } } // --------------------------------------------------------------------------- // other types (requires type conversion and return type cannot be a reference) template cxx::enable_if_t>>, detail::is_not_toml_type, basic_value>, cxx::negation, const typename basic_value::string_type::value_type*>> >::value, cxx::remove_cvref_t> find_or(const basic_value& v, const K& ky, T opt) { try { return ::toml::get>(v.at(detail::key_cast(ky))); } catch(...) { return cxx::remove_cvref_t(std::move(opt)); } } // ---------------------------------------------------------------------------- // recursive namespace detail { template T& last_one(T& arg) { return arg; } template auto last_one(T1&, T2& arg, Ts& ... args) -> decltype(last_one(arg, args...)) { return last_one(arg, args...); } } // detail template auto find_or(Value&& v, const K1& k1, const K2& k2, K3&& k3, Ks&& ... keys) noexcept -> cxx::enable_if_t< detail::is_basic_value>::value, decltype(find_or(v, k2, std::forward(k3), std::forward(keys)...)) > { try { return find_or(v.at(k1), k2, std::forward(k3), std::forward(keys)...); } catch(...) { return detail::last_one(k3, keys...); } } template T find_or(const basic_value& v, const K1& k1, const K2& k2, const K3& k3, const Ks& ... keys) noexcept { try { return find_or(v.at(k1), k2, k3, keys...); } catch(...) { return static_cast(detail::last_one(k3, keys...)); } } } // toml #endif // TOML11_FIND_HPP #ifndef TOML11_CONTEXT_HPP #define TOML11_CONTEXT_HPP #include namespace toml { namespace detail { template class context { public: explicit context(const spec& toml_spec) : toml_spec_(toml_spec), errors_{} {} bool has_error() const noexcept {return !errors_.empty();} std::vector const& errors() const noexcept {return errors_;} semantic_version& toml_version() noexcept {return toml_spec_.version;} semantic_version const& toml_version() const noexcept {return toml_spec_.version;} spec& toml_spec() noexcept {return toml_spec_;} spec const& toml_spec() const noexcept {return toml_spec_;} void report_error(error_info err) { this->errors_.push_back(std::move(err)); } error_info pop_last_error() { assert( ! errors_.empty()); auto e = std::move(errors_.back()); errors_.pop_back(); return e; } private: spec toml_spec_; std::vector errors_; }; } // detail } // toml #if defined(TOML11_COMPILE_SOURCES) namespace toml { struct type_config; struct ordered_type_config; namespace detail { extern template class context<::toml::type_config>; extern template class context<::toml::ordered_type_config>; } // detail } // toml #endif // TOML11_COMPILE_SOURCES #endif // TOML11_CONTEXT_HPP #ifndef TOML11_SCANNER_HPP #define TOML11_SCANNER_HPP #ifndef TOML11_SCANNER_FWD_HPP #define TOML11_SCANNER_FWD_HPP #include #include #include #include #include #include #include namespace toml { namespace detail { class scanner_base { public: virtual ~scanner_base() = default; virtual region scan(location& loc) const = 0; virtual scanner_base* clone() const = 0; // returns expected character or set of characters or literal. // to show the error location, it changes loc (in `sequence`, especially). virtual std::string expected_chars(location& loc) const = 0; virtual std::string name() const = 0; }; // make `scanner*` copyable struct scanner_storage { template>::value, std::nullptr_t> = nullptr> explicit scanner_storage(Scanner&& s) : scanner_(cxx::make_unique>(std::forward(s))) {} ~scanner_storage() = default; scanner_storage(const scanner_storage& other); scanner_storage& operator=(const scanner_storage& other); scanner_storage(scanner_storage&&) = default; scanner_storage& operator=(scanner_storage&&) = default; bool is_ok() const noexcept {return static_cast(scanner_);} region scan(location& loc) const; std::string expected_chars(location& loc) const; scanner_base& get() const noexcept; std::string name() const; private: std::unique_ptr scanner_; }; // ---------------------------------------------------------------------------- class character final : public scanner_base { public: using char_type = location::char_type; public: explicit character(const char_type c) noexcept : value_(c) {} ~character() override = default; region scan(location& loc) const override; std::string expected_chars(location&) const override; scanner_base* clone() const override; std::string name() const override; private: char_type value_; }; // ---------------------------------------------------------------------------- class character_either final : public scanner_base { public: using char_type = location::char_type; public: explicit character_either(std::initializer_list cs) noexcept : chars_(std::move(cs)) { assert(! this->chars_.empty()); } template explicit character_either(const char (&cs)[N]) noexcept : chars_(N-1, '\0') { static_assert(N >= 1, ""); for(std::size_t i=0; i+1 chars_; }; // ---------------------------------------------------------------------------- class character_in_range final : public scanner_base { public: using char_type = location::char_type; public: explicit character_in_range(const char_type from, const char_type to) noexcept : from_(from), to_(to) {} ~character_in_range() override = default; region scan(location& loc) const override; std::string expected_chars(location&) const override; scanner_base* clone() const override; std::string name() const override; private: char_type from_; char_type to_; }; // ---------------------------------------------------------------------------- class literal final : public scanner_base { public: using char_type = location::char_type; public: template explicit literal(const char (&cs)[N]) noexcept : value_(cs), size_(N-1) // remove null character at the end {} ~literal() override = default; region scan(location& loc) const override; std::string expected_chars(location&) const override; scanner_base* clone() const override; std::string name() const override; private: const char* value_; std::size_t size_; }; // ---------------------------------------------------------------------------- class sequence final: public scanner_base { public: using char_type = location::char_type; public: template explicit sequence(Ts&& ... args) { push_back_all(std::forward(args)...); } sequence(const sequence&) = default; sequence(sequence&&) = default; sequence& operator=(const sequence&) = default; sequence& operator=(sequence&&) = default; ~sequence() override = default; region scan(location& loc) const override; std::string expected_chars(location& loc) const override; scanner_base* clone() const override; template void push_back(Scanner&& other_scanner) { this->others_.emplace_back(std::forward(other_scanner)); } std::string name() const override; private: void push_back_all() { return; } template void push_back_all(T&& head, Ts&& ... args) { others_.emplace_back(std::forward(head)); push_back_all(std::forward(args)...); return; } private: std::vector others_; }; // ---------------------------------------------------------------------------- class either final: public scanner_base { public: using char_type = location::char_type; public: template explicit either(Ts&& ... args) { push_back_all(std::forward(args)...); } either(const either&) = default; either(either&&) = default; either& operator=(const either&) = default; either& operator=(either&&) = default; ~either() override = default; region scan(location& loc) const override; std::string expected_chars(location& loc) const override; scanner_base* clone() const override; template void push_back(Scanner&& other_scanner) { this->others_.emplace_back(std::forward(other_scanner)); } std::string name() const override; private: void push_back_all() { return; } template void push_back_all(T&& head, Ts&& ... args) { others_.emplace_back(std::forward(head)); push_back_all(std::forward(args)...); return; } private: std::vector others_; }; // ---------------------------------------------------------------------------- class repeat_exact final: public scanner_base { public: using char_type = location::char_type; public: template repeat_exact(const std::size_t length, Scanner&& other) : length_(length), other_(std::forward(other)) {} repeat_exact(const repeat_exact&) = default; repeat_exact(repeat_exact&&) = default; repeat_exact& operator=(const repeat_exact&) = default; repeat_exact& operator=(repeat_exact&&) = default; ~repeat_exact() override = default; region scan(location& loc) const override; std::string expected_chars(location& loc) const override; scanner_base* clone() const override; std::string name() const override; private: std::size_t length_; scanner_storage other_; }; // ---------------------------------------------------------------------------- class repeat_at_least final: public scanner_base { public: using char_type = location::char_type; public: template repeat_at_least(const std::size_t length, Scanner&& s) : length_(length), other_(std::forward(s)) {} repeat_at_least(const repeat_at_least&) = default; repeat_at_least(repeat_at_least&&) = default; repeat_at_least& operator=(const repeat_at_least&) = default; repeat_at_least& operator=(repeat_at_least&&) = default; ~repeat_at_least() override = default; region scan(location& loc) const override; std::string expected_chars(location& loc) const override; scanner_base* clone() const override; std::string name() const override; private: std::size_t length_; scanner_storage other_; }; // ---------------------------------------------------------------------------- class maybe final: public scanner_base { public: using char_type = location::char_type; public: template explicit maybe(Scanner&& s) : other_(std::forward(s)) {} maybe(const maybe&) = default; maybe(maybe&&) = default; maybe& operator=(const maybe&) = default; maybe& operator=(maybe&&) = default; ~maybe() override = default; region scan(location& loc) const override; std::string expected_chars(location&) const override; scanner_base* clone() const override; std::string name() const override; private: scanner_storage other_; }; } // detail } // toml #endif // TOML11_SCANNER_FWD_HPP #if ! defined(TOML11_COMPILE_SOURCES) #ifndef TOML11_SCANNER_IMPL_HPP #define TOML11_SCANNER_IMPL_HPP namespace toml { namespace detail { TOML11_INLINE scanner_storage::scanner_storage(const scanner_storage& other) : scanner_(nullptr) { if(other.is_ok()) { scanner_.reset(other.get().clone()); } } TOML11_INLINE scanner_storage& scanner_storage::operator=(const scanner_storage& other) { if(this == std::addressof(other)) {return *this;} if(other.is_ok()) { scanner_.reset(other.get().clone()); } return *this; } TOML11_INLINE region scanner_storage::scan(location& loc) const { assert(this->is_ok()); return this->scanner_->scan(loc); } TOML11_INLINE std::string scanner_storage::expected_chars(location& loc) const { assert(this->is_ok()); return this->scanner_->expected_chars(loc); } TOML11_INLINE scanner_base& scanner_storage::get() const noexcept { assert(this->is_ok()); return *scanner_; } TOML11_INLINE std::string scanner_storage::name() const { assert(this->is_ok()); return this->scanner_->name(); } // ---------------------------------------------------------------------------- TOML11_INLINE region character::scan(location& loc) const { if(loc.eof()) {return region{};} if(loc.current() == this->value_) { const auto first = loc; loc.advance(1); return region(first, loc); } return region{}; } TOML11_INLINE std::string character::expected_chars(location&) const { return show_char(value_); } TOML11_INLINE scanner_base* character::clone() const { return new character(*this); } TOML11_INLINE std::string character::name() const { return "character{" + show_char(value_) + "}"; } // ---------------------------------------------------------------------------- TOML11_INLINE region character_either::scan(location& loc) const { if(loc.eof()) {return region{};} for(const auto c : this->chars_) { if(loc.current() == c) { const auto first = loc; loc.advance(1); return region(first, loc); } } return region{}; } TOML11_INLINE std::string character_either::expected_chars(location&) const { assert( ! chars_.empty()); std::string expected; if(chars_.size() == 1) { expected += show_char(chars_.at(0)); } else if(chars_.size() == 2) { expected += show_char(chars_.at(0)) + " or " + show_char(chars_.at(1)); } else { for(std::size_t i=0; ichars_) { n += show_char(c); n += ", "; } if( ! this->chars_.empty()) { n.pop_back(); n.pop_back(); } n += "}"; return n; } // ---------------------------------------------------------------------------- // character_in_range TOML11_INLINE region character_in_range::scan(location& loc) const { if(loc.eof()) {return region{};} const auto curr = loc.current(); if(this->from_ <= curr && curr <= this->to_) { const auto first = loc; loc.advance(1); return region(first, loc); } return region{}; } TOML11_INLINE std::string character_in_range::expected_chars(location&) const { std::string expected("from `"); expected += show_char(from_); expected += "` to `"; expected += show_char(to_); expected += "`"; return expected; } TOML11_INLINE scanner_base* character_in_range::clone() const { return new character_in_range(*this); } TOML11_INLINE std::string character_in_range::name() const { return "character_in_range{" + show_char(from_) + "," + show_char(to_) + "}"; } // ---------------------------------------------------------------------------- // literal TOML11_INLINE region literal::scan(location& loc) const { const auto first = loc; for(std::size_t i=0; iothers_.empty()) { n.pop_back(); n.pop_back(); } n += "}"; return n; } // ---------------------------------------------------------------------------- // either TOML11_INLINE region either::scan(location& loc) const { for(const auto& other : others_) { const auto reg = other.scan(loc); if(reg.is_ok()) { return reg; } } return region{}; } TOML11_INLINE std::string either::expected_chars(location& loc) const { assert( ! others_.empty()); std::string expected = others_.at(0).expected_chars(loc); if(others_.size() == 2) { expected += " or "; expected += others_.at(1).expected_chars(loc); } else { for(std::size_t i=1; iothers_.empty()) { n.pop_back(); n.pop_back(); } n += "}"; return n; } // ---------------------------------------------------------------------------- // repeat_exact TOML11_INLINE region repeat_exact::scan(location& loc) const { const auto first = loc; for(std::size_t i=0; i(b1); } else if((b1 >> 5) == 6) // 0b110 == 6 { const auto b2 = loc.current(); loc.advance(1); const std::uint32_t c1 = b1 & ((1 << 5) - 1); const std::uint32_t c2 = b2 & ((1 << 6) - 1); const std::uint32_t codep = (c1 << 6) + c2; if(codep < 0x80) { return 0xFFFFFFFF; } return codep; } else if((b1 >> 4) == 14) // 0b1110 == 14 { const auto b2 = loc.current(); loc.advance(1); if(loc.eof()) {return 0xFFFFFFFF;} const auto b3 = loc.current(); loc.advance(1); const std::uint32_t c1 = b1 & ((1 << 4) - 1); const std::uint32_t c2 = b2 & ((1 << 6) - 1); const std::uint32_t c3 = b3 & ((1 << 6) - 1); const std::uint32_t codep = (c1 << 12) + (c2 << 6) + c3; if(codep < 0x800) { return 0xFFFFFFFF; } return codep; } else if((b1 >> 3) == 30) // 0b11110 == 30 { const auto b2 = loc.current(); loc.advance(1); if(loc.eof()) {return 0xFFFFFFFF;} const auto b3 = loc.current(); loc.advance(1); if(loc.eof()) {return 0xFFFFFFFF;} const auto b4 = loc.current(); loc.advance(1); const std::uint32_t c1 = b1 & ((1 << 3) - 1); const std::uint32_t c2 = b2 & ((1 << 6) - 1); const std::uint32_t c3 = b3 & ((1 << 6) - 1); const std::uint32_t c4 = b4 & ((1 << 6) - 1); const std::uint32_t codep = (c1 << 18) + (c2 << 12) + (c3 << 6) + c4; if(codep < 0x10000) { return 0xFFFFFFFF; } return codep; } else // not a Unicode codepoint in UTF-8 { return 0xFFFFFFFF; } } TOML11_INLINE region non_ascii_key_char::scan(location& loc) const { if(loc.eof()) {return region{};} const auto first = loc; const auto cp = read_utf8(loc); if(cp == 0xFFFFFFFF) { return region{}; } // ALPHA / DIGIT / %x2D / %x5F ; a-z A-Z 0-9 - _ // / %xB2 / %xB3 / %xB9 / %xBC-BE ; superscript digits, fractions // / %xC0-D6 / %xD8-F6 / %xF8-37D ; non-symbol chars in Latin block // / %x37F-1FFF ; exclude GREEK QUESTION MARK, which is basically a semi-colon // / %x200C-200D / %x203F-2040 ; from General Punctuation Block, include the two tie symbols and ZWNJ, ZWJ // / %x2070-218F / %x2460-24FF ; include super-/subscripts, letterlike/numberlike forms, enclosed alphanumerics // / %x2C00-2FEF / %x3001-D7FF ; skip arrows, math, box drawing etc, skip 2FF0-3000 ideographic up/down markers and spaces // / %xF900-FDCF / %xFDF0-FFFD ; skip D800-DFFF surrogate block, E000-F8FF Private Use area, FDD0-FDEF intended for process-internal use (unicode) // / %x10000-EFFFF ; all chars outside BMP range, excluding Private Use planes (F0000-10FFFF) if(cp == 0xB2 || cp == 0xB3 || cp == 0xB9 || (0xBC <= cp && cp <= 0xBE) || (0xC0 <= cp && cp <= 0xD6 ) || (0xD8 <= cp && cp <= 0xF6) || (0xF8 <= cp && cp <= 0x37D) || (0x37F <= cp && cp <= 0x1FFF) || (0x200C <= cp && cp <= 0x200D) || (0x203F <= cp && cp <= 0x2040) || (0x2070 <= cp && cp <= 0x218F) || (0x2460 <= cp && cp <= 0x24FF) || (0x2C00 <= cp && cp <= 0x2FEF) || (0x3001 <= cp && cp <= 0xD7FF) || (0xF900 <= cp && cp <= 0xFDCF) || (0xFDF0 <= cp && cp <= 0xFFFD) || (0x10000 <= cp && cp <= 0xEFFFF) ) { return region(first, loc); } loc = first; return region{}; } TOML11_INLINE repeat_at_least unquoted_key(const spec& s) { auto keychar = either( alpha(s), digit(s), character{0x2D}, character{0x5F} ); if(s.v1_1_0_allow_non_english_in_bare_keys) { keychar.push_back(non_ascii_key_char(s)); } return repeat_at_least(1, std::move(keychar)); } TOML11_INLINE either quoted_key(const spec& s) { return either(basic_string(s), literal_string(s)); } TOML11_INLINE either simple_key(const spec& s) { return either(unquoted_key(s), quoted_key(s)); } TOML11_INLINE sequence dot_sep(const spec& s) { return sequence(ws(s), character('.'), ws(s)); } TOML11_INLINE sequence dotted_key(const spec& s) { return sequence( simple_key(s), repeat_at_least(1, sequence(dot_sep(s), simple_key(s))) ); } TOML11_INLINE key::key(const spec& s) noexcept : scanner_(dotted_key(s), simple_key(s)) {} TOML11_INLINE sequence keyval_sep(const spec& s) { return sequence(ws(s), character('='), ws(s)); } // =========================================================================== // Table key TOML11_INLINE sequence std_table(const spec& s) { return sequence(character('['), ws(s), key(s), ws(s), character(']')); } TOML11_INLINE sequence array_table(const spec& s) { return sequence(literal("[["), ws(s), key(s), ws(s), literal("]]")); } // =========================================================================== // extension: null TOML11_INLINE literal null_value(const spec&) { return literal("null"); } } // namespace syntax } // namespace detail } // namespace toml #endif // TOML11_SYNTAX_IMPL_HPP #endif #endif// TOML11_SYNTAX_HPP #ifndef TOML11_SKIP_HPP #define TOML11_SKIP_HPP #include namespace toml { namespace detail { template bool skip_whitespace(location& loc, const context& ctx) { return syntax::ws(ctx.toml_spec()).scan(loc).is_ok(); } template bool skip_empty_lines(location& loc, const context& ctx) { return repeat_at_least(1, sequence( syntax::ws(ctx.toml_spec()), syntax::newline(ctx.toml_spec()) )).scan(loc).is_ok(); } // For error recovery. // // In case if a comment line contains an invalid character, we need to skip it // to advance parsing. template void skip_comment_block(location& loc, const context& ctx) { while( ! loc.eof()) { skip_whitespace(loc, ctx); if(loc.current() == '#') { while( ! loc.eof()) { // both CRLF and LF ends with LF. if(loc.current() == '\n') { loc.advance(); break; } } } else if(syntax::newline(ctx.toml_spec()).scan(loc).is_ok()) { ; // an empty line. skip this also } else { // the next token is neither a comment nor empty line. return ; } } return ; } template void skip_empty_or_comment_lines(location& loc, const context& ctx) { const auto& spec = ctx.toml_spec(); repeat_at_least(0, sequence( syntax::ws(spec), maybe(syntax::comment(spec)), syntax::newline(spec)) ).scan(loc); return ; } // For error recovery. // // Sometimes we need to skip a value and find a delimiter, like `,`, `]`, or `}`. // To find delimiter, we need to skip delimiters in a string. // Since we are skipping invalid value while error recovery, we don't need // to check the syntax. Here we just skip string-like region until closing quote // is found. template void skip_string_like(location& loc, const context&) { // if """ is found, skip until the closing """ is found. if(literal("\"\"\"").scan(loc).is_ok()) { while( ! loc.eof()) { if(literal("\"\"\"").scan(loc).is_ok()) { return; } loc.advance(); } } else if(literal("'''").scan(loc).is_ok()) { while( ! loc.eof()) { if(literal("'''").scan(loc).is_ok()) { return; } loc.advance(); } } // if " is found, skip until the closing " or newline is found. else if(loc.current() == '"') { while( ! loc.eof()) { loc.advance(); if(loc.current() == '"' || loc.current() == '\n') { loc.advance(); return; } } } else if(loc.current() == '\'') { while( ! loc.eof()) { loc.advance(); if(loc.current() == '\'' || loc.current() == '\n') { loc.advance(); return ; } } } return; } template void skip_value(location& loc, const context& ctx); template void skip_array_like(location& loc, const context& ctx); template void skip_inline_table_like(location& loc, const context& ctx); template void skip_key_value_pair(location& loc, const context& ctx); template result guess_value_type(const location& loc, const context& ctx); template void skip_array_like(location& loc, const context& ctx) { const auto& spec = ctx.toml_spec(); assert(loc.current() == '['); loc.advance(); while( ! loc.eof()) { if(loc.current() == '\"' || loc.current() == '\'') { skip_string_like(loc, ctx); } else if(loc.current() == '#') { skip_comment_block(loc, ctx); } else if(loc.current() == '{') { skip_inline_table_like(loc, ctx); } else if(loc.current() == '[') { const auto checkpoint = loc; if(syntax::std_table(spec).scan(loc).is_ok() || syntax::array_table(spec).scan(loc).is_ok()) { loc = checkpoint; break; } // if it is not a table-definition, then it is an array. skip_array_like(loc, ctx); } else if(loc.current() == '=') { // key-value pair cannot be inside the array. // guessing the error is "missing closing bracket `]`". // find the previous key just before `=`. while(loc.get_location() != 0) { loc.retrace(); if(loc.current() == '\n') { loc.advance(); break; } } break; } else if(loc.current() == ']') { break; // found closing bracket } else { loc.advance(); } } return ; } template void skip_inline_table_like(location& loc, const context& ctx) { assert(loc.current() == '{'); loc.advance(); const auto& spec = ctx.toml_spec(); while( ! loc.eof()) { if(loc.current() == '\n' && ! spec.v1_1_0_allow_newlines_in_inline_tables) { break; // missing closing `}`. } else if(loc.current() == '\"' || loc.current() == '\'') { skip_string_like(loc, ctx); } else if(loc.current() == '#') { skip_comment_block(loc, ctx); if( ! spec.v1_1_0_allow_newlines_in_inline_tables) { // comment must end with newline. break; // missing closing `}`. } } else if(loc.current() == '[') { const auto checkpoint = loc; if(syntax::std_table(spec).scan(loc).is_ok() || syntax::array_table(spec).scan(loc).is_ok()) { loc = checkpoint; break; // missing closing `}`. } // if it is not a table-definition, then it is an array. skip_array_like(loc, ctx); } else if(loc.current() == '{') { skip_inline_table_like(loc, ctx); } else if(loc.current() == '}') { // closing brace found. guessing the error is inside the table. break; } else { // skip otherwise. loc.advance(); } } return ; } template void skip_value(location& loc, const context& ctx) { value_t ty = guess_value_type(loc, ctx).unwrap_or(value_t::empty); if(ty == value_t::string) { skip_string_like(loc, ctx); } else if(ty == value_t::array) { skip_array_like(loc, ctx); } else if(ty == value_t::table) { // In case of multiline tables, it may skip key-value pair but not the // whole table. skip_inline_table_like(loc, ctx); } else // others are an "in-line" values. skip until the next line { while( ! loc.eof()) { if(loc.current() == '\n') { break; } else if(loc.current() == ',' || loc.current() == ']' || loc.current() == '}') { break; } loc.advance(); } } return; } template void skip_key_value_pair(location& loc, const context& ctx) { while( ! loc.eof()) { if(loc.current() == '=') { skip_whitespace(loc, ctx); skip_value(loc, ctx); return; } else if(loc.current() == '\n') { // newline is found before finding `=`. assuming "missing `=`". return; } loc.advance(); } return ; } template void skip_until_next_table(location& loc, const context& ctx) { const auto& spec = ctx.toml_spec(); while( ! loc.eof()) { if(loc.current() == '\n') { loc.advance(); const auto line_begin = loc; skip_whitespace(loc, ctx); if(syntax::std_table(spec).scan(loc).is_ok()) { loc = line_begin; return ; } if(syntax::array_table(spec).scan(loc).is_ok()) { loc = line_begin; return ; } } loc.advance(); } } } // namespace detail } // namespace toml #if defined(TOML11_COMPILE_SOURCES) namespace toml { struct type_config; struct ordered_type_config; namespace detail { extern template bool skip_whitespace (location& loc, const context&); extern template bool skip_empty_lines (location& loc, const context&); extern template void skip_comment_block (location& loc, const context&); extern template void skip_empty_or_comment_lines(location& loc, const context&); extern template void skip_string_like (location& loc, const context&); extern template void skip_array_like (location& loc, const context&); extern template void skip_inline_table_like (location& loc, const context&); extern template void skip_value (location& loc, const context&); extern template void skip_key_value_pair (location& loc, const context&); extern template void skip_until_next_table (location& loc, const context&); extern template bool skip_whitespace (location& loc, const context&); extern template bool skip_empty_lines (location& loc, const context&); extern template void skip_comment_block (location& loc, const context&); extern template void skip_empty_or_comment_lines(location& loc, const context&); extern template void skip_string_like (location& loc, const context&); extern template void skip_array_like (location& loc, const context&); extern template void skip_inline_table_like (location& loc, const context&); extern template void skip_value (location& loc, const context&); extern template void skip_key_value_pair (location& loc, const context&); extern template void skip_until_next_table (location& loc, const context&); } // detail } // toml #endif // TOML11_COMPILE_SOURCES #endif // TOML11_SKIP_HPP #ifndef TOML11_PARSER_HPP #define TOML11_PARSER_HPP #include #include #include #include #if defined(TOML11_HAS_FILESYSTEM) && TOML11_HAS_FILESYSTEM #include #endif namespace toml { struct syntax_error final : public ::toml::exception { public: syntax_error(std::string what_arg, std::vector err) : what_(std::move(what_arg)), err_(std::move(err)) {} ~syntax_error() noexcept override = default; const char* what() const noexcept override {return what_.c_str();} std::vector const& errors() const noexcept { return err_; } private: std::string what_; std::vector err_; }; struct file_io_error final : public ::toml::exception { public: file_io_error(const std::string& msg, const std::string& fname) : errno_(cxx::make_nullopt()), what_(msg + " \"" + fname + "\"") {} file_io_error(int errnum, const std::string& msg, const std::string& fname) : errno_(errnum), what_(msg + " \"" + fname + "\": errno=" + std::to_string(errnum)) {} ~file_io_error() noexcept override = default; const char* what() const noexcept override {return what_.c_str();} bool has_errno() const noexcept {return errno_.has_value();} int get_errno() const noexcept {return errno_.value_or(0);} private: cxx::optional errno_; std::string what_; }; namespace detail { /* ============================================================================ * __ ___ _ __ _ __ ___ _ _ * / _/ _ \ ' \| ' \/ _ \ ' \ * \__\___/_|_|_|_|_|_\___/_||_| */ template error_info make_syntax_error(std::string title, const S& scanner, location loc, std::string suffix = "") { auto msg = std::string("expected ") + scanner.expected_chars(loc); auto src = source_location(region(loc)); return make_error_info( std::move(title), std::move(src), std::move(msg), std::move(suffix)); } /* ============================================================================ * _ * __ ___ _ __ _ __ ___ _ _| |_ * / _/ _ \ ' \| ' \/ -_) ' \ _| * \__\___/_|_|_|_|_|_\___|_||_\__| */ template result, error_info> parse_comment_line(location& loc, context& ctx) { const auto& spec = ctx.toml_spec(); const auto first = loc; skip_whitespace(loc, ctx); const auto com_reg = syntax::comment(spec).scan(loc); if(com_reg.is_ok()) { // once comment started, newline must follow (or reach EOF). if( ! loc.eof() && ! syntax::newline(spec).scan(loc).is_ok()) { while( ! loc.eof()) // skip until newline to continue parsing { loc.advance(); if(loc.current() == '\n') { /*skip LF*/ loc.advance(); break; } } return err(make_error_info("toml::parse_comment_line: " "newline (LF / CRLF) or EOF is expected", source_location(region(loc)), "but got this", "Hint: most of the control characters are not allowed in comments")); } return ok(cxx::optional(com_reg.as_string())); } else { loc = first; // rollback whitespace to parse indent return ok(cxx::optional(cxx::make_nullopt())); } } /* ============================================================================ * ___ _ * | _ ) ___ ___| |___ __ _ _ _ * | _ \/ _ \/ _ \ / -_) _` | ' \ * |___/\___/\___/_\___\__,_|_||_| */ template result, error_info> parse_boolean(location& loc, const context& ctx) { const auto& spec = ctx.toml_spec(); // ---------------------------------------------------------------------- // check syntax auto reg = syntax::boolean(spec).scan(loc); if( ! reg.is_ok()) { return err(make_syntax_error("toml::parse_boolean: " "invalid boolean: boolean must be `true` or `false`, in lowercase. " "string must be surrounded by `\"`", syntax::boolean(spec), loc)); } // ---------------------------------------------------------------------- // it matches. gen value const auto str = reg.as_string(); const auto val = [&str]() { if(str == "true") { return true; } else { assert(str == "false"); return false; } }(); // ---------------------------------------------------------------------- // no format info for boolean boolean_format_info fmt; return ok(basic_value(val, std::move(fmt), {}, std::move(reg))); } /* ============================================================================ * ___ _ * |_ _|_ _| |_ ___ __ _ ___ _ _ * | || ' \ _/ -_) _` / -_) '_| * |___|_||_\__\___\__, \___|_| * |___/ */ template result, error_info> parse_bin_integer(location& loc, const context& ctx) { const auto first = loc; const auto& spec = ctx.toml_spec(); auto reg = syntax::bin_int(spec).scan(loc); if( ! reg.is_ok()) { return err(make_syntax_error("toml::parse_bin_integer: " "invalid integer: bin_int must be like: 0b0101, 0b1111_0000", syntax::bin_int(spec), loc)); } auto str = reg.as_string(); integer_format_info fmt; fmt.fmt = integer_format::bin; fmt.width = str.size() - 2 - static_cast(std::count(str.begin(), str.end(), '_')); const auto first_underscore = std::find(str.rbegin(), str.rend(), '_'); if(first_underscore != str.rend()) { fmt.spacer = static_cast(std::distance(str.rbegin(), first_underscore)); } // skip prefix `0b` and zeros and underscores at the MSB str.erase(str.begin(), std::find(std::next(str.begin(), 2), str.end(), '1')); // remove all `_` before calling TC::parse_int str.erase(std::remove(str.begin(), str.end(), '_'), str.end()); // 0b0000_0000 becomes empty. if(str.empty()) { str = "0"; } const auto val = TC::parse_int(str, source_location(region(loc)), 2); if(val.is_ok()) { return ok(basic_value(val.as_ok(), std::move(fmt), {}, std::move(reg))); } else { loc = first; return err(val.as_err()); } } // ---------------------------------------------------------------------------- template result, error_info> parse_oct_integer(location& loc, const context& ctx) { const auto first = loc; const auto& spec = ctx.toml_spec(); auto reg = syntax::oct_int(spec).scan(loc); if( ! reg.is_ok()) { return err(make_syntax_error("toml::parse_oct_integer: " "invalid integer: oct_int must be like: 0o775, 0o04_44", syntax::oct_int(spec), loc)); } auto str = reg.as_string(); integer_format_info fmt; fmt.fmt = integer_format::oct; fmt.width = str.size() - 2 - static_cast(std::count(str.begin(), str.end(), '_')); const auto first_underscore = std::find(str.rbegin(), str.rend(), '_'); if(first_underscore != str.rend()) { fmt.spacer = static_cast(std::distance(str.rbegin(), first_underscore)); } // skip prefix `0o` and zeros and underscores at the MSB str.erase(str.begin(), std::find_if( std::next(str.begin(), 2), str.end(), [](const char c) { return c != '0' && c != '_'; })); // remove all `_` before calling TC::parse_int str.erase(std::remove(str.begin(), str.end(), '_'), str.end()); // 0o0000_0000 becomes empty. if(str.empty()) { str = "0"; } const auto val = TC::parse_int(str, source_location(region(loc)), 8); if(val.is_ok()) { return ok(basic_value(val.as_ok(), std::move(fmt), {}, std::move(reg))); } else { loc = first; return err(val.as_err()); } } template result, error_info> parse_hex_integer(location& loc, const context& ctx) { const auto first = loc; const auto& spec = ctx.toml_spec(); auto reg = syntax::hex_int(spec).scan(loc); if( ! reg.is_ok()) { return err(make_syntax_error("toml::parse_hex_integer: " "invalid integer: hex_int must be like: 0xC0FFEE, 0xdead_beef", syntax::hex_int(spec), loc)); } auto str = reg.as_string(); integer_format_info fmt; fmt.fmt = integer_format::hex; fmt.width = str.size() - 2 - static_cast(std::count(str.begin(), str.end(), '_')); const auto first_underscore = std::find(str.rbegin(), str.rend(), '_'); if(first_underscore != str.rend()) { fmt.spacer = static_cast(std::distance(str.rbegin(), first_underscore)); } // skip prefix `0x` and zeros and underscores at the MSB str.erase(str.begin(), std::find_if( std::next(str.begin(), 2), str.end(), [](const char c) { return c != '0' && c != '_'; })); // remove all `_` before calling TC::parse_int str.erase(std::remove(str.begin(), str.end(), '_'), str.end()); // 0x0000_0000 becomes empty. if(str.empty()) { str = "0"; } // prefix zero and _ is removed. check if it uses upper/lower case. // if both upper and lower case letters are found, set upper=true. const auto lower_not_found = std::find_if(str.begin(), str.end(), [](const char c) { return std::islower(static_cast(c)) != 0; }) == str.end(); const auto upper_found = std::find_if(str.begin(), str.end(), [](const char c) { return std::isupper(static_cast(c)) != 0; }) != str.end(); fmt.uppercase = lower_not_found || upper_found; const auto val = TC::parse_int(str, source_location(region(loc)), 16); if(val.is_ok()) { return ok(basic_value(val.as_ok(), std::move(fmt), {}, std::move(reg))); } else { loc = first; return err(val.as_err()); } } template result, error_info> parse_dec_integer(location& loc, const context& ctx) { const auto first = loc; const auto& spec = ctx.toml_spec(); // ---------------------------------------------------------------------- // check syntax auto reg = syntax::dec_int(spec).scan(loc); if( ! reg.is_ok()) { return err(make_syntax_error("toml::parse_dec_integer: " "invalid integer: dec_int must be like: 42, 123_456_789", syntax::dec_int(spec), loc)); } // ---------------------------------------------------------------------- // it matches. gen value auto str = reg.as_string(); integer_format_info fmt; fmt.fmt = integer_format::dec; fmt.width = str.size() - static_cast(std::count(str.begin(), str.end(), '_')); const auto first_underscore = std::find(str.rbegin(), str.rend(), '_'); if(first_underscore != str.rend()) { fmt.spacer = static_cast(std::distance(str.rbegin(), first_underscore)); } // remove all `_` before calling TC::parse_int str.erase(std::remove(str.begin(), str.end(), '_'), str.end()); auto src = source_location(region(loc)); const auto val = TC::parse_int(str, src, 10); if(val.is_err()) { loc = first; return err(val.as_err()); } // ---------------------------------------------------------------------- // parse suffix (extension) if(spec.ext_num_suffix && loc.current() == '_') { const auto sfx_reg = syntax::num_suffix(spec).scan(loc); if( ! sfx_reg.is_ok()) { loc = first; return err(make_error_info("toml::parse_dec_integer: " "invalid suffix: should be `_ non-digit-graph (graph | _graph)`", source_location(region(loc)), "here")); } auto sfx = sfx_reg.as_string(); assert( ! sfx.empty() && sfx.front() == '_'); sfx.erase(sfx.begin()); // remove the first `_` fmt.suffix = sfx; } return ok(basic_value(val.as_ok(), std::move(fmt), {}, std::move(reg))); } template result, error_info> parse_integer(location& loc, const context& ctx) { const auto first = loc; if( ! loc.eof() && (loc.current() == '+' || loc.current() == '-')) { // skip +/- to diagnose +0xDEADBEEF or -0b0011 (invalid). // without this, +0xDEAD_BEEF will be parsed as a decimal int and // unexpected "xDEAD_BEEF" will appear after integer "+0". loc.advance(); } if( ! loc.eof() && loc.current() == '0') { loc.advance(); if(loc.eof()) { // `[+-]?0`. parse as an decimal integer. loc = first; return parse_dec_integer(loc, ctx); } const auto prefix = loc.current(); auto prefix_src = source_location(region(loc)); loc = first; if(prefix == 'b') {return parse_bin_integer(loc, ctx);} if(prefix == 'o') {return parse_oct_integer(loc, ctx);} if(prefix == 'x') {return parse_hex_integer(loc, ctx);} if(std::isdigit(prefix)) { auto src = source_location(region(loc)); return err(make_error_info("toml::parse_integer: " "leading zero in an decimal integer is not allowed", std::move(src), "leading zero")); } } loc = first; return parse_dec_integer(loc, ctx); } /* ============================================================================ * ___ _ _ _ * | __| |___ __ _| |_(_)_ _ __ _ * | _|| / _ \/ _` | _| | ' \/ _` | * |_| |_\___/\__,_|\__|_|_||_\__, | * |___/ */ template result, error_info> parse_floating(location& loc, const context& ctx) { using floating_type = typename basic_value::floating_type; const auto first = loc; const auto& spec = ctx.toml_spec(); // ---------------------------------------------------------------------- // check syntax bool is_hex = false; std::string str; region reg; if(spec.ext_hex_float && sequence(character('0'), character('x')).scan(loc).is_ok()) { loc = first; is_hex = true; reg = syntax::hex_floating(spec).scan(loc); if( ! reg.is_ok()) { return err(make_syntax_error("toml::parse_floating: " "invalid hex floating: float must be like: 0xABCp-3f", syntax::floating(spec), loc)); } str = reg.as_string(); } else { reg = syntax::floating(spec).scan(loc); if( ! reg.is_ok()) { return err(make_syntax_error("toml::parse_floating: " "invalid floating: float must be like: -3.14159_26535, 6.022e+23, " "inf, or nan (lowercase).", syntax::floating(spec), loc)); } str = reg.as_string(); } // ---------------------------------------------------------------------- // it matches. gen value floating_format_info fmt; if(is_hex) { fmt.fmt = floating_format::hex; } else { // since we already checked that the string conforms the TOML standard. if(std::find(str.begin(), str.end(), 'e') != str.end() || std::find(str.begin(), str.end(), 'E') != str.end()) { fmt.fmt = floating_format::scientific; // use exponent part } else { fmt.fmt = floating_format::fixed; // do not use exponent part } } str.erase(std::remove(str.begin(), str.end(), '_'), str.end()); floating_type val{0}; if(str == "inf" || str == "+inf") { TOML11_CONSTEXPR_IF(std::numeric_limits::has_infinity) { val = std::numeric_limits::infinity(); } else { return err(make_error_info("toml::parse_floating: inf value found" " but the current environment does not support inf. Please" " make sure that the floating-point implementation conforms" " IEEE 754/ISO 60559 international standard.", source_location(region(loc)), "floating_type: inf is not supported")); } } else if(str == "-inf") { TOML11_CONSTEXPR_IF(std::numeric_limits::has_infinity) { val = -std::numeric_limits::infinity(); } else { return err(make_error_info("toml::parse_floating: inf value found" " but the current environment does not support inf. Please" " make sure that the floating-point implementation conforms" " IEEE 754/ISO 60559 international standard.", source_location(region(loc)), "floating_type: inf is not supported")); } } else if(str == "nan" || str == "+nan") { TOML11_CONSTEXPR_IF(std::numeric_limits::has_quiet_NaN) { val = std::numeric_limits::quiet_NaN(); } else TOML11_CONSTEXPR_IF(std::numeric_limits::has_signaling_NaN) { val = std::numeric_limits::signaling_NaN(); } else { return err(make_error_info("toml::parse_floating: NaN value found" " but the current environment does not support NaN. Please" " make sure that the floating-point implementation conforms" " IEEE 754/ISO 60559 international standard.", source_location(region(loc)), "floating_type: NaN is not supported")); } } else if(str == "-nan") { using std::copysign; TOML11_CONSTEXPR_IF(std::numeric_limits::has_quiet_NaN) { val = copysign(std::numeric_limits::quiet_NaN(), floating_type(-1)); } else TOML11_CONSTEXPR_IF(std::numeric_limits::has_signaling_NaN) { val = copysign(std::numeric_limits::signaling_NaN(), floating_type(-1)); } else { return err(make_error_info("toml::parse_floating: NaN value found" " but the current environment does not support NaN. Please" " make sure that the floating-point implementation conforms" " IEEE 754/ISO 60559 international standard.", source_location(region(loc)), "floating_type: NaN is not supported")); } } else { // set precision const auto has_sign = ! str.empty() && (str.front() == '+' || str.front() == '-'); const auto decpoint = std::find(str.begin(), str.end(), '.'); const auto exponent = std::find_if(str.begin(), str.end(), [](const char c) { return c == 'e' || c == 'E'; }); if(decpoint != str.end() && exponent != str.end()) { assert(decpoint < exponent); } if(fmt.fmt == floating_format::scientific) { // total width fmt.prec = static_cast(std::distance(str.begin(), exponent)); if(has_sign) { fmt.prec -= 1; } if(decpoint != str.end()) { fmt.prec -= 1; } } else if(fmt.fmt == floating_format::hex) { fmt.prec = std::numeric_limits::max_digits10; } else { // width after decimal point fmt.prec = static_cast(std::distance(std::next(decpoint), exponent)); } auto src = source_location(region(loc)); const auto res = TC::parse_float(str, src, is_hex); if(res.is_ok()) { val = res.as_ok(); } else { return err(res.as_err()); } } // ---------------------------------------------------------------------- // parse suffix (extension) if(spec.ext_num_suffix && loc.current() == '_') { const auto sfx_reg = syntax::num_suffix(spec).scan(loc); if( ! sfx_reg.is_ok()) { auto src = source_location(region(loc)); loc = first; return err(make_error_info("toml::parse_floating: " "invalid suffix: should be `_ non-digit-graph (graph | _graph)`", std::move(src), "here")); } auto sfx = sfx_reg.as_string(); assert( ! sfx.empty() && sfx.front() == '_'); sfx.erase(sfx.begin()); // remove the first `_` fmt.suffix = sfx; } return ok(basic_value(val, std::move(fmt), {}, std::move(reg))); } /* ============================================================================ * ___ _ _ _ * | \ __ _| |_ ___| |_(_)_ __ ___ * | |) / _` | _/ -_) _| | ' \/ -_) * |___/\__,_|\__\___|\__|_|_|_|_\___| */ // all the offset_datetime, local_datetime, local_date parses date part. template result, error_info> parse_local_date_only(location& loc, const context& ctx) { const auto first = loc; const auto& spec = ctx.toml_spec(); local_date_format_info fmt; // ---------------------------------------------------------------------- // check syntax auto reg = syntax::local_date(spec).scan(loc); if( ! reg.is_ok()) { return err(make_syntax_error("toml::parse_local_date: " "invalid date: date must be like: 1234-05-06, yyyy-mm-dd.", syntax::local_date(spec), loc)); } // ---------------------------------------------------------------------- // it matches. gen value const auto str = reg.as_string(); // 0123456789 // yyyy-mm-dd const auto year_r = from_string(str.substr(0, 4)); const auto month_r = from_string(str.substr(5, 2)); const auto day_r = from_string(str.substr(8, 2)); if(year_r.is_err()) { auto src = source_location(region(first)); return err(make_error_info("toml::parse_local_date: " "failed to read year `" + str.substr(0, 4) + "`", std::move(src), "here")); } if(month_r.is_err()) { auto src = source_location(region(first)); return err(make_error_info("toml::parse_local_date: " "failed to read month `" + str.substr(5, 2) + "`", std::move(src), "here")); } if(day_r.is_err()) { auto src = source_location(region(first)); return err(make_error_info("toml::parse_local_date: " "failed to read day `" + str.substr(8, 2) + "`", std::move(src), "here")); } const auto year = year_r.unwrap(); const auto month = month_r.unwrap(); const auto day = day_r.unwrap(); { // We briefly check whether the input date is valid or not. // Actually, because of the historical reasons, there are several // edge cases, such as 1582/10/5-1582/10/14 (only in several countries). // But here, we do not care about it. // It makes the code complicated and there is only low probability // that such a specific date is needed in practice. If someone need to // validate date accurately, that means that the one need a specialized // library for their purpose in another layer. const bool is_leap = (year % 4 == 0) && ((year % 100 != 0) || (year % 400 == 0)); const auto max_day = [month, is_leap]() { if(month == 2) { return is_leap ? 29 : 28; } if(month == 4 || month == 6 || month == 9 || month == 11) { return 30; } return 31; }(); if((month < 1 || 12 < month) || (day < 1 || max_day < day)) { auto src = source_location(region(first)); return err(make_error_info("toml::parse_local_date: invalid date.", std::move(src), "month must be 01-12, day must be any of " "01-28,29,30,31 depending on the month/year.")); } } return ok(std::make_tuple( local_date(year, static_cast(month - 1), day), std::move(fmt), std::move(reg) )); } template result, error_info> parse_local_date(location& loc, const context& ctx) { auto val_fmt_reg = parse_local_date_only(loc, ctx); if(val_fmt_reg.is_err()) { return err(val_fmt_reg.unwrap_err()); } auto val = std::move(std::get<0>(val_fmt_reg.unwrap())); auto fmt = std::move(std::get<1>(val_fmt_reg.unwrap())); auto reg = std::move(std::get<2>(val_fmt_reg.unwrap())); return ok(basic_value(std::move(val), std::move(fmt), {}, std::move(reg))); } // all the offset_datetime, local_datetime, local_time parses date part. template result, error_info> parse_local_time_only(location& loc, const context& ctx) { const auto first = loc; const auto& spec = ctx.toml_spec(); local_time_format_info fmt; // ---------------------------------------------------------------------- // check syntax auto reg = syntax::local_time(spec).scan(loc); if( ! reg.is_ok()) { if(spec.v1_1_0_make_seconds_optional) { return err(make_syntax_error("toml::parse_local_time: " "invalid time: time must be HH:MM(:SS.sss) (seconds are optional)", syntax::local_time(spec), loc)); } else { return err(make_syntax_error("toml::parse_local_time: " "invalid time: time must be HH:MM:SS(.sss) (subseconds are optional)", syntax::local_time(spec), loc)); } } // ---------------------------------------------------------------------- // it matches. gen value const auto str = reg.as_string(); // at least we have HH:MM. // 01234 // HH:MM const auto hour_r = from_string(str.substr(0, 2)); const auto minute_r = from_string(str.substr(3, 2)); if(hour_r.is_err()) { auto src = source_location(region(first)); return err(make_error_info("toml::parse_local_time: " "failed to read hour `" + str.substr(0, 2) + "`", std::move(src), "here")); } if(minute_r.is_err()) { auto src = source_location(region(first)); return err(make_error_info("toml::parse_local_time: " "failed to read minute `" + str.substr(3, 2) + "`", std::move(src), "here")); } const auto hour = hour_r.unwrap(); const auto minute = minute_r.unwrap(); if((hour < 0 || 24 <= hour) || (minute < 0 || 60 <= minute)) { auto src = source_location(region(first)); return err(make_error_info("toml::parse_local_time: invalid time.", std::move(src), "hour must be 00-23, minute must be 00-59.")); } // ----------------------------------------------------------------------- // we have hour and minute. // Since toml v1.1.0, second and subsecond part becomes optional. // Check the version and return if second does not exist. if(str.size() == 5 && spec.v1_1_0_make_seconds_optional) { fmt.has_seconds = false; fmt.subsecond_precision = 0; return ok(std::make_tuple(local_time(hour, minute, 0), std::move(fmt), std::move(reg))); } assert(str.at(5) == ':'); // we have at least `:SS` part. `.subseconds` are optional. // 0 1 // 012345678901234 // HH:MM:SS.subsec const auto sec_r = from_string(str.substr(6, 2)); if(sec_r.is_err()) { auto src = source_location(region(first)); return err(make_error_info("toml::parse_local_time: " "failed to read second `" + str.substr(6, 2) + "`", std::move(src), "here")); } const auto sec = sec_r.unwrap(); if(sec < 0 || 60 < sec) // :60 is allowed { auto src = source_location(region(first)); return err(make_error_info("toml::parse_local_time: invalid time.", std::move(src), "second must be 00-60.")); } if(str.size() == 8) { fmt.has_seconds = true; fmt.subsecond_precision = 0; return ok(std::make_tuple(local_time(hour, minute, sec), std::move(fmt), std::move(reg))); } assert(str.at(8) == '.'); auto secfrac = str.substr(9, str.size() - 9); fmt.has_seconds = true; fmt.subsecond_precision = secfrac.size(); while(secfrac.size() < 9) { secfrac += '0'; } assert(9 <= secfrac.size()); const auto ms_r = from_string(secfrac.substr(0, 3)); const auto us_r = from_string(secfrac.substr(3, 3)); const auto ns_r = from_string(secfrac.substr(6, 3)); if(ms_r.is_err()) { auto src = source_location(region(first)); return err(make_error_info("toml::parse_local_time: " "failed to read milliseconds `" + secfrac.substr(0, 3) + "`", std::move(src), "here")); } if(us_r.is_err()) { auto src = source_location(region(first)); return err(make_error_info("toml::parse_local_time: " "failed to read microseconds`" + str.substr(3, 3) + "`", std::move(src), "here")); } if(ns_r.is_err()) { auto src = source_location(region(first)); return err(make_error_info("toml::parse_local_time: " "failed to read nanoseconds`" + str.substr(6, 3) + "`", std::move(src), "here")); } const auto ms = ms_r.unwrap(); const auto us = us_r.unwrap(); const auto ns = ns_r.unwrap(); return ok(std::make_tuple(local_time(hour, minute, sec, ms, us, ns), std::move(fmt), std::move(reg))); } template result, error_info> parse_local_time(location& loc, const context& ctx) { const auto first = loc; auto val_fmt_reg = parse_local_time_only(loc, ctx); if(val_fmt_reg.is_err()) { return err(val_fmt_reg.unwrap_err()); } auto val = std::move(std::get<0>(val_fmt_reg.unwrap())); auto fmt = std::move(std::get<1>(val_fmt_reg.unwrap())); auto reg = std::move(std::get<2>(val_fmt_reg.unwrap())); return ok(basic_value(std::move(val), std::move(fmt), {}, std::move(reg))); } template result, error_info> parse_local_datetime(location& loc, const context& ctx) { using char_type = location::char_type; const auto first = loc; local_datetime_format_info fmt; // ---------------------------------------------------------------------- auto date_fmt_reg = parse_local_date_only(loc, ctx); if(date_fmt_reg.is_err()) { return err(date_fmt_reg.unwrap_err()); } if(loc.current() == char_type('T')) { loc.advance(); fmt.delimiter = datetime_delimiter_kind::upper_T; } else if(loc.current() == char_type('t')) { loc.advance(); fmt.delimiter = datetime_delimiter_kind::lower_t; } else if(loc.current() == char_type(' ')) { loc.advance(); fmt.delimiter = datetime_delimiter_kind::space; } else { auto src = source_location(region(loc)); return err(make_error_info("toml::parse_local_datetime: " "expect date-time delimiter `T`, `t` or ` `(space).", std::move(src), "here")); } auto time_fmt_reg = parse_local_time_only(loc, ctx); if(time_fmt_reg.is_err()) { return err(time_fmt_reg.unwrap_err()); } fmt.has_seconds = std::get<1>(time_fmt_reg.unwrap()).has_seconds; fmt.subsecond_precision = std::get<1>(time_fmt_reg.unwrap()).subsecond_precision; // ---------------------------------------------------------------------- region reg(first, loc); local_datetime val(std::get<0>(date_fmt_reg.unwrap()), std::get<0>(time_fmt_reg.unwrap())); return ok(basic_value(val, std::move(fmt), {}, std::move(reg))); } template result, error_info> parse_offset_datetime(location& loc, const context& ctx) { using char_type = location::char_type; const auto first = loc; const auto& spec = ctx.toml_spec(); offset_datetime_format_info fmt; // ---------------------------------------------------------------------- // date part auto date_fmt_reg = parse_local_date_only(loc, ctx); if(date_fmt_reg.is_err()) { return err(date_fmt_reg.unwrap_err()); } // ---------------------------------------------------------------------- // delimiter if(loc.current() == char_type('T')) { loc.advance(); fmt.delimiter = datetime_delimiter_kind::upper_T; } else if(loc.current() == char_type('t')) { loc.advance(); fmt.delimiter = datetime_delimiter_kind::lower_t; } else if(loc.current() == char_type(' ')) { loc.advance(); fmt.delimiter = datetime_delimiter_kind::space; } else { auto src = source_location(region(loc)); return err(make_error_info("toml::parse_offset_datetime: " "expect date-time delimiter `T` or ` `(space).", std::move(src), "here" )); } // ---------------------------------------------------------------------- // time part auto time_fmt_reg = parse_local_time_only(loc, ctx); if(time_fmt_reg.is_err()) { return err(time_fmt_reg.unwrap_err()); } fmt.has_seconds = std::get<1>(time_fmt_reg.unwrap()).has_seconds; fmt.subsecond_precision = std::get<1>(time_fmt_reg.unwrap()).subsecond_precision; // ---------------------------------------------------------------------- // offset part const auto ofs_reg = syntax::time_offset(spec).scan(loc); if( ! ofs_reg.is_ok()) { return err(make_syntax_error("toml::parse_offset_datetime: " "invalid offset: offset must be like: Z, +01:00, or -10:00.", syntax::time_offset(spec), loc)); } const auto ofs_str = ofs_reg.as_string(); time_offset offset(0, 0); assert(ofs_str.size() != 0); if(ofs_str.at(0) == char_type('+') || ofs_str.at(0) == char_type('-')) { const auto hour_r = from_string(ofs_str.substr(1, 2)); const auto minute_r = from_string(ofs_str.substr(4, 2)); if(hour_r.is_err()) { auto src = source_location(region(loc)); return err(make_error_info("toml::parse_offset_datetime: " "Failed to read offset hour part", std::move(src), "here")); } if(minute_r.is_err()) { auto src = source_location(region(loc)); return err(make_error_info("toml::parse_offset_datetime: " "Failed to read offset minute part", std::move(src), "here")); } const auto hour = hour_r.unwrap(); const auto minute = minute_r.unwrap(); if(ofs_str.at(0) == '+') { offset = time_offset(hour, minute); } else { offset = time_offset(-hour, -minute); } } else { assert(ofs_str.at(0) == char_type('Z') || ofs_str.at(0) == char_type('z')); } if (offset.hour < -24 || 24 < offset.hour || offset.minute < -60 || 60 < offset.minute) { return err(make_error_info("toml::parse_offset_datetime: " "too large offset: |hour| <= 24, |minute| <= 60", source_location(region(first, loc)), "here")); } // ---------------------------------------------------------------------- region reg(first, loc); offset_datetime val(local_datetime(std::get<0>(date_fmt_reg.unwrap()), std::get<0>(time_fmt_reg.unwrap())), offset); return ok(basic_value(val, std::move(fmt), {}, std::move(reg))); } /* ============================================================================ * ___ _ _ * / __| |_ _ _(_)_ _ __ _ * \__ \ _| '_| | ' \/ _` | * |___/\__|_| |_|_||_\__, | * |___/ */ template result::string_type, error_info> parse_utf8_codepoint(const region& reg) { using string_type = typename basic_value::string_type; using char_type = typename string_type::value_type; // assert(reg.as_lines().size() == 1); // XXX heavy check const auto str = reg.as_string(); assert( ! str.empty()); assert(str.front() == 'u' || str.front() == 'U' || str.front() == 'x'); std::uint_least32_t codepoint; std::istringstream iss(str.substr(1)); iss >> std::hex >> codepoint; const auto to_char = [](const std::uint_least32_t i) noexcept -> char_type { const auto uc = static_cast(i & 0xFF); return cxx::bit_cast(uc); }; string_type character; if(codepoint < 0x80) // U+0000 ... U+0079 ; just an ASCII. { character += static_cast(codepoint); } else if(codepoint < 0x800) //U+0080 ... U+07FF { // 110yyyyx 10xxxxxx; 0x3f == 0b0011'1111 character += to_char(0xC0|(codepoint >> 6 )); character += to_char(0x80|(codepoint & 0x3F)); } else if(codepoint < 0x10000) // U+0800...U+FFFF { if(0xD800 <= codepoint && codepoint <= 0xDFFF) { auto src = source_location(reg); return err(make_error_info("toml::parse_utf8_codepoint: " "[0xD800, 0xDFFF] is not a valid UTF-8", std::move(src), "here")); } assert(codepoint < 0xD800 || 0xDFFF < codepoint); // 1110yyyy 10yxxxxx 10xxxxxx character += to_char(0xE0| (codepoint >> 12)); character += to_char(0x80|((codepoint >> 6 ) & 0x3F)); character += to_char(0x80|((codepoint ) & 0x3F)); } else if(codepoint < 0x110000) // U+010000 ... U+10FFFF { // 11110yyy 10yyxxxx 10xxxxxx 10xxxxxx character += to_char(0xF0| (codepoint >> 18)); character += to_char(0x80|((codepoint >> 12) & 0x3F)); character += to_char(0x80|((codepoint >> 6 ) & 0x3F)); character += to_char(0x80|((codepoint ) & 0x3F)); } else // out of UTF-8 region { auto src = source_location(reg); return err(make_error_info("toml::parse_utf8_codepoint: " "input codepoint is too large.", std::move(src), "must be in range [0x00, 0x10FFFF]")); } return ok(character); } template result::string_type, error_info> parse_escape_sequence(location& loc, const context& ctx) { using string_type = typename basic_value::string_type; using char_type = typename string_type::value_type; const auto& spec = ctx.toml_spec(); assert( ! loc.eof()); assert(loc.current() == '\\'); loc.advance(); // consume the first backslash string_type retval; if (loc.current() == '\\') { retval += char_type('\\'); loc.advance(); } else if(loc.current() == '"') { retval += char_type('\"'); loc.advance(); } else if(loc.current() == 'b') { retval += char_type('\b'); loc.advance(); } else if(loc.current() == 'f') { retval += char_type('\f'); loc.advance(); } else if(loc.current() == 'n') { retval += char_type('\n'); loc.advance(); } else if(loc.current() == 'r') { retval += char_type('\r'); loc.advance(); } else if(loc.current() == 't') { retval += char_type('\t'); loc.advance(); } else if(spec.v1_1_0_add_escape_sequence_e && loc.current() == 'e') { retval += char_type('\x1b'); loc.advance(); } else if(spec.v1_1_0_add_escape_sequence_x && loc.current() == 'x') { auto scanner = sequence(character('x'), repeat_exact(2, syntax::hexdig(spec))); const auto reg = scanner.scan(loc); if( ! reg.is_ok()) { auto src = source_location(region(loc)); return err(make_error_info("toml::parse_escape_sequence: " "invalid token found in UTF-8 codepoint \\xhh", std::move(src), "here")); } const auto utf8 = parse_utf8_codepoint(reg); if(utf8.is_err()) { return err(utf8.as_err()); } retval += utf8.unwrap(); } else if(loc.current() == 'u') { auto scanner = sequence(character('u'), repeat_exact(4, syntax::hexdig(spec))); const auto reg = scanner.scan(loc); if( ! reg.is_ok()) { auto src = source_location(region(loc)); return err(make_error_info("toml::parse_escape_sequence: " "invalid token found in UTF-8 codepoint \\uhhhh", std::move(src), "here")); } const auto utf8 = parse_utf8_codepoint(reg); if(utf8.is_err()) { return err(utf8.as_err()); } retval += utf8.unwrap(); } else if(loc.current() == 'U') { auto scanner = sequence(character('U'), repeat_exact(8, syntax::hexdig(spec))); const auto reg = scanner.scan(loc); if( ! reg.is_ok()) { auto src = source_location(region(loc)); return err(make_error_info("toml::parse_escape_sequence: " "invalid token found in UTF-8 codepoint \\Uhhhhhhhh", std::move(src), "here")); } const auto utf8 = parse_utf8_codepoint(reg); if(utf8.is_err()) { return err(utf8.as_err()); } retval += utf8.unwrap(); } else { auto src = source_location(region(loc)); std::string escape_seqs = "allowed escape seqs: \\\\, \\\", \\b, \\f, \\n, \\r, \\t"; if(spec.v1_1_0_add_escape_sequence_e) { escape_seqs += ", \\e"; } if(spec.v1_1_0_add_escape_sequence_x) { escape_seqs += ", \\xhh"; } escape_seqs += ", \\uhhhh, or \\Uhhhhhhhh"; return err(make_error_info("toml::parse_escape_sequence: " "unknown escape sequence.", std::move(src), escape_seqs)); } return ok(retval); } template result, error_info> parse_ml_basic_string(location& loc, const context& ctx) { const auto first = loc; const auto& spec = ctx.toml_spec(); string_format_info fmt; fmt.fmt = string_format::multiline_basic; auto reg = syntax::ml_basic_string(spec).scan(loc); if( ! reg.is_ok()) { return err(make_syntax_error("toml::parse_ml_basic_string: " "invalid string format", syntax::ml_basic_string(spec), loc)); } // ---------------------------------------------------------------------- // it matches. gen value auto str = reg.as_string(); // we already checked that it starts with """ and ends with """. assert(str.substr(0, 3) == "\"\"\""); str.erase(0, 3); assert(str.size() >= 3); assert(str.substr(str.size()-3, 3) == "\"\"\""); str.erase(str.size()-3, 3); // the first newline just after """ is trimmed if(str.size() >= 1 && str.at(0) == '\n') { str.erase(0, 1); fmt.start_with_newline = true; } else if(str.size() >= 2 && str.at(0) == '\r' && str.at(1) == '\n') { str.erase(0, 2); fmt.start_with_newline = true; } using string_type = typename basic_value::string_type; string_type val; { auto iter = str.cbegin(); while(iter != str.cend()) { if(*iter == '\\') // remove whitespaces around escaped-newline { // we assume that the string is not too long to copy auto loc2 = make_temporary_location(make_string(iter, str.cend())); if(syntax::escaped_newline(spec).scan(loc2).is_ok()) { std::advance(iter, loc2.get_location()); // skip escaped newline and indent // now iter points non-WS char assert(iter == str.end() || (*iter != ' ' && *iter != '\t')); } else // normal escape seq. { auto esc = parse_escape_sequence(loc2, ctx); // syntax does not check its value. the unicode codepoint may be // invalid, e.g. out-of-bound, [0xD800, 0xDFFF] if(esc.is_err()) { return err(esc.unwrap_err()); } val += esc.unwrap(); std::advance(iter, loc2.get_location()); } } else // we already checked the syntax. we don't need to check it again. { val += static_cast(*iter); ++iter; } } } return ok(basic_value( std::move(val), std::move(fmt), {}, std::move(reg) )); } template result::string_type, region>, error_info> parse_basic_string_only(location& loc, const context& ctx) { const auto first = loc; const auto& spec = ctx.toml_spec(); auto reg = syntax::basic_string(spec).scan(loc); if( ! reg.is_ok()) { return err(make_syntax_error("toml::parse_basic_string: " "invalid string format", syntax::basic_string(spec), loc)); } // ---------------------------------------------------------------------- // it matches. gen value auto str = reg.as_string(); assert(str.back() == '\"'); str.pop_back(); assert(str.at(0) == '\"'); str.erase(0, 1); using string_type = typename basic_value::string_type; using char_type = typename string_type::value_type; string_type val; { auto iter = str.begin(); while(iter != str.end()) { if(*iter == '\\') { auto loc2 = make_temporary_location(make_string(iter, str.end())); auto esc = parse_escape_sequence(loc2, ctx); // syntax does not check its value. the unicode codepoint may be // invalid, e.g. out-of-bound, [0xD800, 0xDFFF] if(esc.is_err()) { return err(esc.unwrap_err()); } val += esc.unwrap(); std::advance(iter, loc2.get_location()); } else { val += char_type(*iter); // we already checked the syntax. ++iter; } } } return ok(std::make_pair(val, reg)); } template result, error_info> parse_basic_string(location& loc, const context& ctx) { const auto first = loc; string_format_info fmt; fmt.fmt = string_format::basic; auto val_res = parse_basic_string_only(loc, ctx); if(val_res.is_err()) { return err(std::move(val_res.unwrap_err())); } auto val = std::move(val_res.unwrap().first ); auto reg = std::move(val_res.unwrap().second); return ok(basic_value(std::move(val), std::move(fmt), {}, std::move(reg))); } template result, error_info> parse_ml_literal_string(location& loc, const context& ctx) { const auto first = loc; const auto& spec = ctx.toml_spec(); string_format_info fmt; fmt.fmt = string_format::multiline_literal; auto reg = syntax::ml_literal_string(spec).scan(loc); if( ! reg.is_ok()) { return err(make_syntax_error("toml::parse_ml_literal_string: " "invalid string format", syntax::ml_literal_string(spec), loc)); } // ---------------------------------------------------------------------- // it matches. gen value auto str = reg.as_string(); assert(str.substr(0, 3) == "'''"); assert(str.substr(str.size()-3, 3) == "'''"); str.erase(0, 3); str.erase(str.size()-3, 3); // the first newline just after """ is trimmed if(str.size() >= 1 && str.at(0) == '\n') { str.erase(0, 1); fmt.start_with_newline = true; } else if(str.size() >= 2 && str.at(0) == '\r' && str.at(1) == '\n') { str.erase(0, 2); fmt.start_with_newline = true; } using string_type = typename basic_value::string_type; string_type val(str.begin(), str.end()); return ok(basic_value( std::move(val), std::move(fmt), {}, std::move(reg) )); } template result::string_type, region>, error_info> parse_literal_string_only(location& loc, const context& ctx) { const auto first = loc; const auto& spec = ctx.toml_spec(); auto reg = syntax::literal_string(spec).scan(loc); if( ! reg.is_ok()) { return err(make_syntax_error("toml::parse_literal_string: " "invalid string format", syntax::literal_string(spec), loc)); } // ---------------------------------------------------------------------- // it matches. gen value auto str = reg.as_string(); assert(str.back() == '\''); str.pop_back(); assert(str.at(0) == '\''); str.erase(0, 1); using string_type = typename basic_value::string_type; string_type val(str.begin(), str.end()); return ok(std::make_pair(std::move(val), std::move(reg))); } template result, error_info> parse_literal_string(location& loc, const context& ctx) { const auto first = loc; string_format_info fmt; fmt.fmt = string_format::literal; auto val_res = parse_literal_string_only(loc, ctx); if(val_res.is_err()) { return err(std::move(val_res.unwrap_err())); } auto val = std::move(val_res.unwrap().first ); auto reg = std::move(val_res.unwrap().second); return ok(basic_value( std::move(val), std::move(fmt), {}, std::move(reg) )); } template result, error_info> parse_string(location& loc, const context& ctx) { const auto first = loc; if( ! loc.eof() && loc.current() == '"') { if(literal("\"\"\"").scan(loc).is_ok()) { loc = first; return parse_ml_basic_string(loc, ctx); } else { loc = first; return parse_basic_string(loc, ctx); } } else if( ! loc.eof() && loc.current() == '\'') { if(literal("'''").scan(loc).is_ok()) { loc = first; return parse_ml_literal_string(loc, ctx); } else { loc = first; return parse_literal_string(loc, ctx); } } else { auto src = source_location(region(loc)); return err(make_error_info("toml::parse_string: " "not a string", std::move(src), "here")); } } template result, error_info> parse_null(location& loc, const context& ctx) { const auto& spec = ctx.toml_spec(); if( ! spec.ext_null_value) { return err(make_error_info("toml::parse_null: " "invalid spec: spec.ext_null_value must be true.", source_location(region(loc)), "here")); } // ---------------------------------------------------------------------- // check syntax auto reg = syntax::null_value(spec).scan(loc); if( ! reg.is_ok()) { return err(make_syntax_error("toml::parse_null: " "invalid null: null must be lowercase. ", syntax::null_value(spec), loc)); } // ---------------------------------------------------------------------- // it matches. gen value // ---------------------------------------------------------------------- // no format info for boolean return ok(basic_value(detail::none_t{}, std::move(reg))); } /* ============================================================================ * _ __ * | |/ /___ _ _ * | ' result::key_type, error_info> parse_simple_key(location& loc, const context& ctx) { using key_type = typename basic_value::key_type; const auto& spec = ctx.toml_spec(); if(loc.current() == '\"') { auto str_res = parse_basic_string_only(loc, ctx); if(str_res.is_ok()) { return ok(std::move(str_res.unwrap().first)); } else { return err(std::move(str_res.unwrap_err())); } } else if(loc.current() == '\'') { auto str_res = parse_literal_string_only(loc, ctx); if(str_res.is_ok()) { return ok(std::move(str_res.unwrap().first)); } else { return err(std::move(str_res.unwrap_err())); } } // bare key. if(const auto bare = syntax::unquoted_key(spec).scan(loc)) { return ok(string_conv(bare.as_string())); } else { std::string postfix; if(spec.v1_1_0_allow_non_english_in_bare_keys) { postfix = "Hint: Not all Unicode characters are allowed as bare key.\n"; } else { postfix = "Hint: non-ASCII scripts are allowed in toml v1.1.0, but not in v1.0.0.\n"; } return err(make_syntax_error("toml::parse_simple_key: " "invalid key: key must be \"quoted\", 'quoted-literal', or bare key.", syntax::unquoted_key(spec), loc, postfix)); } } // dotted key become vector of keys template result::key_type>, region>, error_info> parse_key(location& loc, const context& ctx) { const auto first = loc; const auto& spec = ctx.toml_spec(); using key_type = typename basic_value::key_type; std::vector keys; while( ! loc.eof()) { auto key = parse_simple_key(loc, ctx); if( ! key.is_ok()) { return err(key.unwrap_err()); } keys.push_back(std::move(key.unwrap())); auto reg = syntax::dot_sep(spec).scan(loc); if( ! reg.is_ok()) { break; } } if(keys.empty()) { auto src = source_location(region(first)); return err(make_error_info("toml::parse_key: expected a new key, " "but got nothing", std::move(src), "reached EOF")); } return ok(std::make_pair(std::move(keys), region(first, loc))); } // ============================================================================ // forward-decl to implement parse_array and parse_table template result, error_info> parse_value(location&, context& ctx); template result::key_type>, region>, basic_value >, error_info> parse_key_value_pair(location& loc, context& ctx) { const auto first = loc; const auto& spec = ctx.toml_spec(); auto key_res = parse_key(loc, ctx); if(key_res.is_err()) { loc = first; return err(key_res.unwrap_err()); } if( ! syntax::keyval_sep(spec).scan(loc).is_ok()) { auto e = make_syntax_error("toml::parse_key_value_pair: " "invalid key value separator `=`", syntax::keyval_sep(spec), loc); loc = first; return err(std::move(e)); } auto v_res = parse_value(loc, ctx); if(v_res.is_err()) { // loc = first; return err(v_res.unwrap_err()); } return ok(std::make_pair(std::move(key_res.unwrap()), std::move(v_res.unwrap()))); } /* ============================================================================ * __ _ _ _ _ _ __ _ _ _ * / _` | '_| '_/ _` | || | * \__,_|_| |_| \__,_|\_, | * |__/ */ // array(and multiline inline table with `{` and `}`) has the following format. // `[` // (ws|newline|comment-line)? (value) (ws|newline|comment-line)? `,` // (ws|newline|comment-line)? (value) (ws|newline|comment-line)? `,` // ... // (ws|newline|comment-line)? (value) (ws|newline|comment-line)? (`,`)? // (ws|newline|comment-line)? `]` // it skips (ws|newline|comment-line) and returns the token. template struct multiline_spacer { using comment_type = typename TC::comment_type; bool newline_found; indent_char indent_type; std::int32_t indent; comment_type comments; }; template std::ostream& operator<<(std::ostream& os, const multiline_spacer& sp) { os << "{newline=" << sp.newline_found << ", "; os << "indent_type=" << sp.indent_type << ", "; os << "indent=" << sp.indent << ", "; os << "comments=" << sp.comments.size() << "}"; return os; } template cxx::optional> skip_multiline_spacer(location& loc, context& ctx, const bool newline_found = false) { const auto& spec = ctx.toml_spec(); multiline_spacer spacer; spacer.newline_found = newline_found; spacer.indent_type = indent_char::none; spacer.indent = 0; spacer.comments.clear(); bool spacer_found = false; while( ! loc.eof()) { if(auto comm = sequence(syntax::comment(spec), syntax::newline(spec)).scan(loc)) { spacer.newline_found = true; auto comment = comm.as_string(); if( ! comment.empty() && comment.back() == '\n') { comment.pop_back(); if (!comment.empty() && comment.back() == '\r') { comment.pop_back(); } } spacer.comments.push_back(std::move(comment)); spacer.indent_type = indent_char::none; spacer.indent = 0; spacer_found = true; } else if(auto nl = syntax::newline(spec).scan(loc)) { spacer.newline_found = true; spacer.comments.clear(); spacer.indent_type = indent_char::none; spacer.indent = 0; spacer_found = true; } else if(auto sp = repeat_at_least(1, character(cxx::bit_cast(' '))).scan(loc)) { spacer.indent_type = indent_char::space; spacer.indent = static_cast(sp.length()); spacer_found = true; } else if(auto tabs = repeat_at_least(1, character(cxx::bit_cast('\t'))).scan(loc)) { spacer.indent_type = indent_char::tab; spacer.indent = static_cast(tabs.length()); spacer_found = true; } else { break; // done } } if( ! spacer_found) { return cxx::make_nullopt(); } return spacer; } // not an [[array.of.tables]]. It parses ["this", "type"] template result, error_info> parse_array(location& loc, context& ctx) { const auto num_errors = ctx.errors().size(); const auto first = loc; if(loc.eof() || loc.current() != '[') { auto src = source_location(region(loc)); return err(make_error_info("toml::parse_array: " "The next token is not an array", std::move(src), "here")); } loc.advance(); typename basic_value::array_type val; array_format_info fmt; fmt.fmt = array_format::oneline; fmt.indent_type = indent_char::none; auto spacer = skip_multiline_spacer(loc, ctx); if(spacer.has_value() && spacer.value().newline_found) { fmt.fmt = array_format::multiline; } bool comma_found = true; while( ! loc.eof()) { if(loc.current() == location::char_type(']')) { if(spacer.has_value() && spacer.value().newline_found && spacer.value().indent_type != indent_char::none) { fmt.indent_type = spacer.value().indent_type; fmt.closing_indent = spacer.value().indent; } break; } if( ! comma_found) { auto src = source_location(region(loc)); return err(make_error_info("toml::parse_array: " "expected value-separator `,` or closing `]`", std::move(src), "here")); } if(spacer.has_value() && spacer.value().newline_found && spacer.value().indent_type != indent_char::none) { fmt.indent_type = spacer.value().indent_type; fmt.body_indent = spacer.value().indent; } if(auto elem_res = parse_value(loc, ctx)) { auto elem = std::move(elem_res.unwrap()); if(spacer.has_value()) // copy previous comments to value { elem.comments() = std::move(spacer.value().comments); } // parse spaces between a value and a comma // array = [ // 42 , # the answer // ^^^^ // 3.14 # pi // , 2.71 ^^^^ // ^^ spacer = skip_multiline_spacer(loc, ctx); if(spacer.has_value()) { for(std::size_t i=0; i( std::move(val), std::move(fmt), {}, region(first, loc) )); } /* ============================================================================ * _ _ _ _ _ _ * (_)_ _ | (_)_ _ ___ | |_ __ _| |__| |___ * | | ' \| | | ' \/ -_) | _/ _` | '_ \ / -_) * |_|_||_|_|_|_||_\___| \__\__,_|_.__/_\___| */ // ---------------------------------------------------------------------------- // insert_value is the most complicated part of the toml spec. // // To parse a toml file correctly, we sometimes need to check an exising value // is appendable or not. // // For example while parsing an inline array of tables, // // ```toml // aot = [ // {a = "foo"}, // {a = "bar", b = "baz"}, // ] // ``` // // this `aot` is appendable until parser reaches to `]`. After that, it becomes // non-appendable. // // On the other hand, a normal array of tables, such as // // ```toml // [[aot]] // a = "foo" // // [[aot]] // a = "bar" // b = "baz" // ``` // This `[[aot]]` is appendable until the parser reaches to the EOF. // // // It becomes a bit more difficult in case of dotted keys. // In TOML, it is allowed to append a key-value pair to a table that is // *implicitly* defined by a subtable definitino. // // ```toml // [x.y.z] // w = 123 // // [x] // a = "foo" # OK. x is defined implicitly by `[x.y.z]`. // ``` // // But if the table is defined by a dotted keys, it is not appendable. // // ```toml // [x] // y.z.w = 123 // // [x.y] // # ERROR. x.y is already defined by a dotted table in the previous table. // ``` // // Also, reopening a table using dotted keys is invalid. // // ```toml // [x.y.z] // w = 123 // // [x] // y.z.v = 42 # ERROR. [x.y.z] is already defined. // ``` // // // ```toml // [a] // b.c = "foo" // b.d = "bar" // ``` // // // ```toml // a.b = "foo" // [a] // c = "bar" # ERROR // ``` // // In summary, // - a table must be defined only once. // - assignment to an exising table is possible only when: // - defining a subtable [x.y] to an existing table [x]. // - defining supertable [x] explicitly after [x.y]. // - adding dotted keys in the same table. enum class inserting_value_kind : std::uint8_t { std_table, // insert [standard.table] array_table, // insert [[array.of.tables]] dotted_keys // insert a.b.c = "this" }; template result*, error_info> insert_value(const inserting_value_kind kind, typename basic_value::table_type* current_table_ptr, const std::vector::key_type>& keys, region key_reg, basic_value val) { using value_type = basic_value; using array_type = typename basic_value::array_type; using table_type = typename basic_value::table_type; auto key_loc = source_location(key_reg); assert( ! keys.empty()); // dotted key can insert to dotted key tables defined at the same level. // dotted key can NOT reopen a table even if it is implcitly-defined one. // // [x.y.z] # define x and x.y implicitly. // a = 42 // // [x] # reopening implcitly defined table // r.s.t = 3.14 # VALID r and r.s are new tables. // r.s.u = 2.71 # VALID r and r.s are dotted-key tables. valid. // // y.z.b = "foo" # INVALID x.y.z are multiline table, not a dotted key. // y.c = "bar" # INVALID x.y is implicit multiline table, not a dotted key. // a table cannot reopen dotted-key tables. // // [t1] // t2.t3.v = 0 // [t1.t2] # INVALID t1.t2 is defined as a dotted-key table. for(std::size_t i=0; i{}, key_reg)); assert(current_table.at(key).is_table()); current_table_ptr = std::addressof(current_table.at(key).as_table()); } else if (found->second.is_table()) { const auto fmt = found->second.as_table_fmt().fmt; if(fmt == table_format::oneline || fmt == table_format::multiline_oneline) { // foo = {bar = "baz"} or foo = { \n bar = "baz" \n } return err(make_error_info("toml::insert_value: " "failed to insert a value: inline table is immutable", key_loc, "inserting this", found->second.location(), "to this table")); } // dotted key cannot reopen a table. if(kind ==inserting_value_kind::dotted_keys && fmt != table_format::dotted) { return err(make_error_info("toml::insert_value: " "reopening a table using dotted keys", key_loc, "dotted key cannot reopen a table", found->second.location(), "this table is already closed")); } assert(found->second.is_table()); current_table_ptr = std::addressof(found->second.as_table()); } else if(found->second.is_array_of_tables()) { // aot = [{this = "type", of = "aot"}] # cannot be reopened if(found->second.as_array_fmt().fmt != array_format::array_of_tables) { return err(make_error_info("toml::insert_value:" "inline array of tables are immutable", key_loc, "inserting this", found->second.location(), "inline array of tables")); } // appending to [[aot]] if(kind == inserting_value_kind::dotted_keys) { // [[array.of.tables]] // [array.of] # reopening supertable is okay // tables.x = "foo" # appending `x` to the first table return err(make_error_info("toml::insert_value:" "dotted key cannot reopen an array-of-tables", key_loc, "inserting this", found->second.location(), "to this array-of-tables.")); } // insert_value_by_dotkeys::std_table // [[array.of.tables]] // [array.of.tables.subtable] # appending to the last aot // // insert_value_by_dotkeys::array_table // [[array.of.tables]] // [[array.of.tables.subtable]] # appending to the last aot auto& current_array_table = found->second.as_array().back(); assert(current_array_table.is_table()); current_table_ptr = std::addressof(current_array_table.as_table()); } else { return err(make_error_info("toml::insert_value: " "failed to insert a value, value already exists", key_loc, "while inserting this", found->second.location(), "non-table value already exists")); } } else // this is the last key. insert a new value. { switch(kind) { case inserting_value_kind::dotted_keys: { if(current_table.find(key) != current_table.end()) { return err(make_error_info("toml::insert_value: " "failed to insert a value, value already exists", key_loc, "inserting this", current_table.at(key).location(), "but value already exists")); } current_table.emplace(key, std::move(val)); return ok(std::addressof(current_table.at(key))); } case inserting_value_kind::std_table: { // defining a new table or reopening supertable auto found = current_table.find(key); if(found == current_table.end()) // define a new aot { current_table.emplace(key, std::move(val)); return ok(std::addressof(current_table.at(key))); } else // the table is already defined, reopen it { // assigning a [std.table]. it must be an implicit table. auto& target = found->second; if( ! target.is_table() || // could be an array-of-tables target.as_table_fmt().fmt != table_format::implicit) { return err(make_error_info("toml::insert_value: " "failed to insert a table, table already defined", key_loc, "inserting this", target.location(), "this table is explicitly defined")); } // merge table for(const auto& kv : val.as_table()) { if(target.contains(kv.first)) { // [x.y.z] // w = "foo" // [x] // y = "bar" return err(make_error_info("toml::insert_value: " "failed to insert a table, table keys conflict to each other", key_loc, "inserting this table", kv.second.location(), "having this value", target.at(kv.first).location(), "already defined here")); } else { target[kv.first] = kv.second; } } // change implicit -> explicit target.as_table_fmt().fmt = table_format::multiline; // change definition region change_region_of_value(target, val); return ok(std::addressof(current_table.at(key))); } } case inserting_value_kind::array_table: { auto found = current_table.find(key); if(found == current_table.end()) // define a new aot { array_format_info fmt; fmt.fmt = array_format::array_of_tables; fmt.indent_type = indent_char::none; current_table.emplace(key, value_type( array_type{ std::move(val) }, std::move(fmt), std::vector{}, std::move(key_reg) )); assert( ! current_table.at(key).as_array().empty()); return ok(std::addressof(current_table.at(key).as_array().back())); } else // the array is already defined, append to it { if( ! found->second.is_array_of_tables()) { return err(make_error_info("toml::insert_value: " "failed to insert an array of tables, value already exists", key_loc, "while inserting this", found->second.location(), "non-table value already exists")); } if(found->second.as_array_fmt().fmt != array_format::array_of_tables) { return err(make_error_info("toml::insert_value: " "failed to insert a table, inline array of tables is immutable", key_loc, "while inserting this", found->second.location(), "this is inline array-of-tables")); } found->second.as_array().push_back(std::move(val)); assert( ! current_table.at(key).as_array().empty()); return ok(std::addressof(current_table.at(key).as_array().back())); } } default: {assert(false);} } } } return err(make_error_info("toml::insert_key: no keys found", std::move(key_loc), "here")); } // ---------------------------------------------------------------------------- template result, error_info> parse_inline_table(location& loc, context& ctx) { using table_type = typename basic_value::table_type; const auto num_errors = ctx.errors().size(); const auto first = loc; const auto& spec = ctx.toml_spec(); if(loc.eof() || loc.current() != '{') { auto src = source_location(region(loc)); return err(make_error_info("toml::parse_inline_table: " "The next token is not an inline table", std::move(src), "here")); } loc.advance(); table_type table; table_format_info fmt; fmt.fmt = table_format::oneline; fmt.indent_type = indent_char::none; cxx::optional> spacer(cxx::make_nullopt()); if(spec.v1_1_0_allow_newlines_in_inline_tables) { spacer = skip_multiline_spacer(loc, ctx); if(spacer.has_value() && spacer.value().newline_found) { fmt.fmt = table_format::multiline_oneline; } } else { skip_whitespace(loc, ctx); } bool still_empty = true; bool comma_found = false; while( ! loc.eof()) { // closing! if(loc.current() == '}') { if(comma_found && ! spec.v1_1_0_allow_trailing_comma_in_inline_tables) { auto src = source_location(region(loc)); return err(make_error_info("toml::parse_inline_table: trailing " "comma is not allowed in TOML-v1.0.0)", std::move(src), "here")); } if(spec.v1_1_0_allow_newlines_in_inline_tables) { if(spacer.has_value() && spacer.value().newline_found && spacer.value().indent_type != indent_char::none) { fmt.indent_type = spacer.value().indent_type; fmt.closing_indent = spacer.value().indent; } } break; } // if we already found a value and didn't found `,` nor `}`, error. if( ! comma_found && ! still_empty) { auto src = source_location(region(loc)); return err(make_error_info("toml::parse_inline_table: " "expected value-separator `,` or closing `}`", std::move(src), "here")); } // parse indent. if(spacer.has_value() && spacer.value().newline_found && spacer.value().indent_type != indent_char::none) { fmt.indent_type = spacer.value().indent_type; fmt.body_indent = spacer.value().indent; } still_empty = false; // parsing a value... if(auto kv_res = parse_key_value_pair(loc, ctx)) { auto keys = std::move(kv_res.unwrap().first.first); auto key_reg = std::move(kv_res.unwrap().first.second); auto val = std::move(kv_res.unwrap().second); auto ins_res = insert_value(inserting_value_kind::dotted_keys, std::addressof(table), keys, std::move(key_reg), std::move(val)); if(ins_res.is_err()) { ctx.report_error(std::move(ins_res.unwrap_err())); // we need to skip until the next value (or end of the table) // because we don't have valid kv pair. while( ! loc.eof()) { const auto c = loc.current(); if(c == ',' || c == '\n' || c == '}') { comma_found = (c == ','); break; } loc.advance(); } continue; } // if comment line follows immediately(without newline) after `,`, then // the comment is for the elem. we need to check if comment follows `,`. // // (key) = (val) (ws|newline|comment-line)? `,` (ws)? (comment)? if(spec.v1_1_0_allow_newlines_in_inline_tables) { if(spacer.has_value()) // copy previous comments to value { for(std::size_t i=0; icomments().push_back(spacer.value().comments.at(i)); } } spacer = skip_multiline_spacer(loc, ctx); if(spacer.has_value()) { for(std::size_t i=0; icomments().push_back(spacer.value().comments.at(i)); } if(spacer.value().newline_found) { fmt.fmt = table_format::multiline_oneline; if(spacer.value().indent_type != indent_char::none) { fmt.indent_type = spacer.value().indent_type; fmt.body_indent = spacer.value().indent; } } } } else { skip_whitespace(loc, ctx); } comma_found = character(',').scan(loc).is_ok(); if(spec.v1_1_0_allow_newlines_in_inline_tables) { auto com_res = parse_comment_line(loc, ctx); if(com_res.is_err()) { ctx.report_error(com_res.unwrap_err()); } const bool comment_found = com_res.is_ok() && com_res.unwrap().has_value(); if(comment_found) { fmt.fmt = table_format::multiline_oneline; ins_res.unwrap()->comments().push_back(com_res.unwrap().value()); } if(comma_found) { spacer = skip_multiline_spacer(loc, ctx, comment_found); if(spacer.has_value() && spacer.value().newline_found) { fmt.fmt = table_format::multiline_oneline; } } } else { skip_whitespace(loc, ctx); } } else { ctx.report_error(std::move(kv_res.unwrap_err())); while( ! loc.eof()) { if(loc.current() == '}') { break; } if( ! spec.v1_1_0_allow_newlines_in_inline_tables && loc.current() == '\n') { break; } loc.advance(); } break; } } if(loc.current() != '}') { auto src = source_location(region(loc)); return err(make_error_info("toml::parse_inline_table: " "missing closing bracket `}`", std::move(src), "expected `}`, reached line end")); } else { loc.advance(); // skip } } // any error reported from this function if(num_errors < ctx.errors().size()) { assert(ctx.has_error()); // already reported return err(ctx.pop_last_error()); } basic_value retval( std::move(table), std::move(fmt), {}, region(first, loc)); return ok(std::move(retval)); } /* ============================================================================ * _ * __ ____ _| |_ _ ___ * \ V / _` | | || / -_) * \_/\__,_|_|\_,_\___| */ template result guess_number_type(const location& first, const context& ctx) { const auto& spec = ctx.toml_spec(); location loc = first; if(syntax::offset_datetime(spec).scan(loc).is_ok()) { return ok(value_t::offset_datetime); } loc = first; if(syntax::local_datetime(spec).scan(loc).is_ok()) { const auto curr = loc.current(); // if offset_datetime contains bad offset, it syntax::offset_datetime // fails to scan it. if(curr == '+' || curr == '-') { return err(make_syntax_error("bad offset: must be [+-]HH:MM or Z", syntax::time_offset(spec), loc, std::string( "Hint: valid : +09:00, -05:30\n" "Hint: invalid: +9:00, -5:30\n"))); } return ok(value_t::local_datetime); } loc = first; if(syntax::local_date(spec).scan(loc).is_ok()) { // bad time may appear after this. if( ! loc.eof()) { const auto c = loc.current(); if(c == 'T' || c == 't') { loc.advance(); return err(make_syntax_error("bad time: must be HH:MM:SS.subsec", syntax::local_time(spec), loc, std::string( "Hint: valid : 1979-05-27T07:32:00, 1979-05-27 07:32:00.999999\n" "Hint: invalid: 1979-05-27T7:32:00, 1979-05-27 17:32\n"))); } if(c == ' ') { // A space is allowed as a delimiter between local time. // But there is a case where bad time follows a space. // - invalid: 2019-06-16 7:00:00 // - valid : 2019-06-16 07:00:00 loc.advance(); if( ! loc.eof() && ('0' <= loc.current() && loc.current() <= '9')) { return err(make_syntax_error("bad time: must be HH:MM:SS.subsec", syntax::local_time(spec), loc, std::string( "Hint: valid : 1979-05-27T07:32:00, 1979-05-27 07:32:00.999999\n" "Hint: invalid: 1979-05-27T7:32:00, 1979-05-27 17:32\n"))); } } if('0' <= c && c <= '9') { return err(make_syntax_error("bad datetime: missing T or space", character_either{'T', 't', ' '}, loc, std::string( "Hint: valid : 1979-05-27T07:32:00, 1979-05-27 07:32:00.999999\n" "Hint: invalid: 1979-05-27T7:32:00, 1979-05-27 17:32\n"))); } } return ok(value_t::local_date); } loc = first; if(syntax::local_time(spec).scan(loc).is_ok()) { return ok(value_t::local_time); } loc = first; if(syntax::floating(spec).scan(loc).is_ok()) { if( ! loc.eof() && loc.current() == '_') { if(spec.ext_num_suffix && syntax::num_suffix(spec).scan(loc).is_ok()) { return ok(value_t::floating); } auto src = source_location(region(loc)); return err(make_error_info( "bad float: `_` must be surrounded by digits", std::move(src), "invalid underscore", "Hint: valid : +1.0, -2e-2, 3.141_592_653_589, inf, nan\n" "Hint: invalid: .0, 1., _1.0, 1.0_, 1_.0, 1.0__0\n")); } return ok(value_t::floating); } loc = first; if(spec.ext_hex_float) { if(syntax::hex_floating(spec).scan(loc).is_ok()) { if( ! loc.eof() && loc.current() == '_') { if(spec.ext_num_suffix && syntax::num_suffix(spec).scan(loc).is_ok()) { return ok(value_t::floating); } auto src = source_location(region(loc)); return err(make_error_info( "bad float: `_` must be surrounded by digits", std::move(src), "invalid underscore", "Hint: valid : +1.0, -2e-2, 3.141_592_653_589, inf, nan\n" "Hint: invalid: .0, 1., _1.0, 1.0_, 1_.0, 1.0__0\n")); } return ok(value_t::floating); } loc = first; } if(auto int_reg = syntax::integer(spec).scan(loc)) { if( ! loc.eof()) { const auto c = loc.current(); if(c == '_') { if(spec.ext_num_suffix && syntax::num_suffix(spec).scan(loc).is_ok()) { return ok(value_t::integer); } if(int_reg.length() <= 2 && (int_reg.as_string() == "0" || int_reg.as_string() == "-0" || int_reg.as_string() == "+0")) { auto src = source_location(region(loc)); return err(make_error_info( "bad integer: leading zero is not allowed in decimal int", std::move(src), "leading zero", "Hint: valid : -42, 1_000, 1_2_3_4_5, 0xC0FFEE, 0b0010, 0o755\n" "Hint: invalid: _42, 1__000, 0123\n")); } else { auto src = source_location(region(loc)); return err(make_error_info( "bad integer: `_` must be surrounded by digits", std::move(src), "invalid underscore", "Hint: valid : -42, 1_000, 1_2_3_4_5, 0xC0FFEE, 0b0010, 0o755\n" "Hint: invalid: _42, 1__000, 0123\n")); } } if('0' <= c && c <= '9') { if(loc.current() == '0') { loc.retrace(); return err(make_error_info( "bad integer: leading zero", source_location(region(loc)), "leading zero is not allowed", std::string("Hint: valid : -42, 1_000, 1_2_3_4_5, 0xC0FFEE, 0b0010, 0o755\n" "Hint: invalid: _42, 1__000, 0123\n") )); } else // invalid digits, especially in oct/bin ints. { return err(make_error_info( "bad integer: invalid digit after an integer", source_location(region(loc)), "this digit is not allowed", std::string("Hint: valid : -42, 1_000, 1_2_3_4_5, 0xC0FFEE, 0b0010, 0o755\n" "Hint: invalid: _42, 1__000, 0123\n") )); } } if(c == ':' || c == '-') { auto src = source_location(region(loc)); return err(make_error_info("bad datetime: invalid format", std::move(src), "here", std::string("Hint: valid : 1979-05-27T07:32:00-07:00, 1979-05-27 07:32:00.999999Z\n" "Hint: invalid: 1979-05-27T7:32:00-7:00, 1979-05-27 7:32-00:30") )); } if(c == '.' || c == 'e' || c == 'E') { auto src = source_location(region(loc)); return err(make_error_info("bad float: invalid format", std::move(src), "here", std::string( "Hint: valid : +1.0, -2e-2, 3.141_592_653_589, inf, nan\n" "Hint: invalid: .0, 1., _1.0, 1.0_, 1_.0, 1.0__0\n"))); } } return ok(value_t::integer); } if( ! loc.eof() && loc.current() == '.') { auto src = source_location(region(loc)); return err(make_error_info("bad float: integer part is required before decimal point", std::move(src), "missing integer part", std::string( "Hint: valid : +1.0, -2e-2, 3.141_592_653_589, inf, nan\n" "Hint: invalid: .0, 1., _1.0, 1.0_, 1_.0, 1.0__0\n") )); } if( ! loc.eof() && loc.current() == '_') { auto src = source_location(region(loc)); return err(make_error_info("bad number: `_` must be surrounded by digits", std::move(src), "digits required before `_`", std::string( "Hint: valid : -42, 1_000, 1_2_3_4_5, 0xC0FFEE, 0b0010, 0o755\n" "Hint: invalid: _42, 1__000, 0123\n") )); } auto src = source_location(region(loc)); return err(make_error_info("bad format: unknown value appeared", std::move(src), "here")); } template result guess_value_type(const location& loc, const context& ctx) { const auto& sp = ctx.toml_spec(); location inner(loc); switch(loc.current()) { case '"' : {return ok(value_t::string); } case '\'': {return ok(value_t::string); } case '[' : {return ok(value_t::array); } case '{' : {return ok(value_t::table); } case 't' : { return ok(value_t::boolean); } case 'f' : { return ok(value_t::boolean); } case 'T' : // invalid boolean. { return err(make_syntax_error("toml::parse_value: " "`true` must be in lowercase. " "A string must be surrounded by quotes.", syntax::boolean(sp), inner)); } case 'F' : { return err(make_syntax_error("toml::parse_value: " "`false` must be in lowercase. " "A string must be surrounded by quotes.", syntax::boolean(sp), inner)); } case 'i' : // inf or string without quotes(syntax error). { if(literal("inf").scan(inner).is_ok()) { return ok(value_t::floating); } else { return err(make_syntax_error("toml::parse_value: " "`inf` must be in lowercase. " "A string must be surrounded by quotes.", syntax::floating(sp), inner)); } } case 'I' : // Inf or string without quotes(syntax error). { return err(make_syntax_error("toml::parse_value: " "`inf` must be in lowercase. " "A string must be surrounded by quotes.", syntax::floating(sp), inner)); } case 'n' : // nan or null-extension { if(sp.ext_null_value) { if(literal("nan").scan(inner).is_ok()) { return ok(value_t::floating); } else if(literal("null").scan(inner).is_ok()) { return ok(value_t::empty); } else { return err(make_syntax_error("toml::parse_value: " "Both `nan` and `null` must be in lowercase. " "A string must be surrounded by quotes.", syntax::floating(sp), inner)); } } else // must be nan. { if(literal("nan").scan(inner).is_ok()) { return ok(value_t::floating); } else { return err(make_syntax_error("toml::parse_value: " "`nan` must be in lowercase. " "A string must be surrounded by quotes.", syntax::floating(sp), inner)); } } } case 'N' : // nan or null-extension { if(sp.ext_null_value) { return err(make_syntax_error("toml::parse_value: " "Both `nan` and `null` must be in lowercase. " "A string must be surrounded by quotes.", syntax::floating(sp), inner)); } else { return err(make_syntax_error("toml::parse_value: " "`nan` must be in lowercase. " "A string must be surrounded by quotes.", syntax::floating(sp), inner)); } } default : { return guess_number_type(loc, ctx); } } } template result, error_info> parse_value(location& loc, context& ctx) { const auto ty_res = guess_value_type(loc, ctx); if(ty_res.is_err()) { return err(ty_res.unwrap_err()); } switch(ty_res.unwrap()) { case value_t::empty: { if(ctx.toml_spec().ext_null_value) { return parse_null(loc, ctx); } else { auto src = source_location(region(loc)); return err(make_error_info("toml::parse_value: unknown value appeared", std::move(src), "here")); } } case value_t::boolean : {return parse_boolean (loc, ctx);} case value_t::integer : {return parse_integer (loc, ctx);} case value_t::floating : {return parse_floating (loc, ctx);} case value_t::string : {return parse_string (loc, ctx);} case value_t::offset_datetime: {return parse_offset_datetime(loc, ctx);} case value_t::local_datetime : {return parse_local_datetime (loc, ctx);} case value_t::local_date : {return parse_local_date (loc, ctx);} case value_t::local_time : {return parse_local_time (loc, ctx);} case value_t::array : {return parse_array (loc, ctx);} case value_t::table : {return parse_inline_table (loc, ctx);} default: { auto src = source_location(region(loc)); return err(make_error_info("toml::parse_value: unknown value appeared", std::move(src), "here")); } } } /* ============================================================================ * _____ _ _ * |_ _|_ _| |__| |___ * | |/ _` | '_ \ / -_) * |_|\__,_|_.__/_\___| */ template result::key_type>, region>, error_info> parse_table_key(location& loc, context& ctx) { const auto first = loc; const auto& spec = ctx.toml_spec(); auto reg = syntax::std_table(spec).scan(loc); if(!reg.is_ok()) { return err(make_syntax_error("toml::parse_table_key: invalid table key", syntax::std_table(spec), loc)); } loc = first; loc.advance(); // skip [ skip_whitespace(loc, ctx); auto keys_res = parse_key(loc, ctx); if(keys_res.is_err()) { return err(std::move(keys_res.unwrap_err())); } skip_whitespace(loc, ctx); loc.advance(); // ] return ok(std::make_pair(std::move(keys_res.unwrap().first), std::move(reg))); } template result::key_type>, region>, error_info> parse_array_table_key(location& loc, context& ctx) { const auto first = loc; const auto& spec = ctx.toml_spec(); auto reg = syntax::array_table(spec).scan(loc); if(!reg.is_ok()) { return err(make_syntax_error("toml::parse_array_table_key: invalid array-of-tables key", syntax::array_table(spec), loc)); } loc = first; loc.advance(); // [ loc.advance(); // [ skip_whitespace(loc, ctx); auto keys_res = parse_key(loc, ctx); if(keys_res.is_err()) { return err(std::move(keys_res.unwrap_err())); } skip_whitespace(loc, ctx); loc.advance(); // ] loc.advance(); // ] return ok(std::make_pair(std::move(keys_res.unwrap().first), std::move(reg))); } // called after reading [table.keys] and comments around it. // Since table may already contain a subtable ([x.y.z] can be defined before [x]), // the table that is being parsed is passed as an argument. template result parse_table(location& loc, context& ctx, basic_value& table) { assert(table.is_table()); const auto num_errors = ctx.errors().size(); const auto& spec = ctx.toml_spec(); // clear indent info table.as_table_fmt().indent_type = indent_char::none; bool newline_found = true; while( ! loc.eof()) { const auto start = loc; auto sp = skip_multiline_spacer(loc, ctx, newline_found); // if reached to EOF, the table ends here. return. if(loc.eof()) { break; } // if next table is comming, return. if(sequence(syntax::ws(spec), character('[')).scan(loc).is_ok()) { loc = start; break; } // otherwise, it should be a key-value pair. newline_found = newline_found || (sp.has_value() && sp.value().newline_found); if( ! newline_found) { return err(make_error_info("toml::parse_table: " "newline (LF / CRLF) or EOF is expected", source_location(region(loc)), "here")); } if(sp.has_value() && sp.value().indent_type != indent_char::none) { table.as_table_fmt().indent_type = sp.value().indent_type; table.as_table_fmt().body_indent = sp.value().indent; } newline_found = false; // reset if(auto kv_res = parse_key_value_pair(loc, ctx)) { auto keys = std::move(kv_res.unwrap().first.first); auto key_reg = std::move(kv_res.unwrap().first.second); auto val = std::move(kv_res.unwrap().second); if(sp.has_value()) { for(const auto& com : sp.value().comments) { val.comments().push_back(com); } } if(auto com_res = parse_comment_line(loc, ctx)) { if(auto com_opt = com_res.unwrap()) { val.comments().push_back(com_opt.value()); newline_found = true; // comment includes newline at the end } } else { ctx.report_error(std::move(com_res.unwrap_err())); } auto ins_res = insert_value(inserting_value_kind::dotted_keys, std::addressof(table.as_table()), keys, std::move(key_reg), std::move(val)); if(ins_res.is_err()) { ctx.report_error(std::move(ins_res.unwrap_err())); } } else { ctx.report_error(std::move(kv_res.unwrap_err())); skip_key_value_pair(loc, ctx); } } if(num_errors < ctx.errors().size()) { assert(ctx.has_error()); // already reported return err(ctx.pop_last_error()); } return ok(); } template result, std::vector> parse_file(location& loc, context& ctx) { using value_type = basic_value; using table_type = typename value_type::table_type; const auto first = loc; const auto& spec = ctx.toml_spec(); if(loc.eof()) { return ok(value_type(table_type(), table_format_info{}, {}, region(loc))); } value_type root(table_type(), table_format_info{}, {}, region(loc)); root.as_table_fmt().fmt = table_format::multiline; root.as_table_fmt().indent_type = indent_char::none; // parse top comment. // // ```toml // # this is a comment for the top-level table. // // key = "the first value" // ``` // // ```toml // # this is a comment for "the first value". // key = "the first value" // ``` while( ! loc.eof()) { if(auto com_res = parse_comment_line(loc, ctx)) { if(auto com_opt = com_res.unwrap()) { root.comments().push_back(std::move(com_opt.value())); } else // no comment found. { // if it is not an empty line, clear the root comment. if( ! sequence(syntax::ws(spec), syntax::newline(spec)).scan(loc).is_ok()) { loc = first; root.comments().clear(); } break; } } else { ctx.report_error(std::move(com_res.unwrap_err())); skip_comment_block(loc, ctx); } } // parse root table { const auto res = parse_table(loc, ctx, root); if(res.is_err()) { ctx.report_error(std::move(res.unwrap_err())); skip_until_next_table(loc, ctx); } } // parse tables while( ! loc.eof()) { auto sp = skip_multiline_spacer(loc, ctx, /*newline_found=*/true); if(auto key_res = parse_array_table_key(loc, ctx)) { auto key = std::move(std::get<0>(key_res.unwrap())); auto reg = std::move(std::get<1>(key_res.unwrap())); std::vector com; if(sp.has_value()) { for(std::size_t i=0; i(table_type()); auto res = parse_table(loc, ctx, tmp); if(res.is_err()) { ctx.report_error(res.unwrap_err()); skip_until_next_table(loc, ctx); } continue; } auto tab_ptr = inserted.unwrap(); assert(tab_ptr); const auto tab_res = parse_table(loc, ctx, *tab_ptr); if(tab_res.is_err()) { ctx.report_error(tab_res.unwrap_err()); skip_until_next_table(loc, ctx); } // parse_table first clears `indent_type`. // to keep header indent info, we must store it later. if(sp.has_value() && sp.value().indent_type != indent_char::none) { tab_ptr->as_table_fmt().indent_type = sp.value().indent_type; tab_ptr->as_table_fmt().name_indent = sp.value().indent; } continue; } if(auto key_res = parse_table_key(loc, ctx)) { auto key = std::move(std::get<0>(key_res.unwrap())); auto reg = std::move(std::get<1>(key_res.unwrap())); std::vector com; if(sp.has_value()) { for(std::size_t i=0; i(table_type()); auto res = parse_table(loc, ctx, tmp); if(res.is_err()) { ctx.report_error(res.unwrap_err()); skip_until_next_table(loc, ctx); } continue; } auto tab_ptr = inserted.unwrap(); assert(tab_ptr); const auto tab_res = parse_table(loc, ctx, *tab_ptr); if(tab_res.is_err()) { ctx.report_error(tab_res.unwrap_err()); skip_until_next_table(loc, ctx); } if(sp.has_value() && sp.value().indent_type != indent_char::none) { tab_ptr->as_table_fmt().indent_type = sp.value().indent_type; tab_ptr->as_table_fmt().name_indent = sp.value().indent; } continue; } // does not match array_table nor std_table. report an error. const auto keytop = loc; const auto maybe_array_of_tables = literal("[[").scan(loc).is_ok(); loc = keytop; if(maybe_array_of_tables) { ctx.report_error(make_syntax_error("toml::parse_file: invalid array-table key", syntax::array_table(spec), loc)); } else { ctx.report_error(make_syntax_error("toml::parse_file: invalid table key", syntax::std_table(spec), loc)); } skip_until_next_table(loc, ctx); } if( ! ctx.errors().empty()) { return err(std::move(ctx.errors())); } return ok(std::move(root)); } template result, std::vector> parse_impl(std::vector cs, std::string fname, const spec& s) { using value_type = basic_value; using table_type = typename value_type::table_type; // an empty file is a valid toml file. if(cs.empty()) { auto src = std::make_shared>(std::move(cs)); location loc(std::move(src), std::move(fname)); return ok(value_type(table_type(), table_format_info{}, std::vector{}, region(loc))); } // to simplify parser, add newline at the end if there is no LF. // But, if it has raw CR, the file is invalid (in TOML, CR is not a valid // newline char). if it ends with CR, do not add LF and report it. if(cs.back() != '\n' && cs.back() != '\r') { cs.push_back('\n'); } auto src = std::make_shared>(std::move(cs)); location loc(std::move(src), std::move(fname)); // skip BOM if found if(loc.source()->size() >= 3) { auto first = loc.get_location(); const auto c0 = loc.current(); loc.advance(); const auto c1 = loc.current(); loc.advance(); const auto c2 = loc.current(); loc.advance(); const auto bom_found = (c0 == 0xEF) && (c1 == 0xBB) && (c2 == 0xBF); if( ! bom_found) { loc.set_location(first); } } context ctx(s); return parse_file(loc, ctx); } } // detail // ----------------------------------------------------------------------------- // parse(byte array) template result, std::vector> try_parse(std::vector content, std::string filename, spec s = spec::default_version()) { return detail::parse_impl(std::move(content), std::move(filename), std::move(s)); } template basic_value parse(std::vector content, std::string filename, spec s = spec::default_version()) { auto res = try_parse(std::move(content), std::move(filename), std::move(s)); if(res.is_ok()) { return res.unwrap(); } else { std::string msg; for(const auto& err : res.unwrap_err()) { msg += format_error(err); } throw syntax_error(std::move(msg), std::move(res.unwrap_err())); } } // ----------------------------------------------------------------------------- // parse(istream) template result, std::vector> try_parse(std::istream& is, std::string fname = "unknown file", spec s = spec::default_version()) { const auto beg = is.tellg(); is.seekg(0, std::ios::end); const auto end = is.tellg(); const auto fsize = end - beg; is.seekg(beg); // read whole file as a sequence of char assert(fsize >= 0); std::vector letters(static_cast(fsize), '\0'); is.read(reinterpret_cast(letters.data()), fsize); return detail::parse_impl(std::move(letters), std::move(fname), std::move(s)); } template basic_value parse(std::istream& is, std::string fname = "unknown file", spec s = spec::default_version()) { auto res = try_parse(is, std::move(fname), std::move(s)); if(res.is_ok()) { return res.unwrap(); } else { std::string msg; for(const auto& err : res.unwrap_err()) { msg += format_error(err); } throw syntax_error(std::move(msg), std::move(res.unwrap_err())); } } // ----------------------------------------------------------------------------- // parse(filename) template result, std::vector> try_parse(std::string fname, spec s = spec::default_version()) { std::ifstream ifs(fname, std::ios_base::binary); if(!ifs.good()) { std::vector e; e.push_back(error_info("toml::parse: Error opening file \"" + fname + "\"", {})); return err(std::move(e)); } ifs.exceptions(std::ifstream::failbit | std::ifstream::badbit); return try_parse(ifs, std::move(fname), std::move(s)); } template basic_value parse(std::string fname, spec s = spec::default_version()) { std::ifstream ifs(fname, std::ios_base::binary); if(!ifs.good()) { throw file_io_error("toml::parse: error opening file", fname); } ifs.exceptions(std::ifstream::failbit | std::ifstream::badbit); return parse(ifs, std::move(fname), std::move(s)); } template result, std::vector> try_parse(const char (&fname)[N], spec s = spec::default_version()) { return try_parse(std::string(fname), std::move(s)); } template basic_value parse(const char (&fname)[N], spec s = spec::default_version()) { return parse(std::string(fname), std::move(s)); } // ---------------------------------------------------------------------------- // parse_str template result, std::vector> try_parse_str(std::string content, spec s = spec::default_version(), cxx::source_location loc = cxx::source_location::current()) { std::istringstream iss(std::move(content)); std::string name("internal string" + cxx::to_string(loc)); return try_parse(iss, std::move(name), std::move(s)); } template basic_value parse_str(std::string content, spec s = spec::default_version(), cxx::source_location loc = cxx::source_location::current()) { auto res = try_parse_str(std::move(content), std::move(s), std::move(loc)); if(res.is_ok()) { return res.unwrap(); } else { std::string msg; for(const auto& err : res.unwrap_err()) { msg += format_error(err); } throw syntax_error(std::move(msg), std::move(res.unwrap_err())); } } // ---------------------------------------------------------------------------- // filesystem #if defined(TOML11_HAS_FILESYSTEM) template cxx::enable_if_t::value, result, std::vector>> try_parse(const FSPATH& fpath, spec s = spec::default_version()) { std::ifstream ifs(fpath, std::ios_base::binary); if(!ifs.good()) { std::vector e; e.push_back(error_info("toml::parse: Error opening file \"" + fpath.string() + "\"", {})); return err(std::move(e)); } ifs.exceptions(std::ifstream::failbit | std::ifstream::badbit); return try_parse(ifs, fpath.string(), std::move(s)); } template cxx::enable_if_t::value, basic_value> parse(const FSPATH& fpath, spec s = spec::default_version()) { std::ifstream ifs(fpath, std::ios_base::binary); if(!ifs.good()) { throw file_io_error("toml::parse: error opening file", fpath.string()); } ifs.exceptions(std::ifstream::failbit | std::ifstream::badbit); return parse(ifs, fpath.string(), std::move(s)); } #endif // ----------------------------------------------------------------------------- // FILE* template result, std::vector> try_parse(FILE* fp, std::string filename, spec s = spec::default_version()) { const long beg = std::ftell(fp); if (beg == -1L) { return err(std::vector{error_info( std::string("Failed to access: \"") + filename + "\", errno = " + std::to_string(errno), {} )}); } const int res_seekend = std::fseek(fp, 0, SEEK_END); if (res_seekend != 0) { return err(std::vector{error_info( std::string("Failed to seek: \"") + filename + "\", errno = " + std::to_string(errno), {} )}); } const long end = std::ftell(fp); if (end == -1L) { return err(std::vector{error_info( std::string("Failed to access: \"") + filename + "\", errno = " + std::to_string(errno), {} )}); } const auto fsize = end - beg; const auto res_seekbeg = std::fseek(fp, beg, SEEK_SET); if (res_seekbeg != 0) { return err(std::vector{error_info( std::string("Failed to seek: \"") + filename + "\", errno = " + std::to_string(errno), {} )}); } // read whole file as a sequence of char assert(fsize >= 0); std::vector letters(static_cast(fsize)); const auto actual = std::fread(letters.data(), sizeof(char), static_cast(fsize), fp); if(actual != static_cast(fsize)) { return err(std::vector{error_info( std::string("File size changed: \"") + filename + std::string("\" make sure that FILE* is in binary mode " "to avoid LF <-> CRLF conversion"), {} )}); } return detail::parse_impl(std::move(letters), std::move(filename), std::move(s)); } template basic_value parse(FILE* fp, std::string filename, spec s = spec::default_version()) { const long beg = std::ftell(fp); if (beg == -1L) { throw file_io_error(errno, "Failed to access", filename); } const int res_seekend = std::fseek(fp, 0, SEEK_END); if (res_seekend != 0) { throw file_io_error(errno, "Failed to seek", filename); } const long end = std::ftell(fp); if (end == -1L) { throw file_io_error(errno, "Failed to access", filename); } const auto fsize = end - beg; const auto res_seekbeg = std::fseek(fp, beg, SEEK_SET); if (res_seekbeg != 0) { throw file_io_error(errno, "Failed to seek", filename); } // read whole file as a sequence of char assert(fsize >= 0); std::vector letters(static_cast(fsize)); const auto actual = std::fread(letters.data(), sizeof(char), static_cast(fsize), fp); if(actual != static_cast(fsize)) { throw file_io_error(errno, "File size changed; make sure that " "FILE* is in binary mode to avoid LF <-> CRLF conversion", filename); } auto res = detail::parse_impl(std::move(letters), std::move(filename), std::move(s)); if(res.is_ok()) { return res.unwrap(); } else { std::string msg; for(const auto& err : res.unwrap_err()) { msg += format_error(err); } throw syntax_error(std::move(msg), std::move(res.unwrap_err())); } } } // namespace toml #if defined(TOML11_COMPILE_SOURCES) namespace toml { struct type_config; struct ordered_type_config; extern template result, std::vector> try_parse(std::vector, std::string, spec); extern template result, std::vector> try_parse(std::istream&, std::string, spec); extern template result, std::vector> try_parse(std::string, spec); extern template result, std::vector> try_parse(FILE*, std::string, spec); extern template result, std::vector> try_parse_str(std::string, spec, cxx::source_location); extern template basic_value parse(std::vector, std::string, spec); extern template basic_value parse(std::istream&, std::string, spec); extern template basic_value parse(std::string, spec); extern template basic_value parse(FILE*, std::string, spec); extern template basic_value parse_str(std::string, spec, cxx::source_location); extern template result, std::vector> try_parse(std::vector, std::string, spec); extern template result, std::vector> try_parse(std::istream&, std::string, spec); extern template result, std::vector> try_parse(std::string, spec); extern template result, std::vector> try_parse(FILE*, std::string, spec); extern template result, std::vector> try_parse_str(std::string, spec, cxx::source_location); extern template basic_value parse(std::vector, std::string, spec); extern template basic_value parse(std::istream&, std::string, spec); extern template basic_value parse(std::string, spec); extern template basic_value parse(FILE*, std::string, spec); extern template basic_value parse_str(std::string, spec, cxx::source_location); #if defined(TOML11_HAS_FILESYSTEM) extern template cxx::enable_if_t::value, result, std::vector>> try_parse(const std::filesystem::path&, spec); extern template cxx::enable_if_t::value, result, std::vector>> try_parse(const std::filesystem::path&, spec); extern template cxx::enable_if_t::value, basic_value > parse (const std::filesystem::path&, spec); extern template cxx::enable_if_t::value, basic_value > parse (const std::filesystem::path&, spec); #endif // filesystem } // toml #endif // TOML11_COMPILE_SOURCES #endif // TOML11_PARSER_HPP #ifndef TOML11_LITERAL_HPP #define TOML11_LITERAL_HPP #ifndef TOML11_LITERAL_FWD_HPP #define TOML11_LITERAL_FWD_HPP namespace toml { namespace detail { // implementation ::toml::value literal_internal_impl(location loc); } // detail inline namespace literals { inline namespace toml_literals { ::toml::value operator"" _toml(const char* str, std::size_t len); #if defined(TOML11_HAS_CHAR8_T) // value of u8"" literal has been changed from char to char8_t and char8_t is // NOT compatible to char ::toml::value operator"" _toml(const char8_t* str, std::size_t len); #endif } // toml_literals } // literals } // toml #endif // TOML11_LITERAL_FWD_HPP #if ! defined(TOML11_COMPILE_SOURCES) #ifndef TOML11_LITERAL_IMPL_HPP #define TOML11_LITERAL_IMPL_HPP namespace toml { namespace detail { // implementation TOML11_INLINE ::toml::value literal_internal_impl(location loc) { const auto s = ::toml::spec::default_version(); context ctx(s); const auto front = loc; // ------------------------------------------------------------------------ // check if it is a raw value. // skip empty lines and comment lines auto sp = skip_multiline_spacer(loc, ctx); if(loc.eof()) { ::toml::value val; if(sp.has_value()) { for(std::size_t i=0; i(str), reinterpret_cast(str + len), c.begin()); if( ! c.empty() && c.back()) { c.push_back('\n'); // to make it easy to parse comment, we add newline } return literal_internal_impl(::toml::detail::location( std::make_shared(std::move(c)), "TOML literal encoded in a C++ code")); } #if defined(__cpp_char8_t) # if __cpp_char8_t >= 201811L # define TOML11_HAS_CHAR8_T 1 # endif #endif #if defined(TOML11_HAS_CHAR8_T) // value of u8"" literal has been changed from char to char8_t and char8_t is // NOT compatible to char TOML11_INLINE ::toml::value operator"" _toml(const char8_t* str, std::size_t len) { if(len == 0) { return ::toml::value{}; } ::toml::detail::location::container_type c(len); std::copy(reinterpret_cast(str), reinterpret_cast(str + len), c.begin()); if( ! c.empty() && c.back()) { c.push_back('\n'); // to make it easy to parse comment, we add newline } return literal_internal_impl(::toml::detail::location( std::make_shared(std::move(c)), "TOML literal encoded in a C++ code")); } #endif } // toml_literals } // literals } // toml #endif // TOML11_LITERAL_IMPL_HPP #endif #endif // TOML11_LITERAL_HPP #ifndef TOML11_SERIALIZER_HPP #define TOML11_SERIALIZER_HPP #include #include #include #include #include namespace toml { struct serialization_error final : public ::toml::exception { public: explicit serialization_error(std::string what_arg, source_location loc) : what_(std::move(what_arg)), loc_(std::move(loc)) {} ~serialization_error() noexcept override = default; const char* what() const noexcept override {return what_.c_str();} source_location const& location() const noexcept {return loc_;} private: std::string what_; source_location loc_; }; namespace detail { template class serializer { public: using value_type = basic_value; using key_type = typename value_type::key_type ; using comment_type = typename value_type::comment_type ; using boolean_type = typename value_type::boolean_type ; using integer_type = typename value_type::integer_type ; using floating_type = typename value_type::floating_type ; using string_type = typename value_type::string_type ; using local_time_type = typename value_type::local_time_type ; using local_date_type = typename value_type::local_date_type ; using local_datetime_type = typename value_type::local_datetime_type ; using offset_datetime_type = typename value_type::offset_datetime_type; using array_type = typename value_type::array_type ; using table_type = typename value_type::table_type ; using char_type = typename string_type::value_type; public: explicit serializer(const spec& sp) : spec_(sp), force_inline_(false), current_indent_(0) {} string_type operator()(const std::vector& ks, const value_type& v) { for(const auto& k : ks) { this->keys_.push_back(k); } return (*this)(v); } string_type operator()(const key_type& k, const value_type& v) { this->keys_.push_back(k); return (*this)(v); } string_type operator()(const value_type& v) { switch(v.type()) { case value_t::boolean : {return (*this)(v.as_boolean (), v.as_boolean_fmt (), v.location());} case value_t::integer : {return (*this)(v.as_integer (), v.as_integer_fmt (), v.location());} case value_t::floating : {return (*this)(v.as_floating (), v.as_floating_fmt (), v.location());} case value_t::string : {return (*this)(v.as_string (), v.as_string_fmt (), v.location());} case value_t::offset_datetime: {return (*this)(v.as_offset_datetime(), v.as_offset_datetime_fmt(), v.location());} case value_t::local_datetime : {return (*this)(v.as_local_datetime (), v.as_local_datetime_fmt (), v.location());} case value_t::local_date : {return (*this)(v.as_local_date (), v.as_local_date_fmt (), v.location());} case value_t::local_time : {return (*this)(v.as_local_time (), v.as_local_time_fmt (), v.location());} case value_t::array : { return (*this)(v.as_array(), v.as_array_fmt(), v.comments(), v.location()); } case value_t::table : { string_type retval; if(this->keys_.empty()) // it might be the root table. emit comments here. { retval += format_comments(v.comments(), v.as_table_fmt().indent_type); } if( ! retval.empty()) // we have comment. { retval += char_type('\n'); } retval += (*this)(v.as_table(), v.as_table_fmt(), v.comments(), v.location()); return retval; } case value_t::empty: { if(this->spec_.ext_null_value) { return string_conv("null"); } break; } default: { break; } } throw serialization_error(format_error( "[error] toml::serializer: toml::basic_value " "does not have any valid type.", v.location(), "here"), v.location()); } private: string_type operator()(const boolean_type& b, const boolean_format_info&, const source_location&) // {{{ { if(b) { return string_conv("true"); } else { return string_conv("false"); } } // }}} string_type operator()(const integer_type i, const integer_format_info& fmt, const source_location& loc) // {{{ { std::ostringstream oss; this->set_locale(oss); const auto insert_spacer = [&fmt](std::string s) -> std::string { if(fmt.spacer == 0) {return s;} std::string sign; if( ! s.empty() && (s.at(0) == '+' || s.at(0) == '-')) { sign += s.at(0); s.erase(s.begin()); } std::string spaced; std::size_t counter = 0; for(auto iter = s.rbegin(); iter != s.rend(); ++iter) { if(counter != 0 && counter % fmt.spacer == 0) { spaced += '_'; } spaced += *iter; counter += 1; } if(!spaced.empty() && spaced.back() == '_') {spaced.pop_back();} s.clear(); std::copy(spaced.rbegin(), spaced.rend(), std::back_inserter(s)); return sign + s; }; std::string retval; if(fmt.fmt == integer_format::dec) { oss << std::setw(static_cast(fmt.width)) << std::dec << i; retval = insert_spacer(oss.str()); if(this->spec_.ext_num_suffix && ! fmt.suffix.empty()) { retval += '_'; retval += fmt.suffix; } } else { if(i < 0) { throw serialization_error(format_error("binary, octal, hexadecimal " "integer does not allow negative value", loc, "here"), loc); } switch(fmt.fmt) { case integer_format::hex: { oss << std::noshowbase << std::setw(static_cast(fmt.width)) << std::setfill('0') << std::hex; if(fmt.uppercase) { oss << std::uppercase; } else { oss << std::nouppercase; } oss << i; retval = std::string("0x") + insert_spacer(oss.str()); break; } case integer_format::oct: { oss << std::setw(static_cast(fmt.width)) << std::setfill('0') << std::oct << i; retval = std::string("0o") + insert_spacer(oss.str()); break; } case integer_format::bin: { integer_type x{i}; std::string tmp; std::size_t bits(0); while(x != 0) { if(fmt.spacer != 0) { if(bits != 0 && (bits % fmt.spacer) == 0) {tmp += '_';} } if(x % 2 == 1) { tmp += '1'; } else { tmp += '0'; } x >>= 1; bits += 1; } for(; bits < fmt.width; ++bits) { if(fmt.spacer != 0) { if(bits != 0 && (bits % fmt.spacer) == 0) {tmp += '_';} } tmp += '0'; } for(auto iter = tmp.rbegin(); iter != tmp.rend(); ++iter) { oss << *iter; } retval = std::string("0b") + oss.str(); break; } default: { throw serialization_error(format_error( "none of dec, hex, oct, bin: " + to_string(fmt.fmt), loc, "here"), loc); } } } return string_conv(retval); } // }}} string_type operator()(const floating_type f, const floating_format_info& fmt, const source_location&) // {{{ { using std::isnan; using std::isinf; using std::signbit; std::ostringstream oss; this->set_locale(oss); if(isnan(f)) { if(signbit(f)) { oss << '-'; } oss << "nan"; if(this->spec_.ext_num_suffix && ! fmt.suffix.empty()) { oss << '_'; oss << fmt.suffix; } return string_conv(oss.str()); } if(isinf(f)) { if(signbit(f)) { oss << '-'; } oss << "inf"; if(this->spec_.ext_num_suffix && ! fmt.suffix.empty()) { oss << '_'; oss << fmt.suffix; } return string_conv(oss.str()); } switch(fmt.fmt) { case floating_format::defaultfloat: { if(fmt.prec != 0) { oss << std::setprecision(static_cast(fmt.prec)); } oss << f; // since defaultfloat may omit point, we need to add it std::string s = oss.str(); if (s.find('.') == std::string::npos && s.find('e') == std::string::npos && s.find('E') == std::string::npos ) { s += ".0"; } if(this->spec_.ext_num_suffix && ! fmt.suffix.empty()) { s += '_'; s += fmt.suffix; } return string_conv(s); } case floating_format::fixed: { if(fmt.prec != 0) { oss << std::setprecision(static_cast(fmt.prec)); } oss << std::fixed << f; if(this->spec_.ext_num_suffix && ! fmt.suffix.empty()) { oss << '_' << fmt.suffix; } return string_conv(oss.str()); } case floating_format::scientific: { if(fmt.prec != 0) { oss << std::setprecision(static_cast(fmt.prec)); } oss << std::scientific << f; if(this->spec_.ext_num_suffix && ! fmt.suffix.empty()) { oss << '_' << fmt.suffix; } return string_conv(oss.str()); } case floating_format::hex: { if(this->spec_.ext_hex_float) { oss << std::hexfloat << f; // suffix is only for decimal numbers. return string_conv(oss.str()); } else // no hex allowed. output with max precision. { oss << std::setprecision(std::numeric_limits::max_digits10) << std::scientific << f; // suffix is only for decimal numbers. return string_conv(oss.str()); } } default: { if(this->spec_.ext_num_suffix && ! fmt.suffix.empty()) { oss << '_' << fmt.suffix; } return string_conv(oss.str()); } } } // }}} string_type operator()(string_type s, const string_format_info& fmt, const source_location& loc) // {{{ { string_type retval; switch(fmt.fmt) { case string_format::basic: { retval += char_type('"'); retval += this->escape_basic_string(s); retval += char_type('"'); return retval; } case string_format::literal: { if(std::find(s.begin(), s.end(), char_type('\n')) != s.end()) { throw serialization_error(format_error("toml::serializer: " "(non-multiline) literal string cannot have a newline", loc, "here"), loc); } retval += char_type('\''); retval += s; retval += char_type('\''); return retval; } case string_format::multiline_basic: { retval += string_conv("\"\"\""); if(fmt.start_with_newline) { retval += char_type('\n'); } retval += this->escape_ml_basic_string(s); retval += string_conv("\"\"\""); return retval; } case string_format::multiline_literal: { retval += string_conv("'''"); if(fmt.start_with_newline) { retval += char_type('\n'); } retval += s; retval += string_conv("'''"); return retval; } default: { throw serialization_error(format_error( "[error] toml::serializer::operator()(string): " "invalid string_format value", loc, "here"), loc); } } } // }}} string_type operator()(const local_date_type& d, const local_date_format_info&, const source_location&) // {{{ { std::ostringstream oss; oss << d; return string_conv(oss.str()); } // }}} string_type operator()(const local_time_type& t, const local_time_format_info& fmt, const source_location&) // {{{ { return this->format_local_time(t, fmt.has_seconds, fmt.subsecond_precision); } // }}} string_type operator()(const local_datetime_type& dt, const local_datetime_format_info& fmt, const source_location&) // {{{ { std::ostringstream oss; oss << dt.date; switch(fmt.delimiter) { case datetime_delimiter_kind::upper_T: { oss << 'T'; break; } case datetime_delimiter_kind::lower_t: { oss << 't'; break; } case datetime_delimiter_kind::space: { oss << ' '; break; } default: { oss << 'T'; break; } } return string_conv(oss.str()) + this->format_local_time(dt.time, fmt.has_seconds, fmt.subsecond_precision); } // }}} string_type operator()(const offset_datetime_type& odt, const offset_datetime_format_info& fmt, const source_location&) // {{{ { std::ostringstream oss; oss << odt.date; switch(fmt.delimiter) { case datetime_delimiter_kind::upper_T: { oss << 'T'; break; } case datetime_delimiter_kind::lower_t: { oss << 't'; break; } case datetime_delimiter_kind::space: { oss << ' '; break; } default: { oss << 'T'; break; } } oss << string_conv(this->format_local_time(odt.time, fmt.has_seconds, fmt.subsecond_precision)); oss << odt.offset; return string_conv(oss.str()); } // }}} string_type operator()(const array_type& a, const array_format_info& fmt, const comment_type& com, const source_location& loc) // {{{ { array_format f = fmt.fmt; if(fmt.fmt == array_format::default_format) { // [[in.this.form]], you cannot add a comment to the array itself // (but you can add a comment to each table). // To keep comments, we need to avoid multiline array-of-tables // if array itself has a comment. if( ! this->keys_.empty() && ! a.empty() && com.empty() && std::all_of(a.begin(), a.end(), [](const value_type& e) {return e.is_table();})) { f = array_format::array_of_tables; } else { f = array_format::oneline; // check if it becomes long std::size_t approx_len = 0; for(const auto& e : a) { // have a comment. cannot be inlined if( ! e.comments().empty()) { f = array_format::multiline; break; } // possibly long types ... if(e.is_array() || e.is_table() || e.is_offset_datetime() || e.is_local_datetime()) { f = array_format::multiline; break; } else if(e.is_boolean()) { approx_len += (*this)(e.as_boolean(), e.as_boolean_fmt(), e.location()).size(); } else if(e.is_integer()) { approx_len += (*this)(e.as_integer(), e.as_integer_fmt(), e.location()).size(); } else if(e.is_floating()) { approx_len += (*this)(e.as_floating(), e.as_floating_fmt(), e.location()).size(); } else if(e.is_string()) { if(e.as_string_fmt().fmt == string_format::multiline_basic || e.as_string_fmt().fmt == string_format::multiline_literal) { f = array_format::multiline; break; } approx_len += 2 + (*this)(e.as_string(), e.as_string_fmt(), e.location()).size(); } else if(e.is_local_date()) { approx_len += 10; // 1234-56-78 } else if(e.is_local_time()) { approx_len += 15; // 12:34:56.789012 } if(approx_len > 60) // key, ` = `, `[...]` < 80 { f = array_format::multiline; break; } approx_len += 2; // `, ` } } } if(this->force_inline_ && f == array_format::array_of_tables) { f = array_format::multiline; } if(f == array_format::array_of_tables) { if(this->keys_.empty()) { throw serialization_error("array of table must have its key. " "use format(key, v)", loc); } string_type retval; for(const auto& e : a) { assert(e.is_table()); this->current_indent_ += e.as_table_fmt().name_indent; retval += this->format_comments(e.comments(), e.as_table_fmt().indent_type); retval += this->format_indent(e.as_table_fmt().indent_type); this->current_indent_ -= e.as_table_fmt().name_indent; retval += string_conv("[["); retval += this->format_keys(this->keys_).value(); retval += string_conv("]]\n"); retval += this->format_ml_table(e.as_table(), e.as_table_fmt()); } return retval; } else if(f == array_format::oneline) { // ignore comments. we cannot emit comments string_type retval; retval += char_type('['); for(const auto& e : a) { this->force_inline_ = true; retval += (*this)(e); retval += string_conv(", "); } if( ! a.empty()) { retval.pop_back(); // ` ` retval.pop_back(); // `,` } retval += char_type(']'); this->force_inline_ = false; return retval; } else { assert(f == array_format::multiline); string_type retval; retval += string_conv("[\n"); for(const auto& e : a) { this->current_indent_ += fmt.body_indent; retval += this->format_comments(e.comments(), fmt.indent_type); retval += this->format_indent(fmt.indent_type); this->current_indent_ -= fmt.body_indent; this->force_inline_ = true; retval += (*this)(e); retval += string_conv(",\n"); } this->force_inline_ = false; this->current_indent_ += fmt.closing_indent; retval += this->format_indent(fmt.indent_type); this->current_indent_ -= fmt.closing_indent; retval += char_type(']'); return retval; } } // }}} string_type operator()(const table_type& t, const table_format_info& fmt, const comment_type& com, const source_location& loc) // {{{ { if(this->force_inline_) { if(fmt.fmt == table_format::multiline_oneline) { return this->format_ml_inline_table(t, fmt); } else { return this->format_inline_table(t, fmt); } } else { if(fmt.fmt == table_format::multiline) { string_type retval; // comment is emitted inside format_ml_table if(auto k = this->format_keys(this->keys_)) { this->current_indent_ += fmt.name_indent; retval += this->format_comments(com, fmt.indent_type); retval += this->format_indent(fmt.indent_type); this->current_indent_ -= fmt.name_indent; retval += char_type('['); retval += k.value(); retval += string_conv("]\n"); } // otherwise, its the root. retval += this->format_ml_table(t, fmt); return retval; } else if(fmt.fmt == table_format::oneline) { return this->format_inline_table(t, fmt); } else if(fmt.fmt == table_format::multiline_oneline) { return this->format_ml_inline_table(t, fmt); } else if(fmt.fmt == table_format::dotted) { std::vector keys; if(this->keys_.empty()) { throw serialization_error(format_error("toml::serializer: " "dotted table must have its key. use format(key, v)", loc, "here"), loc); } keys.push_back(this->keys_.back()); const auto retval = this->format_dotted_table(t, fmt, loc, keys); keys.pop_back(); return retval; } else { assert(fmt.fmt == table_format::implicit); string_type retval; for(const auto& kv : t) { const auto& k = kv.first; const auto& v = kv.second; if( ! v.is_table() && ! v.is_array_of_tables()) { throw serialization_error(format_error("toml::serializer: " "an implicit table cannot have non-table value.", v.location(), "here"), v.location()); } if(v.is_table()) { if(v.as_table_fmt().fmt != table_format::multiline && v.as_table_fmt().fmt != table_format::implicit) { throw serialization_error(format_error("toml::serializer: " "an implicit table cannot have non-multiline table", v.location(), "here"), v.location()); } } else { assert(v.is_array()); for(const auto& e : v.as_array()) { if(e.as_table_fmt().fmt != table_format::multiline && v.as_table_fmt().fmt != table_format::implicit) { throw serialization_error(format_error("toml::serializer: " "an implicit table cannot have non-multiline table", e.location(), "here"), e.location()); } } } keys_.push_back(k); retval += (*this)(v); keys_.pop_back(); } return retval; } } } // }}} private: string_type escape_basic_string(const string_type& s) const // {{{ { string_type retval; for(const char_type c : s) { switch(c) { case char_type('\\'): {retval += string_conv("\\\\"); break;} case char_type('\"'): {retval += string_conv("\\\""); break;} case char_type('\b'): {retval += string_conv("\\b" ); break;} case char_type('\t'): {retval += string_conv("\\t" ); break;} case char_type('\f'): {retval += string_conv("\\f" ); break;} case char_type('\n'): {retval += string_conv("\\n" ); break;} case char_type('\r'): {retval += string_conv("\\r" ); break;} default : { if(c == char_type(0x1B) && spec_.v1_1_0_add_escape_sequence_e) { retval += string_conv("\\e"); } else if((char_type(0x00) <= c && c <= char_type(0x08)) || (char_type(0x0A) <= c && c <= char_type(0x1F)) || c == char_type(0x7F)) { if(spec_.v1_1_0_add_escape_sequence_x) { retval += string_conv("\\x"); } else { retval += string_conv("\\u00"); } const auto c1 = c / 16; const auto c2 = c % 16; retval += static_cast('0' + c1); if(c2 < 10) { retval += static_cast('0' + c2); } else // 10 <= c2 { retval += static_cast('A' + (c2 - 10)); } } else { retval += c; } } } } return retval; } // }}} string_type escape_ml_basic_string(const string_type& s) // {{{ { string_type retval; for(const char_type c : s) { switch(c) { case char_type('\\'): {retval += string_conv("\\\\"); break;} case char_type('\b'): {retval += string_conv("\\b" ); break;} case char_type('\t'): {retval += string_conv("\\t" ); break;} case char_type('\f'): {retval += string_conv("\\f" ); break;} case char_type('\n'): {retval += string_conv("\n" ); break;} case char_type('\r'): {retval += string_conv("\\r" ); break;} default : { if(c == char_type(0x1B) && spec_.v1_1_0_add_escape_sequence_e) { retval += string_conv("\\e"); } else if((char_type(0x00) <= c && c <= char_type(0x08)) || (char_type(0x0A) <= c && c <= char_type(0x1F)) || c == char_type(0x7F)) { if(spec_.v1_1_0_add_escape_sequence_x) { retval += string_conv("\\x"); } else { retval += string_conv("\\u00"); } const auto c1 = c / 16; const auto c2 = c % 16; retval += static_cast('0' + c1); if(c2 < 10) { retval += static_cast('0' + c2); } else // 10 <= c2 { retval += static_cast('A' + (c2 - 10)); } } else { retval += c; } } } } // Only 1 or 2 consecutive `"`s are allowed in multiline basic string. // 3 consecutive `"`s are considered as a closing delimiter. // We need to check if there are 3 or more consecutive `"`s and insert // backslash to break them down into several short `"`s like the `str6` // in the following example. // ```toml // str4 = """Here are two quotation marks: "". Simple enough.""" // # str5 = """Here are three quotation marks: """.""" # INVALID // str5 = """Here are three quotation marks: ""\".""" // str6 = """Here are fifteen quotation marks: ""\"""\"""\"""\"""\".""" // ``` auto found_3_quotes = retval.find(string_conv("\"\"\"")); while(found_3_quotes != string_type::npos) { retval.replace(found_3_quotes, 3, string_conv("\"\"\\\"")); found_3_quotes = retval.find(string_conv("\"\"\"")); } return retval; } // }}} string_type format_local_time(const local_time_type& t, const bool has_seconds, const std::size_t subsec_prec) // {{{ { std::ostringstream oss; oss << std::setfill('0') << std::setw(2) << static_cast(t.hour); oss << ':'; oss << std::setfill('0') << std::setw(2) << static_cast(t.minute); if(has_seconds) { oss << ':'; oss << std::setfill('0') << std::setw(2) << static_cast(t.second); if(subsec_prec != 0) { std::ostringstream subsec; subsec << std::setfill('0') << std::setw(3) << static_cast(t.millisecond); subsec << std::setfill('0') << std::setw(3) << static_cast(t.microsecond); subsec << std::setfill('0') << std::setw(3) << static_cast(t.nanosecond); std::string subsec_str = subsec.str(); oss << '.' << subsec_str.substr(0, subsec_prec); } } return string_conv(oss.str()); } // }}} string_type format_ml_table(const table_type& t, const table_format_info& fmt) // {{{ { const auto format_later = [](const value_type& v) -> bool { const bool is_ml_table = v.is_table() && v.as_table_fmt().fmt != table_format::oneline && v.as_table_fmt().fmt != table_format::multiline_oneline && v.as_table_fmt().fmt != table_format::dotted ; const bool is_ml_array_table = v.is_array_of_tables() && v.as_array_fmt().fmt != array_format::oneline && v.as_array_fmt().fmt != array_format::multiline; return is_ml_table || is_ml_array_table; }; string_type retval; this->current_indent_ += fmt.body_indent; for(const auto& kv : t) { const auto& key = kv.first; const auto& val = kv.second; if(format_later(val)) { continue; } this->keys_.push_back(key); retval += format_comments(val.comments(), fmt.indent_type); retval += format_indent(fmt.indent_type); if(val.is_table() && val.as_table_fmt().fmt == table_format::dotted) { retval += (*this)(val); } else { retval += format_key(key); retval += string_conv(" = "); retval += (*this)(val); retval += char_type('\n'); } this->keys_.pop_back(); } this->current_indent_ -= fmt.body_indent; if( ! retval.empty()) { retval += char_type('\n'); // for readability, add empty line between tables } for(const auto& kv : t) { if( ! format_later(kv.second)) { continue; } // must be a [multiline.table] or [[multiline.array.of.tables]]. // comments will be generated inside it. this->keys_.push_back(kv.first); retval += (*this)(kv.second); this->keys_.pop_back(); } return retval; } // }}} string_type format_inline_table(const table_type& t, const table_format_info&) // {{{ { // comments are ignored because we cannot write without newline string_type retval; retval += char_type('{'); for(const auto& kv : t) { this->force_inline_ = true; retval += this->format_key(kv.first); retval += string_conv(" = "); retval += (*this)(kv.second); retval += string_conv(", "); } if( ! t.empty()) { retval.pop_back(); // ' ' retval.pop_back(); // ',' } retval += char_type('}'); this->force_inline_ = false; return retval; } // }}} string_type format_ml_inline_table(const table_type& t, const table_format_info& fmt) // {{{ { string_type retval; retval += string_conv("{\n"); this->current_indent_ += fmt.body_indent; for(const auto& kv : t) { this->force_inline_ = true; retval += format_comments(kv.second.comments(), fmt.indent_type); retval += format_indent(fmt.indent_type); retval += kv.first; retval += string_conv(" = "); this->force_inline_ = true; retval += (*this)(kv.second); retval += string_conv(",\n"); } if( ! t.empty()) { retval.pop_back(); // '\n' retval.pop_back(); // ',' } this->current_indent_ -= fmt.body_indent; this->force_inline_ = false; this->current_indent_ += fmt.closing_indent; retval += format_indent(fmt.indent_type); this->current_indent_ -= fmt.closing_indent; retval += char_type('}'); return retval; } // }}} string_type format_dotted_table(const table_type& t, const table_format_info& fmt, // {{{ const source_location&, std::vector& keys) { // lets say we have: `{"a": {"b": {"c": {"d": "foo", "e": "bar"} } }` // and `a` and `b` are `dotted`. // // - in case if `c` is `oneline`: // ```toml // a.b.c = {d = "foo", e = "bar"} // ``` // // - in case if and `c` is `dotted`: // ```toml // a.b.c.d = "foo" // a.b.c.e = "bar" // ``` string_type retval; for(const auto& kv : t) { const auto& key = kv.first; const auto& val = kv.second; keys.push_back(key); // format recursive dotted table? if (val.is_table() && val.as_table_fmt().fmt != table_format::oneline && val.as_table_fmt().fmt != table_format::multiline_oneline) { retval += this->format_dotted_table(val.as_table(), val.as_table_fmt(), val.location(), keys); } else // non-table or inline tables. format normally { retval += format_comments(val.comments(), fmt.indent_type); retval += format_indent(fmt.indent_type); retval += format_keys(keys).value(); retval += string_conv(" = "); this->force_inline_ = true; // sub-table must be inlined retval += (*this)(val); retval += char_type('\n'); this->force_inline_ = false; } keys.pop_back(); } return retval; } // }}} string_type format_key(const key_type& key) // {{{ { if(key.empty()) { return string_conv("\"\""); } // check the key can be a bare (unquoted) key auto loc = detail::make_temporary_location(string_conv(key)); auto reg = detail::syntax::unquoted_key(this->spec_).scan(loc); if(reg.is_ok() && loc.eof()) { return key; } //if it includes special characters, then format it in a "quoted" key. string_type formatted = string_conv("\""); for(const char_type c : key) { switch(c) { case char_type('\\'): {formatted += string_conv("\\\\"); break;} case char_type('\"'): {formatted += string_conv("\\\""); break;} case char_type('\b'): {formatted += string_conv("\\b" ); break;} case char_type('\t'): {formatted += string_conv("\\t" ); break;} case char_type('\f'): {formatted += string_conv("\\f" ); break;} case char_type('\n'): {formatted += string_conv("\\n" ); break;} case char_type('\r'): {formatted += string_conv("\\r" ); break;} default : { // ASCII ctrl char if( (char_type(0x00) <= c && c <= char_type(0x08)) || (char_type(0x0A) <= c && c <= char_type(0x1F)) || c == char_type(0x7F)) { if(spec_.v1_1_0_add_escape_sequence_x) { formatted += string_conv("\\x"); } else { formatted += string_conv("\\u00"); } const auto c1 = c / 16; const auto c2 = c % 16; formatted += static_cast('0' + c1); if(c2 < 10) { formatted += static_cast('0' + c2); } else // 10 <= c2 { formatted += static_cast('A' + (c2 - 10)); } } else { formatted += c; } break; } } } formatted += string_conv("\""); return formatted; } // }}} cxx::optional format_keys(const std::vector& keys) // {{{ { if(keys.empty()) { return cxx::make_nullopt(); } string_type formatted; for(const auto& ky : keys) { formatted += format_key(ky); formatted += char_type('.'); } formatted.pop_back(); // remove the last dot '.' return formatted; } // }}} string_type format_comments(const discard_comments&, const indent_char) const // {{{ { return string_conv(""); } // }}} string_type format_comments(const preserve_comments& comments, const indent_char indent_type) const // {{{ { string_type retval; for(const auto& c : comments) { if(c.empty()) {continue;} retval += format_indent(indent_type); if(c.front() != '#') {retval += char_type('#');} retval += string_conv(c); if(c.back() != '\n') {retval += char_type('\n');} } return retval; } // }}} string_type format_indent(const indent_char indent_type) const // {{{ { const auto indent = static_cast((std::max)(0, this->current_indent_)); if(indent_type == indent_char::space) { return string_conv(make_string(indent, ' ')); } else if(indent_type == indent_char::tab) { return string_conv(make_string(indent, '\t')); } else { return string_type{}; } } // }}} std::locale set_locale(std::ostream& os) const { return os.imbue(std::locale::classic()); } private: spec spec_; bool force_inline_; // table inside an array without fmt specification std::int32_t current_indent_; std::vector keys_; }; } // detail template typename basic_value::string_type format(const basic_value& v, const spec s = spec::default_version()) { detail::serializer ser(s); return ser(v); } template typename basic_value::string_type format(const typename basic_value::key_type& k, const basic_value& v, const spec s = spec::default_version()) { detail::serializer ser(s); return ser(k, v); } template typename basic_value::string_type format(const std::vector::key_type>& ks, const basic_value& v, const spec s = spec::default_version()) { detail::serializer ser(s); return ser(ks, v); } template std::ostream& operator<<(std::ostream& os, const basic_value& v) { os << format(v); return os; } } // toml #if defined(TOML11_COMPILE_SOURCES) namespace toml { struct type_config; struct ordered_type_config; extern template typename basic_value::string_type format(const basic_value&, const spec); extern template typename basic_value::string_type format(const typename basic_value::key_type& k, const basic_value& v, const spec); extern template typename basic_value::string_type format(const std::vector::key_type>& ks, const basic_value& v, const spec s); extern template typename basic_value::string_type format(const basic_value&, const spec); extern template typename basic_value::string_type format(const typename basic_value::key_type& k, const basic_value& v, const spec); extern template typename basic_value::string_type format(const std::vector::key_type>& ks, const basic_value& v, const spec s); namespace detail { extern template class serializer<::toml::type_config>; extern template class serializer<::toml::ordered_type_config>; } // detail } // toml #endif // TOML11_COMPILE_SOURCES #endif // TOML11_SERIALIZER_HPP #ifndef TOML11_TOML_HPP #define TOML11_TOML_HPP // The MIT License (MIT) // // Copyright (c) 2017-now Toru Niina // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. // IWYU pragma: begin_exports // IWYU pragma: end_exports #endif// TOML11_TOML_HPP toml11-4.1.0/src/000077500000000000000000000000001464712047700134345ustar00rootroot00000000000000toml11-4.1.0/src/CMakeLists.txt000066400000000000000000000177241464712047700162070ustar00rootroot00000000000000set(TOML11_FWD_HEADERS ${PROJECT_SOURCE_DIR}/include/toml11/fwd/color_fwd.hpp ${PROJECT_SOURCE_DIR}/include/toml11/fwd/comments_fwd.hpp ${PROJECT_SOURCE_DIR}/include/toml11/fwd/datetime_fwd.hpp ${PROJECT_SOURCE_DIR}/include/toml11/fwd/error_info_fwd.hpp ${PROJECT_SOURCE_DIR}/include/toml11/fwd/format_fwd.hpp ${PROJECT_SOURCE_DIR}/include/toml11/fwd/literal_fwd.hpp ${PROJECT_SOURCE_DIR}/include/toml11/fwd/location_fwd.hpp ${PROJECT_SOURCE_DIR}/include/toml11/fwd/region_fwd.hpp ${PROJECT_SOURCE_DIR}/include/toml11/fwd/scanner_fwd.hpp ${PROJECT_SOURCE_DIR}/include/toml11/fwd/source_location_fwd.hpp ${PROJECT_SOURCE_DIR}/include/toml11/fwd/syntax_fwd.hpp ${PROJECT_SOURCE_DIR}/include/toml11/fwd/value_t_fwd.hpp ) set(TOML11_IMPL_HEADERS ${PROJECT_SOURCE_DIR}/include/toml11/impl/color_impl.hpp ${PROJECT_SOURCE_DIR}/include/toml11/impl/comments_impl.hpp ${PROJECT_SOURCE_DIR}/include/toml11/impl/datetime_impl.hpp ${PROJECT_SOURCE_DIR}/include/toml11/impl/error_info_impl.hpp ${PROJECT_SOURCE_DIR}/include/toml11/impl/format_impl.hpp ${PROJECT_SOURCE_DIR}/include/toml11/impl/literal_impl.hpp ${PROJECT_SOURCE_DIR}/include/toml11/impl/location_impl.hpp ${PROJECT_SOURCE_DIR}/include/toml11/impl/region_impl.hpp ${PROJECT_SOURCE_DIR}/include/toml11/impl/scanner_impl.hpp ${PROJECT_SOURCE_DIR}/include/toml11/impl/source_location_impl.hpp ${PROJECT_SOURCE_DIR}/include/toml11/impl/syntax_impl.hpp ${PROJECT_SOURCE_DIR}/include/toml11/impl/value_t_impl.hpp ) set(TOML11_MAIN_HEADERS ${PROJECT_SOURCE_DIR}/include/toml11/color.hpp ${PROJECT_SOURCE_DIR}/include/toml11/comments.hpp ${PROJECT_SOURCE_DIR}/include/toml11/compat.hpp ${PROJECT_SOURCE_DIR}/include/toml11/context.hpp ${PROJECT_SOURCE_DIR}/include/toml11/conversion.hpp ${PROJECT_SOURCE_DIR}/include/toml11/datetime.hpp ${PROJECT_SOURCE_DIR}/include/toml11/error_info.hpp ${PROJECT_SOURCE_DIR}/include/toml11/exception.hpp ${PROJECT_SOURCE_DIR}/include/toml11/find.hpp ${PROJECT_SOURCE_DIR}/include/toml11/format.hpp ${PROJECT_SOURCE_DIR}/include/toml11/from.hpp ${PROJECT_SOURCE_DIR}/include/toml11/get.hpp ${PROJECT_SOURCE_DIR}/include/toml11/into.hpp ${PROJECT_SOURCE_DIR}/include/toml11/literal.hpp ${PROJECT_SOURCE_DIR}/include/toml11/location.hpp ${PROJECT_SOURCE_DIR}/include/toml11/ordered_map.hpp ${PROJECT_SOURCE_DIR}/include/toml11/parser.hpp ${PROJECT_SOURCE_DIR}/include/toml11/region.hpp ${PROJECT_SOURCE_DIR}/include/toml11/result.hpp ${PROJECT_SOURCE_DIR}/include/toml11/scanner.hpp ${PROJECT_SOURCE_DIR}/include/toml11/serializer.hpp ${PROJECT_SOURCE_DIR}/include/toml11/skip.hpp ${PROJECT_SOURCE_DIR}/include/toml11/source_location.hpp ${PROJECT_SOURCE_DIR}/include/toml11/spec.hpp ${PROJECT_SOURCE_DIR}/include/toml11/storage.hpp ${PROJECT_SOURCE_DIR}/include/toml11/syntax.hpp ${PROJECT_SOURCE_DIR}/include/toml11/traits.hpp ${PROJECT_SOURCE_DIR}/include/toml11/types.hpp ${PROJECT_SOURCE_DIR}/include/toml11/utility.hpp ${PROJECT_SOURCE_DIR}/include/toml11/value.hpp ${PROJECT_SOURCE_DIR}/include/toml11/value_t.hpp ${PROJECT_SOURCE_DIR}/include/toml11/version.hpp ${PROJECT_SOURCE_DIR}/include/toml11/visit.hpp ) set(TOML11_ROOT_HEADER ${PROJECT_SOURCE_DIR}/include/toml.hpp ) if(TOML11_PRECOMPILE) add_library(toml11 ${TOML11_FWD_HEADERS} ${TOML11_IMPL_HEADERS} ${TOML11_MAIN_HEADERS} ${TOML11_ROOT_HEADER} color.cpp context.cpp comments.cpp datetime.cpp error_info.cpp format.cpp literal.cpp location.cpp parser.cpp region.cpp scanner.cpp serializer.cpp skip.cpp source_location.cpp syntax.cpp types.cpp value_t.cpp ) target_compile_definitions(toml11 PUBLIC -DTOML11_COMPILE_SOURCES) target_include_directories(toml11 PUBLIC $ $ ) # required options to use toml11 with MSVC if(MSVC) target_compile_options(toml11 PUBLIC "/utf-8") target_compile_options(toml11 PRIVATE "/W4" "/WX") if(MSVC_VERSION LESS 1910) message(STATUS "MSVC < 1910. DEFINE_CONVERSION_NON_INTRUSIVE is disabled") target_compile_definitions(toml11 PUBLIC -DTOML11_WITHOUT_DEFINE_NON_INTRUSIVE) elseif(MSVC_VERSION LESS 1920) # MSVC 2017 target_compile_options(toml11 PUBLIC "/experimental:preprocessor") else() # MSVC 2019 target_compile_options(toml11 PUBLIC "/Zc:preprocessor") endif() else() target_compile_options(toml11 PRIVATE $<$: -Wall > $<$: -Wextra > $<$: -Wpedantic > $<$: -Werror > $<$: -Wsign-conversion > $<$: -Wconversion > $<$: -Wduplicated-cond > $<$: -Wduplicated-branches> $<$: -Wlogical-op > $<$: -Wdouble-promotion > $<$: -Wrange-loop-analysis> $<$: -Wundef > $<$: -Wshadow > ) endif() else() add_library(toml11 INTERFACE) target_include_directories(toml11 INTERFACE $ $ ) # required options to use toml11 with MSVC if(MSVC) target_compile_options(toml11 INTERFACE "/utf-8") if(MSVC_VERSION LESS 1910) message(STATUS "MSVC < 1910. DEFINE_CONVERSION_NON_INTRUSIVE is disabled") target_compile_definitions(toml11 PUBLIC -DTOML11_WITHOUT_DEFINE_NON_INTRUSIVE) elseif(MSVC_VERSION LESS 1920) # MSVC 2017 target_compile_options(toml11 INTERFACE "/experimental:preprocessor") else() # MSVC 2019 target_compile_options(toml11 INTERFACE "/Zc:preprocessor") endif() endif() endif() if(TOML11_INSTALL) include(CMakePackageConfigHelpers) write_basic_package_version_file(${TOML11_CONFIG_VERSION} VERSION ${toml11_VERSION} COMPATIBILITY SameMajorVersion ) configure_package_config_file( ${PROJECT_SOURCE_DIR}/cmake/toml11Config.cmake.in ${TOML11_CONFIG} INSTALL_DESTINATION ${TOML11_INSTALL_CMAKE_DIR} PATH_VARS TOML11_INSTALL_CMAKE_DIR TOML11_INSTALL_INCLUDE_DIR ) install(FILES ${TOML11_CONFIG} ${TOML11_CONFIG_VERSION} DESTINATION ${TOML11_INSTALL_CMAKE_DIR}) install(FILES ${TOML11_ROOT_HEADER} DESTINATION ${TOML11_INSTALL_INCLUDE_DIR} ) install(FILES ${TOML11_MAIN_HEADERS} DESTINATION ${TOML11_INSTALL_INCLUDE_DIR}/toml11 ) install(FILES ${TOML11_FWD_HEADERS} DESTINATION ${TOML11_INSTALL_INCLUDE_DIR}/toml11/fwd ) install(FILES ${TOML11_IMPL_HEADERS} DESTINATION ${TOML11_INSTALL_INCLUDE_DIR}/toml11/impl ) install(TARGETS toml11 EXPORT toml11Targets) install(EXPORT toml11Targets FILE toml11Targets.cmake DESTINATION ${TOML11_INSTALL_CMAKE_DIR} NAMESPACE toml11:: ) endif() add_library(toml11::toml11 ALIAS toml11) toml11-4.1.0/src/color.cpp000066400000000000000000000002331464712047700152540ustar00rootroot00000000000000#include #if ! defined(TOML11_COMPILE_SOURCES) #error "Define `TOML11_COMPILE_SOURCES` before compiling source code!" #endif toml11-4.1.0/src/comments.cpp000066400000000000000000000002361464712047700157660ustar00rootroot00000000000000#include #if ! defined(TOML11_COMPILE_SOURCES) #error "Define `TOML11_COMPILE_SOURCES` before compiling source code!" #endif toml11-4.1.0/src/context.cpp000066400000000000000000000005141464712047700156240ustar00rootroot00000000000000#include #include #if ! defined(TOML11_COMPILE_SOURCES) #error "Define `TOML11_COMPILE_SOURCES` before compiling source code!" #endif namespace toml { namespace detail { template class context<::toml::type_config>; template class context<::toml::ordered_type_config>; } // detail } // toml toml11-4.1.0/src/datetime.cpp000066400000000000000000000002361464712047700157350ustar00rootroot00000000000000#include #if ! defined(TOML11_COMPILE_SOURCES) #error "Define `TOML11_COMPILE_SOURCES` before compiling source code!" #endif toml11-4.1.0/src/error_info.cpp000066400000000000000000000002401464712047700163000ustar00rootroot00000000000000#include #if ! defined(TOML11_COMPILE_SOURCES) #error "Define `TOML11_COMPILE_SOURCES` before compiling source code!" #endif toml11-4.1.0/src/format.cpp000066400000000000000000000002341464712047700154270ustar00rootroot00000000000000#include #if ! defined(TOML11_COMPILE_SOURCES) #error "Define `TOML11_COMPILE_SOURCES` before compiling source code!" #endif toml11-4.1.0/src/literal.cpp000066400000000000000000000002351464712047700155740ustar00rootroot00000000000000#include #if ! defined(TOML11_COMPILE_SOURCES) #error "Define `TOML11_COMPILE_SOURCES` before compiling source code!" #endif toml11-4.1.0/src/location.cpp000066400000000000000000000002361464712047700157510ustar00rootroot00000000000000#include #if ! defined(TOML11_COMPILE_SOURCES) #error "Define `TOML11_COMPILE_SOURCES` before compiling source code!" #endif toml11-4.1.0/src/parser.cpp000066400000000000000000000067521464712047700154460ustar00rootroot00000000000000#include #include #if ! defined(TOML11_COMPILE_SOURCES) #error "Define `TOML11_COMPILE_SOURCES` before compiling source code!" #endif namespace toml { template result, std::vector> try_parse(std::vector, std::string, spec); template result, std::vector> try_parse(std::istream&, std::string, spec); template result, std::vector> try_parse(std::string, spec); template result, std::vector> try_parse(FILE*, std::string, spec); template result, std::vector> try_parse_str(std::string, spec, cxx::source_location); template basic_value parse(std::vector, std::string, spec); template basic_value parse(std::istream&, std::string, spec); template basic_value parse(std::string, spec); template basic_value parse(FILE*, std::string, spec); template basic_value parse_str(std::string, spec, cxx::source_location); template result, std::vector> try_parse(std::vector, std::string, spec); template result, std::vector> try_parse(std::istream&, std::string, spec); template result, std::vector> try_parse(std::string, spec); template result, std::vector> try_parse(FILE*, std::string, spec); template result, std::vector> try_parse_str(std::string, spec, cxx::source_location); template basic_value parse(std::vector, std::string, spec); template basic_value parse(std::istream&, std::string, spec); template basic_value parse(std::string, spec); template basic_value parse(FILE*, std::string, spec); template basic_value parse_str(std::string, spec, cxx::source_location); #if defined(TOML11_HAS_FILESYSTEM) template cxx::enable_if_t::value, result, std::vector>> try_parse(const std::filesystem::path&, spec); template cxx::enable_if_t::value, result, std::vector>> try_parse(const std::filesystem::path&, spec); template cxx::enable_if_t::value, basic_value > parse (const std::filesystem::path&, spec); template cxx::enable_if_t::value, basic_value > parse (const std::filesystem::path&, spec); #endif // filesystem } // toml toml11-4.1.0/src/region.cpp000066400000000000000000000002341464712047700154220ustar00rootroot00000000000000#include #if ! defined(TOML11_COMPILE_SOURCES) #error "Define `TOML11_COMPILE_SOURCES` before compiling source code!" #endif toml11-4.1.0/src/scanner.cpp000066400000000000000000000002351464712047700155710ustar00rootroot00000000000000#include #if ! defined(TOML11_COMPILE_SOURCES) #error "Define `TOML11_COMPILE_SOURCES` before compiling source code!" #endif toml11-4.1.0/src/serializer.cpp000066400000000000000000000030101464712047700163030ustar00rootroot00000000000000#include #include #if ! defined(TOML11_COMPILE_SOURCES) #error "Define `TOML11_COMPILE_SOURCES` before compiling source code!" #endif namespace toml { struct type_config; struct ordered_type_config; template typename basic_value::string_type format(const basic_value&, const spec); template typename basic_value::string_type format(const typename basic_value::key_type& k, const basic_value& v, const spec); template typename basic_value::string_type format(const std::vector::key_type>& ks, const basic_value& v, const spec s); template typename basic_value::string_type format(const basic_value&, const spec); template typename basic_value::string_type format(const typename basic_value::key_type& k, const basic_value& v, const spec); template typename basic_value::string_type format(const std::vector::key_type>& ks, const basic_value& v, const spec s); namespace detail { template class serializer<::toml::type_config>; template class serializer<::toml::ordered_type_config>; } // detail } // toml toml11-4.1.0/src/skip.cpp000066400000000000000000000045671464712047700151220ustar00rootroot00000000000000#include #include #include #if ! defined(TOML11_COMPILE_SOURCES) #error "Define `TOML11_COMPILE_SOURCES` before compiling source code!" #endif namespace toml { namespace detail { template bool skip_whitespace (location& loc, const context&); template bool skip_empty_lines (location& loc, const context&); template void skip_comment_block (location& loc, const context&); template void skip_empty_or_comment_lines(location& loc, const context&); template void skip_string_like (location& loc, const context&); template void skip_array_like (location& loc, const context&); template void skip_inline_table_like (location& loc, const context&); template void skip_value (location& loc, const context&); template void skip_key_value_pair (location& loc, const context&); template void skip_until_next_table (location& loc, const context&); template bool skip_whitespace (location& loc, const context&); template bool skip_empty_lines (location& loc, const context&); template void skip_comment_block (location& loc, const context&); template void skip_empty_or_comment_lines(location& loc, const context&); template void skip_string_like (location& loc, const context&); template void skip_array_like (location& loc, const context&); template void skip_inline_table_like (location& loc, const context&); template void skip_value (location& loc, const context&); template void skip_key_value_pair (location& loc, const context&); template void skip_until_next_table (location& loc, const context&); } // detail } // toml toml11-4.1.0/src/source_location.cpp000066400000000000000000000002451464712047700173310ustar00rootroot00000000000000#include #if ! defined(TOML11_COMPILE_SOURCES) #error "Define `TOML11_COMPILE_SOURCES` before compiling source code!" #endif toml11-4.1.0/src/syntax.cpp000066400000000000000000000002341464712047700154650ustar00rootroot00000000000000#include #if ! defined(TOML11_COMPILE_SOURCES) #error "Define `TOML11_COMPILE_SOURCES` before compiling source code!" #endif toml11-4.1.0/src/types.cpp000066400000000000000000000004431464712047700153050ustar00rootroot00000000000000#include #include #if ! defined(TOML11_COMPILE_SOURCES) #error "Define `TOML11_COMPILE_SOURCES` before compiling source code!" #endif namespace toml { template class basic_value; template class basic_value; } // toml toml11-4.1.0/src/value_t.cpp000066400000000000000000000002351464712047700155770ustar00rootroot00000000000000#include #if ! defined(TOML11_COMPILE_SOURCES) #error "Define `TOML11_COMPILE_SOURCES` before compiling source code!" #endif toml11-4.1.0/tests/000077500000000000000000000000001464712047700140075ustar00rootroot00000000000000toml11-4.1.0/tests/CMakeLists.txt000066400000000000000000000224371464712047700165570ustar00rootroot00000000000000set(TOML11_TEST_NAMES test_comments test_datetime test_error_message test_find test_find_or test_format_integer test_format_floating test_format_table test_get test_get_or test_location test_literal test_parse_null test_parse_boolean test_parse_integer test_parse_floating test_parse_string test_parse_datetime test_parse_array test_parse_inline_table test_parse_table_keys test_parse_table test_result test_scanner test_serialize test_syntax_boolean test_syntax_integer test_syntax_floating test_syntax_string test_syntax_datetime test_syntax_key test_syntax_comment test_spec test_storage test_traits test_types test_utility test_user_defined_conversion test_value test_visit ) if(BUILD_TESTING) add_library(toml11_test_utility STATIC utility.cpp) target_include_directories(toml11_test_utility PRIVATE ${PROJECT_SOURCE_DIR}/tests/extlib/doctest/doctest/ ) target_link_libraries(toml11_test_utility PUBLIC toml11) foreach(TEST_NAME ${TOML11_TEST_NAMES}) add_executable(${TEST_NAME} ${TEST_NAME}.cpp) target_include_directories(${TEST_NAME} PRIVATE ${PROJECT_SOURCE_DIR}/tests/extlib/doctest/doctest/ ) target_link_libraries(${TEST_NAME} PUBLIC toml11 toml11_test_utility) if(MSVC) target_compile_options(${TEST_NAME} PRIVATE /W4 /WX) else() target_compile_options(${TEST_NAME} PRIVATE $<$: -Wall > $<$: -Wextra > $<$: -Wpedantic > $<$: -Werror > $<$: -Wsign-conversion > $<$: -Wconversion > $<$: -Wduplicated-cond > $<$: -Wduplicated-branches> $<$: -Wlogical-op > $<$: -Wdouble-promotion > $<$: -Wrange-loop-analysis> $<$: -Wundef > $<$: -Wshadow > $<$: -fsanitize=address > $<$: -fsanitize=undefined > ) target_link_options(${TEST_NAME} PRIVATE $<$: -fsanitize=address > $<$: -fsanitize=undefined > ) endif() add_test(NAME ${TEST_NAME} COMMAND ${TEST_NAME}) endforeach(TEST_NAME) endif(BUILD_TESTING) # ============================================================================= # toml-test encoder/decoder if(TOML11_BUILD_TOML_TESTS) add_executable(toml11_decoder to_json.cpp) target_include_directories(toml11_decoder PRIVATE ${PROJECT_SOURCE_DIR}/tests/extlib/doctest/doctest/ PRIVATE ${PROJECT_SOURCE_DIR}/tests/extlib/json/include/ ) target_link_libraries(toml11_decoder PRIVATE toml11) if(MSVC) target_compile_options(${TEST_NAME} PRIVATE /W4 /WX) else() target_compile_options(toml11_decoder PRIVATE $<$: -Wall > $<$: -Wextra > $<$: -Wpedantic > $<$: -Wsign-conversion > $<$: -Wconversion > $<$: -Wduplicated-cond > $<$: -Wduplicated-branches> $<$: -Wlogical-op > $<$: -Wdouble-promotion > $<$: -Wrange-loop-analysis> $<$: -Wundef > $<$: -Wshadow > $<$: -fsanitize=address > $<$: -fsanitize=undefined > ) target_link_options(toml11_decoder PRIVATE $<$: -fsanitize=address > $<$: -fsanitize=undefined > ) endif() add_executable(toml11_decoder_v1_1_0 to_json.cpp) target_include_directories(toml11_decoder_v1_1_0 PRIVATE ${PROJECT_SOURCE_DIR}/tests/extlib/doctest/doctest/ PRIVATE ${PROJECT_SOURCE_DIR}/tests/extlib/json/include/ ) target_compile_definitions(toml11_decoder_v1_1_0 PRIVATE -DTOML11_TO_JSON_USE_V1_1_0) target_link_libraries(toml11_decoder_v1_1_0 PRIVATE toml11) if(MSVC) target_compile_options(${TEST_NAME} PRIVATE /W4 /WX) else() target_compile_options(toml11_decoder_v1_1_0 PRIVATE $<$: -Wall > $<$: -Wextra > $<$: -Wpedantic > $<$: -Wsign-conversion > $<$: -Wconversion > $<$: -Wduplicated-cond > $<$: -Wduplicated-branches> $<$: -Wlogical-op > $<$: -Wdouble-promotion > $<$: -Wrange-loop-analysis> $<$: -Wundef > $<$: -Wshadow > $<$: -fsanitize=address > $<$: -fsanitize=undefined > ) target_link_options(toml11_decoder_v1_1_0 PRIVATE $<$: -fsanitize=address > $<$: -fsanitize=undefined > ) endif() add_executable(toml11_encoder to_toml.cpp) target_include_directories(toml11_encoder PRIVATE ${PROJECT_SOURCE_DIR}/tests/extlib/doctest/doctest/ PRIVATE ${PROJECT_SOURCE_DIR}/tests/extlib/json/include/ ) target_link_libraries(toml11_encoder PRIVATE toml11) if(MSVC) target_compile_options(${TEST_NAME} PRIVATE /W4 /WX) else() target_compile_options(toml11_encoder PRIVATE $<$: -Wall > $<$: -Wextra > $<$: -Wpedantic > $<$: -Wsign-conversion > $<$: -Wconversion > $<$: -Wduplicated-cond > $<$: -Wduplicated-branches> $<$: -Wlogical-op > $<$: -Wdouble-promotion > $<$: -Wrange-loop-analysis> $<$: -Wundef > $<$: -Wshadow > $<$: -fsanitize=address > $<$: -fsanitize=undefined > ) target_link_options(toml11_encoder PRIVATE $<$: -fsanitize=address > $<$: -fsanitize=undefined > ) endif() endif(TOML11_BUILD_TOML_TESTS) toml11-4.1.0/tests/extlib/000077500000000000000000000000001464712047700152765ustar00rootroot00000000000000toml11-4.1.0/tests/extlib/doctest/000077500000000000000000000000001464712047700167435ustar00rootroot00000000000000toml11-4.1.0/tests/extlib/json/000077500000000000000000000000001464712047700162475ustar00rootroot00000000000000toml11-4.1.0/tests/test_comments.cpp000066400000000000000000000007711464712047700174040ustar00rootroot00000000000000#define DOCTEST_CONFIG_IMPLEMENT_WITH_MAIN #include "doctest.h" #include TEST_CASE("testing comments on simple value") { const toml::value root = toml::parse_str(R"( # comment 1 # comment 2 a = "foo" # comment 3 # comment 4 )"); const auto& a = root.at("a"); CHECK_EQ(a.comments().size(), 3); CHECK_EQ(a.comments().at(0), "# comment 1"); CHECK_EQ(a.comments().at(1), "# comment 2"); CHECK_EQ(a.comments().at(2), "# comment 3"); } toml11-4.1.0/tests/test_datetime.cpp000066400000000000000000000063061464712047700173530ustar00rootroot00000000000000#define DOCTEST_CONFIG_IMPLEMENT_WITH_MAIN #include "doctest.h" #include TEST_CASE("testing local date") { const toml::local_date date(2018, toml::month_t::Jan, 1); const toml::local_date date1(date); CHECK(date == date1); const std::chrono::system_clock::time_point tp(date); const toml::local_date date2(tp); CHECK(date == date2); const toml::local_date date3(2017, toml::month_t::Dec, 31); CHECK(date > date3); std::ostringstream oss; oss << date; CHECK(oss.str() == std::string("2018-01-01")); } TEST_CASE("testing local time") { const toml::local_time time(12, 30, 45); const toml::local_time time1(time); CHECK(time == time1); const std::chrono::nanoseconds dur(time); std::chrono::nanoseconds ns(0); ns += std::chrono::hours (12); ns += std::chrono::minutes(30); ns += std::chrono::seconds(45); CHECK(dur.count() == ns.count()); const toml::local_time time3(12, 15, 45); CHECK(time > time3); { std::ostringstream oss; oss << time; CHECK(oss.str() == std::string("12:30:45")); } { const toml::local_time time4(12, 30, 45, 123, 456); std::ostringstream oss; oss << time4; CHECK(oss.str() == std::string("12:30:45.123456")); } } TEST_CASE("testing time offset") { const toml::time_offset time(9, 30); const toml::time_offset time1(time); CHECK(time == time1); const std::chrono::minutes dur(time); std::chrono::minutes m(0); m += std::chrono::hours (9); m += std::chrono::minutes(30); CHECK(dur.count() == m.count()); const toml::time_offset time2(9, 0); CHECK(time2 < time); std::ostringstream oss; oss << time; CHECK(oss.str() == std::string("+09:30")); } TEST_CASE("testing local datetime") { const toml::local_datetime dt(toml::local_date(2018, toml::month_t::Jan, 1), toml::local_time(12, 30, 45)); const toml::local_datetime dt1(dt); CHECK(dt == dt1); const std::chrono::system_clock::time_point tp(dt); const toml::local_datetime dt2(tp); CHECK(dt == dt2); std::ostringstream oss; oss << dt; CHECK(oss.str() == std::string("2018-01-01T12:30:45")); } TEST_CASE("testing offset datetime") { const toml::offset_datetime dt(toml::local_date(2018, toml::month_t::Jan, 1), toml::local_time(12, 30, 45), toml::time_offset(9, 30)); const toml::offset_datetime dt1(dt); CHECK(dt == dt1); const std::chrono::system_clock::time_point tp1(dt); const toml::offset_datetime dt2(tp1); const std::chrono::system_clock::time_point tp2(dt2); const bool tp_same = (tp1 == tp2); CHECK(tp_same); { std::ostringstream oss; oss << dt; CHECK(oss.str() == std::string("2018-01-01T12:30:45+09:30")); } { const toml::offset_datetime dt3( toml::local_date(2018, toml::month_t::Jan, 1), toml::local_time(12, 30, 45), toml::time_offset(0, 0)); std::ostringstream oss; oss << dt3; CHECK(oss.str() == std::string("2018-01-01T12:30:45Z")); } } toml11-4.1.0/tests/test_error_message.cpp000066400000000000000000000053551464712047700204170ustar00rootroot00000000000000#define DOCTEST_CONFIG_IMPLEMENT_WITH_MAIN #include "doctest.h" #include TEST_CASE("testing custom error message using source_location") { const toml::value root = toml::parse_str(R"( range = [0, 42] val = 54 )"); const auto& lower = root.at("range").at(0); const auto& upper = root.at("range").at(1); const auto& val = root.at("val"); const auto err = toml::make_error_info("val not in range", lower.location(), "lower limit is defined here", upper.location(), "upper limit is defined here", val.location(), "this is not in the range", "Hint: upper limit is inclusive" ); CHECK_EQ(err.title(), "val not in range"); CHECK_EQ(err.locations().size(), 3); CHECK_EQ(err.locations().at(0).second, "lower limit is defined here"); CHECK_EQ(err.locations().at(1).second, "upper limit is defined here"); CHECK_EQ(err.locations().at(2).second, "this is not in the range" ); } TEST_CASE("testing custom error message using value") { const toml::value root = toml::parse_str(R"( range = [0, 42] val = 54 )"); const auto& lower = root.at("range").at(0); const auto& upper = root.at("range").at(1); const auto& val = root.at("val"); const auto err = toml::make_error_info("val not in range", lower, "lower limit is defined here", upper, "upper limit is defined here", val, "this is not in the range", "Hint: upper limit is inclusive" ); CHECK_EQ(err.title(), "val not in range"); CHECK_EQ(err.locations().size(), 3); CHECK_EQ(err.locations().at(0).second, "lower limit is defined here"); CHECK_EQ(err.locations().at(1).second, "upper limit is defined here"); CHECK_EQ(err.locations().at(2).second, "this is not in the range" ); } TEST_CASE("testing custom error message using source_location and value") { const toml::value root = toml::parse_str(R"( range = [0, 42] val = 54 )"); const auto& lower = root.at("range").at(0); const auto& upper = root.at("range").at(1); const auto& val = root.at("val"); const auto err = toml::make_error_info("val not in range", lower, "lower limit is defined here", upper, "upper limit is defined here", val.location(), "this is not in the range", "Hint: upper limit is inclusive" ); CHECK_EQ(err.title(), "val not in range"); CHECK_EQ(err.locations().size(), 3); CHECK_EQ(err.locations().at(0).second, "lower limit is defined here"); CHECK_EQ(err.locations().at(1).second, "upper limit is defined here"); CHECK_EQ(err.locations().at(2).second, "this is not in the range" ); } toml11-4.1.0/tests/test_find.cpp000066400000000000000000001062561464712047700165040ustar00rootroot00000000000000#define DOCTEST_CONFIG_IMPLEMENT_WITH_MAIN #include "doctest.h" #include "utility.hpp" #include #include #include #include #include #include #include #include #if defined(TOML11_HAS_STRING_VIEW) #include #endif namespace toml { namespace detail { std::tm localtime_s(const std::time_t* src); std::tm gmtime_s(const std::time_t* src); } // detail } // toml TEST_CASE("testing toml::find with toml type") { using value_type = toml::value; using boolean_type = typename value_type::boolean_type ; using integer_type = typename value_type::integer_type ; using floating_type = typename value_type::floating_type ; using string_type = typename value_type::string_type ; using local_time_type = typename value_type::local_time_type ; using local_date_type = typename value_type::local_date_type ; using local_datetime_type = typename value_type::local_datetime_type ; using offset_datetime_type = typename value_type::offset_datetime_type; using array_type = typename value_type::array_type ; using table_type = typename value_type::table_type ; { value_type v(toml::table{{"a", true}}); CHECK_EQ(true, toml::find(v, "a")); toml::find(v, "a") = false; CHECK_EQ(false, toml::find(v, "a")); boolean_type x = toml::find(std::move(v), "a"); CHECK_EQ(false, x); } { value_type v(toml::table{{"a", 42}}); CHECK_EQ(integer_type(42), toml::find(v, "a")); toml::find(v, "a") = 54; CHECK_EQ(integer_type(54), toml::find(v, "a")); integer_type x = toml::find(std::move(v), "a"); CHECK_EQ(integer_type(54), x); } { value_type v(toml::table{{"a", 3.14}}); CHECK_EQ(floating_type(3.14), toml::find(v, "a")); toml::find(v, "a") = 2.71; CHECK_EQ(floating_type(2.71), toml::find(v, "a")); floating_type x = toml::find(std::move(v), "a"); CHECK_EQ(floating_type(2.71), x); } { value_type v(toml::table{{"a", "foo"}}); CHECK_EQ("foo", toml::find(v, "a")); toml::find(v, "a") += "bar"; CHECK_EQ("foobar", toml::find(v, "a")); string_type x = toml::find(std::move(v), "a"); CHECK_EQ("foobar", x); } { local_date_type d(2018, toml::month_t::Apr, 22); value_type v(toml::table{{"a", d}}); CHECK_EQ(d, toml::find(v, "a")); toml::find(v, "a").year = 2017; d.year = 2017; CHECK_EQ(d, toml::find(v, "a")); local_date_type x = toml::find(std::move(v), "a"); CHECK_EQ(d, x); } { local_time_type t(12, 30, 45); value_type v(toml::table{{"a", t}}); CHECK_EQ(t, toml::find(v, "a")); toml::find(v, "a").hour = 9; t.hour = 9; CHECK_EQ(t, toml::find(v, "a")); local_time_type x = toml::find(std::move(v), "a"); CHECK_EQ(t, x); } { local_datetime_type dt(toml::local_date(2018, toml::month_t::Apr, 22), toml::local_time(12, 30, 45)); value_type v(toml::table{{"a", dt}}); CHECK_EQ(dt, toml::find(v, "a")); toml::find(v, "a").date.year = 2017; dt.date.year = 2017; CHECK_EQ(dt, toml::find(v, "a")); toml::local_datetime x = toml::find(std::move(v), "a"); CHECK_EQ(dt, x); } { offset_datetime_type dt(toml::local_datetime( toml::local_date(2018, toml::month_t::Apr, 22), toml::local_time(12, 30, 45)), toml::time_offset(9, 0)); value_type v(toml::table{{"a", dt}}); CHECK_EQ(dt, toml::find(v, "a")); toml::find(v, "a").date.year = 2017; dt.date.year = 2017; CHECK_EQ(dt, toml::find(v, "a")); offset_datetime_type x = toml::find(std::move(v), "a"); CHECK_EQ(dt, x); } { array_type vec; vec.push_back(value_type(42)); vec.push_back(value_type(54)); value_type v(toml::table{{"a", vec}}); CHECK_EQ(vec, toml::find(v, "a")); toml::find(v, "a").push_back(value_type(123)); vec.push_back(value_type(123)); CHECK_EQ(vec, toml::find(v, "a")); array_type x = toml::find(std::move(v), "a"); CHECK_EQ(vec, x); } { table_type tab; tab["key1"] = value_type(42); tab["key2"] = value_type(3.14); value_type v(toml::table{{"a", tab}}); CHECK_EQ(tab, toml::find(v, "a")); toml::find(v, "a")["key3"] = value_type(123); tab["key3"] = value_type(123); CHECK_EQ(tab, toml::find(v, "a")); table_type x = toml::find(std::move(v), "a"); CHECK_EQ(tab, x); } { value_type v1(toml::table{{"a", 42}}); CHECK_EQ(toml::value(42), toml::find(v1, "a")); value_type v2(54); toml::find(v1, "a") = v2; CHECK_EQ(v2, toml::find(v1, "a")); value_type x = toml::find(std::move(v1), "a"); CHECK_EQ(v2, x); } } TEST_CASE("testing find throws") { using value_type = toml::value; using boolean_type = typename value_type::boolean_type ; using integer_type = typename value_type::integer_type ; // ----------------------------------------------------------------------- // const-reference version { // value is not a table const toml::value v(true); CHECK_THROWS_AS(toml::find(v, "key"), toml::type_error); } { // the value corresponding to the key is not the expected type const toml::value v = toml::table{{"key", 42}}; CHECK_THROWS_AS(toml::find(v, "key"), toml::type_error); } { // the value corresponding to the key is not found const toml::value v = toml::table{{"key", 42}}; CHECK_THROWS_AS(toml::find(v, "different_key"), std::out_of_range); } { // the positive control. const toml::value v = toml::table{{"key", 42}}; CHECK_EQ(42, toml::find(v, "key")); } // ----------------------------------------------------------------------- // reference version { // value is not a table toml::value v(true); CHECK_THROWS_AS(toml::find(v, "key"), toml::type_error); } { // the value corresponding to the key is not the expected type toml::value v = toml::table{{"key", 42}}; CHECK_THROWS_AS(toml::find(v, "key"), toml::type_error); } { // the value corresponding to the key is not found toml::value v = toml::table{{"key", 42}}; CHECK_THROWS_AS(toml::find(v, "different_key"), std::out_of_range); } { // the positive control. toml::value v = toml::table{{"key", 42}}; CHECK_EQ(42, toml::find(v, "key")); } // ----------------------------------------------------------------------- // move version { // value is not a table toml::value v(true); CHECK_THROWS_AS(toml::find(std::move(v), "key"), toml::type_error); } { // the value corresponding to the key is not the expected type toml::value v = toml::table{{"key", 42}}; CHECK_THROWS_AS(toml::find(std::move(v), "key"), toml::type_error); } { // the value corresponding to the key is not found toml::value v = toml::table{{"key", 42}}; CHECK_THROWS_AS(toml::find(std::move(v), "different_key"), std::out_of_range); } { // the positive control. toml::value v = toml::table{{"key", 42}}; CHECK_EQ(42, toml::find(std::move(v), "key")); } } TEST_CASE("testing toml::find(v, idx) throws") { using value_type = toml::value; using boolean_type = typename value_type::boolean_type ; using integer_type = typename value_type::integer_type ; // ----------------------------------------------------------------------- // const-reference version { // value is not an array const toml::value v(true); CHECK_THROWS_AS(toml::find(v, 0), toml::type_error); } { // the value corresponding to the key is not the expected type const toml::value v = toml::array{1, 2, 3, 4, 5}; CHECK_THROWS_AS(toml::find(v, 0), toml::type_error); } { // the value corresponding to the key is not found const toml::value v = toml::array{1, 2, 3, 4, 5}; CHECK_THROWS_AS(toml::find(v, 6), std::out_of_range); } { // the positive control. const toml::value v = toml::array{1, 2, 3, 4, 5}; CHECK_EQ(3, toml::find(v, 2)); } // ----------------------------------------------------------------------- // non-const reference version { // value is not an array toml::value v(true); CHECK_THROWS_AS(toml::find(v, 0), toml::type_error); } { // the value corresponding to the key is not the expected type toml::value v = toml::array{1, 2, 3, 4, 5}; CHECK_THROWS_AS(toml::find(v, 0), toml::type_error); } { // the value corresponding to the key is not found toml::value v = toml::array{1, 2, 3, 4, 5}; CHECK_THROWS_AS(toml::find(v, 6), std::out_of_range); } { // the positive control. toml::value v = toml::array{1, 2, 3, 4, 5}; CHECK_EQ(3, toml::find(v, 2)); } // ----------------------------------------------------------------------- // move version { // value is not an array toml::value v(true); CHECK_THROWS_AS(toml::find(std::move(v), 0), toml::type_error); } { // the value corresponding to the key is not the expected type toml::value v = toml::array{1, 2, 3, 4, 5}; CHECK_THROWS_AS(toml::find(std::move(v), 0), toml::type_error); } { // the value corresponding to the key is not found toml::value v = toml::array{1, 2, 3, 4, 5}; CHECK_THROWS_AS(toml::find(std::move(v), 6), std::out_of_range); } { // the positive control. toml::value v = toml::array{1, 2, 3, 4, 5}; CHECK_EQ(3, toml::find(std::move(v), 2)); } } TEST_CASE("testing toml::find with recursive table/array") { using value_type = toml::value; using integer_type = typename value_type::integer_type ; // recursively search tables { toml::value v = toml::table{ {"a", toml::table{ {"b", toml::table{ {"c", toml::table{ {"d", 42} }} }} }} }; CHECK_EQ(42, toml::find(v, "a", "b", "c", "d")); // reference that can be used to modify the content auto& num = toml::find(v, "a", "b", "c", "d"); num = 54; CHECK_EQ(54, toml::find(v, "a", "b", "c", "d")); const std::string a("a"), b("b"), c("c"), d("d"); auto& num2 = toml::find(v, a, b, c, d); num2 = 42; CHECK_EQ(42, toml::find(v, a, b, c, d)); auto num3 = toml::find(v, a, "b", c, "d"); CHECK_EQ(42, num3); auto num4 = toml::find(std::move(v), a, b, c, d); CHECK_EQ(42, num4); } // recursively search arrays { toml::value v = toml::array{ toml::array{"array", "of", "string"}, toml::array{toml::array{1, 2, 3}, toml::array{3.14, 2.71}} }; CHECK_EQ("array" , toml::find(v, 0, 0)); CHECK_EQ("of" , toml::find(v, 0, 1)); CHECK_EQ("string", toml::find(v, 0, 2)); CHECK_EQ(1, toml::find(v, 1, 0, 0)); CHECK_EQ(2, toml::find(v, 1, 0, 1)); CHECK_EQ(3, toml::find(v, 1, 0, 2)); CHECK_EQ(3.14, toml::find(v, 1, 1, 0)); CHECK_EQ(2.71, toml::find(v, 1, 1, 1)); // reference that can be used to modify the content auto& num = toml::find(v, 1, 0, 2); num = 42; CHECK_EQ( 1, toml::find(v, 1, 0, 0)); CHECK_EQ( 2, toml::find(v, 1, 0, 1)); CHECK_EQ(42, toml::find(v, 1, 0, 2)); // move value auto num2 = toml::find(std::move(v), 1, 0, 2); CHECK_EQ(42, num2); } // recursively search mixtures { toml::value v = toml::table{{"array", toml::array{ toml::array{1, 2, 3}, toml::array{ toml::table{{"foo", "bar"}, {"baz", "qux"}}, toml::table{{"pi", 3.14}, {"e", 2.71}} }} }}; CHECK_EQ(1, toml::find(v, "array", 0, 0)); CHECK_EQ(2, toml::find(v, "array", 0, 1)); CHECK_EQ(3, toml::find(v, "array", 0, 2)); CHECK_EQ("bar", toml::find(v, "array", 1, 0, "foo")); CHECK_EQ("qux", toml::find(v, "array", 1, 0, "baz")); CHECK_EQ(3.14, toml::find(v, "array", 1, 1, "pi")); CHECK_EQ(2.71, toml::find(v, "array", 1, 1, "e")); const std::string ar("array"); const auto ar_c = "array"; const std::string pi("pi"); const auto pi_c = "pi"; CHECK_EQ(3.14, toml::find(v, ar, 1, 1, "pi")); CHECK_EQ(3.14, toml::find(v, ar, 1, 1, pi)); CHECK_EQ(3.14, toml::find(v, ar, 1, 1, pi_c)); CHECK_EQ(3.14, toml::find(v, ar_c, 1, 1, "pi")); CHECK_EQ(3.14, toml::find(v, ar_c, 1, 1, pi)); CHECK_EQ(3.14, toml::find(v, ar_c, 1, 1, pi_c)); CHECK_EQ(3.14, toml::find(v, "array", 1, 1, pi)); CHECK_EQ(3.14, toml::find(v, "array", 1, 1, pi_c)); } } TEST_CASE("testing toml::find with toml types") { using value_type = toml::value; using boolean_type = typename value_type::boolean_type ; using integer_type = typename value_type::integer_type ; using floating_type = typename value_type::floating_type ; using string_type = typename value_type::string_type ; using local_time_type = typename value_type::local_time_type ; using local_date_type = typename value_type::local_date_type ; using local_datetime_type = typename value_type::local_datetime_type ; using offset_datetime_type = typename value_type::offset_datetime_type; using array_type = typename value_type::array_type ; using table_type = typename value_type::table_type ; { value_type v = toml::table{{"key", true}}; CHECK(true == toml::find(v, "key")); toml::find(v, "key") = false; CHECK(false == toml::find(v, "key")); const auto moved = toml::find(std::move(v), "key"); CHECK(false == moved); } { value_type v = toml::table{{"key", 42}}; CHECK(integer_type(42) == toml::find(v, "key")); toml::find(v, "key") = 54; CHECK(integer_type(54) == toml::find(v, "key")); const auto moved = toml::find(std::move(v), "key"); CHECK(integer_type(54) == moved); } { value_type v = toml::table{{"key", 3.14}}; CHECK(floating_type(3.14) == toml::find(v, "key")); toml::find(v, "key") = 2.71; CHECK(floating_type(2.71) == toml::find(v, "key")); const auto moved = toml::find(std::move(v), "key"); CHECK(floating_type(2.71) == moved); } { value_type v = toml::table{{"key", "foo"}}; CHECK("foo" == toml::find(v, "key")); toml::find(v, "key") += "bar"; CHECK("foobar" == toml::find(v, "key")); const auto moved = toml::find(std::move(v), "key"); CHECK("foobar" == moved); } { local_date_type d(2018, toml::month_t::Apr, 22); value_type v = toml::table{{"key", d}}; CHECK(d == toml::find(v, "key")); toml::find(v, "key").year = 2017; d.year = 2017; CHECK(d == toml::find(v, "key")); const auto moved = toml::find(std::move(v), "key"); CHECK(d == moved); } { local_time_type t(12, 30, 45); value_type v = toml::table{{"key", t}}; CHECK(t == toml::find(v, "key")); toml::find(v, "key").hour = 9; t.hour = 9; CHECK(t == toml::find(v, "key")); const auto moved = toml::find(std::move(v), "key"); CHECK(t == moved); } { local_datetime_type dt(toml::local_date(2018, toml::month_t::Apr, 22), local_time_type(12, 30, 45)); value_type v = toml::table{{"key", dt}}; CHECK(dt == toml::find(v, "key")); toml::find(v, "key").date.year = 2017; dt.date.year = 2017; CHECK(dt == toml::find(v, "key")); const auto moved = toml::find(std::move(v), "key"); CHECK(dt == moved); } { offset_datetime_type dt(local_datetime_type( toml::local_date(2018, toml::month_t::Apr, 22), local_time_type(12, 30, 45)), toml::time_offset(9, 0)); value_type v = toml::table{{"key", dt}}; CHECK(dt == toml::find(v, "key")); toml::find(v, "key").date.year = 2017; dt.date.year = 2017; CHECK(dt == toml::find(v, "key")); const auto moved = toml::find(std::move(v), "key"); CHECK(dt == moved); } { array_type vec; vec.push_back(value_type(42)); vec.push_back(value_type(54)); value_type v = toml::table{{"key", vec}}; const bool result1 = (vec == toml::find(v, "key")); CHECK(result1); toml::find(v, "key").push_back(value_type(123)); vec.push_back(value_type(123)); const bool result2 = (vec == toml::find(v, "key")); CHECK(result2); const auto moved = toml::find(std::move(v), "key"); const bool result3 = (vec == moved); CHECK(result3); } { table_type tab; tab["key1"] = value_type(42); tab["key2"] = value_type(3.14); value_type v = toml::table{{"key", tab}}; const bool result1 = (tab == toml::find(v, "key")); CHECK(result1); toml::find(v, "key")["key3"] = value_type(123); tab["key3"] = value_type(123); const bool result2 = (tab == toml::find(v, "key")); CHECK(result2); const auto moved = toml::find(std::move(v), "key"); const bool result3 = (tab == moved); CHECK(result3); } { value_type v1(42); value_type v = toml::table{{"key", v1}}; CHECK(v1 == toml::find(v, "key")); value_type v2(54); toml::find(v, "key") = v2; CHECK(v2 == toml::find(v, "key")); const auto moved = toml::find(std::move(v), "key"); CHECK(v2 == moved); } } TEST_CASE("testing toml::find integer conversion") { using value_type = toml::value; { value_type v = toml::table{{"key", 42}}; CHECK_EQ(int(42) , toml::find(v, "key")); CHECK_EQ(short(42) , toml::find(v, "key")); CHECK_EQ(char(42) , toml::find(v, "key")); CHECK_EQ(unsigned(42) , toml::find(v, "key")); CHECK_EQ(long(42) , toml::find(v, "key")); CHECK_EQ(std::int64_t(42) , toml::find(v, "key")); CHECK_EQ(std::uint64_t(42), toml::find(v, "key")); CHECK_EQ(std::int16_t(42) , toml::find(v, "key")); CHECK_EQ(std::uint16_t(42), toml::find(v, "key")); CHECK_EQ(std::uint16_t(42), toml::find(std::move(v), "key")); } } TEST_CASE("testing toml::find floating conversion") { using value_type = toml::value; { value_type v = toml::table{{"key", 3.14}}; const double ref(3.14); CHECK_EQ(static_cast(ref), toml::find(v, "key")); CHECK_EQ( ref , toml::find(v, "key")); CHECK_EQ(static_cast(ref), toml::find(v, "key")); CHECK_EQ(static_cast(ref), toml::find(std::move(v), "key")); } } TEST_CASE("testing toml::find string conversion") { using value_type = toml::value; { value_type v = toml::table{{"key", "foo"}}; CHECK_EQ("foo", toml::find(v, "key")); toml::find(v, "key") += "bar"; CHECK_EQ("foobar", toml::find(v, "key")); } } #if defined(TOML11_HAS_CHAR8_T) TEST_CASE("testing toml::find") { using value_type = toml::value; { value_type v = toml::table{{"key", "foo"}}; CHECK_EQ(u8"foo", toml::find(v, "key")); } } #endif #if TOML11_CPLUSPLUS_STANDARD_VERSION >= 201703L TEST_CASE("testing toml::get") { using value_type = toml::value; { value_type v("foo"); CHECK_EQ("foo", toml::get(v)); } } #endif TEST_CASE("testing toml::find array conversion") { using value_type = toml::value; value_type v = toml::table{{"key", toml::array{42, 54, 69, 72}}}; const std::vector vec = toml::find>(v, "key"); const std::list lst = toml::find>(v, "key"); const std::deque deq = toml::find>(v, "key"); CHECK_EQ(42, vec.at(0)); CHECK_EQ(54, vec.at(1)); CHECK_EQ(69, vec.at(2)); CHECK_EQ(72, vec.at(3)); std::list::const_iterator iter = lst.begin(); CHECK_EQ(static_cast(42), *(iter++)); CHECK_EQ(static_cast(54), *(iter++)); CHECK_EQ(static_cast(69), *(iter++)); CHECK_EQ(static_cast(72), *(iter++)); CHECK_EQ(static_cast(42), deq.at(0)); CHECK_EQ(static_cast(54), deq.at(1)); CHECK_EQ(static_cast(69), deq.at(2)); CHECK_EQ(static_cast(72), deq.at(3)); std::array ary = toml::find>(v, "key"); CHECK_EQ(42, ary.at(0)); CHECK_EQ(54, ary.at(1)); CHECK_EQ(69, ary.at(2)); CHECK_EQ(72, ary.at(3)); std::tuple tpl = toml::find>(v, "key"); CHECK_EQ( 42 , std::get<0>(tpl)); CHECK_EQ(static_cast(54), std::get<1>(tpl)); CHECK_EQ(static_cast(69), std::get<2>(tpl)); CHECK_EQ(static_cast(72), std::get<3>(tpl)); value_type p = toml::table{{"key", toml::array{3.14, 2.71}}}; std::pair pr = toml::find >(p, "key"); CHECK_EQ(3.14, pr.first); CHECK_EQ(2.71, pr.second); } TEST_CASE("testing toml::find array move conversion") { using value_type = toml::value; value_type v1 = toml::table{{"key", toml::array{42, 54, 69, 72}}}; value_type v2 = toml::table{{"key", toml::array{42, 54, 69, 72}}}; value_type v3 = toml::table{{"key", toml::array{42, 54, 69, 72}}}; value_type v4 = toml::table{{"key", toml::array{42, 54, 69, 72}}}; value_type v5 = toml::table{{"key", toml::array{42, 54, 69, 72}}}; const std::vector vec = toml::find>(std::move(v1), "key"); const std::list lst = toml::find>(std::move(v2), "key"); const std::deque deq = toml::find>(std::move(v3), "key"); CHECK_EQ(42, vec.at(0)); CHECK_EQ(54, vec.at(1)); CHECK_EQ(69, vec.at(2)); CHECK_EQ(72, vec.at(3)); std::list::const_iterator iter = lst.begin(); CHECK_EQ(static_cast(42), *(iter++)); CHECK_EQ(static_cast(54), *(iter++)); CHECK_EQ(static_cast(69), *(iter++)); CHECK_EQ(static_cast(72), *(iter++)); CHECK_EQ(static_cast(42), deq.at(0)); CHECK_EQ(static_cast(54), deq.at(1)); CHECK_EQ(static_cast(69), deq.at(2)); CHECK_EQ(static_cast(72), deq.at(3)); std::array ary = toml::find>(std::move(v4), "key"); CHECK_EQ(42, ary.at(0)); CHECK_EQ(54, ary.at(1)); CHECK_EQ(69, ary.at(2)); CHECK_EQ(72, ary.at(3)); std::tuple tpl = toml::find>(std::move(v5), "key"); CHECK_EQ( 42 , std::get<0>(tpl)); CHECK_EQ(static_cast(54), std::get<1>(tpl)); CHECK_EQ(static_cast(69), std::get<2>(tpl)); CHECK_EQ(static_cast(72), std::get<3>(tpl)); value_type p = toml::table{{"key", toml::array{3.14, 2.71}}}; std::pair pr = toml::find >(std::move(p), "key"); CHECK_EQ(3.14, pr.first); CHECK_EQ(2.71, pr.second); } TEST_CASE("testing toml::find array of array conversion") { using value_type = toml::value; value_type v1 = toml::array{42, 54, 69, 72}; value_type v2 = toml::array{"foo", "bar", "baz"}; value_type v = toml::table{{"key", toml::array{v1, v2}}}; std::pair, std::vector> p = toml::find, std::vector>>(v, "key"); CHECK_EQ(p.first.at(0), 42); CHECK_EQ(p.first.at(1), 54); CHECK_EQ(p.first.at(2), 69); CHECK_EQ(p.first.at(3), 72); CHECK_EQ(p.second.at(0), "foo"); CHECK_EQ(p.second.at(1), "bar"); CHECK_EQ(p.second.at(2), "baz"); std::tuple, std::vector> t = toml::find, std::vector>>(v, "key"); CHECK_EQ(std::get<0>(t).at(0), 42); CHECK_EQ(std::get<0>(t).at(1), 54); CHECK_EQ(std::get<0>(t).at(2), 69); CHECK_EQ(std::get<0>(t).at(3), 72); CHECK_EQ(std::get<1>(t).at(0), "foo"); CHECK_EQ(std::get<1>(t).at(1), "bar"); CHECK_EQ(std::get<1>(t).at(2), "baz"); } TEST_CASE("testing toml::find array of array move conversion") { using value_type = toml::value; value_type a1 = toml::array{42, 54, 69, 72}; value_type a2 = toml::array{"foo", "bar", "baz"}; value_type v1 = toml::table{{"key", toml::array{a1, a2}}}; value_type v2 = toml::table{{"key", toml::array{a1, a2}}}; std::pair, std::vector> p = toml::find, std::vector>>(std::move(v1), "key"); CHECK_EQ(p.first.at(0), 42); CHECK_EQ(p.first.at(1), 54); CHECK_EQ(p.first.at(2), 69); CHECK_EQ(p.first.at(3), 72); CHECK_EQ(p.second.at(0), "foo"); CHECK_EQ(p.second.at(1), "bar"); CHECK_EQ(p.second.at(2), "baz"); std::tuple, std::vector> t = toml::find, std::vector>>(std::move(v2), "key"); CHECK_EQ(std::get<0>(t).at(0), 42); CHECK_EQ(std::get<0>(t).at(1), 54); CHECK_EQ(std::get<0>(t).at(2), 69); CHECK_EQ(std::get<0>(t).at(3), 72); CHECK_EQ(std::get<1>(t).at(0), "foo"); CHECK_EQ(std::get<1>(t).at(1), "bar"); CHECK_EQ(std::get<1>(t).at(2), "baz"); } TEST_CASE("testing toml::find table conversion") { using value_type = toml::value; { value_type v1 = toml::table{{"key", toml::table{ {"key1", 1}, {"key2", 2}, {"key3", 3}, {"key4", 4} }}}; const auto v = toml::find>(v1, "key"); CHECK_EQ(v.at("key1"), 1); CHECK_EQ(v.at("key2"), 2); CHECK_EQ(v.at("key3"), 3); CHECK_EQ(v.at("key4"), 4); } { value_type v1 = toml::table{{"key", toml::table{ {"key1", 1}, {"key2", 2}, {"key3", 3}, {"key4", 4} }}}; const auto v = toml::find>(std::move(v1), "key"); CHECK_EQ(v.at("key1"), 1); CHECK_EQ(v.at("key2"), 2); CHECK_EQ(v.at("key3"), 3); CHECK_EQ(v.at("key4"), 4); } } TEST_CASE("testing toml::find local_date") { using value_type = toml::value; { value_type v1 = toml::table{{"key", toml::local_date{2018, toml::month_t::Apr, 1}}}; const auto date = std::chrono::system_clock::to_time_t( toml::find(v1, "key")); std::tm t; t.tm_year = 2018 - 1900; t.tm_mon = 4 - 1; t.tm_mday = 1; t.tm_hour = 0; t.tm_min = 0; t.tm_sec = 0; t.tm_isdst = -1; const auto c = std::mktime(&t); CHECK_EQ(c, date); } { value_type v1 = toml::table{{"key", toml::local_date{2018, toml::month_t::Apr, 1}}}; const auto date = std::chrono::system_clock::to_time_t( toml::find(std::move(v1), "key")); std::tm t; t.tm_year = 2018 - 1900; t.tm_mon = 4 - 1; t.tm_mday = 1; t.tm_hour = 0; t.tm_min = 0; t.tm_sec = 0; t.tm_isdst = -1; const auto c = std::mktime(&t); CHECK_EQ(c, date); } } TEST_CASE("testing toml::find local_time") { using value_type = toml::value; { value_type v1 = toml::table{{"key", toml::local_time{12, 30, 45}}}; const auto time = toml::find(v1, "key"); CHECK_EQ(time, std::chrono::hours(12) + std::chrono::minutes(30) + std::chrono::seconds(45)); } { value_type v1 = toml::table{{"key", toml::local_time{12, 30, 45}}}; const auto time = toml::find(std::move(v1), "key"); CHECK_EQ(time, std::chrono::hours(12) + std::chrono::minutes(30) + std::chrono::seconds(45)); } } TEST_CASE("testing toml::find local_datetime") { using value_type = toml::value; { value_type v1 = toml::table{{"key", toml::local_datetime( toml::local_date{2018, toml::month_t::Apr, 1}, toml::local_time{12, 30, 45})}}; const auto date = std::chrono::system_clock::to_time_t( toml::find(v1, "key")); std::tm t; t.tm_year = 2018 - 1900; t.tm_mon = 4 - 1; t.tm_mday = 1; t.tm_hour = 12; t.tm_min = 30; t.tm_sec = 45; t.tm_isdst = -1; const auto c = std::mktime(&t); CHECK_EQ(c, date); } { value_type v1 = toml::table{{"key", toml::local_datetime( toml::local_date{2018, toml::month_t::Apr, 1}, toml::local_time{12, 30, 45})}}; const auto date = std::chrono::system_clock::to_time_t( toml::find(std::move(v1), "key")); std::tm t; t.tm_year = 2018 - 1900; t.tm_mon = 4 - 1; t.tm_mday = 1; t.tm_hour = 12; t.tm_min = 30; t.tm_sec = 45; t.tm_isdst = -1; const auto c = std::mktime(&t); CHECK_EQ(c, date); } } TEST_CASE("testing toml::find offset_datetime") { using value_type = toml::value; { value_type v1 = toml::table{{"key", toml::offset_datetime( toml::local_date{2018, toml::month_t::Apr, 1}, toml::local_time{12, 30, 0}, toml::time_offset{9, 0})}}; // 2018-04-01T12:30:00+09:00 //, 2018-04-01T03:30:00Z const auto date = toml::find(v1, "key"); const auto timet = std::chrono::system_clock::to_time_t(date); // get time_t as gmtime (2018-04-01T03:30:00Z) const auto tm = toml::detail::gmtime_s(std::addressof(timet)); CHECK_EQ(tm.tm_year + 1900, 2018); CHECK_EQ(tm.tm_mon + 1, 4); CHECK_EQ(tm.tm_mday, 1); CHECK_EQ(tm.tm_hour, 3); CHECK_EQ(tm.tm_min, 30); CHECK_EQ(tm.tm_sec, 0); } { value_type v1 = toml::table{{"key", toml::offset_datetime( toml::local_date{2018, toml::month_t::Apr, 1}, toml::local_time{12, 30, 0}, toml::time_offset{-8, 0})}}; // 2018-04-01T12:30:00-08:00 //, 2018-04-01T20:30:00Z const auto date = toml::find(v1, "key"); const auto timet = std::chrono::system_clock::to_time_t(date); // get time_t as gmtime (2018-04-01T03:30:00Z) const auto tm = toml::detail::gmtime_s(std::addressof(timet)); CHECK_EQ(tm.tm_year + 1900, 2018); CHECK_EQ(tm.tm_mon + 1, 4); CHECK_EQ(tm.tm_mday, 1); CHECK_EQ(tm.tm_hour, 20); CHECK_EQ(tm.tm_min, 30); CHECK_EQ(tm.tm_sec, 0); } { value_type v1 = toml::table{{"key", toml::offset_datetime( toml::local_date{2018, toml::month_t::Apr, 1}, toml::local_time{12, 30, 0}, toml::time_offset{-8, 0})}}; // 2018-04-01T12:30:00-08:00 //, 2018-04-01T20:30:00Z const auto date = toml::find(std::move(v1), "key"); const auto timet = std::chrono::system_clock::to_time_t(date); // get time_t as gmtime (2018-04-01T03:30:00Z) const auto tm = toml::detail::gmtime_s(std::addressof(timet)); CHECK_EQ(tm.tm_year + 1900, 2018); CHECK_EQ(tm.tm_mon + 1, 4); CHECK_EQ(tm.tm_mday, 1); CHECK_EQ(tm.tm_hour, 20); CHECK_EQ(tm.tm_min, 30); CHECK_EQ(tm.tm_sec, 0); } } toml11-4.1.0/tests/test_find_or.cpp000066400000000000000000000567021464712047700172040ustar00rootroot00000000000000#define DOCTEST_CONFIG_IMPLEMENT_WITH_MAIN #include "doctest.h" #include "utility.hpp" #include #include #include #include #include #include #include #include #if TOML11_CPLUSPLUS_STANDARD_VERSION >= 201703L #include #endif #define TOML11_TEST_FIND_OR_EXACT(ty, init_expr, opt_expr)\ { \ const ty init init_expr ; \ const ty opt opt_expr ; \ const value_type v = toml::table{{"key", init}}; \ CHECK_NE(init, opt); \ CHECK_EQ(init, toml::find_or(v, "key", opt)); \ } TEST_CASE("testing toml::find_or with toml types") { using value_type = toml::value; using boolean_type = typename value_type::boolean_type ; using integer_type = typename value_type::integer_type ; using floating_type = typename value_type::floating_type ; using string_type = typename value_type::string_type ; using local_time_type = typename value_type::local_time_type ; using local_date_type = typename value_type::local_date_type ; using local_datetime_type = typename value_type::local_datetime_type ; using offset_datetime_type = typename value_type::offset_datetime_type; using array_type = typename value_type::array_type ; using table_type = typename value_type::table_type ; TOML11_TEST_FIND_OR_EXACT(boolean_type, ( true), (false)) TOML11_TEST_FIND_OR_EXACT(integer_type, ( 42), ( 54)) TOML11_TEST_FIND_OR_EXACT(floating_type, ( 3.14), ( 2.71)) TOML11_TEST_FIND_OR_EXACT(string_type, ("foo"), ("bar")) TOML11_TEST_FIND_OR_EXACT(local_time_type, (12, 30, 45), (6, 0, 30)) TOML11_TEST_FIND_OR_EXACT(local_date_type, (2019, toml::month_t::Apr, 1), (1999, toml::month_t::Jan, 2)) TOML11_TEST_FIND_OR_EXACT(local_datetime_type, (toml::local_date(2019, toml::month_t::Apr, 1), toml::local_time(12, 30, 45)), (toml::local_date(1999, toml::month_t::Jan, 2), toml::local_time( 6, 0, 30)) ) TOML11_TEST_FIND_OR_EXACT(offset_datetime_type, (toml::local_date(2019, toml::month_t::Apr, 1), toml::local_time(12, 30, 45), toml::time_offset( 9, 0)), (toml::local_date(1999, toml::month_t::Jan, 2), toml::local_time( 6, 0, 30), toml::time_offset(-3, 0)) ) TOML11_TEST_FIND_OR_EXACT(array_type, ({1,2,3,4,5}), ({6,7,8,9,10})); TOML11_TEST_FIND_OR_EXACT(table_type, ({{"key1", 42}, {"key2", "foo"}}), ({{"key1", 54}, {"key2", "bar"}})); } #undef TOML11_TEST_FIND_OR_EXACT #define TOML11_TEST_FIND_OR_MOVE(ty, init_expr, opt_expr) \ { \ const ty init init_expr ; \ ty opt opt_expr ; \ value_type v = toml::table{{"key", init}}; \ CHECK_NE(init, opt); \ const auto moved = toml::find_or(std::move(v), "key", std::move(opt));\ CHECK_EQ(init, moved); \ } TEST_CASE("testing toml::find_or with moved argument") { using value_type = toml::value; using boolean_type = typename value_type::boolean_type ; using integer_type = typename value_type::integer_type ; using floating_type = typename value_type::floating_type ; using string_type = typename value_type::string_type ; using local_time_type = typename value_type::local_time_type ; using local_date_type = typename value_type::local_date_type ; using local_datetime_type = typename value_type::local_datetime_type ; using offset_datetime_type = typename value_type::offset_datetime_type; using array_type = typename value_type::array_type ; using table_type = typename value_type::table_type ; TOML11_TEST_FIND_OR_MOVE(boolean_type, ( true), (false)) TOML11_TEST_FIND_OR_MOVE(integer_type, ( 42), ( 54)) TOML11_TEST_FIND_OR_MOVE(floating_type, ( 3.14), ( 2.71)) TOML11_TEST_FIND_OR_MOVE(string_type, ("foo"), ("bar")) TOML11_TEST_FIND_OR_MOVE(local_time_type, (12, 30, 45), (6, 0, 30)) TOML11_TEST_FIND_OR_MOVE(local_date_type, (2019, toml::month_t::Apr, 1), (1999, toml::month_t::Jan, 2)) TOML11_TEST_FIND_OR_MOVE(local_datetime_type, (toml::local_date(2019, toml::month_t::Apr, 1), toml::local_time(12, 30, 45)), (toml::local_date(1999, toml::month_t::Jan, 2), toml::local_time( 6, 0, 30)) ) TOML11_TEST_FIND_OR_MOVE(offset_datetime_type, (toml::local_date(2019, toml::month_t::Apr, 1), toml::local_time(12, 30, 45), toml::time_offset( 9, 0)), (toml::local_date(1999, toml::month_t::Jan, 2), toml::local_time( 6, 0, 30), toml::time_offset(-3, 0)) ) TOML11_TEST_FIND_OR_MOVE(array_type, ({1,2,3,4,5}), ({6,7,8,9,10})); TOML11_TEST_FIND_OR_MOVE(table_type, ({{"key1", 42}, {"key2", "foo"}}), ({{"key1", 54}, {"key2", "bar"}})); } #undef TOML11_TEST_FIND_OR_MOVE #define TOML11_TEST_FIND_OR_MODIFY(ty, init_expr, opt_expr)\ { \ const ty init init_expr ; \ ty opt1 opt_expr ; \ ty opt2 opt_expr ; \ value_type v = toml::table{{"key", init}}; \ CHECK_NE(init, opt1); \ toml::find_or(v, "key", opt2) = opt1; \ CHECK_EQ(opt1, toml::find(v, "key")); \ } TEST_CASE("testing find_or with modification") { using value_type = toml::value; using boolean_type = typename value_type::boolean_type ; using integer_type = typename value_type::integer_type ; using floating_type = typename value_type::floating_type ; using string_type = typename value_type::string_type ; using local_time_type = typename value_type::local_time_type ; using local_date_type = typename value_type::local_date_type ; using local_datetime_type = typename value_type::local_datetime_type ; using offset_datetime_type = typename value_type::offset_datetime_type; using array_type = typename value_type::array_type ; using table_type = typename value_type::table_type ; TOML11_TEST_FIND_OR_MODIFY(boolean_type, ( true), (false)) TOML11_TEST_FIND_OR_MODIFY(integer_type, ( 42), ( 54)) TOML11_TEST_FIND_OR_MODIFY(floating_type, ( 3.14), ( 2.71)) TOML11_TEST_FIND_OR_MODIFY(string_type, ("foo"), ("bar")) TOML11_TEST_FIND_OR_MODIFY(local_time_type, (12, 30, 45), (6, 0, 30)) TOML11_TEST_FIND_OR_MODIFY(local_date_type, (2019, toml::month_t::Apr, 1), (1999, toml::month_t::Jan, 2)) TOML11_TEST_FIND_OR_MODIFY(local_datetime_type, (toml::local_date(2019, toml::month_t::Apr, 1), toml::local_time(12, 30, 45)), (toml::local_date(1999, toml::month_t::Jan, 2), toml::local_time( 6, 0, 30)) ) TOML11_TEST_FIND_OR_MODIFY(offset_datetime_type, (toml::local_date(2019, toml::month_t::Apr, 1), toml::local_time(12, 30, 45), toml::time_offset( 9, 0)), (toml::local_date(1999, toml::month_t::Jan, 2), toml::local_time( 6, 0, 30), toml::time_offset(-3, 0)) ) TOML11_TEST_FIND_OR_MODIFY(array_type, ({1,2,3,4,5}), ({6,7,8,9,10})); TOML11_TEST_FIND_OR_MODIFY(table_type, ({{"key1", 42}, {"key2", "foo"}}), ({{"key1", 54}, {"key2", "bar"}})); } #undef TOML11_TEST_FIND_OR_MODIFY #define TOML11_TEST_FIND_OR_FALLBACK(init_type, opt_type) \ { \ value_type v1(init_type); \ CHECK_EQ(opt_type, toml::find_or(v1, "key", opt_type)); \ value_type v2 = toml::table{{"different_key", init_type}}; \ CHECK_EQ(opt_type, toml::find_or(v2, "key", opt_type)); \ } TEST_CASE("testing toml::find_or returns fallback if failed") { using value_type = toml::value; using boolean_type = typename value_type::boolean_type ; using integer_type = typename value_type::integer_type ; using floating_type = typename value_type::floating_type ; using string_type = typename value_type::string_type ; using local_time_type = typename value_type::local_time_type ; using local_date_type = typename value_type::local_date_type ; using local_datetime_type = typename value_type::local_datetime_type ; using offset_datetime_type = typename value_type::offset_datetime_type; using array_type = typename value_type::array_type ; using table_type = typename value_type::table_type ; const boolean_type boolean (true); const integer_type integer (42); const floating_type floating (3.14); const string_type string ("foo"); const local_time_type local_time (12, 30, 45); const local_date_type local_date (2019, toml::month_t::Apr, 1); const local_datetime_type local_datetime ( toml::local_date(2019, toml::month_t::Apr, 1), toml::local_time(12, 30, 45)); const offset_datetime_type offset_datetime( toml::local_date(2019, toml::month_t::Apr, 1), toml::local_time(12, 30, 45), toml::time_offset( 9, 0)); const array_type array{1, 2, 3, 4, 5}; const table_type table{{"key1", 42}, {"key2", "foo"}}; TOML11_TEST_FIND_OR_FALLBACK(boolean, integer ); TOML11_TEST_FIND_OR_FALLBACK(boolean, floating ); TOML11_TEST_FIND_OR_FALLBACK(boolean, string ); TOML11_TEST_FIND_OR_FALLBACK(boolean, local_time ); TOML11_TEST_FIND_OR_FALLBACK(boolean, local_date ); TOML11_TEST_FIND_OR_FALLBACK(boolean, local_datetime ); TOML11_TEST_FIND_OR_FALLBACK(boolean, offset_datetime); TOML11_TEST_FIND_OR_FALLBACK(boolean, array ); TOML11_TEST_FIND_OR_FALLBACK(boolean, table ); TOML11_TEST_FIND_OR_FALLBACK(integer, boolean ); TOML11_TEST_FIND_OR_FALLBACK(integer, floating ); TOML11_TEST_FIND_OR_FALLBACK(integer, string ); TOML11_TEST_FIND_OR_FALLBACK(integer, local_time ); TOML11_TEST_FIND_OR_FALLBACK(integer, local_date ); TOML11_TEST_FIND_OR_FALLBACK(integer, local_datetime ); TOML11_TEST_FIND_OR_FALLBACK(integer, offset_datetime); TOML11_TEST_FIND_OR_FALLBACK(integer, array ); TOML11_TEST_FIND_OR_FALLBACK(integer, table ); TOML11_TEST_FIND_OR_FALLBACK(floating, boolean ); TOML11_TEST_FIND_OR_FALLBACK(floating, integer ); TOML11_TEST_FIND_OR_FALLBACK(floating, string ); TOML11_TEST_FIND_OR_FALLBACK(floating, local_time ); TOML11_TEST_FIND_OR_FALLBACK(floating, local_date ); TOML11_TEST_FIND_OR_FALLBACK(floating, local_datetime ); TOML11_TEST_FIND_OR_FALLBACK(floating, offset_datetime); TOML11_TEST_FIND_OR_FALLBACK(floating, array ); TOML11_TEST_FIND_OR_FALLBACK(floating, table ); TOML11_TEST_FIND_OR_FALLBACK(string, boolean ); TOML11_TEST_FIND_OR_FALLBACK(string, integer ); TOML11_TEST_FIND_OR_FALLBACK(string, floating ); TOML11_TEST_FIND_OR_FALLBACK(string, local_time ); TOML11_TEST_FIND_OR_FALLBACK(string, local_date ); TOML11_TEST_FIND_OR_FALLBACK(string, local_datetime ); TOML11_TEST_FIND_OR_FALLBACK(string, offset_datetime); TOML11_TEST_FIND_OR_FALLBACK(string, array ); TOML11_TEST_FIND_OR_FALLBACK(string, table ); TOML11_TEST_FIND_OR_FALLBACK(local_time, boolean ); TOML11_TEST_FIND_OR_FALLBACK(local_time, integer ); TOML11_TEST_FIND_OR_FALLBACK(local_time, floating ); TOML11_TEST_FIND_OR_FALLBACK(local_time, string ); TOML11_TEST_FIND_OR_FALLBACK(local_time, local_date ); TOML11_TEST_FIND_OR_FALLBACK(local_time, local_datetime ); TOML11_TEST_FIND_OR_FALLBACK(local_time, offset_datetime); TOML11_TEST_FIND_OR_FALLBACK(local_time, array ); TOML11_TEST_FIND_OR_FALLBACK(local_time, table ); TOML11_TEST_FIND_OR_FALLBACK(local_date, boolean ); TOML11_TEST_FIND_OR_FALLBACK(local_date, integer ); TOML11_TEST_FIND_OR_FALLBACK(local_date, floating ); TOML11_TEST_FIND_OR_FALLBACK(local_date, string ); TOML11_TEST_FIND_OR_FALLBACK(local_date, local_time ); TOML11_TEST_FIND_OR_FALLBACK(local_date, local_datetime ); TOML11_TEST_FIND_OR_FALLBACK(local_date, offset_datetime); TOML11_TEST_FIND_OR_FALLBACK(local_date, array ); TOML11_TEST_FIND_OR_FALLBACK(local_date, table ); TOML11_TEST_FIND_OR_FALLBACK(local_datetime, boolean ); TOML11_TEST_FIND_OR_FALLBACK(local_datetime, integer ); TOML11_TEST_FIND_OR_FALLBACK(local_datetime, floating ); TOML11_TEST_FIND_OR_FALLBACK(local_datetime, string ); TOML11_TEST_FIND_OR_FALLBACK(local_datetime, local_time ); TOML11_TEST_FIND_OR_FALLBACK(local_datetime, local_date ); TOML11_TEST_FIND_OR_FALLBACK(local_datetime, offset_datetime); TOML11_TEST_FIND_OR_FALLBACK(local_datetime, array ); TOML11_TEST_FIND_OR_FALLBACK(local_datetime, table ); TOML11_TEST_FIND_OR_FALLBACK(offset_datetime, boolean ); TOML11_TEST_FIND_OR_FALLBACK(offset_datetime, integer ); TOML11_TEST_FIND_OR_FALLBACK(offset_datetime, floating ); TOML11_TEST_FIND_OR_FALLBACK(offset_datetime, string ); TOML11_TEST_FIND_OR_FALLBACK(offset_datetime, local_time ); TOML11_TEST_FIND_OR_FALLBACK(offset_datetime, local_date ); TOML11_TEST_FIND_OR_FALLBACK(offset_datetime, local_datetime ); TOML11_TEST_FIND_OR_FALLBACK(offset_datetime, array ); TOML11_TEST_FIND_OR_FALLBACK(offset_datetime, table ); TOML11_TEST_FIND_OR_FALLBACK(array, boolean ); TOML11_TEST_FIND_OR_FALLBACK(array, integer ); TOML11_TEST_FIND_OR_FALLBACK(array, floating ); TOML11_TEST_FIND_OR_FALLBACK(array, string ); TOML11_TEST_FIND_OR_FALLBACK(array, local_time ); TOML11_TEST_FIND_OR_FALLBACK(array, local_date ); TOML11_TEST_FIND_OR_FALLBACK(array, local_datetime ); TOML11_TEST_FIND_OR_FALLBACK(array, offset_datetime); TOML11_TEST_FIND_OR_FALLBACK(array, table ); TOML11_TEST_FIND_OR_FALLBACK(table, boolean ); TOML11_TEST_FIND_OR_FALLBACK(table, integer ); TOML11_TEST_FIND_OR_FALLBACK(table, floating ); TOML11_TEST_FIND_OR_FALLBACK(table, string ); TOML11_TEST_FIND_OR_FALLBACK(table, local_time ); TOML11_TEST_FIND_OR_FALLBACK(table, local_date ); TOML11_TEST_FIND_OR_FALLBACK(table, local_datetime ); TOML11_TEST_FIND_OR_FALLBACK(table, offset_datetime); TOML11_TEST_FIND_OR_FALLBACK(table, array ); } #undef TOML11_TEST_FIND_OR_FALLBACK TEST_CASE("testing find_or; converting to an integer") { { toml::value v = toml::table{{"num", 42}}; CHECK_EQ(42u, toml::find_or(v, "num", 0u)); CHECK_EQ(0u , toml::find_or(v, "foo", 0u)); } { toml::value v = toml::table{{"num", 42}}; const auto moved = toml::find_or(std::move(v), "num", 0u); CHECK_EQ(42u, moved); } { toml::value v = toml::table{{"num", 42}}; const auto moved = toml::find_or(std::move(v), "foo", 0u); CHECK_EQ(0u, moved); } } TEST_CASE("testing find_or; converting to a floating") { { toml::value v1 = toml::table{{"key", 42}}; toml::value v2 = toml::table{{"key", 3.14}}; CHECK_EQ(2.71f, toml::find_or(v1, "key", 2.71f)); const double ref(3.14); CHECK_EQ(static_cast(ref), toml::find_or(v2, "key", 2.71f)); } { toml::value v1 = toml::table{{"key", 42}}; toml::value v2 = toml::table{{"key", 3.14}}; const auto moved1 = toml::find_or(std::move(v1), "key", 2.71f); const auto moved2 = toml::find_or(std::move(v2), "key", 2.71f); CHECK_EQ(2.71f, moved1); const double ref(3.14); CHECK_EQ(static_cast(ref), moved2); } } TEST_CASE("testing find_or; converting to a string") { { toml::value v1 = toml::table{{"key", "foobar"}}; toml::value v2 = toml::table{{"key", 42}}; std::string s1("bazqux"); const std::string s2("bazqux"); CHECK_EQ("foobar", toml::find_or(v1, "key", s1)); CHECK_EQ("bazqux", toml::find_or(v2, "key", s1)); std::string& v1r = toml::find_or(v1, "key", s1); std::string& s1r = toml::find_or(v2, "key", s1); CHECK_EQ("foobar", v1r); CHECK_EQ("bazqux", s1r); CHECK_EQ("foobar", toml::find_or(v1, "key", s2)); CHECK_EQ("bazqux", toml::find_or(v2, "key", s2)); CHECK_EQ("foobar", toml::find_or(std::move(v1), "key", std::move(s1))); s1 = "bazqux"; // restoring moved value CHECK_EQ("bazqux", toml::find_or(std::move(v2), "key", std::move(s1))); } { toml::value v1 = toml::table{{"key", "foobar"}}; toml::value v2 = toml::table{{"key", 42}}; std::string s1("bazqux"); const auto moved1 = toml::find_or(std::move(v1), "key", s1); const auto moved2 = toml::find_or(std::move(v2), "key", s1); CHECK_EQ("foobar", moved1); CHECK_EQ("bazqux", moved2); } { toml::value v1 = toml::table{{"key", "foobar"}}; toml::value v2 = toml::table{{"key", 42}}; std::string s1("bazqux"); std::string s2("bazqux"); const auto moved1 = toml::find_or(std::move(v1), "key", std::move(s1)); const auto moved2 = toml::find_or(std::move(v2), "key", std::move(s2)); CHECK_EQ("foobar", moved1); CHECK_EQ("bazqux", moved2); } // string literal { toml::value v1 = toml::table{{"key", "foobar"}}; toml::value v2 = toml::table{{"key",42}}; CHECK_EQ("foobar", toml::find_or(v1, "key", "bazqux")); CHECK_EQ("bazqux", toml::find_or(v2, "key", "bazqux")); const auto lit = "bazqux"; CHECK_EQ("foobar", toml::find_or(v1, "key", lit)); CHECK_EQ("bazqux", toml::find_or(v2, "key", lit)); } { toml::value v1 = toml::table{{"key", "foobar"}}; toml::value v2 = toml::table{{"key",42}}; const auto moved1 = toml::find_or(std::move(v1), "key", "bazqux"); const auto moved2 = toml::find_or(std::move(v2), "key", "bazqux"); CHECK_EQ("foobar", moved1); CHECK_EQ("bazqux", moved2); } { toml::value v1 = toml::table{{"key", "foobar"}}; toml::value v2 = toml::table{{"key",42}}; const char* lit = "bazqux"; const auto moved1 = toml::find_or(std::move(v1), "key", lit); const auto moved2 = toml::find_or(std::move(v2), "key", lit); CHECK_EQ("foobar", moved1); CHECK_EQ("bazqux", moved2); } } TEST_CASE("testing find_or; converting to a map") { using map_type = std::map; { const toml::value v1 = toml::table{ {"key", toml::table{{"key", "value"}}} }; const auto key = toml::find_or(v1, "key", map_type{}); const auto key2 = toml::find_or(v1, "key2", map_type{}); CHECK_UNARY(!key.empty()); CHECK_UNARY(key2.empty()); CHECK_EQ(key.size() , 1u); CHECK_EQ(key.at("key"), "value"); } { toml::value v1 = toml::table{ {"key", toml::table{{"key", "value"}}} }; const auto key = toml::find_or(v1, "key", map_type{}); const auto key2 = toml::find_or(v1, "key2", map_type{}); CHECK_UNARY(!key.empty()); CHECK_UNARY(key2.empty()); CHECK_EQ(key.size() , 1u); CHECK_EQ(key.at("key"), "value"); } { toml::value v1 = toml::table{ {"key", toml::table{{"key", "value"}}} }; toml::value v2(v1); const auto key = toml::find_or(std::move(v1), "key", map_type{}); const auto key2 = toml::find_or(std::move(v2), "key2", map_type{}); CHECK_UNARY(!key.empty()); CHECK_UNARY(key2.empty()); CHECK_EQ(key.size() , 1u); CHECK_EQ(key.at("key"), "value"); } { toml::value v1 = toml::table{ {"key", toml::table{{"key", "value"}}} }; toml::value v2(v1); const auto key = toml::find_or(std::move(v1), "key", map_type{}); const auto key2 = toml::find_or(std::move(v2), "key2", map_type{}); CHECK_UNARY(!key.empty()); CHECK_UNARY(key2.empty()); CHECK_EQ(key.size() , 1u); CHECK_EQ(key.at("key"), "value"); } } TEST_CASE("testing find_or(val, keys..., opt)") { // &, using type deduction { toml::value v( toml::table{ {"foo", toml::table{ {"bar", toml::table{ {"baz", "qux" } } } } } } ); std::string opt("hoge"); auto& v1 = toml::find_or(v, "foo", "bar", "baz", opt); auto& v2 = toml::find_or(v, "foo", "bar", "qux", opt); CHECK_EQ(v1, "qux"); CHECK_EQ(v2, "hoge"); v1 = "hoge"; v2 = "fuga"; CHECK_EQ(v1, "hoge"); CHECK_EQ(v2, "fuga"); } // const&, type deduction { const toml::value v( toml::table{ {"foo", toml::table{ {"bar", toml::table{ {"baz", "qux" } } } } } } ); std::string opt("hoge"); const auto& v1 = toml::find_or(v, "foo", "bar", "baz", opt); const auto& v2 = toml::find_or(v, "foo", "bar", "qux", opt); CHECK_EQ(v1, "qux"); CHECK_EQ(v2, "hoge"); } // explicitly specify type, doing type conversion { const toml::value v( toml::table{ {"foo", toml::table{ {"bar", toml::table{ {"baz", 42 } } } } } } ); int opt = 6 * 9; auto v1 = toml::find_or(v, "foo", "bar", "baz", opt); auto v2 = toml::find_or(v, "foo", "bar", "qux", opt); CHECK_EQ(v1, 42); CHECK_EQ(v2, 6*9); } { const toml::value v( toml::table{ {"foo", toml::table{ {"bar", toml::table{ {"baz", 42 } } } } } } ); auto v1 = toml::find_or(v, "foo", "bar", "baz", "hoge"); CHECK_EQ(v1, "hoge"); } } toml11-4.1.0/tests/test_format_floating.cpp000066400000000000000000000147111464712047700207310ustar00rootroot00000000000000#define DOCTEST_CONFIG_IMPLEMENT_WITH_MAIN #include "doctest.h" #include #include #include TEST_CASE("testing fractional float") { auto fmt = [](std::size_t prec) { toml::floating_format_info f; f.fmt = toml::floating_format::fixed; f.prec = prec; return f; }; auto format_as = [](const double f, int w) -> std::string { std::ostringstream oss; oss.imbue(std::locale::classic()); oss << std::fixed << std::setprecision(w) << f; return oss.str(); }; CHECK_EQ(format_as( 1.0 , 1), toml::format(toml::value( 1.0 , fmt( 1)))); CHECK_EQ(format_as( 0.1 , 1), toml::format(toml::value( 0.1 , fmt( 1)))); CHECK_EQ(format_as( 0.001 , 3), toml::format(toml::value( 0.001 , fmt( 3)))); CHECK_EQ(format_as( 0.1 , 3), toml::format(toml::value( 0.1 , fmt( 3)))); CHECK_EQ(format_as( 3.14 , 2), toml::format(toml::value( 3.14 , fmt( 2)))); CHECK_EQ(format_as(-3.14 , 2), toml::format(toml::value(-3.14 , fmt( 2)))); CHECK_EQ(format_as( 3.141592653589, 12), toml::format(toml::value( 3.141592653589, fmt(12)))); CHECK_EQ(format_as(-3.141592653589, 12), toml::format(toml::value(-3.141592653589, fmt(12)))); } TEST_CASE("testing scientific float") { auto fmt = [](std::size_t prec) { toml::floating_format_info f; f.fmt = toml::floating_format::scientific; f.prec = prec; return f; }; auto format_as = [](const double f, int w) -> std::string { std::ostringstream oss; oss.imbue(std::locale::classic()); oss << std::scientific << std::setprecision(w) << f; return oss.str(); }; CHECK_EQ(format_as( 1.0 , 1), toml::format(toml::value( 1.0 , fmt( 1)))); CHECK_EQ(format_as( 0.1 , 1), toml::format(toml::value( 0.1 , fmt( 1)))); CHECK_EQ(format_as( 0.001 , 3), toml::format(toml::value( 0.001 , fmt( 3)))); CHECK_EQ(format_as( 0.1 , 3), toml::format(toml::value( 0.1 , fmt( 3)))); CHECK_EQ(format_as( 3.14 , 2), toml::format(toml::value( 3.14 , fmt( 2)))); CHECK_EQ(format_as(-3.14 , 2), toml::format(toml::value(-3.14 , fmt( 2)))); CHECK_EQ(format_as( 3.141592653589, 12), toml::format(toml::value( 3.141592653589, fmt(12)))); CHECK_EQ(format_as(-3.141592653589, 12), toml::format(toml::value(-3.141592653589, fmt(12)))); } TEST_CASE("testing hex float") { toml::spec s = toml::spec::v(1,0,0); s.ext_hex_float = true; toml::floating_format_info fmt; fmt.fmt = toml::floating_format::hex; auto format_as = [](const double f) -> std::string { std::ostringstream oss; oss.imbue(std::locale::classic()); oss << std::hexfloat << f; return oss.str(); }; CHECK_EQ(format_as( 1.0 ), toml::format(toml::value( 1.0 , fmt), s)); CHECK_EQ(format_as( 0.1 ), toml::format(toml::value( 0.1 , fmt), s)); CHECK_EQ(format_as( 0.001 ), toml::format(toml::value( 0.001 , fmt), s)); CHECK_EQ(format_as( 0.1 ), toml::format(toml::value( 0.1 , fmt), s)); CHECK_EQ(format_as( 3.14 ), toml::format(toml::value( 3.14 , fmt), s)); CHECK_EQ(format_as(-3.14 ), toml::format(toml::value(-3.14 , fmt), s)); CHECK_EQ(format_as( 3.141592653589), toml::format(toml::value( 3.141592653589, fmt), s)); CHECK_EQ(format_as(-3.141592653589), toml::format(toml::value(-3.141592653589, fmt), s)); } TEST_CASE("testing suffix + fractional") { toml::spec s = toml::spec::v(1,0,0); s.ext_hex_float = true; s.ext_num_suffix = true; auto fmt = [](std::string sfx) { toml::floating_format_info f; f.fmt = toml::floating_format::fixed; f.suffix = sfx; return f; }; auto format_as = [](const double f, std::string sfx) -> std::string { std::ostringstream oss; oss.imbue(std::locale::classic()); oss << std::fixed << f << "_" << sfx; return oss.str(); }; CHECK_EQ(format_as( 1.0 , "m" ), toml::format(toml::value( 1.0 , fmt("m" )), s)); CHECK_EQ(format_as( 0.1 , "m" ), toml::format(toml::value( 0.1 , fmt("m" )), s)); CHECK_EQ(format_as( 0.001 , "mm" ), toml::format(toml::value( 0.001 , fmt("mm" )), s)); CHECK_EQ(format_as( 0.1 , "mm" ), toml::format(toml::value( 0.1 , fmt("mm" )), s)); CHECK_EQ(format_as( 3.14 , "rad"), toml::format(toml::value( 3.14 , fmt("rad")), s)); CHECK_EQ(format_as(-3.14 , "rad"), toml::format(toml::value(-3.14 , fmt("rad")), s)); CHECK_EQ(format_as( 3.141592653589, "rad"), toml::format(toml::value( 3.141592653589, fmt("rad")), s)); CHECK_EQ(format_as(-3.141592653589, "rad"), toml::format(toml::value(-3.141592653589, fmt("rad")), s)); } TEST_CASE("testing suffix + scientific") { toml::spec s = toml::spec::v(1,0,0); s.ext_hex_float = true; s.ext_num_suffix = true; auto fmt = [](std::string sfx) { toml::floating_format_info f; f.fmt = toml::floating_format::scientific; f.suffix = sfx; return f; }; auto format_as = [](const double f, std::string sfx) -> std::string { std::ostringstream oss; oss.imbue(std::locale::classic()); oss << std::scientific << f << "_" << sfx; return oss.str(); }; CHECK_EQ(format_as( 1.0 , "m" ), toml::format(toml::value( 1.0 , fmt("m" )), s)); CHECK_EQ(format_as( 0.1 , "m" ), toml::format(toml::value( 0.1 , fmt("m" )), s)); CHECK_EQ(format_as( 0.001 , "mm" ), toml::format(toml::value( 0.001 , fmt("mm" )), s)); CHECK_EQ(format_as( 0.1 , "mm" ), toml::format(toml::value( 0.1 , fmt("mm" )), s)); CHECK_EQ(format_as( 3.14 , "rad"), toml::format(toml::value( 3.14 , fmt("rad")), s)); CHECK_EQ(format_as(-3.14 , "rad"), toml::format(toml::value(-3.14 , fmt("rad")), s)); CHECK_EQ(format_as( 3.141592653589, "rad"), toml::format(toml::value( 3.141592653589, fmt("rad")), s)); CHECK_EQ(format_as(-3.141592653589, "rad"), toml::format(toml::value(-3.141592653589, fmt("rad")), s)); } toml11-4.1.0/tests/test_format_integer.cpp000066400000000000000000000105721464712047700205640ustar00rootroot00000000000000#define DOCTEST_CONFIG_IMPLEMENT_WITH_MAIN #include "doctest.h" #include #include #include #include TEST_CASE("testing decimal") { const auto decimal_fmt = [](std::size_t w, std::size_t s) { toml::integer_format_info fmt; fmt.fmt = toml::integer_format::dec; fmt.width = w; fmt.spacer = s; return fmt; }; CHECK_EQ( "0", toml::format(toml::value( 0, decimal_fmt(1, 0)))); CHECK_EQ( "1234", toml::format(toml::value( 1234, decimal_fmt(0, 0)))); CHECK_EQ( "-1234", toml::format(toml::value( -1234, decimal_fmt(0, 0)))); CHECK_EQ( "1_2_3_4", toml::format(toml::value( 1234, decimal_fmt(4, 1)))); CHECK_EQ( "-1_2_3_4", toml::format(toml::value( -1234, decimal_fmt(5, 1)))); CHECK_EQ("123_456_789", toml::format(toml::value(123456789, decimal_fmt(9, 3)))); } TEST_CASE("testing hex") { const auto hex_fmt = [](bool u, std::size_t w, std::size_t s) { toml::integer_format_info fmt; fmt.fmt = toml::integer_format::hex; fmt.uppercase = u; fmt.width = w; fmt.spacer = s; return fmt; }; CHECK_EQ("0xdeadbeef", toml::format(toml::value(0xDEADBEEF, hex_fmt(false, 8, 0)))); CHECK_EQ("0xdead_beef", toml::format(toml::value(0xDEADBEEF, hex_fmt(false, 8, 4)))); CHECK_EQ("0xff", toml::format(toml::value(0xFF, hex_fmt(false, 2, 0)))); CHECK_EQ("0x00ff", toml::format(toml::value(0xFF, hex_fmt(false, 4, 0)))); CHECK_EQ("0x0000ff", toml::format(toml::value(0xFF, hex_fmt(false, 6, 0)))); CHECK_EQ("0xDEADBEEF", toml::format(toml::value(0xDEADBEEF, hex_fmt(true, 8, 0)))); CHECK_EQ("0xDEAD_BEEF", toml::format(toml::value(0xDEADBEEF, hex_fmt(true, 8, 4)))); CHECK_EQ("0xFF", toml::format(toml::value(0xFF, hex_fmt(true, 2, 0)))); CHECK_EQ("0x00FF", toml::format(toml::value(0xFF, hex_fmt(true, 4, 0)))); CHECK_EQ("0x0000FF", toml::format(toml::value(0xFF, hex_fmt(true, 6, 0)))); using namespace toml::literals::toml_literals; CHECK_EQ("0xdeadbeef", toml::format("0xdeadbeef"_toml )); CHECK_EQ("0xdead_beef", toml::format("0xdead_beef"_toml)); CHECK_EQ("0xff", toml::format("0xff"_toml )); CHECK_EQ("0x00ff", toml::format("0x00ff"_toml )); CHECK_EQ("0x0000ff", toml::format("0x0000ff"_toml )); CHECK_EQ("0xDEADBEEF", toml::format("0xDEADBEEF"_toml )); CHECK_EQ("0xDEAD_BEEF", toml::format("0xDEAD_BEEF"_toml)); CHECK_EQ("0xFF", toml::format("0xFF"_toml )); CHECK_EQ("0x00FF", toml::format("0x00FF"_toml )); CHECK_EQ("0x0000FF", toml::format("0x0000FF"_toml )); } TEST_CASE("testing oct") { const auto oct_fmt = [](std::size_t w, std::size_t s) { toml::integer_format_info fmt; fmt.fmt = toml::integer_format::oct; fmt.width = w; fmt.spacer = s; return fmt; }; CHECK_EQ("0o644", toml::format(toml::value(64*6+8*4+4, oct_fmt(3, 0)))); CHECK_EQ("0o7_7_7", toml::format(toml::value(64*7+8*7+7, oct_fmt(3, 1)))); CHECK_EQ("0o000644", toml::format(toml::value(64*6+8*4+4, oct_fmt(6, 0)))); } TEST_CASE("testing bin") { const auto bin_fmt = [](std::size_t w, std::size_t s) { toml::integer_format_info fmt; fmt.fmt = toml::integer_format::bin; fmt.width = w; fmt.spacer = s; return fmt; }; CHECK_EQ("0b1000", toml::format(toml::value(8, bin_fmt(4, 0)))); CHECK_EQ("0b00001000", toml::format(toml::value(8, bin_fmt(8, 0)))); CHECK_EQ("0b0000_1000", toml::format(toml::value(8, bin_fmt(8, 4)))); } TEST_CASE("testing decimal with suffix") { toml::spec sp = toml::spec::v(1,0,0); sp.ext_num_suffix = true; const auto decimal_fmt = [](std::size_t w, std::size_t s, std::string sfx) { toml::integer_format_info fmt; fmt.fmt = toml::integer_format::dec; fmt.width = w; fmt.spacer = s; fmt.suffix = sfx; return fmt; }; CHECK_EQ( "0_J", toml::format(toml::value( 0, decimal_fmt(1, 0, "J")), sp)); CHECK_EQ( "1234_kcal", toml::format(toml::value( 1234, decimal_fmt(0, 0, "kcal")), sp)); CHECK_EQ("1_2_3_4_μm", toml::format(toml::value( 1234, decimal_fmt(4, 1, "μm")), sp)); } toml11-4.1.0/tests/test_format_table.cpp000066400000000000000000000047231464712047700202170ustar00rootroot00000000000000#define DOCTEST_CONFIG_IMPLEMENT_WITH_MAIN #include "doctest.h" #include #include #include TEST_CASE("testing multiline table") { const auto table_fmt = [](toml::indent_char c, std::int32_t body, std::int32_t name) { toml::table_format_info fmt; fmt.fmt = toml::table_format::multiline; fmt.indent_type = c; fmt.body_indent = body; fmt.name_indent = name; fmt.closing_indent = 0; return fmt; }; toml::value v(toml::table{ {"a", 42}, {"b", 3.14}, {"c", "foobar"}, }); v.as_table_fmt() = table_fmt(toml::indent_char::space, 0, 0); CHECK_EQ(v, toml::parse_str(toml::format(v))); v.as_table_fmt() = table_fmt(toml::indent_char::space, 0, 2); CHECK_EQ(v, toml::parse_str(toml::format(v))); v.as_table_fmt() = table_fmt(toml::indent_char::space, 2, 0); CHECK_EQ(v, toml::parse_str(toml::format(v))); v.as_table_fmt() = table_fmt(toml::indent_char::space, 2, 2); CHECK_EQ(v, toml::parse_str(toml::format(v))); v.as_table_fmt() = table_fmt(toml::indent_char::tab, 0, 0); CHECK_EQ(v, toml::parse_str(toml::format(v))); v.as_table_fmt() = table_fmt(toml::indent_char::tab, 0, 2); CHECK_EQ(v, toml::parse_str(toml::format(v))); v.as_table_fmt() = table_fmt(toml::indent_char::tab, 2, 0); CHECK_EQ(v, toml::parse_str(toml::format(v))); v.as_table_fmt() = table_fmt(toml::indent_char::tab, 2, 2); CHECK_EQ(v, toml::parse_str(toml::format(v))); } TEST_CASE("testing multiline table") { const auto table_fmt = [](toml::indent_char c, std::int32_t body, std::int32_t name) { toml::table_format_info fmt; fmt.fmt = toml::table_format::dotted; fmt.indent_type = c; fmt.body_indent = body; fmt.name_indent = name; fmt.closing_indent = 0; return fmt; }; { toml::value v(toml::table{}); v["a"]["b"]["c"] = "foobar"; CHECK_EQ(v, toml::parse_str(toml::format(v))); v.at("a").as_table_fmt() = table_fmt(toml::indent_char::space, 0, 0); CHECK_EQ(v, toml::parse_str(toml::format(v))); v.at("a").at("b").as_table_fmt() = table_fmt(toml::indent_char::space, 0, 0); CHECK_EQ(v, toml::parse_str(toml::format(v))); v.at("a").as_table_fmt().fmt = toml::table_format::multiline; CHECK_EQ(v, toml::parse_str(toml::format(v))); } } toml11-4.1.0/tests/test_get.cpp000066400000000000000000000437051464712047700163420ustar00rootroot00000000000000#define DOCTEST_CONFIG_IMPLEMENT_WITH_MAIN #include "doctest.h" #include "utility.hpp" #include #include #include #include #include #include #include #include #if TOML11_CPLUSPLUS_STANDARD_VERSION >= 201703L #include #endif namespace toml { namespace detail { std::tm localtime_s(const std::time_t* src); std::tm gmtime_s(const std::time_t* src); } // detail } // toml TEST_CASE("testing toml::get with toml types") { using value_type = toml::value; using boolean_type = typename value_type::boolean_type ; using integer_type = typename value_type::integer_type ; using floating_type = typename value_type::floating_type ; using string_type = typename value_type::string_type ; using local_time_type = typename value_type::local_time_type ; using local_date_type = typename value_type::local_date_type ; using local_datetime_type = typename value_type::local_datetime_type ; using offset_datetime_type = typename value_type::offset_datetime_type; using array_type = typename value_type::array_type ; using table_type = typename value_type::table_type ; { value_type v(true); CHECK_EQ(true, toml::get(v)); toml::get(v) = false; CHECK_EQ(false, toml::get(v)); boolean_type x = toml::get(std::move(v)); CHECK_EQ(false, x); } { value_type v(42); CHECK_EQ(integer_type(42), toml::get(v)); toml::get(v) = 54; CHECK_EQ(integer_type(54), toml::get(v)); integer_type x = toml::get(std::move(v)); CHECK_EQ(integer_type(54), x); } { value_type v(3.14); CHECK_EQ(floating_type(3.14), toml::get(v)); toml::get(v) = 2.71; CHECK_EQ(floating_type(2.71), toml::get(v)); floating_type x = toml::get(std::move(v)); CHECK_EQ(floating_type(2.71), x); } { value_type v("foo"); CHECK_EQ("foo", toml::get(v)); toml::get(v) += "bar"; CHECK_EQ("foobar", toml::get(v)); string_type x = toml::get(std::move(v)); CHECK_EQ("foobar", x); } { local_date_type d(2018, toml::month_t::Apr, 22); value_type v(d); CHECK_EQ(d, toml::get(v)); toml::get(v).year = 2017; d.year = 2017; CHECK_EQ(d, toml::get(v)); local_date_type x = toml::get(std::move(v)); CHECK_EQ(d, x); } { local_time_type t(12, 30, 45); value_type v(t); CHECK_EQ(t, toml::get(v)); toml::get(v).hour = 9; t.hour = 9; CHECK_EQ(t, toml::get(v)); local_time_type x = toml::get(std::move(v)); CHECK_EQ(t, x); } { local_datetime_type dt(toml::local_date(2018, toml::month_t::Apr, 22), toml::local_time(12, 30, 45)); value_type v(dt); CHECK_EQ(dt, toml::get(v)); toml::get(v).date.year = 2017; dt.date.year = 2017; CHECK_EQ(dt, toml::get(v)); toml::local_datetime x = toml::get(std::move(v)); CHECK_EQ(dt, x); } { offset_datetime_type dt(toml::local_datetime( toml::local_date(2018, toml::month_t::Apr, 22), toml::local_time(12, 30, 45)), toml::time_offset(9, 0)); value_type v(dt); CHECK_EQ(dt, toml::get(v)); toml::get(v).date.year = 2017; dt.date.year = 2017; CHECK_EQ(dt, toml::get(v)); offset_datetime_type x = toml::get(std::move(v)); CHECK_EQ(dt, x); } { array_type vec; vec.push_back(value_type(42)); vec.push_back(value_type(54)); value_type v(vec); CHECK_EQ(vec, toml::get(v)); toml::get(v).push_back(value_type(123)); vec.push_back(value_type(123)); CHECK_EQ(vec, toml::get(v)); array_type x = toml::get(std::move(v)); CHECK_EQ(vec, x); } { table_type tab; tab["key1"] = value_type(42); tab["key2"] = value_type(3.14); value_type v(tab); CHECK_EQ(tab, toml::get(v)); toml::get(v)["key3"] = value_type(123); tab["key3"] = value_type(123); CHECK_EQ(tab, toml::get(v)); table_type x = toml::get(std::move(v)); CHECK_EQ(tab, x); } { value_type v1(42); CHECK_EQ(v1, toml::get(v1)); value_type v2(54); toml::get(v1) = v2; CHECK_EQ(v2, toml::get(v1)); value_type x = toml::get(std::move(v1)); CHECK_EQ(v2, x); } } TEST_CASE("testing toml::get") { using value_type = toml::value; { value_type v(42); CHECK_EQ(int(42), toml::get(v)); CHECK_EQ(short(42), toml::get(v)); CHECK_EQ(char(42), toml::get(v)); CHECK_EQ(unsigned(42), toml::get(v)); CHECK_EQ(long(42), toml::get(v)); CHECK_EQ(std::int64_t(42), toml::get(v)); CHECK_EQ(std::uint64_t(42), toml::get(v)); CHECK_EQ(std::int16_t(42), toml::get(v)); CHECK_EQ(std::uint16_t(42), toml::get(v)); CHECK_EQ(int(42), toml::get(as_const(v))); CHECK_EQ(short(42), toml::get(as_const(v))); CHECK_EQ(char(42), toml::get(as_const(v))); CHECK_EQ(unsigned(42), toml::get(as_const(v))); CHECK_EQ(long(42), toml::get(as_const(v))); CHECK_EQ(std::int64_t(42), toml::get(as_const(v))); CHECK_EQ(std::uint64_t(42), toml::get(as_const(v))); CHECK_EQ(std::int16_t(42), toml::get(as_const(v))); CHECK_EQ(std::uint16_t(42), toml::get(as_const(v))); value_type v1(v); value_type v2(v); value_type v3(v); value_type v4(v); value_type v5(v); value_type v6(v); value_type v7(v); value_type v8(v); value_type v9(v); CHECK_EQ(int(42), toml::get(v1)); CHECK_EQ(short(42), toml::get(v2)); CHECK_EQ(char(42), toml::get(v3)); CHECK_EQ(unsigned(42), toml::get(v4)); CHECK_EQ(long(42), toml::get(v5)); CHECK_EQ(std::int64_t(42), toml::get(v6)); CHECK_EQ(std::uint64_t(42), toml::get(v7)); CHECK_EQ(std::int16_t(42), toml::get(v8)); CHECK_EQ(std::uint16_t(42), toml::get(v9)); } } TEST_CASE("testing toml::get") { using value_type = toml::value; { const double ref(3.14); value_type v(ref); CHECK_EQ(static_cast(ref), toml::get(v)); CHECK_EQ( ref , toml::get(v)); CHECK_EQ(static_cast(ref), toml::get(v)); value_type v1(ref); value_type v2(ref); value_type v3(ref); CHECK_EQ(static_cast(ref), toml::get(std::move(v1))); CHECK_EQ( ref , toml::get(std::move(v2))); CHECK_EQ(static_cast(ref), toml::get(std::move(v3))); } } #if defined(TOML11_HAS_CHAR8_T) TEST_CASE("testing toml::get") { using value_type = toml::value; { value_type v("foo"); CHECK_EQ(u8"foo", toml::get(v)); } } #endif #if TOML11_CPLUSPLUS_STANDARD_VERSION >= 201703L TEST_CASE("testing toml::get") { using value_type = toml::value; { value_type v("foo"); CHECK_EQ("foo", toml::get(v)); } } #endif TEST_CASE("testing toml::get") { using value_type = toml::value; { const value_type v(toml::array{42, 54, 69, 72}); const std::vector vec = toml::get>(v); const std::list lst = toml::get>(v); const std::deque deq = toml::get>(v); CHECK_EQ(42, vec.at(0)); CHECK_EQ(54, vec.at(1)); CHECK_EQ(69, vec.at(2)); CHECK_EQ(72, vec.at(3)); std::list::const_iterator iter = lst.begin(); CHECK_EQ(static_cast(42), *(iter++)); CHECK_EQ(static_cast(54), *(iter++)); CHECK_EQ(static_cast(69), *(iter++)); CHECK_EQ(static_cast(72), *(iter++)); CHECK_EQ(static_cast(42), deq.at(0)); CHECK_EQ(static_cast(54), deq.at(1)); CHECK_EQ(static_cast(69), deq.at(2)); CHECK_EQ(static_cast(72), deq.at(3)); std::array ary = toml::get>(v); CHECK_EQ(42, ary.at(0)); CHECK_EQ(54, ary.at(1)); CHECK_EQ(69, ary.at(2)); CHECK_EQ(72, ary.at(3)); std::tuple tpl = toml::get>(v); CHECK_EQ( 42 , std::get<0>(tpl)); CHECK_EQ(static_cast(54), std::get<1>(tpl)); CHECK_EQ(static_cast(69), std::get<2>(tpl)); CHECK_EQ(static_cast(72), std::get<3>(tpl)); const value_type p(toml::array{3.14, 2.71}); std::pair pr = toml::get >(p); CHECK_EQ(3.14, pr.first); CHECK_EQ(2.71, pr.second); } { value_type v(toml::array{42, 54, 69, 72}); const std::vector vec = toml::get>(std::move(v)); CHECK_EQ(42, vec.at(0)); CHECK_EQ(54, vec.at(1)); CHECK_EQ(69, vec.at(2)); CHECK_EQ(72, vec.at(3)); } { value_type v(toml::array{42, 54, 69, 72}); const std::deque deq = toml::get>(std::move(v)); CHECK_EQ(42, deq.at(0)); CHECK_EQ(54, deq.at(1)); CHECK_EQ(69, deq.at(2)); CHECK_EQ(72, deq.at(3)); } { value_type v(toml::array{42, 54, 69, 72}); const std::list lst = toml::get>(std::move(v)); std::list::const_iterator iter = lst.begin(); CHECK_EQ(42, *(iter++)); CHECK_EQ(54, *(iter++)); CHECK_EQ(69, *(iter++)); CHECK_EQ(72, *(iter++)); } { value_type v(toml::array{42, 54, 69, 72}); std::array ary = toml::get>(std::move(v)); CHECK_EQ(42, ary.at(0)); CHECK_EQ(54, ary.at(1)); CHECK_EQ(69, ary.at(2)); CHECK_EQ(72, ary.at(3)); } { value_type v(toml::array{42, 54, 69, 72}); std::tuple tpl = toml::get>(std::move(v)); CHECK_EQ( 42 , std::get<0>(tpl)); CHECK_EQ(static_cast(54), std::get<1>(tpl)); CHECK_EQ(static_cast(69), std::get<2>(tpl)); CHECK_EQ(static_cast(72), std::get<3>(tpl)); } } TEST_CASE("testing toml::get") { using value_type = toml::value; { const value_type v1(toml::array{42, 54, 69, 72}); const value_type v2(toml::array{"foo", "bar", "baz"}); const value_type v (toml::array{v1, v2}); std::pair, std::vector> p = toml::get, std::vector>>(v); CHECK_EQ(p.first.size(), 4u); CHECK_EQ(p.first.at(0), 42); CHECK_EQ(p.first.at(1), 54); CHECK_EQ(p.first.at(2), 69); CHECK_EQ(p.first.at(3), 72); CHECK_EQ(p.second.size(), 3u); CHECK_EQ(p.second.at(0), "foo"); CHECK_EQ(p.second.at(1), "bar"); CHECK_EQ(p.second.at(2), "baz"); std::tuple, std::vector> t = toml::get, std::vector>>(v); CHECK_EQ(std::get<0>(t).at(0), 42); CHECK_EQ(std::get<0>(t).at(1), 54); CHECK_EQ(std::get<0>(t).at(2), 69); CHECK_EQ(std::get<0>(t).at(3), 72); CHECK_EQ(std::get<1>(t).at(0), "foo"); CHECK_EQ(std::get<1>(t).at(1), "bar"); CHECK_EQ(std::get<1>(t).at(2), "baz"); } { const value_type v1(toml::array{42, 54, 69, 72}); const value_type v2(toml::array{"foo", "bar", "baz"}); value_type v (toml::array{v1, v2}); std::pair, std::vector> p = toml::get, std::vector>>(std::move(v)); CHECK_EQ(p.first.size(), 4u); CHECK_EQ(p.first.at(0), 42); CHECK_EQ(p.first.at(1), 54); CHECK_EQ(p.first.at(2), 69); CHECK_EQ(p.first.at(3), 72); CHECK_EQ(p.second.size(), 3u); CHECK_EQ(p.second.at(0), "foo"); CHECK_EQ(p.second.at(1), "bar"); CHECK_EQ(p.second.at(2), "baz"); } } TEST_CASE("testing toml::get") { using value_type = toml::value; { const value_type v1(toml::table{ {"key1", 1}, {"key2", 2}, {"key3", 3}, {"key4", 4} }); const auto v = toml::get>(v1); CHECK_EQ(v.at("key1"), 1); CHECK_EQ(v.at("key2"), 2); CHECK_EQ(v.at("key3"), 3); CHECK_EQ(v.at("key4"), 4); } { value_type v1(toml::table{ {"key1", 1}, {"key2", 2}, {"key3", 3}, {"key4", 4} }); const auto v = toml::get>(std::move(v1)); CHECK_EQ(v.at("key1"), 1); CHECK_EQ(v.at("key2"), 2); CHECK_EQ(v.at("key3"), 3); CHECK_EQ(v.at("key4"), 4); } } TEST_CASE("testing toml::get(local_date)") { using value_type = toml::value; value_type v1(toml::local_date{2018, toml::month_t::Apr, 1}); const auto date = std::chrono::system_clock::to_time_t( toml::get(v1)); std::tm t; t.tm_year = 2018 - 1900; t.tm_mon = 4 - 1; t.tm_mday = 1; t.tm_hour = 0; t.tm_min = 0; t.tm_sec = 0; t.tm_isdst = -1; const auto c = std::mktime(&t); CHECK_EQ(c, date); } TEST_CASE("testing toml::get") { using value_type = toml::value; value_type v1(toml::local_time{12, 30, 45}); const auto time = toml::get(v1); const bool result = time == std::chrono::hours(12) + std::chrono::minutes(30) + std::chrono::seconds(45); CHECK_UNARY(result); } TEST_CASE("testing toml::get(local_datetime)") { using value_type = toml::value; value_type v1(toml::local_datetime( toml::local_date{2018, toml::month_t::Apr, 1}, toml::local_time{12, 30, 45})); const auto date = std::chrono::system_clock::to_time_t( toml::get(v1)); std::tm t; t.tm_year = 2018 - 1900; t.tm_mon = 4 - 1; t.tm_mday = 1; t.tm_hour = 12; t.tm_min = 30; t.tm_sec = 45; t.tm_isdst = -1; const auto c = std::mktime(&t); CHECK_EQ(c, date); } TEST_CASE("testing toml::get(offset_datetime)") { using value_type = toml::value; { value_type v1(toml::offset_datetime( toml::local_date{2018, toml::month_t::Apr, 1}, toml::local_time{12, 30, 0}, toml::time_offset{9, 0})); // 2018-04-01T12:30:00+09:00 // == 2018-04-01T03:30:00Z const auto date = toml::get(v1); const auto timet = std::chrono::system_clock::to_time_t(date); // get time_t as gmtime (2018-04-01T03:30:00Z) const auto tm = toml::detail::gmtime_s(std::addressof(timet)); CHECK_EQ(tm.tm_year + 1900, 2018); CHECK_EQ(tm.tm_mon + 1, 4); CHECK_EQ(tm.tm_mday, 1); CHECK_EQ(tm.tm_hour, 3); CHECK_EQ(tm.tm_min, 30); CHECK_EQ(tm.tm_sec, 0); } { value_type v1(toml::offset_datetime( toml::local_date{2018, toml::month_t::Apr, 1}, toml::local_time{12, 30, 0}, toml::time_offset{-8, 0})); // 2018-04-01T12:30:00-08:00 //, 2018-04-01T20:30:00Z const auto date = toml::get(v1); const auto timet = std::chrono::system_clock::to_time_t(date); // get time_t as gmtime (2018-04-01T03:30:00Z) const auto tm = toml::detail::gmtime_s(std::addressof(timet)); CHECK_EQ(tm.tm_year + 1900, 2018); CHECK_EQ(tm.tm_mon + 1, 4); CHECK_EQ(tm.tm_mday, 1); CHECK_EQ(tm.tm_hour, 20); CHECK_EQ(tm.tm_min, 30); CHECK_EQ(tm.tm_sec, 0); } } toml11-4.1.0/tests/test_get_or.cpp000066400000000000000000000414561464712047700170430ustar00rootroot00000000000000#define DOCTEST_CONFIG_IMPLEMENT_WITH_MAIN #include "doctest.h" #include "utility.hpp" #include #include #include #include #include #include #include #include #if TOML11_CPLUSPLUS_STANDARD_VERSION >= 201703L #include #endif #define TOML11_TEST_GET_OR_EXACT(ty, init_expr, opt_expr)\ { \ const ty init init_expr ; \ const ty opt opt_expr ; \ const value_type v(init); \ CHECK_NE(init, opt); \ CHECK_EQ(init, toml::get_or(v, opt)); \ } TEST_CASE("testing get_or with exact types") { using value_type = toml::value; using boolean_type = typename value_type::boolean_type ; using integer_type = typename value_type::integer_type ; using floating_type = typename value_type::floating_type ; using string_type = typename value_type::string_type ; using local_time_type = typename value_type::local_time_type ; using local_date_type = typename value_type::local_date_type ; using local_datetime_type = typename value_type::local_datetime_type ; using offset_datetime_type = typename value_type::offset_datetime_type; using array_type = typename value_type::array_type ; using table_type = typename value_type::table_type ; TOML11_TEST_GET_OR_EXACT(boolean_type, ( true), (false)) TOML11_TEST_GET_OR_EXACT(integer_type, ( 42), ( 54)) TOML11_TEST_GET_OR_EXACT(floating_type, ( 3.14), ( 2.71)) TOML11_TEST_GET_OR_EXACT(string_type, ("foo"), ("bar")) TOML11_TEST_GET_OR_EXACT(local_time_type, (12, 30, 45), (6, 0, 30)) TOML11_TEST_GET_OR_EXACT(local_date_type, (2019, toml::month_t::Apr, 1), (1999, toml::month_t::Jan, 2)) TOML11_TEST_GET_OR_EXACT(local_datetime_type, (toml::local_date(2019, toml::month_t::Apr, 1), toml::local_time(12, 30, 45)), (toml::local_date(1999, toml::month_t::Jan, 2), toml::local_time( 6, 0, 30)) ) TOML11_TEST_GET_OR_EXACT(offset_datetime_type, (toml::local_date(2019, toml::month_t::Apr, 1), toml::local_time(12, 30, 45), toml::time_offset( 9, 0)), (toml::local_date(1999, toml::month_t::Jan, 2), toml::local_time( 6, 0, 30), toml::time_offset(-3, 0)) ) TOML11_TEST_GET_OR_EXACT(array_type, ({1,2,3,4,5}), ({6,7,8,9,10})); TOML11_TEST_GET_OR_EXACT(table_type, ({{"key1", 42}, {"key2", "foo"}}), ({{"key1", 54}, {"key2", "bar"}})); } #undef TOML11_TEST_GET_OR_EXACT #define TOML11_TEST_GET_OR_MOVE_EXACT(ty, init_expr, opt_expr) \ { \ const ty init init_expr ; \ ty opt opt_expr ; \ value_type v(init); \ CHECK_NE(init, opt); \ const auto opt_ = toml::get_or(std::move(v), std::move(opt));\ CHECK_EQ(init, opt_); \ } TEST_CASE("testing toml::get_or with moved argument") { using value_type = toml::value; using boolean_type = typename value_type::boolean_type ; using integer_type = typename value_type::integer_type ; using floating_type = typename value_type::floating_type ; using string_type = typename value_type::string_type ; using local_time_type = typename value_type::local_time_type ; using local_date_type = typename value_type::local_date_type ; using local_datetime_type = typename value_type::local_datetime_type ; using offset_datetime_type = typename value_type::offset_datetime_type; using array_type = typename value_type::array_type ; using table_type = typename value_type::table_type ; TOML11_TEST_GET_OR_MOVE_EXACT(boolean_type, ( true), (false)) TOML11_TEST_GET_OR_MOVE_EXACT(integer_type, ( 42), ( 54)) TOML11_TEST_GET_OR_MOVE_EXACT(floating_type, ( 3.14), ( 2.71)) TOML11_TEST_GET_OR_MOVE_EXACT(string_type, ("foo"), ("bar")) TOML11_TEST_GET_OR_MOVE_EXACT(local_time_type, (12, 30, 45), (6, 0, 30)) TOML11_TEST_GET_OR_MOVE_EXACT(local_date_type, (2019, toml::month_t::Apr, 1), (1999, toml::month_t::Jan, 2)) TOML11_TEST_GET_OR_MOVE_EXACT(local_datetime_type, (toml::local_date(2019, toml::month_t::Apr, 1), toml::local_time(12, 30, 45)), (toml::local_date(1999, toml::month_t::Jan, 2), toml::local_time( 6, 0, 30)) ) TOML11_TEST_GET_OR_MOVE_EXACT(offset_datetime_type, (toml::local_date(2019, toml::month_t::Apr, 1), toml::local_time(12, 30, 45), toml::time_offset( 9, 0)), (toml::local_date(1999, toml::month_t::Jan, 2), toml::local_time( 6, 0, 30), toml::time_offset(-3, 0)) ) TOML11_TEST_GET_OR_MOVE_EXACT(array_type, ({1,2,3,4,5}), ({6,7,8,9,10})); TOML11_TEST_GET_OR_MOVE_EXACT(table_type, ({{"key1", 42}, {"key2", "foo"}}), ({{"key1", 54}, {"key2", "bar"}})); } #undef TOML11_TEST_GET_OR_MOVE_EXACT #define TOML11_TEST_GET_OR_MODIFY(ty, init_expr, opt_expr)\ { \ const ty init init_expr ; \ ty opt1 opt_expr ; \ ty opt2 opt_expr ; \ value_type v(init); \ CHECK_NE(init, opt1); \ toml::get_or(v, opt2) = opt1; \ CHECK_EQ(opt1, toml::get(v)); \ } TEST_CASE("testing if get_or can modify value") { using value_type = toml::value; using boolean_type = typename value_type::boolean_type ; using integer_type = typename value_type::integer_type ; using floating_type = typename value_type::floating_type ; using string_type = typename value_type::string_type ; using local_time_type = typename value_type::local_time_type ; using local_date_type = typename value_type::local_date_type ; using local_datetime_type = typename value_type::local_datetime_type ; using offset_datetime_type = typename value_type::offset_datetime_type; using array_type = typename value_type::array_type ; using table_type = typename value_type::table_type ; TOML11_TEST_GET_OR_MODIFY(boolean_type, ( true), (false)) TOML11_TEST_GET_OR_MODIFY(integer_type, ( 42), ( 54)) TOML11_TEST_GET_OR_MODIFY(floating_type, ( 3.14), ( 2.71)) TOML11_TEST_GET_OR_MODIFY(string_type, ("foo"), ("bar")) TOML11_TEST_GET_OR_MODIFY(local_time_type, (12, 30, 45), (6, 0, 30)) TOML11_TEST_GET_OR_MODIFY(local_date_type, (2019, toml::month_t::Apr, 1), (1999, toml::month_t::Jan, 2)) TOML11_TEST_GET_OR_MODIFY(local_datetime_type, (toml::local_date(2019, toml::month_t::Apr, 1), toml::local_time(12, 30, 45)), (toml::local_date(1999, toml::month_t::Jan, 2), toml::local_time( 6, 0, 30)) ) TOML11_TEST_GET_OR_MODIFY(offset_datetime_type, (toml::local_date(2019, toml::month_t::Apr, 1), toml::local_time(12, 30, 45), toml::time_offset( 9, 0)), (toml::local_date(1999, toml::month_t::Jan, 2), toml::local_time( 6, 0, 30), toml::time_offset(-3, 0)) ) TOML11_TEST_GET_OR_MODIFY(array_type, ({1,2,3,4,5}), ({6,7,8,9,10})); TOML11_TEST_GET_OR_MODIFY(table_type, ({{"key1", 42}, {"key2", "foo"}}), ({{"key1", 54}, {"key2", "bar"}})); } #undef TOML11_TEST_GET_OR_MODIFY #define TOML11_TEST_GET_OR_FALLBACK(init_type, opt_type) \ { \ value_type v(init_type); \ CHECK_EQ(opt_type, toml::get_or(v, opt_type)); \ } TEST_CASE("testing get_or can return optional value on failure") { using value_type = toml::value; using boolean_type = typename value_type::boolean_type ; using integer_type = typename value_type::integer_type ; using floating_type = typename value_type::floating_type ; using string_type = typename value_type::string_type ; using local_time_type = typename value_type::local_time_type ; using local_date_type = typename value_type::local_date_type ; using local_datetime_type = typename value_type::local_datetime_type ; using offset_datetime_type = typename value_type::offset_datetime_type; using array_type = typename value_type::array_type ; using table_type = typename value_type::table_type ; const boolean_type boolean (true); const integer_type integer (42); const floating_type floating (3.14); const string_type string ("foo"); const local_time_type local_time (12, 30, 45); const local_date_type local_date (2019, toml::month_t::Apr, 1); const local_datetime_type local_datetime ( toml::local_date(2019, toml::month_t::Apr, 1), toml::local_time(12, 30, 45) ); const offset_datetime_type offset_datetime( toml::local_date(2019, toml::month_t::Apr, 1), toml::local_time(12, 30, 45), toml::time_offset( 9, 0)); const array_type array{1, 2, 3, 4, 5}; const table_type table{{"key1", 42}, {"key2", "foo"}}; TOML11_TEST_GET_OR_FALLBACK(boolean, integer ); TOML11_TEST_GET_OR_FALLBACK(boolean, floating ); TOML11_TEST_GET_OR_FALLBACK(boolean, string ); TOML11_TEST_GET_OR_FALLBACK(boolean, local_time ); TOML11_TEST_GET_OR_FALLBACK(boolean, local_date ); TOML11_TEST_GET_OR_FALLBACK(boolean, local_datetime ); TOML11_TEST_GET_OR_FALLBACK(boolean, offset_datetime); TOML11_TEST_GET_OR_FALLBACK(boolean, array ); TOML11_TEST_GET_OR_FALLBACK(boolean, table ); TOML11_TEST_GET_OR_FALLBACK(integer, boolean ); TOML11_TEST_GET_OR_FALLBACK(integer, floating ); TOML11_TEST_GET_OR_FALLBACK(integer, string ); TOML11_TEST_GET_OR_FALLBACK(integer, local_time ); TOML11_TEST_GET_OR_FALLBACK(integer, local_date ); TOML11_TEST_GET_OR_FALLBACK(integer, local_datetime ); TOML11_TEST_GET_OR_FALLBACK(integer, offset_datetime); TOML11_TEST_GET_OR_FALLBACK(integer, array ); TOML11_TEST_GET_OR_FALLBACK(integer, table ); TOML11_TEST_GET_OR_FALLBACK(floating, boolean ); TOML11_TEST_GET_OR_FALLBACK(floating, integer ); TOML11_TEST_GET_OR_FALLBACK(floating, string ); TOML11_TEST_GET_OR_FALLBACK(floating, local_time ); TOML11_TEST_GET_OR_FALLBACK(floating, local_date ); TOML11_TEST_GET_OR_FALLBACK(floating, local_datetime ); TOML11_TEST_GET_OR_FALLBACK(floating, offset_datetime); TOML11_TEST_GET_OR_FALLBACK(floating, array ); TOML11_TEST_GET_OR_FALLBACK(floating, table ); TOML11_TEST_GET_OR_FALLBACK(string, boolean ); TOML11_TEST_GET_OR_FALLBACK(string, integer ); TOML11_TEST_GET_OR_FALLBACK(string, floating ); TOML11_TEST_GET_OR_FALLBACK(string, local_time ); TOML11_TEST_GET_OR_FALLBACK(string, local_date ); TOML11_TEST_GET_OR_FALLBACK(string, local_datetime ); TOML11_TEST_GET_OR_FALLBACK(string, offset_datetime); TOML11_TEST_GET_OR_FALLBACK(string, array ); TOML11_TEST_GET_OR_FALLBACK(string, table ); TOML11_TEST_GET_OR_FALLBACK(local_time, boolean ); TOML11_TEST_GET_OR_FALLBACK(local_time, integer ); TOML11_TEST_GET_OR_FALLBACK(local_time, floating ); TOML11_TEST_GET_OR_FALLBACK(local_time, string ); TOML11_TEST_GET_OR_FALLBACK(local_time, local_date ); TOML11_TEST_GET_OR_FALLBACK(local_time, local_datetime ); TOML11_TEST_GET_OR_FALLBACK(local_time, offset_datetime); TOML11_TEST_GET_OR_FALLBACK(local_time, array ); TOML11_TEST_GET_OR_FALLBACK(local_time, table ); TOML11_TEST_GET_OR_FALLBACK(local_date, boolean ); TOML11_TEST_GET_OR_FALLBACK(local_date, integer ); TOML11_TEST_GET_OR_FALLBACK(local_date, floating ); TOML11_TEST_GET_OR_FALLBACK(local_date, string ); TOML11_TEST_GET_OR_FALLBACK(local_date, local_time ); TOML11_TEST_GET_OR_FALLBACK(local_date, local_datetime ); TOML11_TEST_GET_OR_FALLBACK(local_date, offset_datetime); TOML11_TEST_GET_OR_FALLBACK(local_date, array ); TOML11_TEST_GET_OR_FALLBACK(local_date, table ); TOML11_TEST_GET_OR_FALLBACK(local_datetime, boolean ); TOML11_TEST_GET_OR_FALLBACK(local_datetime, integer ); TOML11_TEST_GET_OR_FALLBACK(local_datetime, floating ); TOML11_TEST_GET_OR_FALLBACK(local_datetime, string ); TOML11_TEST_GET_OR_FALLBACK(local_datetime, local_time ); TOML11_TEST_GET_OR_FALLBACK(local_datetime, local_date ); TOML11_TEST_GET_OR_FALLBACK(local_datetime, offset_datetime); TOML11_TEST_GET_OR_FALLBACK(local_datetime, array ); TOML11_TEST_GET_OR_FALLBACK(local_datetime, table ); TOML11_TEST_GET_OR_FALLBACK(offset_datetime, boolean ); TOML11_TEST_GET_OR_FALLBACK(offset_datetime, integer ); TOML11_TEST_GET_OR_FALLBACK(offset_datetime, floating ); TOML11_TEST_GET_OR_FALLBACK(offset_datetime, string ); TOML11_TEST_GET_OR_FALLBACK(offset_datetime, local_time ); TOML11_TEST_GET_OR_FALLBACK(offset_datetime, local_date ); TOML11_TEST_GET_OR_FALLBACK(offset_datetime, local_datetime ); TOML11_TEST_GET_OR_FALLBACK(offset_datetime, array ); TOML11_TEST_GET_OR_FALLBACK(offset_datetime, table ); TOML11_TEST_GET_OR_FALLBACK(array, boolean ); TOML11_TEST_GET_OR_FALLBACK(array, integer ); TOML11_TEST_GET_OR_FALLBACK(array, floating ); TOML11_TEST_GET_OR_FALLBACK(array, string ); TOML11_TEST_GET_OR_FALLBACK(array, local_time ); TOML11_TEST_GET_OR_FALLBACK(array, local_date ); TOML11_TEST_GET_OR_FALLBACK(array, local_datetime ); TOML11_TEST_GET_OR_FALLBACK(array, offset_datetime); TOML11_TEST_GET_OR_FALLBACK(array, table ); TOML11_TEST_GET_OR_FALLBACK(table, boolean ); TOML11_TEST_GET_OR_FALLBACK(table, integer ); TOML11_TEST_GET_OR_FALLBACK(table, floating ); TOML11_TEST_GET_OR_FALLBACK(table, string ); TOML11_TEST_GET_OR_FALLBACK(table, local_time ); TOML11_TEST_GET_OR_FALLBACK(table, local_date ); TOML11_TEST_GET_OR_FALLBACK(table, local_datetime ); TOML11_TEST_GET_OR_FALLBACK(table, offset_datetime); TOML11_TEST_GET_OR_FALLBACK(table, array ); } #undef TOML11_TEST_GET_OR_FALLBACK TEST_CASE("testing get_or with optional integer") { { toml::value v1(42); toml::value v2(3.14); CHECK_EQ(42u, toml::get_or(v1, 0u)); CHECK_EQ(0u , toml::get_or(v2, 0u)); } { toml::value v1(42); toml::value v2(3.14); CHECK_EQ(42u, toml::get_or(std::move(v1), 0u)); CHECK_EQ( 0u, toml::get_or(std::move(v2), 0u)); } } TEST_CASE("testing get_or with optional float") { { toml::value v1(42); toml::value v2(3.14); CHECK_EQ(2.71f, toml::get_or(v1, 2.71f)); CHECK_EQ(static_cast(v2.as_floating()), toml::get_or(v2, 2.71f)); } { toml::value v1(42); toml::value v2(3.14); CHECK_EQ(2.71f , toml::get_or(std::move(v1), 2.71f)); CHECK_EQ(static_cast(3.14), toml::get_or(std::move(v2), 2.71f)); } } TEST_CASE("testing get_or with optional string literal") { { toml::value v1("foobar"); toml::value v2(42); CHECK_EQ("foobar", toml::get_or(v1, "bazqux")); CHECK_EQ("bazqux", toml::get_or(v2, "bazqux")); } { toml::value v1("foobar"); toml::value v2(42); CHECK_EQ("foobar", toml::get_or(std::move(v1), "bazqux")); CHECK_EQ("bazqux", toml::get_or(std::move(v2), "bazqux")); } } toml11-4.1.0/tests/test_literal.cpp000066400000000000000000000075611464712047700172170ustar00rootroot00000000000000#define DOCTEST_CONFIG_IMPLEMENT_WITH_MAIN #include "doctest.h" #include #include #include TEST_CASE("testing toml value literal") { using namespace toml::literals::toml_literals; { const toml::value v1 = "true"_toml; const toml::value v2 = "false"_toml; CHECK_UNARY(v1.is_boolean()); CHECK_UNARY(v2.is_boolean()); CHECK_EQ(v1.as_boolean(), true); CHECK_EQ(v2.as_boolean(), false); } { const toml::value v1 = "123_456"_toml; const toml::value v2 = "0b0010"_toml; const toml::value v3 = "0xDEADBEEF"_toml; CHECK_UNARY(v1.is_integer()); CHECK_UNARY(v2.is_integer()); CHECK_UNARY(v3.is_integer()); CHECK_EQ(v1.as_integer(), 123456); CHECK_EQ(v2.as_integer(), 2); CHECK_EQ(v3.as_integer(), 0xDEADBEEF); } { const toml::value v1 = "3.1415"_toml; const toml::value v2 = "6.02e+23"_toml; CHECK_UNARY(v1.is_floating()); CHECK_UNARY(v2.is_floating()); CHECK_EQ(v1.as_floating(), doctest::Approx(3.1415 ).epsilon(0.00001)); CHECK_EQ(v2.as_floating(), doctest::Approx(6.02e23).epsilon(0.0001 )); } { const toml::value v1 = R"("foo")"_toml; const toml::value v2 = R"('foo')"_toml; const toml::value v3 = R"("""foo""")"_toml; const toml::value v4 = R"('''foo''')"_toml; CHECK_UNARY(v1.is_string()); CHECK_UNARY(v2.is_string()); CHECK_UNARY(v3.is_string()); CHECK_UNARY(v4.is_string()); CHECK_EQ(v1.as_string(), "foo"); CHECK_EQ(v2.as_string(), "foo"); CHECK_EQ(v3.as_string(), "foo"); CHECK_EQ(v4.as_string(), "foo"); } { { const toml::value v1 = R"([1,2,3])"_toml; CHECK_UNARY(v1.is_array()); CHECK_EQ(v1.size(), 3); CHECK_EQ(v1.at(0).as_integer(), 1); CHECK_EQ(v1.at(1).as_integer(), 2); CHECK_EQ(v1.at(2).as_integer(), 3); } { const toml::value v2 = R"([1,])"_toml; CHECK_UNARY(v2.is_array()); CHECK_EQ(v2.size(), 1); CHECK_EQ(v2.at(0).as_integer(), 1); } { const toml::value v3 = R"([[1,]])"_toml; CHECK_UNARY(v3.is_array()); CHECK_EQ(v3.size(), 1); CHECK_UNARY(v3.at(0).is_array()); CHECK_EQ(v3.at(0).at(0).as_integer(), 1); } { const toml::value v4 = R"([[1],])"_toml; CHECK_UNARY(v4.is_array()); CHECK_EQ(v4.size(), 1); CHECK_UNARY(v4.at(0).is_array()); CHECK_EQ(v4.at(0).at(0).as_integer(), 1); } } { const toml::value v1 = R"({a = 42})"_toml; CHECK_UNARY(v1.is_table()); CHECK_EQ(v1.size(), 1); CHECK_UNARY(v1.at("a").is_integer()); CHECK_EQ(v1.at("a").as_integer(), 42); } { const toml::value v1 = "1979-05-27"_toml; CHECK_UNARY(v1.is_local_date()); CHECK_EQ(v1.as_local_date(), toml::local_date(1979, toml::month_t::May, 27)); } { const toml::value v1 = "12:00:00"_toml; CHECK_UNARY(v1.is_local_time()); CHECK_EQ(v1.as_local_time(), toml::local_time(12, 0, 0)); } { const toml::value v1 = "1979-05-27T07:32:00"_toml; CHECK_UNARY(v1.is_local_datetime()); CHECK_EQ(v1.as_local_datetime(), toml::local_datetime( toml::local_date(1979, toml::month_t::May, 27), toml::local_time(7, 32, 0))); } { const toml::value v1 = "1979-05-27T07:32:00Z"_toml; CHECK_UNARY(v1.is_offset_datetime()); CHECK_EQ(v1.as_offset_datetime(), toml::offset_datetime( toml::local_date(1979, toml::month_t::May, 27), toml::local_time(7, 32, 0), toml::time_offset(0, 0))); } } toml11-4.1.0/tests/test_location.cpp000066400000000000000000000031361464712047700173650ustar00rootroot00000000000000#define DOCTEST_CONFIG_IMPLEMENT_WITH_MAIN #include "doctest.h" #include TEST_CASE("testing location") { const auto first = toml::detail::make_temporary_location("hogefuga"); auto loc = first; loc.advance(4); CHECK_UNARY(loc.is_ok()); CHECK_UNARY( ! loc.eof()); CHECK_EQ(loc.current(), 'f'); CHECK_EQ(loc.line_number(), 1); CHECK_EQ(loc.column_number(), 5); const auto loc2 = loc; CHECK_EQ(loc, loc2); CHECK_UNARY(loc == loc2); CHECK_UNARY(!(loc != loc2)); loc.advance(4); CHECK_UNARY(loc.is_ok()); CHECK_UNARY(loc.eof()); CHECK_EQ(loc.current(), '\0'); CHECK_EQ(loc.line_number(), 1); CHECK_EQ(loc.column_number(), 9); CHECK_NE(loc, loc2); CHECK_UNARY(!(loc == loc2)); CHECK_UNARY(loc != loc2); CHECK_EQ(toml::detail::count(first, loc, 'e'), 1); CHECK_EQ(toml::detail::count(first, loc, 'g'), 2); } TEST_CASE("testing multiline location") { const auto first = toml::detail::make_temporary_location("hoge\nfuga"); auto loc = first; loc.advance(5); CHECK_UNARY(loc.is_ok()); CHECK_UNARY( ! loc.eof()); CHECK_EQ(loc.current(), 'f'); CHECK_EQ(loc.line_number(), 2); CHECK_EQ(loc.column_number(), 1); const auto newline_r = toml::detail::rfind(first, loc, '\n'); CHECK_UNARY(newline_r.is_ok()); const auto newline = newline_r.as_ok(); CHECK_EQ(newline.current(), '\n'); loc.advance(4); CHECK_UNARY(loc.is_ok()); CHECK_UNARY(loc.eof()); CHECK_EQ(loc.current(), '\0'); CHECK_EQ(loc.line_number(), 2); CHECK_EQ(loc.column_number(), 5); } toml11-4.1.0/tests/test_parse_array.cpp000066400000000000000000000126431464712047700200700ustar00rootroot00000000000000#define DOCTEST_CONFIG_IMPLEMENT_WITH_MAIN #include #include "utility.hpp" #include #include #include "doctest.h" TEST_CASE("testing an array") { toml::detail::context ctx(toml::spec::v(1,0,0)); const auto make_format = [](toml::array_format ty, toml::indent_char ic, std::int32_t i, std::int32_t c) { toml::array_format_info fmt; fmt.fmt = ty; fmt.indent_type = ic; fmt.body_indent = i; fmt.closing_indent = c; return fmt; }; toml11_test_parse_success("[]", (toml::array{}), comments(), make_format(toml::array_format::oneline, toml::indent_char::none, 4, 0), ctx); toml11_test_parse_success("[1]", (toml::array{1}), comments(), make_format(toml::array_format::oneline, toml::indent_char::none, 4, 0), ctx); toml11_test_parse_success("[1, 2]", (toml::array{1, 2}), comments(), make_format(toml::array_format::oneline, toml::indent_char::none, 4, 0), ctx); toml11_test_parse_success("[1, 2, ]", (toml::array{1, 2}), comments(), make_format(toml::array_format::oneline, toml::indent_char::none, 4, 0), ctx); toml11_test_parse_success("[\n 1,\n 2,\n]", (toml::array{1, 2}), comments(), make_format(toml::array_format::multiline, toml::indent_char::space, 2, 0), ctx); toml11_test_parse_success("[\n 1,\n 2,\n ]", (toml::array{1, 2}), comments(), make_format(toml::array_format::multiline, toml::indent_char::space, 2, 2), ctx); toml11_test_parse_success("[\n\t1,\n\t2,\n]", (toml::array{1, 2}), comments(), make_format(toml::array_format::multiline, toml::indent_char::tab, 1, 0), ctx); toml11_test_parse_success("[\n\t1,\n\t2,\n\t]", (toml::array{1, 2}), comments(), make_format(toml::array_format::multiline, toml::indent_char::tab, 1, 1), ctx); // comment 3 is before comma, so is a comment for `2`. toml11_test_parse_success(R"([ 1, # comment 1 2, # comment 2 # comment 3 # comment 4 # comment 5 ])", (toml::array{ toml::value(1, comments("# comment 1")), toml::value(2, comments("# comment 2")), }), comments(), make_format(toml::array_format::multiline, toml::indent_char::space, 4, 0), ctx); // comment 3 is before comma, so is a comment for `2`. toml11_test_parse_success(R"([ 1, # comment 1 2, # comment 2 # comment 3 # comment 4 # comment 5 ])", (toml::array{ toml::value(1, comments("# comment 1")), toml::value(2, comments("# comment 2")), }), comments(), make_format(toml::array_format::multiline, toml::indent_char::space, 4, 2), ctx); // comment 3 is before comma, so is a comment for `2`. toml11_test_parse_success(R"([1 # comment 1 , 2 # comment 2 # comment 3 , # comment 4 # comment 5 (free comment) ])", (toml::array{ toml::value(1, comments("# comment 1")), toml::value(2, comments("# comment 2", "# comment 3", "# comment 4")), }), comments(), make_format(toml::array_format::multiline, toml::indent_char::none, 4, 0), ctx); toml11_test_parse_success(R"([1 # comment 1 , # comment 2 # comment 3 # ditto # comment 4 2 # comment 5 # comment 6 , 3 # comment 7 ])", (toml::array{ toml::value(1, comments("# comment 1", "# comment 2")), toml::value(2, comments("# comment 4", "# comment 5", "# comment 6")), toml::value(3, comments("# comment 7")), }), comments(), make_format(toml::array_format::multiline, toml::indent_char::none, 4, 0), ctx); } TEST_CASE("testing invalid arrays") { using namespace toml::detail; { toml::detail::context ctx(toml::spec::v(1,0,0)); auto loc = toml::detail::make_temporary_location("[ 1, hoge ]"); const auto res = parse_array(loc, ctx); CHECK_UNARY(res.is_err()); for(const auto& e : ctx.errors()) { std::cerr << toml::format_error(e) << std::endl; } } { toml::detail::context ctx(toml::spec::v(1,0,0)); auto loc = toml::detail::make_temporary_location("[ 1, 2"); const auto res = parse_array(loc, ctx); CHECK_UNARY(res.is_err()); ctx.report_error(res.unwrap_err()); for(const auto& e : ctx.errors()) { std::cerr << toml::format_error(e) << std::endl; } } { toml::detail::context ctx(toml::spec::v(1,0,0)); auto loc = toml::detail::make_temporary_location("["); const auto res = parse_array(loc, ctx); CHECK_UNARY(res.is_err()); ctx.report_error(res.unwrap_err()); for(const auto& e : ctx.errors()) { std::cerr << toml::format_error(e) << std::endl; } } { toml::detail::context ctx(toml::spec::v(1,0,0)); auto loc = toml::detail::make_temporary_location("[ 1, 2, 3\n a = b"); const auto res = parse_array(loc, ctx); CHECK_UNARY(res.is_err()); ctx.report_error(res.unwrap_err()); for(const auto& e : ctx.errors()) { std::cerr << toml::format_error(e) << std::endl; } } } toml11-4.1.0/tests/test_parse_boolean.cpp000066400000000000000000000024601464712047700203650ustar00rootroot00000000000000#define DOCTEST_CONFIG_IMPLEMENT_WITH_MAIN #include "doctest.h" #include "utility.hpp" #include #include TEST_CASE("testing valid boolean") { const toml::detail::context ctx(toml::spec::v(1,0,0)); { auto loc = toml::detail::make_temporary_location("true"); const auto res = toml::detail::parse_boolean(loc, ctx); REQUIRE_UNARY(res.is_ok()); const auto val = res.unwrap(); REQUIRE_UNARY(val.is_boolean()); CHECK_EQ(val.as_boolean(), true); } { auto loc = toml::detail::make_temporary_location("false"); const auto res = toml::detail::parse_boolean(loc, ctx); REQUIRE_UNARY(res.is_ok()); const auto val = res.unwrap(); REQUIRE_UNARY(val.is_boolean()); CHECK_EQ(val.as_boolean(), false); } } TEST_CASE("testing invalid boolean") { using namespace toml::detail; const context ctx(toml::spec::v(1,0,0)); toml11_test_parse_failure(parse_boolean, "True", ctx); toml11_test_parse_failure(parse_boolean, "TRUE", ctx); toml11_test_parse_failure(parse_boolean, "False", ctx); toml11_test_parse_failure(parse_boolean, "FALSE", ctx); } toml11-4.1.0/tests/test_parse_datetime.cpp000066400000000000000000000363431464712047700205510ustar00rootroot00000000000000#define DOCTEST_CONFIG_IMPLEMENT_WITH_MAIN #include "doctest.h" #include "utility.hpp" #include TEST_CASE("testing offset_datetime") { const auto fmt = [](toml::datetime_delimiter_kind d, bool has_sec, std::size_t prec) { toml::offset_datetime_format_info f; f.delimiter = d; f.has_seconds = has_sec; f.subsecond_precision = prec; return f; }; { toml::detail::context ctx(toml::spec::v(1,0,0)); toml11_test_parse_success( "1979-05-27T07:32:00Z", toml::offset_datetime( toml::local_datetime( toml::local_date(1979, toml::month_t::May, 27), toml::local_time(7, 32, 0) ), toml::time_offset(0, 0) ), comments(), fmt(toml::datetime_delimiter_kind::upper_T, true, 0), ctx); toml11_test_parse_success( "1979-05-27T07:32:00-07:00", toml::offset_datetime( toml::local_datetime( toml::local_date(1979, toml::month_t::May, 27), toml::local_time(7, 32, 0) ), toml::time_offset(-7, 0) ), comments(), fmt(toml::datetime_delimiter_kind::upper_T, true, 0), ctx); toml11_test_parse_success( "1979-05-27T07:32:00.999999-07:00", toml::offset_datetime( toml::local_datetime( toml::local_date(1979, toml::month_t::May, 27), toml::local_time(7, 32, 0, 999, 999) ), toml::time_offset(-7, 0) ), comments(), fmt(toml::datetime_delimiter_kind::upper_T, true, 6), ctx); toml11_test_parse_success( "1979-05-27 07:32:00Z", toml::offset_datetime( toml::local_datetime( toml::local_date(1979, toml::month_t::May, 27), toml::local_time(7, 32, 0) ), toml::time_offset(0, 0) ), comments(), fmt(toml::datetime_delimiter_kind::space, true, 0), ctx); toml11_test_parse_success( "1979-05-27 07:32:00-07:00", toml::offset_datetime( toml::local_datetime( toml::local_date(1979, toml::month_t::May, 27), toml::local_time(7, 32, 0) ), toml::time_offset(-7, 0) ), comments(), fmt(toml::datetime_delimiter_kind::space, true, 0), ctx); toml11_test_parse_success( "1979-05-27 07:32:00.999999-07:00", toml::offset_datetime( toml::local_datetime( toml::local_date(1979, toml::month_t::May, 27), toml::local_time(7, 32, 0, 999, 999) ), toml::time_offset(-7, 0) ), comments(), fmt(toml::datetime_delimiter_kind::space, true, 6), ctx); } { auto spec = toml::spec::v(1,0,0); spec.v1_1_0_make_seconds_optional = true; toml::detail::context ctx(spec); toml11_test_parse_success( "1979-05-27T07:32Z", toml::offset_datetime( toml::local_datetime( toml::local_date(1979, toml::month_t::May, 27), toml::local_time(7, 32, 0) ), toml::time_offset(0, 0) ), comments(), fmt(toml::datetime_delimiter_kind::upper_T, false, 0), ctx); toml11_test_parse_success( "1979-05-27T07:32:00Z", toml::offset_datetime( toml::local_datetime( toml::local_date(1979, toml::month_t::May, 27), toml::local_time(7, 32, 0) ), toml::time_offset(0, 0) ), comments(), fmt(toml::datetime_delimiter_kind::upper_T, true, 0), ctx); toml11_test_parse_success( "1979-05-27T07:32:00-07:00", toml::offset_datetime( toml::local_datetime( toml::local_date(1979, toml::month_t::May, 27), toml::local_time(7, 32, 0) ), toml::time_offset(-7, 0) ), comments(), fmt(toml::datetime_delimiter_kind::upper_T, true, 0), ctx); toml11_test_parse_success( "1979-05-27T07:32:00.999999-07:00", toml::offset_datetime( toml::local_datetime( toml::local_date(1979, toml::month_t::May, 27), toml::local_time(7, 32, 0, 999, 999) ), toml::time_offset(-7, 0) ), comments(), fmt(toml::datetime_delimiter_kind::upper_T, true, 6), ctx); toml11_test_parse_success( "1979-05-27 07:32Z", toml::offset_datetime( toml::local_datetime( toml::local_date(1979, toml::month_t::May, 27), toml::local_time(7, 32, 0) ), toml::time_offset(0, 0) ), comments(), fmt(toml::datetime_delimiter_kind::space, false, 0), ctx); toml11_test_parse_success( "1979-05-27 07:32:00Z", toml::offset_datetime( toml::local_datetime( toml::local_date(1979, toml::month_t::May, 27), toml::local_time(7, 32, 0) ), toml::time_offset(0, 0) ), comments(), fmt(toml::datetime_delimiter_kind::space, true, 0), ctx); toml11_test_parse_success( "1979-05-27 07:32:00-07:00", toml::offset_datetime( toml::local_datetime( toml::local_date(1979, toml::month_t::May, 27), toml::local_time(7, 32, 0) ), toml::time_offset(-7, 0) ), comments(), fmt(toml::datetime_delimiter_kind::space, true, 0), ctx); toml11_test_parse_success( "1979-05-27 07:32:00.999999-07:00", toml::offset_datetime( toml::local_datetime( toml::local_date(1979, toml::month_t::May, 27), toml::local_time(7, 32, 0, 999, 999) ), toml::time_offset(-7, 0) ), comments(), fmt(toml::datetime_delimiter_kind::space, true, 6), ctx); } } TEST_CASE("testing local_datetime") { const auto fmt = [](toml::datetime_delimiter_kind d, bool has_sec, std::size_t prec) { toml::local_datetime_format_info f; f.delimiter = d; f.has_seconds = has_sec; f.subsecond_precision = prec; return f; }; { toml::detail::context ctx(toml::spec::v(1,0,0)); toml11_test_parse_success( "1979-05-27T07:32:00", toml::local_datetime(toml::local_date(1979, toml::month_t::May, 27), toml::local_time(7, 32, 0)), comments(), fmt(toml::datetime_delimiter_kind::upper_T, true, 0), ctx); toml11_test_parse_success( "1979-05-27T07:32:00.9999", toml::local_datetime(toml::local_date(1979, toml::month_t::May, 27), toml::local_time(7, 32, 0, 999, 900, 0)), comments(), fmt(toml::datetime_delimiter_kind::upper_T, true, 4), ctx); toml11_test_parse_success( "1979-05-27 07:32:00", toml::local_datetime(toml::local_date(1979, toml::month_t::May, 27), toml::local_time(7, 32, 0)), comments(), fmt(toml::datetime_delimiter_kind::space, true, 0), ctx); toml11_test_parse_success( "1979-05-27 07:32:00.9999", toml::local_datetime(toml::local_date(1979, toml::month_t::May, 27), toml::local_time(7, 32, 0, 999, 900, 0)), comments(), fmt(toml::datetime_delimiter_kind::space, true, 4), ctx); } { auto spec = toml::spec::v(1,0,0); spec.v1_1_0_make_seconds_optional = true; toml::detail::context ctx(spec); toml11_test_parse_success( "1979-05-27T07:32", toml::local_datetime(toml::local_date(1979, toml::month_t::May, 27), toml::local_time(7, 32, 0)), comments(), fmt(toml::datetime_delimiter_kind::upper_T, false, 0), ctx); toml11_test_parse_success( "1979-05-27T07:32:00", toml::local_datetime(toml::local_date(1979, toml::month_t::May, 27), toml::local_time(7, 32, 0)), comments(), fmt(toml::datetime_delimiter_kind::upper_T, true, 0), ctx); toml11_test_parse_success( "1979-05-27T07:32:00.9999", toml::local_datetime(toml::local_date(1979, toml::month_t::May, 27), toml::local_time(7, 32, 0, 999, 900, 0)), comments(), fmt(toml::datetime_delimiter_kind::upper_T, true, 4), ctx); toml11_test_parse_success( "1979-05-27 07:32", toml::local_datetime(toml::local_date(1979, toml::month_t::May, 27), toml::local_time(7, 32, 0)), comments(), fmt(toml::datetime_delimiter_kind::space, false, 0), ctx); toml11_test_parse_success( "1979-05-27 07:32:00", toml::local_datetime(toml::local_date(1979, toml::month_t::May, 27), toml::local_time(7, 32, 0)), comments(), fmt(toml::datetime_delimiter_kind::space, true, 0), ctx); toml11_test_parse_success( "1979-05-27 07:32:00.9999", toml::local_datetime(toml::local_date(1979, toml::month_t::May, 27), toml::local_time(7, 32, 0, 999, 900, 0)), comments(), fmt(toml::datetime_delimiter_kind::space, true, 4), ctx); } } TEST_CASE("testing local_date") { toml::detail::context ctx(toml::spec::v(1,0,0)); toml11_test_parse_success("1979-05-27", toml::local_date(1979, toml::month_t::May, 27), comments(), toml::local_date_format_info{}, ctx); toml11_test_parse_success("0979-12-27", toml::local_date( 979, toml::month_t::Dec, 27), comments(), toml::local_date_format_info{}, ctx); toml11_test_parse_failure(toml::detail::parse_local_date, "1979-00-27", ctx); toml11_test_parse_failure(toml::detail::parse_local_date, "1979-13-27", ctx); toml11_test_parse_failure(toml::detail::parse_local_date, "1979-05-00", ctx); toml11_test_parse_failure(toml::detail::parse_local_date, "1979-05-32", ctx); toml11_test_parse_failure(toml::detail::parse_local_date, "1979-05-2", ctx); toml11_test_parse_failure(toml::detail::parse_local_date, "1979-5-02", ctx); toml11_test_parse_failure(toml::detail::parse_local_date, "1979-5-2", ctx); toml11_test_parse_failure(toml::detail::parse_local_date, "979-5-2", ctx); toml11_test_parse_failure(toml::detail::parse_local_date, "12345-05-27", ctx); } TEST_CASE("testing local_time") { const auto fmt = [](const bool has_sec, const std::size_t sec_prec) { toml::local_time_format_info f; f.has_seconds = has_sec; f.subsecond_precision = sec_prec; return f; }; { toml::detail::context ctx(toml::spec::v(1,0,0)); toml11_test_parse_success("01:02:03", toml::local_time(1, 2, 3), comments(), fmt(true, 0), ctx); toml11_test_parse_success("01:23:45", toml::local_time(1, 23, 45), comments(), fmt(true, 0), ctx); toml11_test_parse_success("01:23:45.1", toml::local_time(1, 23, 45, 100), comments(), fmt(true, 1), ctx); toml11_test_parse_success("01:23:45.12", toml::local_time(1, 23, 45, 120), comments(), fmt(true, 2), ctx); toml11_test_parse_success("01:23:45.123", toml::local_time(1, 23, 45, 123), comments(), fmt(true, 3), ctx); toml11_test_parse_success("01:23:45.1234", toml::local_time(1, 23, 45, 123, 400), comments(), fmt(true, 4), ctx); toml11_test_parse_success("01:23:45.1234567", toml::local_time(1, 23, 45, 123, 456, 700), comments(), fmt(true, 7), ctx); toml11_test_parse_success("01:23:45.123456789", toml::local_time(1, 23, 45, 123, 456, 789), comments(), fmt(true, 9), ctx); } { auto spec = toml::spec::v(1,0,0); spec.v1_1_0_make_seconds_optional = true; toml::detail::context ctx(spec); toml11_test_parse_success("01:23", toml::local_time(1, 23, 0), comments(), fmt(false, 0), ctx); toml11_test_parse_success("01:02", toml::local_time(1, 2, 0), comments(), fmt(false, 0), ctx); toml11_test_parse_success("01:02:03", toml::local_time(1, 2, 3), comments(), fmt(true, 0), ctx); toml11_test_parse_success("01:23:45", toml::local_time(1, 23, 45), comments(), fmt(true, 0), ctx); toml11_test_parse_success("01:23:45.1", toml::local_time(1, 23, 45, 100), comments(), fmt(true, 1), ctx); toml11_test_parse_success("01:23:45.12", toml::local_time(1, 23, 45, 120), comments(), fmt(true, 2), ctx); toml11_test_parse_success("01:23:45.123", toml::local_time(1, 23, 45, 123), comments(), fmt(true, 3), ctx); toml11_test_parse_success("01:23:45.1234", toml::local_time(1, 23, 45, 123, 400), comments(), fmt(true, 4), ctx); toml11_test_parse_success("01:23:45.1234567", toml::local_time(1, 23, 45, 123, 456, 700), comments(), fmt(true, 7), ctx); toml11_test_parse_success("01:23:45.123456789", toml::local_time(1, 23, 45, 123, 456, 789), comments(), fmt(true, 9), ctx); } } toml11-4.1.0/tests/test_parse_floating.cpp000066400000000000000000000167031464712047700205560ustar00rootroot00000000000000#define DOCTEST_CONFIG_IMPLEMENT_WITH_MAIN #include "doctest.h" #include "utility.hpp" #include #include TEST_CASE("testing fractional float") { toml::detail::context ctx(toml::spec::v(1,0,0)); auto fmt = [](std::size_t prec) { toml::floating_format_info f; f.fmt = toml::floating_format::fixed; f.prec = prec; return f; }; toml11_test_parse_success("1.0", 1.0 , comments(), fmt( 1), ctx); toml11_test_parse_success("0.1", 0.1 , comments(), fmt( 1), ctx); toml11_test_parse_success("0.001", 0.001 , comments(), fmt( 3), ctx); toml11_test_parse_success("0.100", 0.1 , comments(), fmt( 3), ctx); toml11_test_parse_success("+3.14", 3.14 , comments(), fmt( 2), ctx); toml11_test_parse_success("-3.14", -3.14 , comments(), fmt( 2), ctx); toml11_test_parse_success("3.1415_9265_3589", 3.141592653589, comments(), fmt(12), ctx); toml11_test_parse_success("+3.1415_9265_3589", 3.141592653589, comments(), fmt(12), ctx); toml11_test_parse_success("-3.1415_9265_3589", -3.141592653589, comments(), fmt(12), ctx); toml11_test_parse_success("123_456.789", 123456.789 , comments(), fmt( 3), ctx); toml11_test_parse_success("+123_456.789", 123456.789 , comments(), fmt( 3), ctx); toml11_test_parse_success("-123_456.789", -123456.789 , comments(), fmt( 3), ctx); toml11_test_parse_success("+0.0", 0.0 , comments(), fmt( 1), ctx); toml11_test_parse_success("-0.0", -0.0 , comments(), fmt( 1), ctx); } TEST_CASE("testing exponents") { toml::detail::context ctx(toml::spec::v(1,0,0)); auto fmt = [](std::size_t prec) { toml::floating_format_info f; f.fmt = toml::floating_format::scientific; f.prec = prec; return f; }; toml11_test_parse_success("1e10", 1e10 , comments(), fmt(1), ctx); toml11_test_parse_success("1e+10", 1e10 , comments(), fmt(1), ctx); toml11_test_parse_success("1e-10", 1e-10 , comments(), fmt(1), ctx); toml11_test_parse_success("+1e10", 1e10 , comments(), fmt(1), ctx); toml11_test_parse_success("+1e+10", 1e10 , comments(), fmt(1), ctx); toml11_test_parse_success("+1e-10", 1e-10 , comments(), fmt(1), ctx); toml11_test_parse_success("-1e10", -1e10 , comments(), fmt(1), ctx); toml11_test_parse_success("-1e+10", -1e10 , comments(), fmt(1), ctx); toml11_test_parse_success("-1e-10", -1e-10 , comments(), fmt(1), ctx); toml11_test_parse_success("123e-10", 123e-10, comments(), fmt(3), ctx); toml11_test_parse_success("1E10", 1e10 , comments(), fmt(1), ctx); toml11_test_parse_success("1E+10", 1e10 , comments(), fmt(1), ctx); toml11_test_parse_success("1E-10", 1e-10 , comments(), fmt(1), ctx); toml11_test_parse_success("123E-10", 123e-10, comments(), fmt(3), ctx); toml11_test_parse_success("1_2_3E-10", 123e-10, comments(), fmt(3), ctx); toml11_test_parse_success("1_2_3E-1_0", 123e-10, comments(), fmt(3), ctx); toml11_test_parse_success("+0e0", 0.0 , comments(), fmt(1), ctx); toml11_test_parse_success("-0e0", -0.0 , comments(), fmt(1), ctx); } TEST_CASE("testing fraction + exponents") { toml::detail::context ctx(toml::spec::v(1,0,0)); auto fmt = [](std::size_t prec) { toml::floating_format_info f; f.fmt = toml::floating_format::scientific; f.prec = prec; return f; }; toml11_test_parse_success("6.02e23", 6.02e23, comments(), fmt(3), ctx); toml11_test_parse_success("6.02e+23", 6.02e23, comments(), fmt(3), ctx); toml11_test_parse_success("1.112_650_06e-17", 1.11265006e-17, comments(), fmt(9), ctx); } TEST_CASE("testing +/-inf") { toml::detail::context ctx(toml::spec::v(1,0,0)); { auto loc = toml::detail::make_temporary_location("inf"); const auto res = toml::detail::parse_floating(loc, ctx); REQUIRE_UNARY(res.is_ok()); const auto val = res.unwrap(); REQUIRE_UNARY(val.is_floating()); CHECK_UNARY(std::isinf(val.as_floating())); CHECK_UNARY(val.as_floating() > 0); // + } { auto loc = toml::detail::make_temporary_location("+inf"); const auto res = toml::detail::parse_floating(loc, ctx); REQUIRE_UNARY(res.is_ok()); const auto val = res.unwrap(); REQUIRE_UNARY(val.is_floating()); CHECK_UNARY(std::isinf(val.as_floating())); CHECK_UNARY(val.as_floating() > 0); // + } { auto loc = toml::detail::make_temporary_location("-inf"); const auto res = toml::detail::parse_floating(loc, ctx); REQUIRE_UNARY(res.is_ok()); const auto val = res.unwrap(); REQUIRE_UNARY(val.is_floating()); CHECK_UNARY(std::isinf(val.as_floating())); CHECK_UNARY(val.as_floating() < 0); // - } } TEST_CASE("testing +/-nan") { toml::detail::context ctx(toml::spec::v(1,0,0)); { auto loc = toml::detail::make_temporary_location("nan"); const auto res = toml::detail::parse_floating(loc, ctx); REQUIRE_UNARY(res.is_ok()); const auto val = res.unwrap(); REQUIRE_UNARY(val.is_floating()); CHECK_UNARY(std::isnan(val.as_floating())); } { auto loc = toml::detail::make_temporary_location("+nan"); const auto res = toml::detail::parse_floating(loc, ctx); REQUIRE_UNARY(res.is_ok()); const auto val = res.unwrap(); REQUIRE_UNARY(val.is_floating()); CHECK_UNARY(std::isnan(val.as_floating())); } { auto loc = toml::detail::make_temporary_location("-nan"); const auto res = toml::detail::parse_floating(loc, ctx); REQUIRE_UNARY(res.is_ok()); const auto val = res.unwrap(); REQUIRE_UNARY(val.is_floating()); CHECK_UNARY(std::isnan(val.as_floating())); } } TEST_CASE("testing hexfloat") { toml::spec s = toml::spec::v(1,0,0); s.ext_hex_float = true; toml::detail::context ctx(s); { auto loc = toml::detail::make_temporary_location("0xABCp-3"); const auto res = toml::detail::parse_floating(loc, ctx); REQUIRE_UNARY(res.is_ok()); const auto val = res.unwrap(); REQUIRE_UNARY(val.is_floating()); CHECK_EQ(val.as_floating(), 343.5); } } toml11-4.1.0/tests/test_parse_inline_table.cpp000066400000000000000000000237661464712047700214070ustar00rootroot00000000000000#define DOCTEST_CONFIG_IMPLEMENT_WITH_MAIN #include #include "utility.hpp" #include #include #include "doctest.h" TEST_CASE("testing an inline table v1.0") { { // no multiline, no trailing comma toml::detail::context ctx(toml::spec::v(1,0,0)); const auto make_format = [](toml::table_format ty) { toml::table_format_info fmt; fmt.fmt = ty; fmt.indent_type = toml::indent_char::none; return fmt; }; toml11_test_parse_success("{}", (toml::table{}), comments(), make_format(toml::table_format::oneline), ctx); toml11_test_parse_success("{a = 1}", (toml::table{{"a", 1}}), comments(), make_format(toml::table_format::oneline), ctx); toml11_test_parse_success("{a = 1, b = 2}", (toml::table{{"a", 1}, {"b", 2}}), comments(), make_format(toml::table_format::oneline), ctx); toml11_test_parse_success("{a.b = 1}", (toml::table{{ "a", toml::table{{"b", 1}} }}), comments(), make_format(toml::table_format::oneline), ctx); toml11_test_parse_success("{a.b = 1, a.c = 2}", (toml::table{{ "a", toml::table{{"b", 1}, {"c", 2}} }}), comments(), make_format(toml::table_format::oneline), ctx); } // invalids { using toml::detail::parse_inline_table; toml::detail::context ctx(toml::spec::v(1,0,0)); toml11_test_parse_failure(parse_inline_table, "{ 1, 2 }", ctx); // no key toml11_test_parse_failure(parse_inline_table, "{ a = 1, b = 2, }", ctx); // trailing comma toml11_test_parse_failure(parse_inline_table, "{ a = 1, b = 2", ctx); // no closing bracket toml11_test_parse_failure(parse_inline_table, "{", ctx); // no closing bracket toml11_test_parse_failure(parse_inline_table, "{ a = 1, b = 2\n a = b", ctx); // no closing bracket toml11_test_parse_failure(parse_inline_table, "{ a.b = 1, a.b.c = 2}", ctx); // a.b is not a table toml11_test_parse_failure(parse_inline_table, "{ a = 1 # comment\n}", ctx); // newline not allowed } } TEST_CASE("testing an inline table v1.0 + trailing comma") { { // no multiline, no trailing comma auto spec = toml::spec::v(1,0,0); spec.v1_1_0_allow_trailing_comma_in_inline_tables = true; toml::detail::context ctx(spec); const auto make_format = [](toml::table_format ty) { toml::table_format_info fmt; fmt.fmt = ty; fmt.indent_type = toml::indent_char::none; return fmt; }; toml11_test_parse_success("{}", (toml::table{}), comments(), make_format(toml::table_format::oneline), ctx); toml11_test_parse_success("{a = 1}", (toml::table{{"a", 1}}), comments(), make_format(toml::table_format::oneline), ctx); toml11_test_parse_success("{a = 1,}", (toml::table{{"a", 1}}), comments(), make_format(toml::table_format::oneline), ctx); toml11_test_parse_success("{a = 1, b = 2,}", (toml::table{{"a", 1}, {"b", 2}}), comments(), make_format(toml::table_format::oneline), ctx); toml11_test_parse_success("{a.b = 1}", (toml::table{{ "a", toml::table{{"b", 1}} }}), comments(), make_format(toml::table_format::oneline), ctx); toml11_test_parse_success("{a.b = 1, a.c = 2}", (toml::table{{ "a", toml::table{{"b", 1}, {"c", 2}} }}), comments(), make_format(toml::table_format::oneline), ctx); } // invalids { using toml::detail::parse_inline_table; auto spec = toml::spec::v(1,0,0); spec.v1_1_0_allow_trailing_comma_in_inline_tables = true; toml::detail::context ctx(spec); toml11_test_parse_failure(parse_inline_table, "{ 1, 2 }", ctx); toml11_test_parse_failure(parse_inline_table, "{ a = 1, b = 2", ctx); toml11_test_parse_failure(parse_inline_table, "{", ctx); toml11_test_parse_failure(parse_inline_table, "{ a = 1, b = 2\n a = b", ctx); toml11_test_parse_failure(parse_inline_table, "{ a.b = 1, a.b.c = 2}", ctx); toml11_test_parse_failure(parse_inline_table, "{ a = 1 # comment\n}", ctx); } } TEST_CASE("testing an inline table v1.1") { { // w/ multiline, w/ trailing comma toml::detail::context ctx(toml::spec::v(1,1,0)); const auto make_format = [](toml::table_format ty, const toml::indent_char ic, const std::int32_t n, const std::int32_t b, const std::int32_t c ) { toml::table_format_info fmt; fmt.fmt = ty; fmt.indent_type = ic; fmt.name_indent = n; fmt.body_indent = b; fmt.closing_indent = c; return fmt; }; toml11_test_parse_success("{}", (toml::table{}), comments(), make_format(toml::table_format::oneline, toml::indent_char::none,0,0,0), ctx); toml11_test_parse_success("{a = 1}", (toml::table{{"a", 1}}), comments(), make_format(toml::table_format::oneline, toml::indent_char::none,0,0,0), ctx); toml11_test_parse_success("{a = 1, b = 2}", (toml::table{{"a", 1}, {"b", 2}}), comments(), make_format(toml::table_format::oneline, toml::indent_char::none,0,0,0), ctx); toml11_test_parse_success("{a.b = 1}", (toml::table{{ "a", toml::table{{"b", 1}} }}), comments(), make_format(toml::table_format::oneline, toml::indent_char::none,0,0,0), ctx); toml11_test_parse_success("{a.b = 1, a.c = 2}", (toml::table{{ "a", toml::table{{"b", 1}, {"c", 2}} }}), comments(), make_format(toml::table_format::oneline, toml::indent_char::none,0,0,0), ctx); toml11_test_parse_success("{\n}", (toml::table{}), comments(), make_format(toml::table_format::multiline_oneline, toml::indent_char::none,0,0,0), ctx); toml11_test_parse_success("{\na = 1\n}", (toml::table{{"a", 1}}), comments(), make_format(toml::table_format::multiline_oneline, toml::indent_char::none,0,0,0), ctx); toml11_test_parse_success("{\n a = 1, \n b = 2\n}", (toml::table{{"a", 1}, {"b", 2}}), comments(), make_format(toml::table_format::multiline_oneline, toml::indent_char::space, 0, 2, 0), ctx); toml11_test_parse_success("{\n a.b = 1\n }", (toml::table{{ "a", toml::table{{"b", 1}} }}), comments(), make_format(toml::table_format::multiline_oneline, toml::indent_char::space, 0, 2, 2), ctx); toml11_test_parse_success("{a.b = 1,\n a.c = 2}", (toml::table{{ "a", toml::table{{"b", 1}, {"c", 2}} }}), comments(), make_format(toml::table_format::multiline_oneline, toml::indent_char::space, 0, 2, 0), ctx); toml11_test_parse_success("{# this table is empty.\n}", (toml::table{}), comments(), make_format(toml::table_format::multiline_oneline, toml::indent_char::none,0,0,0), ctx); toml11_test_parse_success("{\na = 1 # com\n}", (toml::table{{"a", toml::value(1, {"# com"}) }}), comments(), make_format(toml::table_format::multiline_oneline, toml::indent_char::none,0,0,0), ctx); toml11_test_parse_success("{\n a = 1, # com-a\n b = 2 # com-b\n}", (toml::table{{"a", toml::value(1, {"# com-a"}) }, {"b", toml::value(2, {"# com-b"})}}), comments(), make_format(toml::table_format::multiline_oneline, toml::indent_char::space, 0, 2, 0), ctx); toml11_test_parse_success("{# com-a-b\n a.b = 1\n }", (toml::table{{"a", toml::table{{"b", toml::value(1, {"# com-a-b"})}} }}), comments(), make_format(toml::table_format::multiline_oneline, toml::indent_char::space, 0, 2, 2), ctx); toml11_test_parse_success("{a.b = 1,\n a.c = 2 # com-a-c\n}", (toml::table{{"a", toml::table{{"b", 1}, {"c", toml::value(2, {"# com-a-c"})}} }}), comments(), make_format(toml::table_format::multiline_oneline, toml::indent_char::space, 0, 2, 0), ctx); } // invalids { using toml::detail::parse_inline_table; auto spec = toml::spec::v(1,1,0); toml::detail::context ctx(spec); toml11_test_parse_failure(parse_inline_table, "{ 1, 2 }", ctx); // no key toml11_test_parse_failure(parse_inline_table, "{ a = 1, b = 2", ctx); // no closing bracket toml11_test_parse_failure(parse_inline_table, "{", ctx); // no closing bracket toml11_test_parse_failure(parse_inline_table, "{ a = 1, b = 2\n a = b", ctx); // no closing bracket toml11_test_parse_failure(parse_inline_table, "{ a.b = 1, a.b.c = 2}", ctx); // a.b is not a table } } toml11-4.1.0/tests/test_parse_integer.cpp000066400000000000000000000203231464712047700204010ustar00rootroot00000000000000 #define DOCTEST_CONFIG_IMPLEMENT_WITH_MAIN #include "doctest.h" #include "utility.hpp" #include #include TEST_CASE("testing decimal_value") { toml::detail::context ctx(toml::spec::v(1,0,0)); const auto decimal_fmt = [](std::size_t w, std::size_t s) { toml::integer_format_info fmt; fmt.fmt = toml::integer_format::dec; fmt.width = w; fmt.spacer = s; return fmt; }; toml11_test_parse_success( "0", 0, comments(), decimal_fmt(1, 0), ctx); toml11_test_parse_success( "+0", 0, comments(), decimal_fmt(2, 0), ctx); toml11_test_parse_success( "-0", 0, comments(), decimal_fmt(2, 0), ctx); toml11_test_parse_success( "1234", 1234, comments(), decimal_fmt(4, 0), ctx); toml11_test_parse_success( "+1234", 1234, comments(), decimal_fmt(5, 0), ctx); toml11_test_parse_success( "-1234", -1234, comments(), decimal_fmt(5, 0), ctx); toml11_test_parse_success( "0", 0, comments(), decimal_fmt(1, 0), ctx); toml11_test_parse_success( "1_2_3_4", 1234, comments(), decimal_fmt(4, 1), ctx); toml11_test_parse_success( "+1_2_3_4", +1234, comments(), decimal_fmt(5, 1), ctx); toml11_test_parse_success( "-1_2_3_4", -1234, comments(), decimal_fmt(5, 1), ctx); toml11_test_parse_success("123_456_789", 123456789, comments(), decimal_fmt(9, 3), ctx); } TEST_CASE("testing hex_value") { toml::detail::context ctx(toml::spec::v(1,0,0)); const auto hex_fmt = [](bool u, std::size_t w, std::size_t s) { toml::integer_format_info fmt; fmt.fmt = toml::integer_format::hex; fmt.uppercase = u; fmt.width = w; fmt.spacer = s; return fmt; }; toml11_test_parse_success("0xDEADBEEF", 0xDEADBEEF, comments(), hex_fmt(true, 8, 0), ctx); toml11_test_parse_success("0xdeadbeef", 0xDEADBEEF, comments(), hex_fmt(false, 8, 0), ctx); toml11_test_parse_success("0xDEADbeef", 0xDEADBEEF, comments(), hex_fmt(true, 8, 0), ctx); toml11_test_parse_success("0xDEAD_BEEF", 0xDEADBEEF, comments(), hex_fmt(true, 8, 4), ctx); toml11_test_parse_success("0xdead_beef", 0xDEADBEEF, comments(), hex_fmt(false, 8, 4), ctx); toml11_test_parse_success("0xdead_BEEF", 0xDEADBEEF, comments(), hex_fmt(true, 8, 4), ctx); toml11_test_parse_success("0xFF", 0xFF, comments(), hex_fmt(true, 2, 0), ctx); toml11_test_parse_success("0x00FF", 0xFF, comments(), hex_fmt(true, 4, 0), ctx); toml11_test_parse_success("0x0000FF", 0xFF, comments(), hex_fmt(true, 6, 0), ctx); toml11_test_parse_success("0xff", 0xFF, comments(), hex_fmt(false, 2, 0), ctx); toml11_test_parse_success("0x00ff", 0xFF, comments(), hex_fmt(false, 4, 0), ctx); toml11_test_parse_success("0x0000ff", 0xFF, comments(), hex_fmt(false, 6, 0), ctx); toml11_test_parse_success("0x00", 0, comments(), hex_fmt(true, 2, 0), ctx); toml11_test_parse_success("0x12345", 0x12345, comments(), hex_fmt(true, 5, 0), ctx); } TEST_CASE("testing oct_value") { toml::detail::context ctx(toml::spec::v(1,0,0)); const auto oct_fmt = [](std::size_t w, std::size_t s) { toml::integer_format_info fmt; fmt.fmt = toml::integer_format::oct; fmt.width = w; fmt.spacer = s; return fmt; }; toml11_test_parse_success("0o777", 64*7+8*7+7, comments(), oct_fmt(3, 0), ctx); toml11_test_parse_success("0o7_7_7", 64*7+8*7+7, comments(), oct_fmt(3, 1), ctx); toml11_test_parse_success("0o007", 7, comments(), oct_fmt(3, 0), ctx); } TEST_CASE("testing bin_value") { toml::detail::context ctx(toml::spec::v(1,0,0)); const auto bin_fmt = [](std::size_t w, std::size_t s) { toml::integer_format_info fmt; fmt.fmt = toml::integer_format::bin; fmt.width = w; fmt.spacer = s; return fmt; }; toml11_test_parse_success("0b10000", 16, comments(), bin_fmt(5, 0), ctx); toml11_test_parse_success("0b010000", 16, comments(), bin_fmt(6, 0), ctx); toml11_test_parse_success("0b01_00_00", 16, comments(), bin_fmt(6, 2), ctx); toml11_test_parse_success("0b111111", 63, comments(), bin_fmt(6, 0), ctx); toml11_test_parse_success( "0b1000_1000_1000_1000_1000_1000_1000_1000_1000_1000_1000_1000_1000_1000_1000", 0x0888888888888888, comments(), bin_fmt(60, 4), ctx); toml11_test_parse_success( "0b01111111_11111111_11111111_11111111_11111111_11111111_11111111_11111111", 0x7FFFFFFFFFFFFFFF, comments(), bin_fmt(64, 8), ctx); toml11_test_parse_success( "0b00000000_01111111_11111111_11111111_11111111_11111111_11111111_11111111_11111111", 0x7FFFFFFFFFFFFFFF, comments(), bin_fmt(72, 8), ctx); } TEST_CASE("testing integer_overflow") { toml::detail::context ctx(toml::spec::v(1,0,0)); { auto loc = toml::detail::make_temporary_location("9223372036854775808"); const auto res = toml::detail::parse_dec_integer(loc, ctx); CHECK_UNARY(res.is_err()); } { auto loc = toml::detail::make_temporary_location("0x1_00000000_00000000"); const auto res = toml::detail::parse_hex_integer(loc, ctx); CHECK_UNARY(res.is_err()); } { auto loc = toml::detail::make_temporary_location("0o1_000_000_000_000_000_000_000"); const auto res = toml::detail::parse_oct_integer(loc, ctx); CHECK_UNARY(res.is_err()); } { // 64 56 48 40 32 24 16 8 auto loc = toml::detail::make_temporary_location("0b10000000_00000000_00000000_00000000_00000000_00000000_00000000_00000000"); const auto res = toml::detail::parse_oct_integer(loc, ctx); CHECK_UNARY(res.is_err()); } } TEST_CASE("testing decimal_value with suffix extension") { auto spec = toml::spec::v(1, 0, 0); spec.ext_num_suffix = true; toml::detail::context ctx(spec); const auto decimal_fmt = [](std::size_t w, std::size_t s, std::string x) { toml::integer_format_info fmt; fmt.fmt = toml::integer_format::dec; fmt.width = w; fmt.spacer = s; fmt.suffix = std::move(x); return fmt; }; toml11_test_parse_success( "1234_μm", 1234, comments(), decimal_fmt(4, 0, "μm"), ctx); toml11_test_parse_success( "+1234_μm", 1234, comments(), decimal_fmt(5, 0, "μm"), ctx); toml11_test_parse_success( "-1234_μm", -1234, comments(), decimal_fmt(5, 0, "μm"), ctx); toml11_test_parse_success( "0_μm", 0, comments(), decimal_fmt(1, 0, "μm"), ctx); toml11_test_parse_success( "1_2_3_4_μm", 1234, comments(), decimal_fmt(4, 1, "μm"), ctx); toml11_test_parse_success( "+1_2_3_4_μm", +1234, comments(), decimal_fmt(5, 1, "μm"), ctx); toml11_test_parse_success( "-1_2_3_4_μm", -1234, comments(), decimal_fmt(5, 1, "μm"), ctx); toml11_test_parse_success("123_456_789_μm", 123456789, comments(), decimal_fmt(9, 3, "μm"), ctx); } toml11-4.1.0/tests/test_parse_null.cpp000066400000000000000000000050531464712047700177210ustar00rootroot00000000000000#define DOCTEST_CONFIG_IMPLEMENT_WITH_MAIN #include "doctest.h" #include "utility.hpp" #include #include TEST_CASE("testing null value extension") { toml::spec spec = toml::spec::v(1,0,0); spec.ext_null_value = true; toml::detail::context ctx(spec); auto loc = toml::detail::make_temporary_location("null"); const auto res = toml::detail::parse_value(loc, ctx); if(res.is_err()) { std::cerr << format_error(res.unwrap_err()) << std::endl; } REQUIRE_UNARY(res.is_ok()); const auto val = res.unwrap(); REQUIRE_UNARY(val.is_empty()); } TEST_CASE("testing null value extension OFF") { toml::spec spec = toml::spec::v(1,0,0); toml::detail::context ctx(spec); auto loc = toml::detail::make_temporary_location("null"); const auto res = toml::detail::parse_value(loc, ctx); CHECK_UNARY(res.is_err()); } TEST_CASE("testing null value extension, w/ comments") { toml::spec spec = toml::spec::v(1,0,0); spec.ext_null_value = true; toml::detail::context ctx(spec); auto loc = toml::detail::make_temporary_location(R"(a = null # comment)"); toml::value table{toml::table()}; const auto res = toml::detail::parse_table(loc, ctx, table); if(res.is_err()) { std::cerr << format_error(res.unwrap_err()) << std::endl; } REQUIRE_UNARY(res.is_ok()); CHECK_UNARY(table.contains("a")); const auto null = table.at("a"); CHECK_UNARY(null.is_empty()); CHECK_EQ(null.comments().size(), 1); CHECK_EQ(null.comments().at(0), "# comment"); } TEST_CASE("testing null value extension, in an array") { toml::spec spec = toml::spec::v(1,0,0); spec.ext_null_value = true; toml::detail::context ctx(spec); auto loc = toml::detail::make_temporary_location(R"(a = [1, null, 3, 4, 5])"); toml::value table{toml::table()}; const auto res = toml::detail::parse_table(loc, ctx, table); if(res.is_err()) { std::cerr << format_error(res.unwrap_err()) << std::endl; } REQUIRE_UNARY(res.is_ok()); CHECK_UNARY(table.contains("a")); const auto a = table.at("a"); CHECK_UNARY(a.is_array()); CHECK_EQ(a.as_array().size(), 5); CHECK_UNARY(a.as_array().at(0).is_integer()); CHECK_UNARY(a.as_array().at(1).is_empty()); CHECK_UNARY(a.as_array().at(2).is_integer()); CHECK_UNARY(a.as_array().at(3).is_integer()); CHECK_UNARY(a.as_array().at(4).is_integer()); } toml11-4.1.0/tests/test_parse_string.cpp000066400000000000000000000252311464712047700202550ustar00rootroot00000000000000#define DOCTEST_CONFIG_IMPLEMENT_WITH_MAIN #include "doctest.h" #include "utility.hpp" #include #include #include TEST_CASE("testing basic string") { toml::detail::context ctx(toml::spec::v(1,0,0)); const auto string_fmt = []() { // inline basic_string does not use format settings toml::string_format_info fmt; fmt.fmt = toml::string_format::basic; return fmt; }; toml11_test_parse_success( "\"GitHub Cofounder & CEO\\nLikes tater tots and beer.\"", "GitHub Cofounder & CEO\nLikes tater tots and beer.", comments(), string_fmt(), ctx); toml11_test_parse_success( "\"192.168.1.1\"", "192.168.1.1", comments(), string_fmt(), ctx); toml11_test_parse_success( "\"\xE4\xB8\xAD\xE5\x9B\xBD\"", "\xE4\xB8\xAD\xE5\x9B\xBD", comments(), string_fmt(), ctx); toml11_test_parse_success( "\"You'll hate me after this - #\"", "You'll hate me after this - #", comments(), string_fmt(), ctx); toml11_test_parse_success( "\" And when \\\"'s are in the parse_ml_basic_string, along with # \\\"\"", " And when \"'s are in the parse_ml_basic_string, along with # \"", comments(), string_fmt(), ctx); // ------------------------------------------------------------------------- // the same test cases, but with parse_string. toml11_test_parse_success( "\"GitHub Cofounder & CEO\\nLikes tater tots and beer.\"", "GitHub Cofounder & CEO\nLikes tater tots and beer.", comments(), string_fmt(), ctx); toml11_test_parse_success( "\"192.168.1.1\"", "192.168.1.1", comments(), string_fmt(), ctx); toml11_test_parse_success( "\"\xE4\xB8\xAD\xE5\x9B\xBD\"", "\xE4\xB8\xAD\xE5\x9B\xBD", comments(), string_fmt(), ctx); toml11_test_parse_success( "\"You'll hate me after this - #\"", "You'll hate me after this - #", comments(), string_fmt(), ctx); toml11_test_parse_success( "\" And when \\\"'s are in the parse_ml_basic_string, along with # \\\"\"", " And when \"'s are in the parse_ml_basic_string, along with # \"", comments(), string_fmt(), ctx); } TEST_CASE("testing multiline basic string") { toml::detail::context ctx(toml::spec::v(1,0,0)); const auto string_fmt = [](bool nl) { toml::string_format_info fmt; fmt.fmt = toml::string_format::multiline_basic; fmt.start_with_newline = nl; return fmt; }; toml11_test_parse_success( // 0 1 // 01234567890123456 "\"\"\"\nThe quick brown \\\n\n fox jumps over \\\n the lazy dog.\"\"\"", "The quick brown fox jumps over the lazy dog.", comments(), string_fmt(true), ctx ); toml11_test_parse_success( // 0 1 // 012345678901234567 "\"\"\"\\\n The quick brown \\\n\n fox jumps over \\\n the lazy dog.\\\n \"\"\"", "The quick brown fox jumps over the lazy dog.", comments(), string_fmt(false), ctx ); toml11_test_parse_success( "\"\"\"Here are two quotation marks: \"\". Simple enough.\"\"\"", "Here are two quotation marks: \"\". Simple enough.", comments(), string_fmt(false), ctx ); toml11_test_parse_success( "\"\"\"Here are three quotation marks: \"\"\\\".\"\"\"", "Here are three quotation marks: \"\"\".", comments(), string_fmt(false), ctx ); toml11_test_parse_success( "\"\"\"Here are fifteen quotation marks: \"\"\\\"\"\"\\\"\"\"\\\"\"\"\\\"\"\"\\\".\"\"\"", "Here are fifteen quotation marks: \"\"\"\"\"\"\"\"\"\"\"\"\"\"\".", comments(), string_fmt(false), ctx ); toml11_test_parse_success( "\"\"\"\"This,\" she said, \"is just a pointless statement.\"\"\"\"", "\"This,\" she said, \"is just a pointless statement.\"", comments(), string_fmt(false), ctx ); // ------------------------------------------------------------------------- // the same test cases, but with parse_string. toml11_test_parse_success( // 0 1 // 01234567890123456 "\"\"\"\nThe quick brown \\\n\n fox jumps over \\\n the lazy dog.\"\"\"", "The quick brown fox jumps over the lazy dog.", comments(), string_fmt(true), ctx ); toml11_test_parse_success( // 0 1 // 012345678901234567 "\"\"\"\\\n The quick brown \\\n\n fox jumps over \\\n the lazy dog.\\\n \"\"\"", "The quick brown fox jumps over the lazy dog.", comments(), string_fmt(false), ctx ); toml11_test_parse_success( "\"\"\"Here are two quotation marks: \"\". Simple enough.\"\"\"", "Here are two quotation marks: \"\". Simple enough.", comments(), string_fmt(false), ctx ); toml11_test_parse_success( "\"\"\"Here are three quotation marks: \"\"\\\".\"\"\"", "Here are three quotation marks: \"\"\".", comments(), string_fmt(false), ctx ); toml11_test_parse_success( "\"\"\"Here are fifteen quotation marks: \"\"\\\"\"\"\\\"\"\"\\\"\"\"\\\"\"\"\\\".\"\"\"", "Here are fifteen quotation marks: \"\"\"\"\"\"\"\"\"\"\"\"\"\"\".", comments(), string_fmt(false), ctx ); toml11_test_parse_success( "\"\"\"\"This,\" she said, \"is just a pointless statement.\"\"\"\"", "\"This,\" she said, \"is just a pointless statement.\"", comments(), string_fmt(false), ctx ); } TEST_CASE("testing literal_string") { toml::detail::context ctx(toml::spec::v(1,0,0)); const auto string_fmt = []() { // inline basic_string does not use format settings toml::string_format_info fmt; fmt.fmt = toml::string_format::literal; return fmt; }; toml11_test_parse_success( "'C:\\Users\\nodejs\\templates'", "C:\\Users\\nodejs\\templates", comments(), string_fmt(), ctx); toml11_test_parse_success( "'\\\\ServerX\\admin$\\system32\\'", "\\\\ServerX\\admin$\\system32\\", comments(), string_fmt(), ctx); toml11_test_parse_success( "'Tom \"Dubs\" Preston-Werner'", "Tom \"Dubs\" Preston-Werner", comments(), string_fmt(), ctx); toml11_test_parse_success( "'<\\i\\c*\\s*>'", "<\\i\\c*\\s*>", comments(), string_fmt(), ctx); // ------------------------------------------------------------------------- // the same test cases, but with parse_string. toml11_test_parse_success( "'C:\\Users\\nodejs\\templates'", "C:\\Users\\nodejs\\templates", comments(), string_fmt(), ctx); toml11_test_parse_success( "'\\\\ServerX\\admin$\\system32\\'", "\\\\ServerX\\admin$\\system32\\", comments(), string_fmt(), ctx); toml11_test_parse_success( "'Tom \"Dubs\" Preston-Werner'", "Tom \"Dubs\" Preston-Werner", comments(), string_fmt(), ctx); toml11_test_parse_success( "'<\\i\\c*\\s*>'", "<\\i\\c*\\s*>", comments(), string_fmt(), ctx); } TEST_CASE("testing ml_literal_string") { toml::detail::context ctx(toml::spec::v(1,0,0)); const auto string_fmt = [](bool nl) { toml::string_format_info fmt; fmt.fmt = toml::string_format::multiline_literal; fmt.start_with_newline = nl; return fmt; }; toml11_test_parse_success( "'''I [dw]on't need \\d{2} apples'''", "I [dw]on't need \\d{2} apples", comments(), string_fmt(false), ctx); toml11_test_parse_success( "'''\nThe first newline is\ntrimmed in raw strings.\n All other whitespace\n is preserved.\n'''", "The first newline is\ntrimmed in raw strings.\n All other whitespace\n is preserved.\n", comments(), string_fmt(true), ctx); toml11_test_parse_success( "''''That's still pointless', she said.'''", "'That's still pointless', she said.", comments(), string_fmt(false), ctx); toml11_test_parse_success( "'''Here are fifteen quotation marks: \"\"\"\"\"\"\"\"\"\"\"\"\"\"\".'''", "Here are fifteen quotation marks: \"\"\"\"\"\"\"\"\"\"\"\"\"\"\".", comments(), string_fmt(false), ctx); toml11_test_parse_success( "''''This,' she said, 'is just a pointless statement.''''", "'This,' she said, 'is just a pointless statement.'", comments(), string_fmt(false), ctx); // ------------------------------------------------------------------------- // the same test cases, but with parse_string. toml11_test_parse_success( "'''I [dw]on't need \\d{2} apples'''", "I [dw]on't need \\d{2} apples", comments(), string_fmt(false), ctx); toml11_test_parse_success( "'''\nThe first newline is\ntrimmed in raw strings.\n All other whitespace\n is preserved.\n'''", "The first newline is\ntrimmed in raw strings.\n All other whitespace\n is preserved.\n", comments(), string_fmt(true), ctx); toml11_test_parse_success( "''''That's still pointless', she said.'''", "'That's still pointless', she said.", comments(), string_fmt(false), ctx); toml11_test_parse_success( "'''Here are fifteen quotation marks: \"\"\"\"\"\"\"\"\"\"\"\"\"\"\".'''", "Here are fifteen quotation marks: \"\"\"\"\"\"\"\"\"\"\"\"\"\"\".", comments(), string_fmt(false), ctx); toml11_test_parse_success( "''''This,' she said, 'is just a pointless statement.''''", "'This,' she said, 'is just a pointless statement.'", comments(), string_fmt(false), ctx); } toml11-4.1.0/tests/test_parse_table.cpp000066400000000000000000000135141464712047700200370ustar00rootroot00000000000000#define DOCTEST_CONFIG_IMPLEMENT_WITH_MAIN #include #include "utility.hpp" #include #include #include "doctest.h" TEST_CASE("testing a table") { auto spec = toml::spec::v(1,0,0); { toml::detail::context ctx(spec); auto loc = toml::detail::make_temporary_location(R"(a = "foo")"); toml::value table{toml::table()}; const auto res = toml::detail::parse_table(loc, ctx, table); REQUIRE_UNARY(res.is_ok()); REQUIRE_UNARY(table.is_table()); CHECK_UNARY(table.as_table().at("a").is_string()); CHECK_EQ (table.as_table().at("a").as_string(), "foo"); CHECK_UNARY(table.at("a").is_string()); CHECK_EQ (table.at("a").as_string(), "foo"); CHECK_EQ(table.as_table_fmt().indent_type, toml::indent_char::none); CHECK_EQ(table.as_table_fmt().name_indent, 0); CHECK_EQ(table.as_table_fmt().body_indent, 0); CHECK_EQ(table.as_table_fmt().closing_indent, 0); } { toml::detail::context ctx(spec); auto loc = toml::detail::make_temporary_location(" a = \"foo\""); toml::value table{toml::table()}; const auto res = toml::detail::parse_table(loc, ctx, table); REQUIRE_UNARY(res.is_ok()); REQUIRE_UNARY(table.is_table()); CHECK_UNARY(table.as_table().at("a").is_string()); CHECK_EQ (table.as_table().at("a").as_string(), "foo"); CHECK_UNARY(table.at("a").is_string()); CHECK_EQ (table.at("a").as_string(), "foo"); CHECK_EQ(table.as_table_fmt().indent_type, toml::indent_char::space); CHECK_EQ(table.as_table_fmt().name_indent, 0); CHECK_EQ(table.as_table_fmt().body_indent, 2); CHECK_EQ(table.as_table_fmt().closing_indent, 0); } { toml::detail::context ctx(spec); auto loc = toml::detail::make_temporary_location("\ta = \"foo\""); toml::value table{toml::table()}; const auto res = toml::detail::parse_table(loc, ctx, table); REQUIRE_UNARY(res.is_ok()); REQUIRE_UNARY(table.is_table()); CHECK_UNARY(table.as_table().at("a").is_string()); CHECK_EQ (table.as_table().at("a").as_string(), "foo"); CHECK_UNARY(table.at("a").is_string()); CHECK_EQ (table.at("a").as_string(), "foo"); CHECK_EQ(table.as_table_fmt().indent_type, toml::indent_char::tab); CHECK_EQ(table.as_table_fmt().name_indent, 0); CHECK_EQ(table.as_table_fmt().body_indent, 1); CHECK_EQ(table.as_table_fmt().closing_indent, 0); } { toml::detail::context ctx(spec); auto loc = toml::detail::make_temporary_location(R"(a = "foo" b = "bar" c.c1 = "baz" c.c2 = "qux" )"); toml::value table{toml::table()}; const auto res = toml::detail::parse_table(loc, ctx, table); REQUIRE_UNARY(res.is_ok()); REQUIRE_UNARY(table.is_table()); CHECK_UNARY(table.at("a").is_string()); CHECK_EQ (table.at("a").as_string(), "foo"); CHECK_UNARY(table.at("b").is_string()); CHECK_EQ (table.at("b").as_string(), "bar"); CHECK_UNARY(table.at("c").at("c1").is_string()); CHECK_EQ (table.at("c").at("c1").as_string(), "baz"); CHECK_UNARY(table.at("c").at("c2").is_string()); CHECK_EQ (table.at("c").at("c2").as_string(), "qux"); CHECK_EQ(table.as_table_fmt().indent_type, toml::indent_char::none); CHECK_EQ(table.as_table_fmt().name_indent, 0); CHECK_EQ(table.as_table_fmt().body_indent, 0); CHECK_EQ(table.as_table_fmt().closing_indent, 0); } { toml::detail::context ctx(spec); auto loc = toml::detail::make_temporary_location(R"(a = "foo" b = "bar" c.c1 = "baz" [next.table] c.c2 = "qux" )"); toml::value table{toml::table()}; const auto res = toml::detail::parse_table(loc, ctx, table); REQUIRE_UNARY(res.is_ok()); REQUIRE_UNARY(table.is_table()); CHECK_UNARY(table.at("a").is_string()); CHECK_EQ (table.at("a").as_string(), "foo"); CHECK_UNARY(table.at("b").is_string()); CHECK_EQ (table.at("b").as_string(), "bar"); CHECK_UNARY(table.at("c").at("c1").is_string()); CHECK_EQ (table.at("c").at("c1").as_string(), "baz"); CHECK_UNARY( ! table.at("c").contains("c2")); CHECK_EQ(table.as_table_fmt().indent_type, toml::indent_char::none); CHECK_EQ(table.as_table_fmt().name_indent, 0); CHECK_EQ(table.as_table_fmt().body_indent, 0); CHECK_EQ(table.as_table_fmt().closing_indent, 0); } { toml::detail::context ctx(spec); auto loc = toml::detail::make_temporary_location(R"( a = "foo" b = "bar" c.c1 = "baz" [next.table] c.c2 = "qux" )"); toml::value table{toml::table()}; const auto res = toml::detail::parse_table(loc, ctx, table); REQUIRE_UNARY(res.is_ok()); REQUIRE_UNARY(table.is_table()); CHECK_UNARY(table.at("a").is_string()); CHECK_EQ (table.at("a").as_string(), "foo"); CHECK_UNARY(table.at("b").is_string()); CHECK_EQ (table.at("b").as_string(), "bar"); CHECK_UNARY(table.at("c").at("c1").is_string()); CHECK_EQ (table.at("c").at("c1").as_string(), "baz"); CHECK_UNARY( ! table.at("c").contains("c2")); CHECK_EQ(table.as_table_fmt().indent_type, toml::indent_char::space); CHECK_EQ(table.as_table_fmt().name_indent, 0); CHECK_EQ(table.as_table_fmt().body_indent, 4); CHECK_EQ(table.as_table_fmt().closing_indent, 0); } } toml11-4.1.0/tests/test_parse_table_keys.cpp000066400000000000000000000070451464712047700210740ustar00rootroot00000000000000#define DOCTEST_CONFIG_IMPLEMENT_WITH_MAIN #include #include "utility.hpp" #include #include #include "doctest.h" TEST_CASE("testing table keys") { toml::detail::context ctx(toml::spec::v(1,0,0)); { auto loc = toml::detail::make_temporary_location("[one-key]"); const auto res = toml::detail::parse_table_key(loc, ctx); REQUIRE_UNARY(res.is_ok()); const auto val = std::get<0>(res.unwrap()); REQUIRE_UNARY(val.size() == 1); REQUIRE_UNARY(val.at(0) == "one-key"); } { auto loc = toml::detail::make_temporary_location("[many.keys]"); const auto res = toml::detail::parse_table_key(loc, ctx); REQUIRE_UNARY(res.is_ok()); const auto val = std::get<0>(res.unwrap()); REQUIRE_UNARY(val.size() == 2); REQUIRE_UNARY(val.at(0) == "many"); REQUIRE_UNARY(val.at(1) == "keys"); } { auto loc = toml::detail::make_temporary_location("[ many . keys . with . spaces ]"); const auto res = toml::detail::parse_table_key(loc, ctx); REQUIRE_UNARY(res.is_ok()); const auto val = std::get<0>(res.unwrap()); REQUIRE_UNARY(val.size() == 4); REQUIRE_UNARY(val.at(0) == "many"); REQUIRE_UNARY(val.at(1) == "keys"); REQUIRE_UNARY(val.at(2) == "with"); REQUIRE_UNARY(val.at(3) == "spaces"); } { auto loc = toml::detail::make_temporary_location("[ \"one.long.key\" ]"); const auto res = toml::detail::parse_table_key(loc, ctx); REQUIRE_UNARY(res.is_ok()); const auto val = std::get<0>(res.unwrap()); REQUIRE_UNARY(val.size() == 1); REQUIRE_UNARY(val.at(0) == "one.long.key"); } } TEST_CASE("testing array table keys") { toml::detail::context ctx(toml::spec::v(1,0,0)); { auto loc = toml::detail::make_temporary_location("[[one-key]]"); const auto res = toml::detail::parse_array_table_key(loc, ctx); REQUIRE_UNARY(res.is_ok()); const auto val = std::get<0>(res.unwrap()); REQUIRE_UNARY(val.size() == 1); REQUIRE_UNARY(val.at(0) == "one-key"); } { auto loc = toml::detail::make_temporary_location("[[many.keys]]"); const auto res = toml::detail::parse_array_table_key(loc, ctx); REQUIRE_UNARY(res.is_ok()); const auto val = std::get<0>(res.unwrap()); REQUIRE_UNARY(val.size() == 2); REQUIRE_UNARY(val.at(0) == "many"); REQUIRE_UNARY(val.at(1) == "keys"); } { auto loc = toml::detail::make_temporary_location("[[ many . keys . with . spaces ]]"); const auto res = toml::detail::parse_array_table_key(loc, ctx); REQUIRE_UNARY(res.is_ok()); const auto val = std::get<0>(res.unwrap()); REQUIRE_UNARY(val.size() == 4); REQUIRE_UNARY(val.at(0) == "many"); REQUIRE_UNARY(val.at(1) == "keys"); REQUIRE_UNARY(val.at(2) == "with"); REQUIRE_UNARY(val.at(3) == "spaces"); } { auto loc = toml::detail::make_temporary_location("[[ \"one.long.key\" ]]"); const auto res = toml::detail::parse_array_table_key(loc, ctx); REQUIRE_UNARY(res.is_ok()); const auto val = std::get<0>(res.unwrap()); REQUIRE_UNARY(val.size() == 1); REQUIRE_UNARY(val.at(0) == "one.long.key"); } } toml11-4.1.0/tests/test_result.cpp000066400000000000000000000076111464712047700170750ustar00rootroot00000000000000#define DOCTEST_CONFIG_IMPLEMENT_WITH_MAIN #include "doctest.h" #include TEST_CASE("testing constructor") { { auto s = toml::ok(42); toml::result result(s); CHECK(!!result); CHECK(result.is_ok()); CHECK(!result.is_err()); CHECK(result.unwrap() == 42); } { const auto s = toml::ok(42); toml::result result(s); CHECK(!!result); CHECK(result.is_ok()); CHECK(!result.is_err()); CHECK(result.unwrap() == 42); } { toml::result result(toml::ok(42)); CHECK(!!result); CHECK(result.is_ok()); CHECK(!result.is_err()); CHECK(result.unwrap() == 42); } { auto f = toml::err("foobar"); toml::result result(f); CHECK(!result); CHECK(!result.is_ok()); CHECK(result.is_err()); CHECK(result.unwrap_err() == "foobar"); } { const auto f = toml::err("foobar"); toml::result result(f); CHECK(!result); CHECK(!result.is_ok()); CHECK(result.is_err()); CHECK(result.unwrap_err() == "foobar"); } { toml::result result(toml::err("foobar")); CHECK(!result); CHECK(!result.is_ok()); CHECK(result.is_err()); CHECK(result.unwrap_err() == "foobar"); } } TEST_CASE("testing assignment op") { { toml::result result(toml::err("foobar")); result = toml::ok(42); CHECK(!!result); CHECK(result.is_ok()); CHECK(!result.is_err()); CHECK(result.unwrap() == 42); } { toml::result result(toml::err("foobar")); auto s = toml::ok(42); result = s; CHECK(!!result); CHECK(result.is_ok()); CHECK(!result.is_err()); CHECK(result.unwrap() == 42); } { toml::result result(toml::err("foobar")); const auto s = toml::ok(42); result = s; CHECK(!!result); CHECK(result.is_ok()); CHECK(!result.is_err()); CHECK(result.unwrap() == 42); } { toml::result result(toml::err("foobar")); result = toml::err("hoge"); CHECK(!result); CHECK(!result.is_ok()); CHECK(result.is_err()); CHECK(result.unwrap_err() == "hoge"); } { toml::result result(toml::err("foobar")); auto f = toml::err("hoge"); result = f; CHECK(!result); CHECK(!result.is_ok()); CHECK(result.is_err()); CHECK(result.unwrap_err() == "hoge"); } { toml::result result(toml::err("foobar")); const auto f = toml::err("hoge"); result = f; CHECK(!result); CHECK(!result.is_ok()); CHECK(result.is_err()); CHECK(result.unwrap_err() == "hoge"); } } TEST_CASE("testing result") { { int a = 42; toml::result, std::string> result(toml::ok(std::ref(a))); CHECK_UNARY(result); CHECK_UNARY(result.is_ok()); CHECK_UNARY_FALSE(result.is_err()); CHECK_EQ(result.unwrap(), 42); CHECK_EQ(a, 42); result.unwrap() = 6 * 9; CHECK_EQ(result.unwrap(), 6*9); CHECK_EQ(a, 6*9); } { std::string b = "foo"; toml::result> result(toml::err(std::ref(b))); CHECK_UNARY_FALSE(result); CHECK_UNARY_FALSE(result.is_ok()); CHECK_UNARY(result.is_err()); CHECK_EQ(result.unwrap_err(), "foo"); CHECK_EQ(b, "foo"); result.unwrap_err() = "foobar"; CHECK_EQ(result.unwrap_err(), "foobar"); CHECK_EQ(b, "foobar"); } } toml11-4.1.0/tests/test_scanner.cpp000066400000000000000000000120541464712047700172050ustar00rootroot00000000000000#define DOCTEST_CONFIG_IMPLEMENT_WITH_MAIN #include "doctest.h" #include "utility.hpp" #include TEST_CASE("testing scanner: character") { auto loc = toml::detail::make_temporary_location("\t \tA"); toml::detail::character tab('\t'); toml::detail::character space(' '); CHECK_UNARY( tab .scan(loc).is_ok()); // loc += 1 CHECK_UNARY(!tab .scan(loc).is_ok()); CHECK_UNARY( space.scan(loc).is_ok()); CHECK_UNARY( tab .scan(loc).is_ok()); CHECK_UNARY(!tab .scan(loc).is_ok()); CHECK_UNARY(!space.scan(loc).is_ok()); } TEST_CASE("testing scanner: character_either") { auto loc = toml::detail::make_temporary_location("\t \t01\0"); toml::detail::character_either wschar{'\t', ' '}; toml::detail::character_either digit01("01"); CHECK_UNARY( wschar .scan(loc).is_ok()); CHECK_UNARY( wschar .scan(loc).is_ok()); CHECK_UNARY( wschar .scan(loc).is_ok()); CHECK_UNARY(!wschar .scan(loc).is_ok()); CHECK_UNARY( digit01.scan(loc).is_ok()); CHECK_UNARY( digit01.scan(loc).is_ok()); CHECK_UNARY(!digit01.scan(loc).is_ok()); CHECK_UNARY(!wschar .scan(loc).is_ok()); } TEST_CASE("testing scanner: character_in_range") { auto loc = toml::detail::make_temporary_location("abcdz12349"); toml::detail::character_in_range alpha('a', 'z'); toml::detail::character_in_range digit('0', '9'); CHECK_UNARY( alpha.scan(loc).is_ok()); CHECK_UNARY( alpha.scan(loc).is_ok()); CHECK_UNARY( alpha.scan(loc).is_ok()); CHECK_UNARY( alpha.scan(loc).is_ok()); CHECK_UNARY( alpha.scan(loc).is_ok()); CHECK_UNARY(!alpha.scan(loc).is_ok()); CHECK_UNARY( digit.scan(loc).is_ok()); CHECK_UNARY( digit.scan(loc).is_ok()); CHECK_UNARY( digit.scan(loc).is_ok()); CHECK_UNARY( digit.scan(loc).is_ok()); CHECK_UNARY( digit.scan(loc).is_ok()); CHECK_UNARY(!digit.scan(loc).is_ok()); } TEST_CASE("testing scanner: literal") { auto loc = toml::detail::make_temporary_location("abcdz12349"); toml::detail::literal abcd("abcd"); toml::detail::literal z123("z123"); CHECK_UNARY( abcd.scan(loc).is_ok()); CHECK_UNARY(!abcd.scan(loc).is_ok()); CHECK_UNARY( z123.scan(loc).is_ok()); CHECK_UNARY(!z123.scan(loc).is_ok()); CHECK_UNARY(!abcd.scan(loc).is_ok()); } TEST_CASE("testing scanner: sequence") { auto loc = toml::detail::make_temporary_location("abcdz12349"); toml::detail::literal abcd("abcd"); toml::detail::literal z123("z123"); toml::detail::sequence seq(abcd, z123); CHECK_UNARY( seq.scan(loc).is_ok()); CHECK_EQ( loc.current(), toml::detail::location::char_type('4')); auto loc2 = toml::detail::make_temporary_location("abcde12349"); CHECK_UNARY(!seq.scan(loc2).is_ok()); } TEST_CASE("testing scanner: either") { auto loc = toml::detail::make_temporary_location("abcdz123"); toml::detail::literal abcd("abcd"); toml::detail::literal z123("z123"); toml::detail::either e(abcd, z123); CHECK_UNARY( e.scan(loc).is_ok()); CHECK_EQ( loc.current(), toml::detail::location::char_type('z')); CHECK_UNARY( e.scan(loc).is_ok()); CHECK_UNARY( loc.eof() ); } TEST_CASE("testing scanner: repeat_exact") { { auto loc = toml::detail::make_temporary_location("foofoobar"); toml::detail::repeat_exact r(2, toml::detail::literal("foo")); CHECK_UNARY( r.scan(loc).is_ok()); CHECK_EQ( loc.current(), toml::detail::location::char_type('b')); } { auto loc = toml::detail::make_temporary_location("foofoobar"); toml::detail::repeat_exact r(3, toml::detail::literal("foo")); CHECK_UNARY( ! r.scan(loc).is_ok()); } } TEST_CASE("testing scanner: repeat_at_least") { { auto loc = toml::detail::make_temporary_location("foofoobar"); toml::detail::repeat_at_least r(1, toml::detail::literal("foo")); CHECK_UNARY( r.scan(loc).is_ok()); CHECK_EQ( loc.current(), toml::detail::location::char_type('b')); } { auto loc = toml::detail::make_temporary_location("foofoobar"); toml::detail::repeat_at_least r(2, toml::detail::literal("foo")); CHECK_UNARY( r.scan(loc).is_ok()); CHECK_EQ( loc.current(), toml::detail::location::char_type('b')); } { auto loc = toml::detail::make_temporary_location("foofoobar"); toml::detail::repeat_at_least r(3, toml::detail::literal("foo")); CHECK_UNARY( ! r.scan(loc).is_ok()); } } TEST_CASE("testing scanner: maybe") { { auto loc = toml::detail::make_temporary_location("foofoobar"); toml::detail::maybe r(toml::detail::literal("foo")); CHECK_UNARY( r.scan(loc).is_ok()); CHECK_EQ( loc.current(), toml::detail::location::char_type('f')); CHECK_EQ( loc.get_location(), 3); } { auto loc = toml::detail::make_temporary_location("foofoobar"); toml::detail::maybe r(toml::detail::literal("bar")); CHECK_UNARY( r.scan(loc).is_ok()); CHECK_EQ( loc.current(), toml::detail::location::char_type('f')); CHECK_EQ( loc.get_location(), 0); } } toml11-4.1.0/tests/test_serialize.cpp000066400000000000000000000057221464712047700175470ustar00rootroot00000000000000#define DOCTEST_CONFIG_IMPLEMENT_WITH_MAIN #include "doctest.h" #include #include TEST_CASE("testing serialization") { using namespace toml::literals::toml_literals; const auto spec_example = R"(# This is a TOML document title = "TOML Example" [owner] name = "Tom Preston-Werner" dob = 1979-05-27T07:32:00-08:00 [database] enabled = true ports = [ 8000, 8001, 8002 ] data = [ ["delta", "phi"], [3.14] ] temp_targets = { cpu = 79.5, case = 72.0 } [servers] [servers.alpha] ip = "10.0.0.1" role = "frontend" [servers.beta] ip = "10.0.0.2" role = "backend")"_toml; // format and parse const auto v = toml::parse_str(toml::format(spec_example)); CHECK_EQ(v, spec_example); } TEST_CASE("testing serialization with complicated keys") { using namespace toml::literals::toml_literals; const auto spec_example = R"( [keys] key = "value" bare_key = "value" bare-key = "value" 1234 = "value" "127.0.0.1" = "value" "character encoding" = "value" "ʎǝʞ" = "value" 'key2' = "value" 'quoted "value"' = "value" "" = "blank" fruits.apple.skin = "thin" fruits.apple.color = "red" fruits.orange.skin = "thick" fruits.orange.color = "orange" site."google.com" = true 3.14159 = "pi" )"_toml; // format and parse const auto v = toml::parse_str(toml::format(spec_example)); CHECK_EQ(v, spec_example); } TEST_CASE("testing serialization with array of tables") { using namespace toml::literals::toml_literals; const auto spec_example = R"( points = [ { x = 1, y = 2, z = 3 }, { x = 7, y = 8, z = 9 }, { x = 2, y = 4, z = 8 } ] [[products]] name = "Hammer" sku = 738594937 [[products]] # empty table within the array [[products]] name = "Nail" sku = 284758393 color = "gray" [[fruits]] name = "apple" [fruits.physical] # subtable color = "red" shape = "round" [[fruits.varieties]] # nested array of tables name = "red delicious" [[fruits.varieties]] name = "granny smith" [[fruits]] name = "banana" [[fruits.varieties]] name = "plantain" )"_toml; // format and parse const auto v = toml::parse_str(toml::format(spec_example)); CHECK_EQ(v, spec_example); } TEST_CASE("testing serialization with locale") { std::string current_locale = std::setlocale(LC_ALL, nullptr); // fr_FR is a one of locales that uses `,` as a decimal separator. if(const char* try_hyphen = std::setlocale(LC_ALL, "fr_FR.UTF-8")) { current_locale = std::string(try_hyphen); } else if(const char* try_nohyphen = std::setlocale(LC_ALL, "fr_FR.utf8")) { current_locale = std::string(try_nohyphen); } MESSAGE("current_locale = ", current_locale); const auto v1 = toml::parse_str(R"( pi = 3.1415_9265 large_int = 123_456_789 )"); const auto v2 = toml::parse_str(toml::format(v1)); // actually, it checkl if v1 is serialized correctly under FR locale // where 3.1415 -> 3,1415 CHECK_EQ(v1, v2); } toml11-4.1.0/tests/test_spec.cpp000066400000000000000000000027041464712047700165070ustar00rootroot00000000000000#define DOCTEST_CONFIG_IMPLEMENT_WITH_MAIN #include "doctest.h" #include TEST_CASE("testing constructor of semantic_version") { constexpr auto v123 = toml::semantic_version(1, 2, 3); CHECK(v123.major == 1); CHECK(v123.minor == 2); CHECK(v123.patch == 3); } TEST_CASE("testing the ordering of semantic_version") { constexpr auto v100 = toml::semantic_version(1, 0, 0); constexpr auto v101 = toml::semantic_version(1, 0, 1); constexpr auto v110 = toml::semantic_version(1, 1, 0); constexpr auto v112 = toml::semantic_version(1, 1, 2); constexpr auto v121 = toml::semantic_version(1, 2, 1); CHECK(v100 == v100); CHECK(v100 >= v100); CHECK(v100 <= v100); CHECK_FALSE(v100 != v100); CHECK_FALSE(v100 > v100); CHECK_FALSE(v100 < v100); CHECK(v100 != v101); CHECK(v100 <= v101); CHECK(v100 < v101); CHECK_FALSE(v100 == v101); CHECK_FALSE(v100 >= v101); CHECK_FALSE(v100 > v101); CHECK(v101 != v110); CHECK(v101 < v110); CHECK(v101 <= v110); CHECK_FALSE(v101 == v110); CHECK_FALSE(v101 > v110); CHECK_FALSE(v101 >= v110); CHECK(v110 != v101); CHECK(v110 > v101); CHECK(v110 >= v101); CHECK_FALSE(v110 == v101); CHECK_FALSE(v110 < v101); CHECK_FALSE(v110 <= v101); CHECK(v112 != v121); CHECK(v112 < v121); CHECK(v112 <= v121); CHECK(v121 != v112); CHECK(v121 > v112); CHECK(v121 >= v112); } toml11-4.1.0/tests/test_storage.cpp000066400000000000000000000025531464712047700172230ustar00rootroot00000000000000#define DOCTEST_CONFIG_IMPLEMENT_WITH_MAIN #include "doctest.h" #include TEST_CASE("testing storage construct") { toml::detail::storage x(42); REQUIRE_UNARY(x.is_ok()); CHECK_EQ(x.get(), 42); CHECK_NE(x.get(), 6*9); x.get() = 6 * 9; CHECK_EQ(x.get(), 6*9); CHECK_NE(x.get(), 42); } TEST_CASE("testing storage copy") { toml::detail::storage x(42); toml::detail::storage y(x); REQUIRE_UNARY(x.is_ok()); REQUIRE_UNARY(y.is_ok()); CHECK_EQ(x.get(), 42); CHECK_NE(x.get(), 6*9); CHECK_EQ(y.get(), 42); CHECK_NE(y.get(), 6*9); x.get() = 6 * 9; CHECK_EQ(x.get(), 6*9); CHECK_NE(x.get(), 42); CHECK_EQ(y.get(), 42); CHECK_NE(y.get(), 6*9); x = y; CHECK_EQ(x.get(), 42); CHECK_NE(x.get(), 6*9); CHECK_EQ(y.get(), 42); CHECK_NE(y.get(), 6*9); } TEST_CASE("testing storage move") { toml::detail::storage x(42); toml::detail::storage y(x); REQUIRE_UNARY(x.is_ok()); REQUIRE_UNARY(y.is_ok()); CHECK_EQ(x.get(), 42); CHECK_NE(x.get(), 6*9); CHECK_EQ(y.get(), 42); CHECK_NE(y.get(), 6*9); x.get() = 6 * 9; CHECK_EQ(x.get(), 6*9); CHECK_NE(x.get(), 42); CHECK_EQ(y.get(), 42); CHECK_NE(y.get(), 6*9); x = std::move(y); CHECK_EQ(x.get(), 42); CHECK_NE(x.get(), 6*9); } toml11-4.1.0/tests/test_syntax_boolean.cpp000066400000000000000000000014701464712047700206010ustar00rootroot00000000000000#define DOCTEST_CONFIG_IMPLEMENT_WITH_MAIN #include "doctest.h" #include "utility.hpp" #include TEST_CASE("testing boolean should success") { const auto scanner = toml::detail::syntax::boolean(toml::spec::v(1,0,0)); test_scan_success(scanner, "true", "true"); test_scan_success(scanner, "false", "false"); test_scan_success(scanner, "true # comment", "true"); test_scan_success(scanner, "false # comment", "false"); } TEST_CASE("testing boolean should fail") { const auto scanner = toml::detail::syntax::boolean(toml::spec::v(1,0,0)); test_scan_failure(scanner, "TRUE"); test_scan_failure(scanner, "FALSE"); test_scan_failure(scanner, "True"); test_scan_failure(scanner, "False"); test_scan_failure(scanner, "T"); test_scan_failure(scanner, "F"); } toml11-4.1.0/tests/test_syntax_comment.cpp000066400000000000000000000006731464712047700206300ustar00rootroot00000000000000#define DOCTEST_CONFIG_IMPLEMENT_WITH_MAIN #include "doctest.h" #include "utility.hpp" #include TEST_CASE("testing comment") { const auto scanner = toml::detail::syntax::comment(toml::spec::v(1,0,0)); test_scan_success(scanner, "# hoge", "# hoge"); test_scan_success(scanner, "# \n", "# "); test_scan_success(scanner, "# \r\n", "# "); test_scan_success(scanner, "# # \n", "# # "); } toml11-4.1.0/tests/test_syntax_datetime.cpp000066400000000000000000000104671464712047700207640ustar00rootroot00000000000000#define DOCTEST_CONFIG_IMPLEMENT_WITH_MAIN #include "doctest.h" #include "utility.hpp" #include TEST_CASE("testing offset_datetime") { { const auto scanner = toml::detail::syntax::offset_datetime(toml::spec::v(1,0,0)); test_scan_success(scanner, "1979-05-27T07:32:00Z", "1979-05-27T07:32:00Z"); test_scan_success(scanner, "1979-05-27T07:32:00-07:00", "1979-05-27T07:32:00-07:00"); test_scan_success(scanner, "1979-05-27T07:32:00.999999-07:00", "1979-05-27T07:32:00.999999-07:00"); test_scan_success(scanner, "1979-05-27 07:32:00Z", "1979-05-27 07:32:00Z"); test_scan_success(scanner, "1979-05-27 07:32:00-07:00", "1979-05-27 07:32:00-07:00"); test_scan_success(scanner, "1979-05-27 07:32:00.999999-07:00", "1979-05-27 07:32:00.999999-07:00"); } { auto spec = toml::spec::v(1,0,0); spec.v1_1_0_make_seconds_optional = true; const auto scanner = toml::detail::syntax::offset_datetime(spec); test_scan_success(scanner, "1979-05-27T07:32Z", "1979-05-27T07:32Z"); test_scan_success(scanner, "1979-05-27T07:32:00Z", "1979-05-27T07:32:00Z"); test_scan_success(scanner, "1979-05-27T07:32:00-07:00", "1979-05-27T07:32:00-07:00"); test_scan_success(scanner, "1979-05-27T07:32:00.999999-07:00", "1979-05-27T07:32:00.999999-07:00"); test_scan_success(scanner, "1979-05-27 07:32Z", "1979-05-27 07:32Z"); test_scan_success(scanner, "1979-05-27 07:32:00Z", "1979-05-27 07:32:00Z"); test_scan_success(scanner, "1979-05-27 07:32:00-07:00", "1979-05-27 07:32:00-07:00"); test_scan_success(scanner, "1979-05-27 07:32:00.999999-07:00", "1979-05-27 07:32:00.999999-07:00"); } } TEST_CASE("testing local_datetime") { { const auto scanner = toml::detail::syntax::local_datetime(toml::spec::v(1,0,0)); test_scan_success(scanner, "1979-05-27T07:32:00", "1979-05-27T07:32:00"); test_scan_success(scanner, "1979-05-27T07:32:00.999999", "1979-05-27T07:32:00.999999"); test_scan_success(scanner, "1979-05-27 07:32:00", "1979-05-27 07:32:00"); test_scan_success(scanner, "1979-05-27 07:32:00.999999", "1979-05-27 07:32:00.999999"); } { auto spec = toml::spec::v(1,0,0); spec.v1_1_0_make_seconds_optional = true; const auto scanner = toml::detail::syntax::local_datetime(spec); test_scan_success(scanner, "1979-05-27T07:32", "1979-05-27T07:32"); test_scan_success(scanner, "1979-05-27T07:32:00", "1979-05-27T07:32:00"); test_scan_success(scanner, "1979-05-27T07:32:00.999999", "1979-05-27T07:32:00.999999"); test_scan_success(scanner, "1979-05-27 07:32:00", "1979-05-27 07:32:00"); test_scan_success(scanner, "1979-05-27 07:32:00.999999", "1979-05-27 07:32:00.999999"); } } TEST_CASE("testing local_date") { const auto scanner = toml::detail::syntax::local_date(toml::spec::v(1,0,0)); test_scan_success(scanner, "1979-05-27", "1979-05-27"); } TEST_CASE("testing local_time") { { const auto scanner = toml::detail::syntax::local_time(toml::spec::v(1,0,0)); test_scan_success(scanner, "07:32:00", "07:32:00"); test_scan_success(scanner, "07:32:00.999999", "07:32:00.999999"); } { auto spec = toml::spec::v(1,0,0); spec.v1_1_0_make_seconds_optional = true; const auto scanner = toml::detail::syntax::local_time(spec); test_scan_success(scanner, "07:32", "07:32"); test_scan_success(scanner, "07:32:00", "07:32:00"); test_scan_success(scanner, "07:32:00.999999", "07:32:00.999999"); } } toml11-4.1.0/tests/test_syntax_floating.cpp000066400000000000000000000077411464712047700207740ustar00rootroot00000000000000#define DOCTEST_CONFIG_IMPLEMENT_WITH_MAIN #include "doctest.h" #include "utility.hpp" #include TEST_CASE("testing fractional_valid") { const auto floating = toml::detail::syntax::floating(toml::spec::v(1,0,0)); test_scan_success(floating, "1.0", "1.0" ); test_scan_success(floating, "0.1", "0.1" ); test_scan_success(floating, "0.001", "0.001" ); test_scan_success(floating, "0.100", "0.100" ); test_scan_success(floating, "+3.14", "+3.14" ); test_scan_success(floating, "-3.14", "-3.14" ); test_scan_success(floating, "3.1415_9265_3589", "3.1415_9265_3589" ); test_scan_success(floating, "+3.1415_9265_3589", "+3.1415_9265_3589"); test_scan_success(floating, "-3.1415_9265_3589", "-3.1415_9265_3589"); test_scan_success(floating, "123_456.789", "123_456.789" ); test_scan_success(floating, "+123_456.789", "+123_456.789" ); test_scan_success(floating, "-123_456.789", "-123_456.789" ); } TEST_CASE("testing fractional_invalid") { const auto floating = toml::detail::syntax::floating(toml::spec::v(1,0,0)); test_scan_failure(floating, "0."); test_scan_failure(floating, ".0"); test_scan_failure(floating, "01.0"); test_scan_failure(floating, "3,14"); test_scan_failure(floating, "+-1.0"); test_scan_failure(floating, "1._0"); } TEST_CASE("testing exponential_valid") { const auto floating = toml::detail::syntax::floating(toml::spec::v(1,0,0)); test_scan_success(floating, "1e10", "1e10"); test_scan_success(floating, "1e+10", "1e+10"); test_scan_success(floating, "1e-10", "1e-10"); test_scan_success(floating, "+1e10", "+1e10"); test_scan_success(floating, "+1e+10", "+1e+10"); test_scan_success(floating, "+1e-10", "+1e-10"); test_scan_success(floating, "-1e10", "-1e10"); test_scan_success(floating, "-1e+10", "-1e+10"); test_scan_success(floating, "-1e-10", "-1e-10"); test_scan_success(floating, "123e-10", "123e-10"); test_scan_success(floating, "1E10", "1E10"); test_scan_success(floating, "1E+10", "1E+10"); test_scan_success(floating, "1E-10", "1E-10"); test_scan_success(floating, "123E-10", "123E-10"); test_scan_success(floating, "1_2_3E-10", "1_2_3E-10"); test_scan_success(floating, "1_2_3E-1_0", "1_2_3E-1_0"); test_scan_success(floating, "1_2_3E-01", "1_2_3E-01"); test_scan_success(floating, "1_2_3E-0_1", "1_2_3E-0_1"); } TEST_CASE("testing exponential_invalid") { const auto floating = toml::detail::syntax::floating(toml::spec::v(1,0,0)); // accept partially test_scan_success(floating, "1e1E0", "1e1"); test_scan_success(floating, "1E1e0", "1E1"); } TEST_CASE("testing both_valid") { const auto floating = toml::detail::syntax::floating(toml::spec::v(1,0,0)); test_scan_success(floating, "6.02e23", "6.02e23"); test_scan_success(floating, "6.02e+23", "6.02e+23"); test_scan_success(floating, "1.112_650_06e-17", "1.112_650_06e-17"); test_scan_success(floating, "1.0e-07", "1.0e-07"); } TEST_CASE("testing both_invalid") { const auto floating = toml::detail::syntax::floating(toml::spec::v(1,0,0)); test_scan_failure(floating, "01e1.0"); // accept partially test_scan_success(floating, "1e1.0", "1e1"); test_scan_success(floating, "1.0e_01", "1.0"); test_scan_success(floating, "1.0e0__1", "1.0e0"); } TEST_CASE("testing special_floating_point") { const auto floating = toml::detail::syntax::floating(toml::spec::v(1,0,0)); test_scan_success(floating, "inf", "inf"); test_scan_success(floating, "+inf", "+inf"); test_scan_success(floating, "-inf", "-inf"); test_scan_success(floating, "nan", "nan"); test_scan_success(floating, "+nan", "+nan"); test_scan_success(floating, "-nan", "-nan"); } toml11-4.1.0/tests/test_syntax_integer.cpp000066400000000000000000000100341464712047700206130ustar00rootroot00000000000000#define DOCTEST_CONFIG_IMPLEMENT_WITH_MAIN #include "doctest.h" #include "utility.hpp" #include TEST_CASE("testing decimal_correct") { const auto integer = toml::detail::syntax::integer(toml::spec::v(1,0,0)); test_scan_success(integer, "1234", "1234" ); test_scan_success(integer, "+1234", "+1234" ); test_scan_success(integer, "-1234", "-1234" ); test_scan_success(integer, "0", "0" ); test_scan_success(integer, "1_2_3_4", "1_2_3_4" ); test_scan_success(integer, "+1_2_3_4", "+1_2_3_4" ); test_scan_success(integer, "-1_2_3_4", "-1_2_3_4" ); test_scan_success(integer, "123_456_789", "123_456_789"); } TEST_CASE("testing decimal_invalid") { const auto integer = toml::detail::syntax::integer(toml::spec::v(1,0,0)); test_scan_success(integer, "123+45", "123"); test_scan_success(integer, "123-45", "123"); test_scan_success(integer, "01234", "0"); test_scan_success(integer, "123__45", "123"); test_scan_failure(integer, "_1234"); } TEST_CASE("testing hex_correct") { const auto integer = toml::detail::syntax::integer(toml::spec::v(1,0,0)); test_scan_success(integer, "0xDEADBEEF", "0xDEADBEEF" ); test_scan_success(integer, "0xdeadbeef", "0xdeadbeef" ); test_scan_success(integer, "0xDEADbeef", "0xDEADbeef" ); test_scan_success(integer, "0xDEAD_BEEF", "0xDEAD_BEEF"); test_scan_success(integer, "0xdead_beef", "0xdead_beef"); test_scan_success(integer, "0xdead_BEEF", "0xdead_BEEF"); test_scan_success(integer, "0xFF", "0xFF" ); test_scan_success(integer, "0x00FF", "0x00FF" ); test_scan_success(integer, "0x0000FF", "0x0000FF"); } TEST_CASE("testing hex_invalid") { const auto integer = toml::detail::syntax::integer(toml::spec::v(1,0,0)); const auto hex_int = toml::detail::syntax::hex_int(toml::spec::v(1,0,0)); test_scan_success(integer, "0xAPPLE", "0xA"); test_scan_success(integer, "0xDEAD+BEEF", "0xDEAD"); test_scan_success(integer, "0xDEAD__BEEF", "0xDEAD"); test_scan_failure(hex_int, "0x_DEADBEEF"); test_scan_failure(hex_int, "0x+DEADBEEF"); test_scan_failure(hex_int, "-0xFF" ); test_scan_failure(hex_int, "-0x00FF" ); test_scan_success(integer, "0x_DEADBEEF", "0" ); test_scan_success(integer, "0x+DEADBEEF", "0" ); test_scan_success(integer, "-0xFF" , "-0" ); test_scan_success(integer, "-0x00FF" , "-0" ); } TEST_CASE("testing oct_correct") { const auto integer = toml::detail::syntax::integer(toml::spec::v(1,0,0)); test_scan_success(integer, "0o777", "0o777" ); test_scan_success(integer, "0o7_7_7", "0o7_7_7"); test_scan_success(integer, "0o007", "0o007" ); } TEST_CASE("testing oct_invalid") { const auto integer = toml::detail::syntax::integer(toml::spec::v(1,0,0)); const auto oct_int = toml::detail::syntax::hex_int(toml::spec::v(1,0,0)); test_scan_success(integer, "0o77+7", "0o77"); test_scan_success(integer, "0o1__0", "0o1"); test_scan_failure(oct_int, "0o800" ); test_scan_failure(oct_int, "-0o777"); test_scan_failure(oct_int, "0o+777"); test_scan_failure(oct_int, "0o_10" ); test_scan_success(integer, "0o800", "0"); test_scan_success(integer, "-0o777", "-0"); test_scan_success(integer, "0o+777", "0"); test_scan_success(integer, "0o_10", "0"); } TEST_CASE("testing bin_correct") { const auto integer = toml::detail::syntax::integer(toml::spec::v(1,0,0)); test_scan_success(integer, "0b10000", "0b10000" ); test_scan_success(integer, "0b010000", "0b010000" ); test_scan_success(integer, "0b01_00_00", "0b01_00_00"); test_scan_success(integer, "0b111111", "0b111111" ); } TEST_CASE("testing bin_invalid") { const auto bin_int = toml::detail::syntax::bin_int(toml::spec::v(1,0,0)); test_scan_success(bin_int, "0b11__11", "0b11"); test_scan_success(bin_int, "0b11+11" , "0b11"); test_scan_failure(bin_int, "-0b10000"); test_scan_failure(bin_int, "0b_1111" ); } toml11-4.1.0/tests/test_syntax_key.cpp000066400000000000000000000035141464712047700177530ustar00rootroot00000000000000#define DOCTEST_CONFIG_IMPLEMENT_WITH_MAIN #include "doctest.h" #include "utility.hpp" #include TEST_CASE("testing bare_key") { const auto scanner = toml::detail::syntax::key(toml::spec::v(1,0,0)); test_scan_success(scanner, "barekey", "barekey"); test_scan_success(scanner, "bare-key", "bare-key"); test_scan_success(scanner, "bare_key", "bare_key"); test_scan_success(scanner, "1234", "1234"); } TEST_CASE("testing quoted_key") { const auto scanner = toml::detail::syntax::key(toml::spec::v(1,0,0)); test_scan_success(scanner, "\"127.0.0.1\"", "\"127.0.0.1\""); test_scan_success(scanner, "\"character encoding\"", "\"character encoding\""); // UTF-8 codepoint of characters that looks like "key" written upside down test_scan_success(scanner, "\"\xCA\x8E\xC7\x9D\xCA\x9E\"", "\"\xCA\x8E\xC7\x9D\xCA\x9E\""); test_scan_success(scanner, "'key2'", "'key2'"); test_scan_success(scanner, "'quoted \"value\"'", "'quoted \"value\"'"); } TEST_CASE("testing dotted_key") { const auto scanner = toml::detail::syntax::key(toml::spec::v(1,0,0)); test_scan_success(scanner, "physical.color", "physical.color"); test_scan_success(scanner, "physical.shape", "physical.shape"); test_scan_success(scanner, "x.y", "x.y"); test_scan_success(scanner, "x . y", "x . y"); test_scan_success(scanner, "x.y.z", "x.y.z"); test_scan_success(scanner, "x. y .z", "x. y .z"); test_scan_success(scanner, "x .y. z", "x .y. z"); test_scan_success(scanner, "x . y . z", "x . y . z"); test_scan_success(scanner, "x.y.z.w", "x.y.z.w"); test_scan_success(scanner, "x. y .z. w", "x. y .z. w"); test_scan_success(scanner, "x . y . z . w", "x . y . z . w"); test_scan_success(scanner, "site.\"google.com\"", "site.\"google.com\""); } toml11-4.1.0/tests/test_syntax_string.cpp000066400000000000000000000121001464712047700204600ustar00rootroot00000000000000#define DOCTEST_CONFIG_IMPLEMENT_WITH_MAIN #include "doctest.h" #include "utility.hpp" #include TEST_CASE("testing string") { const auto string = toml::detail::syntax::string(toml::spec::v(1,0,0)); const auto ml_basic_string = toml::detail::syntax::ml_basic_string(toml::spec::v(1,0,0)); const auto ml_literal_string = toml::detail::syntax::ml_literal_string(toml::spec::v(1,0,0)); test_scan_success(string, "\"The quick brown fox jumps over the lazy dog\"", "\"The quick brown fox jumps over the lazy dog\""); test_scan_success(string, "\'The quick brown fox jumps over the lazy dog\'", "\'The quick brown fox jumps over the lazy dog\'"); test_scan_success(ml_basic_string, "\"\"\"The quick brown fox jumps over the lazy dog\"\"\"", "\"\"\"The quick brown fox jumps over the lazy dog\"\"\""); test_scan_success(ml_basic_string, "\"\"\"The quick brown fox \\\njumps over the lazy dog\"\"\"", "\"\"\"The quick brown fox \\\njumps over the lazy dog\"\"\""); test_scan_success(ml_literal_string, "'''The quick brown fox jumps over the lazy dog'''", "'''The quick brown fox jumps over the lazy dog'''"); test_scan_success(ml_literal_string, "'''The quick brown fox \njumps over the lazy dog'''", "'''The quick brown fox \njumps over the lazy dog'''"); } TEST_CASE("testing basic_string") { const auto string = toml::detail::syntax::string(toml::spec::v(1,0,0)); test_scan_success(string, "\"GitHub Cofounder & CEO\\nLikes tater tots and beer.\"", "\"GitHub Cofounder & CEO\\nLikes tater tots and beer.\""); test_scan_success(string, "\"192.168.1.1\"", "\"192.168.1.1\""); test_scan_success(string, "\"\xE4\xB8\xAD\xE5\x9B\xBD\"", // UTF-8 string (means "China" in "\"\xE4\xB8\xAD\xE5\x9B\xBD\""); // Chinese characters) test_scan_success(string, "\"You'll hate me after this - #\"", "\"You'll hate me after this - #\""); test_scan_success(string, "\" And when \\\"'s are in the string, along with # \\\"\"", "\" And when \\\"'s are in the string, along with # \\\"\""); } TEST_CASE("testing ml_basic_string") { const auto string = toml::detail::syntax::string(toml::spec::v(1,0,0)); test_scan_success(string, "\"\"\"\nThe quick brown \\\n\n fox jumps over \\\n the lazy dog.\"\"\"", "\"\"\"\nThe quick brown \\\n\n fox jumps over \\\n the lazy dog.\"\"\""); test_scan_success(string, "\"\"\"\\\n The quick brown \\\n\n fox jumps over \\\n the lazy dog.\\\n \"\"\"", "\"\"\"\\\n The quick brown \\\n\n fox jumps over \\\n the lazy dog.\\\n \"\"\""); test_scan_success(string, "\"\"\"Here are two quotation marks: \"\". Simple enough.\"\"\"", "\"\"\"Here are two quotation marks: \"\". Simple enough.\"\"\""); test_scan_success(string, "\"\"\"Here are three quotation marks: \"\"\\\".\"\"\"", "\"\"\"Here are three quotation marks: \"\"\\\".\"\"\""); test_scan_success(string, "\"\"\"Here are fifteen quotation marks: \"\"\\\"\"\"\\\"\"\"\\\"\"\"\\\"\"\"\\\".\"\"\"", "\"\"\"Here are fifteen quotation marks: \"\"\\\"\"\"\\\"\"\"\\\"\"\"\\\"\"\"\\\".\"\"\""); test_scan_success(string, "\"\"\"\"This,\" she said, \"is just a pointless statement.\"\"\"\"", "\"\"\"\"This,\" she said, \"is just a pointless statement.\"\"\"\""); } TEST_CASE("testing literal_string") { const auto string = toml::detail::syntax::string(toml::spec::v(1,0,0)); test_scan_success(string, "'C:\\Users\\nodejs\\templates'", "'C:\\Users\\nodejs\\templates'"); test_scan_success(string, "'\\\\ServerX\\admin$\\system32\\'", "'\\\\ServerX\\admin$\\system32\\'"); test_scan_success(string, "'Tom \"Dubs\" Preston-Werner'", "'Tom \"Dubs\" Preston-Werner'"); test_scan_success(string, "'<\\i\\c*\\s*>'", "'<\\i\\c*\\s*>'"); } TEST_CASE("testing ml_literal_string") { const auto string = toml::detail::syntax::string(toml::spec::v(1,0,0)); test_scan_success(string, "'''I [dw]on't need \\d{2} apples'''", "'''I [dw]on't need \\d{2} apples'''"); test_scan_success(string, "'''\nThe first newline is\ntrimmed in raw strings.\n All other whitespace\n is preserved.\n'''", "'''\nThe first newline is\ntrimmed in raw strings.\n All other whitespace\n is preserved.\n'''"); test_scan_success(string, "''''That's still pointless', she said.'''", "''''That's still pointless', she said.'''"); test_scan_success(string, "'''Here are fifteen quotation marks: \"\"\"\"\"\"\"\"\"\"\"\"\"\"\".'''", "'''Here are fifteen quotation marks: \"\"\"\"\"\"\"\"\"\"\"\"\"\"\".'''"); test_scan_success(string, "''''This,' she said, 'is just a pointless statement.''''", "''''This,' she said, 'is just a pointless statement.''''"); } toml11-4.1.0/tests/test_traits.cpp000066400000000000000000000113341464712047700170620ustar00rootroot00000000000000#define DOCTEST_CONFIG_IMPLEMENT_WITH_MAIN #include "doctest.h" #include #include #include #include #include #include #include #include #include #include struct dummy_type{}; template struct dummy_container { typedef T value_type; typedef value_type* pointer; typedef value_type& reference; typedef value_type const* const_pointer; typedef value_type const& const_reference; typedef pointer iterator; typedef const_pointer const_iterator; }; TEST_CASE("testing has_xxx traits") { CHECK_UNARY(toml::detail::has_iterator>::value); CHECK_UNARY(toml::detail::has_iterator>::value); CHECK_UNARY(toml::detail::has_iterator>::value); CHECK_UNARY(toml::detail::has_iterator>::value); CHECK_UNARY(toml::detail::has_iterator>::value); CHECK_UNARY(toml::detail::has_iterator>::value); CHECK_UNARY(toml::detail::has_iterator>::value); CHECK_UNARY(toml::detail::has_iterator>::value); CHECK_UNARY(toml::detail::has_iterator>::value); CHECK_UNARY(toml::detail::has_iterator>::value); CHECK_UNARY(toml::detail::has_value_type>::value); CHECK_UNARY(toml::detail::has_value_type>::value); CHECK_UNARY(toml::detail::has_value_type>::value); CHECK_UNARY(toml::detail::has_value_type>::value); CHECK_UNARY(toml::detail::has_value_type>::value); CHECK_UNARY(toml::detail::has_value_type>::value); CHECK_UNARY(toml::detail::has_value_type>::value); CHECK_UNARY(toml::detail::has_value_type>::value); CHECK_UNARY(toml::detail::has_value_type>::value); CHECK_UNARY(toml::detail::has_value_type>::value); CHECK_UNARY(toml::detail::has_key_type>::value); CHECK_UNARY(toml::detail::has_key_type>::value); CHECK_UNARY(toml::detail::has_mapped_type>::value); CHECK_UNARY(toml::detail::has_mapped_type>::value); } TEST_CASE("testing is_xxx traits") { CHECK_UNARY(toml::detail::is_container>::value); CHECK_UNARY(toml::detail::is_container>::value); CHECK_UNARY(toml::detail::is_container>::value); CHECK_UNARY(toml::detail::is_container>::value); CHECK_UNARY(toml::detail::is_container>::value); CHECK_UNARY(toml::detail::is_container>::value); CHECK_UNARY(toml::detail::is_container>::value); CHECK_UNARY(toml::detail::is_container>::value); CHECK_UNARY_FALSE(toml::detail::is_container>::value); CHECK_UNARY_FALSE(toml::detail::is_container>::value); CHECK_UNARY(toml::detail::is_map>::value); CHECK_UNARY(toml::detail::is_map>::value); } TEST_CASE("testing make_index_sequence") { static_assert(std::is_same, toml::cxx::index_sequence<>>::value, ""); static_assert(std::is_same, toml::cxx::index_sequence<0>>::value, ""); static_assert(std::is_same, toml::cxx::index_sequence<0,1>>::value, ""); static_assert(std::is_same, toml::cxx::index_sequence<0,1,2>>::value, ""); static_assert(std::is_same, toml::cxx::index_sequence<0,1,2,3>>::value, ""); static_assert(std::is_same, toml::cxx::index_sequence<0,1,2,3,4>>::value, ""); static_assert(std::is_same, toml::cxx::index_sequence<0,1,2,3,4,5>>::value, ""); static_assert(std::is_same, toml::cxx::index_sequence<0,1,2,3,4,5,6>>::value, ""); static_assert(std::is_same, toml::cxx::index_sequence<0,1,2,3,4,5,6,7>>::value, ""); } toml11-4.1.0/tests/test_types.cpp000066400000000000000000000241551464712047700167250ustar00rootroot00000000000000#define DOCTEST_CONFIG_IMPLEMENT_WITH_MAIN #include "doctest.h" #include TEST_CASE("testing value_t to string") { CHECK_EQ(toml::to_string(toml::value_t::boolean ), "boolean"); CHECK_EQ(toml::to_string(toml::value_t::integer ), "integer"); CHECK_EQ(toml::to_string(toml::value_t::floating ), "floating"); CHECK_EQ(toml::to_string(toml::value_t::string ), "string"); CHECK_EQ(toml::to_string(toml::value_t::offset_datetime), "offset_datetime"); CHECK_EQ(toml::to_string(toml::value_t::local_datetime ), "local_datetime"); CHECK_EQ(toml::to_string(toml::value_t::local_date ), "local_date"); CHECK_EQ(toml::to_string(toml::value_t::local_time ), "local_time"); CHECK_EQ(toml::to_string(toml::value_t::array ), "array"); CHECK_EQ(toml::to_string(toml::value_t::table ), "table"); {std::ostringstream oss; oss << toml::value_t::boolean ; CHECK_EQ(oss.str(), "boolean" );} {std::ostringstream oss; oss << toml::value_t::integer ; CHECK_EQ(oss.str(), "integer" );} {std::ostringstream oss; oss << toml::value_t::floating ; CHECK_EQ(oss.str(), "floating" );} {std::ostringstream oss; oss << toml::value_t::string ; CHECK_EQ(oss.str(), "string" );} {std::ostringstream oss; oss << toml::value_t::offset_datetime; CHECK_EQ(oss.str(), "offset_datetime");} {std::ostringstream oss; oss << toml::value_t::local_datetime ; CHECK_EQ(oss.str(), "local_datetime" );} {std::ostringstream oss; oss << toml::value_t::local_date ; CHECK_EQ(oss.str(), "local_date" );} {std::ostringstream oss; oss << toml::value_t::local_time ; CHECK_EQ(oss.str(), "local_time" );} {std::ostringstream oss; oss << toml::value_t::array ; CHECK_EQ(oss.str(), "array" );} {std::ostringstream oss; oss << toml::value_t::table ; CHECK_EQ(oss.str(), "table" );} } #include TEST_CASE("testing type_to_enum") { using value_type = toml::value; using boolean_type = typename value_type::boolean_type ; using integer_type = typename value_type::integer_type ; using floating_type = typename value_type::floating_type ; using string_type = typename value_type::string_type ; using local_time_type = typename value_type::local_time_type ; using local_date_type = typename value_type::local_date_type ; using local_datetime_type = typename value_type::local_datetime_type ; using offset_datetime_type = typename value_type::offset_datetime_type; using array_type = typename value_type::array_type ; using table_type = typename value_type::table_type ; CHECK_EQ((toml::detail::type_to_enum::value), toml::value_t::boolean ); CHECK_EQ((toml::detail::type_to_enum::value), toml::value_t::integer ); CHECK_EQ((toml::detail::type_to_enum::value), toml::value_t::floating ); CHECK_EQ((toml::detail::type_to_enum::value), toml::value_t::string ); CHECK_EQ((toml::detail::type_to_enum::value), toml::value_t::local_time ); CHECK_EQ((toml::detail::type_to_enum::value), toml::value_t::local_date ); CHECK_EQ((toml::detail::type_to_enum::value), toml::value_t::local_datetime ); CHECK_EQ((toml::detail::type_to_enum::value), toml::value_t::offset_datetime); CHECK_EQ((toml::detail::type_to_enum::value), toml::value_t::array ); CHECK_EQ((toml::detail::type_to_enum::value), toml::value_t::table ); } TEST_CASE("testing enum_to_type") { using value_type = toml::value; using boolean_type = typename value_type::boolean_type ; using integer_type = typename value_type::integer_type ; using floating_type = typename value_type::floating_type ; using string_type = typename value_type::string_type ; using local_time_type = typename value_type::local_time_type ; using local_date_type = typename value_type::local_date_type ; using local_datetime_type = typename value_type::local_datetime_type ; using offset_datetime_type = typename value_type::offset_datetime_type; using array_type = typename value_type::array_type ; using table_type = typename value_type::table_type ; CHECK_UNARY(std::is_same::type, boolean_type >::value); CHECK_UNARY(std::is_same::type, integer_type >::value); CHECK_UNARY(std::is_same::type, floating_type >::value); CHECK_UNARY(std::is_same::type, string_type >::value); CHECK_UNARY(std::is_same::type, local_time_type >::value); CHECK_UNARY(std::is_same::type, local_date_type >::value); CHECK_UNARY(std::is_same::type, local_datetime_type >::value); CHECK_UNARY(std::is_same::type, offset_datetime_type>::value); CHECK_UNARY(std::is_same::type, array_type >::value); CHECK_UNARY(std::is_same::type, table_type >::value); } TEST_CASE("testing is_exact_toml_type") { using value_type = toml::value; using boolean_type = typename value_type::boolean_type ; using integer_type = typename value_type::integer_type ; using floating_type = typename value_type::floating_type ; using string_type = typename value_type::string_type ; using local_time_type = typename value_type::local_time_type ; using local_date_type = typename value_type::local_date_type ; using local_datetime_type = typename value_type::local_datetime_type ; using offset_datetime_type = typename value_type::offset_datetime_type; using array_type = typename value_type::array_type ; using table_type = typename value_type::table_type ; CHECK_UNARY(toml::detail::is_exact_toml_type::value); CHECK_UNARY(toml::detail::is_exact_toml_type::value); CHECK_UNARY(toml::detail::is_exact_toml_type::value); CHECK_UNARY(toml::detail::is_exact_toml_type::value); CHECK_UNARY(toml::detail::is_exact_toml_type::value); CHECK_UNARY(toml::detail::is_exact_toml_type::value); CHECK_UNARY(toml::detail::is_exact_toml_type::value); CHECK_UNARY(toml::detail::is_exact_toml_type::value); CHECK_UNARY(toml::detail::is_exact_toml_type::value); CHECK_UNARY(toml::detail::is_exact_toml_type::value); CHECK_UNARY(toml::detail::is_exact_toml_type::value); CHECK_UNARY(toml::detail::is_exact_toml_type::value); CHECK_UNARY(toml::detail::is_exact_toml_type::value); CHECK_UNARY(toml::detail::is_exact_toml_type::value); CHECK_UNARY(toml::detail::is_exact_toml_type::value); CHECK_UNARY(toml::detail::is_exact_toml_type::value); CHECK_UNARY(toml::detail::is_exact_toml_type::value); CHECK_UNARY(toml::detail::is_exact_toml_type::value); CHECK_UNARY(toml::detail::is_exact_toml_type::value); CHECK_UNARY(toml::detail::is_exact_toml_type::value); CHECK_UNARY(toml::detail::is_exact_toml_type::value); CHECK_UNARY(toml::detail::is_exact_toml_type::value); CHECK_UNARY(toml::detail::is_exact_toml_type::value); CHECK_UNARY(toml::detail::is_exact_toml_type::value); CHECK_UNARY(toml::detail::is_exact_toml_type::value); CHECK_UNARY(toml::detail::is_exact_toml_type::value); CHECK_UNARY(toml::detail::is_exact_toml_type::value); CHECK_UNARY(toml::detail::is_exact_toml_type::value); CHECK_UNARY(toml::detail::is_exact_toml_type::value); CHECK_UNARY(toml::detail::is_exact_toml_type::value); CHECK_UNARY_FALSE(toml::detail::is_exact_toml_type::value); CHECK_UNARY_FALSE(toml::detail::is_exact_toml_type::value); CHECK_UNARY_FALSE(toml::detail::is_exact_toml_type, value_type>::value); } toml11-4.1.0/tests/test_user_defined_conversion.cpp000066400000000000000000000410371464712047700224600ustar00rootroot00000000000000#define DOCTEST_CONFIG_IMPLEMENT_WITH_MAIN #include "doctest.h" #include #include "utility.hpp" namespace extlib { struct foo { int a; std::string b; }; struct bar { int a; std::string b; void from_toml(const toml::value& v) { this->a = toml::find(v, "a"); this->b = toml::find(v, "b"); return ; } toml::table into_toml() const { return toml::table{{"a", this->a}, {"b", this->b}}; } }; struct baz { int a; std::string b; }; struct qux { int a; std::string b; }; struct foobar { // via constructor explicit foobar(const toml::value& v) : a(toml::find(v, "a")), b(toml::find(v, "b")) {} int a; std::string b; }; struct corge { int a; std::string b; void from_toml(const toml::value& v) { this->a = toml::find(v, "a"); this->b = toml::find(v, "b"); return ; } template toml::basic_value into_toml() const { return toml::basic_value(typename toml::basic_value::table_type{{"a", this->a}, {"b", this->b}}); } }; } // extlib namespace toml { template<> struct from { static extlib::foo from_toml(const toml::value& v) { return extlib::foo{toml::find(v, "a"), toml::find(v, "b")}; } }; template<> struct into { template static toml::basic_value into_toml(const extlib::foo& f) { return toml::basic_value(typename toml::basic_value::table_type{{"a", f.a}, {"b", f.b}}); } }; template<> struct from { static extlib::baz from_toml(const toml::value& v) { return extlib::baz{toml::find(v, "a"), toml::find(v, "b")}; } }; template<> struct into { template static toml::basic_value into_toml(const extlib::qux& f) { return toml::basic_value(typename toml::basic_value::table_type{{"a", f.a}, {"b", f.b}}); } }; } // toml // --------------------------------------------------------------------------- namespace extlib2 { struct foo { int a; std::string b; }; struct bar { int a; std::string b; template void from_toml(const toml::basic_value& v) { this->a = toml::find(v, "a"); this->b = toml::find(v, "b"); return ; } toml::ordered_table into_toml() const { return toml::ordered_table{{"a", this->a}, {"b", this->b}}; } }; struct baz { int a; std::string b; }; struct qux { int a; std::string b; }; struct foobar { template explicit foobar(const toml::basic_value& v) : a(toml::find(v, "a")), b(toml::find(v, "b")) {} int a; std::string b; }; } // extlib2 namespace toml { template<> struct from { template static extlib2::foo from_toml(const toml::basic_value& v) { return extlib2::foo{toml::find(v, "a"), toml::find(v, "b")}; } }; template<> struct into { template static toml::basic_value into_toml(const extlib2::foo& f) { return toml::basic_value(typename toml::basic_value::table_type{{"a", f.a}, {"b", f.b}}); } }; template<> struct from { template static extlib2::baz from_toml(const toml::basic_value& v) { return extlib2::baz{toml::find(v, "a"), toml::find(v, "b")}; } }; template<> struct into { template static toml::basic_value into_toml(const extlib2::qux& f) { return toml::basic_value{ {"a", f.a}, {"b", f.b} }; } }; } // toml // --------------------------------------------------------------------------- TEST_CASE("test_conversion_by_member_methods") { { const toml::value v(toml::table{{"a", 42}, {"b", "baz"}}); const auto foo = toml::get(v); CHECK_EQ(foo.a, 42); CHECK_EQ(foo.b, "baz"); const toml::value v2(foo); CHECK_EQ(v, v2); } { const toml::value v(toml::table{{"a", 42}, {"b", "baz"}}); const auto foo = toml::get(v); CHECK(foo.a == 42); CHECK(foo.b == "baz"); const toml::value v2(foo); CHECK(v == v2); } { const toml::value v(toml::table{{"a", 42}, {"b", "baz"}}); const auto corge = toml::get(v); CHECK_EQ(corge.a, 42); CHECK_EQ(corge.b, "baz"); const toml::value v2(corge); CHECK_EQ(v, v2); } { const toml::ordered_value v(toml::ordered_table{{"a", 42}, {"b", "baz"}}); const auto foo = toml::get(v); CHECK(foo.a == 42); CHECK(foo.b == "baz"); const toml::ordered_value v2(foo); CHECK(v == v2); } } TEST_CASE("test_conversion_by_specialization") { { const toml::value v(toml::table{{"a", 42}, {"b", "baz"}}); const auto bar = toml::get(v); CHECK(bar.a == 42); CHECK(bar.b == "baz"); const toml::value v2(bar); CHECK(v == v2); } { const toml::value v(toml::table{{"a", 42}, {"b", "baz"}}); const auto bar = toml::get(v); CHECK(bar.a == 42); CHECK(bar.b == "baz"); const toml::value v2(bar); CHECK(v == v2); } { const toml::ordered_value v(toml::ordered_table{{"a", 42}, {"b", "baz"}}); const auto bar = toml::get(v); CHECK(bar.a == 42); CHECK(bar.b == "baz"); const toml::ordered_value v2(bar); CHECK(v == v2); } } TEST_CASE("test_conversion_one_way") { { const toml::value v(toml::table{{"a", 42}, {"b", "baz"}}); const auto baz = toml::get(v); CHECK(baz.a == 42); CHECK(baz.b == "baz"); } { const extlib::qux q{42, "qux"}; const toml::value v(q); CHECK(toml::find(v, "a") == 42); CHECK(toml::find(v, "b") == "qux"); } { const toml::ordered_value v(toml::ordered_table{ {"a", 42}, {"b", "baz"} }); const auto baz = toml::get(v); CHECK(baz.a == 42); CHECK(baz.b == "baz"); } { const extlib::qux q{42, "qux"}; const toml::ordered_value v(q); CHECK(toml::find(v, "a") == 42); CHECK(toml::find(v, "b") == "qux"); } } TEST_CASE("test_conversion_via_constructor") { { const toml::value v(toml::table{{"a", 42}, {"b", "foobar"}}); const auto foobar = toml::get(v); CHECK(foobar.a == 42); CHECK(foobar.b == "foobar"); } { const toml::ordered_value v(toml::ordered_table{ {"a", 42}, {"b", "foobar"} }); const auto foobar = toml::get(v); CHECK(foobar.a == 42); CHECK(foobar.b == "foobar"); } } TEST_CASE("test_recursive_conversion") { { const toml::value v(toml::array{ toml::table{{"a", 42}, {"b", "baz"}}, toml::table{{"a", 43}, {"b", "qux"}}, toml::table{{"a", 44}, {"b", "quux"}}, toml::table{{"a", 45}, {"b", "foobar"}}, }); const auto foos = toml::get>(v); CHECK(foos.size() == 4ul); CHECK(foos.at(0).a == 42); CHECK(foos.at(1).a == 43); CHECK(foos.at(2).a == 44); CHECK(foos.at(3).a == 45); CHECK(foos.at(0).b == "baz"); CHECK(foos.at(1).b == "qux"); CHECK(foos.at(2).b == "quux"); CHECK(foos.at(3).b == "foobar"); const auto bars = toml::get>(v); CHECK(bars.size() == 4ul); CHECK(bars.at(0).a == 42); CHECK(bars.at(1).a == 43); CHECK(bars.at(2).a == 44); CHECK(bars.at(3).a == 45); CHECK(bars.at(0).b == "baz"); CHECK(bars.at(1).b == "qux"); CHECK(bars.at(2).b == "quux"); CHECK(bars.at(3).b == "foobar"); } { const toml::value v(toml::array{ toml::table{{"a", 42}, {"b", "baz"}}, toml::table{{"a", 43}, {"b", "qux"}}, toml::table{{"a", 44}, {"b", "quux"}}, toml::table{{"a", 45}, {"b", "foobar"}}, }); const auto foos = toml::get>(v); CHECK(foos.size() == 4ul); CHECK(foos.at(0).a == 42); CHECK(foos.at(1).a == 43); CHECK(foos.at(2).a == 44); CHECK(foos.at(3).a == 45); CHECK(foos.at(0).b == "baz"); CHECK(foos.at(1).b == "qux"); CHECK(foos.at(2).b == "quux"); CHECK(foos.at(3).b == "foobar"); const auto bars = toml::get>(v); CHECK(bars.size() == 4ul); CHECK(bars.at(0).a == 42); CHECK(bars.at(1).a == 43); CHECK(bars.at(2).a == 44); CHECK(bars.at(3).a == 45); CHECK(bars.at(0).b == "baz"); CHECK(bars.at(1).b == "qux"); CHECK(bars.at(2).b == "quux"); CHECK(bars.at(3).b == "foobar"); } { const toml::ordered_value v(toml::ordered_array{ toml::ordered_table{{"a", 42}, {"b", "baz"}}, toml::ordered_table{{"a", 43}, {"b", "qux"}}, toml::ordered_table{{"a", 44}, {"b", "quux"}}, toml::ordered_table{{"a", 45}, {"b", "foobar"}} }); const auto foos = toml::get>(v); CHECK(foos.size() == 4ul); CHECK(foos.at(0).a == 42); CHECK(foos.at(1).a == 43); CHECK(foos.at(2).a == 44); CHECK(foos.at(3).a == 45); CHECK(foos.at(0).b == "baz"); CHECK(foos.at(1).b == "qux"); CHECK(foos.at(2).b == "quux"); CHECK(foos.at(3).b == "foobar"); const auto bars = toml::get>(v); CHECK(bars.size() == 4ul); CHECK(bars.at(0).a == 42); CHECK(bars.at(1).a == 43); CHECK(bars.at(2).a == 44); CHECK(bars.at(3).a == 45); CHECK(bars.at(0).b == "baz"); CHECK(bars.at(1).b == "qux"); CHECK(bars.at(2).b == "quux"); CHECK(bars.at(3).b == "foobar"); } // via constructor { const toml::value v(toml::array{ toml::table{{"a", 42}, {"b", "baz"}}, toml::table{{"a", 43}, {"b", "qux"}}, toml::table{{"a", 44}, {"b", "quux"}}, toml::table{{"a", 45}, {"b", "foobar"}} }); { const auto foobars = toml::get>(v); CHECK(foobars.size() == 4ul); CHECK(foobars.at(0).a == 42); CHECK(foobars.at(1).a == 43); CHECK(foobars.at(2).a == 44); CHECK(foobars.at(3).a == 45); CHECK(foobars.at(0).b == "baz"); CHECK(foobars.at(1).b == "qux"); CHECK(foobars.at(2).b == "quux"); CHECK(foobars.at(3).b == "foobar"); } { const auto foobars = toml::get>(v); CHECK(foobars.size() == 4ul); CHECK(foobars.at(0).a == 42); CHECK(foobars.at(1).a == 43); CHECK(foobars.at(2).a == 44); CHECK(foobars.at(3).a == 45); CHECK(foobars.at(0).b == "baz"); CHECK(foobars.at(1).b == "qux"); CHECK(foobars.at(2).b == "quux"); CHECK(foobars.at(3).b == "foobar"); } } { const toml::ordered_value v(toml::ordered_array{ toml::ordered_table{{"a", 42}, {"b", "baz"}}, toml::ordered_table{{"a", 43}, {"b", "qux"}}, toml::ordered_table{{"a", 44}, {"b", "quux"}}, toml::ordered_table{{"a", 45}, {"b", "foobar"}} }); const auto foobars = toml::get>(v); CHECK(foobars.size() == 4ul); CHECK(foobars.at(0).a == 42); CHECK(foobars.at(1).a == 43); CHECK(foobars.at(2).a == 44); CHECK(foobars.at(3).a == 45); CHECK(foobars.at(0).b == "baz"); CHECK(foobars.at(1).b == "qux"); CHECK(foobars.at(2).b == "quux"); CHECK(foobars.at(3).b == "foobar"); } // via constructor { const toml::value v(toml::table{ {"0", toml::table{{"a", 42}, {"b", "baz"}}}, {"1", toml::table{{"a", 43}, {"b", "qux"}}}, {"2", toml::table{{"a", 44}, {"b", "quux"}}}, {"3", toml::table{{"a", 45}, {"b", "foobar"}}} }); { const auto foobars = toml::get>(v); CHECK(foobars.size() == 4ul); CHECK(foobars.at("0").a == 42); CHECK(foobars.at("1").a == 43); CHECK(foobars.at("2").a == 44); CHECK(foobars.at("3").a == 45); CHECK(foobars.at("0").b == "baz"); CHECK(foobars.at("1").b == "qux"); CHECK(foobars.at("2").b == "quux"); CHECK(foobars.at("3").b == "foobar"); } { const auto foobars = toml::get>(v); CHECK(foobars.size() == 4ul); CHECK(foobars.at("0").a == 42); CHECK(foobars.at("1").a == 43); CHECK(foobars.at("2").a == 44); CHECK(foobars.at("3").a == 45); CHECK(foobars.at("0").b == "baz"); CHECK(foobars.at("1").b == "qux"); CHECK(foobars.at("2").b == "quux"); CHECK(foobars.at("3").b == "foobar"); } } { const toml::ordered_value v(toml::ordered_table{ {"0", toml::ordered_table{{"a", 42}, {"b", "baz"}}}, {"1", toml::ordered_table{{"a", 43}, {"b", "qux"}}}, {"2", toml::ordered_table{{"a", 44}, {"b", "quux"}}}, {"3", toml::ordered_table{{"a", 45}, {"b", "foobar"}}} }); const auto foobars = toml::get>(v); CHECK(foobars.size() == 4ul); CHECK(foobars.at("0").a == 42); CHECK(foobars.at("1").a == 43); CHECK(foobars.at("2").a == 44); CHECK(foobars.at("3").a == 45); CHECK(foobars.at("0").b == "baz"); CHECK(foobars.at("1").b == "qux"); CHECK(foobars.at("2").b == "quux"); CHECK(foobars.at("3").b == "foobar"); } } // =========================================================================== #ifndef TOML11_WITHOUT_DEFINE_NON_INTRUSIVE namespace extlib3 { struct foo { int a; std::string b; }; struct bar { int a; std::string b; foo f; }; } // extlib3 TOML11_DEFINE_CONVERSION_NON_INTRUSIVE(extlib3::foo, a, b) TOML11_DEFINE_CONVERSION_NON_INTRUSIVE(extlib3::bar, a, b, f) TEST_CASE("test_conversion_via_macro") { { const toml::value v(toml::table{{"a", 42}, {"b", "baz"}}); const auto foo = toml::get(v); CHECK(foo.a == 42); CHECK(foo.b == "baz"); const toml::value v2(foo); CHECK(v2 == v); } { const toml::ordered_value v(toml::ordered_table{ {"a", 42}, {"b", "baz"} }); const auto foo = toml::get(v); CHECK(foo.a == 42); CHECK(foo.b == "baz"); const toml::ordered_value v2(foo); CHECK(v2 == v); } // ----------------------------------------------------------------------- { const toml::value v(toml::table{ {"a", 42}, {"b", "bar.b"}, {"f", toml::table{{"a", 42}, {"b", "foo.b"}}} }); const auto bar = toml::get(v); CHECK(bar.a == 42); CHECK(bar.b == "bar.b"); CHECK(bar.f.a == 42); CHECK(bar.f.b == "foo.b"); const toml::value v2(bar); CHECK(v2 == v); } { const toml::ordered_value v(toml::ordered_table{ {"a", 42}, {"b", "bar.b"}, {"f", toml::ordered_table{{"a", 42}, {"b", "foo.b"}}} }); const auto bar = toml::get(v); CHECK(bar.a == 42); CHECK(bar.b == "bar.b"); CHECK(bar.f.a == 42); CHECK(bar.f.b == "foo.b"); const toml::ordered_value v2(bar); CHECK(v2 == v); } } #endif // TOML11_WITHOUT_DEFINE_NON_INTRUSIVE toml11-4.1.0/tests/test_utility.cpp000066400000000000000000000115711464712047700172620ustar00rootroot00000000000000#define DOCTEST_CONFIG_IMPLEMENT_WITH_MAIN #include "doctest.h" #include TEST_CASE("testing make_unique") { const auto v = toml::cxx::make_unique>(std::size_t(2), "foobar"); CHECK_EQ(v->size(), 2); CHECK_EQ(v->at(0), "foobar"); CHECK_EQ(v->at(1), "foobar"); } TEST_CASE("testing try_reserve") { { // since BOOST_TEST is a macro, it cannot handle commas correctly. // When toml::detail::has_reserve_method>::value // is passed to a macro, C preprocessor considers // toml::detail::has_reserve_method>::value as the second argument. We need an alias to avoid // this problem. using reservable_type = std::vector ; using nonreservable_type = std::array; CHECK_UNARY( toml::detail::has_reserve_method::value); CHECK_UNARY(!toml::detail::has_reserve_method::value); } { std::vector v; toml::detail::try_reserve(v, 100); CHECK_EQ(v.capacity(), 100u); } { std::array v; toml::detail::try_reserve(v, 100); CHECK_EQ(v.size(), 1); } } TEST_CASE("testing from_string") { { const std::string str("123"); REQUIRE_UNARY(toml::detail::from_string(str).is_ok()); CHECK_EQ(toml::detail::from_string(str).unwrap(), 123); } { const std::string str("01"); REQUIRE_UNARY(toml::detail::from_string(str).is_ok()); CHECK_EQ(toml::detail::from_string(str).unwrap(), 1); } } TEST_CASE("testing make_string") { const auto s1 = toml::detail::make_string(3, 'a'); CHECK_EQ(s1, "aaa"); const auto s2 = toml::detail::make_string(0, 'a'); CHECK_EQ(s2, ""); const std::string s("bbb"); const auto s3 = toml::detail::make_string(s.begin(), s.end()); CHECK_EQ(s3, "bbb"); const auto s4 = toml::detail::make_string(s.begin(), s.begin()); CHECK_EQ(s4, ""); } TEST_CASE("testing make_reverse_iterator") { const std::vector v{1, 2, 3, 4, 5}; const auto iter = toml::cxx::make_reverse_iterator(v.begin()); CHECK_EQ(iter, v.rend()); } #if defined(TOML11_HAS_STD_SOURCE_LOCATION) || defined(TOML11_HAS_EXPERIMENTAL_SOURCE_LOCATION) || defined(TOML11_HAS_BUILTIN_FILE_LINE) TEST_CASE("cxx::source_location") { const std::string file = __FILE__; const auto line = __LINE__; const auto loc = toml::cxx::source_location::current(); CHECK_EQ(file, loc.file_name()); CHECK_EQ(line, loc.line()); } #endif TEST_CASE("cxx::optional") { { toml::cxx::optional v(42); CHECK_UNARY(static_cast(v)); CHECK_UNARY(v.has_value()); CHECK_EQ(v.value(), 42); CHECK_EQ(v.value_or(6 * 9), 42); v.value() = 6 * 9; CHECK_EQ(v.value(), 54); } { toml::cxx::optional v; CHECK_UNARY_FALSE(static_cast(v)); CHECK_UNARY_FALSE(v.has_value()); CHECK_THROWS((void)v.value()); CHECK_EQ(v.value_or(6 * 9), 54); v = 42; CHECK_UNARY(static_cast(v)); CHECK_UNARY(v.has_value()); CHECK_EQ(v.value(), 42); } { toml::cxx::optional v(toml::cxx::make_nullopt()); CHECK_UNARY_FALSE(static_cast(v)); CHECK_UNARY_FALSE(v.has_value()); CHECK_THROWS((void)v.value()); CHECK_EQ(v.value_or(6 * 9), 54); v = 42; CHECK_UNARY(static_cast(v)); CHECK_UNARY(v.has_value()); CHECK_EQ(v.value(), 42); } { toml::cxx::optional> v(std::vector{42}); CHECK_UNARY(static_cast(v)); CHECK_UNARY(v.has_value()); CHECK_EQ(v.value(), std::vector{42}); CHECK_EQ(v.value_or(std::vector{6 * 9}), std::vector{42}); v.value() = std::vector{6 * 9}; CHECK_EQ(v.value(), std::vector{54}); } { toml::cxx::optional> v; CHECK_UNARY_FALSE(static_cast(v)); CHECK_UNARY_FALSE(v.has_value()); CHECK_THROWS((void)v.value()); CHECK_EQ(v.value_or(std::vector{6 * 9}), std::vector{54}); v = std::vector{42}; CHECK_UNARY(static_cast(v)); CHECK_UNARY(v.has_value()); CHECK_EQ(v.value(), std::vector{42}); } { toml::cxx::optional> v(toml::cxx::make_nullopt()); CHECK_UNARY_FALSE(static_cast(v)); CHECK_UNARY_FALSE(v.has_value()); CHECK_THROWS((void)v.value()); CHECK_EQ(v.value_or(std::vector{6 * 9}), std::vector{54}); v = std::vector{42}; CHECK_UNARY(static_cast(v)); CHECK_UNARY(v.has_value()); CHECK_EQ(v.value(), std::vector{42}); } } toml11-4.1.0/tests/test_value.cpp000066400000000000000000001562601464712047700167000ustar00rootroot00000000000000#define DOCTEST_CONFIG_IMPLEMENT_WITH_MAIN #include "doctest.h" #include "utility.hpp" #include #include #include template void test_is_type(const toml::basic_value& v, const toml::value_t t) { CHECK_EQ(v.type(), t); if(t == toml::value_t::boolean ) {CHECK_UNARY(v.is_boolean() );} else {CHECK_UNARY_FALSE(v.is_boolean() );} if(t == toml::value_t::integer ) {CHECK_UNARY(v.is_integer() );} else {CHECK_UNARY_FALSE(v.is_integer() );} if(t == toml::value_t::floating ) {CHECK_UNARY(v.is_floating() );} else {CHECK_UNARY_FALSE(v.is_floating() );} if(t == toml::value_t::string ) {CHECK_UNARY(v.is_string() );} else {CHECK_UNARY_FALSE(v.is_string() );} if(t == toml::value_t::offset_datetime) {CHECK_UNARY(v.is_offset_datetime());} else {CHECK_UNARY_FALSE(v.is_offset_datetime());} if(t == toml::value_t::local_datetime ) {CHECK_UNARY(v.is_local_datetime() );} else {CHECK_UNARY_FALSE(v.is_local_datetime() );} if(t == toml::value_t::local_date ) {CHECK_UNARY(v.is_local_date() );} else {CHECK_UNARY_FALSE(v.is_local_date() );} if(t == toml::value_t::local_time ) {CHECK_UNARY(v.is_local_time() );} else {CHECK_UNARY_FALSE(v.is_local_time() );} if(t == toml::value_t::array ) {CHECK_UNARY(v.is_array() );} else {CHECK_UNARY_FALSE(v.is_array() );} if(t == toml::value_t::table ) {CHECK_UNARY(v.is_table() );} else {CHECK_UNARY_FALSE(v.is_table() );} if(t == toml::value_t::empty ) {CHECK_UNARY(v.is_empty() );} else {CHECK_UNARY_FALSE(v.is_empty() );} using value_type = toml::basic_value; using boolean_type = typename value_type::boolean_type ; using integer_type = typename value_type::integer_type ; using floating_type = typename value_type::floating_type ; using string_type = typename value_type::string_type ; using local_time_type = typename value_type::local_time_type ; using local_date_type = typename value_type::local_date_type ; using local_datetime_type = typename value_type::local_datetime_type ; using offset_datetime_type = typename value_type::offset_datetime_type; using array_type = typename value_type::array_type ; using table_type = typename value_type::table_type ; if(t == toml::value_t::boolean ) {CHECK_UNARY(v.template is() );} else {CHECK_UNARY_FALSE(v.template is() );} if(t == toml::value_t::integer ) {CHECK_UNARY(v.template is() );} else {CHECK_UNARY_FALSE(v.template is() );} if(t == toml::value_t::floating ) {CHECK_UNARY(v.template is() );} else {CHECK_UNARY_FALSE(v.template is() );} if(t == toml::value_t::string ) {CHECK_UNARY(v.template is() );} else {CHECK_UNARY_FALSE(v.template is() );} if(t == toml::value_t::offset_datetime) {CHECK_UNARY(v.template is());} else {CHECK_UNARY_FALSE(v.template is());} if(t == toml::value_t::local_datetime ) {CHECK_UNARY(v.template is() );} else {CHECK_UNARY_FALSE(v.template is() );} if(t == toml::value_t::local_date ) {CHECK_UNARY(v.template is() );} else {CHECK_UNARY_FALSE(v.template is() );} if(t == toml::value_t::local_time ) {CHECK_UNARY(v.template is() );} else {CHECK_UNARY_FALSE(v.template is() );} if(t == toml::value_t::array ) {CHECK_UNARY(v.template is() );} else {CHECK_UNARY_FALSE(v.template is() );} if(t == toml::value_t::table ) {CHECK_UNARY(v.template is() );} else {CHECK_UNARY_FALSE(v.template is() );} // if(t == toml::value_t::empty ) {CHECK_UNARY(v.template is());} else {CHECK_UNARY_FALSE(v.template is() );} return; } template void test_as_type_throws(toml::basic_value& v, const toml::value_t t) { if(t != toml::value_t::boolean ) {CHECK_THROWS_AS(v.as_boolean() , toml::type_error); CHECK_THROWS_AS(v.template as() , toml::type_error);} if(t != toml::value_t::integer ) {CHECK_THROWS_AS(v.as_integer() , toml::type_error); CHECK_THROWS_AS(v.template as() , toml::type_error);} if(t != toml::value_t::floating ) {CHECK_THROWS_AS(v.as_floating() , toml::type_error); CHECK_THROWS_AS(v.template as() , toml::type_error);} if(t != toml::value_t::string ) {CHECK_THROWS_AS(v.as_string() , toml::type_error); CHECK_THROWS_AS(v.template as() , toml::type_error);} if(t != toml::value_t::offset_datetime) {CHECK_THROWS_AS(v.as_offset_datetime(), toml::type_error); CHECK_THROWS_AS(v.template as(), toml::type_error);} if(t != toml::value_t::local_datetime ) {CHECK_THROWS_AS(v.as_local_datetime() , toml::type_error); CHECK_THROWS_AS(v.template as() , toml::type_error);} if(t != toml::value_t::local_date ) {CHECK_THROWS_AS(v.as_local_date() , toml::type_error); CHECK_THROWS_AS(v.template as() , toml::type_error);} if(t != toml::value_t::local_time ) {CHECK_THROWS_AS(v.as_local_time() , toml::type_error); CHECK_THROWS_AS(v.template as() , toml::type_error);} if(t != toml::value_t::array ) {CHECK_THROWS_AS(v.as_array() , toml::type_error); CHECK_THROWS_AS(v.template as() , toml::type_error);} if(t != toml::value_t::table ) {CHECK_THROWS_AS(v.as_table() , toml::type_error); CHECK_THROWS_AS(v.template as() , toml::type_error);} if(t != toml::value_t::boolean ) {CHECK_THROWS_AS(as_const(v).as_boolean() , toml::type_error); CHECK_THROWS_AS(as_const(v).template as() , toml::type_error);} if(t != toml::value_t::integer ) {CHECK_THROWS_AS(as_const(v).as_integer() , toml::type_error); CHECK_THROWS_AS(as_const(v).template as() , toml::type_error);} if(t != toml::value_t::floating ) {CHECK_THROWS_AS(as_const(v).as_floating() , toml::type_error); CHECK_THROWS_AS(as_const(v).template as() , toml::type_error);} if(t != toml::value_t::string ) {CHECK_THROWS_AS(as_const(v).as_string() , toml::type_error); CHECK_THROWS_AS(as_const(v).template as() , toml::type_error);} if(t != toml::value_t::offset_datetime) {CHECK_THROWS_AS(as_const(v).as_offset_datetime(), toml::type_error); CHECK_THROWS_AS(as_const(v).template as(), toml::type_error);} if(t != toml::value_t::local_datetime ) {CHECK_THROWS_AS(as_const(v).as_local_datetime() , toml::type_error); CHECK_THROWS_AS(as_const(v).template as() , toml::type_error);} if(t != toml::value_t::local_date ) {CHECK_THROWS_AS(as_const(v).as_local_date() , toml::type_error); CHECK_THROWS_AS(as_const(v).template as() , toml::type_error);} if(t != toml::value_t::local_time ) {CHECK_THROWS_AS(as_const(v).as_local_time() , toml::type_error); CHECK_THROWS_AS(as_const(v).template as() , toml::type_error);} if(t != toml::value_t::array ) {CHECK_THROWS_AS(as_const(v).as_array() , toml::type_error); CHECK_THROWS_AS(as_const(v).template as() , toml::type_error);} if(t != toml::value_t::table ) {CHECK_THROWS_AS(as_const(v).as_table() , toml::type_error); CHECK_THROWS_AS(as_const(v).template as() , toml::type_error);} return; } template struct as_type_tester; #ifndef TOML11_TEST_DEFINE_AS_TYPE_TESTER #define TOML11_TEST_DEFINE_AS_TYPE_TESTER(ty) \ template<> \ struct as_type_tester \ { \ template \ static void test(toml::basic_value& v, const T& eq, const T& ne) \ { \ CHECK_EQ(v.as_ ## ty(), eq); \ CHECK_NE(v.as_ ## ty(), ne); \ CHECK_EQ(as_const(v).as_ ## ty(), eq); \ CHECK_NE(as_const(v).as_ ## ty(), ne); \ if(v.type() == toml::value_t::ty) \ { \ CHECK_EQ(v.as_ ## ty(std::nothrow), eq); \ CHECK_NE(v.as_ ## ty(std::nothrow), ne); \ CHECK_EQ(as_const(v).as_ ## ty(std::nothrow), eq); \ CHECK_NE(as_const(v).as_ ## ty(std::nothrow), ne); \ } \ } \ }; /**/ #endif // TOML11_TEST_DEFINE_AS_TYPE_TESTER TOML11_TEST_DEFINE_AS_TYPE_TESTER(boolean ) TOML11_TEST_DEFINE_AS_TYPE_TESTER(integer ) TOML11_TEST_DEFINE_AS_TYPE_TESTER(floating ) TOML11_TEST_DEFINE_AS_TYPE_TESTER(string ) TOML11_TEST_DEFINE_AS_TYPE_TESTER(offset_datetime) TOML11_TEST_DEFINE_AS_TYPE_TESTER(local_datetime ) TOML11_TEST_DEFINE_AS_TYPE_TESTER(local_date ) TOML11_TEST_DEFINE_AS_TYPE_TESTER(local_time ) TOML11_TEST_DEFINE_AS_TYPE_TESTER(array ) TOML11_TEST_DEFINE_AS_TYPE_TESTER(table ) #undef TOML11_TEST_DEFINE_AS_TYPE_TESTER template void test_as_type(toml::basic_value& v, const T& eq_, const U& ne_) { using elem_type = toml::detail::enum_to_type_t>; const elem_type eq(eq_); const elem_type ne(ne_); as_type_tester::test(v, eq, ne); // as CHECK_EQ(v.template as(), eq); CHECK_NE(v.template as(), ne); CHECK_EQ(as_const(v).template as(), eq); CHECK_NE(as_const(v).template as(), ne); if(v.type() == VT) { CHECK_EQ(v.template as(std::nothrow), eq); CHECK_NE(v.template as(std::nothrow), ne); CHECK_EQ(as_const(v).template as(std::nothrow), eq); CHECK_NE(as_const(v).template as(std::nothrow), ne); } return; } template void test_as_type_fmt_throws(toml::basic_value& v, const toml::value_t t) { if(t != toml::value_t::boolean ) {CHECK_THROWS_AS(v.as_boolean_fmt() , toml::type_error); CHECK_THROWS_AS(v.template as_fmt() , toml::type_error);} if(t != toml::value_t::integer ) {CHECK_THROWS_AS(v.as_integer_fmt() , toml::type_error); CHECK_THROWS_AS(v.template as_fmt() , toml::type_error);} if(t != toml::value_t::floating ) {CHECK_THROWS_AS(v.as_floating_fmt() , toml::type_error); CHECK_THROWS_AS(v.template as_fmt() , toml::type_error);} if(t != toml::value_t::string ) {CHECK_THROWS_AS(v.as_string_fmt() , toml::type_error); CHECK_THROWS_AS(v.template as_fmt() , toml::type_error);} if(t != toml::value_t::offset_datetime) {CHECK_THROWS_AS(v.as_offset_datetime_fmt(), toml::type_error); CHECK_THROWS_AS(v.template as_fmt(), toml::type_error);} if(t != toml::value_t::local_datetime ) {CHECK_THROWS_AS(v.as_local_datetime_fmt() , toml::type_error); CHECK_THROWS_AS(v.template as_fmt() , toml::type_error);} if(t != toml::value_t::local_date ) {CHECK_THROWS_AS(v.as_local_date_fmt() , toml::type_error); CHECK_THROWS_AS(v.template as_fmt() , toml::type_error);} if(t != toml::value_t::local_time ) {CHECK_THROWS_AS(v.as_local_time_fmt() , toml::type_error); CHECK_THROWS_AS(v.template as_fmt() , toml::type_error);} if(t != toml::value_t::array ) {CHECK_THROWS_AS(v.as_array_fmt() , toml::type_error); CHECK_THROWS_AS(v.template as_fmt() , toml::type_error);} if(t != toml::value_t::table ) {CHECK_THROWS_AS(v.as_table_fmt() , toml::type_error); CHECK_THROWS_AS(v.template as_fmt() , toml::type_error);} if(t != toml::value_t::boolean ) {CHECK_THROWS_AS(as_const(v).as_boolean_fmt() , toml::type_error); CHECK_THROWS_AS(as_const(v).template as_fmt() , toml::type_error);} if(t != toml::value_t::integer ) {CHECK_THROWS_AS(as_const(v).as_integer_fmt() , toml::type_error); CHECK_THROWS_AS(as_const(v).template as_fmt() , toml::type_error);} if(t != toml::value_t::floating ) {CHECK_THROWS_AS(as_const(v).as_floating_fmt() , toml::type_error); CHECK_THROWS_AS(as_const(v).template as_fmt() , toml::type_error);} if(t != toml::value_t::string ) {CHECK_THROWS_AS(as_const(v).as_string_fmt() , toml::type_error); CHECK_THROWS_AS(as_const(v).template as_fmt() , toml::type_error);} if(t != toml::value_t::offset_datetime) {CHECK_THROWS_AS(as_const(v).as_offset_datetime_fmt(), toml::type_error); CHECK_THROWS_AS(as_const(v).template as_fmt(), toml::type_error);} if(t != toml::value_t::local_datetime ) {CHECK_THROWS_AS(as_const(v).as_local_datetime_fmt() , toml::type_error); CHECK_THROWS_AS(as_const(v).template as_fmt() , toml::type_error);} if(t != toml::value_t::local_date ) {CHECK_THROWS_AS(as_const(v).as_local_date_fmt() , toml::type_error); CHECK_THROWS_AS(as_const(v).template as_fmt() , toml::type_error);} if(t != toml::value_t::local_time ) {CHECK_THROWS_AS(as_const(v).as_local_time_fmt() , toml::type_error); CHECK_THROWS_AS(as_const(v).template as_fmt() , toml::type_error);} if(t != toml::value_t::array ) {CHECK_THROWS_AS(as_const(v).as_array_fmt() , toml::type_error); CHECK_THROWS_AS(as_const(v).template as_fmt() , toml::type_error);} if(t != toml::value_t::table ) {CHECK_THROWS_AS(as_const(v).as_table_fmt() , toml::type_error); CHECK_THROWS_AS(as_const(v).template as_fmt() , toml::type_error);} return; } template struct as_type_fmt_tester; #ifndef TOML11_TEST_DEFINE_AS_TYPE_FMT_TESTER #define TOML11_TEST_DEFINE_AS_TYPE_FMT_TESTER(ty) \ template<> \ struct as_type_fmt_tester \ { \ template \ static void test(toml::basic_value& v, const FMT& fmt) \ { \ CHECK_EQ( v .as_ ## ty ## _fmt(), fmt); \ CHECK_EQ(as_const(v).as_ ## ty ## _fmt(), fmt); \ if(v.type() == toml::value_t::ty) \ { \ CHECK_EQ( v .as_ ## ty ## _fmt(std::nothrow), fmt); \ CHECK_EQ(as_const(v).as_ ## ty ## _fmt(std::nothrow), fmt); \ } \ } \ }; /**/ #endif // TOML11_TEST_DEFINE_AS_TYPE_TESTER TOML11_TEST_DEFINE_AS_TYPE_FMT_TESTER(boolean ) TOML11_TEST_DEFINE_AS_TYPE_FMT_TESTER(integer ) TOML11_TEST_DEFINE_AS_TYPE_FMT_TESTER(floating ) TOML11_TEST_DEFINE_AS_TYPE_FMT_TESTER(string ) TOML11_TEST_DEFINE_AS_TYPE_FMT_TESTER(offset_datetime) TOML11_TEST_DEFINE_AS_TYPE_FMT_TESTER(local_datetime ) TOML11_TEST_DEFINE_AS_TYPE_FMT_TESTER(local_date ) TOML11_TEST_DEFINE_AS_TYPE_FMT_TESTER(local_time ) TOML11_TEST_DEFINE_AS_TYPE_FMT_TESTER(array ) TOML11_TEST_DEFINE_AS_TYPE_FMT_TESTER(table ) #undef TOML11_TEST_DEFINE_AS_TYPE_FMT_TESTER template void test_as_type_fmt(toml::basic_value& v, const FMT& fmt) { as_type_fmt_tester::test(v, fmt); // as CHECK_EQ(v .template as_fmt(), fmt); CHECK_EQ(as_const(v).template as_fmt(), fmt); if(v.type() == VT) { CHECK_EQ(v .template as_fmt(std::nothrow), fmt); CHECK_EQ(as_const(v).template as_fmt(std::nothrow), fmt); } return; } template void test_ctors(T eq, U ne, FMT fmt) { { toml::value x(eq); test_is_type (x, TYPE); test_as_type_throws (x, TYPE); test_as_type_fmt_throws(x, TYPE); test_as_type(x, eq, ne); CHECK_EQ(x.comments().size(), 0); CHECK_EQ(x.location().is_ok(), false); } // ----------------------------------------------------------------------- { toml::value x_with_comments(eq, std::vector{"foo", "bar"}); test_is_type (x_with_comments, TYPE); test_as_type_throws (x_with_comments, TYPE); test_as_type_fmt_throws(x_with_comments, TYPE); test_as_type(x_with_comments, eq, ne); CHECK_EQ(x_with_comments.comments().size(), 2); CHECK_EQ(x_with_comments.comments().at(0), "foo"); CHECK_EQ(x_with_comments.comments().at(1), "bar"); CHECK_EQ(x_with_comments.location().is_ok(), false); } // ----------------------------------------------------------------------- { toml::value x_with_format(eq, fmt); test_is_type (x_with_format, TYPE); test_as_type_throws (x_with_format, TYPE); test_as_type_fmt_throws(x_with_format, TYPE); test_as_type (x_with_format, eq, ne); test_as_type_fmt(x_with_format, fmt); CHECK_EQ(x_with_format.comments().size(), 0); CHECK_EQ(x_with_format.location().is_ok(), false); } // ----------------------------------------------------------------------- { toml::value x_with_com_fmt(eq, fmt, std::vector{"foo", "bar"}); test_is_type (x_with_com_fmt, TYPE); test_as_type_throws (x_with_com_fmt, TYPE); test_as_type_fmt_throws(x_with_com_fmt, TYPE); test_as_type (x_with_com_fmt, eq, ne); test_as_type_fmt(x_with_com_fmt, fmt); CHECK_EQ(x_with_com_fmt.comments().size(), 2); CHECK_EQ(x_with_com_fmt.comments().at(0), "foo"); CHECK_EQ(x_with_com_fmt.comments().at(1), "bar"); CHECK_EQ(x_with_com_fmt.location().is_ok(), false); } // ----------------------------------------------------------------------- { toml::value x_assign(ne, fmt); x_assign = eq; test_is_type (x_assign, TYPE); test_as_type_throws (x_assign, TYPE); test_as_type_fmt_throws(x_assign, TYPE); test_as_type (x_assign, eq, ne); test_as_type_fmt(x_assign, fmt); CHECK_EQ(x_assign.comments().size(), 0); CHECK_EQ(x_assign.location().is_ok(), false); } // ----------------------------------------------------------------------- { toml::value x_assign_different_type(true); x_assign_different_type = eq; test_is_type (x_assign_different_type, TYPE); test_as_type_throws (x_assign_different_type, TYPE); test_as_type_fmt_throws(x_assign_different_type, TYPE); test_as_type(x_assign_different_type, eq, ne); CHECK_EQ(x_assign_different_type.comments().size(), 0); CHECK_EQ(x_assign_different_type.location().is_ok(), false); } } template void test_copy_move_ctors(T eq, U ne, FMT fmt) { { toml::value x_original(eq, fmt, std::vector{"foo", "bar"}); toml::value x_copy(x_original); test_is_type (x_copy, TYPE); test_as_type_throws (x_copy, TYPE); test_as_type_fmt_throws(x_copy, TYPE); test_as_type (x_copy, eq, ne); test_as_type_fmt(x_copy, fmt); CHECK_EQ(x_copy.comments().size(), 2); CHECK_EQ(x_copy.comments().at(0), "foo"); CHECK_EQ(x_copy.comments().at(1), "bar"); CHECK_EQ(x_copy.location().is_ok(), false); x_copy = 0; // reset with integer 0 x_copy = x_original; test_is_type (x_copy, TYPE); test_as_type_throws (x_copy, TYPE); test_as_type_fmt_throws(x_copy, TYPE); test_as_type (x_copy, eq, ne); test_as_type_fmt(x_copy, fmt); CHECK_EQ(x_copy.comments().size(), 2); CHECK_EQ(x_copy.comments().at(0), "foo"); CHECK_EQ(x_copy.comments().at(1), "bar"); CHECK_EQ(x_copy.location().is_ok(), false); } // ----------------------------------------------------------------------- { toml::value x_original(eq, fmt, std::vector{"foo", "bar"}); toml::value x_move(std::move(x_original)); test_is_type (x_move, TYPE); test_as_type_throws (x_move, TYPE); test_as_type_fmt_throws(x_move, TYPE); test_as_type (x_move, eq, ne); test_as_type_fmt(x_move, fmt); CHECK_EQ(x_move.comments().size(), 2); CHECK_EQ(x_move.comments().at(0), "foo"); CHECK_EQ(x_move.comments().at(1), "bar"); CHECK_EQ(x_move.location().is_ok(), false); x_move = 0; // reset with integer 0 toml::value x_original2(eq, fmt, std::vector{"foo", "bar"}); x_move = std::move(x_original2); test_is_type (x_move, TYPE); test_as_type_throws (x_move, TYPE); test_as_type_fmt_throws(x_move, TYPE); test_as_type (x_move, eq, ne); test_as_type_fmt(x_move, fmt); CHECK_EQ(x_move.comments().size(), 2); CHECK_EQ(x_move.comments().at(0), "foo"); CHECK_EQ(x_move.comments().at(1), "bar"); CHECK_EQ(x_move.location().is_ok(), false); } } TEST_CASE("testing default constructor (empty)") { toml::value v; test_is_type(v, toml::value_t::empty); CHECK_EQ(v.comments().size(), 0); CHECK_EQ(v.location().is_ok(), false); toml::value v1(v); test_is_type(v1, toml::value_t::empty); CHECK_EQ(v1.comments().size(), 0); CHECK_EQ(v1.location().is_ok(), false); toml::value v2(std::move(v)); test_is_type(v2, toml::value_t::empty); CHECK_EQ(v2.comments().size(), 0); CHECK_EQ(v2.location().is_ok(), false); toml::value v3(std::move(v), std::vector{"foo", "bar"}); test_is_type(v3, toml::value_t::empty); CHECK_EQ(v3.comments().size(), 2); CHECK_EQ(v3.comments().at(0), "foo"); CHECK_EQ(v3.comments().at(1), "bar"); CHECK_EQ(v3.location().is_ok(), false); } TEST_CASE("testing constructor (boolean)") { toml::boolean_format_info fmt; const bool eq = true; const bool ne = false; test_ctors (eq, ne, fmt); test_copy_move_ctors(eq, ne, fmt); } TEST_CASE("testing constructor (integer)") { toml::integer_format_info fmt; fmt.width = 10; const int eq = 42; const int ne = 6 * 9; test_ctors (eq, ne, fmt); test_copy_move_ctors(eq, ne, fmt); } TEST_CASE("testing constructor (floating)") { toml::floating_format_info fmt; fmt.fmt = toml::floating_format::fixed; fmt.prec = 6; const double eq = 3.14; const double ne = 2.71; test_ctors (eq, ne, fmt); test_copy_move_ctors(eq, ne, fmt); } TEST_CASE("testing constructor (string)") { toml::string_format_info fmt; fmt.fmt = toml::string_format::literal; const std::string eq("hoge"); const std::string ne("fuga"); test_ctors (eq, ne, fmt); test_copy_move_ctors(eq, ne, fmt); } TEST_CASE("testing constructor (literal string)") { toml::string_format_info fmt; fmt.fmt = toml::string_format::literal; // to explicitly pass (const char&)[N], we write it here directly // {{{ // ----------------------------------------------------------------------- { toml::value x("foo"); test_is_type(x, toml::value_t::string); test_as_type_throws(x, toml::value_t::string); test_as_type_fmt_throws(x, toml::value_t::string); test_as_type(x, "foo", "bar"); CHECK_EQ(x.comments().size(), 0); CHECK_EQ(x.location().is_ok(), false); } // ----------------------------------------------------------------------- { toml::value x_with_comments("foo", std::vector{"foo", "bar"}); test_is_type (x_with_comments, toml::value_t::string); test_as_type_throws (x_with_comments, toml::value_t::string); test_as_type_fmt_throws(x_with_comments, toml::value_t::string); test_as_type(x_with_comments, "foo", "bar"); CHECK_EQ(x_with_comments.comments().size(), 2); CHECK_EQ(x_with_comments.comments().at(0), "foo"); CHECK_EQ(x_with_comments.comments().at(1), "bar"); CHECK_EQ(x_with_comments.location().is_ok(), false); } // ----------------------------------------------------------------------- { toml::value x_with_format("foo", fmt); test_is_type (x_with_format, toml::value_t::string); test_as_type_throws (x_with_format, toml::value_t::string); test_as_type_fmt_throws(x_with_format, toml::value_t::string); test_as_type (x_with_format, "foo", "bar"); test_as_type_fmt(x_with_format, fmt); CHECK_EQ(x_with_format.comments().size(), 0); CHECK_EQ(x_with_format.location().is_ok(), false); } // ----------------------------------------------------------------------- { toml::value x_with_com_fmt("foo", fmt, std::vector{"foo", "bar"}); test_is_type (x_with_com_fmt, toml::value_t::string); test_as_type_throws (x_with_com_fmt, toml::value_t::string); test_as_type_fmt_throws(x_with_com_fmt, toml::value_t::string); test_as_type (x_with_com_fmt, "foo", "bar"); test_as_type_fmt(x_with_com_fmt, fmt); CHECK_EQ(x_with_com_fmt.comments().size(), 2); CHECK_EQ(x_with_com_fmt.comments().at(0), "foo"); CHECK_EQ(x_with_com_fmt.comments().at(1), "bar"); CHECK_EQ(x_with_com_fmt.location().is_ok(), false); } // ----------------------------------------------------------------------- { toml::value x_from_string(std::string("bar"), fmt); x_from_string = "foo"; test_is_type (x_from_string, toml::value_t::string); test_as_type_throws (x_from_string, toml::value_t::string); test_as_type_fmt_throws(x_from_string, toml::value_t::string); test_as_type (x_from_string, "foo", "bar"); test_as_type_fmt(x_from_string, fmt); CHECK_EQ(x_from_string.comments().size(), 0); CHECK_EQ(x_from_string.location().is_ok(), false); } // ----------------------------------------------------------------------- { toml::value x_from_non_string(true); x_from_non_string = "foo"; test_is_type (x_from_non_string, toml::value_t::string); test_as_type_throws (x_from_non_string, toml::value_t::string); test_as_type_fmt_throws(x_from_non_string, toml::value_t::string); test_as_type(x_from_non_string, "foo", "bar"); CHECK_EQ(x_from_non_string.comments().size(), 0); CHECK_EQ(x_from_non_string.location().is_ok(), false); } // }}} } #ifdef TOML11_HAS_STRING_VIEW #include TEST_CASE("testing constructor (string_view)") { toml::string_format_info fmt; fmt.fmt = toml::string_format::literal; const std::string_view eq("hoge"); const std::string_view ne("fuga"); test_ctors(eq, ne, fmt); } #endif #ifdef TOML11_HAS_CHAR8_T TEST_CASE("testing constructor (u8string)") { toml::string_format_info fmt; fmt.fmt = toml::string_format::basic; const std::string eq("hoge"); const std::string ne("fuga"); const std::u8string ref(u8"hoge"); { toml::value x(ref); test_is_type(x, toml::value_t::string); test_as_type_throws(x, toml::value_t::string); test_as_type_fmt_throws(x, toml::value_t::string); test_as_type(x, "hoge", "fuga"); } // ----------------------------------------------------------------------- { toml::value x_with_comments(ref, std::vector{"foo", "bar"}); test_is_type (x_with_comments, toml::value_t::string); test_as_type_throws (x_with_comments, toml::value_t::string); test_as_type_fmt_throws(x_with_comments, toml::value_t::string); test_as_type(x_with_comments, eq, ne); CHECK_EQ(x_with_comments.comments().size(), 2); CHECK_EQ(x_with_comments.comments().at(0), "foo"); CHECK_EQ(x_with_comments.comments().at(1), "bar"); CHECK_EQ(x_with_comments.location().is_ok(), false); } // ----------------------------------------------------------------------- { toml::value x_with_format(eq, fmt); test_is_type (x_with_format, toml::value_t::string); test_as_type_throws (x_with_format, toml::value_t::string); test_as_type_fmt_throws(x_with_format, toml::value_t::string); test_as_type (x_with_format, eq, ne); test_as_type_fmt(x_with_format, fmt); CHECK_EQ(x_with_format.comments().size(), 0); CHECK_EQ(x_with_format.location().is_ok(), false); } // ----------------------------------------------------------------------- { toml::value x_with_com_fmt(ref, fmt, std::vector{"foo", "bar"}); test_is_type (x_with_com_fmt, toml::value_t::string); test_as_type_throws (x_with_com_fmt, toml::value_t::string); test_as_type_fmt_throws(x_with_com_fmt, toml::value_t::string); test_as_type (x_with_com_fmt, eq, ne); test_as_type_fmt(x_with_com_fmt, fmt); CHECK_EQ(x_with_com_fmt.comments().size(), 2); CHECK_EQ(x_with_com_fmt.comments().at(0), "foo"); CHECK_EQ(x_with_com_fmt.comments().at(1), "bar"); CHECK_EQ(x_with_com_fmt.location().is_ok(), false); } // ----------------------------------------------------------------------- { toml::value x_assign(ne, fmt); x_assign = ref; test_is_type (x_assign, toml::value_t::string); test_as_type_throws (x_assign, toml::value_t::string); test_as_type_fmt_throws(x_assign, toml::value_t::string); test_as_type (x_assign, eq, ne); test_as_type_fmt(x_assign, fmt); CHECK_EQ(x_assign.comments().size(), 0); CHECK_EQ(x_assign.location().is_ok(), false); } // ----------------------------------------------------------------------- { toml::value x_assign_different_type(true); x_assign_different_type = ref; test_is_type (x_assign_different_type, toml::value_t::string); test_as_type_throws (x_assign_different_type, toml::value_t::string); test_as_type_fmt_throws(x_assign_different_type, toml::value_t::string); test_as_type(x_assign_different_type, eq, ne); CHECK_EQ(x_assign_different_type.comments().size(), 0); CHECK_EQ(x_assign_different_type.location().is_ok(), false); } } #endif TEST_CASE("testing constructor (local_date)") { toml::local_date_format_info fmt; const toml::local_date eq(2023, toml::month_t::Feb, 2); const toml::local_date ne(2023, toml::month_t::Jan, 1); test_ctors (eq, ne, fmt); test_copy_move_ctors(eq, ne, fmt); } TEST_CASE("testing constructor (local_time)") { toml::local_time_format_info fmt; fmt.subsecond_precision = 3; const toml::local_time eq(12, 30, 45); const toml::local_time ne( 2, 34, 56); test_ctors (eq, ne, fmt); test_copy_move_ctors(eq, ne, fmt); } TEST_CASE("testing constructor (std::chrono::duration)") { toml::local_time_format_info fmt; fmt.subsecond_precision = 3; const auto eq = std::chrono::hours(12) ; const auto ne = std::chrono::seconds(12); test_ctors(eq, ne, fmt); } TEST_CASE("testing constructor (local_datetime)") { toml::local_datetime_format_info fmt; fmt.subsecond_precision = 3; const toml::local_datetime eq(toml::local_date(2023, toml::month_t::Feb, 2), toml::local_time(12, 30, 45)); const toml::local_datetime ne(toml::local_date(2023, toml::month_t::Jan, 1), toml::local_time( 2, 34, 56)); test_ctors (eq, ne, fmt); test_copy_move_ctors(eq, ne, fmt); } TEST_CASE("testing constructor (offset_datetime)") { toml::offset_datetime_format_info fmt; fmt.subsecond_precision = 3; const toml::offset_datetime eq(toml::local_date(2023, toml::month_t::Feb, 2), toml::local_time(12, 30, 45), toml::time_offset(9, 0)); const toml::offset_datetime ne(toml::local_date(2023, toml::month_t::Jan, 1), toml::local_time( 2, 34, 56), toml::time_offset(-9, 0)); test_ctors (eq, ne, fmt); test_copy_move_ctors(eq, ne, fmt); } TEST_CASE("testing constructor (system_clock::time_point)") { toml::offset_datetime_format_info fmt; fmt.subsecond_precision = 3; // we use zero-offset because offset_datetime uses gmtime. const std::chrono::system_clock::time_point eq = toml::offset_datetime(toml::local_date(2023, toml::month_t::Feb, 2), toml::local_time(12, 30, 45), toml::time_offset(0, 0)); const std::chrono::system_clock::time_point ne = toml::offset_datetime(toml::local_date(2023, toml::month_t::Jan, 1), toml::local_time( 2, 34, 56), toml::time_offset(-9, 0)); test_ctors(eq, ne, fmt); } TEST_CASE("testing constructor (array)") { toml::array_format_info fmt; fmt.body_indent = 10; fmt.closing_indent = 5; const toml::array eq{true, 42, "hoge"}; const toml::array ne{false, 3.14, "fuga"}; test_ctors (eq, ne, fmt); test_copy_move_ctors(eq, ne, fmt); } TEST_CASE("testing constructor (array-like)") { toml::array_format_info fmt; fmt.body_indent = 10; fmt.closing_indent = 5; toml::value x(std::deque{true, 42, "hoge"}); test_is_type(x, toml::value_t::array); test_as_type_throws(x, toml::value_t::array); test_as_type_fmt_throws(x, toml::value_t::array); CHECK_UNARY(x.at(0).is_boolean()); CHECK_UNARY(x.at(1).is_integer()); CHECK_UNARY(x.at(2).is_string()); CHECK_EQ(x.at(0).as_boolean(), true); CHECK_EQ(x.at(1).as_integer(), 42); CHECK_EQ(x.at(2).as_string(), std::string("hoge")); // ----------------------------------------------------------------------- toml::value x_with_comments(std::deque{true, 42, "hoge"}, std::vector{"foo", "bar"}); test_is_type (x_with_comments, toml::value_t::array); test_as_type_throws (x_with_comments, toml::value_t::array); test_as_type_fmt_throws(x_with_comments, toml::value_t::array); CHECK_UNARY(x_with_comments.at(0).is_boolean()); CHECK_UNARY(x_with_comments.at(1).is_integer()); CHECK_UNARY(x_with_comments.at(2).is_string()); CHECK_EQ(x_with_comments.at(0).as_boolean(), true); CHECK_EQ(x_with_comments.at(1).as_integer(), 42); CHECK_EQ(x_with_comments.at(2).as_string(), std::string("hoge")); CHECK_EQ(x_with_comments.comments().size(), 2); CHECK_EQ(x_with_comments.comments().at(0), "foo"); CHECK_EQ(x_with_comments.comments().at(1), "bar"); CHECK_EQ(x_with_comments.location().is_ok(), false); // ----------------------------------------------------------------------- toml::value x_with_format(std::deque{true, 42, "hoge"}, fmt); test_is_type (x_with_format, toml::value_t::array); test_as_type_throws (x_with_format, toml::value_t::array); test_as_type_fmt_throws(x_with_format, toml::value_t::array); CHECK_UNARY(x_with_format.at(0).is_boolean()); CHECK_UNARY(x_with_format.at(1).is_integer()); CHECK_UNARY(x_with_format.at(2).is_string()); CHECK_EQ(x_with_format.at(0).as_boolean(), true); CHECK_EQ(x_with_format.at(1).as_integer(), 42); CHECK_EQ(x_with_format.at(2).as_string(), std::string("hoge")); CHECK_EQ(x_with_format.as_array_fmt(), fmt); CHECK_EQ(x_with_format.comments().size(), 0); CHECK_EQ(x_with_format.location().is_ok(), false); // ----------------------------------------------------------------------- toml::value x_with_com_fmt(std::deque{true, 42, "hoge"}, fmt, std::vector{"foo", "bar"}); test_is_type (x_with_com_fmt, toml::value_t::array); test_as_type_throws (x_with_com_fmt, toml::value_t::array); test_as_type_fmt_throws(x_with_com_fmt, toml::value_t::array); CHECK_UNARY(x_with_com_fmt.at(0).is_boolean()); CHECK_UNARY(x_with_com_fmt.at(1).is_integer()); CHECK_UNARY(x_with_com_fmt.at(2).is_string()); CHECK_EQ(x_with_com_fmt.at(0).as_boolean(), true); CHECK_EQ(x_with_com_fmt.at(1).as_integer(), 42); CHECK_EQ(x_with_com_fmt.at(2).as_string(), std::string("hoge")); CHECK_EQ(x_with_com_fmt.as_array_fmt(), fmt); CHECK_EQ(x_with_com_fmt.comments().size(), 2); CHECK_EQ(x_with_com_fmt.comments().at(0), "foo"); CHECK_EQ(x_with_com_fmt.comments().at(1), "bar"); CHECK_EQ(x_with_com_fmt.location().is_ok(), false); // ----------------------------------------------------------------------- toml::value x_from_array(std::deque{3.14, 2.71, "foo"}, fmt); x_from_array = std::deque{true, 42, "hoge"}; test_is_type (x_from_array, toml::value_t::array); test_as_type_throws (x_from_array, toml::value_t::array); test_as_type_fmt_throws(x_from_array, toml::value_t::array); CHECK_UNARY(x_from_array.at(0).is_boolean()); CHECK_UNARY(x_from_array.at(1).is_integer()); CHECK_UNARY(x_from_array.at(2).is_string()); CHECK_EQ(x_from_array.at(0).as_boolean(), true); CHECK_EQ(x_from_array.at(1).as_integer(), 42); CHECK_EQ(x_from_array.at(2).as_string(), std::string("hoge")); CHECK_EQ(x_from_array.as_array_fmt(), fmt); CHECK_EQ(x_from_array.comments().size(), 0); CHECK_EQ(x_from_array.location().is_ok(), false); // ----------------------------------------------------------------------- toml::value x_from_non_array(true); x_from_non_array = std::deque{true, 42, "hoge"}; test_is_type (x_from_non_array, toml::value_t::array); test_as_type_throws (x_from_non_array, toml::value_t::array); test_as_type_fmt_throws(x_from_non_array, toml::value_t::array); CHECK_UNARY(x_from_non_array.at(0).is_boolean()); CHECK_UNARY(x_from_non_array.at(1).is_integer()); CHECK_UNARY(x_from_non_array.at(2).is_string()); CHECK_EQ(x_from_non_array.at(0).as_boolean(), true); CHECK_EQ(x_from_non_array.at(1).as_integer(), 42); CHECK_EQ(x_from_non_array.at(2).as_string(), std::string("hoge")); CHECK_EQ(x_from_non_array.comments().size(), 0); CHECK_EQ(x_from_non_array.location().is_ok(), false); } TEST_CASE("testing constructor (table)") { toml::table_format_info fmt; fmt.body_indent = 10; const toml::table eq{{"a", true }, {"b", 42}, {"c", "hoge"}}; const toml::table ne{{"alpha", false}, {"b", 42}, {"c", "fuga"}}; test_ctors (eq, ne, fmt); test_copy_move_ctors(eq, ne, fmt); } TEST_CASE("testing constructor (table-like)") { toml::table_format_info fmt; fmt.body_indent = 10; toml::value x(std::map{{"a", true}, {"b", 42}, {"c", "hoge"}}); test_is_type(x, toml::value_t::table); test_as_type_throws(x, toml::value_t::table); test_as_type_fmt_throws(x, toml::value_t::table); CHECK_UNARY(x.at("a").is_boolean()); CHECK_UNARY(x.at("b").is_integer()); CHECK_UNARY(x.at("c").is_string()); CHECK_EQ(x.at("a").as_boolean(), true); CHECK_EQ(x.at("b").as_integer(), 42); CHECK_EQ(x.at("c").as_string(), std::string("hoge")); // ----------------------------------------------------------------------- toml::value x_with_comments(std::map{{"a", true}, {"b", 42}, {"c", "hoge"}}, std::vector{"foo", "bar"}); test_is_type (x_with_comments, toml::value_t::table); test_as_type_throws (x_with_comments, toml::value_t::table); test_as_type_fmt_throws(x_with_comments, toml::value_t::table); CHECK_UNARY(x_with_comments.at("a").is_boolean()); CHECK_UNARY(x_with_comments.at("b").is_integer()); CHECK_UNARY(x_with_comments.at("c").is_string()); CHECK_EQ(x_with_comments.at("a").as_boolean(), true); CHECK_EQ(x_with_comments.at("b").as_integer(), 42); CHECK_EQ(x_with_comments.at("c").as_string(), std::string("hoge")); CHECK_EQ(x_with_comments.comments().size(), 2); CHECK_EQ(x_with_comments.comments().at(0), "foo"); CHECK_EQ(x_with_comments.comments().at(1), "bar"); CHECK_EQ(x_with_comments.location().is_ok(), false); // ----------------------------------------------------------------------- toml::value x_with_format(std::map{{"a", true}, {"b", 42}, {"c", "hoge"}}, fmt); test_is_type (x_with_format, toml::value_t::table); test_as_type_throws (x_with_format, toml::value_t::table); test_as_type_fmt_throws(x_with_format, toml::value_t::table); CHECK_UNARY(x_with_format.at("a").is_boolean()); CHECK_UNARY(x_with_format.at("b").is_integer()); CHECK_UNARY(x_with_format.at("c").is_string()); CHECK_EQ(x_with_format.at("a").as_boolean(), true); CHECK_EQ(x_with_format.at("b").as_integer(), 42); CHECK_EQ(x_with_format.at("c").as_string(), std::string("hoge")); CHECK_EQ(x_with_format.as_table_fmt(), fmt); CHECK_EQ(x_with_format.comments().size(), 0); CHECK_EQ(x_with_format.location().is_ok(), false); // ----------------------------------------------------------------------- toml::value x_with_com_fmt(std::map{{"a", true}, {"b", 42}, {"c", "hoge"}}, fmt, std::vector{"foo", "bar"}); test_is_type (x_with_com_fmt, toml::value_t::table); test_as_type_throws (x_with_com_fmt, toml::value_t::table); test_as_type_fmt_throws(x_with_com_fmt, toml::value_t::table); CHECK_UNARY(x_with_com_fmt.at("a").is_boolean()); CHECK_UNARY(x_with_com_fmt.at("b").is_integer()); CHECK_UNARY(x_with_com_fmt.at("c").is_string()); CHECK_EQ(x_with_com_fmt.at("a").as_boolean(), true); CHECK_EQ(x_with_com_fmt.at("b").as_integer(), 42); CHECK_EQ(x_with_com_fmt.at("c").as_string(), std::string("hoge")); CHECK_EQ(x_with_com_fmt.as_table_fmt(), fmt); CHECK_EQ(x_with_com_fmt.comments().size(), 2); CHECK_EQ(x_with_com_fmt.comments().at(0), "foo"); CHECK_EQ(x_with_com_fmt.comments().at(1), "bar"); CHECK_EQ(x_with_com_fmt.location().is_ok(), false); // ----------------------------------------------------------------------- toml::value x_from_table(toml::table{{"foo", "bar"}}, fmt); x_from_table = std::map{{"a", true}, {"b", 42}, {"c", "hoge"}}; test_is_type (x_from_table, toml::value_t::table); test_as_type_throws (x_from_table, toml::value_t::table); test_as_type_fmt_throws(x_from_table, toml::value_t::table); CHECK_UNARY(x_from_table.at("a").is_boolean()); CHECK_UNARY(x_from_table.at("b").is_integer()); CHECK_UNARY(x_from_table.at("c").is_string()); CHECK_EQ(x_from_table.at("a").as_boolean(), true); CHECK_EQ(x_from_table.at("b").as_integer(), 42); CHECK_EQ(x_from_table.at("c").as_string(), std::string("hoge")); CHECK_EQ(x_from_table.as_table_fmt(), fmt); CHECK_EQ(x_from_table.comments().size(), 0); CHECK_EQ(x_from_table.location().is_ok(), false); // ----------------------------------------------------------------------- toml::value x_from_non_table(true); x_from_non_table = std::map{{"a", true}, {"b", 42}, {"c", "hoge"}}; test_is_type (x_from_non_table, toml::value_t::table); test_as_type_throws (x_from_non_table, toml::value_t::table); test_as_type_fmt_throws(x_from_non_table, toml::value_t::table); CHECK_UNARY(x_from_non_table.at("a").is_boolean()); CHECK_UNARY(x_from_non_table.at("b").is_integer()); CHECK_UNARY(x_from_non_table.at("c").is_string()); CHECK_EQ(x_from_non_table.at("a").as_boolean(), true); CHECK_EQ(x_from_non_table.at("b").as_integer(), 42); CHECK_EQ(x_from_non_table.at("c").as_string(), std::string("hoge")); CHECK_EQ(x_from_non_table.comments().size(), 0); CHECK_EQ(x_from_non_table.location().is_ok(), false); } struct X { toml::value into_toml() const { return toml::value(this->value); } int value; }; inline bool operator==(const X& lhs, const X& rhs) { return lhs.value == rhs.value; } inline bool operator!=(const X& lhs, const X& rhs) { return lhs.value != rhs.value; } TEST_CASE("testing constructor (into_toml)") { const X initialized_with{42}; const X different_from{6*9}; // ----------------------------------------------------------------------- toml::value x(initialized_with); test_is_type(x, toml::value_t::integer); test_as_type_throws(x, toml::value_t::integer); test_as_type_fmt_throws(x, toml::value_t::integer); CHECK_EQ(x.as_integer(), initialized_with.value); CHECK_NE(x.as_integer(), different_from.value); CHECK_EQ(x.as_integer(std::nothrow), initialized_with.value); CHECK_NE(x.as_integer(std::nothrow), different_from.value); CHECK_EQ(x.comments().size(), 0); CHECK_EQ(x.location().is_ok(), false); // ----------------------------------------------------------------------- toml::value x_with_comments(initialized_with, std::vector{"foo", "bar"}); test_is_type (x_with_comments, toml::value_t::integer); test_as_type_throws (x_with_comments, toml::value_t::integer); test_as_type_fmt_throws(x_with_comments, toml::value_t::integer); CHECK_EQ(x_with_comments.as_integer(), initialized_with.value); CHECK_NE(x_with_comments.as_integer(), different_from.value); CHECK_EQ(x_with_comments.as_integer(std::nothrow), initialized_with.value); CHECK_NE(x_with_comments.as_integer(std::nothrow), different_from.value); CHECK_EQ(x_with_comments.comments().size(), 2); CHECK_EQ(x_with_comments.comments().at(0), "foo"); CHECK_EQ(x_with_comments.comments().at(1), "bar"); CHECK_EQ(x_with_comments.location().is_ok(), false); // ----------------------------------------------------------------------- toml::value x_from_integer(different_from); x_from_integer = initialized_with; test_is_type (x_from_integer, toml::value_t::integer); test_as_type_throws (x_from_integer, toml::value_t::integer); test_as_type_fmt_throws(x_from_integer, toml::value_t::integer); CHECK_EQ(x_from_integer.as_integer(), initialized_with.value); CHECK_NE(x_from_integer.as_integer(), different_from.value); CHECK_EQ(x_from_integer.as_integer(std::nothrow), initialized_with.value); CHECK_NE(x_from_integer.as_integer(std::nothrow), different_from.value); CHECK_EQ(x_from_integer.comments().size(), 0); CHECK_EQ(x_from_integer.location().is_ok(), false); // ----------------------------------------------------------------------- toml::value x_from_non_integer(true); x_from_non_integer = initialized_with; test_is_type (x_from_non_integer, toml::value_t::integer); test_as_type_throws (x_from_non_integer, toml::value_t::integer); test_as_type_fmt_throws(x_from_non_integer, toml::value_t::integer); CHECK_EQ(x_from_non_integer.as_integer(), initialized_with.value); CHECK_NE(x_from_non_integer.as_integer(), different_from.value); CHECK_EQ(x_from_non_integer.as_integer(std::nothrow), initialized_with.value); CHECK_NE(x_from_non_integer.as_integer(std::nothrow), different_from.value); CHECK_EQ(x_from_non_integer.comments().size(), 0); CHECK_EQ(x_from_non_integer.location().is_ok(), false); } TEST_CASE("testing array-accessors") { toml::value x({true, 42, "hoge"}); CHECK_UNARY(x.at(0).is_boolean()); CHECK_UNARY(x.at(1).is_integer()); CHECK_UNARY(x.at(2).is_string()); CHECK_UNARY(as_const(x).at(0).is_boolean()); CHECK_UNARY(as_const(x).at(1).is_integer()); CHECK_UNARY(as_const(x).at(2).is_string()); CHECK_EQ(x.at(0).as_boolean(), true); CHECK_EQ(x.at(1).as_integer(), 42); CHECK_EQ(x.at(2).as_string(), std::string("hoge")); CHECK_EQ(as_const(x).at(0).as_boolean(), true); CHECK_EQ(as_const(x).at(1).as_integer(), 42); CHECK_EQ(as_const(x).at(2).as_string(), std::string("hoge")); CHECK_UNARY(x[0].is_boolean()); CHECK_UNARY(x[1].is_integer()); CHECK_UNARY(x[2].is_string()); CHECK_EQ(x[0].as_boolean(), true); CHECK_EQ(x[1].as_integer(), 42); CHECK_EQ(x[2].as_string(), std::string("hoge")); const toml::value v1(3.14); toml::value v2(2.71); x.push_back(v1); x.push_back(std::move(v2)); CHECK_UNARY(x.at(3).is_floating()); CHECK_UNARY(x.at(4).is_floating()); CHECK_EQ(x.at(3).as_floating(), 3.14); CHECK_EQ(x.at(4).as_floating(), 2.71); x.emplace_back(6.022e23, std::vector{"mol"}); CHECK_UNARY(x.at(5).is_floating()); CHECK_EQ(x.at(5).as_floating(), 6.022e23); CHECK_EQ(x.at(5).comments().size(), 1); CHECK_EQ(x.at(5).comments().at(0), "mol"); CHECK_EQ(x.size(), 6); CHECK_THROWS_AS(x.at(6), std::out_of_range); CHECK_THROWS_AS(x.at(7), std::out_of_range); CHECK_THROWS_AS(as_const(x).at(6), std::out_of_range); CHECK_THROWS_AS(as_const(x).at(7), std::out_of_range); // ----------------------------------------- toml::value non_array("foobar"); toml::value v3("foo"); CHECK_THROWS_AS(non_array.at(0), toml::type_error); CHECK_THROWS_AS(as_const(non_array).at(0), toml::type_error); CHECK_THROWS_AS(non_array.push_back(as_const(v3)), toml::type_error); CHECK_THROWS_AS(non_array.push_back(std::move(v3)), toml::type_error); CHECK_THROWS_AS(non_array.emplace_back(42), toml::type_error); // ----------------------------------------- toml::value string_has_size("foobar"); CHECK_EQ(string_has_size.size(), 6); } TEST_CASE("testing table-accessors") { toml::value x({ {"a", true}, {"b", 42}, {"c", "hoge"} }); CHECK_UNARY(x.contains("a")); CHECK_UNARY(x.contains("b")); CHECK_UNARY(x.contains("c")); CHECK_UNARY_FALSE(x.contains("d")); CHECK_UNARY_FALSE(x.contains("e")); CHECK_EQ(x.count("a"), 1); CHECK_EQ(x.count("b"), 1); CHECK_EQ(x.count("c"), 1); CHECK_EQ(x.count("d"), 0); CHECK_EQ(x.count("e"), 0); CHECK_UNARY(x.at("a").is_boolean()); CHECK_UNARY(x.at("b").is_integer()); CHECK_UNARY(x.at("c").is_string()); CHECK_EQ(x.at("a").as_boolean(), true); CHECK_EQ(x.at("b").as_integer(), 42); CHECK_EQ(x.at("c").as_string(), std::string("hoge")); CHECK_UNARY(x["a"].is_boolean()); CHECK_UNARY(x["b"].is_integer()); CHECK_UNARY(x["c"].is_string()); CHECK_EQ(x["a"].as_boolean(), true); CHECK_EQ(x["b"].as_integer(), 42); CHECK_EQ(x["c"].as_string(), std::string("hoge")); const toml::value v1(3.14); x["d"] = v1; CHECK_UNARY(x.at("d").is_floating()); CHECK_EQ(x.at("d").as_floating(), 3.14); CHECK_UNARY(x.contains("d")); CHECK_EQ(x.count("d"), 1); CHECK_EQ(x.size(), 4); CHECK_THROWS_AS(x.at("f"), std::out_of_range); CHECK_THROWS_AS(x.at("g"), std::out_of_range); CHECK_THROWS_AS(as_const(x).at("f"), std::out_of_range); CHECK_THROWS_AS(as_const(x).at("g"), std::out_of_range); // ----------------------------------------------- toml::value non_table("foobar"); CHECK_THROWS_AS(non_table.at("foo"), toml::type_error); CHECK_THROWS_AS(as_const(non_table).at("foo"), toml::type_error); CHECK_THROWS_AS(non_table["foo"], toml::type_error); CHECK_THROWS_AS(non_table.count("foo"), toml::type_error); CHECK_THROWS_AS(non_table.contains("foo"), toml::type_error); toml::value no_size(42); CHECK_THROWS_AS(no_size.size(), toml::type_error); // ----------------------------------------------- toml::value empty; CHECK_UNARY(empty.is_empty()); empty["is"] = "table"; CHECK_UNARY(empty.is_table()); CHECK_UNARY(empty.contains("is")); CHECK_EQ(empty.at("is").as_string(), "table"); } toml11-4.1.0/tests/test_visit.cpp000066400000000000000000000110601464712047700167060ustar00rootroot00000000000000#define DOCTEST_CONFIG_IMPLEMENT_WITH_MAIN #include "doctest.h" #include #include #include #include template struct printer { using value_type = Value; using boolean_type = typename value_type::boolean_type ; using integer_type = typename value_type::integer_type ; using floating_type = typename value_type::floating_type ; using string_type = typename value_type::string_type ; using local_time_type = typename value_type::local_time_type ; using local_date_type = typename value_type::local_date_type ; using local_datetime_type = typename value_type::local_datetime_type ; using offset_datetime_type = typename value_type::offset_datetime_type; using array_type = typename value_type::array_type ; using table_type = typename value_type::table_type ; std::string operator()(const boolean_type& x) const { if(x) {return "true";} else {return "false";} } std::string operator()(const integer_type& x) const { return std::to_string(x); } std::string operator()(const floating_type& x) const { return std::to_string(x); } std::string operator()(const string_type& x) const { return "\"" + x + "\""; } std::string operator()(const local_time_type& x) const { std::ostringstream oss; oss << x; return oss.str(); } std::string operator()(const local_date_type& x) const { std::ostringstream oss; oss << x; return oss.str(); } std::string operator()(const local_datetime_type& x) const { std::ostringstream oss; oss << x; return oss.str(); } std::string operator()(const offset_datetime_type& x) const { std::ostringstream oss; oss << x; return oss.str(); } std::string operator()(const array_type& a) const { std::string str; str += '['; for(const auto& e : a) { str += toml::visit(*this, e); str += ", "; } str += ']'; return str; } std::string operator()(const table_type& t) const { std::string str; str += '{'; for(const auto& e : t) { str += e.first; str += " = "; str += toml::visit(*this, e.second); str += ", "; } if( ! t.empty()) { str.pop_back(); str.pop_back(); } str += '}'; return str; } }; struct type_config_ordered_map { using comment_type = toml::preserve_comments; using boolean_type = bool; using integer_type = std::int64_t; using floating_type = double; using string_type = std::string; template using array_type = std::vector; template using table_type = std::map; static toml::result parse_int(const std::string& str, const toml::source_location src, const std::uint8_t base) { assert(base == 10 || base == 16 || base == 8 || base == 2); return toml::read_int(str, src, base); } static toml::result parse_float(const std::string& str, const toml::source_location src, const bool is_hex) { return toml::read_float(str, src, is_hex); } }; TEST_CASE("testing toml::get with toml types") { using value_type = toml::basic_value; using array_type = typename value_type::array_type; using table_type = typename value_type::table_type; const printer p; { value_type v(true); CHECK_EQ("true", toml::visit(p, v)); } { value_type v(false); CHECK_EQ("false", toml::visit(p, v)); } { value_type v(42); CHECK_EQ(std::to_string(42), toml::visit(p, v)); } { value_type v(3.14); CHECK_EQ(std::to_string(3.14), toml::visit(p, v)); } { value_type v("foo"); CHECK_EQ("\"foo\"", toml::visit(p, v)); } { value_type v(array_type{true, 42, 3.14, "foo"}); CHECK_EQ("[true, 42, " + std::to_string(3.14) + ", \"foo\", ]", toml::visit(p, v)); } { value_type v(table_type{{"foo", true}, {"bar", 42}}); CHECK_EQ("{bar = 42, foo = true}", toml::visit(p, v)); } } toml11-4.1.0/tests/to_json.cpp000066400000000000000000000110571464712047700161720ustar00rootroot00000000000000 #include #include #include #include #include #include #ifdef TOML11_TO_JSON_USE_V1_1_0 # define TOML11_TO_JSON_SPEC toml::spec::v(1, 1, 0) #else # define TOML11_TO_JSON_SPEC toml::spec::v(1, 0, 0) #endif template struct json_converter { using value_type = Value; using boolean_type = typename value_type::boolean_type ; using integer_type = typename value_type::integer_type ; using floating_type = typename value_type::floating_type ; using string_type = typename value_type::string_type ; using local_time_type = typename value_type::local_time_type ; using local_date_type = typename value_type::local_date_type ; using local_datetime_type = typename value_type::local_datetime_type ; using offset_datetime_type = typename value_type::offset_datetime_type; using array_type = typename value_type::array_type ; using table_type = typename value_type::table_type ; nlohmann::json operator()(boolean_type v) { nlohmann::json j; j["type"] = "bool"; if(v) { j["value"] = "true"; } else { j["value"] = "false"; } return j; } nlohmann::json operator()(integer_type v) { nlohmann::json j; j["type"] = "integer"; j["value"] = std::to_string(v); return j; } nlohmann::json operator()(floating_type v) { std::ostringstream oss; oss << std::setprecision(16); // if we set the precision as max_digit, 1.1 will be 1.1000000000000001. // But toml-test does not allow 1.1000000000000001. if(std::isnan(v) && std::signbit(v)) { // toml-test does not allow negative NaN represented in "-nan" because // there are languages that does not distinguish nan and -nan. // But toml11 keeps sign from input. To resolve this difference, // we convert -nan to nan here. v = std::numeric_limits::quiet_NaN(); } oss << v; nlohmann::json j; j["type"] = "float"; j["value"] = oss.str(); return j; } nlohmann::json operator()(const string_type& v) { nlohmann::json j; j["type"] = "string"; j["value"] = v; return j; } nlohmann::json operator()(const toml::local_time& v) { nlohmann::json j; j["type"] = "time-local"; j["value"] = toml::to_string(v); return j; } nlohmann::json operator()(const toml::local_date& v) { nlohmann::json j; j["type"] = "date-local"; j["value"] = toml::to_string(v); return j; } nlohmann::json operator()(const toml::local_datetime& v) { nlohmann::json j; j["type"] = "datetime-local"; j["value"] = toml::to_string(v); return j; } nlohmann::json operator()(const toml::offset_datetime& v) { nlohmann::json j; j["type"] = "datetime"; j["value"] = toml::to_string(v); return j; } nlohmann::json operator()(const toml::array& v) { nlohmann::json j = nlohmann::json::array(); for(const auto& elem : v) { j.push_back(toml::visit(*this, elem)); } return j; } nlohmann::json operator()(const toml::table& v) { nlohmann::json j = nlohmann::json::object(); for(const auto& kv : v) { j[kv.first] = toml::visit(*this, kv.second); } return j; } }; int main(int argc, char** argv) { try { if(argc == 2) { const std::string fname(argv[1]); const auto data = toml::parse(fname, TOML11_TO_JSON_SPEC); std::cout << toml::visit(json_converter<>(), data); return 0; } else { std::vector buf; std::cin.peek(); while(!std::cin.eof()) { buf.push_back(static_cast(std::cin.get())); std::cin.peek(); } const auto data = toml::parse(buf, "cin", TOML11_TO_JSON_SPEC); std::cout << toml::visit(json_converter<>(), data); return 0; } } catch(const std::exception& err) { std::cout << "what(): " << err.what() << std::endl; return 1; } } toml11-4.1.0/tests/to_toml.cpp000066400000000000000000000066041464712047700161760ustar00rootroot00000000000000#include #include #include #include #include toml::value convert_to_toml(const nlohmann::json& j) { if(j.is_array()) { toml::value a(toml::array{}); for(const auto& v : j) { a.push_back(convert_to_toml(v)); } return a; } else if(j.size() == 2 && j.contains("type") && j.at("type").is_string() && j.contains("value") && j.at("value").is_string() ) { const auto type = j.at("type" ).get(); const auto value = j.at("value").get(); if(type == "string") { return toml::value(value); } else if(type == "int") { toml::detail::context ctx(toml::spec::default_version()); auto loc = toml::detail::make_temporary_location(value); return toml::detail::parse_integer(loc, ctx).unwrap(); } else if(type == "float") { toml::detail::context ctx(toml::spec::default_version()); auto loc = toml::detail::make_temporary_location(value); if(auto f_r = toml::detail::parse_floating(loc, ctx)) { return f_r.unwrap(); } else // not conform TOML floating-point syntax. { // toml-test converts "inf" into "Inf" if(value == "Inf" || value == "+Inf") { return toml::value(std::numeric_limits::infinity()); } else if(value == "-Inf") { return toml::value(-std::numeric_limits::infinity()); } else // sometimes toml-test uses large int with type:float { toml::floating_format_info fmt; fmt.prec = value.size(); return toml::value(toml::detail::from_string(value).unwrap(), fmt); } } } else { return toml::detail::literal_internal_impl( toml::detail::make_temporary_location(value)); } } else // table. { toml::value t(toml::table{}); for(const auto& kv : j.items()) { t[kv.key()] = convert_to_toml(kv.value()); } return t; } } int main(int argc, char** argv) { try { if(argc == 2) { const std::string fname(argv[1]); std::ifstream ifs(fname); const auto j = nlohmann::json::parse(ifs); const auto t = convert_to_toml(j); std::cout << toml::format(t) << std::endl; return 0; } else { std::vector buf; std::cin.peek(); while(!std::cin.eof()) { buf.push_back(static_cast(std::cin.get())); std::cin.peek(); } std::string str(buf.begin(), buf.end()); const auto j = nlohmann::json::parse(str); const auto t = convert_to_toml(j); std::cout << toml::format(t) << std::endl; return 0; } } catch(const std::exception& err) { std::cout << "what(): " << err.what() << std::endl; return 1; } } toml11-4.1.0/tests/utility.cpp000066400000000000000000000066401464712047700162240ustar00rootroot00000000000000#include "doctest.h" #include "utility.hpp" #include #include #include void test_scan_success(const toml::detail::scanner_base& s, const std::string& in, const std::string& out) { auto loc = toml::detail::make_temporary_location(in); const auto reg = s.scan(loc); CHECK_UNARY(reg.is_ok()); CHECK_EQ(reg.as_string(), out); } void test_scan_failure(const toml::detail::scanner_base& s, const std::string& in) { auto loc = toml::detail::make_temporary_location(in); const auto reg = s.scan(loc); CHECK_UNARY_FALSE(reg.is_ok()); } namespace toml { std::ostream& operator<<(std::ostream& os, const integer_format_info& fmt) { os << "integer_format_info{"; os << "fmt = " << fmt.fmt << ", "; os << "uppercase = " << fmt.uppercase << ", "; os << "width = " << fmt.width << ", "; os << "spacer = " << fmt.spacer << ", "; os << "suffix = \"" << fmt.suffix << "\"}"; return os; } std::ostream& operator<<(std::ostream& os, const floating_format_info& fmt) { os << "floating_format_info{"; os << "fmt = " << fmt.fmt << ", "; os << "prec = " << fmt.prec << ", "; os << "suffix = \"" << fmt.suffix << "\"}"; return os; } std::ostream& operator<<(std::ostream& os, const string_format_info& fmt) { os << "string_format_info{"; os << "fmt = " << fmt.fmt << ", "; os << "start_with_newline = " << fmt.start_with_newline << "}"; return os; } std::ostream& operator<<(std::ostream& os, const offset_datetime_format_info& fmt) { os << "offset_datetime_format_info{"; os << std::boolalpha; os << "delimiter = " << fmt.delimiter << ", "; os << "has_seconds = " << fmt.has_seconds << ", "; os << "subsecond_precision = " << fmt.subsecond_precision << "}"; return os; } std::ostream& operator<<(std::ostream& os, const local_datetime_format_info& fmt) { os << "local_datetime_format_info{"; os << std::boolalpha; os << "delimiter = " << fmt.delimiter << ", "; os << "has_seconds = " << fmt.has_seconds << ", "; os << "subsecond_precision = " << fmt.subsecond_precision << "}"; return os; } std::ostream& operator<<(std::ostream& os, const local_date_format_info&) { os << "local_date_format_info{}"; return os; } std::ostream& operator<<(std::ostream& os, const local_time_format_info& fmt) { os << "local_time_format_info{"; os << std::boolalpha; os << "has_seconds = " << fmt.has_seconds << ", "; os << "subsecond_precision = " << fmt.subsecond_precision << "}"; return os; } std::ostream& operator<<(std::ostream& os, const array_format_info& fmt) { os << "array_format_info{"; os << "fmt = " << fmt.fmt << ", "; os << "indent_type = " << fmt.indent_type << ", "; os << "body_indent = " << fmt.body_indent << ", "; os << "closing_indent = " << fmt.closing_indent << "}"; return os; } std::ostream& operator<<(std::ostream& os, const table_format_info& fmt) { os << "table_format_info{"; os << "fmt = " << fmt.fmt << ", "; os << "indent_type = " << fmt.indent_type << ", "; os << "name_indent = " << fmt.name_indent << ", "; os << "body_indent = " << fmt.body_indent << ", "; os << "closing_indent = " << fmt.closing_indent << "}"; return os; } } // toml toml11-4.1.0/tests/utility.hpp000066400000000000000000000057471464712047700162400ustar00rootroot00000000000000#ifndef TOML11_TEST_UTILITY_HPP #define TOML11_TEST_UTILITY_HPP #include #include #include #include #include #include #include #include "doctest.h" #include #include #include #include #include template toml::basic_value const& as_const(toml::basic_value& v) noexcept { return v; } template std::vector comments(Ts ... args) { return std::vector{std::move(args)...}; } void test_scan_success(const toml::detail::scanner_base& s, const std::string& in, const std::string& out); void test_scan_failure(const toml::detail::scanner_base& s, const std::string& in); template void toml11_test_parse_success( std::string in, const T& out, const std::vector& com, const Format& fmt, toml::detail::context ctx) { auto loc = toml::detail::make_temporary_location(in); const auto res = toml::detail::parse_value(loc, ctx); if(res.is_err()) { std::cerr << toml::format_error(res.unwrap_err()) << std::endl; } REQUIRE_UNARY(res.is_ok()); const auto val = res.unwrap(); REQUIRE(val.is(VT)); REQUIRE_EQ(val.type(), VT); CHECK_EQ(val.template as(), out); const std::vector com_actual( val.comments().begin(), val.comments().end()); CHECK_EQ(com_actual, com); const auto fmt_actual = val.template as_fmt(); CHECK_EQ(fmt_actual, fmt); } template void toml11_test_parse_failure(F fn, std::string in, toml::detail::context ctx) { using namespace toml::detail; auto loc = toml::detail::make_temporary_location(in); const auto res = fn(loc, ctx); REQUIRE_UNARY(res.is_err()); ctx.report_error(res.unwrap_err()); std::cerr << "-------- error messages about: `" << in.substr(0, in.find('\n')) << "` --------" << std::endl; for(const auto& e : ctx.errors()) { std::cerr << toml::format_error(e) << std::endl; } std::cerr << "-------- end --------" << std::endl; } namespace toml { std::ostream& operator<<(std::ostream& os, const integer_format_info&); std::ostream& operator<<(std::ostream& os, const floating_format_info&); std::ostream& operator<<(std::ostream& os, const string_format_info&); std::ostream& operator<<(std::ostream& os, const offset_datetime_format_info&); std::ostream& operator<<(std::ostream& os, const local_datetime_format_info&); std::ostream& operator<<(std::ostream& os, const local_date_format_info&); std::ostream& operator<<(std::ostream& os, const local_time_format_info&); std::ostream& operator<<(std::ostream& os, const array_format_info&); std::ostream& operator<<(std::ostream& os, const table_format_info&); } // toml #endif// TOML11_TEST_UTILITY_HPP toml11-4.1.0/tools/000077500000000000000000000000001464712047700140055ustar00rootroot00000000000000toml11-4.1.0/tools/expand/000077500000000000000000000000001464712047700152645ustar00rootroot00000000000000toml11-4.1.0/tools/expand/README.md000066400000000000000000000010131464712047700165360ustar00rootroot00000000000000# expand `expand` is a tool designed to process `#include "..."` directives within a file, generating a single `single_include/toml.hpp` file. `expand` is optimized specifically for the structure of `toml11`. It assumes that the argument provided is `include/toml.hpp` and that there are files separated into `fwd/` and `impl/` directories. Therefore, it is not a general-purpose tool. ## Usage ```console $ g++-13 -std=c++20 -O2 main.cpp -o expand $ ./expand ../../include/toml.hpp > ../../single_include/toml.hpp ``` toml11-4.1.0/tools/expand/main.cpp000066400000000000000000000227651464712047700167300ustar00rootroot00000000000000#include #include #include #include #include #include #include #include #include #include std::optional is_include(const std::string& line, const std::filesystem::path& filepath) { // [ws] # [ws] include [ws] \".+\" auto iter = line.begin(); while(iter < line.end()) { if(*iter != ' ' && *iter != '\t') { break; } ++iter; } if(iter == line.end() || *iter != '#') {return std::nullopt;} assert(*iter == '#'); ++iter; while(iter < line.end()) { if(*iter != ' ' && *iter != '\t') { break; } ++iter; } if(iter == line.end() || *iter != 'i') {return std::nullopt;} else {++iter;} if(iter == line.end() || *iter != 'n') {return std::nullopt;} else {++iter;} if(iter == line.end() || *iter != 'c') {return std::nullopt;} else {++iter;} if(iter == line.end() || *iter != 'l') {return std::nullopt;} else {++iter;} if(iter == line.end() || *iter != 'u') {return std::nullopt;} else {++iter;} if(iter == line.end() || *iter != 'd') {return std::nullopt;} else {++iter;} if(iter == line.end() || *iter != 'e') {return std::nullopt;} else {++iter;} while(iter < line.end()) { if(*iter != ' ' && *iter != '\t') { break; } ++iter; } if(iter == line.end() || *iter != '"') {return std::nullopt;} else {++iter;} std::string filename; while(iter < line.end()) { if(*iter == '"') {break;} filename += *iter; ++iter; } if(iter == line.end() || *iter != '"') {return std::nullopt;} else {++iter;} return std::filesystem::canonical(filepath.parent_path() / std::filesystem::path(filename)); } struct File { File() = default; explicit File(std::filesystem::path f) : filename(std::move(f)) { std::ifstream ifs(filename); if( ! ifs.good()) { throw std::runtime_error("file open error: " + filename.string()); } std::string line; while(std::getline(ifs, line)) { if(const auto incl = is_include(line, filename)) { includes.push_back(incl.value()); } else { content.push_back(line); } } } File(std::filesystem::path f, std::vector c, std::vector i) : filename(std::move(f)), content(std::move(c)), includes(std::move(i)) {} std::filesystem::path filename; std::vector content; // w/o include std::vector includes; }; struct Graph { struct Node { std::vector included; std::vector includes; }; std::map nodes; }; int main(int argc, char** argv) { using namespace std::literals::string_literals; if(argc != 2) { std::cerr << "Usage: ./a.out path/to/toml.hpp > single_include/toml.hpp" << std::endl; return 1; } const auto input_file = std::filesystem::path(std::string(argv[1])); assert(input_file.filename() == "toml.hpp"); const auto include_path = input_file.parent_path(); // ------------------------------------------------------------------------- // load files and detect `include "xxx.hpp"`. // If the file has `_fwd` and `_impl`, expand those files first. std::set fwd_impl_files; for(const auto& entities : std::filesystem::directory_iterator(include_path/"toml11"/"fwd")) { if( ! entities.is_regular_file()) {continue;} std::string fname = entities.path().filename().string(); if(fname.ends_with("_fwd.hpp")) { for(const auto c : "_fwd.hpp"s) {fname.pop_back(); (void)c;} fwd_impl_files.insert(std::move(fname)); } } for(const auto& entities : std::filesystem::directory_iterator(include_path/"toml11"/"impl")) { if( ! entities.is_regular_file()) {continue;} std::string fname = entities.path().filename().string(); if(fname.ends_with("_impl.hpp")) { for(const auto c : "_impl.hpp"s) {fname.pop_back(); (void)c;} // all impl files has fwd file assert(fwd_impl_files.contains(fname)); } } const auto input = File(input_file); std::map files; files[input_file] = input; for(const auto& fname : input.includes) { if(fwd_impl_files.contains(fname.stem().string())) { std::cerr << "expanding fwd/impl file of " << fname.string() << std::endl; // expand the first include std::ifstream ifs(fname); std::vector content; std::vector includes; std::string line; while(std::getline(ifs, line)) { // expand _fwd and _impl files first. const auto incl = is_include(line, fname); if(incl.has_value()) { // if a file has _fwd/_impl files, it only includes fwd/impl files. assert(incl.value().string().ends_with("_impl.hpp") || incl.value().string().ends_with("_fwd.hpp") ); const File included(incl.value()); for(const auto& l : included.content) { content.push_back(l); } for(const auto& i : included.includes) { includes.push_back(i); } } else { content.push_back(line); } } files[fname] = File(fname, std::move(content), std::move(includes)); } else { files[fname] = File(fname); } std::cerr << "file " << fname << " has " << files.at(fname).content.size() << " lines." << std::endl; } std::cerr << "-------------------------------------------------------------\n"; std::cerr << "files have been read. next: constructing dependency graph...\n"; std::cerr << "-------------------------------------------------------------\n"; // ------------------------------------------------------------------------- // construct dependency graph Graph g; for(const auto& [k, v] : files) { g.nodes[k] = Graph::Node{}; } for(const auto& [fname, file] : files) { for(auto incl : file.includes) { auto incl_stem = incl.stem().string(); if(incl_stem.ends_with("_fwd")) { for(const char c : "_fwd"s) {incl_stem.pop_back(); (void)c;} // include original file instaed incl = incl.parent_path() / ".." / std::filesystem::path(incl_stem + ".hpp"); } else if(incl_stem.ends_with("_impl")) { for(const char c : "_impl"s) {incl_stem.pop_back(); (void)c;} // include original file instaed incl = incl.parent_path() / ".." / std::filesystem::path(incl_stem + ".hpp"); } incl = std::filesystem::canonical(incl); // avoid self include loop if(fname != incl) { std::cerr << fname << " includes " << incl << std::endl; g.nodes.at(fname).includes.push_back(incl); g.nodes.at(incl) .included.push_back(fname); } } } std::cerr << "-------------------------------------------------------------\n"; std::cerr << "graph has been constructed. flattening...\n"; std::cerr << "-------------------------------------------------------------\n"; // ------------------------------------------------------------------------- // flatten graph by topological sort // collect files that does not include std::vector sources; for(const auto& [fname, node] : g.nodes) { if(node.includes.empty()) { sources.push_back(fname); } } assert( ! sources.empty()); std::vector sorted; while( ! sources.empty()) { const auto file = sources.back(); sorted.push_back(sources.back()); sources.pop_back(); for(const auto& included : g.nodes.at(file).included) { auto found = std::find(g.nodes.at(included).includes.begin(), g.nodes.at(included).includes.end(), file); g.nodes.at(included).includes.erase(found); if(g.nodes.at(included).includes.empty()) { sources.push_back(included); } } } std::cerr << "-------------------------------------------------------------\n"; std::cerr << "flattened. outputting...\n"; std::cerr << "-------------------------------------------------------------\n"; // ------------------------------------------------------------------------- // output all the file in the sorted order for(const auto& fname : sorted) { std::cerr << "expanding: " << fname << std::endl; for(const auto& line : files.at(fname).content) { std::cout << line << '\n'; } } return 0; }