Compare commits

...

74 Commits

Author SHA1 Message Date
ToruNiina
717f5929c2 feat: use detail::none_t instead of char
Although the error value from combinators currently does not have any
information, it can have an information because it is a char value. It
is better to use no-information-type explicitly to make it clear that
it does not have any information. So I added none_t in toml::detai and
use it in combinators and parsers as an error value from combinators.
2019-05-31 17:07:52 +09:00
ToruNiina
81abb6c9d7 perf: remove err-msg from combinator
Generate error message in `parse_something()`, not in `lex_something`.
Since the error message generated by `lex_something` is too difficult to
read for humans, I've disabled the error message generation for the sake
of efficiency (it takes time to generate error message that will never
be read). I think now the error message generation itself safely can be
removed from combinators. At this stage, `lex_something` does not need
to return `result<T, E>` because all the error type would be discarded.
Now it is turned out that returing `optional<T>` from lex_* is enough.
Maybe later I would change the return type itself, but currently I
changed the error type from std::string to char because implementing
optional takes time and effort. It makes the parsing process a bit
faster.
2019-05-30 20:08:37 +09:00
ToruNiina
8bba3c8a14 refactor: use literal instead of empty string
so far, the error value of the lexer is just ignored because they are
not readable (results from all the nested combinator are concatenated,
so they are too redundant). those ones are replaced by a simple literal.
2019-05-30 19:33:25 +09:00
ToruNiina
b13e727b90 refactor: remove unused func, combinator::pattern
because it is not human-readable (too long and redundant)
2019-05-30 18:05:47 +09:00
ToruNiina
d352c9e66f perf: suppress unused error message generation 2019-05-30 17:47:06 +09:00
ToruNiina
c0aaba06d0 Merge branch 'refactoring' 2019-05-30 16:25:10 +09:00
ToruNiina
1633268d57 refactor: use snake_case typename only 2019-05-30 14:39:15 +09:00
ToruNiina
3bf1c2b820 Merge branch 'refactoring' to master 2019-05-30 00:18:07 +09:00
ToruNiina
4dbd2cb9fe refactor: use as_* to avoid needless checking 2019-05-29 21:22:32 +09:00
ToruNiina
65124a8d2e refactor: use is_something instead of is(...)
to reduce the code size a bit
2019-05-29 21:20:22 +09:00
ToruNiina
1b78f161f5 refactor: use is_something/as_something in parser
this reduces the size of the code. And also it skips needless
double-checking, so we can expect it makes parsing a bit faster.
2019-05-29 21:18:17 +09:00
ToruNiina
0ce259ada0 refactor: split throw_bad_cast from value::cast 2019-05-29 21:06:25 +09:00
ToruNiina
74da49f87f refactor: move switch_cast from inside of value
use as_something() instead of it. To realize this, the implementation of
as_something() is also changed. Now as_something does not depends on
`cast`. This reduces complexity around casting toml::value to other types.
2019-05-29 20:18:15 +09:00
ToruNiina
d5d697639c docs: add contributor to README 2019-05-10 23:02:23 +09:00
Toru Niina
0b365ca7d3 Merge pull request #63 from chronoxor/master
Fix Visual Studio 2019 warnings in pedantic compilation mode (/W4 /WX)
2019-05-10 22:58:17 +09:00
Ivan Shynkarenka
db6f3d5d11 Fix Visual Studio 2019 warnings in pedantic compilation mode (/W4 /WX) 2019-05-10 14:58:22 +03:00
ToruNiina
87be890e07 feat: remove deprecated functions 2019-04-28 15:59:09 +09:00
Toru Niina
d72dc706d0 Merge pull request #61 from ToruNiina/as-something
feat: add as_something functions for convenience
2019-04-28 15:02:19 +09:00
ToruNiina
4cbbcd8f62 Merge branch 'master' into as-something 2019-04-27 19:04:44 +09:00
Toru Niina
a2631ecacb Merge pull request #60 from ToruNiina/string-view
support std::string_view
2019-04-27 18:33:59 +09:00
ToruNiina
4bcc5e8375 Merge branch 'master' into as-something 2019-04-27 17:42:12 +09:00
Toru Niina
90f84000ba Merge pull request #59 from ToruNiina/preserve-comments
Preserve comments; related to #48
2019-04-27 17:40:26 +09:00
ToruNiina
20a13754a7 chore: update README for as_* functions 2019-04-27 16:50:44 +09:00
ToruNiina
aa7b9a3965 refactor: rename as_floating -> as_float
Actually, since `floating` is used for toml::types, `as_floating`
seems to be clearer. But currently `is_*` functions uses `float`,
not `floating`, so `as_float` is chosen for the consistency.
In a future release, possibly v3, those names may need to be
re-considered for clarity.
2019-04-27 16:45:25 +09:00
ToruNiina
84ac1d10f3 test: add test for toml::value::as_something 2019-04-27 16:22:50 +09:00
ToruNiina
0d623856a7 feat: add value::as_something() for convenience 2019-04-27 16:22:23 +09:00
ToruNiina
ec0d4e4e8c chore: update README for comments 2019-04-27 15:50:54 +09:00
ToruNiina
80ea736b3f ci: try to update standard library on travis 2019-04-27 14:46:40 +09:00
ToruNiina
ebaa5dfb51 chore: fix build settings for OS X on Travis 2019-04-26 21:10:29 +09:00
ToruNiina
f3bdf083fe fix: fix typo in test code for string_view 2019-04-26 16:51:23 +09:00
ToruNiina
1ce54a9cf9 chore: add auto test with c++17 + latest compilers 2019-04-26 16:35:03 +09:00
ToruNiina
6383a93ce7 chore: check CXX_STANDARD exists or not 2019-04-26 16:33:48 +09:00
ToruNiina
01aa2ef5b2 feat: add ctor to value to init with string_view 2019-04-26 16:33:09 +09:00
ToruNiina
819351f5a4 test: add test for init toml::value by string_view 2019-04-26 16:32:23 +09:00
ToruNiina
2967cebfb3 test: add test to get a toml::value as string_view 2019-04-26 16:31:59 +09:00
ToruNiina
32e9a2c1c7 test: add test for comments in an array 2019-04-26 15:35:41 +09:00
ToruNiina
8e0a40a1aa test: add test for getting comments 2019-04-25 22:34:12 +09:00
ToruNiina
e460826084 feat: enable to get a comment related to a value
- comment_before(): get comments just before a value.
- comment_inline(): get a comment in the same line as a value.
- comment(): get comment_before() + comment_inline().
2019-04-25 22:32:39 +09:00
ToruNiina
aa3445f38c feat: add functions to get comments around region 2019-04-25 22:32:18 +09:00
ToruNiina
408b7bf35e Merge branch 'master' into string-view 2019-04-23 23:32:08 +09:00
ToruNiina
6185dfee14 chore: fix typo in README 2019-04-23 23:31:37 +09:00
ToruNiina
37aa2739a5 chore: add description about string_view to README 2019-04-23 23:27:53 +09:00
ToruNiina
d061c33a16 feat: enable toml::get with std::string_view 2019-04-23 23:24:23 +09:00
ToruNiina
0c7d2d07d4 feat: do not consider string_view as a container
it is a kind of string.
2019-04-23 23:23:57 +09:00
ToruNiina
62cf4373bd feat: conversion toml::string <-> string_view 2019-04-22 23:18:05 +09:00
Toru Niina
a74ad23514 Merge pull request #58 from ToruNiina/improve-err-msg-literal
Improve error message from toml literal
2019-04-22 20:50:11 +09:00
ToruNiina
2d9b4992ec fix: restrict length of underline by size of line
in some cases, `region` contains several lines and `region::size`
returns the whole size that is a sum of lengthes of all the lines.
To avoid too long underlines, restrict the length of underline by
the length of the line that is shown in the message.
2019-04-21 16:38:08 +09:00
ToruNiina
82e8c1e68b fix: skip first ws/newlines in toml literal
when ""_toml literal is used with C++11 raw-string literal,
it normally starts with newline like the following.
```cpp
const auto v = u8R"(
    [table]
    key = "value"
    )"_toml;
```
With this, the error message shows the first empty line that starts just
after `u8R"(` and thus the error message shows nothing. To avoid this,
skip the first empty lines and whitespaces in literal.
2019-04-21 16:31:24 +09:00
ToruNiina
46be054ce9 fix: improve err msg for multiline inline table
show "missing curly brace" instead of "missing table key-value separator"
2019-04-19 13:22:13 +09:00
ToruNiina
789d784769 chore: update README; about literals 2019-04-19 13:18:35 +09:00
ToruNiina
81deb8efde chore: update README 2019-04-19 12:41:24 +09:00
Toru Niina
072dccd05d Merge pull request #56 from ToruNiina/optimization
Optimization
2019-04-19 01:30:29 +09:00
ToruNiina
637c99d637 refactor: generate error message in parser 2019-04-18 15:09:58 +09:00
ToruNiina
0f48852730 perf: check value type before parsing
to avoid needless error message generation
2019-04-18 14:26:27 +09:00
ToruNiina
0499b2907d Merge branch 'master' into optimization 2019-04-18 14:10:08 +09:00
ToruNiina
61e69c9251 fix: count line number from 1, not 0 2019-04-18 13:56:19 +09:00
ToruNiina
4a560ea1e5 fix: show correct error message 2019-04-18 00:04:33 +09:00
ToruNiina
c5b6ee6f81 feat: add yet another constructor to value
to make implementation of parse_value easier
2019-04-17 23:43:42 +09:00
ToruNiina
1a7bf63622 Merge branch 'master' into optimization 2019-04-17 14:58:28 +09:00
Toru Niina
8847cdc0a9 Merge pull request #55 from wbenny/master
fix /W4 warnings on MSVC
2019-04-17 13:16:19 +09:00
ToruNiina
c82e76a111 perf: check string type before parsing it
to avoid unncessary error message generation, check the first some
characters before parsing it. It makes parsing process faster and
is also helpful to generate more accurate error messages.
2019-04-16 21:47:24 +09:00
ToruNiina
4db486d76d perf: check integer prefix before trying to parse
all the parsers generate error messages and error message generation is
not a lightweight task. It concatenates a lot of strings, it formats
many values, etc. To avoid useless error-message generation, first check
which prefix is used and then parse special integers. Additionally, by
checking that, the quality of the error message can be improved (later).
2019-04-16 21:37:12 +09:00
ToruNiina
91966a6917 perf: do not use concat_string if it is not needed
At the earlier stage of the development, I thought that it is useful if
lexer-combinators generate error messages, because by doing this,
parser would not need to generate an error message. But now it turned
out that to show an appropriate error message, parser need to generate
according to the context. And almost all the messages from lexer are
discarded. So I added another parameter to lexer-combinator to suppress
error message generation. In the future, we may want to remove messages
completely from lexers, but currently I will keep it. Removing those
unused message generation makes the parsing process faster.
2019-04-16 21:09:59 +09:00
ToruNiina
b3917aaadf refactor: use snprintf to show char in hex
instead of std::ostringstream.
2019-04-16 20:54:29 +09:00
Petr Benes
ba307003c4 fix /W4 warnings on MSVC 2019-04-16 13:25:45 +02:00
Toru Niina
21fd1271d9 Merge pull request #54 from ToruNiina/hotfix
fix: resolve ambiguity in the `""_toml` literal
2019-04-15 13:34:35 +09:00
ToruNiina
f9ab7d6f56 chore: add note about literals to README.md 2019-04-14 20:08:23 +09:00
ToruNiina
0a3a41a708 test: add test for literals for difficult case 2019-04-14 20:06:11 +09:00
ToruNiina
6c2a536fa5 fix: check literal has a table or an array first
The literal like this `"[[table]]"_toml` caused a syntax error. It is
because the literal parser first check that it might be a bare value
without a key, and parse_array directory throws syntax_error. This
change makes the parser first check a literal is a name of table, and
then parse the content.
2019-04-14 19:48:43 +09:00
Toru Niina
26eced3640 Merge pull request #52 from ToruNiina/speedup-for-large-files
Speedup parsing large files
2019-04-13 16:11:21 +09:00
ToruNiina
6f950c9ec8 perf: cache current line number in location
`location::line_num()` function used to be implemented by using
`std::count`, so each time the parser encounters a type mismatch,
`std::count` was called with almost whole file. It decelerates the
parsing process too much, so I decided to add `line_number_` member
variable to `location` and add `advance/retrace/reset` to `location`
in order to modify the position that is pointed.
2019-04-12 18:32:46 +09:00
ToruNiina
ea13e40889 feat: add static_assert for location/range
to check the container is randomly-accessible
2019-04-12 18:00:53 +09:00
ToruNiina
595fb1aef3 refactor: remove unused function parameter names 2019-04-06 19:39:13 +09:00
ToruNiina
18986978fb chore: add short example code to README 2019-03-24 21:30:27 +09:00
22 changed files with 1360 additions and 488 deletions

View File

@@ -5,7 +5,7 @@ matrix:
- os: linux - os: linux
language: cpp language: cpp
compiler: gcc compiler: gcc
env: COMPILER="g++-5" env: COMPILER="g++-5" CXX_STANDARD=11
addons: addons:
apt: apt:
sources: sources:
@@ -16,7 +16,7 @@ matrix:
- os: linux - os: linux
language: cpp language: cpp
compiler: gcc compiler: gcc
env: COMPILER="g++-6" env: COMPILER="g++-6" CXX_STANDARD=11
addons: addons:
apt: apt:
sources: sources:
@@ -27,7 +27,7 @@ matrix:
- os: linux - os: linux
language: cpp language: cpp
compiler: gcc compiler: gcc
env: COMPILER="g++-7" env: COMPILER="g++-7" CXX_STANDARD=11
addons: addons:
apt: apt:
sources: sources:
@@ -38,7 +38,18 @@ matrix:
- os: linux - os: linux
language: cpp language: cpp
compiler: gcc compiler: gcc
env: COMPILER="g++-8" env: COMPILER="g++-8" CXX_STANDARD=11
addons:
apt:
sources:
- ubuntu-toolchain-r-test
packages:
- g++-8
- libboost-all-dev
- os: linux
language: cpp
compiler: gcc
env: COMPILER="g++-8" CXX_STANDARD=17
addons: addons:
apt: apt:
sources: sources:
@@ -49,7 +60,7 @@ matrix:
- os: linux - os: linux
language: cpp language: cpp
compiler: clang compiler: clang
env: COMPILER="clang++-3.7" env: COMPILER="clang++-3.7" CXX_STANDARD=11
addons: addons:
apt: apt:
sources: sources:
@@ -61,7 +72,7 @@ matrix:
- os: linux - os: linux
language: cpp language: cpp
compiler: clang compiler: clang
env: COMPILER="clang++-4.0" env: COMPILER="clang++-4.0" CXX_STANDARD=11
addons: addons:
apt: apt:
sources: sources:
@@ -73,7 +84,7 @@ matrix:
- os: linux - os: linux
language: cpp language: cpp
compiler: clang compiler: clang
env: COMPILER="clang++-5.0" env: COMPILER="clang++-5.0" CXX_STANDARD=11
addons: addons:
apt: apt:
sources: sources:
@@ -85,7 +96,7 @@ matrix:
- os: linux - os: linux
language: cpp language: cpp
compiler: clang compiler: clang
env: COMPILER="clang++-6.0" env: COMPILER="clang++-6.0" CXX_STANDARD=11
addons: addons:
apt: apt:
sources: sources:
@@ -97,7 +108,7 @@ matrix:
- os: linux - os: linux
language: cpp language: cpp
compiler: clang compiler: clang
env: COMPILER="clang++-7" env: COMPILER="clang++-7" CXX_STANDARD=11
addons: addons:
apt: apt:
sources: sources:
@@ -109,7 +120,7 @@ matrix:
- os: linux - os: linux
language: cpp language: cpp
compiler: clang compiler: clang
env: COMPILER="clang++-8" env: COMPILER="clang++-8" CXX_STANDARD=11
addons: addons:
apt: apt:
sources: sources:
@@ -118,14 +129,38 @@ matrix:
packages: packages:
- clang-8 - clang-8
- libboost-all-dev - libboost-all-dev
- os: linux
language: cpp
compiler: clang
env: COMPILER="clang++-8" CXX_STANDARD=17
addons:
apt:
sources:
- ubuntu-toolchain-r-test
- llvm-toolchain-trusty-8
packages:
- clang-8
- g++-8
- libboost-all-dev
- os: osx - os: osx
language: cpp language: cpp
compiler: clang compiler: clang
env: CXX_STANDARD=11
script: script:
- |
if [[ "${TRAVIS_OS_NAME}" == "linux" ]]; then
mkdir -p cmake
travis_retry wget "https://cmake.org/files/v3.11/cmake-3.11.2-Linux-x86_64.tar.gz"
tar xf cmake-3.11.2-Linux-x86_64.tar.gz -C cmake --strip-components=1
export PATH=${TRAVIS_BUILD_DIR}/cmake/bin:${PATH}
else
brew upgrade cmake
fi
- cmake --version
- mkdir build - mkdir build
- cd build - cd build
- git clone https://github.com/toml-lang/toml.git - git clone https://github.com/toml-lang/toml.git
- cmake -DCMAKE_CXX_COMPILER=$COMPILER .. - cmake -DCMAKE_CXX_COMPILER=$COMPILER -DCMAKE_CXX_STANDARD=$CXX_STANDARD ..
- make - make
- ctest --output-on-failure - ctest --output-on-failure

View File

@@ -4,7 +4,10 @@ project(toml11)
include(CheckCXXCompilerFlag) include(CheckCXXCompilerFlag)
if("${CMAKE_VERSION}" VERSION_GREATER 3.1) if("${CMAKE_VERSION}" VERSION_GREATER 3.1)
set(CMAKE_CXX_STANDARD 11) set(CMAKE_CXX_EXTENSIONS OFF)
if(NOT DEFINED CMAKE_CXX_STANDARD)
set(CMAKE_CXX_STANDARD 11)
endif()
set(CXX_STANDARD_REQUIRED ON) set(CXX_STANDARD_REQUIRED ON)
else() else()
# Manually check for C++11 compiler flag. # Manually check for C++11 compiler flag.

139
README.md
View File

