Compare commits

..

5 Commits

Author SHA1 Message Date
ToruNiina
bfe5e50acf test: add missing include files 2020-02-07 21:00:11 +09:00
ToruNiina
05077dee0c test: add test_visit 2020-02-07 15:58:15 +09:00
ToruNiina
b9f1726e26 feat: add multi-argument toml::visit(visitor, ...) 2020-02-07 15:57:34 +09:00
ToruNiina
c583e38ebf fix: include visit.hpp from serializer 2020-02-07 15:57:09 +09:00
ToruNiina
388e9db32b refactor: move visit() from value.hpp to visit.hpp 2020-01-31 22:48:37 +09:00
18 changed files with 793 additions and 812 deletions

View File

@@ -2,30 +2,6 @@ dist: trusty
matrix: matrix:
include: include:
- os: linux
language: cpp
compiler: gcc
env: COMPILER="g++-4.8" CXX_STANDARD=11
addons:
apt:
sources:
- sourceline: 'ppa:ubuntu-toolchain-r/test'
- sourceline: 'ppa:mhier/libboost-latest'
packages:
- g++-4.8
- boost1.70
- os: linux
language: cpp
compiler: gcc
env: COMPILER="g++-4.9" CXX_STANDARD=11
addons:
apt:
sources:
- sourceline: 'ppa:ubuntu-toolchain-r/test'
- sourceline: 'ppa:mhier/libboost-latest'
packages:
- g++-4.9
- boost1.70
- os: linux - os: linux
language: cpp language: cpp
compiler: gcc compiler: gcc
@@ -293,6 +269,16 @@ matrix:
- os: osx - os: osx
language: cpp language: cpp
compiler: clang compiler: clang
env: CXX_STANDARD=11
cache:
directories:
- $HOME/Library/Caches/Homebrew
addons:
homebrew:
update: true
packages:
- cmake
- boost
script: script:
- | - |
@@ -302,10 +288,6 @@ script:
tar xf cmake-3.14.5-Linux-x86_64.tar.gz -C cmake --strip-components=1 tar xf cmake-3.14.5-Linux-x86_64.tar.gz -C cmake --strip-components=1
export PATH=${TRAVIS_BUILD_DIR}/cmake/bin:${PATH} export PATH=${TRAVIS_BUILD_DIR}/cmake/bin:${PATH}
fi fi
- |
if [[ "${CXX_STANDARD}" == "" ]]; then
export CXX_STANDARD="11"
fi
- | - |
if [[ "${TOML_HEAD}" != "ON" ]]; then if [[ "${TOML_HEAD}" != "ON" ]]; then
export TOML_HEAD="OFF" export TOML_HEAD="OFF"
@@ -324,8 +306,14 @@ script:
- cmake --version - cmake --version
- mkdir build - mkdir build
- cd build - cd build
- echo "COMPILER = ${COMPILER}" - git clone https://github.com/toml-lang/toml.git
- echo "CXX_STANDARD = ${CXX_STANDARD}"
- cmake -DCMAKE_CXX_COMPILER=$COMPILER -DCMAKE_CXX_STANDARD=$CXX_STANDARD -DTOML11_USE_UNRELEASED_TOML_FEATURES=${TOML_HEAD} -Dtoml11_TEST_WITH_ASAN=${WITH_ASAN} -Dtoml11_TEST_WITH_UBSAN=${WITH_UBSAN} .. - cmake -DCMAKE_CXX_COMPILER=$COMPILER -DCMAKE_CXX_STANDARD=$CXX_STANDARD -DTOML11_USE_UNRELEASED_TOML_FEATURES=${TOML_HEAD} -Dtoml11_TEST_WITH_ASAN=${WITH_ASAN} -Dtoml11_TEST_WITH_UBSAN=${WITH_UBSAN} ..
- make - make
- ctest --output-on-failure - ctest --output-on-failure
# https://stackoverflow.com/a/53331571
before_cache:
- |
if [[ "${TRAVIS_OS_NAME}" == "osx" ]]; then
brew cleanup
fi

View File

@@ -1,7 +1,14 @@
cmake_minimum_required(VERSION 3.1) cmake_minimum_required(VERSION 2.8)
enable_testing() enable_testing()
project(toml11 VERSION 3.3.1) project(toml11)
set(toml11_VERSION_MAYOR 3)
set(toml11_VERSION_MINOR 3)
set(toml11_VERSION_PATCH 0)
set(toml11_VERSION
"${toml11_VERSION_MAYOR}.${toml11_VERSION_MINOR}.${toml11_VERSION_PATCH}"
)
option(toml11_BUILD_TEST "Build toml tests" ON) option(toml11_BUILD_TEST "Build toml tests" ON)
option(toml11_TEST_WITH_ASAN "use LLVM address sanitizer" OFF) option(toml11_TEST_WITH_ASAN "use LLVM address sanitizer" OFF)
@@ -9,9 +16,11 @@ option(toml11_TEST_WITH_UBSAN "use LLVM undefined behavior sanitizer" OFF)
include(CheckCXXCompilerFlag) include(CheckCXXCompilerFlag)
if("${CMAKE_VERSION}" VERSION_GREATER 3.1) if("${CMAKE_VERSION}" VERSION_GREATER 3.1)
set(CMAKE_CXX_STANDARD 11 CACHE STRING "The C++ standard whose features are requested to build all targets.") set(CMAKE_CXX_EXTENSIONS OFF)
set(CMAKE_CXX_STANDARD_REQUIRED ON CACHE BOOL "Boolean describing whether the value of CXX_STANDARD is a requirement.") if(NOT DEFINED CMAKE_CXX_STANDARD)
set(CMAKE_CXX_EXTENSIONS OFF CACHE BOOL "Boolean specifying whether compiler specific extensions are requested.") set(CMAKE_CXX_STANDARD 11)
endif()
set(CXX_STANDARD_REQUIRED ON)
else() else()
# Manually check for C++11 compiler flag. # Manually check for C++11 compiler flag.
CHECK_CXX_COMPILER_FLAG("-std=c++11" COMPILER_SUPPORTS_CXX11) CHECK_CXX_COMPILER_FLAG("-std=c++11" COMPILER_SUPPORTS_CXX11)

View File

@@ -8,17 +8,16 @@ toml11
[![License](https://img.shields.io/github/license/ToruNiina/toml11.svg?style=flat)](LICENSE) [![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) [![DOI](https://zenodo.org/badge/DOI/10.5281/zenodo.1209136.svg)](https://doi.org/10.5281/zenodo.1209136)
toml11 is a C++11 (or later) header-only toml parser/encoder depending only on C++ standard library. toml11 is a C++11 header-only toml parser/encoder depending only on C++ standard library.
- It is compatible to the latest version of [TOML v0.5.0](https://github.com/toml-lang/toml/blob/master/versions/en/toml-v0.5.0.md). compatible to the latest version of
- It optionally supports the [unreleased features](#unreleased-toml-features) in the master branch of toml-lang/toml. [TOML v0.5.0](https://github.com/toml-lang/toml/blob/master/versions/en/toml-v0.5.0.md)
- It is one of the most TOML standard compliant libraries, tested with [the language agnostic test suite for TOML parsers by BurntSushi](https://github.com/BurntSushi/toml-test). after version 2.0.0.
- It shows highly informative error messages. You can see the error messages about invalid files at [CircleCI](https://circleci.com/gh/ToruNiina/toml11).
- It has configurable container. You can use any random-access containers and key-value maps as backend containers. It passes [the language agnostic test suite for TOML parsers by BurntSushi](https://github.com/BurntSushi/toml-test).
- It optionally preserves comments without any overhead. Not only the test suite itself, a TOML reader/encoder also runs on [CircleCI](https://circleci.com/gh/ToruNiina/toml11).
- It has configurable serializer that supports comments, inline tables, literal strings and multiline strings. You can see the error messages about invalid files and serialization results of valid files at
- It supports user-defined type conversion from/into toml values. [CircleCI](https://circleci.com/gh/ToruNiina/toml11).
- It correctly handles UTF-8 sequences, with or without BOM, both on posix and Windows.
## Example ## Example
@@ -28,25 +27,15 @@ toml11 is a C++11 (or later) header-only toml parser/encoder depending only on C
int main() int main()
{ {
auto data = toml::parse("example.toml"); const auto data = toml::parse("example.toml");
// find a value with the specified type from a table // title = "an example toml file"
std::string title = toml::find<std::string>(data, "title"); std::string title = toml::find<std::string>(data, "title");
std::cout << "the title is " << title << std::endl;
// convert the whole array into any container automatically // nums = [1, 2, 3, 4, 5]
std::vector<int> nums = toml::find<std::vector<int>>(data, "nums"); std::vector<int> nums = toml::find<std::vector<int>>(data, "nums");
std::cout << "the length of `nums` is" << nums.size() << std::endl;
// access with STL-like manner
if(not data.at("a").contains("b"))
{
data["a"]["b"] = "c";
}
// pass a fallback
std::string name = toml::find_or<std::string>(data, "name", "not found");
// width-dependent formatting
std::cout << std::setw(80) << data << std::endl;
return 0; return 0;
} }
@@ -1424,7 +1413,7 @@ const toml::source_location loc = v.location();
## Exceptions ## Exceptions
The following `exception` classes inherits `toml::exception` that inherits All the exceptions thrown by toml11 inherits `toml::exception` that inherits
`std::exception`. `std::exception`.
```cpp ```cpp
@@ -1451,16 +1440,6 @@ struct exception : public std::exception
It represents where the error occurs. It represents where the error occurs.
`syntax_error` will be thrown from `toml::parse` and `_toml` literal.
`type_error` will be thrown from `toml::get/find`, `toml::value::as_xxx()`, and
other functions that takes a content inside of `toml::value`.
Note that, currently, from `toml::value::at()` and `toml::find(value, key)`
may throw an `std::out_of_range` that does not inherits `toml::exception`.
Also, in some cases, most likely in the file open error, it will throw an
`std::runtime_error`.
## Colorize Error Messages ## Colorize Error Messages
By defining `TOML11_COLORIZE_ERROR_MESSAGE`, the error messages from By defining `TOML11_COLORIZE_ERROR_MESSAGE`, the error messages from
@@ -1764,12 +1743,13 @@ Such a big change will not happen in the coming years.
## Running Tests ## Running Tests
After cloning this repository, run the following command (thank you @jwillikers To run test codes, you need to clone toml-lang/toml repository under `build/` directory
for automating test set fetching!). because some of the test codes read a file in the repository.
```sh ```sh
$ mkdir build $ mkdir build
$ cd build $ cd build
$ git clone https://github.com/toml-lang/toml.git
$ cmake .. $ cmake ..
$ make $ make
$ make test $ make test
@@ -1803,11 +1783,6 @@ I appreciate the help of the contributors who introduced the great feature to th
- Suppress warnings in Debug mode - Suppress warnings in Debug mode
- OGAWA Kenichi (@kenichiice) - OGAWA Kenichi (@kenichiice)
- Suppress warnings on intel compiler - Suppress warnings on intel compiler
- 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
## Licensing terms ## Licensing terms

View File

@@ -17,9 +17,10 @@ build_script:
- cd C:\toml11 - cd C:\toml11
- mkdir build - mkdir build
- cd build - cd build
- git clone https://github.com/toml-lang/toml.git
- file --mime-encoding toml/tests/hard_example_unicode.toml
- cmake -G"%generator%" -DBOOST_ROOT=C:/Libraries/boost_1_69_0 .. - cmake -G"%generator%" -DBOOST_ROOT=C:/Libraries/boost_1_69_0 ..
- cmake --build . --config "%configuration%" - cmake --build . --config "%configuration%"
- file --mime-encoding tests/toml/tests/hard_example_unicode.toml
test_script: test_script:
- ctest --build-config "%configuration%" --timeout 300 --output-on-failure - ctest --build-config "%configuration%" --timeout 300 --output-on-failure

View File

@@ -1,12 +1,3 @@
include(ExternalProject)
ExternalProject_Add(toml
SOURCE_DIR ${CMAKE_CURRENT_BINARY_DIR}/toml
GIT_REPOSITORY https://github.com/toml-lang/toml
GIT_TAG v0.5.0
CONFIGURE_COMMAND ""
BUILD_COMMAND ""
INSTALL_COMMAND "")
set(TEST_NAMES set(TEST_NAMES
test_datetime test_datetime
test_string test_string
@@ -43,6 +34,7 @@ set(TEST_NAMES
test_error_detection test_error_detection
test_format_error test_format_error
test_extended_conversions test_extended_conversions
test_visit
) )
CHECK_CXX_COMPILER_FLAG("-Wall" COMPILER_SUPPORTS_WALL) CHECK_CXX_COMPILER_FLAG("-Wall" COMPILER_SUPPORTS_WALL)
@@ -57,8 +49,6 @@ CHECK_CXX_COMPILER_FLAG("-Wduplicated-branches" COMPILER_SUPPORTS_WDUPLICATED_BR
CHECK_CXX_COMPILER_FLAG("-Wlogical-op" COMPILER_SUPPORTS_WLOGICAL_OP) CHECK_CXX_COMPILER_FLAG("-Wlogical-op" COMPILER_SUPPORTS_WLOGICAL_OP)
CHECK_CXX_COMPILER_FLAG("-Wuseless-cast" COMPILER_SUPPORTS_WUSELESS_CAST) CHECK_CXX_COMPILER_FLAG("-Wuseless-cast" COMPILER_SUPPORTS_WUSELESS_CAST)
CHECK_CXX_COMPILER_FLAG("-Wdouble-promotion" COMPILER_SUPPORTS_WDOUBLE_PROMOTION) CHECK_CXX_COMPILER_FLAG("-Wdouble-promotion" COMPILER_SUPPORTS_WDOUBLE_PROMOTION)
CHECK_CXX_COMPILER_FLAG("-Wrange-loop-analysis" COMPILER_SUPPORTS_WRANGE_LOOP_ANALYSIS)
CHECK_CXX_COMPILER_FLAG("-Wundef" COMPILER_SUPPORTS_WUNDEF)
if(COMPILER_SUPPORTS_WALL) if(COMPILER_SUPPORTS_WALL)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall") set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall")
@@ -93,12 +83,6 @@ endif()
if(COMPILER_SUPPORTS_WDOUBLE_PROMOTION) if(COMPILER_SUPPORTS_WDOUBLE_PROMOTION)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wdouble-promotion") set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wdouble-promotion")
endif() endif()
if(COMPILER_SUPPORTS_WRANGE_LOOP_ANALYSIS)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wrange-loop-analysis")
endif()
if(COMPILER_SUPPORTS_WUNDEF)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wundef")
endif()
option(TOML11_USE_UNRELEASED_TOML_FEATURES option(TOML11_USE_UNRELEASED_TOML_FEATURES
"use features in toml-lang/toml master while testing" OFF) "use features in toml-lang/toml master while testing" OFF)
@@ -167,7 +151,7 @@ foreach(TEST_NAME ${TEST_NAMES})
endif() endif()
endif() endif()
add_test(NAME ${TEST_NAME} COMMAND ${TEST_NAME} WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}) add_test(NAME ${TEST_NAME} COMMAND ${TEST_NAME} WORKING_DIRECTORY ${PROJECT_BINARY_DIR})
# Set the PATH to be able to find Boost DLL # Set the PATH to be able to find Boost DLL
if(WIN32) if(WIN32)

View File

@@ -84,7 +84,9 @@ struct json_serializer
{ {
if(!is_first) {std::cout << ", ";} if(!is_first) {std::cout << ", ";}
is_first = false; is_first = false;
std::cout << this->format_key(elem.first) << ':'; std::cout << toml::format(toml::value(elem.first),
std::numeric_limits<std::size_t>::max());
std::cout << ':';
toml::visit(*this, elem.second); toml::visit(*this, elem.second);
} }
std::cout << '}'; std::cout << '}';
@@ -110,12 +112,6 @@ struct json_serializer
} }
return retval; return retval;
} }
std::string format_key(const std::string& s) const
{
const auto quote("\"");
return quote + escape_string(s) + quote;
}
}; };
int main() int main()

View File

@@ -57,22 +57,6 @@ BOOST_AUTO_TEST_CASE(test_ml_basic_string)
TOML11_TEST_LEX_ACCEPT(lex_string, TOML11_TEST_LEX_ACCEPT(lex_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 \"\"\"",
"\"\"\"\\\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 \"\"\"");
TOML11_TEST_LEX_ACCEPT(lex_string,
"\"\"\"Here are two quotation marks: \"\". Simple enough.\"\"\"",
"\"\"\"Here are two quotation marks: \"\". Simple enough.\"\"\"");
TOML11_TEST_LEX_ACCEPT(lex_string,
"\"\"\"Here are three quotation marks: \"\"\\\".\"\"\"",
"\"\"\"Here are three quotation marks: \"\"\\\".\"\"\"");
TOML11_TEST_LEX_ACCEPT(lex_string,
"\"\"\"Here are fifteen quotation marks: \"\"\\\"\"\"\\\"\"\"\\\"\"\"\\\"\"\"\\\".\"\"\"",
"\"\"\"Here are fifteen quotation marks: \"\"\\\"\"\"\\\"\"\"\\\"\"\"\\\"\"\"\\\".\"\"\"");
TOML11_TEST_LEX_ACCEPT(lex_string,
"\"\"\"\"This,\" she said, \"is just a pointless statement.\"\"\"\"",
"\"\"\"\"This,\" she said, \"is just a pointless statement.\"\"\"\"");
} }
BOOST_AUTO_TEST_CASE(test_literal_string) BOOST_AUTO_TEST_CASE(test_literal_string)
@@ -99,16 +83,4 @@ BOOST_AUTO_TEST_CASE(test_ml_literal_string)
TOML11_TEST_LEX_ACCEPT(lex_string, TOML11_TEST_LEX_ACCEPT(lex_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'''",
"'''\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'''");
TOML11_TEST_LEX_ACCEPT(lex_string,
"''''That's still pointless', she said.'''",
"''''That's still pointless', she said.'''");
TOML11_TEST_LEX_ACCEPT(lex_string,
"'''Here are fifteen quotation marks: \"\"\"\"\"\"\"\"\"\"\"\"\"\"\".'''",
"'''Here are fifteen quotation marks: \"\"\"\"\"\"\"\"\"\"\"\"\"\"\".'''");
TOML11_TEST_LEX_ACCEPT(lex_string,
"''''This,' she said, 'is just a pointless statement.''''",
"''''This,' she said, 'is just a pointless statement.''''");
} }

