Compare commits

...

48 Commits

Author SHA1 Message Date
ToruNiina
31826b55ce feat: avoid double checking in helper methods 2020-03-25 22:49:19 +09:00
ToruNiina
e3fc354e8d Merge branch 'shorten-switch-cast' 2020-03-24 22:43:09 +09:00
ToruNiina
ea87f92358 doc: update exception section in README (fix #107) 2020-03-23 20:57:36 +09:00
ToruNiina
c259456282 ci: fix Travis.CI OS X build 2020-03-22 20:40:02 +09:00
ToruNiina
d7662347f2 refactor: shorten switch_cast definition by macro 2020-03-21 17:44:23 +09:00
ToruNiina
5f5539d402 feat: throw informative error from value.at(...) 2020-03-21 17:09:04 +09:00
ToruNiina
c2151cab0b refactor: show func name in bad_cast from helpers 2020-03-21 17:06:34 +09:00
ToruNiina
653c87592c feat: enable to show function name in bad_cast 2020-03-21 17:04:05 +09:00
ToruNiina
bdf4e75122 refactor: move helper function from get to value 2020-03-21 16:57:12 +09:00
ToruNiina
60d23116ba Merge branch 'master' of github.com:ToruNiina/toml11 2020-03-13 14:38:33 +09:00
ToruNiina
af8cf9ddc5 refactor: remove redundant functions in serializer 2020-03-13 13:55:14 +09:00
ToruNiina
f125cca010 refactor: simplify serializer's template argument 2020-03-12 13:46:17 +09:00
ToruNiina
a20a2c0b80 doc: update README 2020-03-01 00:35:27 +09:00
ToruNiina
9694afbe32 Merge branch 'improve-error-message' 2020-02-29 23:43:23 +09:00
ToruNiina
d11e42ca7e fix: explicitly say the table is top-level
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".
2020-02-29 22:56:29 +09:00
ToruNiina
128b66bda9 refactor: add missing whitespace 2020-02-29 22:54:50 +09:00
ToruNiina
d1af42f151 refactor: add throw_key_not_found_error
and replace related throw statements with it
2020-02-29 22:23:15 +09:00
ToruNiina
8acf105b56 doc: update contributor list and test commands 2020-02-27 19:30:34 +09:00
Toru Niina
b86b5364ba Merge pull request #103 from jwillikers/fix_tests
Use FetchContent to retrieve TOML test data
2020-02-27 19:13:02 +09:00
Jordan Williams
bfe57340f4 no longer explicitly clone the TOML repository in CI builds 2020-02-24 08:03:29 -06:00
Jordan Williams
02a6f029ad use ExternalProject to download the toml tests
In order to evaluate the relative paths correctly, add_test should use the current binary directory as the working directory.
2020-02-24 07:59:59 -06:00
Jordan Williams
9017900ff3 set version directly from CMake project command
This silences the warning for CMake policy CMP0048: https://cmake.org/cmake/help/v3.0/policy/CMP0048.html
2020-02-24 07:35:59 -06:00
Jordan Williams
3c5ebd73d7 require CMake version 3.1 2020-02-24 07:31:48 -06:00
Jordan Williams
2223eb4f62 Revert "no longer explicitly clone the TOML repository in CI builds"
This reverts commit fe644ea4b7.
2020-02-24 07:31:15 -06:00
Jordan Williams
a655a71cef Revert "use FetchContent to retrieve TOML test data"
This reverts commit 4c34986db0.
2020-02-24 07:31:06 -06:00
ToruNiina
c34001725c Merge branch 'workaround-gcc-48x' 2020-02-20 11:59:49 +09:00
ToruNiina
5e3ffb70dd fix: check clang macro when checking gcc is used 2020-02-19 17:00:22 +09:00
ToruNiina
2265ca41c6 ci: test with gcc 4.8 and 4.9 on CI 2020-02-19 15:47:34 +09:00
ToruNiina
82fec38e37 refactor: simplify internally-used function 2020-02-19 15:46:25 +09:00
ToruNiina
189b910384 fix: solve #97 in the naivest way, macros 2020-02-19 15:44:38 +09:00
Jordan Williams
fe644ea4b7 no longer explicitly clone the TOML repository in CI builds 2020-02-18 20:21:02 -06:00
Jordan Williams
4c34986db0 use FetchContent to retrieve TOML test data 2020-02-18 19:37:28 -06:00
ToruNiina
ac1130f9f4 tag: update patch version v3.3.1 2020-02-16 22:02:40 +09:00
ToruNiina
d290c3b7e5 doc: add contributor to README 2020-02-14 19:25:41 +09:00
Toru Niina
e4140ac1fd Merge pull request #102 from jwillikers/cmake_cache_variables
Set CMake Standard Cache Variables. Fixes #101
2020-02-13 13:22:25 +09:00
Jordan Williams
ef33c10ba8 use cache variables for the CMake standard and extensions settings 2020-02-12 07:44:47 -06:00
Toru Niina
ced710bb4c Merge pull request #100 from jwillikers/clang_warnings
Silence Clang Warnings. Fixes #98 & #99
2020-02-12 13:37:58 +09:00
Jordan Williams
6b5944e839 fix -Wundef warnings 2020-02-11 06:30:18 -06:00
Jordan Williams
76cae8c057 enable -Wundef flag for tests 2020-02-11 06:25:10 -06:00
Jordan Williams
3930a44ccd enable range-loop-analysis flag for tests 2020-02-11 06:17:54 -06:00
Jordan Williams
3b6417de00 fix clang range-loop-analysis warnings 2020-02-11 06:13:55 -06:00
ToruNiina
573a6f1d81 test: use JSON format_key to format a key in JSON
The rule to format a basic string and a key is different. `"""` cannot
be used with JSON keys. Also, toml key is basically not wrapped by `"`.
So we need a function to format a key in JSON.
2020-02-06 01:28:37 +09:00
ToruNiina
f6a41d986c feat: handle quotes in strings in the better way
- if a basic string contains any double quote, make it multiline.
  - because 1 or 2 consecutive "s do not require escape sequence in it.
- if a basic string will be sufficiently long, make it multiline.
- if 3 consecutive "s appeared, insert backslash to break it down.
2020-02-05 22:42:10 +09:00
ToruNiina
16fc172b21 feat: check string length before adding newline
In literal strings, only the first newline will be trimmed.
```toml
str = '''
The first newline will be trimmed.'''
```
The previous code always adds this first-newline, but after this commit
it checks the length of the string and adds newline if the string is
sufficiently long.
2020-02-05 22:39:08 +09:00
ToruNiina
7d03eb489a test: add test cases with quotes in ml-string
some of these example strings are copied from toml-lang/toml:README.md
and some are modified.
2020-02-04 22:37:11 +09:00
ToruNiina
0582e1535b fix: handle edge-cases with quotes in ml-string
See comments in the code for detail.
2020-02-04 22:36:39 +09:00
ToruNiina
d495df93a6 refactor: remove trailing whitespaces 2020-02-04 22:21:37 +09:00
ToruNiina
5ca3a3c262 refactor: change ifdef UNRELEASED_FEATURE region
to eliminate dead code after returning from a function
2020-02-04 21:05:03 +09:00
16 changed files with 742 additions and 487 deletions

View File

@@ -2,6 +2,30 @@ dist: trusty
matrix:
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
language: cpp
compiler: gcc
@@ -269,16 +293,6 @@ matrix:
- os: osx
language: cpp
compiler: clang
env: CXX_STANDARD=11
cache:
directories:
- $HOME/Library/Caches/Homebrew
addons:
homebrew:
update: true
packages:
- cmake
- boost
script:
- |
@@ -288,6 +302,10 @@ script:
tar xf cmake-3.14.5-Linux-x86_64.tar.gz -C cmake --strip-components=1
export PATH=${TRAVIS_BUILD_DIR}/cmake/bin:${PATH}
fi
- |
if [[ "${CXX_STANDARD}" == "" ]]; then
export CXX_STANDARD="11"
fi
- |
if [[ "${TOML_HEAD}" != "ON" ]]; then
export TOML_HEAD="OFF"
@@ -306,14 +324,8 @@ script:
- cmake --version
- mkdir build
- cd build
- git clone https://github.com/toml-lang/toml.git
- echo "COMPILER = ${COMPILER}"
- 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} ..
- make
- ctest --output-on-failure
# https://stackoverflow.com/a/53331571
before_cache:
- |
if [[ "${TRAVIS_OS_NAME}" == "osx" ]]; then
brew cleanup
fi