@@ -19,6 +19,28 @@ Not only the test suite itself, a TOML reader/encoder also runs on [CircleCI](ht
You can see the error messages about invalid files and serialization results of valid files at You can see the error messages about invalid files and serialization results of valid files at
[CircleCI](https://circleci.com/gh/ToruNiina/toml11). [CircleCI](https://circleci.com/gh/ToruNiina/toml11).
## Example
```cpp
#include <toml11/toml.hpp>
#include <iostream>
int main()
{
const auto data = toml::parse("example.toml");
// title = "an example toml file"
std::string title = toml::get<std::string>(data.at("title"));
std::cout << "the title is " << title << std::endl;
// nums = [1, 2, 3, 4, 5]
std::vector<int> nums = toml::get<std::vector<int>>(data.at("nums"));
std::cout << "the length of `nums` is" << nums.size() << std::endl;
return 0;
}
```
## Table of Contents ## Table of Contents
- [Integration](#integration) - [Integration](#integration)
@@ -41,6 +63,7 @@ You can see the error messages about invalid files and serialization results of
- [Conversion between toml value and arbitrary types](#conversion-between-toml-value-and-arbitrary-types) - [Conversion between toml value and arbitrary types](#conversion-between-toml-value-and-arbitrary-types)
- [Invalid UTF-8 Codepoints](#invalid-utf-8-codepoints) - [Invalid UTF-8 Codepoints](#invalid-utf-8-codepoints)
- [Formatting user-defined error messages](#formatting-user-defined-error-messages) - [Formatting user-defined error messages](#formatting-user-defined-error-messages)
- [Getting comments related to a value](#getting-comments)
- [Serializing TOML data](#serializing-toml-data) - [Serializing TOML data](#serializing-toml-data)
- [Underlying types](#underlying-types) - [Underlying types](#underlying-types)
- [Running Tests](#running-tests) - [Running Tests](#running-tests)
@@ -53,12 +76,14 @@ Just include the file after adding it to the include path.
```cpp ```cpp
#include <toml11/toml.hpp> // that's all! now you can use it. #include <toml11/toml.hpp> // that's all! now you can use it.
#include <iostream>
int main() int main()
{ {
const auto data = toml::parse("example.toml"); const auto data = toml::parse("example.toml");
const auto title = toml::get<std::string>(data.at("title")); const auto title = toml::get<std::string>(data.at("title"));
std::cout << "the title is " << title << std::endl; std::cout << "the title is " << title << std::endl;
return 0;
} }
``` ```
@@ -163,6 +188,12 @@ See also [underlying types](#underlying-types).
NOTE: To enable to get a reference, conversions between Float and Integer are not supported. NOTE: To enable to get a reference, conversions between Float and Integer are not supported.
After C++17, you can use `std::string_view` to get a string from a `toml::value`.
```cpp
const auto sv = toml::get<std::string_view>(tab.at("key"));
```
### In the case of type error ### In the case of type error
If you pass an invalid type to `toml::get`, `toml::type_error` will be thrown. If you pass an invalid type to `toml::get`, `toml::type_error` will be thrown.
@@ -476,9 +507,9 @@ You can check what type of value does `toml::value` contains by `is_*` function.
```cpp ```cpp
toml::value v = /* ... */; toml::value v = /* ... */;
if(v.is_integer() && toml::get<int>(v) == 42) if(v.is_integer())
{ {
std::cout << "value is 42" << std::endl; std::cout << "value is an integer" << std::endl;
} }
``` ```
@@ -522,6 +553,36 @@ toml::value v = /* ... */;
if(v.is(toml::value_t::Boolean)) // ... if(v.is(toml::value_t::Boolean)) // ...
``` ```
## Casting value
So far, `toml::get` is introduced, but if you don't need any type conversion,
`as_*` is simpler to use.
```cpp
toml::value v = /* ... */;
if(v.is_integer() && v.as_integer() == 42)
{
std::cout << "value is 42" << std::endl;
}
```
The complete list of the functions is below.
```cpp
const toml::value v(/*...*/);
v.as_boolean();
v.as_integer();
v.as_float();
v.as_string();
v.as_offset_datetime();
v.as_local_datetime();
v.as_local_date();
v.as_local_time();
v.as_array();
v.as_table();
v.as_uninitialized();
```
## Visiting a toml::value ## Visiting a toml::value
toml11 provides `toml::visit` to apply a function to `toml::value` in the toml11 provides `toml::visit` to apply a function to `toml::value` in the
@@ -579,6 +640,27 @@ toml::value operator""_toml(const char* str, std::size_t len);
Access to the operator can be gained with `using namespace toml::literals;`, Access to the operator can be gained with `using namespace toml::literals;`,
`using namespace toml::toml_literals`, and `using namespace toml::literals::toml_literals`. `using namespace toml::toml_literals`, and `using namespace toml::literals::toml_literals`.
Note that a key that is composed only of digits is allowed in TOML.
And, unlike the file parser, toml-literal allows a bare value without a key.
Thus it is difficult to distinguish arrays having integers and definitions of
tables that are named as digits.
Currently, literal `[1]` becomes a table named "1".
To ensure a literal to be considered as an array with one element, you need to
add a comma after the first element (like `[1,]`).
```cpp
"[1,2,3]"_toml; // This is an array
"[table]"_toml; // This is a table that has an empty table named "table" inside.
"[[1,2,3]]"_toml; // This is an array of arrays
"[[table]]"_toml; // This is a table that has an array of tables inside.
"[[1]]"_toml; // This literal is ambiguous.
// Currently, it becomes a table that has array of table "1".
"1 = [{}]"_toml; // This is a table that has an array of table named 1.
"[[1,]]"_toml; // This is an array of arrays.
"[[1],]"_toml; // ditto.
```
## Conversion between toml value and arbitrary types ## Conversion between toml value and arbitrary types
You can also use `toml::get` and other related functions with the types you defined You can also use `toml::get` and other related functions with the types you defined
@@ -786,6 +868,53 @@ you will get an error message like this.
| ~~ maximum number here | ~~ maximum number here
``` ```
## Getting comments
Since toml11 keeps a file data until all the values are destructed, you can
also extract a comment related to a value by calling `toml::value::comment()`.
If there is a comment just after a value (within the same line), you can get
the specific comment by `toml::value::comment_inline()`.
If there are comments just before a value (without any newline between them),
you can get the comments by `toml::value::comment_before()`.
`toml::value::comment()` returns the results of both functions after
concatenating them.
```toml
a = 42 # comment for a.
# comment for b.
# this is also a comment for b.
b = "foo"
c = [ # comment for c.
3.14, # this is not a comment for c, but for 3.14.
] # this is also a comment for c.
```
```cpp
// "# comment for a."
const std::string com1 = toml::find(data, "a").comment();
// "# comment for b."
const std::string com2 = toml::find(data, "b").comment();
// "# comment for c.\n# this is also a comment for c."
const std::string com3 = toml::find(data, "c").comment();
// "# this is not a comment for c, but for 3.14."
const std::string com3 = toml::find<toml::array>(data, "c").front().comment();
```
Note that once a data in a value is modified, the related file region
information would be deleted. Thus after modifying a data, you cannot find any
comments.
Also note that currently it does not support any way to set a comment to a value.
And currently, serializer does not take comments into account.
## Serializing TOML data ## Serializing TOML data
toml11 (after v2.1.0) enables you to serialize data into toml format. toml11 (after v2.1.0) enables you to serialize data into toml format.
@@ -937,11 +1066,15 @@ I appreciate the help of the contributors who introduced the great feature to th
- Quentin Khan (@xaxousis) - Quentin Khan (@xaxousis)
- Found & Fixed a bug around ODR - Found & Fixed a bug around ODR
- Improved error messages for invaild keys to show the location where the parser fails - Improved error messages for invaild keys to show the location where the parser fails
- Petr Beneš (@wbenny)
- Fixed warnings on MSVC
- Ivan Shynkarenka (@chronoxor)
- Fixed Visual Studio 2019 warnings
## Licensing terms ## Licensing terms
This product is licensed under the terms of the [MIT License](LICENSE). This product is licensed under the terms of the [MIT License](LICENSE).
- Copyright (c) 2017 Toru Niina - Copyright (c) 2017-2019 Toru Niina
All rights reserved. All rights reserved.

View File

@@ -21,6 +21,7 @@ set(TEST_NAMES
test_parse_key test_parse_key
test_parse_table_key test_parse_table_key
test_literals test_literals
test_comments
test_get test_get
test_get_related_func test_get_related_func
test_from_toml test_from_toml

125
tests/test_comments.cpp Normal file
View File

@@ -0,0 +1,125 @@
#define BOOST_TEST_MODULE "test_comments"
#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>
BOOST_AUTO_TEST_CASE(test_comment_before)
{
using namespace toml::literals::toml_literals;
{
const toml::value v = u8R"(
# comment for a.
a = 42
# comment for b.
b = "baz"
)"_toml;
BOOST_CHECK_EQUAL(toml::find(v, "a").comment_before(), u8"# comment for a.");
BOOST_CHECK_EQUAL(toml::find(v, "b").comment_before(), u8"# comment for b.");
BOOST_CHECK_EQUAL(toml::find(v, "a").comment_inline(), "");
BOOST_CHECK_EQUAL(toml::find(v, "b").comment_inline(), "");
BOOST_CHECK_EQUAL(toml::find(v, "a").comment(), u8"# comment for a.");
BOOST_CHECK_EQUAL(toml::find(v, "b").comment(), u8"# comment for b.");
}
{
const toml::value v = u8R"(
# comment for a.
# another comment for a.
a = 42
# comment for b.
# also comment for b.
b = "baz"
)"_toml;
BOOST_CHECK_EQUAL(toml::find(v, "a").comment_before(), u8R"(# comment for a.
# another comment for a.)");
BOOST_CHECK_EQUAL(toml::find(v, "b").comment_before(), u8R"(# comment for b.
# also comment for b.)");
BOOST_CHECK_EQUAL(toml::find(v, "a").comment_inline(), u8"");
BOOST_CHECK_EQUAL(toml::find(v, "b").comment_inline(), u8"");
BOOST_CHECK_EQUAL(toml::find(v, "a").comment(), u8R"(# comment for a.
# another comment for a.)");
BOOST_CHECK_EQUAL(toml::find(v, "b").comment(), u8R"(# comment for b.
# also comment for b.)");
}
}
BOOST_AUTO_TEST_CASE(test_comment_inline)
{
using namespace toml::literals::toml_literals;
{
const toml::value v = u8R"(
a = 42 # comment for a.
b = "baz" # comment for b.
)"_toml;
BOOST_CHECK_EQUAL(toml::find(v, "a").comment_before(), "");
BOOST_CHECK_EQUAL(toml::find(v, "b").comment_before(), "");
BOOST_CHECK_EQUAL(toml::find(v, "a").comment_inline(), u8"# comment for a.");
BOOST_CHECK_EQUAL(toml::find(v, "b").comment_inline(), u8"# comment for b.");
BOOST_CHECK_EQUAL(toml::find(v, "a").comment(), u8"# comment for a.");
BOOST_CHECK_EQUAL(toml::find(v, "b").comment(), u8"# comment for b.");
}
{
const toml::value v = u8R"(
a = [ # comment for a.
42,
] # this also.
b = [ # comment for b.
"bar",
]
c = [
3.14, # this is not a comment for c, but 3.14.
] # comment for c.
)"_toml;
BOOST_CHECK_EQUAL(toml::find(v, "a").comment_before(), "");
BOOST_CHECK_EQUAL(toml::find(v, "b").comment_before(), "");
BOOST_CHECK_EQUAL(toml::find(v, "c").comment_before(), "");
BOOST_CHECK_EQUAL(toml::find(v, "a").comment_inline(), u8R"(# comment for a.
# this also.)");
BOOST_CHECK_EQUAL(toml::find(v, "b").comment_inline(), u8"# comment for b.");
BOOST_CHECK_EQUAL(toml::find(v, "c").comment_inline(), u8"# comment for c.");
BOOST_CHECK_EQUAL(toml::find(v, "a").comment(), u8R"(# comment for a.
# this also.)");
BOOST_CHECK_EQUAL(toml::find(v, "b").comment(), u8"# comment for b.");
BOOST_CHECK_EQUAL(toml::find(v, "c").comment(), u8"# comment for c.");
const auto& c0 = toml::find<toml::array>(v, "c").at(0);
BOOST_CHECK_EQUAL(c0.comment(), u8"# this is not a comment for c, but 3.14.");
}
}
BOOST_AUTO_TEST_CASE(test_comment_both)
{
using namespace toml::literals::toml_literals;
{
const toml::value v = u8R"(
# comment for a.
a = 42 # inline comment for a.
# comment for b.
b = "baz" # inline comment for b.
)"_toml;
BOOST_CHECK_EQUAL(toml::find(v, "a").comment_before(), "# comment for a.");
BOOST_CHECK_EQUAL(toml::find(v, "b").comment_before(), "# comment for b.");
BOOST_CHECK_EQUAL(toml::find(v, "a").comment_inline(), "# inline comment for a.");
BOOST_CHECK_EQUAL(toml::find(v, "b").comment_inline(), "# inline comment for b.");
BOOST_CHECK_EQUAL(toml::find(v, "a").comment(), u8R"(# comment for a.
# inline comment for a.)");
BOOST_CHECK_EQUAL(toml::find(v, "b").comment(), u8R"(# comment for b.
# inline comment for b.)");
}
}

View File

@@ -12,6 +12,9 @@
#include <list> #include <list>
#include <deque> #include <deque>
#include <array> #include <array>
#if __cplusplus >= 201703L
#include <string_view>
#endif
BOOST_AUTO_TEST_CASE(test_get_exact) BOOST_AUTO_TEST_CASE(test_get_exact)
@@ -166,6 +169,17 @@ BOOST_AUTO_TEST_CASE(test_get_string_type)
toml::get<std::string>(v) += "bar"; toml::get<std::string>(v) += "bar";
BOOST_CHECK_EQUAL("foobar", toml::get<std::string>(v)); BOOST_CHECK_EQUAL("foobar", toml::get<std::string>(v));
} }
#if __cplusplus >= 201703L
{
toml::value v("foo", toml::string_t::basic);
BOOST_CHECK_EQUAL("foo", toml::get<std::string_view>(v));
}
{
toml::value v("foo", toml::string_t::literal);
BOOST_CHECK_EQUAL("foo", toml::get<std::string_view>(v));
}
#endif
} }
BOOST_AUTO_TEST_CASE(test_get_toml_array) BOOST_AUTO_TEST_CASE(test_get_toml_array)

View File

@@ -18,7 +18,7 @@ do { \
BOOST_CHECK_EQUAL(static_cast<std::size_t>(std::distance( \ BOOST_CHECK_EQUAL(static_cast<std::size_t>(std::distance( \
loc.begin(), loc.iter())), region.size()); \ loc.begin(), loc.iter())), region.size()); \
} else { \ } else { \
std::cerr << "lexer " << lxr::pattern() << " failed with input `"; \ std::cerr << "lexer failed with input `"; \
std::cerr << token << "`. expected `" << expected << "`\n"; \ std::cerr << token << "`. expected `" << expected << "`\n"; \
std::cerr << "reason: " << result.unwrap_err() << '\n'; \ std::cerr << "reason: " << result.unwrap_err() << '\n'; \
} \ } \

View File

@@ -33,6 +33,18 @@ BOOST_AUTO_TEST_CASE(test_file_as_literal)
b = "baz" b = "baz"
)"_toml; )"_toml;
BOOST_CHECK_EQUAL(r, v);
}
{
const toml::value r{
{"table", toml::table{{"a", 42}, {"b", "baz"}}}
};
const toml::value v = u8R"(
[table]
a = 42
b = "baz"
)"_toml;
BOOST_CHECK_EQUAL(r, v); BOOST_CHECK_EQUAL(r, v);
} }
} }
@@ -91,6 +103,19 @@ BOOST_AUTO_TEST_CASE(test_value_as_literal)
BOOST_CHECK(v1.is_array()); BOOST_CHECK(v1.is_array());
BOOST_CHECK((toml::get<std::vector<int>>(v1) == std::vector<int>{1,2,3})); BOOST_CHECK((toml::get<std::vector<int>>(v1) == std::vector<int>{1,2,3}));
const toml::value v2 = u8R"([1,])"_toml;
BOOST_CHECK(v2.is_array());
BOOST_CHECK((toml::get<std::vector<int>>(v2) == std::vector<int>{1}));
const toml::value v3 = u8R"([[1,]])"_toml;
BOOST_CHECK(v3.is_array());
BOOST_CHECK((toml::get<std::vector<int>>(toml::get<toml::array>(v3).front()) == std::vector<int>{1}));
const toml::value v4 = u8R"([[1],])"_toml;
BOOST_CHECK(v4.is_array());
BOOST_CHECK((toml::get<std::vector<int>>(toml::get<toml::array>(v4).front()) == std::vector<int>{1}));
} }
{ {
const toml::value v1 = u8R"({a = 42})"_toml; const toml::value v1 = u8R"({a = 42})"_toml;

View File

@@ -9,6 +9,11 @@
#include <map> #include <map>
#include <list> #include <list>
#if __cplusplus >= 201703L
#include <string_view>
#endif
BOOST_AUTO_TEST_CASE(test_value_boolean) BOOST_AUTO_TEST_CASE(test_value_boolean)
{ {
toml::value v1(true); toml::value v1(true);
@@ -25,6 +30,8 @@ BOOST_AUTO_TEST_CASE(test_value_boolean)
BOOST_CHECK_EQUAL(v1.cast<toml::value_t::Boolean>(), true); BOOST_CHECK_EQUAL(v1.cast<toml::value_t::Boolean>(), true);
BOOST_CHECK_EQUAL(v2.cast<toml::value_t::Boolean>(), false); BOOST_CHECK_EQUAL(v2.cast<toml::value_t::Boolean>(), false);
BOOST_CHECK_EQUAL(v1.as_boolean(), true);
BOOST_CHECK_EQUAL(v2.as_boolean(), false);
v1 = false; v1 = false;
v2 = true; v2 = true;
@@ -40,6 +47,8 @@ BOOST_AUTO_TEST_CASE(test_value_boolean)
BOOST_CHECK_EQUAL(v1.cast<toml::value_t::Boolean>(), false); BOOST_CHECK_EQUAL(v1.cast<toml::value_t::Boolean>(), false);
BOOST_CHECK_EQUAL(v2.cast<toml::value_t::Boolean>(), true); BOOST_CHECK_EQUAL(v2.cast<toml::value_t::Boolean>(), true);
BOOST_CHECK_EQUAL(v1.as_boolean(), false);
BOOST_CHECK_EQUAL(v2.as_boolean(), true);
toml::value v3(v1); toml::value v3(v1);
toml::value v4(v2); toml::value v4(v2);
@@ -57,6 +66,8 @@ BOOST_AUTO_TEST_CASE(test_value_boolean)
BOOST_CHECK_EQUAL(v3.cast<toml::value_t::Boolean>(), false); BOOST_CHECK_EQUAL(v3.cast<toml::value_t::Boolean>(), false);
BOOST_CHECK_EQUAL(v4.cast<toml::value_t::Boolean>(), true); BOOST_CHECK_EQUAL(v4.cast<toml::value_t::Boolean>(), true);
BOOST_CHECK_EQUAL(v3.as_boolean(), false);
BOOST_CHECK_EQUAL(v4.as_boolean(), true);
toml::value v5(std::move(v1)); toml::value v5(std::move(v1));
toml::value v6(std::move(v2)); toml::value v6(std::move(v2));
@@ -72,6 +83,8 @@ BOOST_AUTO_TEST_CASE(test_value_boolean)
BOOST_CHECK_EQUAL(v5.cast<toml::value_t::Boolean>(), false); BOOST_CHECK_EQUAL(v5.cast<toml::value_t::Boolean>(), false);
BOOST_CHECK_EQUAL(v6.cast<toml::value_t::Boolean>(), true); BOOST_CHECK_EQUAL(v6.cast<toml::value_t::Boolean>(), true);
BOOST_CHECK_EQUAL(v5.as_boolean(), false);
BOOST_CHECK_EQUAL(v6.as_boolean(), true);
v1 = 42; v1 = 42;
v2 = 3.14; v2 = 3.14;
@@ -87,6 +100,8 @@ BOOST_AUTO_TEST_CASE(test_value_boolean)
BOOST_CHECK_EQUAL(v1.cast<toml::value_t::Integer>(), 42); BOOST_CHECK_EQUAL(v1.cast<toml::value_t::Integer>(), 42);
BOOST_CHECK_EQUAL(v2.cast<toml::value_t::Float>(), 3.14); BOOST_CHECK_EQUAL(v2.cast<toml::value_t::Float>(), 3.14);
BOOST_CHECK_EQUAL(v1.as_integer(), 42);
BOOST_CHECK_EQUAL(v2.as_float(), 3.14);
} }
BOOST_AUTO_TEST_CASE(test_value_integer) BOOST_AUTO_TEST_CASE(test_value_integer)
@@ -105,6 +120,8 @@ BOOST_AUTO_TEST_CASE(test_value_integer)
BOOST_CHECK_EQUAL(v1.cast<toml::value_t::Integer>(), -42); BOOST_CHECK_EQUAL(v1.cast<toml::value_t::Integer>(), -42);
BOOST_CHECK_EQUAL(v2.cast<toml::value_t::Integer>(), 42u); BOOST_CHECK_EQUAL(v2.cast<toml::value_t::Integer>(), 42u);
BOOST_CHECK_EQUAL(v1.as_integer(), -42);
BOOST_CHECK_EQUAL(v2.as_integer(), 42u);
v1 = 54; v1 = 54;
v2 = -54; v2 = -54;
@@ -120,6 +137,8 @@ BOOST_AUTO_TEST_CASE(test_value_integer)
BOOST_CHECK_EQUAL(v1.cast<toml::value_t::Integer>(), 54); BOOST_CHECK_EQUAL(v1.cast<toml::value_t::Integer>(), 54);
BOOST_CHECK_EQUAL(v2.cast<toml::value_t::Integer>(), -54); BOOST_CHECK_EQUAL(v2.cast<toml::value_t::Integer>(), -54);
BOOST_CHECK_EQUAL(v1.as_integer(), 54);
BOOST_CHECK_EQUAL(v2.as_integer(), -54);
toml::value v3(v1); toml::value v3(v1);
toml::value v4(v2); toml::value v4(v2);
@@ -137,6 +156,8 @@ BOOST_AUTO_TEST_CASE(test_value_integer)
BOOST_CHECK_EQUAL(v3.cast<toml::value_t::Integer>(), 54); BOOST_CHECK_EQUAL(v3.cast<toml::value_t::Integer>(), 54);
BOOST_CHECK_EQUAL(v4.cast<toml::value_t::Integer>(), -54); BOOST_CHECK_EQUAL(v4.cast<toml::value_t::Integer>(), -54);
BOOST_CHECK_EQUAL(v3.as_integer(), 54);
BOOST_CHECK_EQUAL(v4.as_integer(), -54);
toml::value v5(std::move(v1)); toml::value v5(std::move(v1));
toml::value v6(std::move(v2)); toml::value v6(std::move(v2));
@@ -152,6 +173,8 @@ BOOST_AUTO_TEST_CASE(test_value_integer)
BOOST_CHECK_EQUAL(v5.cast<toml::value_t::Integer>(), 54); BOOST_CHECK_EQUAL(v5.cast<toml::value_t::Integer>(), 54);
BOOST_CHECK_EQUAL(v6.cast<toml::value_t::Integer>(), -54); BOOST_CHECK_EQUAL(v6.cast<toml::value_t::Integer>(), -54);
BOOST_CHECK_EQUAL(v5.as_integer(), 54);
BOOST_CHECK_EQUAL(v6.as_integer(), -54);
v1 = true; v1 = true;
v2 = false; v2 = false;
@@ -167,6 +190,8 @@ BOOST_AUTO_TEST_CASE(test_value_integer)
BOOST_CHECK_EQUAL(v1.cast<toml::value_t::Boolean>(), true); BOOST_CHECK_EQUAL(v1.cast<toml::value_t::Boolean>(), true);
BOOST_CHECK_EQUAL(v2.cast<toml::value_t::Boolean>(), false); BOOST_CHECK_EQUAL(v2.cast<toml::value_t::Boolean>(), false);
BOOST_CHECK_EQUAL(v1.as_boolean(), true);
BOOST_CHECK_EQUAL(v2.as_boolean(), false);
} }
BOOST_AUTO_TEST_CASE(test_value_float) BOOST_AUTO_TEST_CASE(test_value_float)
@@ -183,8 +208,10 @@ BOOST_AUTO_TEST_CASE(test_value_float)
BOOST_CHECK(v1.is_float()); BOOST_CHECK(v1.is_float());
BOOST_CHECK(v2.is_float()); BOOST_CHECK(v2.is_float());
BOOST_CHECK_EQUAL(v1.cast<toml::value_t::Float>(), 3.14); BOOST_CHECK_EQUAL (v1.cast<toml::value_t::Float>(), 3.14);
BOOST_CHECK_CLOSE_FRACTION(v2.cast<toml::value_t::Float>(), 3.14, 1e-2); BOOST_CHECK_CLOSE_FRACTION(v2.cast<toml::value_t::Float>(), 3.14, 1e-2);
BOOST_CHECK_EQUAL (v1.as_float(), 3.14);
BOOST_CHECK_CLOSE_FRACTION(v2.as_float(), 3.14, 1e-2);
v1 = 2.718f; v1 = 2.718f;
v2 = 2.718; v2 = 2.718;
@@ -199,7 +226,9 @@ BOOST_AUTO_TEST_CASE(test_value_float)
BOOST_CHECK(v2.is_float()); BOOST_CHECK(v2.is_float());
BOOST_CHECK_CLOSE_FRACTION(v1.cast<toml::value_t::Float>(), 2.718, 1e-3); BOOST_CHECK_CLOSE_FRACTION(v1.cast<toml::value_t::Float>(), 2.718, 1e-3);
BOOST_CHECK_EQUAL(v2.cast<toml::value_t::Float>(), 2.718); BOOST_CHECK_EQUAL (v2.cast<toml::value_t::Float>(), 2.718);
BOOST_CHECK_CLOSE_FRACTION(v1.as_float(), 2.718, 1e-3);
BOOST_CHECK_EQUAL (v2.as_float(), 2.718);
toml::value v3(v1); toml::value v3(v1);
toml::value v4(v2); toml::value v4(v2);
@@ -216,7 +245,9 @@ BOOST_AUTO_TEST_CASE(test_value_float)
BOOST_CHECK(v4.is_float()); BOOST_CHECK(v4.is_float());
BOOST_CHECK_CLOSE_FRACTION(v3.cast<toml::value_t::Float>(), 2.718, 1e-3); BOOST_CHECK_CLOSE_FRACTION(v3.cast<toml::value_t::Float>(), 2.718, 1e-3);
BOOST_CHECK_EQUAL(v4.cast<toml::value_t::Float>(), 2.718); BOOST_CHECK_EQUAL (v4.cast<toml::value_t::Float>(), 2.718);
BOOST_CHECK_CLOSE_FRACTION(v3.as_float(), 2.718, 1e-3);
BOOST_CHECK_EQUAL (v4.as_float(), 2.718);
toml::value v5(std::move(v1)); toml::value v5(std::move(v1));
toml::value v6(std::move(v2)); toml::value v6(std::move(v2));
@@ -231,7 +262,9 @@ BOOST_AUTO_TEST_CASE(test_value_float)
BOOST_CHECK(v6.is_float()); BOOST_CHECK(v6.is_float());
BOOST_CHECK_CLOSE_FRACTION(v5.cast<toml::value_t::Float>(), 2.718, 1e-3); BOOST_CHECK_CLOSE_FRACTION(v5.cast<toml::value_t::Float>(), 2.718, 1e-3);
BOOST_CHECK_EQUAL(v6.cast<toml::value_t::Float>(), 2.718); BOOST_CHECK_EQUAL (v6.cast<toml::value_t::Float>(), 2.718);
BOOST_CHECK_CLOSE_FRACTION(v5.as_float(), 2.718, 1e-3);
BOOST_CHECK_EQUAL (v6.as_float(), 2.718);
v1 = true; v1 = true;
v2 = false; v2 = false;
@@ -247,6 +280,8 @@ BOOST_AUTO_TEST_CASE(test_value_float)
BOOST_CHECK_EQUAL(v1.cast<toml::value_t::Boolean>(), true); BOOST_CHECK_EQUAL(v1.cast<toml::value_t::Boolean>(), true);
BOOST_CHECK_EQUAL(v2.cast<toml::value_t::Boolean>(), false); BOOST_CHECK_EQUAL(v2.cast<toml::value_t::Boolean>(), false);
BOOST_CHECK_EQUAL(v1.as_boolean(), true);
BOOST_CHECK_EQUAL(v2.as_boolean(), false);
} }
BOOST_AUTO_TEST_CASE(test_value_string) BOOST_AUTO_TEST_CASE(test_value_string)
@@ -271,6 +306,10 @@ BOOST_AUTO_TEST_CASE(test_value_string)
BOOST_CHECK_EQUAL(v1.cast<toml::value_t::String>(), "foo"); BOOST_CHECK_EQUAL(v1.cast<toml::value_t::String>(), "foo");
BOOST_CHECK_EQUAL(v2.cast<toml::value_t::String>(), "foo"); BOOST_CHECK_EQUAL(v2.cast<toml::value_t::String>(), "foo");
BOOST_CHECK_EQUAL(v3.cast<toml::value_t::String>(), "foo"); BOOST_CHECK_EQUAL(v3.cast<toml::value_t::String>(), "foo");
BOOST_CHECK_EQUAL(v1.as_string(), "foo");
BOOST_CHECK_EQUAL(v2.as_string(), "foo");
BOOST_CHECK_EQUAL(v3.as_string(), "foo");
v1 = "bar"; v1 = "bar";
v2 = "bar"; v2 = "bar";
@@ -289,6 +328,10 @@ BOOST_AUTO_TEST_CASE(test_value_string)
BOOST_CHECK_EQUAL(v1.cast<toml::value_t::String>(), "bar"); BOOST_CHECK_EQUAL(v1.cast<toml::value_t::String>(), "bar");
BOOST_CHECK_EQUAL(v2.cast<toml::value_t::String>(), "bar"); BOOST_CHECK_EQUAL(v2.cast<toml::value_t::String>(), "bar");
BOOST_CHECK_EQUAL(v3.cast<toml::value_t::String>(), "bar"); BOOST_CHECK_EQUAL(v3.cast<toml::value_t::String>(), "bar");
BOOST_CHECK_EQUAL(v1.as_string(), "bar");
BOOST_CHECK_EQUAL(v2.as_string(), "bar");
BOOST_CHECK_EQUAL(v3.as_string(), "bar");
toml::value v4(v1); toml::value v4(v1);
toml::value v5(v2); toml::value v5(v2);
@@ -313,6 +356,10 @@ BOOST_AUTO_TEST_CASE(test_value_string)
BOOST_CHECK_EQUAL(v4.cast<toml::value_t::String>(), "bar"); BOOST_CHECK_EQUAL(v4.cast<toml::value_t::String>(), "bar");
BOOST_CHECK_EQUAL(v5.cast<toml::value_t::String>(), "bar"); BOOST_CHECK_EQUAL(v5.cast<toml::value_t::String>(), "bar");
BOOST_CHECK_EQUAL(v6.cast<toml::value_t::String>(), "bar"); BOOST_CHECK_EQUAL(v6.cast<toml::value_t::String>(), "bar");
BOOST_CHECK_EQUAL(v4.as_string(), "bar");
BOOST_CHECK_EQUAL(v5.as_string(), "bar");
BOOST_CHECK_EQUAL(v6.as_string(), "bar");
v4.cast<toml::value_t::String>().str.at(2) = 'z'; v4.cast<toml::value_t::String>().str.at(2) = 'z';
v5.cast<toml::value_t::String>().str.at(2) = 'z'; v5.cast<toml::value_t::String>().str.at(2) = 'z';
@@ -331,9 +378,9 @@ BOOST_AUTO_TEST_CASE(test_value_string)
BOOST_CHECK(v5.is_string()); BOOST_CHECK(v5.is_string());
BOOST_CHECK(v6.is_string()); BOOST_CHECK(v6.is_string());
BOOST_CHECK_EQUAL(v4.cast<toml::value_t::String>(), "baz"); BOOST_CHECK_EQUAL(v4.as_string(), "baz");
BOOST_CHECK_EQUAL(v5.cast<toml::value_t::String>(), "baz"); BOOST_CHECK_EQUAL(v5.as_string(), "baz");
BOOST_CHECK_EQUAL(v6.cast<toml::value_t::String>(), "baz"); BOOST_CHECK_EQUAL(v6.as_string(), "baz");
v1 = true; v1 = true;
v2 = true; v2 = true;
@@ -355,6 +402,28 @@ BOOST_AUTO_TEST_CASE(test_value_string)
BOOST_CHECK_EQUAL(v1.cast<toml::value_t::Boolean>(), true); BOOST_CHECK_EQUAL(v1.cast<toml::value_t::Boolean>(), true);
BOOST_CHECK_EQUAL(v2.cast<toml::value_t::Boolean>(), true); BOOST_CHECK_EQUAL(v2.cast<toml::value_t::Boolean>(), true);
BOOST_CHECK_EQUAL(v3.cast<toml::value_t::Boolean>(), true); BOOST_CHECK_EQUAL(v3.cast<toml::value_t::Boolean>(), true);
BOOST_CHECK_EQUAL(v1.as_boolean(), true);
BOOST_CHECK_EQUAL(v2.as_boolean(), true);
BOOST_CHECK_EQUAL(v3.as_boolean(), true);
#if __cplusplus >= 201703L
std::string_view sv = "foo";
toml::value v7(sv);
toml::value v8(sv, toml::string_t::literal);
BOOST_CHECK_EQUAL(v7.type(), toml::value_t::String);
BOOST_CHECK_EQUAL(v8.type(), toml::value_t::String);
BOOST_CHECK(v7.is(toml::value_t::String));
BOOST_CHECK(v8.is(toml::value_t::String));
BOOST_CHECK(v7.is<toml::String>());
BOOST_CHECK(v8.is<toml::String>());
BOOST_CHECK(v7.is_string());
BOOST_CHECK(v8.is_string());
BOOST_CHECK_EQUAL(v7.cast<toml::value_t::String>(), "foo");
BOOST_CHECK_EQUAL(v8.cast<toml::value_t::String>(), "foo");
#endif
} }
BOOST_AUTO_TEST_CASE(test_value_local_date) BOOST_AUTO_TEST_CASE(test_value_local_date)
@@ -368,6 +437,8 @@ BOOST_AUTO_TEST_CASE(test_value_local_date)
BOOST_CHECK_EQUAL(v1.cast<toml::value_t::LocalDate>(), BOOST_CHECK_EQUAL(v1.cast<toml::value_t::LocalDate>(),
toml::local_date(2018, toml::month_t::Jan, 31)); toml::local_date(2018, toml::month_t::Jan, 31));
BOOST_CHECK_EQUAL(v1.as_local_date(),
toml::local_date(2018, toml::month_t::Jan, 31));
v1 = toml::local_date(2018, toml::month_t::Apr, 1); v1 = toml::local_date(2018, toml::month_t::Apr, 1);
@@ -378,6 +449,8 @@ BOOST_AUTO_TEST_CASE(test_value_local_date)
BOOST_CHECK_EQUAL(v1.cast<toml::value_t::LocalDate>(), BOOST_CHECK_EQUAL(v1.cast<toml::value_t::LocalDate>(),
toml::local_date(2018, toml::month_t::Apr, 1)); toml::local_date(2018, toml::month_t::Apr, 1));
BOOST_CHECK_EQUAL(v1.as_local_date(),
toml::local_date(2018, toml::month_t::Apr, 1));
toml::value v2(v1); toml::value v2(v1);
BOOST_CHECK(v2 == v1); BOOST_CHECK(v2 == v1);
@@ -389,6 +462,8 @@ BOOST_AUTO_TEST_CASE(test_value_local_date)
BOOST_CHECK_EQUAL(v2.cast<toml::value_t::LocalDate>(), BOOST_CHECK_EQUAL(v2.cast<toml::value_t::LocalDate>(),
toml::local_date(2018, toml::month_t::Apr, 1)); toml::local_date(2018, toml::month_t::Apr, 1));
BOOST_CHECK_EQUAL(v2.as_local_date(),
toml::local_date(2018, toml::month_t::Apr, 1));
v1 = true; v1 = true;
BOOST_CHECK_EQUAL(v1.type(), toml::value_t::Boolean); BOOST_CHECK_EQUAL(v1.type(), toml::value_t::Boolean);
@@ -396,6 +471,7 @@ BOOST_AUTO_TEST_CASE(test_value_local_date)
BOOST_CHECK(v1.is<toml::Boolean>()); BOOST_CHECK(v1.is<toml::Boolean>());
BOOST_CHECK(v1.is_boolean()); BOOST_CHECK(v1.is_boolean());
BOOST_CHECK_EQUAL(v1.cast<toml::value_t::Boolean>(), true); BOOST_CHECK_EQUAL(v1.cast<toml::value_t::Boolean>(), true);
BOOST_CHECK_EQUAL(v1.as_boolean(), true);
} }
BOOST_AUTO_TEST_CASE(test_value_local_time) BOOST_AUTO_TEST_CASE(test_value_local_time)
@@ -415,10 +491,18 @@ BOOST_AUTO_TEST_CASE(test_value_local_time)
BOOST_CHECK_EQUAL(v1.cast<toml::value_t::LocalTime>(), BOOST_CHECK_EQUAL(v1.cast<toml::value_t::LocalTime>(),
toml::local_time(12, 30, 45)); toml::local_time(12, 30, 45));
BOOST_CHECK_EQUAL(v1.as_local_time(),
toml::local_time(12, 30, 45));
BOOST_CHECK_EQUAL(v2.cast<toml::value_t::LocalTime>(), BOOST_CHECK_EQUAL(v2.cast<toml::value_t::LocalTime>(),
toml::local_time(12, 30, 45)); toml::local_time(12, 30, 45));
BOOST_CHECK_EQUAL(v2.as_local_time(),
toml::local_time(12, 30, 45));
BOOST_CHECK_EQUAL(v1.cast<toml::value_t::LocalTime>(), BOOST_CHECK_EQUAL(v1.cast<toml::value_t::LocalTime>(),
v2.cast<toml::value_t::LocalTime>()); v2.cast<toml::value_t::LocalTime>());
BOOST_CHECK_EQUAL(v1.as_local_time(),
v2.as_local_time());
v1 = toml::local_time(1, 30, 0, /*ms*/ 100, /*us*/ 0); v1 = toml::local_time(1, 30, 0, /*ms*/ 100, /*us*/ 0);
@@ -428,6 +512,8 @@ BOOST_AUTO_TEST_CASE(test_value_local_time)
BOOST_CHECK(v1.is_local_time()); BOOST_CHECK(v1.is_local_time());
BOOST_CHECK_EQUAL(v1.cast<toml::value_t::LocalTime>(), BOOST_CHECK_EQUAL(v1.cast<toml::value_t::LocalTime>(),
toml::local_time(1, 30, 0, 100, 0)); toml::local_time(1, 30, 0, 100, 0));
BOOST_CHECK_EQUAL(v1.as_local_time(),
toml::local_time(1, 30, 0, 100, 0));
toml::value v3(v1); toml::value v3(v1);
BOOST_CHECK(v3 == v1); BOOST_CHECK(v3 == v1);
@@ -439,6 +525,8 @@ BOOST_AUTO_TEST_CASE(test_value_local_time)
BOOST_CHECK_EQUAL(v3.cast<toml::value_t::LocalTime>(), BOOST_CHECK_EQUAL(v3.cast<toml::value_t::LocalTime>(),
toml::local_time(1, 30, 0, 100, 0)); toml::local_time(1, 30, 0, 100, 0));
BOOST_CHECK_EQUAL(v3.as_local_time(),
toml::local_time(1, 30, 0, 100, 0));
v1 = true; v1 = true;
BOOST_CHECK_EQUAL(v1.type(), toml::value_t::Boolean); BOOST_CHECK_EQUAL(v1.type(), toml::value_t::Boolean);
@@ -446,6 +534,7 @@ BOOST_AUTO_TEST_CASE(test_value_local_time)
BOOST_CHECK(v1.is<toml::Boolean>()); BOOST_CHECK(v1.is<toml::Boolean>());
BOOST_CHECK(v1.is_boolean()); BOOST_CHECK(v1.is_boolean());
BOOST_CHECK_EQUAL(v1.cast<toml::value_t::Boolean>(), true); BOOST_CHECK_EQUAL(v1.cast<toml::value_t::Boolean>(), true);
BOOST_CHECK_EQUAL(v1.as_boolean(), true);
} }
BOOST_AUTO_TEST_CASE(test_value_local_datetime) BOOST_AUTO_TEST_CASE(test_value_local_datetime)
@@ -464,6 +553,10 @@ BOOST_AUTO_TEST_CASE(test_value_local_datetime)
toml::local_datetime( toml::local_datetime(
toml::local_date(2018, toml::month_t::Jan, 31), toml::local_date(2018, toml::month_t::Jan, 31),
toml::local_time(12, 30, 45))); toml::local_time(12, 30, 45)));
BOOST_CHECK_EQUAL(v1.as_local_datetime(),
toml::local_datetime(
toml::local_date(2018, toml::month_t::Jan, 31),
toml::local_time(12, 30, 45)));
v1 = toml::local_datetime( v1 = toml::local_datetime(
toml::local_date(2018, toml::month_t::Apr, 1), toml::local_date(2018, toml::month_t::Apr, 1),
@@ -478,6 +571,10 @@ BOOST_AUTO_TEST_CASE(test_value_local_datetime)
toml::local_datetime( toml::local_datetime(
toml::local_date(2018, toml::month_t::Apr, 1), toml::local_date(2018, toml::month_t::Apr, 1),
toml::local_time(1, 15, 30))); toml::local_time(1, 15, 30)));
BOOST_CHECK_EQUAL(v1.as_local_datetime(),
toml::local_datetime(
toml::local_date(2018, toml::month_t::Apr, 1),
toml::local_time(1, 15, 30)));
toml::value v2(v1); toml::value v2(v1);
BOOST_CHECK(v2 == v1); BOOST_CHECK(v2 == v1);
@@ -491,6 +588,11 @@ BOOST_AUTO_TEST_CASE(test_value_local_datetime)
toml::local_datetime( toml::local_datetime(
toml::local_date(2018, toml::month_t::Apr, 1), toml::local_date(2018, toml::month_t::Apr, 1),
toml::local_time(1, 15, 30))); toml::local_time(1, 15, 30)));
BOOST_CHECK_EQUAL(v2.as_local_datetime(),
toml::local_datetime(
toml::local_date(2018, toml::month_t::Apr, 1),
toml::local_time(1, 15, 30)));
v1 = true; v1 = true;
BOOST_CHECK_EQUAL(v1.type(), toml::value_t::Boolean); BOOST_CHECK_EQUAL(v1.type(), toml::value_t::Boolean);
@@ -498,6 +600,7 @@ BOOST_AUTO_TEST_CASE(test_value_local_datetime)
BOOST_CHECK(v1.is<toml::Boolean>()); BOOST_CHECK(v1.is<toml::Boolean>());
BOOST_CHECK(v1.is_boolean()); BOOST_CHECK(v1.is_boolean());
BOOST_CHECK_EQUAL(v1.cast<toml::value_t::Boolean>(), true); BOOST_CHECK_EQUAL(v1.cast<toml::value_t::Boolean>(), true);
BOOST_CHECK_EQUAL(v1.as_boolean(), true);
} }
BOOST_AUTO_TEST_CASE(test_value_offset_datetime) BOOST_AUTO_TEST_CASE(test_value_offset_datetime)
@@ -519,6 +622,12 @@ BOOST_AUTO_TEST_CASE(test_value_offset_datetime)
toml::local_time(12, 30, 45), toml::local_time(12, 30, 45),
toml::time_offset(9, 0) toml::time_offset(9, 0)
)); ));
BOOST_CHECK_EQUAL(v1.as_offset_datetime(),
toml::offset_datetime(
toml::local_date(2018, toml::month_t::Jan, 31),
toml::local_time(12, 30, 45),
toml::time_offset(9, 0)
));
v1 = toml::offset_datetime( v1 = toml::offset_datetime(
toml::local_date(2018, toml::month_t::Apr, 1), toml::local_date(2018, toml::month_t::Apr, 1),
@@ -535,6 +644,12 @@ BOOST_AUTO_TEST_CASE(test_value_offset_datetime)
toml::local_date(2018, toml::month_t::Apr, 1), toml::local_date(2018, toml::month_t::Apr, 1),
toml::local_time(1, 15, 30), toml::local_time(1, 15, 30),
toml::time_offset(9, 0))); toml::time_offset(9, 0)));
BOOST_CHECK_EQUAL(v1.as_offset_datetime(),
toml::offset_datetime(
toml::local_date(2018, toml::month_t::Apr, 1),
toml::local_time(1, 15, 30),
toml::time_offset(9, 0)));
toml::value v2(v1); toml::value v2(v1);
BOOST_CHECK(v2 == v1); BOOST_CHECK(v2 == v1);
@@ -549,12 +664,19 @@ BOOST_AUTO_TEST_CASE(test_value_offset_datetime)
toml::local_date(2018, toml::month_t::Apr, 1), toml::local_date(2018, toml::month_t::Apr, 1),
toml::local_time(1, 15, 30), toml::local_time(1, 15, 30),
toml::time_offset(9, 0))); toml::time_offset(9, 0)));
BOOST_CHECK_EQUAL(v2.as_offset_datetime(),
toml::offset_datetime(
toml::local_date(2018, toml::month_t::Apr, 1),
toml::local_time(1, 15, 30),
toml::time_offset(9, 0)));
v1 = true; v1 = true;
BOOST_CHECK_EQUAL(v1.type(), toml::value_t::Boolean); BOOST_CHECK_EQUAL(v1.type(), toml::value_t::Boolean);
BOOST_CHECK(v1.is(toml::value_t::Boolean)); BOOST_CHECK(v1.is(toml::value_t::Boolean));
BOOST_CHECK(v1.is<toml::Boolean>()); BOOST_CHECK(v1.is<toml::Boolean>());
BOOST_CHECK(v1.is_boolean()); BOOST_CHECK(v1.is_boolean());
BOOST_CHECK_EQUAL(v1.cast<toml::value_t::Boolean>(), true); BOOST_CHECK_EQUAL(v1.cast<toml::value_t::Boolean>(), true);
BOOST_CHECK_EQUAL(v1.as_boolean(), true);
} }
BOOST_AUTO_TEST_CASE(test_value_array) BOOST_AUTO_TEST_CASE(test_value_array)
@@ -578,6 +700,12 @@ BOOST_AUTO_TEST_CASE(test_value_array)
BOOST_CHECK_EQUAL(v1.cast<toml::value_t::Array>().at(2).cast<toml::value_t::Integer>(), 3); BOOST_CHECK_EQUAL(v1.cast<toml::value_t::Array>().at(2).cast<toml::value_t::Integer>(), 3);
BOOST_CHECK_EQUAL(v1.cast<toml::value_t::Array>().at(3).cast<toml::value_t::Integer>(), 4); BOOST_CHECK_EQUAL(v1.cast<toml::value_t::Array>().at(3).cast<toml::value_t::Integer>(), 4);
BOOST_CHECK_EQUAL(v1.cast<toml::value_t::Array>().at(4).cast<toml::value_t::Integer>(), 5); BOOST_CHECK_EQUAL(v1.cast<toml::value_t::Array>().at(4).cast<toml::value_t::Integer>(), 5);
BOOST_CHECK_EQUAL(v1.as_array().at(0).as_integer(), 1);
BOOST_CHECK_EQUAL(v1.as_array().at(1).as_integer(), 2);
BOOST_CHECK_EQUAL(v1.as_array().at(2).as_integer(), 3);
BOOST_CHECK_EQUAL(v1.as_array().at(3).as_integer(), 4);
BOOST_CHECK_EQUAL(v1.as_array().at(4).as_integer(), 5);
BOOST_CHECK_EQUAL(v2.cast<toml::value_t::Array>().at(0).cast<toml::value_t::Integer>(), 6); BOOST_CHECK_EQUAL(v2.cast<toml::value_t::Array>().at(0).cast<toml::value_t::Integer>(), 6);
BOOST_CHECK_EQUAL(v2.cast<toml::value_t::Array>().at(1).cast<toml::value_t::Integer>(), 7); BOOST_CHECK_EQUAL(v2.cast<toml::value_t::Array>().at(1).cast<toml::value_t::Integer>(), 7);
@@ -603,12 +731,24 @@ BOOST_AUTO_TEST_CASE(test_value_array)
BOOST_CHECK_EQUAL(v1.cast<toml::value_t::Array>().at(2).cast<toml::value_t::Integer>(), 8); BOOST_CHECK_EQUAL(v1.cast<toml::value_t::Array>().at(2).cast<toml::value_t::Integer>(), 8);
BOOST_CHECK_EQUAL(v1.cast<toml::value_t::Array>().at(3).cast<toml::value_t::Integer>(), 9); BOOST_CHECK_EQUAL(v1.cast<toml::value_t::Array>().at(3).cast<toml::value_t::Integer>(), 9);
BOOST_CHECK_EQUAL(v1.cast<toml::value_t::Array>().at(4).cast<toml::value_t::Integer>(), 0); BOOST_CHECK_EQUAL(v1.cast<toml::value_t::Array>().at(4).cast<toml::value_t::Integer>(), 0);
BOOST_CHECK_EQUAL(v1.as_array().at(0).as_integer(), 6);
BOOST_CHECK_EQUAL(v1.as_array().at(1).as_integer(), 7);
BOOST_CHECK_EQUAL(v1.as_array().at(2).as_integer(), 8);
BOOST_CHECK_EQUAL(v1.as_array().at(3).as_integer(), 9);
BOOST_CHECK_EQUAL(v1.as_array().at(4).as_integer(), 0);
BOOST_CHECK_EQUAL(v2.cast<toml::value_t::Array>().at(0).cast<toml::value_t::Integer>(), 1); BOOST_CHECK_EQUAL(v2.cast<toml::value_t::Array>().at(0).cast<toml::value_t::Integer>(), 1);
BOOST_CHECK_EQUAL(v2.cast<toml::value_t::Array>().at(1).cast<toml::value_t::Integer>(), 2); BOOST_CHECK_EQUAL(v2.cast<toml::value_t::Array>().at(1).cast<toml::value_t::Integer>(), 2);
BOOST_CHECK_EQUAL(v2.cast<toml::value_t::Array>().at(2).cast<toml::value_t::Integer>(), 3); BOOST_CHECK_EQUAL(v2.cast<toml::value_t::Array>().at(2).cast<toml::value_t::Integer>(), 3);
BOOST_CHECK_EQUAL(v2.cast<toml::value_t::Array>().at(3).cast<toml::value_t::Integer>(), 4); BOOST_CHECK_EQUAL(v2.cast<toml::value_t::Array>().at(3).cast<toml::value_t::Integer>(), 4);
BOOST_CHECK_EQUAL(v2.cast<toml::value_t::Array>().at(4).cast<toml::value_t::Integer>(), 5); BOOST_CHECK_EQUAL(v2.cast<toml::value_t::Array>().at(4).cast<toml::value_t::Integer>(), 5);
BOOST_CHECK_EQUAL(v2.as_array().at(0).as_integer(), 1);
BOOST_CHECK_EQUAL(v2.as_array().at(1).as_integer(), 2);
BOOST_CHECK_EQUAL(v2.as_array().at(2).as_integer(), 3);
BOOST_CHECK_EQUAL(v2.as_array().at(3).as_integer(), 4);
BOOST_CHECK_EQUAL(v2.as_array().at(4).as_integer(), 5);
toml::value v3(v1); toml::value v3(v1);
BOOST_CHECK(v3 == v1); BOOST_CHECK(v3 == v1);
@@ -623,6 +763,12 @@ BOOST_AUTO_TEST_CASE(test_value_array)
BOOST_CHECK_EQUAL(v3.cast<toml::value_t::Array>().at(2).cast<toml::value_t::Integer>(), 8); BOOST_CHECK_EQUAL(v3.cast<toml::value_t::Array>().at(2).cast<toml::value_t::Integer>(), 8);
BOOST_CHECK_EQUAL(v3.cast<toml::value_t::Array>().at(3).cast<toml::value_t::Integer>(), 9); BOOST_CHECK_EQUAL(v3.cast<toml::value_t::Array>().at(3).cast<toml::value_t::Integer>(), 9);
BOOST_CHECK_EQUAL(v3.cast<toml::value_t::Array>().at(4).cast<toml::value_t::Integer>(), 0); BOOST_CHECK_EQUAL(v3.cast<toml::value_t::Array>().at(4).cast<toml::value_t::Integer>(), 0);
BOOST_CHECK_EQUAL(v3.as_array().at(0).as_integer(), 6);
BOOST_CHECK_EQUAL(v3.as_array().at(1).as_integer(), 7);
BOOST_CHECK_EQUAL(v3.as_array().at(2).as_integer(), 8);
BOOST_CHECK_EQUAL(v3.as_array().at(3).as_integer(), 9);
BOOST_CHECK_EQUAL(v3.as_array().at(4).as_integer(), 0);
v1 = true; v1 = true;
BOOST_CHECK_EQUAL(v1.type(), toml::value_t::Boolean); BOOST_CHECK_EQUAL(v1.type(), toml::value_t::Boolean);
@@ -630,6 +776,7 @@ BOOST_AUTO_TEST_CASE(test_value_array)
BOOST_CHECK(v1.is<toml::Boolean>()); BOOST_CHECK(v1.is<toml::Boolean>());
BOOST_CHECK(v1.is_boolean()); BOOST_CHECK(v1.is_boolean());
BOOST_CHECK_EQUAL(v1.cast<toml::value_t::Boolean>(), true); BOOST_CHECK_EQUAL(v1.cast<toml::value_t::Boolean>(), true);
BOOST_CHECK_EQUAL(v1.as_boolean(), true);
} }
BOOST_AUTO_TEST_CASE(test_value_table) BOOST_AUTO_TEST_CASE(test_value_table)
@@ -644,6 +791,10 @@ BOOST_AUTO_TEST_CASE(test_value_table)
BOOST_CHECK_EQUAL(v1.cast<toml::value_t::Table>().at("foo").cast<toml::value_t::Integer>(), 42); BOOST_CHECK_EQUAL(v1.cast<toml::value_t::Table>().at("foo").cast<toml::value_t::Integer>(), 42);
BOOST_CHECK_EQUAL(v1.cast<toml::value_t::Table>().at("bar").cast<toml::value_t::Float>(), 3.14); BOOST_CHECK_EQUAL(v1.cast<toml::value_t::Table>().at("bar").cast<toml::value_t::Float>(), 3.14);
BOOST_CHECK_EQUAL(v1.cast<toml::value_t::Table>().at("baz").cast<toml::value_t::String>().str, "qux"); BOOST_CHECK_EQUAL(v1.cast<toml::value_t::Table>().at("baz").cast<toml::value_t::String>().str, "qux");
BOOST_CHECK_EQUAL(v1.as_table().at("foo").as_integer(), 42);
BOOST_CHECK_EQUAL(v1.as_table().at("bar").as_float(), 3.14);
BOOST_CHECK_EQUAL(v1.as_table().at("baz").as_string().str, "qux");
v1 = toml::table{{"foo", 2.71}, {"bar", 54}, {"baz", "quux"}}; v1 = toml::table{{"foo", 2.71}, {"bar", 54}, {"baz", "quux"}};
@@ -655,6 +806,10 @@ BOOST_AUTO_TEST_CASE(test_value_table)
BOOST_CHECK_EQUAL(v1.cast<toml::value_t::Table>().at("foo").cast<toml::value_t::Float>(), 2.71); BOOST_CHECK_EQUAL(v1.cast<toml::value_t::Table>().at("foo").cast<toml::value_t::Float>(), 2.71);
BOOST_CHECK_EQUAL(v1.cast<toml::value_t::Table>().at("bar").cast<toml::value_t::Integer>(), 54); BOOST_CHECK_EQUAL(v1.cast<toml::value_t::Table>().at("bar").cast<toml::value_t::Integer>(), 54);
BOOST_CHECK_EQUAL(v1.cast<toml::value_t::Table>().at("baz").cast<toml::value_t::String>().str, "quux"); BOOST_CHECK_EQUAL(v1.cast<toml::value_t::Table>().at("baz").cast<toml::value_t::String>().str, "quux");
BOOST_CHECK_EQUAL(v1.as_table().at("foo").as_float(), 2.71);
BOOST_CHECK_EQUAL(v1.as_table().at("bar").as_integer(), 54);
BOOST_CHECK_EQUAL(v1.as_table().at("baz").as_string().str, "quux");
toml::value v3(v1); toml::value v3(v1);
BOOST_CHECK(v3 == v1); BOOST_CHECK(v3 == v1);
@@ -667,6 +822,10 @@ BOOST_AUTO_TEST_CASE(test_value_table)
BOOST_CHECK_EQUAL(v3.cast<toml::value_t::Table>().at("foo").cast<toml::value_t::Float>(), 2.71); BOOST_CHECK_EQUAL(v3.cast<toml::value_t::Table>().at("foo").cast<toml::value_t::Float>(), 2.71);
BOOST_CHECK_EQUAL(v3.cast<toml::value_t::Table>().at("bar").cast<toml::value_t::Integer>(), 54); BOOST_CHECK_EQUAL(v3.cast<toml::value_t::Table>().at("bar").cast<toml::value_t::Integer>(), 54);
BOOST_CHECK_EQUAL(v3.cast<toml::value_t::Table>().at("baz").cast<toml::value_t::String>().str, "quux"); BOOST_CHECK_EQUAL(v3.cast<toml::value_t::Table>().at("baz").cast<toml::value_t::String>().str, "quux");
BOOST_CHECK_EQUAL(v3.as_table().at("foo").as_float(), 2.71);
BOOST_CHECK_EQUAL(v3.as_table().at("bar").as_integer(), 54);
BOOST_CHECK_EQUAL(v3.as_table().at("baz").as_string().str, "quux");
v1 = true; v1 = true;
BOOST_CHECK_EQUAL(v1.type(), toml::value_t::Boolean); BOOST_CHECK_EQUAL(v1.type(), toml::value_t::Boolean);
@@ -674,6 +833,7 @@ BOOST_AUTO_TEST_CASE(test_value_table)
BOOST_CHECK(v1.is<toml::Boolean>()); BOOST_CHECK(v1.is<toml::Boolean>());
BOOST_CHECK(v1.is_boolean()); BOOST_CHECK(v1.is_boolean());
BOOST_CHECK_EQUAL(v1.cast<toml::value_t::Boolean>(), true); BOOST_CHECK_EQUAL(v1.cast<toml::value_t::Boolean>(), true);
BOOST_CHECK_EQUAL(v1.as_boolean(), true);
} }
BOOST_AUTO_TEST_CASE(test_value_empty) BOOST_AUTO_TEST_CASE(test_value_empty)