View File

@@ -29,16 +29,16 @@ BOOST_AUTO_TEST_CASE(test_string)
BOOST_AUTO_TEST_CASE(test_string_value) BOOST_AUTO_TEST_CASE(test_string_value)
{ {
TOML11_TEST_PARSE_EQUAL_VALUE(parse_value<toml::value>, TOML11_TEST_PARSE_EQUAL_VALUE(parse_value<toml::value>,
"\"The quick brown fox jumps over the lazy dog\"", "\"The quick brown fox jumps over the lazy dog\"",
toml::value("The quick brown fox jumps over the lazy dog", string_t::basic)); toml::value("The quick brown fox jumps over the lazy dog", string_t::basic));
TOML11_TEST_PARSE_EQUAL_VALUE(parse_value<toml::value>, TOML11_TEST_PARSE_EQUAL_VALUE(parse_value<toml::value>,
"\'The quick brown fox jumps over the lazy dog\'", "\'The quick brown fox jumps over the lazy dog\'",
toml::value("The quick brown fox jumps over the lazy dog", string_t::literal)); toml::value("The quick brown fox jumps over the lazy dog", string_t::literal));
TOML11_TEST_PARSE_EQUAL_VALUE(parse_value<toml::value>, TOML11_TEST_PARSE_EQUAL_VALUE(parse_value<toml::value>,
"\"\"\"The quick brown fox \\\njumps over the lazy dog\"\"\"", "\"\"\"The quick brown fox \\\njumps over the lazy dog\"\"\"",
toml::value("The quick brown fox jumps over the lazy dog", string_t::basic)); toml::value("The quick brown fox jumps over the lazy dog", string_t::basic));
TOML11_TEST_PARSE_EQUAL_VALUE(parse_value<toml::value>, TOML11_TEST_PARSE_EQUAL_VALUE(parse_value<toml::value>,
"'''The quick brown fox \njumps over the lazy dog'''", "'''The quick brown fox \njumps over the lazy dog'''",
toml::value("The quick brown fox \njumps over the lazy dog", string_t::literal)); toml::value("The quick brown fox \njumps over the lazy dog", string_t::literal));
} }
@@ -69,17 +69,14 @@ BOOST_AUTO_TEST_CASE(test_basic_string)
TOML11_TEST_PARSE_EQUAL(parse_string, TOML11_TEST_PARSE_EQUAL(parse_string,
"\" And when \\\"'s are in the along with # \\\"\"", "\" And when \\\"'s are in the along with # \\\"\"",
string(" And when \"'s are in the along with # \"", string_t::basic)); string(" And when \"'s are in the along with # \"", string_t::basic));
TOML11_TEST_PARSE_EQUAL(parse_string,
"\"Here are fifteen apostrophes: '''''''''''''''\"",
string("Here are fifteen apostrophes: '''''''''''''''", string_t::basic));
} }
BOOST_AUTO_TEST_CASE(test_basic_string_value) BOOST_AUTO_TEST_CASE(test_basic_string_value)
{ {
TOML11_TEST_PARSE_EQUAL_VALUE(parse_value<toml::value>, TOML11_TEST_PARSE_EQUAL_VALUE(parse_value<toml::value>,
"\"GitHub Cofounder & CEO\\nLikes tater tots and beer.\"", "\"GitHub Cofounder & CEO\\nLikes tater tots and beer.\"",
value("GitHub Cofounder & CEO\nLikes tater tots and beer.", string_t::basic)); value("GitHub Cofounder & CEO\nLikes tater tots and beer.", string_t::basic));
TOML11_TEST_PARSE_EQUAL_VALUE(parse_value<toml::value>, TOML11_TEST_PARSE_EQUAL_VALUE(parse_value<toml::value>,
"\"192.168.1.1\"", "\"192.168.1.1\"",
value("192.168.1.1", string_t::basic)); value("192.168.1.1", string_t::basic));
#if defined(_MSC_VER) || defined(__INTEL_COMPILER) #if defined(_MSC_VER) || defined(__INTEL_COMPILER)
@@ -87,19 +84,16 @@ BOOST_AUTO_TEST_CASE(test_basic_string_value)
"\"\xE4\xB8\xAD\xE5\x9B\xBD\"", "\"\xE4\xB8\xAD\xE5\x9B\xBD\"",
value("\xE4\xB8\xAD\xE5\x9B\xBD", string_t::basic)); value("\xE4\xB8\xAD\xE5\x9B\xBD", string_t::basic));
#else #else
TOML11_TEST_PARSE_EQUAL_VALUE(parse_value<toml::value>, TOML11_TEST_PARSE_EQUAL_VALUE(parse_value<toml::value>,
"\"中国\"", "\"中国\"",
value("中国", string_t::basic)); value("中国", string_t::basic));
#endif #endif
TOML11_TEST_PARSE_EQUAL_VALUE(parse_value<toml::value>, TOML11_TEST_PARSE_EQUAL_VALUE(parse_value<toml::value>,
"\"You'll hate me after this - #\"", "\"You'll hate me after this - #\"",
value("You'll hate me after this - #", string_t::basic)); value("You'll hate me after this - #", string_t::basic));
TOML11_TEST_PARSE_EQUAL_VALUE(parse_value<toml::value>, TOML11_TEST_PARSE_EQUAL_VALUE(parse_value<toml::value>,
"\" And when \\\"'s are in the along with # \\\"\"", "\" And when \\\"'s are in the along with # \\\"\"",
value(" And when \"'s are in the along with # \"", string_t::basic)); value(" And when \"'s are in the along with # \"", string_t::basic));
TOML11_TEST_PARSE_EQUAL_VALUE(parse_value<toml::value>,
"\"Here are fifteen apostrophes: '''''''''''''''\"",
value("Here are fifteen apostrophes: '''''''''''''''", string_t::basic));
} }
BOOST_AUTO_TEST_CASE(test_ml_basic_string) BOOST_AUTO_TEST_CASE(test_ml_basic_string)
@@ -110,41 +104,16 @@ BOOST_AUTO_TEST_CASE(test_ml_basic_string)
TOML11_TEST_PARSE_EQUAL(parse_string, TOML11_TEST_PARSE_EQUAL(parse_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 \"\"\"",
string("The quick brown fox jumps over the lazy dog.", string_t::basic)); string("The quick brown fox jumps over the lazy dog.", string_t::basic));
TOML11_TEST_PARSE_EQUAL(parse_string,
"\"\"\"Here are two quotation marks: \"\". Simple enough.\"\"\"",
string("Here are two quotation marks: \"\". Simple enough.", string_t::basic));
TOML11_TEST_PARSE_EQUAL(parse_string,
"\"\"\"Here are three quotation marks: \"\"\\\".\"\"\"",
string("Here are three quotation marks: \"\"\".", string_t::basic));
TOML11_TEST_PARSE_EQUAL(parse_string,
"\"\"\"Here are fifteen quotation marks: \"\"\\\"\"\"\\\"\"\"\\\"\"\"\\\"\"\"\\\".\"\"\"",
string("Here are fifteen quotation marks: \"\"\"\"\"\"\"\"\"\"\"\"\"\"\".", string_t::basic));
TOML11_TEST_PARSE_EQUAL(parse_string,
"\"\"\"\"This,\" she said, \"is just a pointless statement.\"\"\"\"",
string("\"This,\" she said, \"is just a pointless statement.\"", string_t::basic));
} }
BOOST_AUTO_TEST_CASE(test_ml_basic_string_value) BOOST_AUTO_TEST_CASE(test_ml_basic_string_value)
{ {
TOML11_TEST_PARSE_EQUAL_VALUE(parse_value<toml::value>, TOML11_TEST_PARSE_EQUAL_VALUE(parse_value<toml::value>,
"\"\"\"\nThe quick brown \\\n\n fox jumps over \\\n the lazy dog.\"\"\"", "\"\"\"\nThe quick brown \\\n\n fox jumps over \\\n the lazy dog.\"\"\"",
value("The quick brown fox jumps over the lazy dog.", string_t::basic)); value("The quick brown fox jumps over the lazy dog.", string_t::basic));
TOML11_TEST_PARSE_EQUAL_VALUE(parse_value<toml::value>, TOML11_TEST_PARSE_EQUAL_VALUE(parse_value<toml::value>,
"\"\"\"\\\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 \"\"\"",
value("The quick brown fox jumps over the lazy dog.", string_t::basic)); value("The quick brown fox jumps over the lazy dog.", string_t::basic));
TOML11_TEST_PARSE_EQUAL_VALUE(parse_value<toml::value>,
"\"\"\"Here are two quotation marks: \"\". Simple enough.\"\"\"",
value("Here are two quotation marks: \"\". Simple enough.", string_t::basic));
TOML11_TEST_PARSE_EQUAL_VALUE(parse_value<toml::value>,
"\"\"\"Here are three quotation marks: \"\"\\\".\"\"\"",
value("Here are three quotation marks: \"\"\".", string_t::basic));
TOML11_TEST_PARSE_EQUAL_VALUE(parse_value<toml::value>,
"\"\"\"Here are fifteen quotation marks: \"\"\\\"\"\"\\\"\"\"\\\"\"\"\\\"\"\"\\\".\"\"\"",
value("Here are fifteen quotation marks: \"\"\"\"\"\"\"\"\"\"\"\"\"\"\".", string_t::basic));
TOML11_TEST_PARSE_EQUAL_VALUE(parse_value<toml::value>,
"\"\"\"\"This,\" she said, \"is just a pointless statement.\"\"\"\"",
value("\"This,\" she said, \"is just a pointless statement.\"", string_t::basic));
} }
BOOST_AUTO_TEST_CASE(test_literal_string) BOOST_AUTO_TEST_CASE(test_literal_string)
@@ -165,16 +134,16 @@ BOOST_AUTO_TEST_CASE(test_literal_string)
BOOST_AUTO_TEST_CASE(test_literal_string_value) BOOST_AUTO_TEST_CASE(test_literal_string_value)
{ {
TOML11_TEST_PARSE_EQUAL_VALUE(parse_value<toml::value>, TOML11_TEST_PARSE_EQUAL_VALUE(parse_value<toml::value>,
"'C:\\Users\\nodejs\\templates'", "'C:\\Users\\nodejs\\templates'",
value("C:\\Users\\nodejs\\templates", string_t::literal)); value("C:\\Users\\nodejs\\templates", string_t::literal));
TOML11_TEST_PARSE_EQUAL_VALUE(parse_value<toml::value>, TOML11_TEST_PARSE_EQUAL_VALUE(parse_value<toml::value>,
"'\\\\ServerX\\admin$\\system32\\'", "'\\\\ServerX\\admin$\\system32\\'",
value("\\\\ServerX\\admin$\\system32\\", string_t::literal)); value("\\\\ServerX\\admin$\\system32\\", string_t::literal));
TOML11_TEST_PARSE_EQUAL_VALUE(parse_value<toml::value>, TOML11_TEST_PARSE_EQUAL_VALUE(parse_value<toml::value>,
"'Tom \"Dubs\" Preston-Werner'", "'Tom \"Dubs\" Preston-Werner'",
value("Tom \"Dubs\" Preston-Werner", string_t::literal)); value("Tom \"Dubs\" Preston-Werner", string_t::literal));
TOML11_TEST_PARSE_EQUAL_VALUE(parse_value<toml::value>, TOML11_TEST_PARSE_EQUAL_VALUE(parse_value<toml::value>,
"'<\\i\\c*\\s*>'", "'<\\i\\c*\\s*>'",
value("<\\i\\c*\\s*>", string_t::literal)); value("<\\i\\c*\\s*>", string_t::literal));
} }
@@ -187,34 +156,16 @@ BOOST_AUTO_TEST_CASE(test_ml_literal_string)
TOML11_TEST_PARSE_EQUAL(parse_string, TOML11_TEST_PARSE_EQUAL(parse_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'''",
string("The first newline is\ntrimmed in raw strings.\n All other whitespace\n is preserved.\n", string_t::literal)); string("The first newline is\ntrimmed in raw strings.\n All other whitespace\n is preserved.\n", string_t::literal));
TOML11_TEST_PARSE_EQUAL(parse_string,
"''''That's still pointless', she said.'''",
string("'That's still pointless', she said.", string_t::literal));
TOML11_TEST_PARSE_EQUAL(parse_string,
"'''Here are fifteen quotation marks: \"\"\"\"\"\"\"\"\"\"\"\"\"\"\".'''",
string("Here are fifteen quotation marks: \"\"\"\"\"\"\"\"\"\"\"\"\"\"\".", string_t::literal));
TOML11_TEST_PARSE_EQUAL(parse_string,
"''''This,' she said, 'is just a pointless statement.''''",
string("'This,' she said, 'is just a pointless statement.'", string_t::literal));
} }
BOOST_AUTO_TEST_CASE(test_ml_literal_string_value) BOOST_AUTO_TEST_CASE(test_ml_literal_string_value)
{ {
TOML11_TEST_PARSE_EQUAL_VALUE(parse_value<toml::value>, TOML11_TEST_PARSE_EQUAL_VALUE(parse_value<toml::value>,
"'''I [dw]on't need \\d{2} apples'''", "'''I [dw]on't need \\d{2} apples'''",
value("I [dw]on't need \\d{2} apples", string_t::literal)); value("I [dw]on't need \\d{2} apples", string_t::literal));
TOML11_TEST_PARSE_EQUAL_VALUE(parse_value<toml::value>, TOML11_TEST_PARSE_EQUAL_VALUE(parse_value<toml::value>,
"'''\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'''",
value("The first newline is\ntrimmed in raw strings.\n All other whitespace\n is preserved.\n", string_t::literal)); value("The first newline is\ntrimmed in raw strings.\n All other whitespace\n is preserved.\n", string_t::literal));
TOML11_TEST_PARSE_EQUAL_VALUE(parse_value<toml::value>,
"''''That's still pointless', she said.'''",
value("'That's still pointless', she said.", string_t::literal));
TOML11_TEST_PARSE_EQUAL_VALUE(parse_value<toml::value>,
"'''Here are fifteen quotation marks: \"\"\"\"\"\"\"\"\"\"\"\"\"\"\".'''",
value("Here are fifteen quotation marks: \"\"\"\"\"\"\"\"\"\"\"\"\"\"\".", string_t::literal));
TOML11_TEST_PARSE_EQUAL_VALUE(parse_value<toml::value>,
"''''This,' she said, 'is just a pointless statement.''''",
value("'This,' she said, 'is just a pointless statement.'", string_t::literal));
} }
BOOST_AUTO_TEST_CASE(test_unicode_escape_sequence) BOOST_AUTO_TEST_CASE(test_unicode_escape_sequence)