View File

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

View File

@@ -8,16 +8,17 @@ toml11
[![License](https://img.shields.io/github/license/ToruNiina/toml11.svg?style=flat)](LICENSE)
[![DOI](https://zenodo.org/badge/DOI/10.5281/zenodo.1209136.svg)](https://doi.org/10.5281/zenodo.1209136)
toml11 is a C++11 header-only toml parser/encoder depending only on C++ standard library.
toml11 is a C++11 (or later) header-only toml parser/encoder depending only on C++ standard library.
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)
after version 2.0.0.
It passes [the language agnostic test suite for TOML parsers by BurntSushi](https://github.com/BurntSushi/toml-test).
Not only the test suite itself, a TOML reader/encoder also runs on [CircleCI](https://circleci.com/gh/ToruNiina/toml11).
You can see the error messages about invalid files and serialization results of valid files at
[CircleCI](https://circleci.com/gh/ToruNiina/toml11).
- 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).
- It optionally supports the [unreleased features](#unreleased-toml-features) in the master branch of toml-lang/toml.
- 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).
- 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 optionally preserves comments without any overhead.
- It has configurable serializer that supports comments, inline tables, literal strings and multiline strings.
- It supports user-defined type conversion from/into toml values.
- It correctly handles UTF-8 sequences, with or without BOM, both on posix and Windows.
## Example
@@ -27,15 +28,25 @@ You can see the error messages about invalid files and serialization results of
int main()
{
const auto data = toml::parse("example.toml");
auto data = toml::parse("example.toml");
// title = "an example toml file"
// find a value with the specified type from a table
std::string title = toml::find<std::string>(data, "title");
std::cout << "the title is " << title << std::endl;
// nums = [1, 2, 3, 4, 5]
std::vector<int> nums = toml::find<std::vector<int>>(data, "nums");
std::cout << "the length of `nums` is" << nums.size() << std::endl;
// convert the whole array into any container automatically
std::vector<int> nums = toml::find<std::vector<int>>(data, "nums");
// 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;
}
@@ -1413,7 +1424,7 @@ const toml::source_location loc = v.location();
## Exceptions
All the exceptions thrown by toml11 inherits `toml::exception` that inherits
The following `exception` classes inherits `toml::exception` that inherits
`std::exception`.
```cpp
@@ -1440,6 +1451,16 @@ struct exception : public std::exception
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
By defining `TOML11_COLORIZE_ERROR_MESSAGE`, the error messages from
@@ -1743,13 +1764,12 @@ Such a big change will not happen in the coming years.
## Running Tests
To run test codes, you need to clone toml-lang/toml repository under `build/` directory
because some of the test codes read a file in the repository.
After cloning this repository, run the following command (thank you @jwillikers
for automating test set fetching!).
```sh
$ mkdir build
$ cd build
$ git clone https://github.com/toml-lang/toml.git
$ cmake ..
$ make
$ make test
@@ -1783,6 +1803,11 @@ I appreciate the help of the contributors who introduced the great feature to th
- Suppress warnings in Debug mode
- OGAWA Kenichi (@kenichiice)
- 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

View File

@@ -17,10 +17,9 @@ build_script:
- cd C:\toml11
- mkdir 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 --build . --config "%configuration%"
- file --mime-encoding tests/toml/tests/hard_example_unicode.toml
test_script:
- ctest --build-config "%configuration%" --timeout 300 --output-on-failure

View File

@@ -1,3 +1,12 @@
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
test_datetime
test_string
@@ -48,6 +57,8 @@ CHECK_CXX_COMPILER_FLAG("-Wduplicated-branches" COMPILER_SUPPORTS_WDUPLICATED_BR
CHECK_CXX_COMPILER_FLAG("-Wlogical-op" COMPILER_SUPPORTS_WLOGICAL_OP)
CHECK_CXX_COMPILER_FLAG("-Wuseless-cast" COMPILER_SUPPORTS_WUSELESS_CAST)
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)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall")
@@ -82,6 +93,12 @@ endif()
if(COMPILER_SUPPORTS_WDOUBLE_PROMOTION)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wdouble-promotion")
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
"use features in toml-lang/toml master while testing" OFF)
@@ -150,7 +167,7 @@ foreach(TEST_NAME ${TEST_NAMES})
endif()
endif()
add_test(NAME ${TEST_NAME} COMMAND ${TEST_NAME} WORKING_DIRECTORY ${PROJECT_BINARY_DIR})
add_test(NAME ${TEST_NAME} COMMAND ${TEST_NAME} WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR})
# Set the PATH to be able to find Boost DLL
if(WIN32)

View File

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

View File

@@ -57,6 +57,22 @@ BOOST_AUTO_TEST_CASE(test_ml_basic_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 \"\"\"");
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)
@@ -83,4 +99,16 @@ BOOST_AUTO_TEST_CASE(test_ml_literal_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'''");
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)
{
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\"",
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\'",
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\"\"\"",
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'''",
toml::value("The quick brown fox \njumps over the lazy dog", string_t::literal));
}
@@ -69,14 +69,17 @@ BOOST_AUTO_TEST_CASE(test_basic_string)
TOML11_TEST_PARSE_EQUAL(parse_string,
"\" And when \\\"'s are in the along with # \\\"\"",
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)
{
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.\"",
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\"",
value("192.168.1.1", string_t::basic));
#if defined(_MSC_VER) || defined(__INTEL_COMPILER)
@@ -84,16 +87,19 @@ BOOST_AUTO_TEST_CASE(test_basic_string_value)
"\"\xE4\xB8\xAD\xE5\x9B\xBD\"",
value("\xE4\xB8\xAD\xE5\x9B\xBD", string_t::basic));
#else
TOML11_TEST_PARSE_EQUAL_VALUE(parse_value<toml::value>,
TOML11_TEST_PARSE_EQUAL_VALUE(parse_value<toml::value>,
"\"中国\"",
value("中国", string_t::basic));
#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 - #\"",
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 # \\\"\"",
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)
@@ -104,16 +110,41 @@ BOOST_AUTO_TEST_CASE(test_ml_basic_string)
TOML11_TEST_PARSE_EQUAL(parse_string,
"\"\"\"\\\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));
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)
{
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.\"\"\"",
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 \"\"\"",
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)
@@ -134,16 +165,16 @@ BOOST_AUTO_TEST_CASE(test_literal_string)
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'",
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\\'",
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'",
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*>'",
value("<\\i\\c*\\s*>", string_t::literal));
}
@@ -156,16 +187,34 @@ BOOST_AUTO_TEST_CASE(test_ml_literal_string)
TOML11_TEST_PARSE_EQUAL(parse_string,
"'''\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));
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)
{
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'''",
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'''",
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)