View File

@@ -9,7 +9,10 @@
#include <type_traits> #include <type_traits>
#include <iterator> #include <iterator>
#include <limits> #include <limits>
#include <array>
#include <iomanip> #include <iomanip>
#include <cstdio>
#include <cassert>
#include <cctype> #include <cctype>
// they scans characters and returns region if it matches to the condition. // they scans characters and returns region if it matches to the condition.
@@ -38,10 +41,12 @@ inline std::string show_char(const char c)
} }
else else
{ {
std::ostringstream oss; std::array<char, 5> buf;
oss << "0x" << std::hex << std::setfill('0') << std::setw(2) buf.fill('\0');
<< static_cast<int>(c); const auto r = std::snprintf(
return oss.str(); buf.data(), buf.size(), "0x%02x", static_cast<int>(c) & 0xFF);
assert(r == buf.size() - 1);
return std::string(buf.data());
} }
} }
@@ -51,26 +56,24 @@ struct character
static constexpr char target = C; static constexpr char target = C;
template<typename Cont> template<typename Cont>
static result<region<Cont>, std::string> invoke(location<Cont>& loc) static result<region<Cont>, none_t>
invoke(location<Cont>& loc)
{ {
static_assert(std::is_same<char, typename Cont::value_type>::value, static_assert(std::is_same<char, typename Cont::value_type>::value,
"internal error: container::value_type should be `char`."); "internal error: container::value_type should be `char`.");
if(loc.iter() == loc.end()) {return err("not sufficient characters");} if(loc.iter() == loc.end()) {return none();}
const auto first = loc.iter(); const auto first = loc.iter();
const char c = *(loc.iter()); const char c = *(loc.iter());
if(c != target) if(c != target)
{ {
return err(concat_to_string("expected '", show_char(target), return none();
"' but got '", show_char(c), "'."));
} }
++(loc.iter()); // update location loc.advance(); // update location
return ok(region<Cont>(loc, first, loc.iter())); return ok(region<Cont>(loc, first, loc.iter()));
} }
static std::string pattern() {return show_char(target);}
}; };
template<char C> template<char C>
constexpr char character<C>::target; constexpr char character<C>::target;
@@ -86,30 +89,24 @@ struct in_range
static constexpr char lower = Low; static constexpr char lower = Low;
template<typename Cont> template<typename Cont>
static result<region<Cont>, std::string> invoke(location<Cont>& loc) static result<region<Cont>, none_t>
invoke(location<Cont>& loc)
{ {
static_assert(std::is_same<char, typename Cont::value_type>::value, static_assert(std::is_same<char, typename Cont::value_type>::value,
"internal error: container::value_type should be `char`."); "internal error: container::value_type should be `char`.");
if(loc.iter() == loc.end()) {return err("not sufficient characters");} if(loc.iter() == loc.end()) {return none();}
const auto first = loc.iter(); const auto first = loc.iter();
const char c = *(loc.iter()); const char c = *(loc.iter());
if(c < lower || upper < c) if(c < lower || upper < c)
{ {
return err(concat_to_string("expected character in range " return none();
"[", show_char(lower), ", ", show_char(upper), "] but got ",
"'", show_char(c), "'."));
} }
++(loc.iter()); loc.advance();
return ok(region<Cont>(loc, first, loc.iter())); return ok(region<Cont>(loc, first, loc.iter()));
} }
static std::string pattern()
{
return concat_to_string("[",show_char(lower),"-",show_char(upper),"]");
}
}; };
template<char L, char U> constexpr char in_range<L, U>::upper; template<char L, char U> constexpr char in_range<L, U>::upper;
template<char L, char U> constexpr char in_range<L, U>::lower; template<char L, char U> constexpr char in_range<L, U>::lower;
@@ -120,30 +117,24 @@ template<typename Combinator>
struct exclude struct exclude
{ {
template<typename Cont> template<typename Cont>
static result<region<Cont>, std::string> invoke(location<Cont>& loc) static result<region<Cont>, none_t>
invoke(location<Cont>& loc)
{ {
static_assert(std::is_same<char, typename Cont::value_type>::value, static_assert(std::is_same<char, typename Cont::value_type>::value,
"internal error: container::value_type should be `char`."); "internal error: container::value_type should be `char`.");
if(loc.iter() == loc.end()) {return err("not sufficient characters");} if(loc.iter() == loc.end()) {return none();}
auto first = loc.iter(); auto first = loc.iter();
auto rslt = Combinator::invoke(loc); auto rslt = Combinator::invoke(loc);
if(rslt.is_ok()) if(rslt.is_ok())
{ {
loc.iter() = first; // rollback loc.reset(first);
return err(concat_to_string( return none();
"invalid pattern (", Combinator::pattern(), ") appeared ",
rslt.unwrap().str()));
} }
loc.iter() = std::next(first); loc.reset(std::next(first)); // XXX maybe loc.advance() is okay but...
return ok(region<Cont>(loc, first, loc.iter())); return ok(region<Cont>(loc, first, loc.iter()));
} }
static std::string pattern()
{
return concat_to_string("^(", Combinator::pattern(), ')');
}
}; };
// increment `iter`, if matches. otherwise, just return empty string. // increment `iter`, if matches. otherwise, just return empty string.
@@ -151,7 +142,8 @@ template<typename Combinator>
struct maybe struct maybe
{ {
template<typename Cont> template<typename Cont>
static result<region<Cont>, std::string> invoke(location<Cont>& loc) static result<region<Cont>, none_t>
invoke(location<Cont>& loc)
{ {
static_assert(std::is_same<char, typename Cont::value_type>::value, static_assert(std::is_same<char, typename Cont::value_type>::value,
"internal error: container::value_type should be `char`."); "internal error: container::value_type should be `char`.");
@@ -163,11 +155,6 @@ struct maybe
} }
return ok(region<Cont>(loc)); return ok(region<Cont>(loc));
} }
static std::string pattern()
{
return concat_to_string('(', Combinator::pattern(), ")?");
}
}; };
template<typename ... Ts> template<typename ... Ts>
@@ -177,7 +164,8 @@ template<typename Head, typename ... Tail>
struct sequence<Head, Tail...> struct sequence<Head, Tail...>
{ {
template<typename Cont> template<typename Cont>
static result<region<Cont>, std::string> invoke(location<Cont>& loc) static result<region<Cont>, none_t>
invoke(location<Cont>& loc)
{ {
static_assert(std::is_same<char, typename Cont::value_type>::value, static_assert(std::is_same<char, typename Cont::value_type>::value,
"internal error: container::value_type should be `char`."); "internal error: container::value_type should be `char`.");
@@ -186,31 +174,26 @@ struct sequence<Head, Tail...>
const auto rslt = Head::invoke(loc); const auto rslt = Head::invoke(loc);
if(rslt.is_err()) if(rslt.is_err())
{ {
loc.iter() = first; loc.reset(first);
return err(rslt.unwrap_err()); return none();
} }
return sequence<Tail...>::invoke(loc, std::move(rslt.unwrap()), first); return sequence<Tail...>::invoke(loc, std::move(rslt.unwrap()), first);
} }
// called from the above function only, recursively. // called from the above function only, recursively.
template<typename Cont, typename Iterator> template<typename Cont, typename Iterator>
static result<region<Cont>, std::string> static result<region<Cont>, none_t>
invoke(location<Cont>& loc, region<Cont> reg, Iterator first) invoke(location<Cont>& loc, region<Cont> reg, Iterator first)
{ {
const auto rslt = Head::invoke(loc); const auto rslt = Head::invoke(loc);
if(rslt.is_err()) if(rslt.is_err())
{ {
loc.iter() = first; loc.reset(first);
return err(rslt.unwrap_err()); return none();
} }
reg += rslt.unwrap(); // concat regions reg += rslt.unwrap(); // concat regions
return sequence<Tail...>::invoke(loc, std::move(reg), first); return sequence<Tail...>::invoke(loc, std::move(reg), first);
} }
static std::string pattern()
{
return concat_to_string(Head::pattern(), sequence<Tail...>::pattern());
}
}; };
template<typename Head> template<typename Head>
@@ -218,19 +201,18 @@ struct sequence<Head>
{ {
// would be called from sequence<T ...>::invoke only. // would be called from sequence<T ...>::invoke only.
template<typename Cont, typename Iterator> template<typename Cont, typename Iterator>
static result<region<Cont>, std::string> static result<region<Cont>, none_t>
invoke(location<Cont>& loc, region<Cont> reg, Iterator first) invoke(location<Cont>& loc, region<Cont> reg, Iterator first)
{ {
const auto rslt = Head::invoke(loc); const auto rslt = Head::invoke(loc);
if(rslt.is_err()) if(rslt.is_err())
{ {
loc.iter() = first; loc.reset(first);
return err(rslt.unwrap_err()); return none();
} }
reg += rslt.unwrap(); // concat regions reg += rslt.unwrap(); // concat regions
return ok(reg); return ok(reg);
} }
static std::string pattern() {return Head::pattern();}
}; };
template<typename ... Ts> template<typename ... Ts>
@@ -240,7 +222,8 @@ template<typename Head, typename ... Tail>
struct either<Head, Tail...> struct either<Head, Tail...>
{ {
template<typename Cont> template<typename Cont>
static result<region<Cont>, std::string> invoke(location<Cont>& loc) static result<region<Cont>, none_t>
invoke(location<Cont>& loc)
{ {
static_assert(std::is_same<char, typename Cont::value_type>::value, static_assert(std::is_same<char, typename Cont::value_type>::value,
"internal error: container::value_type should be `char`."); "internal error: container::value_type should be `char`.");
@@ -249,26 +232,18 @@ struct either<Head, Tail...>
if(rslt.is_ok()) {return rslt;} if(rslt.is_ok()) {return rslt;}
return either<Tail...>::invoke(loc); return either<Tail...>::invoke(loc);
} }
static std::string pattern()
{
return concat_to_string('(', Head::pattern(), ")|", either<Tail...>::pattern());
}
}; };
template<typename Head> template<typename Head>
struct either<Head> struct either<Head>
{ {
template<typename Cont> template<typename Cont>
static result<region<Cont>, std::string> invoke(location<Cont>& loc) static result<region<Cont>, none_t>
invoke(location<Cont>& loc)
{ {
static_assert(std::is_same<char, typename Cont::value_type>::value, static_assert(std::is_same<char, typename Cont::value_type>::value,
"internal error: container::value_type should be `char`."); "internal error: container::value_type should be `char`.");
return Head::invoke(loc); return Head::invoke(loc);
} }
static std::string pattern()
{
return concat_to_string('(', Head::pattern(), ')');
}
}; };
template<typename T, typename N> template<typename T, typename N>
@@ -282,7 +257,8 @@ template<typename T, std::size_t N>
struct repeat<T, exactly<N>> struct repeat<T, exactly<N>>
{ {
template<typename Cont> template<typename Cont>
static result<region<Cont>, std::string> invoke(location<Cont>& loc) static result<region<Cont>, none_t>
invoke(location<Cont>& loc)
{ {
region<Cont> retval(loc); region<Cont> retval(loc);
const auto first = loc.iter(); const auto first = loc.iter();
@@ -291,24 +267,21 @@ struct repeat<T, exactly<N>>
auto rslt = T::invoke(loc); auto rslt = T::invoke(loc);
if(rslt.is_err()) if(rslt.is_err())
{ {
loc.iter() = first; loc.reset(first);
return err(rslt.unwrap_err()); return none();
} }
retval += rslt.unwrap(); retval += rslt.unwrap();
} }
return ok(std::move(retval)); return ok(std::move(retval));
} }
static std::string pattern()
{
return concat_to_string('(', T::pattern(), "){", N, '}');
}
}; };
template<typename T, std::size_t N> template<typename T, std::size_t N>
struct repeat<T, at_least<N>> struct repeat<T, at_least<N>>
{ {
template<typename Cont> template<typename Cont>
static result<region<Cont>, std::string> invoke(location<Cont>& loc) static result<region<Cont>, none_t>
invoke(location<Cont>& loc)
{ {
region<Cont> retval(loc); region<Cont> retval(loc);
@@ -318,8 +291,8 @@ struct repeat<T, at_least<N>>
auto rslt = T::invoke(loc); auto rslt = T::invoke(loc);
if(rslt.is_err()) if(rslt.is_err())
{ {
loc.iter() = first; loc.reset(first);
return err(rslt.unwrap_err()); return none();
} }
retval += rslt.unwrap(); retval += rslt.unwrap();
} }
@@ -333,17 +306,14 @@ struct repeat<T, at_least<N>>
retval += rslt.unwrap(); retval += rslt.unwrap();
} }
} }
static std::string pattern()
{
return concat_to_string('(',T::pattern(), "){", N, ",}");
}
}; };
template<typename T> template<typename T>
struct repeat<T, unlimited> struct repeat<T, unlimited>
{ {
template<typename Cont> template<typename Cont>
static result<region<Cont>, std::string> invoke(location<Cont>& loc) static result<region<Cont>, none_t>
invoke(location<Cont>& loc)
{ {
region<Cont> retval(loc); region<Cont> retval(loc);
while(true) while(true)
@@ -356,7 +326,6 @@ struct repeat<T, unlimited>
retval += rslt.unwrap(); retval += rslt.unwrap();
} }
} }
static std::string pattern() {return concat_to_string('(', T::pattern(), ")*");}
}; };
} // detail } // detail