172
tests/test_visit.cpp Normal file
View File

@@ -0,0 +1,172 @@
#define BOOST_TEST_MODULE "test_visit"
#ifdef UNITTEST_FRAMEWORK_LIBRARY_EXIST
#include <boost/test/unit_test.hpp>
#else
#define BOOST_TEST_NO_LIB
#include <boost/test/included/unit_test.hpp>
#endif
#include <toml.hpp>
#include <map>
#include <deque>
using test_value_types = std::tuple<
toml::value,
toml::basic_value<toml::preserve_comments>,
toml::basic_value<toml::discard_comments, std::map, std::deque>,
toml::basic_value<toml::preserve_comments, std::map, std::deque>
>;
template<typename Value>
struct visitor1
{
std::string operator()(const toml::boolean&) const {return "boolean";}
std::string operator()(const toml::integer&) const {return "integer";}
std::string operator()(const toml::floating&) const {return "floating";}
std::string operator()(const toml::string&) const {return "string";}
std::string operator()(const toml::local_time&) const {return "local_time";}
std::string operator()(const toml::local_date&) const {return "local_date";}
std::string operator()(const toml::local_datetime&) const {return "local_datetime";}
std::string operator()(const toml::offset_datetime&) const {return "offset_datetime";}
std::string operator()(const typename Value::array_type&) const {return "array";}
std::string operator()(const typename Value::table_type&) const {return "table";}
};
BOOST_AUTO_TEST_CASE_TEMPLATE(test_visit_one, value_type, test_value_types)
{
{
const value_type v(true);
BOOST_TEST(toml::visit(visitor1<value_type>{}, v) == "boolean");
}
{
const value_type v(42);
BOOST_TEST(toml::visit(visitor1<value_type>{}, v) == "integer");
}
{
const value_type v(3.14);
BOOST_TEST(toml::visit(visitor1<value_type>{}, v) == "floating");
}
{
const value_type v("foo");
BOOST_TEST(toml::visit(visitor1<value_type>{}, v) == "string");
}
{
const value_type v(toml::local_date(2018, toml::month_t::Apr, 22));
BOOST_TEST(toml::visit(visitor1<value_type>{}, v) == "local_date");
}
{
const value_type v(toml::local_time(12, 34, 56));
BOOST_TEST(toml::visit(visitor1<value_type>{}, v) == "local_time");
}
{
const value_type v(toml::local_datetime(
toml::local_date(2018, toml::month_t::Apr, 22),
toml::local_time(12, 34, 56)));
BOOST_TEST(toml::visit(visitor1<value_type>{}, v) == "local_datetime");
}
{
const value_type v(toml::offset_datetime(
toml::local_date(2018, toml::month_t::Apr, 22),
toml::local_time(12, 34, 56),
toml::time_offset(9, 0)));
BOOST_TEST(toml::visit(visitor1<value_type>{}, v) == "offset_datetime");
}
{
const value_type v{1,2,3,4,5};
BOOST_TEST(toml::visit(visitor1<value_type>{}, v) == "array");
}
{
const value_type v{
{"foo", 42}, {"bar", "baz"}
};
BOOST_TEST(toml::visit(visitor1<value_type>{}, v) == "table");
}
}
template<typename Value>
struct visitor2
{
template<typename T1, typename T2>
std::string operator()(const T1& v1, const T2& v2) const
{
visitor1<Value> vis;
return vis(v1) + "+" + vis(v2);
}
};
BOOST_AUTO_TEST_CASE_TEMPLATE(test_visit_two, value_type, test_value_types)
{
std::vector<value_type> vs;
vs.push_back(value_type(true));
vs.push_back(value_type(42));
vs.push_back(value_type(3.14));
vs.push_back(value_type("foo"));
vs.push_back(value_type(toml::local_date(2018, toml::month_t::Apr, 22)));
vs.push_back(value_type(toml::local_time(12, 34, 56)));
vs.push_back(value_type(toml::local_datetime(
toml::local_date(2018, toml::month_t::Apr, 22),
toml::local_time(12, 34, 56))));
vs.push_back(value_type(toml::offset_datetime(
toml::local_date(2018, toml::month_t::Apr, 22),
toml::local_time(12, 34, 56),
toml::time_offset(9, 0))));
vs.push_back(value_type{1,2,3,4,5});
vs.push_back(value_type{{"foo", 42}, {"bar", "baz"}});
for(const auto& v1 : vs)
{
const auto t1 = toml::visit(visitor1<value_type>{}, v1);
for(const auto& v2 : vs)
{
const auto t2 = toml::visit(visitor1<value_type>{}, v2);
BOOST_TEST(toml::visit(visitor2<value_type>{}, v1, v2) ==
t1 + "+" + t2);
}
}
}
template<typename Value>
struct visitor3
{
template<typename T1, typename T2, typename T3>
std::string operator()(const T1& v1, const T2& v2, const T3& v3) const
{
visitor1<Value> vis;
return vis(v1) + "+" + vis(v2) + "+" + vis(v3);
}
};
BOOST_AUTO_TEST_CASE_TEMPLATE(test_visit_three, value_type, test_value_types)
{
std::vector<value_type> vs;
vs.push_back(value_type(true));
vs.push_back(value_type(42));
vs.push_back(value_type(3.14));
vs.push_back(value_type("foo"));
vs.push_back(value_type(toml::local_date(2018, toml::month_t::Apr, 22)));
vs.push_back(value_type(toml::local_time(12, 34, 56)));
vs.push_back(value_type(toml::local_datetime(
toml::local_date(2018, toml::month_t::Apr, 22),
toml::local_time(12, 34, 56))));
vs.push_back(value_type(toml::offset_datetime(
toml::local_date(2018, toml::month_t::Apr, 22),
toml::local_time(12, 34, 56),
toml::time_offset(9, 0))));
vs.push_back(value_type{1,2,3,4,5});
vs.push_back(value_type{{"foo", 42}, {"bar", "baz"}});
for(const auto& v1 : vs)
{
const auto t1 = toml::visit(visitor1<value_type>{}, v1);
for(const auto& v2 : vs)
{
const auto t2 = toml::visit(visitor1<value_type>{}, v2);
for(const auto& v3 : vs)
{
const auto t3 = toml::visit(visitor1<value_type>{}, v3);
BOOST_TEST(toml::visit(visitor3<value_type>{}, v1, v2, v3) ==
t1 + "+" + t2 + "+" + t3);
}
}
}
}

View File