View File

@@ -81,6 +81,54 @@ struct preserve_comments
void assign(std::initializer_list<std::string> ini) {comments.assign(ini);}
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)
{
return comments.insert(p, x);
@@ -114,6 +162,7 @@ struct preserve_comments
{
return comments.erase(first, last);
}
#endif
void swap(preserve_comments& other) {comments.swap(other.comments);}

View File

@@ -20,7 +20,7 @@ namespace toml
namespace detail
{
// TODO: find more sophisticated way to handle this
#if _POSIX_C_SOURCE >= 1 || defined(_XOPEN_SOURCE) || defined(_BSD_SOURCE) || defined(_SVID_SOURCE) || defined(_POSIX_SOURCE)
#if (defined(_POSIX_C_SOURCE) && _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)
{
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."); }
return dst;
}
#elif _MSC_VER
#elif defined(_MSC_VER)
inline std::tm localtime_s(const std::time_t* src)
{
std::tm dst;

View File

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

View File

@@ -154,12 +154,53 @@ using lex_basic_string = sequence<lex_quotation_mark,
repeat<lex_basic_char, unlimited>,
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_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
using lex_ml_basic_unescaped = exclude<either<in_range<0x00, 0x08>, // 0x09
in_range<0x0a, 0x1F>, // is tab
character<0x5C>,
character<0x7F>,
character<0x5C>, // backslash
character<0x7F>, // DEL
lex_ml_basic_string_delim>>;
#else // TOML v0.5.0
using lex_ml_basic_unescaped = exclude<either<in_range<0x00,0x1F>,
@@ -176,9 +217,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,
lex_ml_basic_escaped_newline>,
unlimited>;
using lex_ml_basic_string = sequence<lex_ml_basic_string_delim,
using lex_ml_basic_string = sequence<lex_ml_basic_string_open,
lex_ml_basic_body,
lex_ml_basic_string_delim>;
lex_ml_basic_string_close>;
using lex_literal_char = exclude<either<in_range<0x00, 0x08>,
in_range<0x10, 0x19>, character<0x27>>>;
@@ -187,7 +228,13 @@ using lex_literal_string = sequence<lex_apostrophe,
repeat<lex_literal_char, unlimited>,
lex_apostrophe>;
// the same reason as above.
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>,
in_range<0x10, 0x1F>,
@@ -195,9 +242,9 @@ using lex_ml_literal_char = exclude<either<in_range<0x00, 0x08>,
lex_ml_literal_string_delim>>;
using lex_ml_literal_body = repeat<either<lex_ml_literal_char, lex_newline>,
unlimited>;
using lex_ml_literal_string = sequence<lex_ml_literal_string_delim,
using lex_ml_literal_string = sequence<lex_ml_literal_string_open,
lex_ml_literal_body,
lex_ml_literal_string_delim>;
lex_ml_literal_string_close>;
using lex_string = either<lex_ml_basic_string, lex_basic_string,
lex_ml_literal_string, lex_literal_string>;

View File

@@ -375,7 +375,7 @@ parse_ml_basic_string(location<Container>& loc)
std::string retval;
retval.reserve(token.unwrap().size());
auto delim = lex_ml_basic_string_delim::invoke(inner_loc);
auto delim = lex_ml_basic_string_open::invoke(inner_loc);
if(!delim)
{
throw internal_error(format_underline(
@@ -410,7 +410,26 @@ parse_ml_basic_string(location<Container>& loc)
{{std::addressof(inner_loc), "not sufficient token"}}),
source_location(std::addressof(inner_loc)));
}
delim = lex_ml_basic_string_delim::invoke(inner_loc);
delim = lex_ml_basic_string_close::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()));
}
@@ -485,7 +504,7 @@ parse_ml_literal_string(location<Container>& loc)
{
location<std::string> inner_loc(loc.name(), token.unwrap().str());
const auto open = lex_ml_literal_string_delim::invoke(inner_loc);
const auto open = lex_ml_literal_string_open::invoke(inner_loc);
if(!open)
{
throw internal_error(format_underline(
@@ -498,7 +517,7 @@ parse_ml_literal_string(location<Container>& loc)
const auto body = lex_ml_literal_body::invoke(inner_loc);
const auto close = lex_ml_literal_string_delim::invoke(inner_loc);
const auto close = lex_ml_literal_string_close::invoke(inner_loc);
if(!close)
{
throw internal_error(format_underline(
@@ -506,9 +525,29 @@ parse_ml_literal_string(location<Container>& loc)
{{std::addressof(inner_loc), "should be '''"}}),
source_location(std::addressof(inner_loc)));
}
return ok(std::make_pair(
toml::string(body.unwrap().str(), toml::string_t::literal),
token.unwrap()));
// `lex_ml_literal_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`.
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
{

View File

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

View File

@@ -22,39 +22,62 @@ namespace toml
// 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
// a `"` and escaping some special character is boring.
inline std::string format_key(const toml::key& key)
template<typename charT, typename traits, typename Alloc>
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::lex_unquoted_key::invoke(loc);
if(loc.iter() == loc.end())
{
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)
{
switch(c)
{
case '\\': {token += "\\\\"; break;}
case '\"': {token += "\\\""; break;}
case '\b': {token += "\\b"; break;}
case '\t': {token += "\\t"; break;}
case '\f': {token += "\\f"; break;}
case '\n': {token += "\\n"; break;}
case '\r': {token += "\\r"; break;}
default : {token += c; break;}
case '\\': {serialized += "\\\\"; break;}
case '\"': {serialized += "\\\""; break;}
case '\b': {serialized += "\\b"; break;}
case '\t': {serialized += "\\t"; break;}
case '\f': {serialized += "\\f"; break;}
case '\n': {serialized += "\\n"; break;}
case '\r': {serialized += "\\r"; break;}
default : {serialized += c; break;}
}
}
token += "\"";
return token;
serialized += "\"";
return serialized;
}
template<typename Comment,
template<typename ...> class Table,
template<typename ...> class Array>
template<typename charT, typename traits, typename Alloc>
std::basic_string<charT, traits, Alloc>
format_keys(const std::vector<std::basic_string<charT, traits, Alloc>>& keys)
{
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
{
using value_type = basic_value<Comment, Table, Array>;
static_assert(detail::is_basic_value<Value>::value,
"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 comment_type = typename value_type::comment_type ;
using boolean_type = typename value_type::boolean_type ;
@@ -119,12 +142,17 @@ struct serializer
return token; // there is no exponent part. just return it.
}
#ifdef TOML11_USE_UNRELEASED_TOML_FEATURES
// Although currently it is not released yet, TOML will allow
// zero-prefix in an exponent part such as 1.234e+01.
// The following code removes the zero prefixes.
// If the feature is activated, the following codes can be skipped.
// Although currently it is not released yet as a tagged version,
// TOML will allow zero-prefix in an exponent part, such as `1.234e+01`.
// ```toml
// num1 = 1.234e+1 # OK in TOML v0.5.0
// 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;
#endif
#else
// zero-prefix in an exponent is NOT allowed in TOML v0.5.0.
// remove it if it exists.
bool sign_exists = false;
@@ -143,17 +171,29 @@ struct serializer
zero_prefix);
}
return token;
#endif
}
std::string operator()(const string_type& s) const
{
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 is contained, make it multiline-string.
const std::string open("\"\"\"\n");
const std::string close("\\\n\"\"\"");
return open + this->escape_ml_basic_string(s.str) + close;
// if linefeed or double-quote is contained,
// make it multiline basic string.
const auto escaped = this->escape_ml_basic_string(s.str);
std::string open("\"\"\"");
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.
@@ -194,7 +234,11 @@ struct serializer
if(std::find(s.str.cbegin(), s.str.cend(), '\n') != s.str.cend() ||
std::find(s.str.cbegin(), s.str.cend(), '\'') != s.str.cend() )
{
const std::string open("'''\n");
std::string open("'''");
if(this->width_ + 6 < s.str.size())
{
open += '\n'; // the first newline is ignored by TOML spec
}
const std::string close("'''");
return open + s.str + close;
}
@@ -249,7 +293,7 @@ struct serializer
std::string token;
if(!keys_.empty())
{
token += this->serialize_key(keys_.back());
token += format_key(keys_.back());
token += " = ";
}
bool failed = false;
@@ -305,7 +349,7 @@ struct serializer
}
}
token += "[[";
token += this->serialize_dotted_key(keys_);
token += format_keys(keys_);
token += "]]\n";
token += this->make_multiline_table(item.as_table());
}
@@ -417,7 +461,7 @@ struct serializer
std::string token;
if(!this->keys_.empty())
{
token += this->serialize_key(this->keys_.back());
token += format_key(this->keys_.back());
token += " = ";
}
token += this->make_inline_table(v);
@@ -432,7 +476,7 @@ struct serializer
if(!keys_.empty())
{
token += '[';
token += this->serialize_dotted_key(keys_);
token += format_keys(keys_);
token += "]\n";
}
token += this->make_multiline_table(v);
@@ -441,25 +485,6 @@ struct serializer
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
{
//XXX assuming `s` is a valid utf-8 sequence.
@@ -489,7 +514,9 @@ struct serializer
switch(*i)
{
case '\\': {retval += "\\\\"; break;}
case '\"': {retval += "\\\""; break;}
// One or two consecutive "s are allowed.
// Later we will check there are no three consecutive "s.
// case '\"': {retval += "\\\""; break;}
case '\b': {retval += "\\b"; break;}
case '\t': {retval += "\\t"; break;}
case '\f': {retval += "\\f"; break;}
@@ -510,6 +537,23 @@ struct serializer
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;
}
@@ -564,7 +608,7 @@ struct serializer
{
// in inline tables, trailing comma is not allowed (toml-lang #569).
if(is_first) {is_first = false;} else {token += ',';}
token += this->serialize_key(kv.first);
token += format_key(kv.first);
token += '=';
token += visit(serializer(std::numeric_limits<std::size_t>::max(),
this->float_prec_, true), kv.second);
@@ -579,7 +623,7 @@ struct serializer
// print non-table stuff first. because after printing [foo.bar], the
// 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))
{
@@ -595,7 +639,7 @@ struct serializer
token += '\n';
}
}
const auto key_and_sep = this->serialize_key(kv.first) + " = ";
const auto key_and_sep = format_key(kv.first) + " = ";
const auto residual_width = (this->width_ > key_and_sep.size()) ?
this->width_ - key_and_sep.size() : 0;
token += key_and_sep;
@@ -677,6 +721,7 @@ format(const basic_value<C, M, V>& v, std::size_t w = 80u,
int fprec = std::numeric_limits<toml::floating>::max_digits10,
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.
// the root object can't be an inline table.
if(v.is_table())
@@ -687,10 +732,10 @@ format(const basic_value<C, M, V>& v, std::size_t w = 80u,
oss << v.comments();
oss << '\n'; // to split the file comment from the first element
}
oss << visit(serializer<C, M, V>(w, fprec, no_comment, false), v);
oss << visit(serializer<value_type>(w, fprec, no_comment, false), v);
return oss.str();
}
return visit(serializer<C, M, V>(w, fprec, force_inline), v);
return visit(serializer<value_type>(w, fprec, force_inline), v);
}
namespace detail
@@ -726,6 +771,8 @@ template<typename charT, typename traits, typename C,
std::basic_ostream<charT, traits>&
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().
const auto w = static_cast<std::size_t>(os.width());
const int fprec = static_cast<int>(os.precision());
@@ -741,7 +788,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
}
// the root object can't be an inline table. so pass `false`.
os << visit(serializer<C, M, V>(w, fprec, false, no_comment), v);
os << visit(serializer<value_type>(w, fprec, false, no_comment), v);
// if v is a non-table value, and has only one comment, then
// put a comment just after a value. in the following way.