View File

@@ -25,20 +25,22 @@ inline std::tm localtime_s(const std::time_t* src)
{ {
std::tm dst; std::tm dst;
const auto result = ::localtime_r(src, &dst); const auto result = ::localtime_r(src, &dst);
if(!result) if (!result) { throw std::runtime_error("localtime_r failed."); }
{ return dst;
throw std::runtime_error("localtime_r failed."); }
} #elif _MSC_VER
inline std::tm localtime_s(const std::time_t* src)
{
std::tm dst;
const auto result = ::localtime_s(&dst, src);
if (result) { throw std::runtime_error("localtime_s failed."); }
return dst; return dst;
} }
#else #else
// XXX: On Windows, std::localtime is thread-safe because they uses thread-local
// storage to store the instance of std::tm. On the other platforms, it may not
// be thread-safe.
inline std::tm localtime_s(const std::time_t* src) inline std::tm localtime_s(const std::time_t* src)
{ {
const auto result = std::localtime(src); const auto result = std::localtime(src);
if(!result) {throw std::runtime_error("localtime failed.");} if (!result) { throw std::runtime_error("localtime failed."); }
return *result; return *result;
} }
#endif #endif
@@ -351,21 +353,21 @@ struct local_datetime
explicit local_datetime(const std::chrono::system_clock::time_point& tp) explicit local_datetime(const std::chrono::system_clock::time_point& tp)
{ {
const auto t = std::chrono::system_clock::to_time_t(tp); const auto t = std::chrono::system_clock::to_time_t(tp);
std::tm time = detail::localtime_s(&t); std::tm ltime = detail::localtime_s(&t);
this->date = local_date(time); this->date = local_date(ltime);
this->time = local_time(time); this->time = local_time(ltime);
// std::tm lacks subsecond information, so diff between tp and tm // std::tm lacks subsecond information, so diff between tp and tm
// can be used to get millisecond & microsecond information. // can be used to get millisecond & microsecond information.
const auto t_diff = tp - const auto t_diff = tp -
std::chrono::system_clock::from_time_t(std::mktime(&time)); std::chrono::system_clock::from_time_t(std::mktime(&ltime));
this->time.millisecond = std::chrono::duration_cast< this->time.millisecond = static_cast<std::uint16_t>(
std::chrono::milliseconds>(t_diff).count(); std::chrono::duration_cast<std::chrono::milliseconds>(t_diff).count());
this->time.microsecond = std::chrono::duration_cast< this->time.microsecond = static_cast<std::uint16_t>(
std::chrono::microseconds>(t_diff).count(); std::chrono::duration_cast<std::chrono::microseconds>(t_diff).count());
this->time.nanosecond = std::chrono::duration_cast< this->time.nanosecond = static_cast<std::uint16_t>(
std::chrono::nanoseconds >(t_diff).count(); std::chrono::duration_cast<std::chrono::nanoseconds >(t_diff).count());
} }
explicit local_datetime(const std::time_t t) explicit local_datetime(const std::time_t t)

View File

@@ -51,7 +51,7 @@ struct from_toml_tie_impl
template<typename ... Ts> template<typename ... Ts>
struct from_toml_tie_impl<0, Ts...> struct from_toml_tie_impl<0, Ts...>
{ {
static void invoke(std::tuple<Ts& ...> tie, const toml::value& v) static void invoke(std::tuple<Ts& ...>, const toml::value&)
{ {
return; return;
} }

View File

@@ -108,6 +108,18 @@ inline std::string get(value&& v)
return std::move(v.cast<value_t::String>().str); return std::move(v.cast<value_t::String>().str);
} }
// ============================================================================
// std::string_view
#if __cplusplus >= 201703L
template<typename T, typename std::enable_if<
std::is_same<T, std::string_view>::value, std::nullptr_t>::type = nullptr>
inline std::string_view get(const value& v)
{
return std::string_view(v.cast<value_t::String>().str);
}
#endif
// ============================================================================ // ============================================================================
// std::chrono::duration from toml::local_time. // std::chrono::duration from toml::local_time.
@@ -264,7 +276,7 @@ namespace detail
{ {
template<typename T, std::size_t ...I> template<typename T, std::size_t ...I>
T get_tuple_impl(const toml::Array& a, index_sequence<I...>) T get_tuple_impl(const toml::array& a, index_sequence<I...>)
{ {
return std::make_tuple( return std::make_tuple(
::toml::get<typename std::tuple_element<I, T>::type>(a.at(I))...); ::toml::get<typename std::tuple_element<I, T>::type>(a.at(I))...);
@@ -546,67 +558,6 @@ T get_or(const toml::value& v, T&& opt)
} }
} }
// ===========================================================================
// get_or(table, key, fallback)
//
// DEPRECATED: use find_or instead.
template<typename T>
TOML11_MARK_AS_DEPRECATED("use toml::find_or(table, key, opt) instead.")
auto get_or(const toml::table& tab, const toml::key& ky, T&& opt)
-> decltype(get_or(std::declval<value const&>(), std::forward<T>(opt)))
{
if(tab.count(ky) == 0) {return opt;}
return ::toml::get_or(tab.at(ky), std::forward<T>(opt));
}
template<typename T>
TOML11_MARK_AS_DEPRECATED("use toml::find_or(table, key, opt) instead.")
auto get_or(toml::table& tab, const toml::key& ky, T&& opt)
-> decltype(get_or(std::declval<value&>(), std::forward<T>(opt)))
{
if(tab.count(ky) == 0) {return opt;}
return ::toml::get_or(tab[ky], std::forward<T>(opt));
}
template<typename T>
TOML11_MARK_AS_DEPRECATED("use toml::find_or(table, key, opt) instead.")
auto get_or(toml::table&& tab, const toml::key& ky, T&& opt)
-> decltype(get_or(std::declval<value&&>(), std::forward<T>(opt)))
{
if(tab.count(ky) == 0) {return opt;}
return ::toml::get_or(std::move(tab[ky]), std::forward<T>(opt));
}
template<typename T>
TOML11_MARK_AS_DEPRECATED("use toml::find_or(value, key, opt) instead.")
auto get_or(const toml::value& v, const toml::key& ky, T&& opt)
-> decltype(get_or(std::declval<value const&>(), std::forward<T>(opt)))
{
if(!v.is_table()) {return opt;}
const auto& tab = toml::get<toml::table>(v);
if(tab.count(ky) == 0) {return opt;}
return ::toml::get_or(tab.at(ky), std::forward<T>(opt));
}
template<typename T>
TOML11_MARK_AS_DEPRECATED("use toml::find_or(value, key, opt) instead.")
auto get_or(toml::value& v, const toml::key& ky, T&& opt)
-> decltype(get_or(std::declval<value&>(), std::forward<T>(opt)))
{
if(!v.is_table()) {return opt;}
auto& tab = toml::get<toml::table>(v);
if(tab.count(ky) == 0) {return opt;}
return ::toml::get_or(tab[ky], std::forward<T>(opt));
}
template<typename T>
TOML11_MARK_AS_DEPRECATED("use toml::find_or(value, key, opt) instead.")
auto get_or(toml::value&& v, const toml::key& ky, T&& opt)
-> decltype(get_or(std::declval<value&&>(), std::forward<T>(opt)))
{
if(!v.is_table()) {return opt;}
auto tab = toml::get<toml::table>(std::move(v));
if(tab.count(ky) == 0) {return opt;}
return ::toml::get_or(std::move(tab[ky]), std::forward<T>(opt));
}
// =========================================================================== // ===========================================================================
// find_or(value, key, fallback) // find_or(value, key, fallback)