@@ -81,54 +81,6 @@ struct preserve_comments
void assign(std::initializer_list<std::string> ini) {comments.assign(ini);} void assign(std::initializer_list<std::string> ini) {comments.assign(ini);}
void assign(size_type n, const std::string& val) {comments.assign(n, val);} void assign(size_type n, const std::string& val) {comments.assign(n, val);}
// Related to the issue #97.
//
// It is known that `std::vector::insert` and `std::vector::erase` in
// the standard library implementation included in GCC 4.8.5 takes
// `std::vector::iterator` instead of `std::vector::const_iterator`.
// Because of the const-correctness, we cannot convert a `const_iterator` to
// an `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<typename InputIterator>
void insert(iterator p, InputIterator first, InputIterator last)
{
return comments.insert(p, first, last);
}
void insert(iterator p, std::initializer_list<std::string> ini)
{
return comments.insert(p, ini);
}
template<typename ... Ts>
iterator emplace(iterator p, Ts&& ... args)
{
return comments.emplace(p, std::forward<Ts>(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) iterator insert(const_iterator p, const std::string& x)
{ {
return comments.insert(p, x); return comments.insert(p, x);
@@ -162,7 +114,6 @@ struct preserve_comments
{ {
return comments.erase(first, last); return comments.erase(first, last);
} }
#endif
void swap(preserve_comments& other) {comments.swap(other.comments);} void swap(preserve_comments& other) {comments.swap(other.comments);}

View File

@@ -20,7 +20,7 @@ namespace toml
namespace detail namespace detail
{ {
// TODO: find more sophisticated way to handle this // TODO: find more sophisticated way to handle this
#if (defined(_POSIX_C_SOURCE) && _POSIX_C_SOURCE >= 1) || defined(_XOPEN_SOURCE) || defined(_BSD_SOURCE) || defined(_SVID_SOURCE) || defined(_POSIX_SOURCE) #if _POSIX_C_SOURCE >= 1 || defined(_XOPEN_SOURCE) || defined(_BSD_SOURCE) || defined(_SVID_SOURCE) || defined(_POSIX_SOURCE)
inline std::tm localtime_s(const std::time_t* src) inline std::tm localtime_s(const std::time_t* src)
{ {
std::tm dst; std::tm dst;
@@ -35,7 +35,7 @@ inline std::tm gmtime_s(const std::time_t* src)
if (!result) { throw std::runtime_error("gmtime_r failed."); } if (!result) { throw std::runtime_error("gmtime_r failed."); }
return dst; return dst;
} }
#elif defined(_MSC_VER) #elif _MSC_VER
inline std::tm localtime_s(const std::time_t* src) inline std::tm localtime_s(const std::time_t* src)
{ {
std::tm dst; std::tm dst;

View File

@@ -449,7 +449,10 @@ basic_value<C, M, V> const& find(const basic_value<C, M, V>& v, const key& ky)
const auto& tab = v.as_table(); const auto& tab = v.as_table();
if(tab.count(ky) == 0) if(tab.count(ky) == 0)
{ {
detail::throw_key_not_found_error(v, ky); throw std::out_of_range(detail::format_underline(concat_to_string(
"key \"", ky, "\" not found"), {
{std::addressof(detail::get_region(v)), "in this table"}
}));
} }
return tab.at(ky); return tab.at(ky);
} }
@@ -460,7 +463,10 @@ basic_value<C, M, V>& find(basic_value<C, M, V>& v, const key& ky)
auto& tab = v.as_table(); auto& tab = v.as_table();
if(tab.count(ky) == 0) if(tab.count(ky) == 0)
{ {
detail::throw_key_not_found_error(v, ky); throw std::out_of_range(detail::format_underline(concat_to_string(
"key \"", ky, "\" not found"), {
{std::addressof(detail::get_region(v)), "in this table"}
}));
} }
return tab.at(ky); return tab.at(ky);
} }
@@ -471,7 +477,10 @@ basic_value<C, M, V> find(basic_value<C, M, V>&& v, const key& ky)
typename basic_value<C, M, V>::table_type tab = std::move(v).as_table(); typename basic_value<C, M, V>::table_type tab = std::move(v).as_table();
if(tab.count(ky) == 0) if(tab.count(ky) == 0)
{ {
detail::throw_key_not_found_error(v, ky); throw std::out_of_range(detail::format_underline(concat_to_string(
"key \"", ky, "\" not found"), {
{std::addressof(detail::get_region(v)), "in this table"}
}));
} }
return basic_value<C, M, V>(std::move(tab.at(ky))); return basic_value<C, M, V>(std::move(tab.at(ky)));
} }
@@ -533,7 +542,10 @@ find(const basic_value<C, M, V>& v, const key& ky)
const auto& tab = v.as_table(); const auto& tab = v.as_table();
if(tab.count(ky) == 0) if(tab.count(ky) == 0)
{ {
detail::throw_key_not_found_error(v, ky); throw std::out_of_range(detail::format_underline(concat_to_string(
"key \"", ky, "\" not found"), {
{std::addressof(detail::get_region(v)), "in this table"}
}));
} }
return ::toml::get<T>(tab.at(ky)); return ::toml::get<T>(tab.at(ky));
} }
@@ -546,7 +558,10 @@ find(basic_value<C, M, V>& v, const key& ky)
auto& tab = v.as_table(); auto& tab = v.as_table();
if(tab.count(ky) == 0) if(tab.count(ky) == 0)
{ {
detail::throw_key_not_found_error(v, ky); throw std::out_of_range(detail::format_underline(concat_to_string(
"key \"", ky, "\" not found"), {
{std::addressof(detail::get_region(v)), "in this table"}
}));
} }
return ::toml::get<T>(tab.at(ky)); return ::toml::get<T>(tab.at(ky));
} }
@@ -559,7 +574,10 @@ find(basic_value<C, M, V>&& v, const key& ky)
typename basic_value<C, M, V>::table_type tab = std::move(v).as_table(); typename basic_value<C, M, V>::table_type tab = std::move(v).as_table();
if(tab.count(ky) == 0) if(tab.count(ky) == 0)
{ {
detail::throw_key_not_found_error(v, ky); throw std::out_of_range(detail::format_underline(concat_to_string(
"key \"", ky, "\" not found"), {
{std::addressof(detail::get_region(v)), "in this table"}
}));
} }
return ::toml::get<T>(std::move(tab.at(ky))); return ::toml::get<T>(std::move(tab.at(ky)));
} }

View File

@@ -154,53 +154,12 @@ using lex_basic_string = sequence<lex_quotation_mark,
repeat<lex_basic_char, unlimited>, repeat<lex_basic_char, unlimited>,
lex_quotation_mark>; lex_quotation_mark>;
// After toml post-v0.5.0, it is explicitly clarified how quotes in ml-strings
// are allowed to be used.
// After this, the following strings are *explicitly* allowed.
// - One or two `"`s in a multi-line basic string is allowed wherever it is.
// - Three consecutive `"`s in a multi-line basic string is considered as a delimiter.
// - One or two `"`s can appear just before or after the delimiter.
// ```toml
// str4 = """Here are two quotation marks: "". Simple enough."""
// str5 = """Here are three quotation marks: ""\"."""
// str6 = """Here are fifteen quotation marks: ""\"""\"""\"""\"""\"."""
// str7 = """"This," she said, "is just a pointless statement.""""
// ```
// In the current implementation (v3.3.0), it is difficult to parse `str7` in
// the above example. It is difficult to recognize `"` at the end of string body
// collectly. It will be misunderstood as a `"""` delimiter and an additional,
// invalid `"`. Like this:
// ```console
// what(): [error] toml::parse_table: invalid line format
// --> hoge.toml
// |
// 13 | str7 = """"This," she said, "is just a pointless statement.""""
// | ^- expected newline, but got '"'.
// ```
// As a quick workaround for this problem, `lex_ml_basic_string_delim` was
// splitted into two, `lex_ml_basic_string_open` and `lex_ml_basic_string_close`.
// `lex_ml_basic_string_open` allows only `"""`. `_close` allows 3-5 `"`s.
// In parse_ml_basic_string() function, the trailing `"`s will be attached to
// the string body.
//
// Note: This feature is a "clarification". Therefore this change is considered
// as a spec that has been defined since the time when the multi-line
// basic string was introduced. Although it is a post-v0.5.0 changes,
// this change will be activated regardless of the flag,
// `TOML11_USE_UNRELEASED_TOML_FEATURES`.
//
using lex_ml_basic_string_delim = repeat<lex_quotation_mark, exactly<3>>; using lex_ml_basic_string_delim = repeat<lex_quotation_mark, exactly<3>>;
using lex_ml_basic_string_open = lex_ml_basic_string_delim;
using lex_ml_basic_string_close = sequence<
repeat<lex_quotation_mark, exactly<3>>,
maybe<lex_quotation_mark>, maybe<lex_quotation_mark>
>;
#ifdef TOML11_USE_UNRELEASED_TOML_FEATURES #ifdef TOML11_USE_UNRELEASED_TOML_FEATURES
using lex_ml_basic_unescaped = exclude<either<in_range<0x00, 0x08>, // 0x09 using lex_ml_basic_unescaped = exclude<either<in_range<0x00, 0x08>, // 0x09
in_range<0x0a, 0x1F>, // is tab in_range<0x0a, 0x1F>, // is tab
character<0x5C>, // backslash character<0x5C>,
character<0x7F>, // DEL character<0x7F>,
lex_ml_basic_string_delim>>; lex_ml_basic_string_delim>>;
#else // TOML v0.5.0 #else // TOML v0.5.0
using lex_ml_basic_unescaped = exclude<either<in_range<0x00,0x1F>, using lex_ml_basic_unescaped = exclude<either<in_range<0x00,0x1F>,
@@ -217,9 +176,9 @@ using lex_ml_basic_char = either<lex_ml_basic_unescaped, lex_escaped>;
using lex_ml_basic_body = repeat<either<lex_ml_basic_char, lex_newline, using lex_ml_basic_body = repeat<either<lex_ml_basic_char, lex_newline,
lex_ml_basic_escaped_newline>, lex_ml_basic_escaped_newline>,
unlimited>; unlimited>;
using lex_ml_basic_string = sequence<lex_ml_basic_string_open, using lex_ml_basic_string = sequence<lex_ml_basic_string_delim,
lex_ml_basic_body, lex_ml_basic_body,
lex_ml_basic_string_close>; lex_ml_basic_string_delim>;
using lex_literal_char = exclude<either<in_range<0x00, 0x08>, using lex_literal_char = exclude<either<in_range<0x00, 0x08>,
in_range<0x10, 0x19>, character<0x27>>>; in_range<0x10, 0x19>, character<0x27>>>;
@@ -228,13 +187,7 @@ using lex_literal_string = sequence<lex_apostrophe,
repeat<lex_literal_char, unlimited>, repeat<lex_literal_char, unlimited>,
lex_apostrophe>; lex_apostrophe>;
// the same reason as above.
using lex_ml_literal_string_delim = repeat<lex_apostrophe, exactly<3>>; using lex_ml_literal_string_delim = repeat<lex_apostrophe, exactly<3>>;
using lex_ml_literal_string_open = lex_ml_literal_string_delim;
using lex_ml_literal_string_close = sequence<
repeat<lex_apostrophe, exactly<3>>,
maybe<lex_apostrophe>, maybe<lex_apostrophe>
>;
using lex_ml_literal_char = exclude<either<in_range<0x00, 0x08>, using lex_ml_literal_char = exclude<either<in_range<0x00, 0x08>,
in_range<0x10, 0x1F>, in_range<0x10, 0x1F>,
@@ -242,9 +195,9 @@ using lex_ml_literal_char = exclude<either<in_range<0x00, 0x08>,
lex_ml_literal_string_delim>>; lex_ml_literal_string_delim>>;
using lex_ml_literal_body = repeat<either<lex_ml_literal_char, lex_newline>, using lex_ml_literal_body = repeat<either<lex_ml_literal_char, lex_newline>,
unlimited>; unlimited>;
using lex_ml_literal_string = sequence<lex_ml_literal_string_open, using lex_ml_literal_string = sequence<lex_ml_literal_string_delim,
lex_ml_literal_body, lex_ml_literal_body,
lex_ml_literal_string_close>; lex_ml_literal_string_delim>;
using lex_string = either<lex_ml_basic_string, lex_basic_string, using lex_string = either<lex_ml_basic_string, lex_basic_string,
lex_ml_literal_string, lex_literal_string>; lex_ml_literal_string, lex_literal_string>;

View File