View File

@@ -20,223 +20,145 @@ namespace detail
{
// to show error messages. not recommended for users.
template<typename C, template<typename ...> class T, template<typename ...> class A>
region_base const& get_region(const basic_value<C, T, A>&);
template<typename Region,
typename C, template<typename ...> class T, template<typename ...> class A>
void change_region(basic_value<C, T, A>&, Region&&);
template<value_t Expected,
typename C, template<typename ...> class T, template<typename ...> class A>
[[noreturn]] inline void
throw_bad_cast(value_t actual, const ::toml::basic_value<C, T, A>& v)
template<typename Value>
inline region_base const& get_region(const Value& v)
{
throw type_error(detail::format_underline(concat_to_string(
"toml::value: bad_cast to ", Expected), {
return *(v.region_info_);
}
template<typename Value, typename Region>
void change_region(Value& 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;
}
template<value_t Expected, typename Value>
[[noreturn]] inline void
throw_bad_cast(const std::string& funcname, value_t actual, const Value& v)
{
throw type_error(detail::format_underline(
concat_to_string(funcname, "bad_cast to ", Expected), {
{std::addressof(get_region(v)),
concat_to_string("the actual type is ", actual)}
}), v.location());
}
// switch by `value_t` and call the corresponding `value::as_xxx()`. {{{
// Throw `out_of_range` from `toml::value::at()` and `toml::find()`
// 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>
struct switch_cast {};
template<>
struct switch_cast<value_t::boolean>
{
template<typename C, template<typename ...> class T, template<typename ...> class A>
static ::toml::boolean& invoke(basic_value<C, T, A>& v) noexcept
{
return v.as_boolean();
}
template<typename C, template<typename ...> class T, template<typename ...> class A>
static ::toml::boolean const& invoke(basic_value<C, T, A> const& v) noexcept
{
return v.as_boolean();
}
template<typename C, template<typename ...> class T, template<typename ...> class A>
static ::toml::boolean&& invoke(basic_value<C, T, A>&& v) noexcept
{
return std::move(v).as_boolean();
}
};
template<>
struct switch_cast<value_t::integer>
{
template<typename C, template<typename ...> class T, template<typename ...> class A>
static ::toml::integer& invoke(basic_value<C, T, A>& v) noexcept
{
return v.as_integer();
}
template<typename C, template<typename ...> class T, template<typename ...> class A>
static ::toml::integer const& invoke(basic_value<C, T, A> const& v) noexcept
{
return v.as_integer();
}
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();
}
}; // }}}
#define TOML11_GENERATE_SWITCH_CASTER(TYPE) \
template<> \
struct switch_cast<value_t::TYPE> \
{ \
template<typename Value> \
static typename Value::TYPE##_type& invoke(Value& v) \
{ \
return v.as_##TYPE(); \
} \
template<typename Value> \
static typename Value::TYPE##_type const& invoke(const Value& v) \
{ \
return v.as_##TYPE(); \
} \
template<typename Value> \
static typename Value::TYPE##_type&& invoke(Value&& v) \
{ \
return std::move(v).as_##TYPE(); \
} \
}; \
/**/
TOML11_GENERATE_SWITCH_CASTER(boolean)
TOML11_GENERATE_SWITCH_CASTER(integer)
TOML11_GENERATE_SWITCH_CASTER(floating)
TOML11_GENERATE_SWITCH_CASTER(string)
TOML11_GENERATE_SWITCH_CASTER(offset_datetime)
TOML11_GENERATE_SWITCH_CASTER(local_datetime)
TOML11_GENERATE_SWITCH_CASTER(local_date)
TOML11_GENERATE_SWITCH_CASTER(local_time)
TOML11_GENERATE_SWITCH_CASTER(array)
TOML11_GENERATE_SWITCH_CASTER(table)
#undef TOML11_GENERATE_SWITCH_CASTER
}// detail
@@ -1255,7 +1177,7 @@ class basic_value
{
if(this->type_ != T)
{
detail::throw_bad_cast<T>(this->type_, *this);
detail::throw_bad_cast<T>("toml::value::cast: ", this->type_, *this);
}
return detail::switch_cast<T>::invoke(*this);
}
@@ -1264,7 +1186,7 @@ class basic_value
{
if(this->type_ != T)
{
detail::throw_bad_cast<T>(this->type_, *this);
detail::throw_bad_cast<T>("toml::value::cast: ", this->type_, *this);
}
return detail::switch_cast<T>::invoke(*this);
}
@@ -1273,7 +1195,7 @@ class basic_value
{
if(this->type_ != T)
{
detail::throw_bad_cast<T>(this->type_, *this);
detail::throw_bad_cast<T>("toml::value::cast: ", this->type_, *this);
}
return detail::switch_cast<T>::invoke(std::move(*this));
}
@@ -1317,13 +1239,14 @@ class basic_value
// ========================================================================
// throw version
// ------------------------------------------------------------------------
// const reference
// const reference {{{
boolean const& as_boolean() const&
{
if(this->type_ != value_t::boolean)
{
detail::throw_bad_cast<value_t::boolean>(this->type_, *this);
detail::throw_bad_cast<value_t::boolean>(
"toml::value::as_boolean(): ", this->type_, *this);
}
return this->boolean_;
}
@@ -1331,7 +1254,8 @@ class basic_value
{
if(this->type_ != value_t::integer)
{
detail::throw_bad_cast<value_t::integer>(this->type_, *this);
detail::throw_bad_cast<value_t::integer>(
"toml::value::as_integer(): ", this->type_, *this);
}
return this->integer_;
}
@@ -1339,7 +1263,8 @@ class basic_value
{
if(this->type_ != value_t::floating)
{
detail::throw_bad_cast<value_t::floating>(this->type_, *this);
detail::throw_bad_cast<value_t::floating>(
"toml::value::as_floating(): ", this->type_, *this);
}
return this->floating_;
}
@@ -1347,7 +1272,8 @@ class basic_value
{
if(this->type_ != value_t::string)
{
detail::throw_bad_cast<value_t::string>(this->type_, *this);
detail::throw_bad_cast<value_t::string>(
"toml::value::as_string(): ", this->type_, *this);
}
return this->string_;
}
@@ -1355,7 +1281,8 @@ class basic_value
{
if(this->type_ != value_t::offset_datetime)
{
detail::throw_bad_cast<value_t::offset_datetime>(this->type_, *this);
detail::throw_bad_cast<value_t::offset_datetime>(
"toml::value::as_offset_datetime(): ", this->type_, *this);
}
return this->offset_datetime_;
}
@@ -1363,7 +1290,8 @@ class basic_value
{
if(this->type_ != value_t::local_datetime)
{
detail::throw_bad_cast<value_t::local_datetime>(this->type_, *this);
detail::throw_bad_cast<value_t::local_datetime>(
"toml::value::as_local_datetime(): ", this->type_, *this);
}
return this->local_datetime_;
}
@@ -1371,7 +1299,8 @@ class basic_value
{
if(this->type_ != value_t::local_date)
{
detail::throw_bad_cast<value_t::local_date>(this->type_, *this);
detail::throw_bad_cast<value_t::local_date>(
"toml::value::as_local_date(): ", this->type_, *this);
}
return this->local_date_;
}
@@ -1379,7 +1308,8 @@ class basic_value
{
if(this->type_ != value_t::local_time)
{
detail::throw_bad_cast<value_t::local_time>(this->type_, *this);
detail::throw_bad_cast<value_t::local_time>(
"toml::value::as_local_time(): ", this->type_, *this);
}
return this->local_time_;
}
@@ -1387,7 +1317,8 @@ class basic_value
{
if(this->type_ != value_t::array)
{
detail::throw_bad_cast<value_t::array>(this->type_, *this);
detail::throw_bad_cast<value_t::array>(
"toml::value::as_array(): ", this->type_, *this);
}
return this->array_.value();
}
@@ -1395,19 +1326,21 @@ class basic_value
{
if(this->type_ != value_t::table)
{
detail::throw_bad_cast<value_t::table>(this->type_, *this);
detail::throw_bad_cast<value_t::table>(
"toml::value::as_table(): ", this->type_, *this);
}
return this->table_.value();
}
// }}}
// ------------------------------------------------------------------------
// nonconst reference
// nonconst reference {{{
boolean & as_boolean() &
{
if(this->type_ != value_t::boolean)
{
detail::throw_bad_cast<value_t::boolean>(this->type_, *this);
detail::throw_bad_cast<value_t::boolean>(
"toml::value::as_boolean(): ", this->type_, *this);
}
return this->boolean_;
}
@@ -1415,7 +1348,8 @@ class basic_value
{
if(this->type_ != value_t::integer)
{
detail::throw_bad_cast<value_t::integer>(this->type_, *this);
detail::throw_bad_cast<value_t::integer>(
"toml::value::as_integer(): ", this->type_, *this);
}
return this->integer_;
}
@@ -1423,7 +1357,8 @@ class basic_value
{
if(this->type_ != value_t::floating)
{
detail::throw_bad_cast<value_t::floating>(this->type_, *this);
detail::throw_bad_cast<value_t::floating>(
"toml::value::as_floating(): ", this->type_, *this);
}
return this->floating_;
}
@@ -1431,7 +1366,8 @@ class basic_value
{
if(this->type_ != value_t::string)
{
detail::throw_bad_cast<value_t::string>(this->type_, *this);
detail::throw_bad_cast<value_t::string>(
"toml::value::as_string(): ", this->type_, *this);
}
return this->string_;
}
@@ -1439,7 +1375,8 @@ class basic_value
{
if(this->type_ != value_t::offset_datetime)
{
detail::throw_bad_cast<value_t::offset_datetime>(this->type_, *this);
detail::throw_bad_cast<value_t::offset_datetime>(
"toml::value::as_offset_datetime(): ", this->type_, *this);
}
return this->offset_datetime_;
}
@@ -1447,7 +1384,8 @@ class basic_value
{
if(this->type_ != value_t::local_datetime)
{
detail::throw_bad_cast<value_t::local_datetime>(this->type_, *this);
detail::throw_bad_cast<value_t::local_datetime>(
"toml::value::as_local_datetime(): ", this->type_, *this);
}
return this->local_datetime_;
}
@@ -1455,7 +1393,8 @@ class basic_value
{
if(this->type_ != value_t::local_date)
{
detail::throw_bad_cast<value_t::local_date>(this->type_, *this);
detail::throw_bad_cast<value_t::local_date>(
"toml::value::as_local_date(): ", this->type_, *this);
}
return this->local_date_;
}
@@ -1463,7 +1402,8 @@ class basic_value
{
if(this->type_ != value_t::local_time)
{
detail::throw_bad_cast<value_t::local_time>(this->type_, *this);
detail::throw_bad_cast<value_t::local_time>(
"toml::value::as_local_time(): ", this->type_, *this);
}
return this->local_time_;
}
@@ -1471,7 +1411,8 @@ class basic_value
{
if(this->type_ != value_t::array)
{
detail::throw_bad_cast<value_t::array>(this->type_, *this);
detail::throw_bad_cast<value_t::array>(
"toml::value::as_array(): ", this->type_, *this);
}
return this->array_.value();
}
@@ -1479,19 +1420,22 @@ class basic_value
{
if(this->type_ != value_t::table)
{
detail::throw_bad_cast<value_t::table>(this->type_, *this);
detail::throw_bad_cast<value_t::table>(
"toml::value::as_table(): ", this->type_, *this);
}
return this->table_.value();
}
// }}}
// ------------------------------------------------------------------------
// rvalue reference
// rvalue reference {{{
boolean && as_boolean() &&
{
if(this->type_ != value_t::boolean)
{
detail::throw_bad_cast<value_t::boolean>(this->type_, *this);
detail::throw_bad_cast<value_t::boolean>(
"toml::value::as_boolean(): ", this->type_, *this);
}
return std::move(this->boolean_);
}
@@ -1499,7 +1443,8 @@ class basic_value
{
if(this->type_ != value_t::integer)
{
detail::throw_bad_cast<value_t::integer>(this->type_, *this);
detail::throw_bad_cast<value_t::integer>(
"toml::value::as_integer(): ", this->type_, *this);
}
return std::move(this->integer_);
}
@@ -1507,7 +1452,8 @@ class basic_value
{
if(this->type_ != value_t::floating)
{
detail::throw_bad_cast<value_t::floating>(this->type_, *this);
detail::throw_bad_cast<value_t::floating>(
"toml::value::as_floating(): ", this->type_, *this);
}
return std::move(this->floating_);
}
@@ -1515,7 +1461,8 @@ class basic_value
{
if(this->type_ != value_t::string)
{
detail::throw_bad_cast<value_t::string>(this->type_, *this);
detail::throw_bad_cast<value_t::string>(
"toml::value::as_string(): ", this->type_, *this);
}
return std::move(this->string_);
}
@@ -1523,7 +1470,8 @@ class basic_value
{
if(this->type_ != value_t::offset_datetime)
{
detail::throw_bad_cast<value_t::offset_datetime>(this->type_, *this);
detail::throw_bad_cast<value_t::offset_datetime>(
"toml::value::as_offset_datetime(): ", this->type_, *this);
}
return std::move(this->offset_datetime_);
}
@@ -1531,7 +1479,8 @@ class basic_value
{
if(this->type_ != value_t::local_datetime)
{
detail::throw_bad_cast<value_t::local_datetime>(this->type_, *this);
detail::throw_bad_cast<value_t::local_datetime>(
"toml::value::as_local_datetime(): ", this->type_, *this);
}
return std::move(this->local_datetime_);
}
@@ -1539,7 +1488,8 @@ class basic_value
{
if(this->type_ != value_t::local_date)
{
detail::throw_bad_cast<value_t::local_date>(this->type_, *this);
detail::throw_bad_cast<value_t::local_date>(
"toml::value::as_local_date(): ", this->type_, *this);
}
return std::move(this->local_date_);
}
@@ -1547,7 +1497,8 @@ class basic_value
{
if(this->type_ != value_t::local_time)
{
detail::throw_bad_cast<value_t::local_time>(this->type_, *this);
detail::throw_bad_cast<value_t::local_time>(
"toml::value::as_local_time(): ", this->type_, *this);
}
return std::move(this->local_time_);
}
@@ -1555,7 +1506,8 @@ class basic_value
{
if(this->type_ != value_t::array)
{
detail::throw_bad_cast<value_t::array>(this->type_, *this);
detail::throw_bad_cast<value_t::array>(
"toml::value::as_array(): ", this->type_, *this);
}
return std::move(this->array_.value());
}
@@ -1563,10 +1515,12 @@ class basic_value
{
if(this->type_ != value_t::table)
{
detail::throw_bad_cast<value_t::table>(this->type_, *this);
detail::throw_bad_cast<value_t::table>(
"toml::value::as_table(): ", this->type_, *this);
}
return std::move(this->table_.value());
}
// }}}
// accessors =============================================================
//
@@ -1574,11 +1528,29 @@ class basic_value
//
value_type& at(const key& k)
{
return this->as_table().at(k);
if(!this->is_table())
{
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
{
return this->as_table().at(k);
if(!this->is_table())
{
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)
{
@@ -1586,49 +1558,78 @@ class basic_value
{
*this = table_type{};
}
return this->as_table()[k];
else if(!this->is_table()) // initialized, but not a table
{
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)
{
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);
}
value_type const& at(const std::size_t idx) const
{
return this->as_array().at(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(std::nothrow).at(idx);
}
value_type& operator[](const std::size_t idx) noexcept
{
// no check...
return this->as_array(std::nothrow)[idx];
}
value_type const& operator[](const std::size_t idx) const noexcept
{
// no check...
return this->as_array(std::nothrow)[idx];
}
void push_back(const value_type& x)
{
if(this->type_ != value_t::array)
if(!this->is_array())
{
throw type_error(detail::format_underline(
"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());
detail::throw_bad_cast<value_t::array>(
"toml::value::push_back(value): ", this->type_, *this);
}
this->as_array(std::nothrow).push_back(x);
return;
}
void push_back(value_type&& x)
{
if(this->type_ != value_t::array)
if(!this->is_array())
{
throw type_error(detail::format_underline(
"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());
detail::throw_bad_cast<value_t::array>(
"toml::value::push_back(value): ", this->type_, *this);
}
this->as_array(std::nothrow).push_back(std::move(x));
return;
@@ -1637,13 +1638,10 @@ class basic_value
template<typename ... Ts>
value_type& emplace_back(Ts&& ... args)
{
if(this->type_ != value_t::array)
if(!this->is_array())
{
throw type_error(detail::format_underline(
"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());
detail::throw_bad_cast<value_t::array>(
"toml::value::emplace_back(...): ", this->type_, *this);
}
this->as_array(std::nothrow).emplace_back(std::forward<Ts>(args) ...);
return this->as_array(std::nothrow).back();
@@ -1655,15 +1653,15 @@ class basic_value
{
case value_t::array:
{
return this->as_array().size();
return this->as_array(std::nothrow).size();
}
case value_t::table:
{
return this->as_table().size();
return this->as_table(std::nothrow).size();
}
case value_t::string:
{
return this->as_string().str.size();
return this->as_string(std::nothrow).str.size();
}
default:
{
@@ -1678,28 +1676,22 @@ class basic_value
std::size_t count(const key_type& k) const
{
if(this->type_ != value_t::table)
if(!this->is_table())
{
throw type_error(detail::format_underline(
"toml::value::count(key): bad_cast to table type", {
{this->region_info_.get(),
concat_to_string("the actual type is ", this->type_)}
}), this->location());
detail::throw_bad_cast<value_t::table>(
"toml::value::count(key): ", this->type_, *this);
}
return this->as_table().count(k);
return this->as_table(std::nothrow).count(k);
}
bool contains(const key_type& k) const
{
if(this->type_ != value_t::table)
if(!this->is_table())
{
throw type_error(detail::format_underline(
"toml::value::contains(key): bad_cast to table type", {
{this->region_info_.get(),
concat_to_string("the actual type is ", this->type_)}
}), this->location());
detail::throw_bad_cast<value_t::table>(
"toml::value::contains(key): ", this->type_, *this);
}
return (this->as_table().count(k) != 0);
return (this->as_table(std::nothrow).count(k) != 0);
}
source_location location() const
@@ -1724,13 +1716,11 @@ class basic_value
}
// for error messages
template<typename C,
template<typename ...> class T, template<typename ...> class A>
friend region_base const& detail::get_region(const basic_value<C, T, A>&);
template<typename Value>
friend region_base const& detail::get_region(const Value& v);
template<typename Region, typename C,
template<typename ...> class T, template<typename ...> class A>
friend void detail::change_region(basic_value<C, T, A>&, Region&&);
template<typename Value, typename Region>
friend void detail::change_region(Value& v, Region&& reg);
private:
@@ -1760,30 +1750,6 @@ using value = basic_value<discard_comments, std::unordered_map, std::vector>;
using array = typename value::array_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>
inline bool
operator==(const basic_value<C, T, A>& lhs, const basic_value<C, T, A>& rhs)