View File

@@ -30,16 +30,52 @@ inline ::toml::value operator""_toml(const char* str, std::size_t len)
::toml::detail::lex_ws, ::toml::detail::at_least<1>>; ::toml::detail::lex_ws, ::toml::detail::at_least<1>>;
skip_ws::invoke(loc); skip_ws::invoke(loc);
// literal may be a bare value. try them first. // to distinguish arrays and tables, first check it is a table or not.
if(auto data = ::toml::detail::parse_value(loc)) //
// "[1,2,3]"_toml; // this is an array
// "[table]"_toml; // a table that has an empty table named "table" inside.
// "[[1,2,3]]"_toml; // this is an array of arrays
// "[[table]]"_toml; // this is a table that has an array of tables inside.
//
// "[[1]]"_toml; // this can be both... (currently it becomes a table)
// "1 = [{}]"_toml; // this is a table that has an array of table named 1.
// "[[1,]]"_toml; // this is an array of arrays.
// "[[1],]"_toml; // this also.
const auto the_front = loc.iter();
const bool is_table_key = ::toml::detail::lex_std_table::invoke(loc);
loc.reset(the_front);
const bool is_aots_key = ::toml::detail::lex_array_table::invoke(loc);
loc.reset(the_front);
// If it is neither a table-key or a array-of-table-key, it may be a value.
if(!is_table_key && !is_aots_key)
{ {
return data.unwrap(); if(auto data = ::toml::detail::parse_value(loc))
{
return data.unwrap();
}
} }
// literal is a TOML file (i.e. multiline table). // Note that still it can be a table, because the literal might be something
// like the following.
// ```cpp
// R"( // c++11 raw string literals
// key = "value"
// int = 42
// )"_toml;
// ```
// It is a valid toml file.
// It should be parsed as if we parse a file with this content.
if(auto data = ::toml::detail::parse_toml_file(loc)) if(auto data = ::toml::detail::parse_toml_file(loc))
{ {
loc.iter() = loc.begin(); // rollback to the top of the literal loc.reset(loc.begin()); // rollback to the top of the literal
// skip needless characters for error message
skip_line::invoke(loc); // skip the first several needless lines
skip_ws::invoke(loc); // skip the first several needless whitespaces
return ::toml::value(std::move(data.unwrap()), return ::toml::value(std::move(data.unwrap()),
::toml::detail::region<std::vector<char>>(std::move(loc))); ::toml::detail::region<std::vector<char>>(std::move(loc)));
} }

View File