@@ -375,7 +375,7 @@ parse_ml_basic_string(location<Container>& loc)
std::string retval; std::string retval;
retval.reserve(token.unwrap().size()); retval.reserve(token.unwrap().size());
auto delim = lex_ml_basic_string_open::invoke(inner_loc); auto delim = lex_ml_basic_string_delim::invoke(inner_loc);
if(!delim) if(!delim)
{ {
throw internal_error(format_underline( throw internal_error(format_underline(
@@ -410,26 +410,7 @@ parse_ml_basic_string(location<Container>& loc)
{{std::addressof(inner_loc), "not sufficient token"}}), {{std::addressof(inner_loc), "not sufficient token"}}),
source_location(std::addressof(inner_loc))); source_location(std::addressof(inner_loc)));
} }
delim = lex_ml_basic_string_close::invoke(inner_loc); delim = lex_ml_basic_string_delim::invoke(inner_loc);
}
// `lex_ml_basic_string_close` allows 3 to 5 `"`s to allow 1 or 2 `"`s
// at just before the delimiter. Here, we need to attach `"`s at the
// end of the string body, if it exists.
// For detail, see the definition of `lex_ml_basic_string_close`.
assert(std::all_of(delim.unwrap().first(), delim.unwrap().last(),
[](const char c) noexcept {return c == '\"';}));
switch(delim.unwrap().size())
{
case 3: {break;}
case 4: {retval += "\""; break;}
case 5: {retval += "\"\""; break;}
default:
{
throw internal_error(format_underline(
"parse_ml_basic_string: closing delimiter has invalid length",
{{std::addressof(inner_loc), "end of this"}}),
source_location(std::addressof(inner_loc)));
}
} }
return ok(std::make_pair(toml::string(retval), token.unwrap())); return ok(std::make_pair(toml::string(retval), token.unwrap()));
} }
@@ -504,7 +485,7 @@ parse_ml_literal_string(location<Container>& loc)
{ {
location<std::string> inner_loc(loc.name(), token.unwrap().str()); location<std::string> inner_loc(loc.name(), token.unwrap().str());
const auto open = lex_ml_literal_string_open::invoke(inner_loc); const auto open = lex_ml_literal_string_delim::invoke(inner_loc);
if(!open) if(!open)
{ {
throw internal_error(format_underline( throw internal_error(format_underline(
@@ -517,7 +498,7 @@ parse_ml_literal_string(location<Container>& loc)
const auto body = lex_ml_literal_body::invoke(inner_loc); const auto body = lex_ml_literal_body::invoke(inner_loc);
const auto close = lex_ml_literal_string_close::invoke(inner_loc); const auto close = lex_ml_literal_string_delim::invoke(inner_loc);
if(!close) if(!close)
{ {
throw internal_error(format_underline( throw internal_error(format_underline(
@@ -525,29 +506,9 @@ parse_ml_literal_string(location<Container>& loc)
{{std::addressof(inner_loc), "should be '''"}}), {{std::addressof(inner_loc), "should be '''"}}),
source_location(std::addressof(inner_loc))); source_location(std::addressof(inner_loc)));
} }
// `lex_ml_literal_string_close` allows 3 to 5 `'`s to allow 1 or 2 `'`s return ok(std::make_pair(
// at just before the delimiter. Here, we need to attach `'`s at the toml::string(body.unwrap().str(), toml::string_t::literal),
// end of the string body, if it exists. token.unwrap()));
// For detail, see the definition of `lex_ml_basic_string_close`.
std::string retval = body.unwrap().str();
assert(std::all_of(close.unwrap().first(), close.unwrap().last(),
[](const char c) noexcept {return c == '\'';}));
switch(close.unwrap().size())
{
case 3: {break;}
case 4: {retval += "'"; break;}
case 5: {retval += "''"; break;}
default:
{
throw internal_error(format_underline(
"parse_ml_literal_string: closing delimiter has invalid length",
{{std::addressof(inner_loc), "end of this"}}),
source_location(std::addressof(inner_loc)));
}
}
return ok(std::make_pair(toml::string(retval, toml::string_t::literal),
token.unwrap()));
} }
else else
{ {

View File

@@ -55,7 +55,7 @@ struct region_base
// number of characters in the line after the region // number of characters in the line after the region
virtual std::size_t after() const noexcept {return 0;} virtual std::size_t after() const noexcept {return 0;}
virtual std::vector<std::string> comments() const {return {};} virtual std::vector<std::string> comments()const {return {};}
// ```toml // ```toml
// # comment_before // # comment_before
// key = "value" # comment_inline // key = "value" # comment_inline
@@ -511,7 +511,7 @@ inline std::string format_underline(const std::string& message,
retval << '\n'; retval << '\n';
retval << make_string(static_cast<std::size_t>(line_num_width + 1), ' '); retval << make_string(static_cast<std::size_t>(line_num_width + 1), ' ');
retval << color::bold << color::blue << " | " << color::reset; retval << color::bold << color::blue << " | " << color::reset;
for(const auto& help : helps) for(const auto help : helps)
{ {
retval << color::bold << "\nHint: " << color::reset; retval << color::bold << "\nHint: " << color::reset;
retval << help; retval << help;

View File

@@ -4,6 +4,7 @@
#define TOML11_SERIALIZER_HPP #define TOML11_SERIALIZER_HPP
#include "value.hpp" #include "value.hpp"
#include "lexer.hpp" #include "lexer.hpp"
#include "visit.hpp"
#include <limits> #include <limits>
#include <cstdio> #include <cstdio>
@@ -22,62 +23,39 @@ namespace toml
// Since toml restricts characters available in a bare key, generally a string // Since toml restricts characters available in a bare key, generally a string
// should be escaped. But checking whether a string needs to be surrounded by // should be escaped. But checking whether a string needs to be surrounded by
// a `"` and escaping some special character is boring. // a `"` and escaping some special character is boring.
template<typename charT, typename traits, typename Alloc> inline std::string format_key(const toml::key& key)
std::basic_string<charT, traits, Alloc>
format_key(const std::basic_string<charT, traits, Alloc>& key)
{ {
// check the key can be a bare (unquoted) key
detail::location<toml::key> loc(key, key); detail::location<toml::key> loc(key, key);
detail::lex_unquoted_key::invoke(loc); detail::lex_unquoted_key::invoke(loc);
if(loc.iter() == loc.end()) if(loc.iter() == loc.end())
{ {
return key; // all the tokens are consumed. the key is unquoted-key. return key; // all the tokens are consumed. the key is unquoted-key.
} }
std::string token("\"");
//if it includes special characters, then format it in a "quoted" key.
std::basic_string<charT, traits, Alloc> serialized("\"");
for(const char c : key) for(const char c : key)
{ {
switch(c) switch(c)
{ {
case '\\': {serialized += "\\\\"; break;} case '\\': {token += "\\\\"; break;}
case '\"': {serialized += "\\\""; break;} case '\"': {token += "\\\""; break;}
case '\b': {serialized += "\\b"; break;} case '\b': {token += "\\b"; break;}
case '\t': {serialized += "\\t"; break;} case '\t': {token += "\\t"; break;}
case '\f': {serialized += "\\f"; break;} case '\f': {token += "\\f"; break;}
case '\n': {serialized += "\\n"; break;} case '\n': {token += "\\n"; break;}
case '\r': {serialized += "\\r"; break;} case '\r': {token += "\\r"; break;}
default : {serialized += c; break;} default : {token += c; break;}
} }
} }
serialized += "\""; token += "\"";
return serialized; return token;
} }
template<typename charT, typename traits, typename Alloc> template<typename Comment,
std::basic_string<charT, traits, Alloc> template<typename ...> class Table,
format_keys(const std::vector<std::basic_string<charT, traits, Alloc>>& keys) template<typename ...> class Array>
{
std::basic_string<charT, traits, Alloc> serialized;
if(keys.empty()) {return serialized;}
for(const auto& ky : keys)
{
serialized += format_key(ky);
serialized += charT('.');
}
serialized.pop_back(); // remove the last dot '.'
return serialized;
}
template<typename Value>
struct serializer struct serializer
{ {
static_assert(detail::is_basic_value<Value>::value, using value_type = basic_value<Comment, Table, Array>;
"toml::serializer is for toml::value and its variants, "
"toml::basic_value<...>.");
using value_type = Value;
using key_type = typename value_type::key_type ; using key_type = typename value_type::key_type ;
using comment_type = typename value_type::comment_type ; using comment_type = typename value_type::comment_type ;
using boolean_type = typename value_type::boolean_type ; using boolean_type = typename value_type::boolean_type ;
@@ -142,17 +120,12 @@ struct serializer
return token; // there is no exponent part. just return it. return token; // there is no exponent part. just return it.
} }
#ifdef TOML11_USE_UNRELEASED_TOML_FEATURES #ifdef TOML11_USE_UNRELEASED_TOML_FEATURES
// Although currently it is not released yet as a tagged version, // Although currently it is not released yet, TOML will allow
// TOML will allow zero-prefix in an exponent part, such as `1.234e+01`. // zero-prefix in an exponent part such as 1.234e+01.
// ```toml // The following code removes the zero prefixes.
// num1 = 1.234e+1 # OK in TOML v0.5.0 // If the feature is activated, the following codes can be skipped.
// num2 = 1.234e+01 # error in TOML v0.5.0 but will be allowed soon
// ```
// To avoid `e+01`, the following `else` section removes the zero
// prefixes in the exponent part.
// If the feature is activated, it can be skipped.
return token; return token;
#else #endif
// zero-prefix in an exponent is NOT allowed in TOML v0.5.0. // zero-prefix in an exponent is NOT allowed in TOML v0.5.0.
// remove it if it exists. // remove it if it exists.
bool sign_exists = false; bool sign_exists = false;
@@ -171,29 +144,17 @@ struct serializer
zero_prefix); zero_prefix);
} }
return token; return token;
#endif
} }
std::string operator()(const string_type& s) const std::string operator()(const string_type& s) const
{ {
if(s.kind == string_t::basic) if(s.kind == string_t::basic)
{ {
if(std::find(s.str.cbegin(), s.str.cend(), '\n') != s.str.cend() || if(std::find(s.str.cbegin(), s.str.cend(), '\n') != s.str.cend())
std::find(s.str.cbegin(), s.str.cend(), '\"') != s.str.cend())
{ {
// if linefeed or double-quote is contained, // if linefeed is contained, make it multiline-string.
// make it multiline basic string. const std::string open("\"\"\"\n");
const auto escaped = this->escape_ml_basic_string(s.str); const std::string close("\\\n\"\"\"");
std::string open("\"\"\""); return open + this->escape_ml_basic_string(s.str) + close;
std::string close("\"\"\"");
if(escaped.find('\n') != std::string::npos ||
this->width_ < escaped.size() + 6)
{
// if the string body contains newline or is enough long,
// add newlines after and before delimiters.
open += "\n";
close = std::string("\\\n") + close;
}
return open + escaped + close;
} }
// no linefeed. try to make it oneline-string. // no linefeed. try to make it oneline-string.
@@ -234,11 +195,7 @@ struct serializer
if(std::find(s.str.cbegin(), s.str.cend(), '\n') != s.str.cend() || if(std::find(s.str.cbegin(), s.str.cend(), '\n') != s.str.cend() ||
std::find(s.str.cbegin(), s.str.cend(), '\'') != s.str.cend() ) std::find(s.str.cbegin(), s.str.cend(), '\'') != s.str.cend() )
{ {
std::string open("'''"); const std::string open("'''\n");
if(this->width_ + 6 < s.str.size())
{
open += '\n'; // the first newline is ignored by TOML spec
}
const std::string close("'''"); const std::string close("'''");
return open + s.str + close; return open + s.str + close;
} }
@@ -293,7 +250,7 @@ struct serializer
std::string token; std::string token;
if(!keys_.empty()) if(!keys_.empty())
{ {
token += format_key(keys_.back()); token += this->serialize_key(keys_.back());
token += " = "; token += " = ";
} }
bool failed = false; bool failed = false;
@@ -349,7 +306,7 @@ struct serializer
} }
} }
token += "[["; token += "[[";
token += format_keys(keys_); token += this->serialize_dotted_key(keys_);
token += "]]\n"; token += "]]\n";
token += this->make_multiline_table(item.as_table()); token += this->make_multiline_table(item.as_table());
} }
@@ -461,7 +418,7 @@ struct serializer
std::string token; std::string token;
if(!this->keys_.empty()) if(!this->keys_.empty())
{ {
token += format_key(this->keys_.back()); token += this->serialize_key(this->keys_.back());
token += " = "; token += " = ";
} }
token += this->make_inline_table(v); token += this->make_inline_table(v);
@@ -476,7 +433,7 @@ struct serializer
if(!keys_.empty()) if(!keys_.empty())
{ {
token += '['; token += '[';
token += format_keys(keys_); token += this->serialize_dotted_key(keys_);
token += "]\n"; token += "]\n";
} }
token += this->make_multiline_table(v); token += this->make_multiline_table(v);
@@ -485,6 +442,25 @@ struct serializer
private: private:
std::string serialize_key(const toml::key& key) const
{
return ::toml::format_key(key);
}
std::string serialize_dotted_key(const std::vector<toml::key>& keys) const
{
std::string token;
if(keys.empty()){return token;}
for(const auto& k : keys)
{
token += this->serialize_key(k);
token += '.';
}
token.erase(token.size() - 1, 1); // remove trailing `.`
return token;
}
std::string escape_basic_string(const std::string& s) const std::string escape_basic_string(const std::string& s) const
{ {
//XXX assuming `s` is a valid utf-8 sequence. //XXX assuming `s` is a valid utf-8 sequence.
@@ -514,9 +490,7 @@ struct serializer
switch(*i) switch(*i)
{ {
case '\\': {retval += "\\\\"; break;} case '\\': {retval += "\\\\"; break;}
// One or two consecutive "s are allowed. case '\"': {retval += "\\\""; break;}
// Later we will check there are no three consecutive "s.
// case '\"': {retval += "\\\""; break;}
case '\b': {retval += "\\b"; break;} case '\b': {retval += "\\b"; break;}
case '\t': {retval += "\\t"; break;} case '\t': {retval += "\\t"; break;}
case '\f': {retval += "\\f"; break;} case '\f': {retval += "\\f"; break;}
@@ -537,23 +511,6 @@ struct serializer
default: {retval += *i; break;} default: {retval += *i; break;}
} }
} }
// 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("\"\"\"");
while(found_3_quotes != std::string::npos)
{
retval.replace(found_3_quotes, 3, "\"\"\\\"");
found_3_quotes = retval.find("\"\"\"");
}
return retval; return retval;
} }
@@ -608,7 +565,7 @@ struct serializer
{ {
// in inline tables, trailing comma is not allowed (toml-lang #569). // in inline tables, trailing comma is not allowed (toml-lang #569).
if(is_first) {is_first = false;} else {token += ',';} if(is_first) {is_first = false;} else {token += ',';}
token += format_key(kv.first); token += this->serialize_key(kv.first);
token += '='; token += '=';
token += visit(serializer(std::numeric_limits<std::size_t>::max(), token += visit(serializer(std::numeric_limits<std::size_t>::max(),
this->float_prec_, true), kv.second); this->float_prec_, true), kv.second);
@@ -623,7 +580,7 @@ struct serializer
// print non-table stuff first. because after printing [foo.bar], the // print non-table stuff first. because after printing [foo.bar], the
// remaining non-table values will be assigned into [foo.bar], not [foo] // remaining non-table values will be assigned into [foo.bar], not [foo]
for(const auto& kv : v) for(const auto kv : v)
{ {
if(kv.second.is_table() || is_array_of_tables(kv.second)) if(kv.second.is_table() || is_array_of_tables(kv.second))
{ {
@@ -639,7 +596,7 @@ struct serializer
token += '\n'; token += '\n';
} }
} }
const auto key_and_sep = format_key(kv.first) + " = "; const auto key_and_sep = this->serialize_key(kv.first) + " = ";
const auto residual_width = (this->width_ > key_and_sep.size()) ? const auto residual_width = (this->width_ > key_and_sep.size()) ?
this->width_ - key_and_sep.size() : 0; this->width_ - key_and_sep.size() : 0;
token += key_and_sep; token += key_and_sep;
@@ -721,7 +678,6 @@ format(const basic_value<C, M, V>& v, std::size_t w = 80u,
int fprec = std::numeric_limits<toml::floating>::max_digits10, int fprec = std::numeric_limits<toml::floating>::max_digits10,
bool no_comment = false, bool force_inline = false) bool no_comment = false, bool force_inline = false)
{ {
using value_type = basic_value<C, M, V>;
// if value is a table, it is considered to be a root object. // if value is a table, it is considered to be a root object.
// the root object can't be an inline table. // the root object can't be an inline table.
if(v.is_table()) if(v.is_table())
@@ -732,10 +688,10 @@ format(const basic_value<C, M, V>& v, std::size_t w = 80u,
oss << v.comments(); oss << v.comments();
oss << '\n'; // to split the file comment from the first element oss << '\n'; // to split the file comment from the first element
} }
oss << visit(serializer<value_type>(w, fprec, no_comment, false), v); oss << visit(serializer<C, M, V>(w, fprec, no_comment, false), v);
return oss.str(); return oss.str();
} }
return visit(serializer<value_type>(w, fprec, force_inline), v); return visit(serializer<C, M, V>(w, fprec, force_inline), v);
} }
namespace detail namespace detail
@@ -771,8 +727,6 @@ template<typename charT, typename traits, typename C,
std::basic_ostream<charT, traits>& std::basic_ostream<charT, traits>&
operator<<(std::basic_ostream<charT, traits>& os, const basic_value<C, M, V>& v) operator<<(std::basic_ostream<charT, traits>& os, const basic_value<C, M, V>& v)
{ {
using value_type = basic_value<C, M, V>;
// get status of std::setw(). // get status of std::setw().
const auto w = static_cast<std::size_t>(os.width()); const auto w = static_cast<std::size_t>(os.width());
const int fprec = static_cast<int>(os.precision()); const int fprec = static_cast<int>(os.precision());
@@ -788,7 +742,7 @@ operator<<(std::basic_ostream<charT, traits>& os, const basic_value<C, M, V>& v)
os << '\n'; // to split the file comment from the first element os << '\n'; // to split the file comment from the first element
} }
// the root object can't be an inline table. so pass `false`. // the root object can't be an inline table. so pass `false`.
os << visit(serializer<value_type>(w, fprec, false, no_comment), v); os << visit(serializer<C, M, V>(w, fprec, false, no_comment), v);
// if v is a non-table value, and has only one comment, then // if v is a non-table value, and has only one comment, then
// put a comment just after a value. in the following way. // put a comment just after a value. in the following way.

View File

@@ -20,145 +20,223 @@ namespace detail
{ {
// to show error messages. not recommended for users. // to show error messages. not recommended for users.
template<typename Value> template<typename C, template<typename ...> class T, template<typename ...> class A>
inline region_base const& get_region(const Value& v) region_base const& get_region(const basic_value<C, T, A>&);
{ template<typename Region,
return *(v.region_info_); typename C, template<typename ...> class T, template<typename ...> class A>
} void change_region(basic_value<C, T, A>&, Region&&);
template<typename Value, typename Region> template<value_t Expected,
void change_region(Value& v, Region&& reg) typename C, template<typename ...> class T, template<typename ...> class A>
{
using region_type = typename std::remove_reference<
typename std::remove_cv<Region>::type
>::type;
std::shared_ptr<region_base> new_reg =
std::make_shared<region_type>(std::forward<region_type>(reg));
v.region_info_ = new_reg;
return;
}
template<value_t Expected, typename Value>
[[noreturn]] inline void [[noreturn]] inline void
throw_bad_cast(const std::string& funcname, value_t actual, const Value& v) throw_bad_cast(value_t actual, const ::toml::basic_value<C, T, A>& v)
{ {
throw type_error(detail::format_underline( throw type_error(detail::format_underline(concat_to_string(
concat_to_string(funcname, "bad_cast to ", Expected), { "toml::value: bad_cast to ", Expected), {
{std::addressof(get_region(v)), {std::addressof(get_region(v)),
concat_to_string("the actual type is ", actual)} concat_to_string("the actual type is ", actual)}
}), v.location()); }), v.location());
} }
// Throw `out_of_range` from `toml::value::at()` and `toml::find()` // switch by `value_t` and call the corresponding `value::as_xxx()`. {{{
// after generating an error message.
//
// The implementation is a bit complicated and there are many edge-cases.
// If you are not interested in the error message generation, just skip this.
template<typename Value>
[[noreturn]] void
throw_key_not_found_error(const Value& v, const key& ky)
{
// The top-level table has its region at the first 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
// its 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".
const auto& reg = get_region(v);
if(reg.line_num() == "1" && reg.size() == 1)
{
// Here it assumes that top-level table starts at the first character.
// The region corresponds to the top-level table will be generated at
// `parse_toml_file` function.
// It also assumes that the top-level table size is just one and
// the line number is `1`. It is always satisfied. And those conditions
// are satisfied only if the table is the top-level table.
//
// 1. one-character dot-key at the first line
// ```toml
// a.b = "c"
// ```
// toml11 counts whole key as the table key. Here, `a.b` is the region
// of the table "a". It could be counter intuitive, but it works.
// The size of the region is 3, not 1. The above example is the shortest
// dot-key example. The size cannot be 1.
//
// 2. one-character inline-table at the first line
// ```toml
// a = {b = "c"}
// ```
// toml11 consideres the inline table body as the table region. Here,
// `{b = "c"}` is the region of the table "a". The size of the region
// is 9, not 1. The shotest inline table still has two characters, `{`
// and `}`. The size cannot be 1.
//
// 3. one-character table declaration at the first line
// ```toml
// [a]
// ```
// toml11 consideres the whole table key as the table region. Here,
// `[a]` is the table region. The size is 3, not 1.
//
throw std::out_of_range(format_underline(concat_to_string(
"key \"", ky, "\" not found in the top-level table"), {
{std::addressof(reg), "the top-level table starts here"}
}));
}
else
{
// normal table.
throw std::out_of_range(format_underline(concat_to_string(
"key \"", ky, "\" not found"), {
{std::addressof(reg), "in this table"}
}));
}
}
// switch by `value_t` at the compile time.
template<value_t T> template<value_t T>
struct switch_cast {}; struct switch_cast {};
#define TOML11_GENERATE_SWITCH_CASTER(TYPE) \ template<>
template<> \ struct switch_cast<value_t::boolean>
struct switch_cast<value_t::TYPE> \ {
{ \ template<typename C, template<typename ...> class T, template<typename ...> class A>
template<typename Value> \ static ::toml::boolean& invoke(basic_value<C, T, A>& v) noexcept
static typename Value::TYPE##_type& invoke(Value& v) \ {
{ \ return v.as_boolean();
return v.as_##TYPE(); \ }
} \ template<typename C, template<typename ...> class T, template<typename ...> class A>
template<typename Value> \ static ::toml::boolean const& invoke(basic_value<C, T, A> const& v) noexcept
static typename Value::TYPE##_type const& invoke(const Value& v) \ {
{ \ return v.as_boolean();
return v.as_##TYPE(); \ }
} \ template<typename C, template<typename ...> class T, template<typename ...> class A>
template<typename Value> \ static ::toml::boolean&& invoke(basic_value<C, T, A>&& v) noexcept
static typename Value::TYPE##_type&& invoke(Value&& v) \ {
{ \ return std::move(v).as_boolean();
return std::move(v).as_##TYPE(); \ }
} \ };
}; \ template<>
/**/ struct switch_cast<value_t::integer>
TOML11_GENERATE_SWITCH_CASTER(boolean) {
TOML11_GENERATE_SWITCH_CASTER(integer) template<typename C, template<typename ...> class T, template<typename ...> class A>
TOML11_GENERATE_SWITCH_CASTER(floating) static ::toml::integer& invoke(basic_value<C, T, A>& v) noexcept
TOML11_GENERATE_SWITCH_CASTER(string) {
TOML11_GENERATE_SWITCH_CASTER(offset_datetime) return v.as_integer();
TOML11_GENERATE_SWITCH_CASTER(local_datetime) }
TOML11_GENERATE_SWITCH_CASTER(local_date) template<typename C, template<typename ...> class T, template<typename ...> class A>
TOML11_GENERATE_SWITCH_CASTER(local_time) static ::toml::integer const& invoke(basic_value<C, T, A> const& v) noexcept
TOML11_GENERATE_SWITCH_CASTER(array) {
TOML11_GENERATE_SWITCH_CASTER(table) return v.as_integer();
}
#undef TOML11_GENERATE_SWITCH_CASTER template<typename C, template<typename ...> class T, template<typename ...> class A>
static ::toml::integer&& invoke(basic_value<C, T, A>&& v) noexcept
{
return std::move(v).as_integer();
}
};
template<>
struct switch_cast<value_t::floating>
{
template<typename C, template<typename ...> class T, template<typename ...> class A>
static ::toml::floating& invoke(basic_value<C, T, A>& v) noexcept
{
return v.as_floating();
}
template<typename C, template<typename ...> class T, template<typename ...> class A>
static ::toml::floating const& invoke(basic_value<C, T, A> const& v) noexcept
{
return v.as_floating();
}
template<typename C, template<typename ...> class T, template<typename ...> class A>
static ::toml::floating&& invoke(basic_value<C, T, A>&& v) noexcept
{
return std::move(v).as_floating();
}
};
template<>
struct switch_cast<value_t::string>
{
template<typename C, template<typename ...> class T, template<typename ...> class A>
static ::toml::string& invoke(basic_value<C, T, A>& v) noexcept
{
return v.as_string();
}
template<typename C, template<typename ...> class T, template<typename ...> class A>
static ::toml::string const& invoke(basic_value<C, T, A> const& v) noexcept
{
return v.as_string();
}
template<typename C, template<typename ...> class T, template<typename ...> class A>
static ::toml::string&& invoke(basic_value<C, T, A>&& v) noexcept
{
return std::move(v).as_string();
}
};
template<>
struct switch_cast<value_t::offset_datetime>
{
template<typename C, template<typename ...> class T, template<typename ...> class A>
static ::toml::offset_datetime& invoke(basic_value<C, T, A>& v) noexcept
{
return v.as_offset_datetime();
}
template<typename C, template<typename ...> class T, template<typename ...> class A>
static ::toml::offset_datetime const& invoke(basic_value<C, T, A> const& v) noexcept
{
return v.as_offset_datetime();
}
template<typename C, template<typename ...> class T, template<typename ...> class A>
static ::toml::offset_datetime&& invoke(basic_value<C, T, A>&& v) noexcept
{
return std::move(v).as_offset_datetime();
}
};
template<>
struct switch_cast<value_t::local_datetime>
{
template<typename C, template<typename ...> class T, template<typename ...> class A>
static ::toml::local_datetime& invoke(basic_value<C, T, A>& v) noexcept
{
return v.as_local_datetime();
}
template<typename C, template<typename ...> class T, template<typename ...> class A>
static ::toml::local_datetime const& invoke(basic_value<C, T, A> const& v) noexcept
{
return v.as_local_datetime();
}
template<typename C, template<typename ...> class T, template<typename ...> class A>
static ::toml::local_datetime&& invoke(basic_value<C, T, A>&& v) noexcept
{
return std::move(v).as_local_datetime();
}
};
template<>
struct switch_cast<value_t::local_date>
{
template<typename C, template<typename ...> class T, template<typename ...> class A>
static ::toml::local_date& invoke(basic_value<C, T, A>& v) noexcept
{
return v.as_local_date();
}
template<typename C, template<typename ...> class T, template<typename ...> class A>
static ::toml::local_date const& invoke(basic_value<C, T, A> const& v) noexcept
{
return v.as_local_date();
}
template<typename C, template<typename ...> class T, template<typename ...> class A>
static ::toml::local_date&& invoke(basic_value<C, T, A>&& v) noexcept
{
return std::move(v).as_local_date();
}
};
template<>
struct switch_cast<value_t::local_time>
{
template<typename C, template<typename ...> class T, template<typename ...> class A>
static ::toml::local_time& invoke(basic_value<C, T, A>& v) noexcept
{
return v.as_local_time();
}
template<typename C, template<typename ...> class T, template<typename ...> class A>
static ::toml::local_time const& invoke(basic_value<C, T, A> const& v) noexcept
{
return v.as_local_time();
}
template<typename C, template<typename ...> class T, template<typename ...> class A>
static ::toml::local_time&& invoke(basic_value<C, T, A>&& v) noexcept
{
return std::move(v).as_local_time();
}
};
template<>
struct switch_cast<value_t::array>
{
template<typename C, template<typename ...> class T, template<typename ...> class A>
static typename basic_value<C, T, A>::array_type&
invoke(basic_value<C, T, A>& v) noexcept
{
return v.as_array();
}
template<typename C, template<typename ...> class T, template<typename ...> class A>
static typename basic_value<C, T, A>::array_type const&
invoke(basic_value<C, T, A> const& v) noexcept
{
return v.as_array();
}
template<typename C, template<typename ...> class T, template<typename ...> class A>
static typename basic_value<C, T, A>::array_type &&
invoke(basic_value<C, T, A>&& v) noexcept
{
return std::move(v).as_array();
}
};
template<>
struct switch_cast<value_t::table>
{
template<typename C, template<typename ...> class T, template<typename ...> class A>
static typename basic_value<C, T, A>::table_type&
invoke(basic_value<C, T, A>& v) noexcept
{
return v.as_table();
}
template<typename C, template<typename ...> class T, template<typename ...> class A>
static typename basic_value<C, T, A>::table_type const&
invoke(basic_value<C, T, A> const& v) noexcept
{
return v.as_table();
}
template<typename C, template<typename ...> class T, template<typename ...> class A>
static typename basic_value<C, T, A>::table_type &&
invoke(basic_value<C, T, A>&& v) noexcept
{
return std::move(v).as_table();
}
}; // }}}
}// detail }// detail
@@ -1177,7 +1255,7 @@ class basic_value
{ {
if(this->type_ != T) if(this->type_ != T)
{ {
detail::throw_bad_cast<T>("toml::value::cast: ", this->type_, *this); detail::throw_bad_cast<T>(this->type_, *this);
} }
return detail::switch_cast<T>::invoke(*this); return detail::switch_cast<T>::invoke(*this);
} }
@@ -1186,7 +1264,7 @@ class basic_value
{ {
if(this->type_ != T) if(this->type_ != T)
{ {
detail::throw_bad_cast<T>("toml::value::cast: ", this->type_, *this); detail::throw_bad_cast<T>(this->type_, *this);
} }
return detail::switch_cast<T>::invoke(*this); return detail::switch_cast<T>::invoke(*this);
} }
@@ -1195,7 +1273,7 @@ class basic_value
{ {
if(this->type_ != T) if(this->type_ != T)
{ {
detail::throw_bad_cast<T>("toml::value::cast: ", this->type_, *this); detail::throw_bad_cast<T>(this->type_, *this);
} }
return detail::switch_cast<T>::invoke(std::move(*this)); return detail::switch_cast<T>::invoke(std::move(*this));
} }
@@ -1239,14 +1317,13 @@ class basic_value
// ======================================================================== // ========================================================================
// throw version // throw version
// ------------------------------------------------------------------------ // ------------------------------------------------------------------------
// const reference {{{ // const reference
boolean const& as_boolean() const& boolean const& as_boolean() const&
{ {
if(this->type_ != value_t::boolean) if(this->type_ != value_t::boolean)
{ {
detail::throw_bad_cast<value_t::boolean>( detail::throw_bad_cast<value_t::boolean>(this->type_, *this);
"toml::value::as_boolean(): ", this->type_, *this);
} }
return this->boolean_; return this->boolean_;
} }
@@ -1254,8 +1331,7 @@ class basic_value
{ {
if(this->type_ != value_t::integer) if(this->type_ != value_t::integer)
{ {
detail::throw_bad_cast<value_t::integer>( detail::throw_bad_cast<value_t::integer>(this->type_, *this);
"toml::value::as_integer(): ", this->type_, *this);
} }
return this->integer_; return this->integer_;
} }
@@ -1263,8 +1339,7 @@ class basic_value
{ {
if(this->type_ != value_t::floating) if(this->type_ != value_t::floating)
{ {
detail::throw_bad_cast<value_t::floating>( detail::throw_bad_cast<value_t::floating>(this->type_, *this);
"toml::value::as_floating(): ", this->type_, *this);
} }
return this->floating_; return this->floating_;
} }
@@ -1272,8 +1347,7 @@ class basic_value
{ {
if(this->type_ != value_t::string) if(this->type_ != value_t::string)
{ {
detail::throw_bad_cast<value_t::string>( detail::throw_bad_cast<value_t::string>(this->type_, *this);
"toml::value::as_string(): ", this->type_, *this);
} }
return this->string_; return this->string_;
} }
@@ -1281,8 +1355,7 @@ class basic_value
{ {
if(this->type_ != value_t::offset_datetime) if(this->type_ != value_t::offset_datetime)
{ {
detail::throw_bad_cast<value_t::offset_datetime>( detail::throw_bad_cast<value_t::offset_datetime>(this->type_, *this);
"toml::value::as_offset_datetime(): ", this->type_, *this);
} }
return this->offset_datetime_; return this->offset_datetime_;
} }
@@ -1290,8 +1363,7 @@ class basic_value
{ {
if(this->type_ != value_t::local_datetime) if(this->type_ != value_t::local_datetime)
{ {
detail::throw_bad_cast<value_t::local_datetime>( detail::throw_bad_cast<value_t::local_datetime>(this->type_, *this);
"toml::value::as_local_datetime(): ", this->type_, *this);
} }
return this->local_datetime_; return this->local_datetime_;
} }
@@ -1299,8 +1371,7 @@ class basic_value
{ {
if(this->type_ != value_t::local_date) if(this->type_ != value_t::local_date)
{ {
detail::throw_bad_cast<value_t::local_date>( detail::throw_bad_cast<value_t::local_date>(this->type_, *this);
"toml::value::as_local_date(): ", this->type_, *this);
} }
return this->local_date_; return this->local_date_;
} }
@@ -1308,8 +1379,7 @@ class basic_value
{ {
if(this->type_ != value_t::local_time) if(this->type_ != value_t::local_time)
{ {
detail::throw_bad_cast<value_t::local_time>( detail::throw_bad_cast<value_t::local_time>(this->type_, *this);
"toml::value::as_local_time(): ", this->type_, *this);
} }
return this->local_time_; return this->local_time_;
} }
@@ -1317,8 +1387,7 @@ class basic_value
{ {
if(this->type_ != value_t::array) if(this->type_ != value_t::array)
{ {
detail::throw_bad_cast<value_t::array>( detail::throw_bad_cast<value_t::array>(this->type_, *this);
"toml::value::as_array(): ", this->type_, *this);
} }
return this->array_.value(); return this->array_.value();
} }
@@ -1326,21 +1395,19 @@ class basic_value
{ {
if(this->type_ != value_t::table) if(this->type_ != value_t::table)
{ {
detail::throw_bad_cast<value_t::table>( detail::throw_bad_cast<value_t::table>(this->type_, *this);
"toml::value::as_table(): ", this->type_, *this);
} }
return this->table_.value(); return this->table_.value();
} }
// }}}
// ------------------------------------------------------------------------ // ------------------------------------------------------------------------
// nonconst reference {{{ // nonconst reference
boolean & as_boolean() & boolean & as_boolean() &
{ {
if(this->type_ != value_t::boolean) if(this->type_ != value_t::boolean)
{ {
detail::throw_bad_cast<value_t::boolean>( detail::throw_bad_cast<value_t::boolean>(this->type_, *this);
"toml::value::as_boolean(): ", this->type_, *this);
} }
return this->boolean_; return this->boolean_;
} }
@@ -1348,8 +1415,7 @@ class basic_value
{ {
if(this->type_ != value_t::integer) if(this->type_ != value_t::integer)
{ {
detail::throw_bad_cast<value_t::integer>( detail::throw_bad_cast<value_t::integer>(this->type_, *this);
"toml::value::as_integer(): ", this->type_, *this);
} }
return this->integer_; return this->integer_;
} }
@@ -1357,8 +1423,7 @@ class basic_value
{ {
if(this->type_ != value_t::floating) if(this->type_ != value_t::floating)
{ {
detail::throw_bad_cast<value_t::floating>( detail::throw_bad_cast<value_t::floating>(this->type_, *this);
"toml::value::as_floating(): ", this->type_, *this);
} }
return this->floating_; return this->floating_;
} }
@@ -1366,8 +1431,7 @@ class basic_value
{ {
if(this->type_ != value_t::string) if(this->type_ != value_t::string)
{ {
detail::throw_bad_cast<value_t::string>( detail::throw_bad_cast<value_t::string>(this->type_, *this);
"toml::value::as_string(): ", this->type_, *this);
} }
return this->string_; return this->string_;
} }
@@ -1375,8 +1439,7 @@ class basic_value
{ {
if(this->type_ != value_t::offset_datetime) if(this->type_ != value_t::offset_datetime)
{ {
detail::throw_bad_cast<value_t::offset_datetime>( detail::throw_bad_cast<value_t::offset_datetime>(this->type_, *this);
"toml::value::as_offset_datetime(): ", this->type_, *this);
} }
return this->offset_datetime_; return this->offset_datetime_;
} }
@@ -1384,8 +1447,7 @@ class basic_value
{ {
if(this->type_ != value_t::local_datetime) if(this->type_ != value_t::local_datetime)
{ {
detail::throw_bad_cast<value_t::local_datetime>( detail::throw_bad_cast<value_t::local_datetime>(this->type_, *this);
"toml::value::as_local_datetime(): ", this->type_, *this);
} }
return this->local_datetime_; return this->local_datetime_;
} }
@@ -1393,8 +1455,7 @@ class basic_value
{ {
if(this->type_ != value_t::local_date) if(this->type_ != value_t::local_date)
{ {
detail::throw_bad_cast<value_t::local_date>( detail::throw_bad_cast<value_t::local_date>(this->type_, *this);
"toml::value::as_local_date(): ", this->type_, *this);
} }
return this->local_date_; return this->local_date_;
} }
@@ -1402,8 +1463,7 @@ class basic_value
{ {
if(this->type_ != value_t::local_time) if(this->type_ != value_t::local_time)
{ {
detail::throw_bad_cast<value_t::local_time>( detail::throw_bad_cast<value_t::local_time>(this->type_, *this);
"toml::value::as_local_time(): ", this->type_, *this);
} }
return this->local_time_; return this->local_time_;
} }
@@ -1411,8 +1471,7 @@ class basic_value
{ {
if(this->type_ != value_t::array) if(this->type_ != value_t::array)
{ {
detail::throw_bad_cast<value_t::array>( detail::throw_bad_cast<value_t::array>(this->type_, *this);
"toml::value::as_array(): ", this->type_, *this);
} }
return this->array_.value(); return this->array_.value();
} }
@@ -1420,22 +1479,19 @@ class basic_value
{ {
if(this->type_ != value_t::table) if(this->type_ != value_t::table)
{ {
detail::throw_bad_cast<value_t::table>( detail::throw_bad_cast<value_t::table>(this->type_, *this);
"toml::value::as_table(): ", this->type_, *this);
} }
return this->table_.value(); return this->table_.value();
} }
// }}}
// ------------------------------------------------------------------------ // ------------------------------------------------------------------------
// rvalue reference {{{ // rvalue reference
boolean && as_boolean() && boolean && as_boolean() &&
{ {
if(this->type_ != value_t::boolean) if(this->type_ != value_t::boolean)
{ {
detail::throw_bad_cast<value_t::boolean>( detail::throw_bad_cast<value_t::boolean>(this->type_, *this);
"toml::value::as_boolean(): ", this->type_, *this);
} }
return std::move(this->boolean_); return std::move(this->boolean_);
} }
@@ -1443,8 +1499,7 @@ class basic_value
{ {
if(this->type_ != value_t::integer) if(this->type_ != value_t::integer)
{ {
detail::throw_bad_cast<value_t::integer>( detail::throw_bad_cast<value_t::integer>(this->type_, *this);
"toml::value::as_integer(): ", this->type_, *this);
} }
return std::move(this->integer_); return std::move(this->integer_);
} }
@@ -1452,8 +1507,7 @@ class basic_value
{ {
if(this->type_ != value_t::floating) if(this->type_ != value_t::floating)
{ {
detail::throw_bad_cast<value_t::floating>( detail::throw_bad_cast<value_t::floating>(this->type_, *this);
"toml::value::as_floating(): ", this->type_, *this);
} }
return std::move(this->floating_); return std::move(this->floating_);
} }
@@ -1461,8 +1515,7 @@ class basic_value
{ {
if(this->type_ != value_t::string) if(this->type_ != value_t::string)
{ {
detail::throw_bad_cast<value_t::string>( detail::throw_bad_cast<value_t::string>(this->type_, *this);
"toml::value::as_string(): ", this->type_, *this);
} }
return std::move(this->string_); return std::move(this->string_);
} }
@@ -1470,8 +1523,7 @@ class basic_value
{ {
if(this->type_ != value_t::offset_datetime) if(this->type_ != value_t::offset_datetime)
{ {
detail::throw_bad_cast<value_t::offset_datetime>( detail::throw_bad_cast<value_t::offset_datetime>(this->type_, *this);
"toml::value::as_offset_datetime(): ", this->type_, *this);
} }
return std::move(this->offset_datetime_); return std::move(this->offset_datetime_);
} }
@@ -1479,8 +1531,7 @@ class basic_value
{ {
if(this->type_ != value_t::local_datetime) if(this->type_ != value_t::local_datetime)
{ {
detail::throw_bad_cast<value_t::local_datetime>( detail::throw_bad_cast<value_t::local_datetime>(this->type_, *this);
"toml::value::as_local_datetime(): ", this->type_, *this);
} }
return std::move(this->local_datetime_); return std::move(this->local_datetime_);
} }
@@ -1488,8 +1539,7 @@ class basic_value
{ {
if(this->type_ != value_t::local_date) if(this->type_ != value_t::local_date)
{ {
detail::throw_bad_cast<value_t::local_date>( detail::throw_bad_cast<value_t::local_date>(this->type_, *this);
"toml::value::as_local_date(): ", this->type_, *this);
} }
return std::move(this->local_date_); return std::move(this->local_date_);
} }
@@ -1497,8 +1547,7 @@ class basic_value
{ {
if(this->type_ != value_t::local_time) if(this->type_ != value_t::local_time)
{ {
detail::throw_bad_cast<value_t::local_time>( detail::throw_bad_cast<value_t::local_time>(this->type_, *this);
"toml::value::as_local_time(): ", this->type_, *this);
} }
return std::move(this->local_time_); return std::move(this->local_time_);
} }
@@ -1506,8 +1555,7 @@ class basic_value
{ {
if(this->type_ != value_t::array) if(this->type_ != value_t::array)
{ {
detail::throw_bad_cast<value_t::array>( detail::throw_bad_cast<value_t::array>(this->type_, *this);
"toml::value::as_array(): ", this->type_, *this);
} }
return std::move(this->array_.value()); return std::move(this->array_.value());
} }
@@ -1515,12 +1563,10 @@ class basic_value
{ {
if(this->type_ != value_t::table) if(this->type_ != value_t::table)
{ {
detail::throw_bad_cast<value_t::table>( detail::throw_bad_cast<value_t::table>(this->type_, *this);
"toml::value::as_table(): ", this->type_, *this);
} }
return std::move(this->table_.value()); return std::move(this->table_.value());
} }
// }}}
// accessors ============================================================= // accessors =============================================================
// //
@@ -1528,29 +1574,11 @@ class basic_value
// //
value_type& at(const key& k) value_type& at(const key& k)
{ {
if(!this->is_table()) return this->as_table().at(k);
{
detail::throw_bad_cast<value_t::table>(
"toml::value::at(key): ", this->type_, *this);
}
if(this->as_table(std::nothrow).count(k) == 0)
{
detail::throw_key_not_found_error(*this, k);
}
return this->as_table(std::nothrow).at(k);
} }
value_type const& at(const key& k) const value_type const& at(const key& k) const
{ {
if(!this->is_table()) return this->as_table().at(k);
{
detail::throw_bad_cast<value_t::table>(
"toml::value::at(key): ", this->type_, *this);
}
if(this->as_table(std::nothrow).count(k) == 0)
{
detail::throw_key_not_found_error(*this, k);
}
return this->as_table(std::nothrow).at(k);
} }
value_type& operator[](const key& k) value_type& operator[](const key& k)
{ {
@@ -1558,78 +1586,49 @@ class basic_value
{ {
*this = table_type{}; *this = table_type{};
} }
else if(!this->is_table()) // initialized, but not a table return this->as_table()[k];
{
detail::throw_bad_cast<value_t::table>(
"toml::value::operator[](key): ", this->type_, *this);
}
return this->as_table(std::nothrow)[k];
} }
value_type& at(const std::size_t idx) value_type& at(const std::size_t idx)
{ {
if(!this->is_array())
{
detail::throw_bad_cast<value_t::array>(
"toml::value::at(idx): ", this->type_, *this);
}
if(this->as_array(std::nothrow).size() <= idx)
{
throw std::out_of_range(detail::format_underline(
"toml::value::at(idx): no element corresponding to the index", {
{this->region_info_.get(),
concat_to_string("the length is ", this->as_array(std::nothrow).size(),
", and the specified index is ", idx)}
}));
}
return this->as_array().at(idx); return this->as_array().at(idx);
} }
value_type const& at(const std::size_t idx) const value_type const& at(const std::size_t idx) const
{ {
if(!this->is_array()) return this->as_array().at(idx);
{
detail::throw_bad_cast<value_t::array>(
"toml::value::at(idx): ", this->type_, *this);
}
if(this->as_array(std::nothrow).size() <= idx)
{
throw std::out_of_range(detail::format_underline(
"toml::value::at(idx): no element corresponding to the index", {
{this->region_info_.get(),
concat_to_string("the length is ", this->as_array(std::nothrow).size(),
", and the specified index is ", idx)}
}));
}
return this->as_array(std::nothrow).at(idx);
} }
value_type& operator[](const std::size_t idx) noexcept value_type& operator[](const std::size_t idx) noexcept
{ {
// no check...
return this->as_array(std::nothrow)[idx]; return this->as_array(std::nothrow)[idx];
} }
value_type const& operator[](const std::size_t idx) const noexcept value_type const& operator[](const std::size_t idx) const noexcept
{ {
// no check...
return this->as_array(std::nothrow)[idx]; return this->as_array(std::nothrow)[idx];
} }
void push_back(const value_type& x) void push_back(const value_type& x)
{ {
if(!this->is_array()) if(this->type_ != value_t::array)
{ {
detail::throw_bad_cast<value_t::array>( throw type_error(detail::format_underline(
"toml::value::push_back(value): ", this->type_, *this); "toml::value::push_back(value): bad_cast to array type", {
{this->region_info_.get(),
concat_to_string("the actual type is ", this->type_)}
}), this->location());
} }
this->as_array(std::nothrow).push_back(x); this->as_array(std::nothrow).push_back(x);
return; return;
} }
void push_back(value_type&& x) void push_back(value_type&& x)
{ {
if(!this->is_array()) if(this->type_ != value_t::array)
{ {
detail::throw_bad_cast<value_t::array>( throw type_error(detail::format_underline(
"toml::value::push_back(value): ", this->type_, *this); "toml::value::push_back(value): bad_cast to array type", {
{this->region_info_.get(),
concat_to_string("the actual type is ", this->type_)}
}), this->location());
} }
this->as_array(std::nothrow).push_back(std::move(x)); this->as_array(std::nothrow).push_back(std::move(x));
return; return;
@@ -1638,10 +1637,13 @@ class basic_value
template<typename ... Ts> template<typename ... Ts>
value_type& emplace_back(Ts&& ... args) value_type& emplace_back(Ts&& ... args)
{ {
if(!this->is_array()) if(this->type_ != value_t::array)
{ {
detail::throw_bad_cast<value_t::array>( throw type_error(detail::format_underline(
"toml::value::emplace_back(...): ", this->type_, *this); "toml::value::emplace_back(value): bad_cast to array type", {
{this->region_info_.get(),
concat_to_string("the actual type is ", this->type_)}
}), this->location());
} }
this->as_array(std::nothrow).emplace_back(std::forward<Ts>(args) ...); this->as_array(std::nothrow).emplace_back(std::forward<Ts>(args) ...);
return this->as_array(std::nothrow).back(); return this->as_array(std::nothrow).back();
@@ -1653,15 +1655,15 @@ class basic_value
{ {
case value_t::array: case value_t::array:
{ {
return this->as_array(std::nothrow).size(); return this->as_array().size();
} }
case value_t::table: case value_t::table:
{ {
return this->as_table(std::nothrow).size(); return this->as_table().size();
} }
case value_t::string: case value_t::string:
{ {
return this->as_string(std::nothrow).str.size(); return this->as_string().str.size();
} }
default: default:
{ {
@@ -1676,22 +1678,28 @@ class basic_value
std::size_t count(const key_type& k) const std::size_t count(const key_type& k) const
{ {
if(!this->is_table()) if(this->type_ != value_t::table)
{ {
detail::throw_bad_cast<value_t::table>( throw type_error(detail::format_underline(
"toml::value::count(key): ", this->type_, *this); "toml::value::count(key): bad_cast to table type", {
{this->region_info_.get(),
concat_to_string("the actual type is ", this->type_)}
}), this->location());
} }
return this->as_table(std::nothrow).count(k); return this->as_table().count(k);
} }
bool contains(const key_type& k) const bool contains(const key_type& k) const
{ {
if(!this->is_table()) if(this->type_ != value_t::table)
{ {
detail::throw_bad_cast<value_t::table>( throw type_error(detail::format_underline(
"toml::value::contains(key): ", this->type_, *this); "toml::value::contains(key): bad_cast to table type", {
{this->region_info_.get(),
concat_to_string("the actual type is ", this->type_)}
}), this->location());
} }
return (this->as_table(std::nothrow).count(k) != 0); return (this->as_table().count(k) != 0);
} }
source_location location() const source_location location() const
@@ -1716,11 +1724,13 @@ class basic_value
} }
// for error messages // for error messages
template<typename Value> template<typename C,
friend region_base const& detail::get_region(const Value& v); template<typename ...> class T, template<typename ...> class A>
friend region_base const& detail::get_region(const basic_value<C, T, A>&);
template<typename Value, typename Region> template<typename Region, typename C,
friend void detail::change_region(Value& v, Region&& reg); template<typename ...> class T, template<typename ...> class A>
friend void detail::change_region(basic_value<C, T, A>&, Region&&);
private: private:
@@ -1750,6 +1760,30 @@ using value = basic_value<discard_comments, std::unordered_map, std::vector>;
using array = typename value::array_type; using array = typename value::array_type;
using table = typename value::table_type; using table = typename value::table_type;
namespace detail
{
template<typename C,
template<typename ...> class T, template<typename ...> class A>
inline region_base const& get_region(const basic_value<C, T, A>& v)
{
return *(v.region_info_);
}
template<typename Region, typename C,
template<typename ...> class T, template<typename ...> class A>
void change_region(basic_value<C, T, A>& v, Region&& reg)
{
using region_type = typename std::remove_reference<
typename std::remove_cv<Region>::type
>::type;
std::shared_ptr<region_base> new_reg =
std::make_shared<region_type>(std::forward<region_type>(reg));
v.region_info_ = new_reg;
return;
}
}// detail
template<typename C, template<typename ...> class T, template<typename ...> class A> template<typename C, template<typename ...> class T, template<typename ...> class A>
inline bool inline bool
operator==(const basic_value<C, T, A>& lhs, const basic_value<C, T, A>& rhs) operator==(const basic_value<C, T, A>& lhs, const basic_value<C, T, A>& rhs)
@@ -1961,77 +1995,5 @@ inline std::string format_error(const std::string& err_msg,
}, std::move(hints), colorize); }, std::move(hints), colorize);
} }
template<typename Visitor, typename C,
template<typename ...> class T, template<typename ...> class A>
detail::return_type_of_t<Visitor, const toml::boolean&>
visit(Visitor&& visitor, const toml::basic_value<C, T, A>& 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 std::runtime_error(format_error("[error] toml::visit: toml::basic_value "
"does not have any valid basic_value.", v, "here"));
}
template<typename Visitor, typename C,
template<typename ...> class T, template<typename ...> class A>
detail::return_type_of_t<Visitor, toml::boolean&>
visit(Visitor&& visitor, toml::basic_value<C, T, A>& 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 std::runtime_error(format_error("[error] toml::visit: toml::basic_value "
"does not have any valid basic_value.", v, "here"));
}
template<typename Visitor, typename C,
template<typename ...> class T, template<typename ...> class A>
detail::return_type_of_t<Visitor, toml::boolean&&>
visit(Visitor&& visitor, toml::basic_value<C, T, A>&& 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 std::runtime_error(format_error("[error] toml::visit: toml::basic_value "
"does not have any valid basic_value.", v, "here"));
}
}// toml }// toml
#endif// TOML11_VALUE #endif// TOML11_VALUE

134
toml/visit.hpp Normal file
View File

@@ -0,0 +1,134 @@
// Copyright Toru Niina 2019.
// Distributed under the MIT License.
#ifndef TOML11_VISIT_HPP
#define TOML11_VISIT_HPP
#include "value.hpp"
namespace toml
{
template<typename Visitor, typename C,
template<typename ...> class T, template<typename ...> class A>
detail::return_type_of_t<Visitor, const toml::boolean&>
visit(Visitor&& visitor, const toml::basic_value<C, T, A>& 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 std::runtime_error(format_error("[error] toml::visit: toml::basic_value "
"does not have any valid basic_value.", v, "here"));
}
template<typename Visitor, typename C,
template<typename ...> class T, template<typename ...> class A>
detail::return_type_of_t<Visitor, toml::boolean&>
visit(Visitor&& visitor, toml::basic_value<C, T, A>& 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 std::runtime_error(format_error("[error] toml::visit: toml::basic_value "
"does not have any valid basic_value.", v, "here"));
}
template<typename Visitor, typename C,
template<typename ...> class T, template<typename ...> class A>
detail::return_type_of_t<Visitor, toml::boolean&&>
visit(Visitor&& visitor, toml::basic_value<C, T, A>&& 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 std::runtime_error(format_error("[error] toml::visit: toml::basic_value "
"does not have any valid basic_value.", v, "here"));
}
namespace detail
{
template<typename Result, typename Visitor, typename Value>
struct visitor
{
template<typename ... Ts>
Result operator()(Ts&& ... args)
{
return vis(value, args ...);
}
Visitor vis;
Value value;
};
template<typename Result, typename Visitor, typename Value>
visitor<Result, Visitor, Value> make_visitor(Visitor&& vis, Value&& val)
{
return visitor<Result, Visitor, Value>{
std::forward<Visitor>(vis), std::forward<Value>(val)
};
}
} // detail
template<typename Visitor, typename Value, typename ... Values>
auto visit(Visitor&& visitor, Value&& v, Values&& ... vs)
-> detail::enable_if_t<detail::conjunction<
detail::is_basic_value<Value>, detail::is_basic_value<Values> ...
>::value, decltype(visitor(std::forward<Value >(v ).as_boolean(),
std::forward<Values>(vs).as_boolean()...))>
{
using result_t = decltype(visitor(v.as_boolean(), vs.as_boolean()...));
using detail::make_visitor;
switch(v.type())
{
case value_t::boolean : {return visit(make_visitor<result_t>(std::forward<Visitor>(visitor), v.as_boolean ()), std::forward<Values>(vs)...);}
case value_t::integer : {return visit(make_visitor<result_t>(std::forward<Visitor>(visitor), v.as_integer ()), std::forward<Values>(vs)...);}
case value_t::floating : {return visit(make_visitor<result_t>(std::forward<Visitor>(visitor), v.as_floating ()), std::forward<Values>(vs)...);}
case value_t::string : {return visit(make_visitor<result_t>(std::forward<Visitor>(visitor), v.as_string ()), std::forward<Values>(vs)...);}
case value_t::offset_datetime: {return visit(make_visitor<result_t>(std::forward<Visitor>(visitor), v.as_offset_datetime()), std::forward<Values>(vs)...);}
case value_t::local_datetime : {return visit(make_visitor<result_t>(std::forward<Visitor>(visitor), v.as_local_datetime ()), std::forward<Values>(vs)...);}
case value_t::local_date : {return visit(make_visitor<result_t>(std::forward<Visitor>(visitor), v.as_local_date ()), std::forward<Values>(vs)...);}
case value_t::local_time : {return visit(make_visitor<result_t>(std::forward<Visitor>(visitor), v.as_local_time ()), std::forward<Values>(vs)...);}
case value_t::array : {return visit(make_visitor<result_t>(std::forward<Visitor>(visitor), v.as_array ()), std::forward<Values>(vs)...);}
case value_t::table : {return visit(make_visitor<result_t>(std::forward<Visitor>(visitor), v.as_table ()), std::forward<Values>(vs)...);}
case value_t::empty : break;
default: break;
}
throw std::runtime_error(format_error("[error] toml::visit: toml::basic_value "
"does not have any valid basic_value.", v, "here"));
}
} // toml
#endif// TOML11_VISIT_HPP