@@ -33,7 +33,7 @@ parse_boolean(location<Container>& loc)
{{std::addressof(reg), "invalid token"}})); {{std::addressof(reg), "invalid token"}}));
} }
} }
loc.iter() = first; //rollback loc.reset(first); //rollback
return err(format_underline("[error] toml::parse_boolean: ", return err(format_underline("[error] toml::parse_boolean: ",
{{std::addressof(loc), "the next token is not a boolean"}})); {{std::addressof(loc), "the next token is not a boolean"}}));
} }
@@ -62,7 +62,7 @@ parse_binary_integer(location<Container>& loc)
} }
return ok(std::make_pair(retval, token.unwrap())); return ok(std::make_pair(retval, token.unwrap()));
} }
loc.iter() = first; loc.reset(first);
return err(format_underline("[error] toml::parse_binary_integer:", return err(format_underline("[error] toml::parse_binary_integer:",
{{std::addressof(loc), "the next token is not an integer"}})); {{std::addressof(loc), "the next token is not an integer"}}));
} }
@@ -83,7 +83,7 @@ parse_octal_integer(location<Container>& loc)
iss >> std::oct >> retval; iss >> std::oct >> retval;
return ok(std::make_pair(retval, token.unwrap())); return ok(std::make_pair(retval, token.unwrap()));
} }
loc.iter() = first; loc.reset(first);
return err(format_underline("[error] toml::parse_octal_integer:", return err(format_underline("[error] toml::parse_octal_integer:",
{{std::addressof(loc), "the next token is not an integer"}})); {{std::addressof(loc), "the next token is not an integer"}}));
} }
@@ -104,7 +104,7 @@ parse_hexadecimal_integer(location<Container>& loc)
iss >> std::hex >> retval; iss >> std::hex >> retval;
return ok(std::make_pair(retval, token.unwrap())); return ok(std::make_pair(retval, token.unwrap()));
} }
loc.iter() = first; loc.reset(first);
return err(format_underline("[error] toml::parse_hexadecimal_integer", return err(format_underline("[error] toml::parse_hexadecimal_integer",
{{std::addressof(loc), "the next token is not an integer"}})); {{std::addressof(loc), "the next token is not an integer"}}));
} }
@@ -116,10 +116,28 @@ parse_integer(location<Container>& loc)
const auto first = loc.iter(); const auto first = loc.iter();
if(first != loc.end() && *first == '0') if(first != loc.end() && *first == '0')
{ {
if(const auto bin = parse_binary_integer (loc)) {return bin;} const auto second = std::next(first);
if(const auto oct = parse_octal_integer (loc)) {return oct;} if(second == loc.end()) // the token is just zero.
if(const auto hex = parse_hexadecimal_integer(loc)) {return hex;} {
// else, maybe just zero. return ok(std::make_pair(0, region<Container>(loc, first, second)));
}
if(*second == 'b') {return parse_binary_integer (loc);} // 0b1100
if(*second == 'o') {return parse_octal_integer (loc);} // 0o775
if(*second == 'x') {return parse_hexadecimal_integer(loc);} // 0xC0FFEE
if(std::isdigit(*second))
{
return err(format_underline("[error] toml::parse_integer: "
"leading zero in an Integer is not allowed.",
{{std::addressof(loc), "leading zero"}}));
}
else if(std::isalpha(*second))
{
return err(format_underline("[error] toml::parse_integer: "
"unknown integer prefix appeared.",
{{std::addressof(loc), "none of 0x, 0o, 0b"}}));
}
} }
if(const auto token = lex_dec_int::invoke(loc)) if(const auto token = lex_dec_int::invoke(loc))
@@ -132,7 +150,7 @@ parse_integer(location<Container>& loc)
iss >> retval; iss >> retval;
return ok(std::make_pair(retval, token.unwrap())); return ok(std::make_pair(retval, token.unwrap()));
} }
loc.iter() = first; loc.reset(first);
return err(format_underline("[error] toml::parse_integer: ", return err(format_underline("[error] toml::parse_integer: ",
{{std::addressof(loc), "the next token is not an integer"}})); {{std::addressof(loc), "the next token is not an integer"}}));
} }
@@ -221,7 +239,7 @@ parse_floating(location<Container>& loc)
iss >> v; iss >> v;
return ok(std::make_pair(v, token.unwrap())); return ok(std::make_pair(v, token.unwrap()));
} }
loc.iter() = first; loc.reset(first);
return err(format_underline("[error] toml::parse_floating: ", return err(format_underline("[error] toml::parse_floating: ",
{{std::addressof(loc), "the next token is not a float"}})); {{std::addressof(loc), "the next token is not a float"}}));
} }
@@ -288,16 +306,16 @@ result<std::string, std::string> parse_escape_sequence(location<Container>& loc)
return err(format_underline("[error]: toml::parse_escape_sequence: ", {{ return err(format_underline("[error]: toml::parse_escape_sequence: ", {{
std::addressof(loc), "the next token is not a backslash \"\\\""}})); std::addressof(loc), "the next token is not a backslash \"\\\""}}));
} }
++loc.iter(); loc.advance();
switch(*loc.iter()) switch(*loc.iter())
{ {
case '\\':{++loc.iter(); return ok(std::string("\\"));} case '\\':{loc.advance(); return ok(std::string("\\"));}
case '"' :{++loc.iter(); return ok(std::string("\""));} case '"' :{loc.advance(); return ok(std::string("\""));}
case 'b' :{++loc.iter(); return ok(std::string("\b"));} case 'b' :{loc.advance(); return ok(std::string("\b"));}
case 't' :{++loc.iter(); return ok(std::string("\t"));} case 't' :{loc.advance(); return ok(std::string("\t"));}
case 'n' :{++loc.iter(); return ok(std::string("\n"));} case 'n' :{loc.advance(); return ok(std::string("\n"));}
case 'f' :{++loc.iter(); return ok(std::string("\f"));} case 'f' :{loc.advance(); return ok(std::string("\f"));}
case 'r' :{++loc.iter(); return ok(std::string("\r"));} case 'r' :{loc.advance(); return ok(std::string("\r"));}
case 'u' : case 'u' :
{ {
if(const auto token = lex_escape_unicode_short::invoke(loc)) if(const auto token = lex_escape_unicode_short::invoke(loc))
@@ -308,7 +326,7 @@ result<std::string, std::string> parse_escape_sequence(location<Container>& loc)
{ {
return err(format_underline("[error] parse_escape_sequence: " return err(format_underline("[error] parse_escape_sequence: "
"invalid token found in UTF-8 codepoint uXXXX.", "invalid token found in UTF-8 codepoint uXXXX.",
{{std::addressof(loc), token.unwrap_err()}})); {{std::addressof(loc), "here"}}));
} }
} }
case 'U': case 'U':
@@ -321,7 +339,7 @@ result<std::string, std::string> parse_escape_sequence(location<Container>& loc)
{ {
return err(format_underline("[error] parse_escape_sequence: " return err(format_underline("[error] parse_escape_sequence: "
"invalid token found in UTF-8 codepoint Uxxxxxxxx", "invalid token found in UTF-8 codepoint Uxxxxxxxx",
{{std::addressof(loc), token.unwrap_err()}})); {{std::addressof(loc), "here"}}));
} }
} }
} }
@@ -331,7 +349,7 @@ result<std::string, std::string> parse_escape_sequence(location<Container>& loc)
"escape sequence is one of \\, \", b, t, n, f, r, uxxxx, Uxxxxxxxx"}}, "escape sequence is one of \\, \", b, t, n, f, r, uxxxx, Uxxxxxxxx"}},
/* Hints = */{"if you want to write backslash as just one backslash, " /* Hints = */{"if you want to write backslash as just one backslash, "
"use literal string like: regex = '<\\i\\c*\\s*>'"}); "use literal string like: regex = '<\\i\\c*\\s*>'"});
loc.iter() = first; loc.reset(first);
return err(msg); return err(msg);
} }
@@ -343,7 +361,7 @@ parse_ml_basic_string(location<Container>& loc)
if(const auto token = lex_ml_basic_string::invoke(loc)) if(const auto token = lex_ml_basic_string::invoke(loc))
{ {
auto inner_loc = loc; auto inner_loc = loc;
inner_loc.iter() = first; inner_loc.reset(first);
std::string retval; std::string retval;
retval.reserve(token.unwrap().size()); retval.reserve(token.unwrap().size());
@@ -358,7 +376,7 @@ parse_ml_basic_string(location<Container>& loc)
// immediate newline is ignored (if exists) // immediate newline is ignored (if exists)
/* discard return value */ lex_newline::invoke(inner_loc); /* discard return value */ lex_newline::invoke(inner_loc);
delim = err("tmp"); delim = none();
while(!delim) while(!delim)
{ {
using lex_unescaped_seq = repeat< using lex_unescaped_seq = repeat<
@@ -387,8 +405,10 @@ parse_ml_basic_string(location<Container>& loc)
} }
else else
{ {
loc.iter() = first; loc.reset(first);
return err(token.unwrap_err()); return err(format_underline("[error] toml::parse_ml_basic_string: "
"the next token is not a multiline string",
{{std::addressof(loc), "here"}}));
} }
} }
@@ -400,7 +420,7 @@ parse_basic_string(location<Container>& loc)
if(const auto token = lex_basic_string::invoke(loc)) if(const auto token = lex_basic_string::invoke(loc))
{ {
auto inner_loc = loc; auto inner_loc = loc;
inner_loc.iter() = first; inner_loc.reset(first);
auto quot = lex_quotation_mark::invoke(inner_loc); auto quot = lex_quotation_mark::invoke(inner_loc);
if(!quot) if(!quot)
@@ -412,7 +432,7 @@ parse_basic_string(location<Container>& loc)
std::string retval; std::string retval;
retval.reserve(token.unwrap().size()); retval.reserve(token.unwrap().size());
quot = err("tmp"); quot = none();
while(!quot) while(!quot)
{ {
using lex_unescaped_seq = repeat<lex_basic_unescaped, unlimited>; using lex_unescaped_seq = repeat<lex_basic_unescaped, unlimited>;
@@ -436,8 +456,10 @@ parse_basic_string(location<Container>& loc)
} }
else else
{ {
loc.iter() = first; // rollback loc.reset(first); // rollback
return err(token.unwrap_err()); return err(format_underline("[error] toml::parse_basic_string: "
"the next token is not a string",
{{std::addressof(loc), "here"}}));
} }
} }
@@ -475,8 +497,10 @@ parse_ml_literal_string(location<Container>& loc)
} }
else else
{ {
loc.iter() = first; // rollback loc.reset(first); // rollback
return err(token.unwrap_err()); return err(format_underline("[error] toml::parse_ml_literal_string: "
"the next token is not a multiline literal string",
{{std::addressof(loc), "here"}}));
} }
} }
@@ -512,8 +536,10 @@ parse_literal_string(location<Container>& loc)
} }
else else
{ {
loc.iter() = first; // rollback loc.reset(first); // rollback
return err(token.unwrap_err()); return err(format_underline("[error] toml::parse_literal_string: "
"the next token is not a literal string",
{{std::addressof(loc), "here"}}));
} }
} }
@@ -521,10 +547,30 @@ template<typename Container>
result<std::pair<toml::string, region<Container>>, std::string> result<std::pair<toml::string, region<Container>>, std::string>
parse_string(location<Container>& loc) parse_string(location<Container>& loc)
{ {
if(const auto rslt = parse_ml_basic_string(loc)) {return rslt;} if(loc.iter() != loc.end() && *(loc.iter()) == '"')
if(const auto rslt = parse_ml_literal_string(loc)) {return rslt;} {
if(const auto rslt = parse_basic_string(loc)) {return rslt;} if(loc.iter() + 1 != loc.end() && *(loc.iter() + 1) == '"' &&
if(const auto rslt = parse_literal_string(loc)) {return rslt;} loc.iter() + 2 != loc.end() && *(loc.iter() + 2) == '"')
{
return parse_ml_basic_string(loc);
}
else
{
return parse_basic_string(loc);
}
}
else if(loc.iter() != loc.end() && *(loc.iter()) == '\'')
{
if(loc.iter() + 1 != loc.end() && *(loc.iter() + 1) == '\'' &&
loc.iter() + 2 != loc.end() && *(loc.iter() + 2) == '\'')
{
return parse_ml_literal_string(loc);
}
else
{
return parse_literal_string(loc);
}
}
return err(format_underline("[error] toml::parse_string: ", return err(format_underline("[error] toml::parse_string: ",
{{std::addressof(loc), "the next token is not a string"}})); {{std::addressof(loc), "the next token is not a string"}}));
} }
@@ -541,31 +587,25 @@ parse_local_date(location<Container>& loc)
const auto y = lex_date_fullyear::invoke(inner_loc); const auto y = lex_date_fullyear::invoke(inner_loc);
if(!y || inner_loc.iter() == inner_loc.end() || *inner_loc.iter() != '-') if(!y || inner_loc.iter() == inner_loc.end() || *inner_loc.iter() != '-')
{ {
const std::string msg = y.map_err_or_else(
[](const std::string& msg) {return msg;}, "should be `-`");
throw internal_error(format_underline("[error]: " throw internal_error(format_underline("[error]: "
"toml::parse_inner_local_date: invalid year format", "toml::parse_inner_local_date: invalid year format",
{{std::addressof(inner_loc), msg}})); {{std::addressof(inner_loc), "should be `-`"}}));
} }
++inner_loc.iter(); inner_loc.advance();
const auto m = lex_date_month::invoke(inner_loc); const auto m = lex_date_month::invoke(inner_loc);
if(!m || inner_loc.iter() == inner_loc.end() || *inner_loc.iter() != '-') if(!m || inner_loc.iter() == inner_loc.end() || *inner_loc.iter() != '-')
{ {
const std::string msg = m.map_err_or_else(
[](const std::string& msg) {return msg;}, "should be `-`");
throw internal_error(format_underline("[error]: " throw internal_error(format_underline("[error]: "
"toml::parse_local_date: invalid month format", "toml::parse_local_date: invalid month format",
{{std::addressof(inner_loc), msg}})); {{std::addressof(inner_loc), "should be `-`"}}));
} }
++inner_loc.iter(); inner_loc.advance();
const auto d = lex_date_mday::invoke(inner_loc); const auto d = lex_date_mday::invoke(inner_loc);
if(!d) if(!d)
{ {
throw internal_error(format_underline("[error]: " throw internal_error(format_underline("[error]: "
"toml::parse_local_date: invalid day format", "toml::parse_local_date: invalid day format",
{{std::addressof(inner_loc), d.unwrap_err()}})); {{std::addressof(inner_loc), "here"}}));
} }
return ok(std::make_pair(local_date( return ok(std::make_pair(local_date(
static_cast<std::int16_t>(from_string<int>(y.unwrap().str(), 0)), static_cast<std::int16_t>(from_string<int>(y.unwrap().str(), 0)),
@@ -576,7 +616,7 @@ parse_local_date(location<Container>& loc)
} }
else else
{ {
loc.iter() = first; loc.reset(first);
return err(format_underline("[error]: toml::parse_local_date: ", return err(format_underline("[error]: toml::parse_local_date: ",
{{std::addressof(loc), "the next token is not a local_date"}})); {{std::addressof(loc), "the next token is not a local_date"}}));
} }
@@ -594,31 +634,25 @@ parse_local_time(location<Container>& loc)
const auto h = lex_time_hour::invoke(inner_loc); const auto h = lex_time_hour::invoke(inner_loc);
if(!h || inner_loc.iter() == inner_loc.end() || *inner_loc.iter() != ':') if(!h || inner_loc.iter() == inner_loc.end() || *inner_loc.iter() != ':')
{ {
const std::string msg = h.map_err_or_else(
[](const std::string& msg) {return msg;}, "should be `:`");
throw internal_error(format_underline("[error]: " throw internal_error(format_underline("[error]: "
"toml::parse_local_time: invalid year format", "toml::parse_local_time: invalid year format",
{{std::addressof(inner_loc), msg}})); {{std::addressof(inner_loc), "should be `:`"}}));
} }
++inner_loc.iter(); inner_loc.advance();
const auto m = lex_time_minute::invoke(inner_loc); const auto m = lex_time_minute::invoke(inner_loc);
if(!m || inner_loc.iter() == inner_loc.end() || *inner_loc.iter() != ':') if(!m || inner_loc.iter() == inner_loc.end() || *inner_loc.iter() != ':')
{ {
const std::string msg = m.map_err_or_else(
[](const std::string& msg) {return msg;}, "should be `:`");
throw internal_error(format_underline("[error]: " throw internal_error(format_underline("[error]: "
"toml::parse_local_time: invalid month format", "toml::parse_local_time: invalid month format",
{{std::addressof(inner_loc), msg}})); {{std::addressof(inner_loc), "should be `:`"}}));
} }
++inner_loc.iter(); inner_loc.advance();
const auto s = lex_time_second::invoke(inner_loc); const auto s = lex_time_second::invoke(inner_loc);
if(!s) if(!s)
{ {
throw internal_error(format_underline("[error]: " throw internal_error(format_underline("[error]: "
"toml::parse_local_time: invalid second format", "toml::parse_local_time: invalid second format",
{{std::addressof(inner_loc), s.unwrap_err()}})); {{std::addressof(inner_loc), "here"}}));
} }
local_time time( local_time time(
static_cast<std::int8_t>(from_string<int>(h.unwrap().str(), 0)), static_cast<std::int8_t>(from_string<int>(h.unwrap().str(), 0)),
@@ -654,14 +688,14 @@ parse_local_time(location<Container>& loc)
{ {
throw internal_error(format_underline("[error]: " throw internal_error(format_underline("[error]: "
"toml::parse_local_time: invalid subsecond format", "toml::parse_local_time: invalid subsecond format",
{{std::addressof(inner_loc), secfrac.unwrap_err()}})); {{std::addressof(inner_loc), "here"}}));
} }
} }
return ok(std::make_pair(time, token.unwrap())); return ok(std::make_pair(time, token.unwrap()));
} }
else else
{ {
loc.iter() = first; loc.reset(first);
return err(format_underline("[error]: toml::parse_local_time: ", return err(format_underline("[error]: toml::parse_local_time: ",
{{std::addressof(loc), "the next token is not a local_time"}})); {{std::addressof(loc), "the next token is not a local_time"}}));
} }
@@ -678,20 +712,18 @@ parse_local_datetime(location<Container>& loc)
const auto date = parse_local_date(inner_loc); const auto date = parse_local_date(inner_loc);
if(!date || inner_loc.iter() == inner_loc.end()) if(!date || inner_loc.iter() == inner_loc.end())
{ {
const std::string msg = date.map_err_or_else(
[](const std::string& msg) {return msg;}, "date, not datetime");
throw internal_error(format_underline("[error]: " throw internal_error(format_underline("[error]: "
"toml::parse_local_datetime: invalid datetime format", "toml::parse_local_datetime: invalid datetime format",
{{std::addressof(inner_loc), msg}})); {{std::addressof(inner_loc), "date, not datetime"}}));
} }
const char delim = *(inner_loc.iter()++); const char delim = *(inner_loc.iter());
if(delim != 'T' && delim != 't' && delim != ' ') if(delim != 'T' && delim != 't' && delim != ' ')
{ {
throw internal_error(format_underline("[error]: " throw internal_error(format_underline("[error]: "
"toml::parse_local_datetime: invalid datetime format", "toml::parse_local_datetime: invalid datetime format",
{{std::addressof(inner_loc), "should be `T` or ` ` (space)"}})); {{std::addressof(inner_loc), "should be `T` or ` ` (space)"}}));
} }
inner_loc.advance();
const auto time = parse_local_time(inner_loc); const auto time = parse_local_time(inner_loc);
if(!time) if(!time)
{ {
@@ -705,7 +737,7 @@ parse_local_datetime(location<Container>& loc)
} }
else else
{ {
loc.iter() = first; loc.reset(first);
return err(format_underline("[error]: toml::parse_local_datetime: ", return err(format_underline("[error]: toml::parse_local_datetime: ",
{{std::addressof(loc), "the next token is not a local_datetime"}})); {{std::addressof(loc), "the next token is not a local_datetime"}}));
} }
@@ -722,12 +754,9 @@ parse_offset_datetime(location<Container>& loc)
const auto datetime = parse_local_datetime(inner_loc); const auto datetime = parse_local_datetime(inner_loc);
if(!datetime || inner_loc.iter() == inner_loc.end()) if(!datetime || inner_loc.iter() == inner_loc.end())
{ {
const std::string msg = datetime.map_err_or_else(
[](const std::string& msg){return msg;}, "date, not datetime");
throw internal_error(format_underline("[error]: " throw internal_error(format_underline("[error]: "
"toml::parse_offset_datetime: invalid datetime format", "toml::parse_offset_datetime: invalid datetime format",
{{std::addressof(inner_loc), msg}})); {{std::addressof(inner_loc), "date, not datetime"}}));
} }
time_offset offset(0, 0); time_offset offset(0, 0);
if(const auto ofs = lex_time_numoffset::invoke(inner_loc)) if(const auto ofs = lex_time_numoffset::invoke(inner_loc))
@@ -755,9 +784,9 @@ parse_offset_datetime(location<Container>& loc)
} }
else else
{ {
loc.iter() = first; loc.reset(first);
return err(format_underline("[error]: toml::parse_offset_datetime: ", return err(format_underline("[error]: toml::parse_offset_datetime: ",
{{std::addressof(loc), "the next token is not a local_datetime"}})); {{std::addressof(loc), "the next token is not a offset_datetime"}}));
} }
} }
@@ -816,7 +845,7 @@ parse_key(location<Container>& loc)
} }
else if(*inner_loc.iter() == '.') else if(*inner_loc.iter() == '.')
{ {
++inner_loc.iter(); // to skip `.` inner_loc.advance(); // to skip `.`
} }
else else
{ {
@@ -827,7 +856,7 @@ parse_key(location<Container>& loc)
} }
return ok(std::make_pair(keys, reg)); return ok(std::make_pair(keys, reg));
} }
loc.iter() = first; loc.reset(first);
// simple key -> foo // simple key -> foo
if(const auto smpl = parse_simple_key(loc)) if(const auto smpl = parse_simple_key(loc))
@@ -856,7 +885,7 @@ parse_array(location<Container>& loc)
{ {
return err("[error] toml::parse_array: token is not an array"); return err("[error] toml::parse_array: token is not an array");
} }
++loc.iter(); loc.advance();
using lex_ws_comment_newline = repeat< using lex_ws_comment_newline = repeat<
either<lex_wschar, lex_newline, lex_comment>, unlimited>; either<lex_wschar, lex_newline, lex_comment>, unlimited>;
@@ -868,7 +897,7 @@ parse_array(location<Container>& loc)
if(loc.iter() != loc.end() && *loc.iter() == ']') if(loc.iter() != loc.end() && *loc.iter() == ']')
{ {
++loc.iter(); // skip ']' loc.advance(); // skip ']'
return ok(std::make_pair(retval, return ok(std::make_pair(retval,
region<Container>(loc, first, loc.iter()))); region<Container>(loc, first, loc.iter())));
} }
@@ -878,7 +907,7 @@ parse_array(location<Container>& loc)
if(!retval.empty() && retval.front().type() != val.as_ok().type()) if(!retval.empty() && retval.front().type() != val.as_ok().type())
{ {
auto array_start_loc = loc; auto array_start_loc = loc;
array_start_loc.iter() = first; array_start_loc.reset(first);
throw syntax_error(format_underline("[error] toml::parse_array: " throw syntax_error(format_underline("[error] toml::parse_array: "
"type of elements should be the same each other.", { "type of elements should be the same each other.", {
@@ -898,7 +927,7 @@ parse_array(location<Container>& loc)
else else
{ {
auto array_start_loc = loc; auto array_start_loc = loc;
array_start_loc.iter() = first; array_start_loc.reset(first);
throw syntax_error(format_underline("[error] toml::parse_array: " throw syntax_error(format_underline("[error] toml::parse_array: "
"value having invalid format appeared in an array", { "value having invalid format appeared in an array", {
@@ -914,14 +943,14 @@ parse_array(location<Container>& loc)
lex_ws_comment_newline::invoke(loc); lex_ws_comment_newline::invoke(loc);
if(loc.iter() != loc.end() && *loc.iter() == ']') if(loc.iter() != loc.end() && *loc.iter() == ']')
{ {
++loc.iter(); // skip ']' loc.advance(); // skip ']'
return ok(std::make_pair(retval, return ok(std::make_pair(retval,
region<Container>(loc, first, loc.iter()))); region<Container>(loc, first, loc.iter())));
} }
else else
{ {
auto array_start_loc = loc; auto array_start_loc = loc;
array_start_loc.iter() = first; array_start_loc.reset(first);
throw syntax_error(format_underline("[error] toml::parse_array:" throw syntax_error(format_underline("[error] toml::parse_array:"
" missing array separator `,` after a value", { " missing array separator `,` after a value", {
@@ -931,7 +960,7 @@ parse_array(location<Container>& loc)
} }
} }
} }
loc.iter() = first; loc.reset(first);
throw syntax_error(format_underline("[error] toml::parse_array: " throw syntax_error(format_underline("[error] toml::parse_array: "
"array did not closed by `]`", "array did not closed by `]`",
{{std::addressof(loc), "should be closed"}})); {{std::addressof(loc), "should be closed"}}));
@@ -950,7 +979,7 @@ parse_key_value_pair(location<Container>& loc)
// key. then we need to show error as "empty key is not allowed". // key. then we need to show error as "empty key is not allowed".
if(const auto keyval_sep = lex_keyval_sep::invoke(loc)) if(const auto keyval_sep = lex_keyval_sep::invoke(loc))
{ {
loc.iter() = first; loc.reset(first);
msg = format_underline("[error] toml::parse_key_value_pair: " msg = format_underline("[error] toml::parse_key_value_pair: "
"empty key is not allowed.", "empty key is not allowed.",
{{std::addressof(loc), "key expected before '='"}}); {{std::addressof(loc), "key expected before '='"}});
@@ -979,7 +1008,7 @@ parse_key_value_pair(location<Container>& loc)
"missing key-value separator `=`", "missing key-value separator `=`",
{{std::addressof(loc), "should be `=`"}}); {{std::addressof(loc), "should be `=`"}});
} }
loc.iter() = first; loc.reset(first);
return err(std::move(msg)); return err(std::move(msg));
} }
@@ -988,11 +1017,11 @@ parse_key_value_pair(location<Container>& loc)
if(!val) if(!val)
{ {
std::string msg; std::string msg;
loc.iter() = after_kvsp; loc.reset(after_kvsp);
// check there is something not a comment/whitespace after `=` // check there is something not a comment/whitespace after `=`
if(sequence<maybe<lex_ws>, maybe<lex_comment>, lex_newline>::invoke(loc)) if(sequence<maybe<lex_ws>, maybe<lex_comment>, lex_newline>::invoke(loc))
{ {
loc.iter() = after_kvsp; loc.reset(after_kvsp);
msg = format_underline("[error] toml::parse_key_value_pair: " msg = format_underline("[error] toml::parse_key_value_pair: "
"missing value after key-value separator '='", "missing value after key-value separator '='",
{{std::addressof(loc), "expected value, but got nothing"}}); {{std::addressof(loc), "expected value, but got nothing"}});
@@ -1001,7 +1030,7 @@ parse_key_value_pair(location<Container>& loc)
{ {
msg = std::move(val.unwrap_err()); msg = std::move(val.unwrap_err());
} }
loc.iter() = first; loc.reset(first);
return err(msg); return err(msg);
} }
return ok(std::make_pair(std::move(key_reg.unwrap()), return ok(std::make_pair(std::move(key_reg.unwrap()),
@@ -1028,6 +1057,7 @@ std::string format_dotted_keys(InputIterator first, const InputIterator last)
template<typename Container> template<typename Container>
result<std::pair<std::vector<key>, region<Container>>, std::string> result<std::pair<std::vector<key>, region<Container>>, std::string>
parse_table_key(location<Container>& loc); parse_table_key(location<Container>& loc);
// The following toml file is allowed. // The following toml file is allowed.
// ```toml // ```toml
// [a.b.c] # here, table `a` has element `b`. // [a.b.c] # here, table `a` has element `b`.
@@ -1119,7 +1149,7 @@ insert_nested_key(table& root, const toml::value& v,
{ {
if(tab->count(k) == 1) // there is already an array of table if(tab->count(k) == 1) // there is already an array of table
{ {
if(tab->at(k).is(value_t::Table)) if(tab->at(k).is_table())
{ {
// show special err msg for conflicting table // show special err msg for conflicting table
throw syntax_error(format_underline(concat_to_string( throw syntax_error(format_underline(concat_to_string(
@@ -1132,7 +1162,7 @@ insert_nested_key(table& root, const toml::value& v,
"this conflicts with the previous table"} "this conflicts with the previous table"}
})); }));
} }
else if(!(tab->at(k).is(value_t::Array))) else if(!(tab->at(k).is_array()))
{ {
throw syntax_error(format_underline(concat_to_string( throw syntax_error(format_underline(concat_to_string(
"[error] toml::insert_value: array of table (\"", "[error] toml::insert_value: array of table (\"",
@@ -1145,8 +1175,9 @@ insert_nested_key(table& root, const toml::value& v,
"while inserting this array-of-tables"} "while inserting this array-of-tables"}
})); }));
} }
array& a = tab->at(k).template cast<toml::value_t::Array>(); // the above if-else-if checks tab->at(k) is an array
if(!(a.front().is(value_t::Table))) array& a = tab->at(k).as_array();
if(!(a.front().is_table()))
{ {
throw syntax_error(format_underline(concat_to_string( throw syntax_error(format_underline(concat_to_string(
"[error] toml::insert_value: array of table (\"", "[error] toml::insert_value: array of table (\"",
@@ -1200,7 +1231,7 @@ insert_nested_key(table& root, const toml::value& v,
if(tab->count(k) == 1) if(tab->count(k) == 1)
{ {
if(tab->at(k).is(value_t::Table) && v.is(value_t::Table)) if(tab->at(k).is_table() && v.is_table())
{ {
if(!is_valid_forward_table_definition( if(!is_valid_forward_table_definition(
tab->at(k), first, iter, last)) tab->at(k), first, iter, last))
@@ -1220,18 +1251,18 @@ insert_nested_key(table& root, const toml::value& v,
// d = 42 // d = 42
// [a] // [a]
// e = 2.71 // e = 2.71
auto& t = tab->at(k).cast<value_t::Table>(); auto& t = tab->at(k).as_table();
for(const auto& kv : v.cast<value_t::Table>()) for(const auto& kv : v.as_table())
{ {
t[kv.first] = kv.second; t[kv.first] = kv.second;
} }
detail::change_region(tab->at(k), key_reg); detail::change_region(tab->at(k), key_reg);
return ok(true); return ok(true);
} }
else if(v.is(value_t::Table) && else if(v.is_table() &&
tab->at(k).is(value_t::Array) && tab->at(k).is_array() &&
tab->at(k).cast<value_t::Array>().size() > 0 && tab->at(k).as_array().size() > 0 &&
tab->at(k).cast<value_t::Array>().front().is(value_t::Table)) tab->at(k).as_array().front().is_table())
{ {
throw syntax_error(format_underline(concat_to_string( throw syntax_error(format_underline(concat_to_string(
"[error] toml::insert_value: array of tables (\"", "[error] toml::insert_value: array of tables (\"",
@@ -1271,14 +1302,14 @@ insert_nested_key(table& root, const toml::value& v,
} }
// type checking... // type checking...
if(tab->at(k).is(value_t::Table)) if(tab->at(k).is_table())
{ {
tab = std::addressof((*tab)[k].template cast<value_t::Table>()); tab = std::addressof((*tab)[k].as_table());
} }
else if(tab->at(k).is(value_t::Array)) // inserting to array-of-tables? else if(tab->at(k).is_array()) // inserting to array-of-tables?
{ {
array& a = (*tab)[k].template cast<value_t::Array>(); array& a = (*tab)[k].as_array();
if(!a.back().is(value_t::Table)) if(!a.back().is_table())
{ {
throw syntax_error(format_underline(concat_to_string( throw syntax_error(format_underline(concat_to_string(
"[error] toml::insert_value: target (", "[error] toml::insert_value: target (",
@@ -1289,7 +1320,7 @@ insert_nested_key(table& root, const toml::value& v,
{std::addressof(get_region(v)), "inserting this"} {std::addressof(get_region(v)), "inserting this"}
})); }));
} }
tab = std::addressof(a.back().template cast<value_t::Table>()); tab = std::addressof(a.back().as_table());
} }
else else
{ {
@@ -1318,14 +1349,14 @@ parse_inline_table(location<Container>& loc)
return err(format_underline("[error] toml::parse_inline_table: ", return err(format_underline("[error] toml::parse_inline_table: ",
{{std::addressof(loc), "the next token is not an inline table"}})); {{std::addressof(loc), "the next token is not an inline table"}}));
} }
++loc.iter(); loc.advance();
// it starts from "{". it should be formatted as inline-table // it starts from "{". it should be formatted as inline-table
while(loc.iter() != loc.end()) while(loc.iter() != loc.end())
{ {
maybe<lex_ws>::invoke(loc); maybe<lex_ws>::invoke(loc);
if(loc.iter() != loc.end() && *loc.iter() == '}') if(loc.iter() != loc.end() && *loc.iter() == '}')
{ {
++loc.iter(); // skip `}` loc.advance(); // skip `}`
return ok(std::make_pair( return ok(std::make_pair(
retval, region<Container>(loc, first, loc.iter()))); retval, region<Container>(loc, first, loc.iter())));
} }
@@ -1354,24 +1385,70 @@ parse_inline_table(location<Container>& loc)
maybe<lex_ws>::invoke(loc); maybe<lex_ws>::invoke(loc);
if(loc.iter() != loc.end() && *loc.iter() == '}') if(loc.iter() != loc.end() && *loc.iter() == '}')
{ {
++loc.iter(); // skip `}` loc.advance(); // skip `}`
return ok(std::make_pair( return ok(std::make_pair(
retval, region<Container>(loc, first, loc.iter()))); retval, region<Container>(loc, first, loc.iter())));
} }
else if(*loc.iter() == '#' || *loc.iter() == '\r' || *loc.iter() == '\n')
{
throw syntax_error(format_underline("[error] "
"toml::parse_inline_table: missing curly brace `}`",
{{std::addressof(loc), "should be `}`"}}));
}
else else
{ {
throw syntax_error(format_underline("[error] " throw syntax_error(format_underline("[error] "
"toml:::parse_inline_table: missing table separator `,` ", "toml::parse_inline_table: missing table separator `,` ",
{{std::addressof(loc), "should be `,`"}})); {{std::addressof(loc), "should be `,`"}}));
} }
} }
} }
loc.iter() = first; loc.reset(first);
throw syntax_error(format_underline("[error] toml::parse_inline_table: " throw syntax_error(format_underline("[error] toml::parse_inline_table: "
"inline table did not closed by `}`", "inline table did not closed by `}`",
{{std::addressof(loc), "should be closed"}})); {{std::addressof(loc), "should be closed"}}));
} }
template<typename Container>
value_t guess_number_type(const location<Container>& l)
{
location<Container> loc = l;
if(lex_offset_date_time::invoke(loc)) {return value_t::OffsetDatetime;}
loc.reset(l.iter());
if(lex_local_date_time::invoke(loc)) {return value_t::LocalDatetime;}
loc.reset(l.iter());
if(lex_local_date::invoke(loc)) {return value_t::LocalDate;}
loc.reset(l.iter());
if(lex_local_time::invoke(loc)) {return value_t::LocalTime;}
loc.reset(l.iter());
if(lex_float::invoke(loc)) {return value_t::Float;}
loc.reset(l.iter());
return value_t::Integer;
}
template<typename Container>
value_t guess_value_type(const location<Container>& loc)
{
switch(*loc.iter())
{
case '"' : {return value_t::String; }
case '\'': {return value_t::String; }
case 't' : {return value_t::Boolean;}
case 'f' : {return value_t::Boolean;}
case '[' : {return value_t::Array; }
case '{' : {return value_t::Table; }
case 'i' : {return value_t::Float; } // inf.
case 'n' : {return value_t::Float; } // nan.
default : {return guess_number_type(loc);}
}
}
template<typename Container> template<typename Container>
result<value, std::string> parse_value(location<Container>& loc) result<value, std::string> parse_value(location<Container>& loc)
{ {
@@ -1381,31 +1458,27 @@ result<value, std::string> parse_value(location<Container>& loc)
return err(format_underline("[error] toml::parse_value: input is empty", return err(format_underline("[error] toml::parse_value: input is empty",
{{std::addressof(loc), ""}})); {{std::addressof(loc), ""}}));
} }
if(auto r = parse_string (loc))
{return ok(value(std::move(r.unwrap().first), std::move(r.unwrap().second)));}
if(auto r = parse_array (loc))
{return ok(value(std::move(r.unwrap().first), std::move(r.unwrap().second)));}
if(auto r = parse_inline_table (loc))
{return ok(value(std::move(r.unwrap().first), std::move(r.unwrap().second)));}
if(auto r = parse_boolean (loc))
{return ok(value(std::move(r.unwrap().first), std::move(r.unwrap().second)));}
if(auto r = parse_offset_datetime(loc))
{return ok(value(std::move(r.unwrap().first), std::move(r.unwrap().second)));}
if(auto r = parse_local_datetime (loc))
{return ok(value(std::move(r.unwrap().first), std::move(r.unwrap().second)));}
if(auto r = parse_local_date (loc))
{return ok(value(std::move(r.unwrap().first), std::move(r.unwrap().second)));}
if(auto r = parse_local_time (loc))
{return ok(value(std::move(r.unwrap().first), std::move(r.unwrap().second)));}
if(auto r = parse_floating (loc))
{return ok(value(std::move(r.unwrap().first), std::move(r.unwrap().second)));}
if(auto r = parse_integer (loc))
{return ok(value(std::move(r.unwrap().first), std::move(r.unwrap().second)));}
const auto msg = format_underline("[error] toml::parse_value: " switch(guess_value_type(loc))
"unknown token appeared", {{std::addressof(loc), "unknown"}}); {
loc.iter() = first; case value_t::Boolean : {return parse_boolean(loc); }
return err(msg); case value_t::Integer : {return parse_integer(loc); }
case value_t::Float : {return parse_floating(loc); }
case value_t::String : {return parse_string(loc); }
case value_t::OffsetDatetime : {return parse_offset_datetime(loc);}
case value_t::LocalDatetime : {return parse_local_datetime(loc); }
case value_t::LocalDate : {return parse_local_date(loc); }
case value_t::LocalTime : {return parse_local_time(loc); }
case value_t::Array : {return parse_array(loc); }
case value_t::Table : {return parse_inline_table(loc); }
default:
{
const auto msg = format_underline("[error] toml::parse_value: "
"unknown token appeared", {{std::addressof(loc), "unknown"}});
loc.reset(first);
return err(msg);
}
}
} }
template<typename Container> template<typename Container>
@@ -1461,7 +1534,8 @@ parse_table_key(location<Container>& loc)
} }
else else
{ {
return err(token.unwrap_err()); return err(format_underline("[error] toml::parse_table_key: "
"not a valid table key", {{std::addressof(loc), "here"}}));
} }
} }
@@ -1514,7 +1588,8 @@ parse_array_table_key(location<Container>& loc)
} }
else else
{ {
return err(token.unwrap_err()); return err(format_underline("[error] toml::parse_array_table_key: "
"not a valid table key", {{std::addressof(loc), "here"}}));
} }
} }
@@ -1540,12 +1615,12 @@ result<table, std::string> parse_ml_table(location<Container>& loc)
const auto before = loc.iter(); const auto before = loc.iter();
if(const auto tmp = parse_array_table_key(loc)) // next table found if(const auto tmp = parse_array_table_key(loc)) // next table found
{ {
loc.iter() = before; loc.reset(before);
return ok(tab); return ok(tab);
} }
if(const auto tmp = parse_table_key(loc)) // next table found if(const auto tmp = parse_table_key(loc)) // next table found
{ {
loc.iter() = before; loc.reset(before);
return ok(tab); return ok(tab);
} }
@@ -1580,12 +1655,12 @@ result<table, std::string> parse_ml_table(location<Container>& loc)
const auto newline = skip_line::invoke(loc); const auto newline = skip_line::invoke(loc);
if(!newline && loc.iter() != loc.end()) if(!newline && loc.iter() != loc.end())
{ {
const auto before = loc.iter(); const auto before2 = loc.iter();
lex_ws::invoke(loc); // skip whitespace lex_ws::invoke(loc); // skip whitespace
const auto msg = format_underline("[error] toml::parse_table: " const auto msg = format_underline("[error] toml::parse_table: "
"invalid line format", {{std::addressof(loc), concat_to_string( "invalid line format", {{std::addressof(loc), concat_to_string(
"expected newline, but got '", show_char(*loc.iter()), "'.")}}); "expected newline, but got '", show_char(*loc.iter()), "'.")}});
loc.iter() = before; loc.reset(before2);
return err(msg); return err(msg);
} }
@@ -1688,7 +1763,7 @@ inline table parse(std::istream& is, std::string fname = "unknown file")
std::memcpy(BOM.data(), loc.source()->data(), 3); std::memcpy(BOM.data(), loc.source()->data(), 3);
if(BOM[0] == 0xEF && BOM[1] == 0xBB && BOM[2] == 0xBF) if(BOM[0] == 0xEF && BOM[1] == 0xBB && BOM[2] == 0xBF)
{ {
loc.iter() += 3; // BOM found. skip. loc.advance(3); // BOM found. skip.
} }
} }

View File

@@ -52,6 +52,14 @@ struct region_base
virtual std::size_t before() const noexcept {return 0;} virtual std::size_t before() const noexcept {return 0;}
// 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::string comment_before() const {return "";} // just before
virtual std::string comment_inline() const {return "";} // in the same line
virtual std::string comment() const {return "";} // concatenate
// ```toml
// # comment_before
// key = "value" # comment_inline
// ```
}; };
// location represents a position in a container, which contains a file content. // location represents a position in a container, which contains a file content.
@@ -62,12 +70,16 @@ struct region_base
template<typename Container> template<typename Container>
struct location final : public region_base struct location final : public region_base
{ {
static_assert(std::is_same<char, typename Container::value_type>::value,"");
using const_iterator = typename Container::const_iterator; using const_iterator = typename Container::const_iterator;
using source_ptr = std::shared_ptr<const Container>; using source_ptr = std::shared_ptr<const Container>;
static_assert(std::is_same<char, typename Container::value_type>::value,"");
static_assert(std::is_same<std::random_access_iterator_tag,
typename std::iterator_traits<const_iterator>::iterator_category>::value,
"container should be randomly accessible");
location(std::string name, Container cont) location(std::string name, Container cont)
: source_(std::make_shared<Container>(std::move(cont))), : source_(std::make_shared<Container>(std::move(cont))), line_number_(1),
source_name_(std::move(name)), iter_(source_->cbegin()) source_name_(std::move(name)), iter_(source_->cbegin())
{} {}
location(const location&) = default; location(const location&) = default;
@@ -78,18 +90,54 @@ struct location final : public region_base
bool is_ok() const noexcept override {return static_cast<bool>(source_);} bool is_ok() const noexcept override {return static_cast<bool>(source_);}
const_iterator& iter() noexcept {return iter_;} // this const prohibits codes like `++(loc.iter())`.
const_iterator iter() const noexcept {return iter_;} const const_iterator iter() const noexcept {return iter_;}
const_iterator begin() const noexcept {return source_->cbegin();} const_iterator begin() const noexcept {return source_->cbegin();}
const_iterator end() const noexcept {return source_->cend();} const_iterator end() const noexcept {return source_->cend();}
// XXX `location::line_num()` used to be implemented using `std::count` to
// count a number of '\n'. But with a long toml file (typically, 10k lines),
// it becomes intolerably slow because each time it generates error messages,
// it counts '\n' from thousands of characters. To workaround it, I decided
// to introduce `location::line_number_` member variable and synchronize it
// to the location changes the point to look. So an overload of `iter()`
// which returns mutable reference is removed and `advance()`, `retrace()`
// and `reset()` is added.
void advance(std::size_t n = 1) noexcept
{
this->line_number_ += std::count(this->iter_, this->iter_ + n, '\n');
this->iter_ += n;
return;
}
void retrace(std::size_t n = 1) noexcept
{
this->line_number_ -= std::count(this->iter_ - n, this->iter_, '\n');
this->iter_ -= n;
return;
}
void reset(const_iterator rollback) noexcept
{
// since c++11, std::distance works in both ways for random-access
// iterators and returns a negative value if `first > last`.
if(0 <= std::distance(rollback, this->iter_)) // rollback < iter
{
this->line_number_ -= std::count(rollback, this->iter_, '\n');
}
else // iter < rollback [[unlikely]]
{
this->line_number_ += std::count(this->iter_, rollback, '\n');
}
this->iter_ = rollback;
return;
}
std::string str() const override {return make_string(1, *this->iter());} std::string str() const override {return make_string(1, *this->iter());}
std::string name() const override {return source_name_;} std::string name() const override {return source_name_;}
std::string line_num() const override std::string line_num() const override
{ {
return std::to_string(1+std::count(this->begin(), this->iter(), '\n')); return std::to_string(this->line_number_);
} }
std::string line() const override std::string line() const override
@@ -128,6 +176,7 @@ struct location final : public region_base
private: private:
source_ptr source_; source_ptr source_;
std::size_t line_number_;
std::string source_name_; std::string source_name_;
const_iterator iter_; const_iterator iter_;
}; };
@@ -139,10 +188,14 @@ struct location final : public region_base
template<typename Container> template<typename Container>
struct region final : public region_base struct region final : public region_base
{ {
static_assert(std::is_same<char, typename Container::value_type>::value,"");
using const_iterator = typename Container::const_iterator; using const_iterator = typename Container::const_iterator;
using source_ptr = std::shared_ptr<const Container>; using source_ptr = std::shared_ptr<const Container>;
static_assert(std::is_same<char, typename Container::value_type>::value,"");
static_assert(std::is_same<std::random_access_iterator_tag,
typename std::iterator_traits<const_iterator>::iterator_category>::value,
"container should be randomly accessible");
// delete default constructor. source_ never be null. // delete default constructor. source_ never be null.
region() = delete; region() = delete;
@@ -235,6 +288,92 @@ struct region final : public region_base
std::string name() const override {return source_name_;} std::string name() const override {return source_name_;}
std::string comment_before() const override
{
auto iter = this->line_begin(); // points the first element
std::vector<std::pair<decltype(iter), decltype(iter)>> comments;
while(iter != this->begin())
{
iter = std::prev(iter);
using rev_iter = std::reverse_iterator<decltype(iter)>;
auto line_before = std::find(rev_iter(iter), rev_iter(this->begin()),
'\n').base();
// range [line_before, iter) represents the previous line
auto comment_found = std::find(line_before, iter, '#');
if(iter != comment_found && std::all_of(line_before, comment_found,
[](const char c) noexcept -> bool {
return c == ' ' || c == '\t';
}))
{
// the line before this range contains only a comment.
comments.push_back(std::make_pair(comment_found, iter));
}
else
{
break;
}
iter = line_before;
}
std::string com;
for(auto i = comments.crbegin(), e = comments.crend(); i!=e; ++i)
{
if(i != comments.crbegin()) {com += '\n';}
com += std::string(i->first, i->second);
}
return com;
}
std::string comment_inline() const override
{
if(this->contain_newline())
{
std::string com;
// check both the first and the last line.
const auto first_line_end =
std::find(this->line_begin(), this->last(), '\n');
const auto first_comment_found =
std::find(this->line_begin(), first_line_end, '#');
if(first_comment_found != first_line_end)
{
com += std::string(first_comment_found, first_line_end);
}
const auto last_comment_found =
std::find(this->last(), this->line_end(), '#');
if(last_comment_found != this->line_end())
{
if(!com.empty()){com += '\n';}
com += std::string(last_comment_found, this->line_end());
}
return com;
}
const auto comment_found =
std::find(this->line_begin(), this->line_end(), '#');
return std::string(comment_found, this->line_end());
}
std::string comment() const override
{
std::string com_bef = this->comment_before();
std::string com_inl = this->comment_inline();
if(!com_bef.empty() && !com_inl.empty())
{
com_bef += '\n';
return com_bef + com_inl;
}
else if(com_bef.empty())
{
return com_inl;
}
else
{
return com_bef;
}
}
private: private:
source_ptr source_; source_ptr source_;
@@ -298,7 +437,8 @@ inline std::string format_underline(const std::string& message,
{ {
// invalid // invalid
// ~~~~~~~ // ~~~~~~~
retval << make_string(reg->size(), '~'); const auto underline_len = std::min(reg->size(), reg->line().size());
retval << make_string(underline_len, '~');
} }
retval << ' '; retval << ' ';

View File

@@ -113,21 +113,25 @@ struct result
{ {
auto tmp = ::new(std::addressof(this->succ)) success_type(s); auto tmp = ::new(std::addressof(this->succ)) success_type(s);
assert(tmp == std::addressof(this->succ)); assert(tmp == std::addressof(this->succ));
(void)tmp;
} }
result(const failure_type& f): is_ok_(false) result(const failure_type& f): is_ok_(false)
{ {
auto tmp = ::new(std::addressof(this->fail)) failure_type(f); auto tmp = ::new(std::addressof(this->fail)) failure_type(f);
assert(tmp == std::addressof(this->fail)); assert(tmp == std::addressof(this->fail));
(void)tmp;
} }
result(success_type&& s): is_ok_(true) result(success_type&& s): is_ok_(true)
{ {
auto tmp = ::new(std::addressof(this->succ)) success_type(std::move(s)); auto tmp = ::new(std::addressof(this->succ)) success_type(std::move(s));
assert(tmp == std::addressof(this->succ)); assert(tmp == std::addressof(this->succ));
(void)tmp;
} }
result(failure_type&& f): is_ok_(false) result(failure_type&& f): is_ok_(false)
{ {
auto tmp = ::new(std::addressof(this->fail)) failure_type(std::move(f)); auto tmp = ::new(std::addressof(this->fail)) failure_type(std::move(f));
assert(tmp == std::addressof(this->fail)); assert(tmp == std::addressof(this->fail));
(void)tmp;
} }
template<typename U> template<typename U>
@@ -135,24 +139,28 @@ struct result
{ {
auto tmp = ::new(std::addressof(this->succ)) success_type(s.value); auto tmp = ::new(std::addressof(this->succ)) success_type(s.value);
assert(tmp == std::addressof(this->succ)); assert(tmp == std::addressof(this->succ));
(void)tmp;
} }
template<typename U> template<typename U>
result(const failure<U>& f): is_ok_(false) result(const failure<U>& f): is_ok_(false)
{ {
auto tmp = ::new(std::addressof(this->fail)) failure_type(f.value); auto tmp = ::new(std::addressof(this->fail)) failure_type(f.value);
assert(tmp == std::addressof(this->fail)); assert(tmp == std::addressof(this->fail));
(void)tmp;
} }
template<typename U> template<typename U>
result(success<U>&& s): is_ok_(true) result(success<U>&& s): is_ok_(true)
{ {
auto tmp = ::new(std::addressof(this->succ)) success_type(std::move(s.value)); auto tmp = ::new(std::addressof(this->succ)) success_type(std::move(s.value));
assert(tmp == std::addressof(this->succ)); assert(tmp == std::addressof(this->succ));
(void)tmp;
} }
template<typename U> template<typename U>
result(failure<U>&& f): is_ok_(false) result(failure<U>&& f): is_ok_(false)
{ {
auto tmp = ::new(std::addressof(this->fail)) failure_type(std::move(f.value)); auto tmp = ::new(std::addressof(this->fail)) failure_type(std::move(f.value));
assert(tmp == std::addressof(this->fail)); assert(tmp == std::addressof(this->fail));
(void)tmp;
} }
result& operator=(const success_type& s) result& operator=(const success_type& s)
@@ -161,6 +169,7 @@ struct result
this->is_ok_ = true; this->is_ok_ = true;
auto tmp = ::new(std::addressof(this->succ)) success_type(s); auto tmp = ::new(std::addressof(this->succ)) success_type(s);
assert(tmp == std::addressof(this->succ)); assert(tmp == std::addressof(this->succ));
(void)tmp;
return *this; return *this;
} }
result& operator=(const failure_type& f) result& operator=(const failure_type& f)
@@ -169,6 +178,7 @@ struct result
this->is_ok_ = false; this->is_ok_ = false;
auto tmp = ::new(std::addressof(this->fail)) failure_type(f); auto tmp = ::new(std::addressof(this->fail)) failure_type(f);
assert(tmp == std::addressof(this->fail)); assert(tmp == std::addressof(this->fail));
(void)tmp;
return *this; return *this;
} }
result& operator=(success_type&& s) result& operator=(success_type&& s)
@@ -177,6 +187,7 @@ struct result
this->is_ok_ = true; this->is_ok_ = true;
auto tmp = ::new(std::addressof(this->succ)) success_type(std::move(s)); auto tmp = ::new(std::addressof(this->succ)) success_type(std::move(s));
assert(tmp == std::addressof(this->succ)); assert(tmp == std::addressof(this->succ));
(void)tmp;
return *this; return *this;
} }
result& operator=(failure_type&& f) result& operator=(failure_type&& f)
@@ -185,6 +196,7 @@ struct result
this->is_ok_ = false; this->is_ok_ = false;
auto tmp = ::new(std::addressof(this->fail)) failure_type(std::move(f)); auto tmp = ::new(std::addressof(this->fail)) failure_type(std::move(f));
assert(tmp == std::addressof(this->fail)); assert(tmp == std::addressof(this->fail));
(void)tmp;
return *this; return *this;
} }
@@ -195,6 +207,7 @@ struct result
this->is_ok_ = true; this->is_ok_ = true;
auto tmp = ::new(std::addressof(this->succ)) success_type(s.value); auto tmp = ::new(std::addressof(this->succ)) success_type(s.value);
assert(tmp == std::addressof(this->succ)); assert(tmp == std::addressof(this->succ));
(void)tmp;
return *this; return *this;
} }
template<typename U> template<typename U>
@@ -204,6 +217,7 @@ struct result
this->is_ok_ = false; this->is_ok_ = false;
auto tmp = ::new(std::addressof(this->fail)) failure_type(f.value); auto tmp = ::new(std::addressof(this->fail)) failure_type(f.value);
assert(tmp == std::addressof(this->fail)); assert(tmp == std::addressof(this->fail));
(void)tmp;
return *this; return *this;
} }
template<typename U> template<typename U>
@@ -213,6 +227,7 @@ struct result
this->is_ok_ = true; this->is_ok_ = true;
auto tmp = ::new(std::addressof(this->succ)) success_type(std::move(s.value)); auto tmp = ::new(std::addressof(this->succ)) success_type(std::move(s.value));
assert(tmp == std::addressof(this->succ)); assert(tmp == std::addressof(this->succ));
(void)tmp;
return *this; return *this;
} }
template<typename U> template<typename U>
@@ -222,6 +237,7 @@ struct result
this->is_ok_ = false; this->is_ok_ = false;
auto tmp = ::new(std::addressof(this->fail)) failure_type(std::move(f.value)); auto tmp = ::new(std::addressof(this->fail)) failure_type(std::move(f.value));
assert(tmp == std::addressof(this->fail)); assert(tmp == std::addressof(this->fail));
(void)tmp;
return *this; return *this;
} }
@@ -233,11 +249,13 @@ struct result
{ {
auto tmp = ::new(std::addressof(this->succ)) success_type(other.as_ok()); auto tmp = ::new(std::addressof(this->succ)) success_type(other.as_ok());
assert(tmp == std::addressof(this->succ)); assert(tmp == std::addressof(this->succ));
(void)tmp;
} }
else else
{ {
auto tmp = ::new(std::addressof(this->fail)) failure_type(other.as_err()); auto tmp = ::new(std::addressof(this->fail)) failure_type(other.as_err());
assert(tmp == std::addressof(this->fail)); assert(tmp == std::addressof(this->fail));
(void)tmp;
} }
} }
result(result&& other): is_ok_(other.is_ok()) result(result&& other): is_ok_(other.is_ok())
@@ -246,11 +264,13 @@ struct result
{ {
auto tmp = ::new(std::addressof(this->succ)) success_type(std::move(other.as_ok())); auto tmp = ::new(std::addressof(this->succ)) success_type(std::move(other.as_ok()));
assert(tmp == std::addressof(this->succ)); assert(tmp == std::addressof(this->succ));
(void)tmp;
} }
else else
{ {
auto tmp = ::new(std::addressof(this->fail)) failure_type(std::move(other.as_err())); auto tmp = ::new(std::addressof(this->fail)) failure_type(std::move(other.as_err()));
assert(tmp == std::addressof(this->fail)); assert(tmp == std::addressof(this->fail));
(void)tmp;
} }
} }
@@ -261,11 +281,13 @@ struct result
{ {
auto tmp = ::new(std::addressof(this->succ)) success_type(other.as_ok()); auto tmp = ::new(std::addressof(this->succ)) success_type(other.as_ok());
assert(tmp == std::addressof(this->succ)); assert(tmp == std::addressof(this->succ));
(void)tmp;
} }
else else
{ {
auto tmp = ::new(std::addressof(this->fail)) failure_type(other.as_err()); auto tmp = ::new(std::addressof(this->fail)) failure_type(other.as_err());
assert(tmp == std::addressof(this->fail)); assert(tmp == std::addressof(this->fail));
(void)tmp;
} }
} }
template<typename U, typename F> template<typename U, typename F>
@@ -275,11 +297,13 @@ struct result
{ {
auto tmp = ::new(std::addressof(this->succ)) success_type(std::move(other.as_ok())); auto tmp = ::new(std::addressof(this->succ)) success_type(std::move(other.as_ok()));
assert(tmp == std::addressof(this->succ)); assert(tmp == std::addressof(this->succ));
(void)tmp;
} }
else else
{ {
auto tmp = ::new(std::addressof(this->fail)) failure_type(std::move(other.as_err())); auto tmp = ::new(std::addressof(this->fail)) failure_type(std::move(other.as_err()));
assert(tmp == std::addressof(this->fail)); assert(tmp == std::addressof(this->fail));
(void)tmp;
} }
} }
@@ -290,11 +314,13 @@ struct result
{ {
auto tmp = ::new(std::addressof(this->succ)) success_type(other.as_ok()); auto tmp = ::new(std::addressof(this->succ)) success_type(other.as_ok());
assert(tmp == std::addressof(this->succ)); assert(tmp == std::addressof(this->succ));
(void)tmp;
} }
else else
{ {
auto tmp = ::new(std::addressof(this->fail)) failure_type(other.as_err()); auto tmp = ::new(std::addressof(this->fail)) failure_type(other.as_err());
assert(tmp == std::addressof(this->fail)); assert(tmp == std::addressof(this->fail));
(void)tmp;
} }
is_ok_ = other.is_ok(); is_ok_ = other.is_ok();
return *this; return *this;
@@ -306,11 +332,13 @@ struct result
{ {
auto tmp = ::new(std::addressof(this->succ)) success_type(std::move(other.as_ok())); auto tmp = ::new(std::addressof(this->succ)) success_type(std::move(other.as_ok()));
assert(tmp == std::addressof(this->succ)); assert(tmp == std::addressof(this->succ));
(void)tmp;
} }
else else
{ {
auto tmp = ::new(std::addressof(this->fail)) failure_type(std::move(other.as_err())); auto tmp = ::new(std::addressof(this->fail)) failure_type(std::move(other.as_err()));
assert(tmp == std::addressof(this->fail)); assert(tmp == std::addressof(this->fail));
(void)tmp;
} }
is_ok_ = other.is_ok(); is_ok_ = other.is_ok();
return *this; return *this;
@@ -324,11 +352,13 @@ struct result
{ {
auto tmp = ::new(std::addressof(this->succ)) success_type(other.as_ok()); auto tmp = ::new(std::addressof(this->succ)) success_type(other.as_ok());
assert(tmp == std::addressof(this->succ)); assert(tmp == std::addressof(this->succ));
(void)tmp;
} }
else else
{ {
auto tmp = ::new(std::addressof(this->fail)) failure_type(other.as_err()); auto tmp = ::new(std::addressof(this->fail)) failure_type(other.as_err());
assert(tmp == std::addressof(this->fail)); assert(tmp == std::addressof(this->fail));
(void)tmp;
} }
is_ok_ = other.is_ok(); is_ok_ = other.is_ok();
return *this; return *this;
@@ -341,11 +371,13 @@ struct result
{ {
auto tmp = ::new(std::addressof(this->succ)) success_type(std::move(other.as_ok())); auto tmp = ::new(std::addressof(this->succ)) success_type(std::move(other.as_ok()));
assert(tmp == std::addressof(this->succ)); assert(tmp == std::addressof(this->succ));
(void)tmp;
} }
else else
{ {
auto tmp = ::new(std::addressof(this->fail)) failure_type(std::move(other.as_err())); auto tmp = ::new(std::addressof(this->fail)) failure_type(std::move(other.as_err()));
assert(tmp == std::addressof(this->fail)); assert(tmp == std::addressof(this->fail));
(void)tmp;
} }
is_ok_ = other.is_ok(); is_ok_ = other.is_ok();
return *this; return *this;
@@ -660,5 +692,26 @@ void swap(result<T, E>& lhs, result<T, E>& rhs)
// return lhs.is_ok() ? lhs : rhs; // return lhs.is_ok() ? lhs : rhs;
// } // }
// ----------------------------------------------------------------------------
// re-use result<T, E> as a optional<T> with none_t
namespace detail
{
struct none_t {};
inline bool operator==(const none_t&, const none_t&) noexcept {return true;}
inline bool operator!=(const none_t&, const none_t&) noexcept {return false;}
inline bool operator< (const none_t&, const none_t&) noexcept {return false;}
inline bool operator<=(const none_t&, const none_t&) noexcept {return true;}
inline bool operator> (const none_t&, const none_t&) noexcept {return false;}
inline bool operator>=(const none_t&, const none_t&) noexcept {return true;}
template<typename charT, typename traitsT>
std::basic_ostream<charT, traitsT>&
operator<<(std::basic_ostream<charT, traitsT>& os, const none_t&)
{
os << "none";
return os;
}
inline failure<none_t> none() noexcept {return failure<none_t>{none_t{}};}
} // detail
} // toml11 } // toml11
#endif// TOML11_RESULT_H #endif// TOML11_RESULT_H

View File

@@ -157,7 +157,7 @@ struct serializer
std::string operator()(const array& v) const std::string operator()(const array& v) const
{ {
if(!v.empty() && v.front().is(value_t::Table))// v is an array of tables if(!v.empty() && v.front().is_table())// v is an array of tables
{ {
// if it's not inlined, we need to add `[[table.key]]`. // if it's not inlined, we need to add `[[table.key]]`.
// but if it can be inlined, we need `table.key = [...]`. // but if it can be inlined, we need `table.key = [...]`.
@@ -411,7 +411,7 @@ struct serializer
// 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(value_t::Table) || is_array_of_tables(kv.second)) if(kv.second.is_table() || is_array_of_tables(kv.second))
{ {
continue; continue;
} }
@@ -438,7 +438,7 @@ struct serializer
bool multiline_table_printed = false; bool multiline_table_printed = false;
for(const auto& kv : v) for(const auto& kv : v)
{ {
if(!kv.second.is(value_t::Table) && !is_array_of_tables(kv.second)) if(!kv.second.is_table() && !is_array_of_tables(kv.second))
{ {
continue; // other stuff are already serialized. skip them. continue; // other stuff are already serialized. skip them.
} }
@@ -467,10 +467,9 @@ struct serializer
bool is_array_of_tables(const value& v) const bool is_array_of_tables(const value& v) const
{ {
if(!v.is(value_t::Array)) {return false;} if(!v.is_array()) {return false;}
const auto& a = v.as_array();
const auto& a = v.cast<value_t::Array>(); return !a.empty() && a.front().is_table();
return !a.empty() && a.front().is(value_t::Table);
} }
private: private:

View File

@@ -4,6 +4,11 @@
#define TOML11_STRING_HPP #define TOML11_STRING_HPP
#include <string> #include <string>
#include <cstdint> #include <cstdint>
#if __cplusplus >= 201703L
#if __has_include(<string_view>)
#include <string_view>
#endif
#endif
namespace toml namespace toml
{ {
@@ -40,6 +45,17 @@ struct string
operator std::string const& () const& noexcept {return str;} operator std::string const& () const& noexcept {return str;}
operator std::string&& () && noexcept {return std::move(str);} operator std::string&& () && noexcept {return std::move(str);}
#if __cplusplus >= 201703L
explicit string(std::string_view s): kind(string_t::basic), str(s){}
string(std::string_view s, string_t k): kind(k), str(s){}
string& operator=(std::string_view s)
{kind = string_t::basic; str = s; return *this;}
explicit operator std::string_view() const noexcept
{return std::string_view(str);}
#endif
string_t kind; string_t kind;
std::string str; std::string str;
}; };

View File

@@ -7,6 +7,11 @@
#include "traits.hpp" #include "traits.hpp"
#include <vector> #include <vector>
#include <unordered_map> #include <unordered_map>
#if __cplusplus >= 201703L
#if __has_include(<string_view>)
#include <string_view>
#endif
#endif
namespace toml namespace toml
{ {
@@ -126,30 +131,30 @@ template<> struct toml_default_type<value_t::Table > {typedef table
template<> struct toml_default_type<value_t::Empty > {typedef void type;}; template<> struct toml_default_type<value_t::Empty > {typedef void type;};
template<> struct toml_default_type<value_t::Unknown > {typedef void type;}; template<> struct toml_default_type<value_t::Unknown > {typedef void type;};
template<typename T> struct toml_value_t {static constexpr value_t value = value_t::Unknown ;}; template<typename T> struct toml_value_t {static constexpr value_t value = value_t::Unknown ;};
template<> struct toml_value_t<Boolean >{static constexpr value_t value = value_t::Boolean ;}; template<> struct toml_value_t<boolean >{static constexpr value_t value = value_t::Boolean ;};
template<> struct toml_value_t<Integer >{static constexpr value_t value = value_t::Integer ;}; template<> struct toml_value_t<integer >{static constexpr value_t value = value_t::Integer ;};
template<> struct toml_value_t<Float >{static constexpr value_t value = value_t::Float ;}; template<> struct toml_value_t<floating >{static constexpr value_t value = value_t::Float ;};
template<> struct toml_value_t<String >{static constexpr value_t value = value_t::String ;}; template<> struct toml_value_t<string >{static constexpr value_t value = value_t::String ;};
template<> struct toml_value_t<OffsetDatetime>{static constexpr value_t value = value_t::OffsetDatetime;}; template<> struct toml_value_t<offset_datetime>{static constexpr value_t value = value_t::OffsetDatetime;};
template<> struct toml_value_t<LocalDatetime >{static constexpr value_t value = value_t::LocalDatetime ;}; template<> struct toml_value_t<local_datetime >{static constexpr value_t value = value_t::LocalDatetime ;};
template<> struct toml_value_t<LocalDate >{static constexpr value_t value = value_t::LocalDate ;}; template<> struct toml_value_t<local_date >{static constexpr value_t value = value_t::LocalDate ;};
template<> struct toml_value_t<LocalTime >{static constexpr value_t value = value_t::LocalTime ;}; template<> struct toml_value_t<local_time >{static constexpr value_t value = value_t::LocalTime ;};
template<> struct toml_value_t<Array >{static constexpr value_t value = value_t::Array ;}; template<> struct toml_value_t<array >{static constexpr value_t value = value_t::Array ;};
template<> struct toml_value_t<Table >{static constexpr value_t value = value_t::Table ;}; template<> struct toml_value_t<table >{static constexpr value_t value = value_t::Table ;};
template<typename T> template<typename T>
struct is_exact_toml_type : disjunction< struct is_exact_toml_type : disjunction<
std::is_same<T, Boolean >, std::is_same<T, boolean >,
std::is_same<T, Integer >, std::is_same<T, integer >,
std::is_same<T, Float >, std::is_same<T, floating >,
std::is_same<T, String >, std::is_same<T, string >,
std::is_same<T, offset_datetime>, std::is_same<T, offset_datetime>,
std::is_same<T, local_datetime>, std::is_same<T, local_datetime >,
std::is_same<T, local_date>, std::is_same<T, local_date >,
std::is_same<T, local_time>, std::is_same<T, local_time >,
std::is_same<T, Array >, std::is_same<T, array >,
std::is_same<T, Table > std::is_same<T, table >
>{}; >{};
template<typename T> struct is_exact_toml_type<T&> : is_exact_toml_type<T>{}; template<typename T> struct is_exact_toml_type<T&> : is_exact_toml_type<T>{};
template<typename T> struct is_exact_toml_type<T const&> : is_exact_toml_type<T>{}; template<typename T> struct is_exact_toml_type<T const&> : is_exact_toml_type<T>{};
@@ -172,6 +177,9 @@ template<typename T>
struct is_container : conjunction< struct is_container : conjunction<
negation<is_map<T>>, negation<is_map<T>>,
negation<std::is_same<T, std::string>>, negation<std::is_same<T, std::string>>,
#if __cplusplus >= 201703L
negation<std::is_same<T, std::string_view>>,
#endif
has_iterator<T>, has_iterator<T>,
has_value_type<T> has_value_type<T>
>{}; >{};

View File

@@ -79,7 +79,7 @@ std::string concat_to_string(Ts&& ... args)
template<typename T, typename U> template<typename T, typename U>
T from_string(const std::string& str, U&& opt) T from_string(const std::string& str, U&& opt)
{ {
T v(std::forward<U>(opt)); T v(static_cast<T>(std::forward<U>(opt)));
std::istringstream iss(str); std::istringstream iss(str);
iss >> v; iss >> v;
return v; return v;

View File

@@ -14,6 +14,9 @@
#include <unordered_map> #include <unordered_map>
#include <cassert> #include <cassert>
#include <cstdint> #include <cstdint>
#if __cplusplus >= 201703L
#include <string_view>
#endif
namespace toml namespace toml
{ {
@@ -45,6 +48,7 @@ class value
{ {
const auto tmp = ::new(std::addressof(dst)) T(std::forward<U>(v)); const auto tmp = ::new(std::addressof(dst)) T(std::forward<U>(v));
assert(tmp == std::addressof(dst)); assert(tmp == std::addressof(dst));
(void)tmp;
} }
using region_base = detail::region_base; using region_base = detail::region_base;
@@ -293,6 +297,29 @@ class value
assigner(this->string_, toml::string(std::string(s), kind)); assigner(this->string_, toml::string(std::string(s), kind));
} }
#if __cplusplus >= 201703L
value(std::string_view s)
: type_(value_t::String),
region_info_(std::make_shared<region_base>(region_base{}))
{
assigner(this->string_, toml::string(s));
}
value& operator=(std::string_view s)
{
this->cleanup();
this->type_ = value_t::String;
this->region_info_ = std::make_shared<region_base>(region_base{});
assigner(this->string_, toml::string(s));
return *this;
}
value(std::string_view s, string_t kind)
: type_(value_t::String),
region_info_(std::make_shared<region_base>(region_base{}))
{
assigner(this->string_, toml::string(s, kind));
}
#endif
// local date =========================================================== // local date ===========================================================
value(const local_date& ld) value(const local_date& ld)
@@ -572,6 +599,14 @@ class value
return *this; return *this;
} }
// for internal use ------------------------------------------------------
template<typename T, typename Container, typename std::enable_if<
detail::is_exact_toml_type<T>::value, std::nullptr_t>::type = nullptr>
value(std::pair<T, detail::region<Container>> parse_result)
: value(std::move(parse_result.first), std::move(parse_result.second))
{}
// type checking and casting ============================================ // type checking and casting ============================================
template<typename T> template<typename T>
@@ -599,6 +634,52 @@ class value
template<value_t T> template<value_t T>
typename detail::toml_default_type<T>::type&& cast() &&; typename detail::toml_default_type<T>::type&& cast() &&;
boolean const& as_boolean() const& noexcept {return this->boolean_;}
integer const& as_integer() const& noexcept {return this->integer_;}
floating const& as_float() const& noexcept {return this->floating_;}
string const& as_string() const& noexcept {return this->string_;}
offset_datetime const& as_offset_datetime() const& noexcept {return this->offset_datetime_;}
local_datetime const& as_local_datetime() const& noexcept {return this->local_datetime_;}
local_date const& as_local_date() const& noexcept {return this->local_date_;}
local_time const& as_local_time() const& noexcept {return this->local_time_;}
array const& as_array() const& noexcept {return this->array_.value();}
table const& as_table() const& noexcept {return this->table_.value();}
boolean & as_boolean() & noexcept {return this->boolean_;}
integer & as_integer() & noexcept {return this->integer_;}
floating & as_float() & noexcept {return this->floating_;}
string & as_string() & noexcept {return this->string_;}
offset_datetime& as_offset_datetime() & noexcept {return this->offset_datetime_;}
local_datetime & as_local_datetime() & noexcept {return this->local_datetime_;}
local_date & as_local_date() & noexcept {return this->local_date_;}
local_time & as_local_time() & noexcept {return this->local_time_;}
array & as_array() & noexcept {return this->array_.value();}
table & as_table() & noexcept {return this->table_.value();}
boolean && as_boolean() && noexcept {return std::move(this->boolean_);}
integer && as_integer() && noexcept {return std::move(this->integer_);}
floating && as_float() && noexcept {return std::move(this->floating_);}
string && as_string() && noexcept {return std::move(this->string_);}
offset_datetime&& as_offset_datetime() && noexcept {return std::move(this->offset_datetime_);}
local_datetime && as_local_datetime() && noexcept {return std::move(this->local_datetime_);}
local_date && as_local_date() && noexcept {return std::move(this->local_date_);}
local_time && as_local_time() && noexcept {return std::move(this->local_time_);}
array && as_array() && noexcept {return std::move(this->array_.value());}
table && as_table() && noexcept {return std::move(this->table_.value());}
std::string comment() const
{
return this->region_info_->comment();
}
std::string comment_before() const
{
return this->region_info_->comment_before();
}
std::string comment_inline() const
{
return this->region_info_->comment_inline();
}
private: private:
void cleanup() noexcept void cleanup() noexcept
@@ -618,9 +699,6 @@ class value
template<typename Region> template<typename Region>
friend void detail::change_region(value&, Region&&); friend void detail::change_region(value&, Region&&);
template<value_t T>
struct switch_cast;
private: private:
using array_storage = detail::storage<array>; using array_storage = detail::storage<array>;
@@ -666,107 +744,116 @@ void change_region(value& v, Region&& reg)
return; return;
} }
}// detail template<value_t Expected>
[[noreturn]] inline void throw_bad_cast(value_t actual, const ::toml::value& v)
{
throw type_error(detail::format_underline(concat_to_string(
"[error] toml::value bad_cast to ", Expected), {
{std::addressof(get_region(v)),
concat_to_string("the actual type is ", actual)}
}));
}
template<> struct value::switch_cast<value_t::Boolean> template<value_t T>
struct switch_cast;
template<>
struct switch_cast<value_t::Boolean>
{ {
static Boolean& invoke(value& v) {return v.boolean_;} static ::toml::boolean& invoke(value& v) {return v.as_boolean();}
static Boolean const& invoke(value const& v) {return v.boolean_;} static ::toml::boolean const& invoke(value const& v) {return v.as_boolean();}
static Boolean&& invoke(value&& v) {return std::move(v.boolean_);} static ::toml::boolean&& invoke(value&& v) {return std::move(v).as_boolean();}
}; };
template<> struct value::switch_cast<value_t::Integer> template<>
struct switch_cast<value_t::Integer>
{ {
static Integer& invoke(value& v) {return v.integer_;} static ::toml::integer& invoke(value& v) {return v.as_integer();}
static Integer const& invoke(value const& v) {return v.integer_;} static ::toml::integer const& invoke(value const& v) {return v.as_integer();}
static Integer&& invoke(value&& v) {return std::move(v.integer_);} static ::toml::integer&& invoke(value&& v) {return std::move(v).as_integer();}
}; };
template<> struct value::switch_cast<value_t::Float> template<>
struct switch_cast<value_t::Float>
{ {
static Float& invoke(value& v) {return v.floating_;} static ::toml::floating& invoke(value& v) {return v.as_float();}
static Float const& invoke(value const& v) {return v.floating_;} static ::toml::floating const& invoke(value const& v) {return v.as_float();}
static Float&& invoke(value&& v) {return std::move(v.floating_);} static ::toml::floating&& invoke(value&& v) {return std::move(v).as_float();}
}; };
template<> struct value::switch_cast<value_t::String> template<>
struct switch_cast<value_t::String>
{ {
static String& invoke(value& v) {return v.string_;} static ::toml::string& invoke(value& v) {return v.as_string();}
static String const& invoke(value const& v) {return v.string_;} static ::toml::string const& invoke(value const& v) {return v.as_string();}
static String&& invoke(value&& v) {return std::move(v.string_);} static ::toml::string&& invoke(value&& v) {return std::move(v).as_string();}
}; };
template<> struct value::switch_cast<value_t::OffsetDatetime> template<>
struct switch_cast<value_t::OffsetDatetime>
{ {
static OffsetDatetime& invoke(value& v) {return v.offset_datetime_;} static ::toml::offset_datetime& invoke(value& v) {return v.as_offset_datetime();}
static OffsetDatetime const& invoke(value const& v) {return v.offset_datetime_;} static ::toml::offset_datetime const& invoke(value const& v) {return v.as_offset_datetime();}
static OffsetDatetime&& invoke(value&& v) {return std::move(v.offset_datetime_);} static ::toml::offset_datetime&& invoke(value&& v) {return std::move(v).as_offset_datetime();}
}; };
template<> struct value::switch_cast<value_t::LocalDatetime> template<>
struct switch_cast<value_t::LocalDatetime>
{ {
static LocalDatetime& invoke(value& v) {return v.local_datetime_;} static ::toml::local_datetime& invoke(value& v) {return v.as_local_datetime();}
static LocalDatetime const& invoke(value const& v) {return v.local_datetime_;} static ::toml::local_datetime const& invoke(value const& v) {return v.as_local_datetime();}
static LocalDatetime&& invoke(value&& v) {return std::move(v.local_datetime_);} static ::toml::local_datetime&& invoke(value&& v) {return std::move(v).as_local_datetime();}
}; };
template<> struct value::switch_cast<value_t::LocalDate> template<>
struct switch_cast<value_t::LocalDate>
{ {
static LocalDate& invoke(value& v) {return v.local_date_;} static ::toml::local_date& invoke(value& v) {return v.as_local_date();}
static LocalDate const& invoke(value const& v) {return v.local_date_;} static ::toml::local_date const& invoke(value const& v) {return v.as_local_date();}
static LocalDate&& invoke(value&& v) {return std::move(v.local_date_);} static ::toml::local_date&& invoke(value&& v) {return std::move(v).as_local_date();}
}; };
template<> struct value::switch_cast<value_t::LocalTime> template<>
struct switch_cast<value_t::LocalTime>
{ {
static LocalTime& invoke(value& v) {return v.local_time_;} static ::toml::local_time& invoke(value& v) {return v.as_local_time();}
static LocalTime const& invoke(value const& v) {return v.local_time_;} static ::toml::local_time const& invoke(value const& v) {return v.as_local_time();}
static LocalTime&& invoke(value&& v) {return std::move(v.local_time_);} static ::toml::local_time&& invoke(value&& v) {return std::move(v).as_local_time();}
}; };
template<> struct value::switch_cast<value_t::Array> template<>
struct switch_cast<value_t::Array>
{ {
static Array& invoke(value& v) {return v.array_.value();} static ::toml::array& invoke(value& v) {return v.as_array();}
static Array const& invoke(value const& v) {return v.array_.value();} static ::toml::array const& invoke(value const& v) {return v.as_array();}
static Array&& invoke(value&& v) {return std::move(v.array_.value());} static ::toml::array&& invoke(value&& v) {return std::move(v).as_array();}
}; };
template<> struct value::switch_cast<value_t::Table> template<>
struct switch_cast<value_t::Table>
{ {
static Table& invoke(value& v) {return v.table_.value();} static ::toml::table& invoke(value& v) {return v.as_table();}
static Table const& invoke(value const& v) {return v.table_.value();} static ::toml::table const& invoke(value const& v) {return v.as_table();}
static Table&& invoke(value&& v) {return std::move(v.table_.value());} static ::toml::table&& invoke(value&& v) {return std::move(v).as_table();}
}; };
}// detail
template<value_t T> template<value_t T>
typename detail::toml_default_type<T>::type& value::cast() & typename detail::toml_default_type<T>::type& value::cast() &
{ {
if(T != this->type_) if(T != this->type_)
{ {
throw type_error(detail::format_underline(concat_to_string( detail::throw_bad_cast<T>(this->type_, *this);
"[error] toml::value bad_cast to ", T), {
{this->region_info_.get(),
concat_to_string("the actual type is ", this->type_)}
}));
} }
return switch_cast<T>::invoke(*this); return detail::switch_cast<T>::invoke(*this);
} }
template<value_t T> template<value_t T>
typename detail::toml_default_type<T>::type const& value::cast() const& typename detail::toml_default_type<T>::type const& value::cast() const&
{ {
if(T != this->type_) if(T != this->type_)
{ {
throw type_error(detail::format_underline(concat_to_string( detail::throw_bad_cast<T>(this->type_, *this);
"[error] toml::value bad_cast to ", T), {
{this->region_info_.get(),
concat_to_string("the actual type is ", this->type_)}
}));
} }
return switch_cast<T>::invoke(*this); return detail::switch_cast<T>::invoke(*this);
} }
template<value_t T> template<value_t T>
typename detail::toml_default_type<T>::type&& value::cast() && typename detail::toml_default_type<T>::type&& value::cast() &&
{ {
if(T != this->type_) if(T != this->type_)
{ {
throw type_error(detail::format_underline(concat_to_string( detail::throw_bad_cast<T>(this->type_, *this);
"[error] toml::value bad_cast to ", T), {
{this->region_info_.get(),
concat_to_string("the actual type is ", this->type_)}
}));
} }
return switch_cast<T>::invoke(std::move(*this)); return detail::switch_cast<T>::invoke(std::move(*this));
} }
inline bool operator==(const toml::value& lhs, const toml::value& rhs) inline bool operator==(const toml::value& lhs, const toml::value& rhs)
@@ -775,28 +862,48 @@ inline bool operator==(const toml::value& lhs, const toml::value& rhs)
switch(lhs.type()) switch(lhs.type())
{ {
case value_t::Boolean : case value_t::Boolean :
return lhs.cast<value_t::Boolean >() == rhs.cast<value_t::Boolean >(); {
return lhs.as_boolean() == rhs.as_boolean();
}
case value_t::Integer : case value_t::Integer :
return lhs.cast<value_t::Integer >() == rhs.cast<value_t::Integer >(); {
return lhs.as_integer() == rhs.as_integer();
}
case value_t::Float : case value_t::Float :
return lhs.cast<value_t::Float >() == rhs.cast<value_t::Float >(); {
return lhs.as_float() == rhs.as_float();
}
case value_t::String : case value_t::String :
return lhs.cast<value_t::String >() == rhs.cast<value_t::String >(); {
return lhs.as_string() == rhs.as_string();
}
case value_t::OffsetDatetime: case value_t::OffsetDatetime:
return lhs.cast<value_t::OffsetDatetime>() == rhs.cast<value_t::OffsetDatetime>(); {
return lhs.as_offset_datetime() == rhs.as_offset_datetime();
}
case value_t::LocalDatetime: case value_t::LocalDatetime:
return lhs.cast<value_t::LocalDatetime>() == rhs.cast<value_t::LocalDatetime>(); {
return lhs.as_local_datetime() == rhs.as_local_datetime();
}
case value_t::LocalDate: case value_t::LocalDate:
return lhs.cast<value_t::LocalDate>() == rhs.cast<value_t::LocalDate>(); {
return lhs.as_local_date() == rhs.as_local_date();
}
case value_t::LocalTime: case value_t::LocalTime:
return lhs.cast<value_t::LocalTime>() == rhs.cast<value_t::LocalTime>(); {
return lhs.as_local_time() == rhs.as_local_time();
}
case value_t::Array : case value_t::Array :
return lhs.cast<value_t::Array >() == rhs.cast<value_t::Array >(); {
return lhs.as_array() == rhs.as_array();
}
case value_t::Table : case value_t::Table :
return lhs.cast<value_t::Table >() == rhs.cast<value_t::Table >(); {
case value_t::Empty : return true; return lhs.as_table() == rhs.as_table();
case value_t::Unknown : return false; }
default: return false; case value_t::Empty : {return true; }
case value_t::Unknown : {return false;}
default: {return false;}
} }
} }
inline bool operator<(const toml::value& lhs, const toml::value& rhs) inline bool operator<(const toml::value& lhs, const toml::value& rhs)
@@ -805,28 +912,48 @@ inline bool operator<(const toml::value& lhs, const toml::value& rhs)
switch(lhs.type()) switch(lhs.type())
{ {
case value_t::Boolean : case value_t::Boolean :
return lhs.cast<value_t::Boolean >() < rhs.cast<value_t::Boolean >(); {
return lhs.as_boolean() < rhs.as_boolean();
}
case value_t::Integer : case value_t::Integer :
return lhs.cast<value_t::Integer >() < rhs.cast<value_t::Integer >(); {
return lhs.as_integer() < rhs.as_integer();
}
case value_t::Float : case value_t::Float :
return lhs.cast<value_t::Float >() < rhs.cast<value_t::Float >(); {
return lhs.as_float() < rhs.as_float();
}
case value_t::String : case value_t::String :
return lhs.cast<value_t::String >() < rhs.cast<value_t::String >(); {
return lhs.as_string() < rhs.as_string();
}
case value_t::OffsetDatetime: case value_t::OffsetDatetime:
return lhs.cast<value_t::OffsetDatetime>() < rhs.cast<value_t::OffsetDatetime>(); {
return lhs.as_offset_datetime() < rhs.as_offset_datetime();
}
case value_t::LocalDatetime: case value_t::LocalDatetime:
return lhs.cast<value_t::LocalDatetime>() < rhs.cast<value_t::LocalDatetime>(); {
return lhs.as_local_datetime() < rhs.as_local_datetime();
}
case value_t::LocalDate: case value_t::LocalDate:
return lhs.cast<value_t::LocalDate>() < rhs.cast<value_t::LocalDate>(); {
return lhs.as_local_date() < rhs.as_local_date();
}
case value_t::LocalTime: case value_t::LocalTime:
return lhs.cast<value_t::LocalTime>() < rhs.cast<value_t::LocalTime>(); {
return lhs.as_local_time() < rhs.as_local_time();
}
case value_t::Array : case value_t::Array :
return lhs.cast<value_t::Array >() < rhs.cast<value_t::Array >(); {
return lhs.as_array() < rhs.as_array();
}
case value_t::Table : case value_t::Table :
return lhs.cast<value_t::Table >() < rhs.cast<value_t::Table >(); {
case value_t::Empty : return false; return lhs.as_table() < rhs.as_table();
case value_t::Unknown : return false; }
default: return false; case value_t::Empty : {return false;}
case value_t::Unknown : {return false;}
default: {return false;}
} }
} }
@@ -889,16 +1016,16 @@ visit(Visitor&& visitor, const toml::value& v)
{ {
switch(v.type()) switch(v.type())
{ {
case value_t::Boolean : {return visitor(v.cast<value_t::Boolean >());} case value_t::Boolean : {return visitor(v.as_boolean ());}
case value_t::Integer : {return visitor(v.cast<value_t::Integer >());} case value_t::Integer : {return visitor(v.as_integer ());}
case value_t::Float : {return visitor(v.cast<value_t::Float >());} case value_t::Float : {return visitor(v.as_float ());}
case value_t::String : {return visitor(v.cast<value_t::String >());} case value_t::String : {return visitor(v.as_string ());}
case value_t::OffsetDatetime: {return visitor(v.cast<value_t::OffsetDatetime>());} case value_t::OffsetDatetime: {return visitor(v.as_offset_datetime());}
case value_t::LocalDatetime : {return visitor(v.cast<value_t::LocalDatetime >());} case value_t::LocalDatetime : {return visitor(v.as_local_datetime ());}
case value_t::LocalDate : {return visitor(v.cast<value_t::LocalDate >());} case value_t::LocalDate : {return visitor(v.as_local_date ());}
case value_t::LocalTime : {return visitor(v.cast<value_t::LocalTime >());} case value_t::LocalTime : {return visitor(v.as_local_time ());}
case value_t::Array : {return visitor(v.cast<value_t::Array >());} case value_t::Array : {return visitor(v.as_array ());}
case value_t::Table : {return visitor(v.cast<value_t::Table >());} case value_t::Table : {return visitor(v.as_table ());}
case value_t::Empty : break; case value_t::Empty : break;
case value_t::Unknown : break; case value_t::Unknown : break;
default: break; default: break;
@@ -913,16 +1040,16 @@ visit(Visitor&& visitor, toml::value& v)
{ {
switch(v.type()) switch(v.type())
{ {
case value_t::Boolean : {return visitor(v.cast<value_t::Boolean >());} case value_t::Boolean : {return visitor(v.as_boolean ());}
case value_t::Integer : {return visitor(v.cast<value_t::Integer >());} case value_t::Integer : {return visitor(v.as_integer ());}
case value_t::Float : {return visitor(v.cast<value_t::Float >());} case value_t::Float : {return visitor(v.as_float ());}
case value_t::String : {return visitor(v.cast<value_t::String >());} case value_t::String : {return visitor(v.as_string ());}
case value_t::OffsetDatetime: {return visitor(v.cast<value_t::OffsetDatetime>());} case value_t::OffsetDatetime: {return visitor(v.as_offset_datetime());}
case value_t::LocalDatetime : {return visitor(v.cast<value_t::LocalDatetime >());} case value_t::LocalDatetime : {return visitor(v.as_local_datetime ());}
case value_t::LocalDate : {return visitor(v.cast<value_t::LocalDate >());} case value_t::LocalDate : {return visitor(v.as_local_date ());}
case value_t::LocalTime : {return visitor(v.cast<value_t::LocalTime >());} case value_t::LocalTime : {return visitor(v.as_local_time ());}
case value_t::Array : {return visitor(v.cast<value_t::Array >());} case value_t::Array : {return visitor(v.as_array ());}
case value_t::Table : {return visitor(v.cast<value_t::Table >());} case value_t::Table : {return visitor(v.as_table ());}
case value_t::Empty : break; case value_t::Empty : break;
case value_t::Unknown : break; case value_t::Unknown : break;
default: break; default: break;
@@ -937,16 +1064,16 @@ visit(Visitor&& visitor, toml::value&& v)
{ {
switch(v.type()) switch(v.type())
{ {
case value_t::Boolean : {return visitor(std::move(v.cast<value_t::Boolean >()));} case value_t::Boolean : {return visitor(std::move(v.as_boolean ()));}
case value_t::Integer : {return visitor(std::move(v.cast<value_t::Integer >()));} case value_t::Integer : {return visitor(std::move(v.as_integer ()));}
case value_t::Float : {return visitor(std::move(v.cast<value_t::Float >()));} case value_t::Float : {return visitor(std::move(v.as_float ()));}
case value_t::String : {return visitor(std::move(v.cast<value_t::String >()));} case value_t::String : {return visitor(std::move(v.as_string ()));}
case value_t::OffsetDatetime: {return visitor(std::move(v.cast<value_t::OffsetDatetime>()));} case value_t::OffsetDatetime: {return visitor(std::move(v.as_offset_datetime()));}
case value_t::LocalDatetime : {return visitor(std::move(v.cast<value_t::LocalDatetime >()));} case value_t::LocalDatetime : {return visitor(std::move(v.as_local_datetime ()));}
case value_t::LocalDate : {return visitor(std::move(v.cast<value_t::LocalDate >()));} case value_t::LocalDate : {return visitor(std::move(v.as_local_date ()));}
case value_t::LocalTime : {return visitor(std::move(v.cast<value_t::LocalTime >()));} case value_t::LocalTime : {return visitor(std::move(v.as_local_time ()));}
case value_t::Array : {return visitor(std::move(v.cast<value_t::Array >()));} case value_t::Array : {return visitor(std::move(v.as_array ()));}
case value_t::Table : {return visitor(std::move(v.cast<value_t::Table >()));} case value_t::Table : {return visitor(std::move(v.as_table ()));}
case value_t::Empty : break; case value_t::Empty : break;
case value_t::Unknown : break; case value_t::Unknown : break;
default: break; default: break;