Compare commits

..

120 Commits

Author SHA1 Message Date
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
ToruNiina
c3cb22a789 chore: fix example of err msg by re-running sample 2019-03-21 18:12:35 +09:00
ToruNiina
5aebd6b562 fix: restore the back compat of format_error
the following code was okay in the last release
```
toml::format_error("[test]", v, "test", {"hint1", "hint2"})
```
but was not okay in the current master. This commit fixes this.

cons: By this, the number of values to show is limited upto 3.
2019-03-20 20:46:22 +09:00
ToruNiina
4c13085b35 fix: add stream operator for toml::table 2019-03-20 19:30:08 +09:00
ToruNiina
8709e8a14e chore: fix incorrect syntax highlight in README 2019-03-20 19:29:03 +09:00
ToruNiina
9eea46ec01 chore: fix typoes and broken links in README 2019-03-20 12:06:55 +09:00
ToruNiina
2e9f937c43 chore: update README 2019-03-20 11:53:03 +09:00
Toru Niina
65b10b6537 Merge pull request #46 from ToruNiina/toml-literal
feat: add ""_toml literal
2019-03-20 10:12:56 +09:00
ToruNiina
b51a8d5966 fix: add missing include file in test code 2019-03-20 00:58:58 +09:00
Toru Niina
55e3d70869 Merge pull request #47 from ToruNiina/format-table
`toml::format` and top-level table
2019-03-20 00:49:59 +09:00
ToruNiina
20ba57e389 fix: add missing const specifier to some of get()s 2019-03-20 00:37:13 +09:00
ToruNiina
39bc3c64fe test: add test for ""_toml literals 2019-03-20 00:36:46 +09:00
ToruNiina
40ccf1d912 feat: add argument to control top-level inlinization 2019-03-19 23:25:26 +09:00
ToruNiina
982ae36428 feat: add ""_toml literal 2019-03-19 21:34:57 +09:00
ToruNiina
d6714ec450 feat: detect value type and format as a file
in toml::format
2019-03-19 21:24:51 +09:00
ToruNiina
773c3816be ci: remove needless confirmation on CI 2019-03-19 19:39:45 +09:00
Toru Niina
1b417ddc7a Merge pull request #45 from ToruNiina/get_or
fix `get_or` and add `find_or`
2019-03-19 01:41:21 +09:00
ToruNiina
7a0ecf977d feat: add find_or(table, key, fallback)
get_or(value, fallback) is still ok, but get_or(table, key, fallback)
is now deprecated.
2019-03-18 17:44:03 +09:00
ToruNiina
aade704411 refactor: remove needless overload of get_or 2019-03-18 17:10:18 +09:00
ToruNiina
ca3f6102ef fix: correctly resolve overloads of get_or 2019-03-18 16:44:36 +09:00
ToruNiina
4a58b629ce feat: add a way to check arg is "string literal" 2019-03-18 16:31:12 +09:00
ToruNiina
3adba237b8 feat: enable to show message for deprecated() 2019-03-18 16:28:27 +09:00
Toru Niina
ccf03d9291 Merge pull request #44 from ToruNiina/test-link
test: add test for multiple translation unit
2019-03-18 15:20:04 +09:00
Toru Niina
30ae90ebd5 Merge pull request #43 from ToruNiina/hotfix
fix: incorrect move in constructor and return type deducing in toml::visit
2019-03-18 14:08:23 +09:00
ToruNiina
d5369c3429 test: add test for multiple translation unit 2019-03-18 12:39:58 +09:00
Toru Niina
48f2f0555d Merge pull request #42 from ToruNiina/test-suite
run toml-test suite
2019-03-18 12:33:48 +09:00
ToruNiina
f40fd12e25 refactor: add and rewrite comments 2019-03-18 11:09:12 +09:00
ToruNiina
65c2c3c238 fix: correctly deduce return value of visitor 2019-03-18 10:53:04 +09:00
ToruNiina
891a61a5e3 fix: do not move array element without checking 2019-03-18 02:05:55 +09:00
ToruNiina
1e6f30f6fa chore: update README.md 2019-03-18 01:50:23 +09:00
ToruNiina
02346a3126 Merge branch 'master' into test-suite 2019-03-18 01:40:17 +09:00
Toru Niina
1908f18e95 Merge pull request #41 from ToruNiina/hotfix
fix: simplify and correct the format of timezone
2019-03-18 01:39:11 +09:00
ToruNiina
3bfa7f09ba test: use the test suite in the effective way
add tests/check_toml_test.cpp to compare json object
2019-03-18 01:36:43 +09:00
ToruNiina
243f43fafd Merge branch 'master' into hotfix 2019-03-17 21:16:37 +09:00
ToruNiina
66e27a94b6 fix: simplify and correct the format of timezone 2019-03-17 21:14:17 +09:00
ToruNiina
227688ec63 ci: make result clearer a bit 2019-03-17 19:36:23 +09:00
ToruNiina
e761a503c0 ci: fix silly mistake in circleci script 2019-03-17 19:27:58 +09:00
ToruNiina
209ad79a8f ci: fix config file of circleci 2019-03-17 19:26:22 +09:00
ToruNiina
cdf209d7f6 ci: show the status on CI 2019-03-17 19:23:52 +09:00
ToruNiina
77ab391885 ci: fix name of directory and add test script 2019-03-17 19:20:24 +09:00
ToruNiina
6628fe5ace test: add language agnostic toml-test 2019-03-17 19:12:13 +09:00
Toru Niina
f3e3000d45 Merge pull request #40 from ToruNiina/remove-to-toml
refactor: remove to_toml and related tests
2019-03-17 13:12:30 +09:00
Toru Niina
f7380c6e32 Merge pull request #39 from ToruNiina/throw-incorrect-unicode
Throw syntax_error when parser encounter an incorrect utf-8 codepoint
2019-03-17 13:12:16 +09:00
Toru Niina
d86870e038 Merge pull request #38 from ToruNiina/get-any-type
extended conversions
2019-03-17 13:11:59 +09:00
Toru Niina
0908806915 Merge pull request #33 from ToruNiina/is-something
add `is_boolean` and other stuffs like that
2019-03-16 23:55:01 +09:00
ToruNiina
d17c192681 refactor: remove to_toml and related tests 2019-03-16 17:05:58 +09:00
ToruNiina
cad8f51256 doc: add explanation of conversions to README 2019-03-16 16:56:37 +09:00
ToruNiina
43014c6619 fix: remove redefined default template argument 2019-03-16 16:24:10 +09:00
ToruNiina
30a41aa710 fix: use older style in BOOST_TEST 2019-03-16 16:15:01 +09:00
ToruNiina
04bfeba3f2 merge branch master into get-any-type 2019-03-16 15:58:18 +09:00
ToruNiina
190636b791 fix: support getting a container of external types 2019-03-16 15:52:22 +09:00
ToruNiina
31e450f9af test: add test for from/into based conversions 2019-03-16 15:46:21 +09:00
ToruNiina
b1b72a94a8 feat: support conversion with external types 2019-03-16 14:44:04 +09:00
ToruNiina
6929bcdf78 feat: add from<T> and into<T> 2019-03-16 14:27:05 +09:00
ToruNiina
fd063af7ce refactor: make include guard style uniform 2019-03-16 14:19:47 +09:00
ToruNiina
df6dcbc4ed feat: check a class has from/into_toml member fn
to support better serialization
2019-03-16 14:16:31 +09:00
ToruNiina
9b8db6a225 fix: remove extraneous null character after float
the bug was introduced by snprintf
2019-03-15 19:30:36 +09:00
ToruNiina
76863cb27f refactor: simplify branches about utf8 codepoint 2019-03-15 17:48:47 +09:00
ToruNiina
514df99e40 feat: consider invalid UTF-8 as syntax_error
the following codepoints are considered to be a syntax_error
- [0xD800, 0xDFFF]
- larger than 0x10FFFF
2019-03-15 17:39:31 +09:00
ToruNiina
055353a460 chore: merge branch 'master' into is-something 2019-03-15 17:25:17 +09:00
ToruNiina
c4c416e8b2 doc: add is_* function to README 2019-03-12 22:18:25 +09:00
ToruNiina
6693ec78f4 test: add test for toml::value::is_something() 2019-03-12 20:44:27 +09:00
ToruNiina
dc112bd6c1 feat: add is_[boolean|integer|...]() member func
it is an alias to is<toml::value_t::[Boolean|Integer|...]>
2019-03-12 20:43:07 +09:00
36 changed files with 2873 additions and 730 deletions

59
.circleci/config.yml Normal file
View File

@@ -0,0 +1,59 @@
version: 2.1
jobs:
test_suite:
environment:
- GOPATH: /home/circleci/go
docker:
- image: circleci/golang:1.9
steps:
- checkout
- run:
command: |
g++ --version
cd tests/
g++ -std=c++11 -O2 -Wall -Wextra -Werror -I../ check_toml_test.cpp -o check_toml_test
go get github.com/BurntSushi/toml-test
$GOPATH/bin/toml-test ./check_toml_test
output_result:
docker:
- image: circleci/buildpack-deps:bionic
steps:
- checkout
- run:
command: |
g++ --version
cd tests/
g++ -std=c++11 -O2 -Wall -Wextra -Werror -I../ check.cpp -o check
git clone https://github.com/BurntSushi/toml-test.git
cp check toml-test/tests/invalid
cp check toml-test/tests/valid
cd toml-test/tests/invalid
for f in $(ls ./*.toml);
do echo "==> ${f}";
cat ${f};
echo "---------------------------------------";
./check ${f} invalid;
if [ $? -ne 0 ] ; then
exit 1
fi
echo "=======================================";
done
cd ../valid
for f in $(ls ./*.toml);
do echo "==> ${f}";
cat ${f};
echo "---------------------------------------";
./check ${f} valid;
if [ $? -ne 0 ] ; then
exit 1
fi
echo "=======================================";
done
workflows:
version: 2.1
test:
jobs:
- test_suite
- output_result

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_EXTENSIONS OFF)
if(NOT DEFINED CMAKE_CXX_STANDARD)
set(CMAKE_CXX_STANDARD 11) 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.

695
README.md
View File

@@ -1,74 +1,126 @@
toml11 toml11
====== ======
[![Build Status](https://travis-ci.org/ToruNiina/toml11.svg?branch=master)](https://travis-ci.org/ToruNiina/toml11) [![Build Status on TravisCI](https://travis-ci.org/ToruNiina/toml11.svg?branch=master)](https://travis-ci.org/ToruNiina/toml11)
[![Build status](https://ci.appveyor.com/api/projects/status/m2n08a926asvg5mg/branch/master?svg=true)](https://ci.appveyor.com/project/ToruNiina/toml11/branch/master) [![Build status on Appveyor](https://ci.appveyor.com/api/projects/status/m2n08a926asvg5mg/branch/master?svg=true)](https://ci.appveyor.com/project/ToruNiina/toml11/branch/master)
[![Build status on CircleCI](https://circleci.com/gh/ToruNiina/toml11/tree/master.svg?style=svg)](https://circleci.com/gh/ToruNiina/toml11/tree/master)
[![Version](https://img.shields.io/github/release/ToruNiina/toml11.svg?style=flat)](https://github.com/ToruNiina/toml11/releases) [![Version](https://img.shields.io/github/release/ToruNiina/toml11.svg?style=flat)](https://github.com/ToruNiina/toml11/releases)
[![License](https://img.shields.io/github/license/ToruNiina/toml11.svg?style=flat)](LICENSE) [![License](https://img.shields.io/github/license/ToruNiina/toml11.svg?style=flat)](LICENSE)
[![DOI](https://zenodo.org/badge/DOI/10.5281/zenodo.1209136.svg)](https://doi.org/10.5281/zenodo.1209136) [![DOI](https://zenodo.org/badge/DOI/10.5281/zenodo.1209136.svg)](https://doi.org/10.5281/zenodo.1209136)
C++11 header-only toml parser/encoder depending only on C++ standard library. toml11 is a C++11 header-only toml parser/encoder depending only on C++ standard library.
compatible to the latest version of compatible to the latest version of
[TOML v0.5.0](https://github.com/toml-lang/toml/blob/master/versions/en/toml-v0.5.0.md) [TOML v0.5.0](https://github.com/toml-lang/toml/blob/master/versions/en/toml-v0.5.0.md)
after version 2.0.0. after version 2.0.0.
Are you looking for pre-C++11 compatible toml parser? It passes [the language agnostic test suite for TOML parsers by BurntSushi](https://github.com/BurntSushi/toml-test).
Try [Boost.toml](https://github.com/ToruNiina/Boost.toml)! Not only the test suite itself, a TOML reader/encoder also runs on [CircleCI](https://circleci.com/gh/ToruNiina/toml11).
It has almost the same functionality as this library and works with C++98 & Boost. You can see the error messages about invalid files and serialization results of valid files at
[CircleCI](https://circleci.com/gh/ToruNiina/toml11).
## How to use ## Example
## Installation ```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
- [Integration](#integration)
- [Decoding a toml file](#decoding-a-toml-file)
- [In the case of syntax error](#in-the-case-of-syntax-error)
- [Getting a toml value](#getting-a-toml-value)
- [In the case of type error](#in-the-case-of-type-error)
- [Getting an array](#getting-an-array)
- [Getting a table](#getting-a-table)
- [Dotted keys](#dotted-keys)
- [Getting an array of tables](#getting-an-array-of-tables)
- [Cost of conversion](#cost-of-conversion)
- [Getting datetime and its variants](#getting-datetime-and-its-variants)
- [Getting with a fallback](#getting-with-a-fallback)
- [Expecting conversion](#expecting-conversion)
- [Finding a value from a table](#finding-a-value-from-a-table)
- [Checking value type](#checking-value-type)
- [Visiting a toml::value](#visiting-a-tomlvalue)
- [TOML literal](#toml-literal)
- [Conversion between toml value and arbitrary types](#conversion-between-toml-value-and-arbitrary-types)
- [Invalid UTF-8 Codepoints](#invalid-utf-8-codepoints)
- [Formatting user-defined error messages](#formatting-user-defined-error-messages)
- [Getting comments related to a value](#getting-comments)
- [Serializing TOML data](#serializing-toml-data)
- [Underlying types](#underlying-types)
- [Running Tests](#running-tests)
- [Contributors](#contributors)
- [Licensing Terms](#licensing-terms)
## Integration
Just include the file after adding it to the include path. 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;
} }
``` ```
### Decoding toml file ## Decoding a toml file
The only thing you have to do is to pass a filename to the `toml::parse` function. To parse a toml file, the only thing you have to do is
to pass a filename to the `toml::parse` function.
```cpp ```cpp
const std::string fname("sample.toml"); const std::string fname("sample.toml");
const toml::table data = toml::parse(fname); const toml::table data = toml::parse(fname);
``` ```
In the case of file open error, it will throw `std::runtime_error`. If it encounters a file open error, it will throw `std::runtime_error`.
You can also pass a `stream` to the `toml::parse` function after checking the status. You can also pass a `std::istream` to the `toml::parse` function.
To show a filename in an error message, it is recommended to pass the filename
Note that on __Windows OS__, stream that is opened as text-mode automatically converts with the stream.
CRLF ("\r\n") into LF ("\n") and this leads inconsistency between file size and
the contents that would be read. This causes weird error. To use a file stream
with `toml::parse`, don't forget to pass binary mode flag when you open the
stream.
```cpp ```cpp
std::ifstream ifs("sample.toml", std::ios_base::binary); std::ifstream ifs("sample.toml", std::ios_base::binary);
assert(ifs.good()); assert(ifs.good());
const auto data = toml::parse(ifs /*, "filename" (optional)*/); const auto data = toml::parse(ifs, /*optional*/ "sample.toml");
``` ```
To show a better error message, it is recommended to pass a filename with `istream`. Note that on Windows, if a file is opened in text-mode, CRLF ("\r\n") will
See also [in the case of syntax error](#in-the-case-of-syntax-error) automatically be converted to LF ("\n") and this causes inconsistency between
and [passing invalid type to toml::get](#passing-invalid-type-to-tomlget). file size and the contents that would be read. This causes weird error.
To use a file stream with `toml::parse` on Windows, don't forget to open it
in binary mode.
### In the case of syntax error ### In the case of syntax error
If there is a syntax error in a toml file, `toml::parse` will throw `toml::syntax_error`. If there is a syntax error in a toml file, `toml::parse` will throw `toml::syntax_error`.
toml11 now has clean and informative error messages inspired by Rust and toml11 has clean and informative error messages inspired by Rust and
it looks like the following (comment after hash sign are actually not shown). it looks like the following.
```console ```console
terminate called after throwing an instance of 'toml::syntax_error' terminate called after throwing an instance of 'toml::syntax_error'
@@ -78,8 +130,8 @@ terminate called after throwing an instance of 'toml::syntax_error'
| ^------ expected newline, but got '='. # error reason | ^------ expected newline, but got '='. # error reason
``` ```
If you (mistakenly) duplicate tables and got an error, you may want to see If you (mistakenly) duplicate tables and got an error, it is helpful to see
where the other is. toml11 shows both at the same time. where they are. toml11 shows both at the same time like the following.
```console ```console
terminate called after throwing an instance of 'toml::syntax_error' terminate called after throwing an instance of 'toml::syntax_error'
@@ -93,15 +145,16 @@ terminate called after throwing an instance of 'toml::syntax_error'
``` ```
Since the error message generation is generally a difficult task, the current Since the error message generation is generally a difficult task, the current
status is not ideal. toml11 needs your help. If you encounter a weird error message, status is not ideal. If you encounter a weird error message, please let us know
please let us know and contribute to improve the quality! and contribute to improve the quality!
### Getting a toml value ## Getting a toml value
After parsing successfully, you can obtain the values from the result of After parsing successfully, you can obtain the values from the result of
`toml::parse` (here, `data`) using `toml::get` function. `toml::parse` using `toml::get` function.
```toml ```toml
# sample.toml
answer = 42 answer = 42
pi = 3.14 pi = 3.14
numbers = [1,2,3] numbers = [1,2,3]
@@ -111,6 +164,7 @@ key = "value"
``` ```
``` cpp ``` cpp
const auto data = toml::parse("sample.toml");
const auto answer = toml::get<std::int64_t >(data.at("answer")); const auto answer = toml::get<std::int64_t >(data.at("answer"));
const auto pi = toml::get<double >(data.at("pi")); const auto pi = toml::get<double >(data.at("pi"));
const auto numbers = toml::get<std::vector<int>>(data.at("numbers")); const auto numbers = toml::get<std::vector<int>>(data.at("numbers"));
@@ -120,20 +174,30 @@ const auto key = toml::get<std::string>( tab.at("key"));
``` ```
When you pass an exact TOML type that does not require type conversion, When you pass an exact TOML type that does not require type conversion,
`toml::get` returns also a reference through which you can modify the content. `toml::get` returns a reference through which you can modify the content.
```cpp ```cpp
toml::get<toml::integer>(data["answer"]) = 6 * 9; auto data = toml::parse("sample.toml");
auto& answer = toml::get<toml::integer>(data["answer"]); // get reference
answer = 6 * 9; // write to data.answer
std::cout << toml::get<int>(data.at("answer")) << std::endl; // 54 std::cout << toml::get<int>(data.at("answer")) << std::endl; // 54
``` ```
If the specified type requires conversion, you can't take a reference to the value. If the specified type requires conversion, you can't take a reference to the value.
See also [underlying types](#underlying-types). See also [underlying types](#underlying-types).
#### Passing invalid type to toml::get NOTE: To enable to get a reference, conversions between Float and Integer are not supported.
If you choose the invalid type, `toml::type_error` will be thrown. After C++17, you can use `std::string_view` to get a string from a `toml::value`.
Similar to the `syntax_error`, toml11 also displays informative error messages.
```cpp
const auto sv = toml::get<std::string_view>(tab.at("key"));
```
### In the case of type error
If you pass an invalid type to `toml::get`, `toml::type_error` will be thrown.
Similar to the case of syntax error, toml11 also displays clean error messages.
The error message when you choose `int` to get `string` value would be like this. The error message when you choose `int` to get `string` value would be like this.
```console ```console
@@ -144,15 +208,21 @@ terminate called after throwing an instance of 'toml::type_error'
| ~~~~~~~~~~~~~~ the actual type is string | ~~~~~~~~~~~~~~ the actual type is string
``` ```
NOTE: In order to show this kind of error message, all the toml values have 1 NOTE: In order to show this kind of error message, all the toml values have
`shared_ptr` that points the corresponding byte sequence and 2 iterators that point the range. pointers to represent its range in a file. The entire contents of a file is
It is recommended to destruct all the `toml::value` classes after configuring your application to save memory resources. shared by `toml::value`s and remains on the heap memory. It is recommended to
destruct all the `toml::value` classes after configuring your application
if you have a large TOML file compared to the memory resource.
### Getting arrays ## Getting an array
You can set any kind of `container` class to obtain a `toml::array` except for `map`-like classes. You can get any kind of `container` class from a `toml::array`
except for `map`-like classes.
``` cpp ``` cpp
// # sample.toml
// numbers = [1,2,3]
const auto vc = toml::get<std::vector<int> >(data.at("numbers")); const auto vc = toml::get<std::vector<int> >(data.at("numbers"));
const auto ls = toml::get<std::list<int> >(data.at("numbers")); const auto ls = toml::get<std::list<int> >(data.at("numbers"));
const auto dq = toml::get<std::deque<int> >(data.at("numbers")); const auto dq = toml::get<std::deque<int> >(data.at("numbers"));
@@ -161,16 +231,16 @@ const auto ar = toml::get<std::array<int, 3>>(data.at("numbers"));
// it will throw toml::type_error because std::array is not resizable. // it will throw toml::type_error because std::array is not resizable.
``` ```
Surprisingly, you can also get a `toml::array` as `std::pair` and `std::tuple.` Surprisingly, you can also get `std::pair`s and `std::tuple`s from `toml::array`.
```cpp ```cpp
const auto tp = toml::get<std::tuple<short, int, unsigned int>>(data.at("numbers")); const auto tp = toml::get<std::tuple<short, int, unsigned int>>(data.at("numbers"));
``` ```
The case when you need this functionality is to get an array of arrays. This functionality is helpful when you have the following toml file.
```toml ```toml
aofa = [[1,2,3], ["foo", "bar", "baz"]] # toml allows this array_of_arrays = [[1, 2, 3], ["foo", "bar", "baz"]] # toml allows this
``` ```
What is the corresponding C++ type? Obviously, it is a `std::pair` of `std::vector`s. What is the corresponding C++ type? Obviously, it is a `std::pair` of `std::vector`s.
@@ -178,21 +248,21 @@ What is the corresponding C++ type? Obviously, it is a `std::pair` of `std::vect
```cpp ```cpp
const auto aofa = toml::get< const auto aofa = toml::get<
std::pair<std::vector<int>, std::vector<std::string>> std::pair<std::vector<int>, std::vector<std::string>>
>(data.at("aofa")); >(data.at("array_of_arrays"));
``` ```
If you don't know what the type is inside the array, you can use `toml::array`, If you don't know the type of the elements, you can use `toml::array`,
which is a `std::vector` of `toml::value`, instead. which is a `std::vector` of `toml::value`, instead.
```cpp ```cpp
const auto aofa = toml::get<toml::array>(data.at("aofa")); const auto aofa = toml::get<toml::array>(data.at("array_of_arrays"));
const auto first = toml::get<toml::array>(aofa.at(0)); const auto first = toml::get<std::vector<int>>(aofa.at(0));
``` ```
See also [expecting conversion](#expecting-conversion) See also [expecting conversion](#expecting-conversion)
and [checking-value-type](#checking-value-type). and [checking-value-type](#checking-value-type).
### Getting tables ## Getting a table
`toml::table` is a key component of this library, which is an alias of `toml::table` is a key component of this library, which is an alias of
a `std::unordered_map` from `toml::key (a.k.a. std::string)` to `toml::value`. a `std::unordered_map` from `toml::key (a.k.a. std::string)` to `toml::value`.
@@ -219,12 +289,13 @@ key2 = "bar" # toml String
``` ```
```cpp ```cpp
const auto data = toml::parse("sample.toml");
const auto tab = toml::get<std::map<std::string, std::string>>(data.at("tab")); const auto tab = toml::get<std::map<std::string, std::string>>(data.at("tab"));
std::cout << tab["key1"] << std::endl; // foo std::cout << tab["key1"] << std::endl; // foo
std::cout << tab["key2"] << std::endl; // bar std::cout << tab["key2"] << std::endl; // bar
``` ```
### Dotted keys ## Dotted keys
TOML v0.5.0 has a new feature named "dotted keys". TOML v0.5.0 has a new feature named "dotted keys".
You can chain keys to represent the structure of the data. You can chain keys to represent the structure of the data.
@@ -249,41 +320,49 @@ const auto physical = toml::get<toml::table>(data.at("physical"));
const auto color = toml::get<std::string>(physical.at("color")); const auto color = toml::get<std::string>(physical.at("color"));
``` ```
### An array of tables ## Getting an array of tables
An array of tables is just an array of tables. An array of tables is just an array of tables.
You can get it completely in the same way as the other arrays and tables. You can get it completely in the same way as the other arrays and tables.
```toml ```toml
array_of_inline_table = [{key = "value1"}, {key = "value2"}, {key = "value3"}] # sample.toml
array_of_inline_tables = [{key = "value1"}, {key = "value2"}, {key = "value3"}]
[[array_of_table]] [[array_of_tables]]
key = "value4" key = "value4"
[[array_of_table]] [[array_of_tables]]
key = "value5" key = "value5"
[[array_of_table]] [[array_of_tables]]
key = "value6" key = "value6"
``` ```
```cpp ```cpp
const auto aot1 = toml::get<std::vector<toml::table>>(data.at("array_of_inline_table")); const auto data = toml::parse("sample.toml");
const auto aot2 = toml::get<std::vector<toml::table>>(data.at("array_of_table")); const auto aot1 = toml::get<std::vector<toml::table>>(data.at("array_of_inline_tables"));
const auto aot2 = toml::get<std::vector<toml::table>>(data.at("array_of_tables"));
``` ```
### Cost of conversion ## Cost of conversion
Although `toml::get` is convenient, it has additional copy-cost because Although `toml::get` is convenient, it has additional copy-cost because
it copies data contained in `toml::value` to the user-specified type. it copies data contained in `toml::value` to the user-specified type.
Of course in some case this overhead is not ignorable. Of course in some cases this overhead is not ignorable.
By passing the exact types, `toml::get` returns reference that has nealy zero overhead. ```cpp
// the following code constructs a std::vector.
// it requires heap allocation for vector and element conversion.
const auto array = toml::get<std::vector<int>>(data.at("foo"));
```
By passing the exact types, `toml::get` returns reference that has no overhead.
``` cpp ``` cpp
const auto& tab = toml::get<toml::array>(data.at("tab")); const auto& tab = toml::get<toml::array>(data.at("tab"));
const auto& numbers = toml::get<toml::table>(data.at("numbers")); const auto& numbers = toml::get<toml::table>(data.at("numbers"));
``` ```
Unfortunately, in this case you need to call `toml::get` each time you access to In this case you need to call `toml::get` each time you access to
the element of `toml::array` because `toml::array` is an array of `toml::value`. the element of `toml::array` because `toml::array` is an array of `toml::value`.
```cpp ```cpp
@@ -292,34 +371,61 @@ const auto& num1 = toml::get<toml::integer>(numbers.at(1));
const auto& num2 = toml::get<toml::integer>(numbers.at(2)); const auto& num2 = toml::get<toml::integer>(numbers.at(2));
``` ```
### Datetime and its variants ## Getting datetime and its variants
TOML v0.5.0 has 4 different datetime objects, `local_date`, `local_time`, TOML v0.5.0 has 4 different datetime objects, `local_date`, `local_time`,
`local_datetime`, and `offset_datetime`. With toml11, you can convert `local_time` `local_datetime`, and `offset_datetime`.
to your favorite `std::chrono::duration` and others to `std::chrono::system_clock::time_point`.
Since `local_date`, `local_datetime`, and `offset_datetime` represent a time
point, you can convert them to `std::chrono::system_clock::time_point`.
Contrary, `local_time` does not represents a time point because they lack a
date information, but it can be converted to `std::chrono::duration` that
represents a duration from the beginning of the day, `00:00:00.000`.
```toml ```toml
time = 12:30:00
date = 2018-12-23 date = 2018-12-23
time = 12:30:00
l_dt = 2018-12-23T12:30:00
o_dt = 2018-12-23T12:30:00+09:30
``` ```
```cpp ```cpp
const auto dur = toml::get<std::chrono::minutes>(data.at("time")); // 12 * 60 + 30 min const auto data = toml::parse("sample.toml");
const auto tp = toml::get<std::chrono::system_clock::time_point>(data.at("date"));
const auto date = toml::get<std::chrono::system_clock::time_point>(data.at("date"));
const auto l_dt = toml::get<std::chrono::system_clock::time_point>(data.at("l_dt"));
const auto o_dt = toml::get<std::chrono::system_clock::time_point>(data.at("o_dt"));
const auto time = toml::get<std::chrono::minutes>(data.at("time")); // 12 * 60 + 30 min
``` ```
### Getting with a fallback toml11 contains datetime as its own struct.
You can see the definitions in [toml/datetime.hpp](toml/datetime.hpp).
## Getting with a fallback
`toml::get_or` returns a default value if `toml::get<T>` failed. `toml::get_or` returns a default value if `toml::get<T>` failed.
```cpp ```cpp
toml::table data; // empty table! toml::value v("foo"); // v contains String
const auto value = toml::get_or(data, "key", 42); // value => int 42. const int value = toml::get_or(v, 42); // conversion fails. it returns 42.
``` ```
`toml::get_or` automatically deduces what type you want to get from the default value you passed. `toml::get_or` automatically deduces what type you want to get from
the default value you passed.
### Expecting conversion To get a reference through this function, take care about the default value.
```cpp
toml::value v("foo"); // v contains String
toml::integer& i = toml::get_or(v, 42); // does not work because binding `42`
// to `integer&` is invalid
toml::integer opt = 42;
toml::integer& i = toml::get_or(v, opt); // this works.
```
## Expecting conversion
By using `toml::expect`, you will get your expected value or an error message By using `toml::expect`, you will get your expected value or an error message
without throwing `toml::type_error`. without throwing `toml::type_error`.
@@ -343,7 +449,7 @@ const auto value = toml::expect<int>(data.at("number"))
}).unwrap_or(/*default value =*/ 3.14); }).unwrap_or(/*default value =*/ 3.14);
``` ```
### Finding value from table ## Finding a value from a table
toml11 provides utility function to find a value from `toml::table`. toml11 provides utility function to find a value from `toml::table`.
Of course, you can do this in your own way with `toml::get` because Of course, you can do this in your own way with `toml::get` because
@@ -354,7 +460,7 @@ const auto data = toml::parse("example.toml");
const auto num = toml::find<int>(data, "num", /*for err msg*/"example.toml"); const auto num = toml::find<int>(data, "num", /*for err msg*/"example.toml");
``` ```
If the value does not exist, it throws `std::out_of_range` with informative error message. If the value does not exist, it throws `std::out_of_range` with an error message.
```console ```console
terminate called after throwing an instance of 'std::out_of_range' terminate called after throwing an instance of 'std::out_of_range'
@@ -373,7 +479,8 @@ const auto num = toml::find<int>(data.at("table"), "num");
``` ```
In this case, because the value `data.at("table")` knows the locatoin of itself, In this case, because the value `data.at("table")` knows the locatoin of itself,
you don't need to pass where you find the value. `toml::find` will show you a great error message. you don't need to pass where you find the value.
`toml::find` will show you an error message including table location.
```console ```console
terminate called after throwing an instance of 'std::out_of_range' terminate called after throwing an instance of 'std::out_of_range'
@@ -385,9 +492,45 @@ terminate called after throwing an instance of 'std::out_of_range'
If it's not a `toml::table`, the same error as "invalid type" would be thrown. If it's not a `toml::table`, the same error as "invalid type" would be thrown.
### Checking value type There is another utility function, `toml::find_or`.
It is almost same as `toml::find`, but returns a default value if the value is
not found or has a different type, like `toml::get_or`.
When you don't know the exact type of toml-value, you can get `enum` type from `toml::value`. ```cpp
const auto data = toml::parse("example.toml");
const auto num = toml::find_or(data.at("table"), "num", 42);
```
## Checking value type
You can check what type of value does `toml::value` contains by `is_*` function.
```cpp
toml::value v = /* ... */;
if(v.is_integer())
{
std::cout << "value is an integer" << std::endl;
}
```
The complete list of the functions is below.
```cpp
const toml::value v(/*...*/);
v.is_boolean();
v.is_integer();
v.is_float();
v.is_string();
v.is_offset_datetime();
v.is_local_datetime();
v.is_local_date();
v.is_local_time();
v.is_array();
v.is_table();
v.is_uninitialized();
```
Also, you can get `enum class` value from `toml::value`.
```cpp ```cpp
switch(data.at("something").type()) switch(data.at("something").type())
@@ -400,32 +543,49 @@ switch(data.at("something").type())
} }
``` ```
### Fill only the matched value The complete list of the `enum`s can be found in the section
[underlying types](#underlying-types).
The more sophisticated way is using `toml::from_toml` and `std::tie`. The `enum`s can be used as a parameter of `toml::value::is` function like the following.
```cpp ```cpp
toml::table data{{"something", toml::value("foo")}}; toml::value v = /* ... */;
int i = 0; if(v.is(toml::value_t::Boolean)) // ...
double d = 0.;
std::string s;
toml::from_toml(std::tie(i, d, s), data.at("something"));
std::cout << i << ", " << d << ", " << s << std::endl; // 0, 0, foo
``` ```
Here, only matched value will be filled. The others are left intact after calling `from_toml`. ## Casting value
It should be noted that `toml::from_toml` returns as usual even if there are no matched type.
`from_toml` can be used also for single type. So far, `toml::get` is introduced, but if you don't need any type conversion,
`as_*` is simpler to use.
```cpp ```cpp
int i = 0; toml::value v = /* ... */;
toml::from_toml(i, data.at("something")); if(v.is_integer() && v.as_integer() == 42)
{
std::cout << "value is 42" << std::endl;
}
``` ```
### visiting toml::value The complete list of the functions is below.
TOML v2.1.0+ provides `toml::visit` to apply a function to `toml::value` in the ```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
toml11 provides `toml::visit` to apply a function to `toml::value` in the
same way as `std::variant`. same way as `std::variant`.
```cpp ```cpp
@@ -439,29 +599,223 @@ The function object that would be passed to `toml::visit` must be able to
recieve all the possible TOML types. Also, the result types should be the same recieve all the possible TOML types. Also, the result types should be the same
each other. each other.
### Sanitizing UTF-8 codepoints ## TOML literal
toml11 shows warning if a value of an escape sequence used toml11 supports `"..."_toml` literal.
to represent unicode character exceeds the unicode range. It accept both a bare value and a file content.
```console ```cpp
[warning] input codepoint (0011FFFF) is too large to decode as a unicode character. The result may not be able to render to your screen. using namespace toml::literals::toml_literals;
--> example.toml
3 | exceeds_unicode = "\U0011FFFF example" // `_toml` can convert a bare value without key
| ~~~~~~~~~ should be in [0x00..0x10FFFF] const toml::value v = u8"0xDEADBEEF"_toml;
// v is an Integer value containing 0xDEADBEEF.
// raw string literal (`R"(...)"` is useful for this purpose)
const toml::value t = u8R"(
title = "this is TOML literal"
[table]
key = "value"
)"_toml;
// the literal will be parsed and the result will be contained in t
``` ```
Also, toml11 throws `std::domain_error` if the code point exceeds the range that can be represented by utf-8. The literal function is defined in the same way as the standard library literals
such as `std::literals::string_literals::operator""s`.
```console ```cpp
terminate called after throwing an instance of 'std::range_error' namespace toml
what(): [error] input codepoint (0020FFFF) is too large to encode as utf-8. {
--> example.toml inline namespace literals
3 | exceeds_utf8 = "\U0020FFFF example" {
| ~~~~~~~~~ should be in [0x00..0x10FFFF] inline namespace toml_literals
{
toml::value operator""_toml(const char* str, std::size_t len);
} // toml_literals
} // literals
} // toml
``` ```
### Formatting your error Access to the operator can be gained with `using namespace 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
You can also use `toml::get` and other related functions with the types you defined
after you implement some stuff.
```cpp
namespace ext
{
struct foo
{
int a;
double b;
std::string c;
};
} // ext
const auto data = toml::parse("example.toml");
const foo f = toml::get<ext::foo>(data.at("foo"));
```
There are 2 ways to use `toml::get` with the types that you defined.
The first one is to implement `from_toml(const toml::value&)` member function.
```cpp
namespace ext
{
struct foo
{
int a;
double b;
std::string c;
void from_toml(const toml::value& v)
{
this->a = toml::find<int >(v, "a");
this->b = toml::find<double >(v, "b");
this->c = toml::find<std::string>(v, "c");
return;
}
};
} // ext
```
In this way, because `toml::get` first constructs `foo` without arguments,
the type should be default-constructible.
The second is to implement specialization of `toml::from` for your type.
```cpp
namespace ext
{
struct foo
{
int a;
double b;
std::string c;
};
} // ext
namespace toml
{
template<>
struct from<ext::foo>
{
ext::foo from_toml(const toml::value& v)
{
ext::foo f;
f.a = toml::find<int >(v, "a");
f.b = toml::find<double >(v, "b");
f.c = toml::find<std::string>(v, "c");
return f;
}
};
} // toml
```
In this way, since the conversion function is defined outside of the class,
you can add conversion between `toml::value` and classes defined in another library.
Note that you cannot implement both of the functions because the overload
resolution of `toml::get` will be ambiguous.
----
The opposite direction is also supported in a similar way. You can directly
pass your type to `toml::value`'s constructor by introducing `into_toml` or
`toml::into<T>`.
```cpp
namespace ext
{
struct foo
{
int a;
double b;
std::string c;
toml::table into_toml() const // you need to mark it const.
{
return toml::table{{"a", this->a}, {"b", this->b}, {"c", this->c}};
}
};
} // ext
ext::foo f{42, 3.14, "foobar"};
toml::value v(f);
```
The definition of `toml::into<T>` is similar to `toml::from<T>`.
```cpp
namespace ext
{
struct foo
{
int a;
double b;
std::string c;
};
} // ext
namespace toml
{
template<>
struct into<ext::foo>
{
toml::table into_toml(const ext::foo& f)
{
return toml::table{{"a", f.a}, {"b", f.b}, {"c", f.c}};
}
};
} // toml
ext::foo f{42, 3.14, "foobar"};
toml::value v(f);
```
Any type that can be converted to `toml::value`, e.g. `toml::table`, `toml::array`,
is okay to return from `into_toml`.
## Invalid UTF-8 codepoints
toml11 throws `syntax_error` if a value of an escape sequence
representing unicode character is not a valid UTF-8 codepoint.
```console
what(): [error] toml::read_utf8_codepoint: input codepoint is too large.
--> utf8.toml
1 | exceeds_unicode = "\U0011FFFF example"
| ^--------- should be in [0x00..0x10FFFF]
```
## Formatting user-defined error messages
When you encounter an error after you read the toml value, you may want to When you encounter an error after you read the toml value, you may want to
show the error with the value. show the error with the value.
@@ -514,22 +868,65 @@ you will get an error message like this.
| ~~ maximum number here | ~~ maximum number here
``` ```
### Serializing TOML data ## Getting comments
toml11 v2.1.0 enables you to serialize data into toml format. 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
toml11 (after v2.1.0) enables you to serialize data into toml format.
```cpp ```cpp
const auto data = toml::table{{"foo", 42}, {"bar", "baz"}}; const auto data = toml::table{{"foo", 42}, {"bar", "baz"}};
const std::string serial = toml::format(data);
// serial == "{bar=\"baz\",foo=42}"
std::cout << data << std::endl; std::cout << data << std::endl;
// bar = "baz" // bar = "baz"
// foo = 42 // foo = 42
``` ```
toml11 automatically makes a tiny table and array inline. toml11 automatically makes a small table and small array inline.
You can specify the width to make them inline by `std::setw` for streams. You can specify the width to make them inline by `std::setw` for streams.
```cpp ```cpp
@@ -572,23 +969,8 @@ It is recommended to set width before printing data. Some I/O functions changes
width to 0, and it makes all the stuff (including `toml::array`) multiline. width to 0, and it makes all the stuff (including `toml::array`) multiline.
The resulting files becomes too long. The resulting files becomes too long.
`toml::format` receives optional second argument to set the width.
By default, it is 80.
```cpp
const auto data = toml::table{
{"qux", toml::table{{"foo", 42}, {"bar", "baz"}}}
};
const std::string serial = toml::format(data, /*width = */ 0);
// [qux]
// bar = "baz"
// foo = 42
```
To control the precision of floating point numbers, you need to pass To control the precision of floating point numbers, you need to pass
`std::setprecision` to stream or pass `int` to the optional third argument of `std::setprecision` to stream.
`toml::format` (by default, it is `std::numeric_limits<double>::max_digit10`).
```cpp ```cpp
const auto data = toml::table{ const auto data = toml::table{
@@ -601,8 +983,32 @@ std::cout << std::setprecision(17) << data << std::endl;
std::cout << std::setprecision( 7) << data << std::endl; std::cout << std::setprecision( 7) << data << std::endl;
// e = 2.718282 // e = 2.718282
// pi = 3.141593 // pi = 3.141593
```
const std::string serial = toml::format(data, /*width = */ 0, /*prec = */ 17); There is another way to format toml values, `toml::format()`.
It returns `std::string` that represents a value.
```cpp
const toml::value v{{"a", 42}};
const std::string fmt = toml::format(v);
// a = 42
```
Note that since `toml::format` formats a value, the resulting string may lack
the key value.
```cpp
const toml::value v{3.14};
const std::string fmt = toml::format(v);
// 3.14
```
To control the width and precision, `toml::format` receives optional second and
third arguments to set them. By default, the witdh is 80 and the precision is
`std::numeric_limits<double>::max_digit10`.
```cpp
const auto serial = toml::format(data, /*width = */ 0, /*prec = */ 17);
``` ```
## Underlying types ## Underlying types
@@ -618,7 +1024,7 @@ The toml types (can be used as `toml::*` in this library) and corresponding `enu
| LocalDate | `toml::local_date` | `toml::value_t::LocalDate` | | LocalDate | `toml::local_date` | `toml::value_t::LocalDate` |
| LocalTime | `toml::local_time` | `toml::value_t::LocalTime` | | LocalTime | `toml::local_time` | `toml::value_t::LocalTime` |
| LocalDatetime | `toml::local_datetime` | `toml::value_t::LocalDatetime` | | LocalDatetime | `toml::local_datetime` | `toml::value_t::LocalDatetime` |
| OffsetDatetime | `toml::offset_datetime` | `toml::value_t::offsetDatetime` | | OffsetDatetime | `toml::offset_datetime` | `toml::value_t::OffsetDatetime` |
| Array | `std::vector<toml::value>` | `toml::value_t::Array` | | Array | `std::vector<toml::value>` | `toml::value_t::Array` |
| Table | `std::unordered_map<toml::key, toml::value>` | `toml::value_t::Table` | | Table | `std::unordered_map<toml::key, toml::value>` | `toml::value_t::Table` |
@@ -633,21 +1039,40 @@ not capable of representing a Local Time independent from a specific day.
It is recommended to get `Datetime`s as `std::chrono` classes through `toml::get`. It is recommended to get `Datetime`s as `std::chrono` classes through `toml::get`.
## Running Tests
To run test codes, you need to clone toml-lang/toml repository under `build/` directory
because some of the test codes read a file in the repository.
```sh
$ mkdir build
$ cd build
$ git clone https://github.com/toml-lang/toml.git
$ cmake ..
$ make
$ make test
```
To run the language agnostic test suite, you need to compile
`tests/check_toml_test.cpp` and pass it to the tester.
## Contributors ## Contributors
I thank the contributor for providing great feature to this repository. I appreciate the help of the contributors who introduced the great feature to this library.
- Guillaume Fraux (@Luthaf) - Guillaume Fraux (@Luthaf)
- Windows support and CI on Appvayor - Windows support and CI on Appvayor
- Intel Compiler support - Intel Compiler support
- Quentin Khan (@xaxousis) - Quentin Khan (@xaxousis)
- Found & Fixed a bug around ODR - Found & Fixed a bug around ODR
- Improved error message 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
## 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

@@ -20,15 +20,17 @@ set(TEST_NAMES
test_parse_inline_table test_parse_inline_table
test_parse_key test_parse_key
test_parse_table_key test_parse_table_key
test_literals
test_comments
test_get test_get
test_get_related_func test_get_related_func
test_to_toml
test_from_toml test_from_toml
test_parse_file test_parse_file
test_serialize_file test_serialize_file
test_parse_unicode test_parse_unicode
test_error_detection test_error_detection
test_format_error test_format_error
test_extended_conversions
) )
CHECK_CXX_COMPILER_FLAG("-Wall" COMPILER_SUPPORTS_WALL) CHECK_CXX_COMPILER_FLAG("-Wall" COMPILER_SUPPORTS_WALL)
@@ -98,3 +100,8 @@ foreach(TEST_NAME ${TEST_NAMES})
) )
endif() endif()
endforeach(TEST_NAME) endforeach(TEST_NAME)
# this test is to check it compiles. it will not run
add_executable(test_multiple_translation_unit
test_multiple_translation_unit_1.cpp
test_multiple_translation_unit_2.cpp)

41
tests/check.cpp Normal file
View File

@@ -0,0 +1,41 @@
#include "toml.hpp"
#include <iostream>
#include <iomanip>
int main(int argc, char **argv)
{
if(argc != 3)
{
std::cerr << "usage: ./check [filename] [valid|invalid]" << std::endl;
return 1;
}
const std::string file_kind(argv[2]);
try
{
const auto data = toml::parse(argv[1]);
std::cout << std::setprecision(16) << std::setw(80) << data;
if(file_kind == "valid")
{
return 0;
}
else
{
return 1;
}
}
catch(const toml::syntax_error& err)
{
std::cout << "what(): " << err.what() << std::endl;
if(file_kind == "invalid")
{
return 0;
}
else
{
return 1;
}
}
return 127;
}

142
tests/check_toml_test.cpp Normal file
View File

@@ -0,0 +1,142 @@
#include "toml.hpp"
#include <iostream>
#include <iomanip>
struct json_serializer
{
void operator()(toml::boolean v)
{
std::cout << "{\"type\":\"bool\",\"value\":\"" << toml::value(v) << "\"}";
return ;
}
void operator()(toml::integer v)
{
std::cout << "{\"type\":\"integer\",\"value\":\"" << toml::value(v) << "\"}";
return ;
}
void operator()(toml::floating v)
{
std::cout << "{\"type\":\"float\",\"value\":\"" << toml::value(v) << "\"}";
return ;
}
void operator()(const toml::string& v)
{
// since toml11 automatically convert string to multiline string that is
// valid only in TOML, we need to format the string to make it valid in
// JSON.
std::cout << "{\"type\":\"string\",\"value\":\""
<< this->escape_string(v.str) << "\"}";
return ;
}
void operator()(const toml::local_time& v)
{
std::cout << "{\"type\":\"local_time\",\"value\":\"" << toml::value(v) << "\"}";
return ;
}
void operator()(const toml::local_date& v)
{
std::cout << "{\"type\":\"local_date\",\"value\":\"" << toml::value(v) << "\"}";
return ;
}
void operator()(const toml::local_datetime& v)
{
std::cout << "{\"type\":\"local_datetime\",\"value\":\"" << toml::value(v) << "\"}";
return ;
}
void operator()(const toml::offset_datetime& v)
{
std::cout << "{\"type\":\"datetime\",\"value\":\"" << toml::value(v) << "\"}";
return ;
}
void operator()(const toml::array& v)
{
if(!v.empty() && v.front().is_table())
{
std::cout << '[';
bool is_first = true;
for(const auto& elem : v)
{
if(!is_first) {std::cout << ", ";}
is_first = false;
toml::visit(*this, elem);
}
std::cout << ']';
}
else
{
std::cout << "{\"type\":\"array\",\"value\":[";
bool is_first = true;
for(const auto& elem : v)
{
if(!is_first) {std::cout << ", ";}
is_first = false;
toml::visit(*this, elem);
}
std::cout << "]}";
}
return ;
}
void operator()(const toml::table& v)
{
std::cout << '{';
bool is_first = true;
for(const auto& elem : v)
{
if(!is_first) {std::cout << ", ";}
is_first = false;
std::cout << toml::format(toml::string(elem.first),
std::numeric_limits<std::size_t>::max());
std::cout << ':';
toml::visit(*this, elem.second);
}
std::cout << '}';
return ;
}
std::string escape_string(const std::string& s) const
{
std::string retval;
for(const char c : s)
{
switch(c)
{
case '\\': {retval += "\\\\"; break;}
case '\"': {retval += "\\\""; break;}
case '\b': {retval += "\\b"; break;}
case '\t': {retval += "\\t"; break;}
case '\f': {retval += "\\f"; break;}
case '\n': {retval += "\\n"; break;}
case '\r': {retval += "\\r"; break;}
default : {retval += c; break;}
}
}
return retval;
}
};
int main()
{
try
{
std::vector<char> buf;
std::cin.peek();
while(!std::cin.eof())
{
buf.push_back(std::cin.get());
std::cin.peek();
}
std::string bufstr(buf.begin(), buf.end());
std::istringstream ss(bufstr);
const auto data = toml::parse(ss);
std::cout << std::setprecision(std::numeric_limits<double>::max_digits10);
toml::visit(json_serializer(), data);
return 0;
}
catch(const toml::syntax_error& err)
{
std::cout << "what(): " << err.what() << std::endl;
return 1;
}
}

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

@@ -0,0 +1,116 @@
#define BOOST_TEST_MODULE "test_extended_conversions"
#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>
namespace extlib
{
struct foo
{
int a;
std::string b;
};
struct bar
{
int a;
std::string b;
void from_toml(const toml::value& v)
{
this->a = toml::find<int>(v, "a");
this->b = toml::find<std::string>(v, "b");
return ;
}
toml::table into_toml() const
{
return toml::table{{"a", this->a}, {"b", this->b}};
}
};
} // extlib
namespace toml
{
template<>
struct from<extlib::foo>
{
static extlib::foo from_toml(const toml::value& v)
{
return extlib::foo{toml::find<int>(v, "a"), toml::find<std::string>(v, "b")};
}
};
template<>
struct into<extlib::foo>
{
static toml::table into_toml(const extlib::foo& f)
{
return toml::table{{"a", f.a}, {"b", f.b}};
}
};
} // toml
BOOST_AUTO_TEST_CASE(test_conversion_by_member_methods)
{
const toml::value v{{"a", 42}, {"b", "baz"}};
const auto foo = toml::get<extlib::foo>(v);
BOOST_CHECK_EQUAL(foo.a, 42);
BOOST_CHECK_EQUAL(foo.b, "baz");
const toml::value v2(foo);
BOOST_CHECK_EQUAL(v, v2);
}
BOOST_AUTO_TEST_CASE(test_conversion_by_specialization)
{
const toml::value v{{"a", 42}, {"b", "baz"}};
const auto bar = toml::get<extlib::bar>(v);
BOOST_CHECK_EQUAL(bar.a, 42);
BOOST_CHECK_EQUAL(bar.b, "baz");
const toml::value v2(bar);
BOOST_CHECK_EQUAL(v, v2);
}
BOOST_AUTO_TEST_CASE(test_recursive_conversion)
{
const toml::value v{
toml::table{{"a", 42}, {"b", "baz"}},
toml::table{{"a", 43}, {"b", "qux"}},
toml::table{{"a", 44}, {"b", "quux"}},
toml::table{{"a", 45}, {"b", "foobar"}},
};
const auto foos = toml::get<std::vector<extlib::foo>>(v);
BOOST_CHECK_EQUAL(foos.size() , 4ul);
BOOST_CHECK_EQUAL(foos.at(0).a , 42);
BOOST_CHECK_EQUAL(foos.at(1).a , 43);
BOOST_CHECK_EQUAL(foos.at(2).a , 44);
BOOST_CHECK_EQUAL(foos.at(3).a , 45);
BOOST_CHECK_EQUAL(foos.at(0).b , "baz");
BOOST_CHECK_EQUAL(foos.at(1).b , "qux");
BOOST_CHECK_EQUAL(foos.at(2).b , "quux");
BOOST_CHECK_EQUAL(foos.at(3).b , "foobar");
const auto bars = toml::get<std::vector<extlib::bar>>(v);
BOOST_CHECK_EQUAL(bars.size() , 4ul);
BOOST_CHECK_EQUAL(bars.at(0).a , 42);
BOOST_CHECK_EQUAL(bars.at(1).a , 43);
BOOST_CHECK_EQUAL(bars.at(2).a , 44);
BOOST_CHECK_EQUAL(bars.at(3).a , 45);
BOOST_CHECK_EQUAL(bars.at(0).b , "baz");
BOOST_CHECK_EQUAL(bars.at(1).b , "qux");
BOOST_CHECK_EQUAL(bars.at(2).b , "quux");
BOOST_CHECK_EQUAL(bars.at(3).b , "foobar");
}

View File

@@ -22,7 +22,7 @@ BOOST_AUTO_TEST_CASE(test_1_value)
{ {
const std::string pretty_error = const std::string pretty_error =
toml::format_error("[error] test error", val, "this is a value", toml::format_error("[error] test error", val, "this is a value",
std::vector<std::string>{"this is a hint"}); {"this is a hint"});
std::cout << pretty_error << std::endl; std::cout << pretty_error << std::endl;
} }
} }
@@ -44,7 +44,7 @@ BOOST_AUTO_TEST_CASE(test_2_values)
toml::format_error("[error] test error with two values", toml::format_error("[error] test error with two values",
v1, "this is the answer", v1, "this is the answer",
v2, "this is the pi", v2, "this is the pi",
std::vector<std::string>{"hint"}); {"hint"});
std::cout << pretty_error << std::endl; std::cout << pretty_error << std::endl;
} }
} }
@@ -69,7 +69,7 @@ BOOST_AUTO_TEST_CASE(test_3_values)
v1, "this is the answer", v1, "this is the answer",
v2, "this is the pi", v2, "this is the pi",
v3, "this is a meta-syntactic variable", v3, "this is a meta-syntactic variable",
std::vector<std::string>{"hint 1", "hint 2"}); {"hint 1", "hint 2"});
std::cout << pretty_error << std::endl; std::cout << pretty_error << std::endl;
} }
} }

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

@@ -45,21 +45,194 @@ BOOST_AUTO_TEST_CASE(test_find)
BOOST_AUTO_TEST_CASE(test_get_or) BOOST_AUTO_TEST_CASE(test_get_or)
{ {
{ // requires conversion int -> uint
toml::table v{{"num", 42}};
BOOST_CHECK_EQUAL(42, toml::get_or<int>(v, "num", 0));
BOOST_CHECK_EQUAL(0, toml::get_or<int>(v, "foo", 0));
}
{
toml::value v = toml::table{{"num", 42}};
BOOST_CHECK_EQUAL(42, toml::get_or<int>(v, "num", 0));
BOOST_CHECK_EQUAL(0, toml::get_or<int>(v, "foo", 0));
}
{ {
toml::value v1(42); toml::value v1(42);
toml::value v2(3.14); toml::value v2(3.14);
BOOST_CHECK_EQUAL(42, toml::get_or<int>(v1, 0)); BOOST_CHECK_EQUAL(42u, toml::get_or(v1, 0u));
BOOST_CHECK_EQUAL(0, toml::get_or<int>(v2, 0)); BOOST_CHECK_EQUAL(0u, toml::get_or(v2, 0u));
}
// exact toml type
{
toml::value v1(42);
toml::value v2(3.14);
toml::integer opt(0);
BOOST_CHECK_EQUAL(42, toml::get_or(v1, opt));
BOOST_CHECK_EQUAL(0, toml::get_or(v2, opt));
toml::value v3("foobar");
toml::string s("bazqux");
BOOST_CHECK_EQUAL("foobar", toml::get_or(v3, s));
BOOST_CHECK_EQUAL("bazqux", toml::get_or(v1, s));
}
// std::string
{
toml::value v1("foobar");
toml::value v2(42);
std::string s1("bazqux");
const std::string s2("bazqux");
BOOST_CHECK_EQUAL("foobar", toml::get_or(v1, s1));
BOOST_CHECK_EQUAL("bazqux", toml::get_or(v2, s1));
std::string& v1r = toml::get_or(v1, s1);
std::string& s1r = toml::get_or(v2, s1);
BOOST_CHECK_EQUAL("foobar", v1r);
BOOST_CHECK_EQUAL("bazqux", s1r);
BOOST_CHECK_EQUAL("foobar", toml::get_or(v1, s2));
BOOST_CHECK_EQUAL("bazqux", toml::get_or(v2, s2));
BOOST_CHECK_EQUAL("foobar", toml::get_or(v1, std::move(s1)));
BOOST_CHECK_EQUAL("bazqux", toml::get_or(v2, std::move(s1)));
}
// string literal
{
toml::value v1("foobar");
toml::value v2(42);
BOOST_CHECK_EQUAL("foobar", toml::get_or(v1, "bazqux"));
BOOST_CHECK_EQUAL("bazqux", toml::get_or(v2, "bazqux"));
const char* lit = "bazqux";
BOOST_CHECK_EQUAL("foobar", toml::get_or(v1, lit));
BOOST_CHECK_EQUAL("bazqux", toml::get_or(v2, lit));
}
}
BOOST_AUTO_TEST_CASE(test_find_or)
{
// ========================================================================
// pass toml::value
//
// requires conversion int -> uint
{
toml::table v{{"num", 42}};
BOOST_CHECK_EQUAL(42u, toml::find_or(v, "num", 0u));
BOOST_CHECK_EQUAL(0u, toml::find_or(v, "foo", 0u));
}
// exact toml type
{
toml::table v1{{"key", 42 }};
toml::table v2{{"key", 3.14}};
toml::table v3{{"not", "key"}};
toml::integer opt(0);
BOOST_CHECK_EQUAL(42, toml::find_or(v1, "key", opt));
BOOST_CHECK_EQUAL(0, toml::find_or(v2, "key", opt));
BOOST_CHECK_EQUAL(0, toml::find_or(v3, "key", opt));
toml::table v4{{"str", "foobar"}};
toml::string s("bazqux");
BOOST_CHECK_EQUAL("foobar", toml::find_or(v4, "str", s));
BOOST_CHECK_EQUAL("bazqux", toml::find_or(v1, "str", s));
}
// std::string
{
toml::table v1{{"key", "foobar"}};
toml::table v2{{"key", 42}};
std::string s1("bazqux");
const std::string s2("bazqux");
BOOST_CHECK_EQUAL("foobar", toml::find_or(v1, "key", s1));
BOOST_CHECK_EQUAL("bazqux", toml::find_or(v2, "key", s1));
std::string& v1r = toml::find_or(v1, "key", s1);
std::string& s1r = toml::find_or(v2, "key", s1);
BOOST_CHECK_EQUAL("foobar", v1r);
BOOST_CHECK_EQUAL("bazqux", s1r);
BOOST_CHECK_EQUAL("foobar", toml::find_or(v1, "key", s2));
BOOST_CHECK_EQUAL("bazqux", toml::find_or(v2, "key", s2));
BOOST_CHECK_EQUAL("foobar", toml::find_or(std::move(v1), "key", std::move(s1)));
s1 = "bazqux"; // restoring moved value
BOOST_CHECK_EQUAL("bazqux", toml::find_or(std::move(v2), "key", std::move(s1)));
}
// string literal
{
toml::table v1{{"key", "foobar"}};
toml::table v2{{"key",42}};
BOOST_CHECK_EQUAL("foobar", toml::find_or(v1, "key", "bazqux"));
BOOST_CHECK_EQUAL("bazqux", toml::find_or(v2, "key", "bazqux"));
const char* lit = "bazqux";
BOOST_CHECK_EQUAL("foobar", toml::find_or(v1, "key", lit));
BOOST_CHECK_EQUAL("bazqux", toml::find_or(v2, "key", lit));
}
// ========================================================================
// pass toml::value
//
// requires conversion int -> uint
{
toml::table v = toml::table{{"num", 42}};
BOOST_CHECK_EQUAL(42u, toml::find_or(v, "num", 0u));
BOOST_CHECK_EQUAL(0u, toml::find_or(v, "foo", 0u));
}
// exact toml type
{
toml::value v1 = toml::table{{"key", 42 }};
toml::value v2 = toml::table{{"key", 3.14}};
toml::value v3 = toml::table{{"not", "key"}};
BOOST_CHECK_EQUAL(42, toml::find_or(v1, "key", toml::integer(0)));
BOOST_CHECK_EQUAL( 0, toml::find_or(v2, "key", toml::integer(0)));
BOOST_CHECK_EQUAL( 0, toml::find_or(v3, "key", toml::integer(0)));
toml::value v4 = toml::table{{"str", "foobar"}};
toml::string s("bazqux");
BOOST_CHECK_EQUAL("foobar", toml::find_or(v4, "str", s));
BOOST_CHECK_EQUAL("bazqux", toml::find_or(v1, "str", s));
}
// std::string
{
toml::value v1 = toml::table{{"key", "foobar"}};
toml::value v2 = toml::table{{"key", 42}};
std::string s1("bazqux");
const std::string s2("bazqux");
BOOST_CHECK_EQUAL("foobar", toml::find_or(v1, "key", s1));
BOOST_CHECK_EQUAL("bazqux", toml::find_or(v2, "key", s1));
std::string& v1r = toml::find_or(v1, "key", s1);
std::string& s1r = toml::find_or(v2, "key", s1);
BOOST_CHECK_EQUAL("foobar", v1r);
BOOST_CHECK_EQUAL("bazqux", s1r);
BOOST_CHECK_EQUAL("foobar", toml::find_or(v1, "key", s2));
BOOST_CHECK_EQUAL("bazqux", toml::find_or(v2, "key", s2));
BOOST_CHECK_EQUAL("foobar", toml::find_or(std::move(v1), "key", std::move(s1)));
s1 = "bazqux"; // restoring moved value
BOOST_CHECK_EQUAL("bazqux", toml::find_or(std::move(v2), "key", std::move(s1)));
}
// string literal
{
toml::value v1 = toml::table{{"key", "foobar"}};
toml::value v2 = toml::table{{"key",42}};
BOOST_CHECK_EQUAL("foobar", toml::find_or(v1, "key", "bazqux"));
BOOST_CHECK_EQUAL("bazqux", toml::find_or(v2, "key", "bazqux"));
const char* lit = "bazqux";
BOOST_CHECK_EQUAL("foobar", toml::find_or(v1, "key", lit));
BOOST_CHECK_EQUAL("bazqux", toml::find_or(v2, "key", lit));
} }
} }

154
tests/test_literals.cpp Normal file
View File

@@ -0,0 +1,154 @@
#define BOOST_TEST_MODULE "test_literals"
#ifdef UNITTEST_FRAMEWORK_LIBRARY_EXIST
#include <boost/test/unit_test.hpp>
#else
#define BOOST_TEST_NO_LIB
#include <boost/test/included/unit_test.hpp>
#endif
#include <toml.hpp>
#include <map>
BOOST_AUTO_TEST_CASE(test_file_as_literal)
{
using namespace toml::literals::toml_literals;
{
const toml::value r{{"a", 42}, {"b", "baz"}};
const toml::value v = u8R"(
a = 42
b = "baz"
)"_toml;
BOOST_CHECK_EQUAL(r, v);
}
{
const toml::value r{
{"c", 3.14},
{"table", toml::table{{"a", 42}, {"b", "baz"}}}
};
const toml::value v = u8R"(
c = 3.14
[table]
a = 42
b = "baz"
)"_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_AUTO_TEST_CASE(test_value_as_literal)
{
using namespace toml::literals::toml_literals;
{
const toml::value v1 = u8"true"_toml;
const toml::value v2 = u8"false"_toml;
BOOST_CHECK(v1.is_boolean());
BOOST_CHECK(v2.is_boolean());
BOOST_CHECK(toml::get<bool>(v1));
BOOST_CHECK(!toml::get<bool>(v2));
}
{
const toml::value v1 = u8"123_456"_toml;
const toml::value v2 = u8"0b0010"_toml;
const toml::value v3 = u8"0xDEADBEEF"_toml;
BOOST_CHECK(v1.is_integer());
BOOST_CHECK(v2.is_integer());
BOOST_CHECK(v3.is_integer());
BOOST_CHECK_EQUAL(toml::get<toml::integer>(v1), 123456);
BOOST_CHECK_EQUAL(toml::get<toml::integer>(v2), 2);
BOOST_CHECK_EQUAL(toml::get<toml::integer>(v3), 0xDEADBEEF);
}
{
const toml::value v1 = u8"3.1415"_toml;
const toml::value v2 = u8"6.02e+23"_toml;
BOOST_CHECK(v1.is_float());
BOOST_CHECK(v2.is_float());
BOOST_CHECK_CLOSE(toml::get<double>(v1), 3.1415, 0.00001);
BOOST_CHECK_CLOSE(toml::get<double>(v2), 6.02e23, 0.0001);
}
{
const toml::value v1 = u8R"("foo")"_toml;
const toml::value v2 = u8R"('foo')"_toml;
const toml::value v3 = u8R"("""foo""")"_toml;
const toml::value v4 = u8R"('''foo''')"_toml;
BOOST_CHECK(v1.is_string());
BOOST_CHECK(v2.is_string());
BOOST_CHECK(v3.is_string());
BOOST_CHECK(v4.is_string());
BOOST_CHECK_EQUAL(toml::get<std::string>(v1), "foo");
BOOST_CHECK_EQUAL(toml::get<std::string>(v2), "foo");
BOOST_CHECK_EQUAL(toml::get<std::string>(v3), "foo");
BOOST_CHECK_EQUAL(toml::get<std::string>(v4), "foo");
}
{
const toml::value v1 = u8R"([1,2,3])"_toml;
BOOST_CHECK(v1.is_array());
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;
BOOST_CHECK(v1.is_table());
BOOST_CHECK((toml::get<std::map<std::string,int>>(v1) ==
std::map<std::string,int>{{"a", 42}}));
}
{
const toml::value v1 = u8"1979-05-27"_toml;
BOOST_CHECK(v1.is_local_date());
BOOST_CHECK_EQUAL(toml::get<toml::local_date>(v1),
toml::local_date(1979, toml::month_t::May, 27));
}
{
const toml::value v1 = u8"12:00:00"_toml;
BOOST_CHECK(v1.is_local_time());
BOOST_CHECK(toml::get<std::chrono::hours>(v1) == std::chrono::hours(12));
}
{
const toml::value v1 = u8"1979-05-27T07:32:00"_toml;
BOOST_CHECK(v1.is_local_datetime());
BOOST_CHECK_EQUAL(toml::get<toml::local_datetime>(v1),
toml::local_datetime(toml::local_date(1979, toml::month_t::May, 27),
toml::local_time(7, 32, 0)));
}
{
const toml::value v1 = "1979-05-27T07:32:00Z"_toml;
BOOST_CHECK(v1.is_offset_datetime());
BOOST_CHECK_EQUAL(toml::get<toml::offset_datetime>(v1),
toml::offset_datetime(toml::local_date(1979, toml::month_t::May, 27),
toml::local_time(7, 32, 0), toml::time_offset(0, 0)));
}
}

View File

@@ -0,0 +1,11 @@
#include <toml.hpp>
int read_a(const toml::table&);
int main()
{
const std::string content("a = 0");
std::istringstream iss(content);
const auto data = toml::parse(iss, "test_multiple_translation_unit.toml");
return read_a(data);
}

View File

@@ -0,0 +1,6 @@
#include <toml.hpp>
int read_a(const toml::table& t)
{
return toml::get<int>(t.at("a"));
}

View File

@@ -1,189 +0,0 @@
#define BOOST_TEST_MODULE "test_to_toml"
#ifdef UNITTEST_FRAMEWORK_LIBRARY_EXIST
#include <boost/test/unit_test.hpp>
#else
#define BOOST_TEST_NO_LIB
#include <boost/test/included/unit_test.hpp>
#endif
#include <toml.hpp>
#include <map>
#include <list>
BOOST_AUTO_TEST_CASE(test_value_boolean)
{
toml::value v1 = toml::to_toml(true);
toml::value v2 = toml::to_toml(false);
BOOST_CHECK_EQUAL(v1.type(), toml::value_t::Boolean);
BOOST_CHECK_EQUAL(v2.type(), toml::value_t::Boolean);
BOOST_CHECK(v1.is(toml::value_t::Boolean));
BOOST_CHECK(v2.is(toml::value_t::Boolean));
BOOST_CHECK(v1.is<toml::Boolean>());
BOOST_CHECK(v2.is<toml::Boolean>());
BOOST_CHECK_EQUAL(v1.cast<toml::value_t::Boolean>(), true);
BOOST_CHECK_EQUAL(v2.cast<toml::value_t::Boolean>(), false);
}
BOOST_AUTO_TEST_CASE(test_value_integer)
{
toml::value v1 = toml::to_toml(-42);
toml::value v2 = toml::to_toml(42u);
BOOST_CHECK_EQUAL(v1.type(), toml::value_t::Integer);
BOOST_CHECK_EQUAL(v2.type(), toml::value_t::Integer);
BOOST_CHECK(v1.is(toml::value_t::Integer));
BOOST_CHECK(v2.is(toml::value_t::Integer));
BOOST_CHECK(v1.is<toml::Integer>());
BOOST_CHECK(v2.is<toml::Integer>());
BOOST_CHECK_EQUAL(v1.cast<toml::value_t::Integer>(), -42);
BOOST_CHECK_EQUAL(v2.cast<toml::value_t::Integer>(), 42u);
}
BOOST_AUTO_TEST_CASE(test_value_float)
{
toml::value v1 = toml::to_toml(3.14);
toml::value v2 = toml::to_toml(3.14f);
BOOST_CHECK_EQUAL(v1.type(), toml::value_t::Float);
BOOST_CHECK_EQUAL(v2.type(), toml::value_t::Float);
BOOST_CHECK(v1.is(toml::value_t::Float));
BOOST_CHECK(v2.is(toml::value_t::Float));
BOOST_CHECK(v1.is<toml::Float>());
BOOST_CHECK(v2.is<toml::Float>());
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_AUTO_TEST_CASE(test_value_string)
{
toml::value v1 = toml::to_toml(std::string("foo"));
toml::value v2 = toml::to_toml(std::string("foo"), toml::string_t::literal);
toml::value v3 = toml::to_toml("foo");
BOOST_CHECK_EQUAL(v1.type(), toml::value_t::String);
BOOST_CHECK_EQUAL(v2.type(), toml::value_t::String);
BOOST_CHECK_EQUAL(v3.type(), toml::value_t::String);
BOOST_CHECK(v1.is(toml::value_t::String));
BOOST_CHECK(v2.is(toml::value_t::String));
BOOST_CHECK(v3.is(toml::value_t::String));
BOOST_CHECK(v1.is<toml::String>());
BOOST_CHECK(v2.is<toml::String>());
BOOST_CHECK(v3.is<toml::String>());
BOOST_CHECK_EQUAL(v1.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_AUTO_TEST_CASE(test_value_local_date)
{
toml::value v1 = toml::to_toml(toml::local_date(2018, toml::month_t::Jan, 31));
BOOST_CHECK_EQUAL(v1.type(), toml::value_t::LocalDate);
BOOST_CHECK(v1.is(toml::value_t::LocalDate));
BOOST_CHECK(v1.is<toml::LocalDate>());
BOOST_CHECK_EQUAL(v1.cast<toml::value_t::LocalDate>(),
toml::local_date(2018, toml::month_t::Jan, 31));
}
BOOST_AUTO_TEST_CASE(test_value_local_time)
{
toml::value v1 = toml::to_toml(toml::local_time(12, 30, 45));
toml::value v2 = toml::to_toml(std::chrono::hours(12) + std::chrono::minutes(30) +
std::chrono::seconds(45));
BOOST_CHECK_EQUAL(v1.type(), toml::value_t::LocalTime);
BOOST_CHECK_EQUAL(v2.type(), toml::value_t::LocalTime);
BOOST_CHECK(v1.is(toml::value_t::LocalTime));
BOOST_CHECK(v2.is(toml::value_t::LocalTime));
BOOST_CHECK(v1.is<toml::LocalTime>());
BOOST_CHECK(v2.is<toml::LocalTime>());
BOOST_CHECK_EQUAL(v1.cast<toml::value_t::LocalTime>(),
toml::local_time(12, 30, 45));
BOOST_CHECK_EQUAL(v2.cast<toml::value_t::LocalTime>(),
toml::local_time(12, 30, 45));
BOOST_CHECK_EQUAL(v1.cast<toml::value_t::LocalTime>(),
v2.cast<toml::value_t::LocalTime>());
}
BOOST_AUTO_TEST_CASE(test_value_local_datetime)
{
toml::value v1 = toml::to_toml(toml::local_datetime(
toml::local_date(2018, toml::month_t::Jan, 31),
toml::local_time(12, 30, 45)
));
BOOST_CHECK_EQUAL(v1.type(), toml::value_t::LocalDatetime);
BOOST_CHECK(v1.is(toml::value_t::LocalDatetime));
BOOST_CHECK(v1.is<toml::LocalDatetime>());
BOOST_CHECK_EQUAL(v1.cast<toml::value_t::LocalDatetime>(),
toml::local_datetime(
toml::local_date(2018, toml::month_t::Jan, 31),
toml::local_time(12, 30, 45)));
}
BOOST_AUTO_TEST_CASE(test_value_offset_datetime)
{
toml::value v1 = toml::to_toml(toml::offset_datetime(
toml::local_date(2018, toml::month_t::Jan, 31),
toml::local_time(12, 30, 45),
toml::time_offset(9, 0)
));
BOOST_CHECK_EQUAL(v1.type(), toml::value_t::OffsetDatetime);
BOOST_CHECK(v1.is(toml::value_t::OffsetDatetime));
BOOST_CHECK(v1.is<toml::OffsetDatetime>());
BOOST_CHECK_EQUAL(v1.cast<toml::value_t::OffsetDatetime>(),
toml::offset_datetime(
toml::local_date(2018, toml::month_t::Jan, 31),
toml::local_time(12, 30, 45),
toml::time_offset(9, 0)
));
}
BOOST_AUTO_TEST_CASE(test_value_array)
{
std::vector<int> v{1,2,3,4,5};
toml::value v1 = toml::to_toml(v);
toml::value v2 = toml::to_toml(6,7,8,9,0);
BOOST_CHECK_EQUAL(v1.type(), toml::value_t::Array);
BOOST_CHECK(v1.is(toml::value_t::Array));
BOOST_CHECK(v1.is<toml::Array>());
BOOST_CHECK_EQUAL(v2.type(), toml::value_t::Array);
BOOST_CHECK(v2.is(toml::value_t::Array));
BOOST_CHECK(v2.is<toml::Array>());
BOOST_CHECK_EQUAL(v1.cast<toml::value_t::Array>().at(0).cast<toml::value_t::Integer>(), 1);
BOOST_CHECK_EQUAL(v1.cast<toml::value_t::Array>().at(1).cast<toml::value_t::Integer>(), 2);
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(4).cast<toml::value_t::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(1).cast<toml::value_t::Integer>(), 7);
BOOST_CHECK_EQUAL(v2.cast<toml::value_t::Array>().at(2).cast<toml::value_t::Integer>(), 8);
BOOST_CHECK_EQUAL(v2.cast<toml::value_t::Array>().at(3).cast<toml::value_t::Integer>(), 9);
BOOST_CHECK_EQUAL(v2.cast<toml::value_t::Array>().at(4).cast<toml::value_t::Integer>(), 0);
}
BOOST_AUTO_TEST_CASE(test_value_table)
{
toml::value v1 = toml::to_toml({{"foo", 42}, {"bar", 3.14}, {"baz", "qux"}});
BOOST_CHECK_EQUAL(v1.type(), toml::value_t::Table);
BOOST_CHECK(v1.is(toml::value_t::Table));
BOOST_CHECK(v1.is<toml::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("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");
}

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);
@@ -20,9 +25,13 @@ BOOST_AUTO_TEST_CASE(test_value_boolean)
BOOST_CHECK(v2.is(toml::value_t::Boolean)); BOOST_CHECK(v2.is(toml::value_t::Boolean));
BOOST_CHECK(v1.is<toml::Boolean>()); BOOST_CHECK(v1.is<toml::Boolean>());
BOOST_CHECK(v2.is<toml::Boolean>()); BOOST_CHECK(v2.is<toml::Boolean>());
BOOST_CHECK(v1.is_boolean());
BOOST_CHECK(v2.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(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;
@@ -33,9 +42,13 @@ BOOST_AUTO_TEST_CASE(test_value_boolean)
BOOST_CHECK(v2.is(toml::value_t::Boolean)); BOOST_CHECK(v2.is(toml::value_t::Boolean));
BOOST_CHECK(v1.is<toml::Boolean>()); BOOST_CHECK(v1.is<toml::Boolean>());
BOOST_CHECK(v2.is<toml::Boolean>()); BOOST_CHECK(v2.is<toml::Boolean>());
BOOST_CHECK(v1.is_boolean());
BOOST_CHECK(v2.is_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);
@@ -48,9 +61,13 @@ BOOST_AUTO_TEST_CASE(test_value_boolean)
BOOST_CHECK(v4.is(toml::value_t::Boolean)); BOOST_CHECK(v4.is(toml::value_t::Boolean));
BOOST_CHECK(v3.is<toml::Boolean>()); BOOST_CHECK(v3.is<toml::Boolean>());
BOOST_CHECK(v4.is<toml::Boolean>()); BOOST_CHECK(v4.is<toml::Boolean>());
BOOST_CHECK(v3.is_boolean());
BOOST_CHECK(v4.is_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));
@@ -61,9 +78,13 @@ BOOST_AUTO_TEST_CASE(test_value_boolean)
BOOST_CHECK(v6.is(toml::value_t::Boolean)); BOOST_CHECK(v6.is(toml::value_t::Boolean));
BOOST_CHECK(v5.is<toml::Boolean>()); BOOST_CHECK(v5.is<toml::Boolean>());
BOOST_CHECK(v6.is<toml::Boolean>()); BOOST_CHECK(v6.is<toml::Boolean>());
BOOST_CHECK(v3.is_boolean());
BOOST_CHECK(v4.is_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;
@@ -74,9 +95,13 @@ BOOST_AUTO_TEST_CASE(test_value_boolean)
BOOST_CHECK(v2.is(toml::value_t::Float)); BOOST_CHECK(v2.is(toml::value_t::Float));
BOOST_CHECK(v1.is<toml::Integer>()); BOOST_CHECK(v1.is<toml::Integer>());
BOOST_CHECK(v2.is<toml::Float>()); BOOST_CHECK(v2.is<toml::Float>());
BOOST_CHECK(v1.is_integer());
BOOST_CHECK(v2.is_float());
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)
@@ -90,9 +115,13 @@ BOOST_AUTO_TEST_CASE(test_value_integer)
BOOST_CHECK(v2.is(toml::value_t::Integer)); BOOST_CHECK(v2.is(toml::value_t::Integer));
BOOST_CHECK(v1.is<toml::Integer>()); BOOST_CHECK(v1.is<toml::Integer>());
BOOST_CHECK(v2.is<toml::Integer>()); BOOST_CHECK(v2.is<toml::Integer>());
BOOST_CHECK(v1.is_integer());
BOOST_CHECK(v2.is_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;
@@ -103,9 +132,13 @@ BOOST_AUTO_TEST_CASE(test_value_integer)
BOOST_CHECK(v2.is(toml::value_t::Integer)); BOOST_CHECK(v2.is(toml::value_t::Integer));
BOOST_CHECK(v1.is<toml::Integer>()); BOOST_CHECK(v1.is<toml::Integer>());
BOOST_CHECK(v2.is<toml::Integer>()); BOOST_CHECK(v2.is<toml::Integer>());
BOOST_CHECK(v1.is_integer());
BOOST_CHECK(v2.is_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);
@@ -118,9 +151,13 @@ BOOST_AUTO_TEST_CASE(test_value_integer)
BOOST_CHECK(v4.is(toml::value_t::Integer)); BOOST_CHECK(v4.is(toml::value_t::Integer));
BOOST_CHECK(v3.is<toml::Integer>()); BOOST_CHECK(v3.is<toml::Integer>());
BOOST_CHECK(v4.is<toml::Integer>()); BOOST_CHECK(v4.is<toml::Integer>());
BOOST_CHECK(v3.is_integer());
BOOST_CHECK(v4.is_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));
@@ -131,9 +168,13 @@ BOOST_AUTO_TEST_CASE(test_value_integer)
BOOST_CHECK(v6.is(toml::value_t::Integer)); BOOST_CHECK(v6.is(toml::value_t::Integer));
BOOST_CHECK(v5.is<toml::Integer>()); BOOST_CHECK(v5.is<toml::Integer>());
BOOST_CHECK(v6.is<toml::Integer>()); BOOST_CHECK(v6.is<toml::Integer>());
BOOST_CHECK(v5.is_integer());
BOOST_CHECK(v6.is_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;
@@ -144,9 +185,13 @@ BOOST_AUTO_TEST_CASE(test_value_integer)
BOOST_CHECK(v2.is(toml::value_t::Boolean)); BOOST_CHECK(v2.is(toml::value_t::Boolean));
BOOST_CHECK(v1.is<toml::Boolean>()); BOOST_CHECK(v1.is<toml::Boolean>());
BOOST_CHECK(v2.is<toml::Boolean>()); BOOST_CHECK(v2.is<toml::Boolean>());
BOOST_CHECK(v1.is_boolean());
BOOST_CHECK(v2.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(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)
@@ -160,9 +205,13 @@ BOOST_AUTO_TEST_CASE(test_value_float)
BOOST_CHECK(v2.is(toml::value_t::Float)); BOOST_CHECK(v2.is(toml::value_t::Float));
BOOST_CHECK(v1.is<toml::Float>()); BOOST_CHECK(v1.is<toml::Float>());
BOOST_CHECK(v2.is<toml::Float>()); BOOST_CHECK(v2.is<toml::Float>());
BOOST_CHECK(v1.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;
@@ -173,9 +222,13 @@ BOOST_AUTO_TEST_CASE(test_value_float)
BOOST_CHECK(v2.is(toml::value_t::Float)); BOOST_CHECK(v2.is(toml::value_t::Float));
BOOST_CHECK(v1.is<toml::Float>()); BOOST_CHECK(v1.is<toml::Float>());
BOOST_CHECK(v2.is<toml::Float>()); BOOST_CHECK(v2.is<toml::Float>());
BOOST_CHECK(v1.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);
@@ -188,9 +241,13 @@ BOOST_AUTO_TEST_CASE(test_value_float)
BOOST_CHECK(v4.is(toml::value_t::Float)); BOOST_CHECK(v4.is(toml::value_t::Float));
BOOST_CHECK(v3.is<toml::Float>()); BOOST_CHECK(v3.is<toml::Float>());
BOOST_CHECK(v4.is<toml::Float>()); BOOST_CHECK(v4.is<toml::Float>());
BOOST_CHECK(v3.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));
@@ -201,9 +258,13 @@ BOOST_AUTO_TEST_CASE(test_value_float)
BOOST_CHECK(v6.is(toml::value_t::Float)); BOOST_CHECK(v6.is(toml::value_t::Float));
BOOST_CHECK(v5.is<toml::Float>()); BOOST_CHECK(v5.is<toml::Float>());
BOOST_CHECK(v6.is<toml::Float>()); BOOST_CHECK(v6.is<toml::Float>());
BOOST_CHECK(v5.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;
@@ -214,9 +275,13 @@ BOOST_AUTO_TEST_CASE(test_value_float)
BOOST_CHECK(v2.is(toml::value_t::Boolean)); BOOST_CHECK(v2.is(toml::value_t::Boolean));
BOOST_CHECK(v1.is<toml::Boolean>()); BOOST_CHECK(v1.is<toml::Boolean>());
BOOST_CHECK(v2.is<toml::Boolean>()); BOOST_CHECK(v2.is<toml::Boolean>());
BOOST_CHECK(v1.is_boolean());
BOOST_CHECK(v2.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(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)
@@ -234,10 +299,17 @@ BOOST_AUTO_TEST_CASE(test_value_string)
BOOST_CHECK(v1.is<toml::String>()); BOOST_CHECK(v1.is<toml::String>());
BOOST_CHECK(v2.is<toml::String>()); BOOST_CHECK(v2.is<toml::String>());
BOOST_CHECK(v3.is<toml::String>()); BOOST_CHECK(v3.is<toml::String>());
BOOST_CHECK(v1.is_string());
BOOST_CHECK(v2.is_string());
BOOST_CHECK(v3.is_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";
@@ -249,13 +321,17 @@ BOOST_AUTO_TEST_CASE(test_value_string)
BOOST_CHECK(v1.is(toml::value_t::String)); BOOST_CHECK(v1.is(toml::value_t::String));
BOOST_CHECK(v2.is(toml::value_t::String)); BOOST_CHECK(v2.is(toml::value_t::String));
BOOST_CHECK(v3.is(toml::value_t::String)); BOOST_CHECK(v3.is(toml::value_t::String));
BOOST_CHECK(v1.is<toml::String>()); BOOST_CHECK(v1.is_string());
BOOST_CHECK(v2.is<toml::String>()); BOOST_CHECK(v2.is_string());
BOOST_CHECK(v3.is<toml::String>()); BOOST_CHECK(v3.is_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);
@@ -273,10 +349,17 @@ BOOST_AUTO_TEST_CASE(test_value_string)
BOOST_CHECK(v4.is<toml::String>()); BOOST_CHECK(v4.is<toml::String>());
BOOST_CHECK(v5.is<toml::String>()); BOOST_CHECK(v5.is<toml::String>());
BOOST_CHECK(v6.is<toml::String>()); BOOST_CHECK(v6.is<toml::String>());
BOOST_CHECK(v4.is_string());
BOOST_CHECK(v5.is_string());
BOOST_CHECK(v6.is_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';
@@ -291,10 +374,13 @@ BOOST_AUTO_TEST_CASE(test_value_string)
BOOST_CHECK(v4.is<toml::String>()); BOOST_CHECK(v4.is<toml::String>());
BOOST_CHECK(v5.is<toml::String>()); BOOST_CHECK(v5.is<toml::String>());
BOOST_CHECK(v6.is<toml::String>()); BOOST_CHECK(v6.is<toml::String>());
BOOST_CHECK(v4.is_string());
BOOST_CHECK(v5.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;
@@ -309,10 +395,35 @@ BOOST_AUTO_TEST_CASE(test_value_string)
BOOST_CHECK(v1.is<toml::Boolean>()); BOOST_CHECK(v1.is<toml::Boolean>());
BOOST_CHECK(v2.is<toml::Boolean>()); BOOST_CHECK(v2.is<toml::Boolean>());
BOOST_CHECK(v3.is<toml::Boolean>()); BOOST_CHECK(v3.is<toml::Boolean>());
BOOST_CHECK(v1.is_boolean());
BOOST_CHECK(v2.is_boolean());
BOOST_CHECK(v3.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(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)
@@ -322,18 +433,24 @@ BOOST_AUTO_TEST_CASE(test_value_local_date)
BOOST_CHECK_EQUAL(v1.type(), toml::value_t::LocalDate); BOOST_CHECK_EQUAL(v1.type(), toml::value_t::LocalDate);
BOOST_CHECK(v1.is(toml::value_t::LocalDate)); BOOST_CHECK(v1.is(toml::value_t::LocalDate));
BOOST_CHECK(v1.is<toml::LocalDate>()); BOOST_CHECK(v1.is<toml::LocalDate>());
BOOST_CHECK(v1.is_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);
BOOST_CHECK_EQUAL(v1.type(), toml::value_t::LocalDate); BOOST_CHECK_EQUAL(v1.type(), toml::value_t::LocalDate);
BOOST_CHECK(v1.is(toml::value_t::LocalDate)); BOOST_CHECK(v1.is(toml::value_t::LocalDate));
BOOST_CHECK(v1.is<toml::LocalDate>()); BOOST_CHECK(v1.is<toml::LocalDate>());
BOOST_CHECK(v1.is_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);
@@ -341,15 +458,20 @@ BOOST_AUTO_TEST_CASE(test_value_local_date)
BOOST_CHECK_EQUAL(v2.type(), toml::value_t::LocalDate); BOOST_CHECK_EQUAL(v2.type(), toml::value_t::LocalDate);
BOOST_CHECK(v2.is(toml::value_t::LocalDate)); BOOST_CHECK(v2.is(toml::value_t::LocalDate));
BOOST_CHECK(v2.is<toml::LocalDate>()); BOOST_CHECK(v2.is<toml::LocalDate>());
BOOST_CHECK(v2.is_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);
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_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)
@@ -364,21 +486,34 @@ BOOST_AUTO_TEST_CASE(test_value_local_time)
BOOST_CHECK(v2.is(toml::value_t::LocalTime)); BOOST_CHECK(v2.is(toml::value_t::LocalTime));
BOOST_CHECK(v1.is<toml::LocalTime>()); BOOST_CHECK(v1.is<toml::LocalTime>());
BOOST_CHECK(v2.is<toml::LocalTime>()); BOOST_CHECK(v2.is<toml::LocalTime>());
BOOST_CHECK(v1.is_local_time());
BOOST_CHECK(v2.is_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);
BOOST_CHECK_EQUAL(v1.type(), toml::value_t::LocalTime); BOOST_CHECK_EQUAL(v1.type(), toml::value_t::LocalTime);
BOOST_CHECK(v1.is(toml::value_t::LocalTime)); BOOST_CHECK(v1.is(toml::value_t::LocalTime));
BOOST_CHECK(v1.is<toml::LocalTime>()); BOOST_CHECK(v1.is<toml::LocalTime>());
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);
@@ -386,15 +521,20 @@ BOOST_AUTO_TEST_CASE(test_value_local_time)
BOOST_CHECK_EQUAL(v3.type(), toml::value_t::LocalTime); BOOST_CHECK_EQUAL(v3.type(), toml::value_t::LocalTime);
BOOST_CHECK(v3.is(toml::value_t::LocalTime)); BOOST_CHECK(v3.is(toml::value_t::LocalTime));
BOOST_CHECK(v3.is<toml::LocalTime>()); BOOST_CHECK(v3.is<toml::LocalTime>());
BOOST_CHECK(v3.is_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);
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_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)
@@ -407,11 +547,16 @@ BOOST_AUTO_TEST_CASE(test_value_local_datetime)
BOOST_CHECK_EQUAL(v1.type(), toml::value_t::LocalDatetime); BOOST_CHECK_EQUAL(v1.type(), toml::value_t::LocalDatetime);
BOOST_CHECK(v1.is(toml::value_t::LocalDatetime)); BOOST_CHECK(v1.is(toml::value_t::LocalDatetime));
BOOST_CHECK(v1.is<toml::LocalDatetime>()); BOOST_CHECK(v1.is<toml::LocalDatetime>());
BOOST_CHECK(v1.is_local_datetime());
BOOST_CHECK_EQUAL(v1.cast<toml::value_t::LocalDatetime>(), BOOST_CHECK_EQUAL(v1.cast<toml::value_t::LocalDatetime>(),
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),
@@ -420,11 +565,16 @@ BOOST_AUTO_TEST_CASE(test_value_local_datetime)
BOOST_CHECK_EQUAL(v1.type(), toml::value_t::LocalDatetime); BOOST_CHECK_EQUAL(v1.type(), toml::value_t::LocalDatetime);
BOOST_CHECK(v1.is(toml::value_t::LocalDatetime)); BOOST_CHECK(v1.is(toml::value_t::LocalDatetime));
BOOST_CHECK(v1.is<toml::LocalDatetime>()); BOOST_CHECK(v1.is<toml::LocalDatetime>());
BOOST_CHECK(v1.is_local_datetime());
BOOST_CHECK_EQUAL(v1.cast<toml::value_t::LocalDatetime>(), BOOST_CHECK_EQUAL(v1.cast<toml::value_t::LocalDatetime>(),
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);
@@ -432,17 +582,25 @@ BOOST_AUTO_TEST_CASE(test_value_local_datetime)
BOOST_CHECK_EQUAL(v2.type(), toml::value_t::LocalDatetime); BOOST_CHECK_EQUAL(v2.type(), toml::value_t::LocalDatetime);
BOOST_CHECK(v2.is(toml::value_t::LocalDatetime)); BOOST_CHECK(v2.is(toml::value_t::LocalDatetime));
BOOST_CHECK(v2.is<toml::LocalDatetime>()); BOOST_CHECK(v2.is<toml::LocalDatetime>());
BOOST_CHECK(v2.is_local_datetime());
BOOST_CHECK_EQUAL(v2.cast<toml::value_t::LocalDatetime>(), BOOST_CHECK_EQUAL(v2.cast<toml::value_t::LocalDatetime>(),
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);
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_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)
@@ -456,6 +614,7 @@ BOOST_AUTO_TEST_CASE(test_value_offset_datetime)
BOOST_CHECK_EQUAL(v1.type(), toml::value_t::OffsetDatetime); BOOST_CHECK_EQUAL(v1.type(), toml::value_t::OffsetDatetime);
BOOST_CHECK(v1.is(toml::value_t::OffsetDatetime)); BOOST_CHECK(v1.is(toml::value_t::OffsetDatetime));
BOOST_CHECK(v1.is<toml::OffsetDatetime>()); BOOST_CHECK(v1.is<toml::OffsetDatetime>());
BOOST_CHECK(v1.is_offset_datetime());
BOOST_CHECK_EQUAL(v1.cast<toml::value_t::OffsetDatetime>(), BOOST_CHECK_EQUAL(v1.cast<toml::value_t::OffsetDatetime>(),
toml::offset_datetime( toml::offset_datetime(
@@ -463,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),
@@ -472,12 +637,19 @@ BOOST_AUTO_TEST_CASE(test_value_offset_datetime)
BOOST_CHECK_EQUAL(v1.type(), toml::value_t::OffsetDatetime); BOOST_CHECK_EQUAL(v1.type(), toml::value_t::OffsetDatetime);
BOOST_CHECK(v1.is(toml::value_t::OffsetDatetime)); BOOST_CHECK(v1.is(toml::value_t::OffsetDatetime));
BOOST_CHECK(v1.is<toml::OffsetDatetime>()); BOOST_CHECK(v1.is<toml::OffsetDatetime>());
BOOST_CHECK(v1.is_offset_datetime());
BOOST_CHECK_EQUAL(v1.cast<toml::value_t::OffsetDatetime>(), BOOST_CHECK_EQUAL(v1.cast<toml::value_t::OffsetDatetime>(),
toml::offset_datetime( toml::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);
@@ -485,17 +657,26 @@ BOOST_AUTO_TEST_CASE(test_value_offset_datetime)
BOOST_CHECK_EQUAL(v2.type(), toml::value_t::OffsetDatetime); BOOST_CHECK_EQUAL(v2.type(), toml::value_t::OffsetDatetime);
BOOST_CHECK(v2.is(toml::value_t::OffsetDatetime)); BOOST_CHECK(v2.is(toml::value_t::OffsetDatetime));
BOOST_CHECK(v2.is<toml::OffsetDatetime>()); BOOST_CHECK(v2.is<toml::OffsetDatetime>());
BOOST_CHECK(v2.is_offset_datetime());
BOOST_CHECK_EQUAL(v2.cast<toml::value_t::OffsetDatetime>(), BOOST_CHECK_EQUAL(v2.cast<toml::value_t::OffsetDatetime>(),
toml::offset_datetime( toml::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_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)
@@ -507,16 +688,24 @@ BOOST_AUTO_TEST_CASE(test_value_array)
BOOST_CHECK_EQUAL(v1.type(), toml::value_t::Array); BOOST_CHECK_EQUAL(v1.type(), toml::value_t::Array);
BOOST_CHECK(v1.is(toml::value_t::Array)); BOOST_CHECK(v1.is(toml::value_t::Array));
BOOST_CHECK(v1.is<toml::Array>()); BOOST_CHECK(v1.is<toml::Array>());
BOOST_CHECK(v1.is_array());
BOOST_CHECK_EQUAL(v2.type(), toml::value_t::Array); BOOST_CHECK_EQUAL(v2.type(), toml::value_t::Array);
BOOST_CHECK(v2.is(toml::value_t::Array)); BOOST_CHECK(v2.is(toml::value_t::Array));
BOOST_CHECK(v2.is<toml::Array>()); BOOST_CHECK(v2.is<toml::Array>());
BOOST_CHECK(v2.is_array());
BOOST_CHECK_EQUAL(v1.cast<toml::value_t::Array>().at(0).cast<toml::value_t::Integer>(), 1); BOOST_CHECK_EQUAL(v1.cast<toml::value_t::Array>().at(0).cast<toml::value_t::Integer>(), 1);
BOOST_CHECK_EQUAL(v1.cast<toml::value_t::Array>().at(1).cast<toml::value_t::Integer>(), 2); BOOST_CHECK_EQUAL(v1.cast<toml::value_t::Array>().at(1).cast<toml::value_t::Integer>(), 2);
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);
@@ -530,22 +719,36 @@ BOOST_AUTO_TEST_CASE(test_value_array)
BOOST_CHECK_EQUAL(v1.type(), toml::value_t::Array); BOOST_CHECK_EQUAL(v1.type(), toml::value_t::Array);
BOOST_CHECK(v1.is(toml::value_t::Array)); BOOST_CHECK(v1.is(toml::value_t::Array));
BOOST_CHECK(v1.is<toml::Array>()); BOOST_CHECK(v1.is<toml::Array>());
BOOST_CHECK(v1.is_array());
BOOST_CHECK_EQUAL(v2.type(), toml::value_t::Array); BOOST_CHECK_EQUAL(v2.type(), toml::value_t::Array);
BOOST_CHECK(v2.is(toml::value_t::Array)); BOOST_CHECK(v2.is(toml::value_t::Array));
BOOST_CHECK(v2.is<toml::Array>()); BOOST_CHECK(v2.is<toml::Array>());
BOOST_CHECK(v2.is_array());
BOOST_CHECK_EQUAL(v1.cast<toml::value_t::Array>().at(0).cast<toml::value_t::Integer>(), 6); BOOST_CHECK_EQUAL(v1.cast<toml::value_t::Array>().at(0).cast<toml::value_t::Integer>(), 6);
BOOST_CHECK_EQUAL(v1.cast<toml::value_t::Array>().at(1).cast<toml::value_t::Integer>(), 7); BOOST_CHECK_EQUAL(v1.cast<toml::value_t::Array>().at(1).cast<toml::value_t::Integer>(), 7);
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);
@@ -553,18 +756,27 @@ BOOST_AUTO_TEST_CASE(test_value_array)
BOOST_CHECK_EQUAL(v3.type(), toml::value_t::Array); BOOST_CHECK_EQUAL(v3.type(), toml::value_t::Array);
BOOST_CHECK(v3.is(toml::value_t::Array)); BOOST_CHECK(v3.is(toml::value_t::Array));
BOOST_CHECK(v3.is<toml::Array>()); BOOST_CHECK(v3.is<toml::Array>());
BOOST_CHECK(v3.is_array());
BOOST_CHECK_EQUAL(v3.cast<toml::value_t::Array>().at(0).cast<toml::value_t::Integer>(), 6); BOOST_CHECK_EQUAL(v3.cast<toml::value_t::Array>().at(0).cast<toml::value_t::Integer>(), 6);
BOOST_CHECK_EQUAL(v3.cast<toml::value_t::Array>().at(1).cast<toml::value_t::Integer>(), 7); BOOST_CHECK_EQUAL(v3.cast<toml::value_t::Array>().at(1).cast<toml::value_t::Integer>(), 7);
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);
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_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)
@@ -574,20 +786,30 @@ BOOST_AUTO_TEST_CASE(test_value_table)
BOOST_CHECK_EQUAL(v1.type(), toml::value_t::Table); BOOST_CHECK_EQUAL(v1.type(), toml::value_t::Table);
BOOST_CHECK(v1.is(toml::value_t::Table)); BOOST_CHECK(v1.is(toml::value_t::Table));
BOOST_CHECK(v1.is<toml::Table>()); BOOST_CHECK(v1.is<toml::Table>());
BOOST_CHECK(v1.is_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"}};
BOOST_CHECK_EQUAL(v1.type(), toml::value_t::Table); BOOST_CHECK_EQUAL(v1.type(), toml::value_t::Table);
BOOST_CHECK(v1.is(toml::value_t::Table)); BOOST_CHECK(v1.is(toml::value_t::Table));
BOOST_CHECK(v1.is<toml::Table>()); BOOST_CHECK(v1.is<toml::Table>());
BOOST_CHECK(v1.is_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);
@@ -595,14 +817,28 @@ BOOST_AUTO_TEST_CASE(test_value_table)
BOOST_CHECK_EQUAL(v3.type(), toml::value_t::Table); BOOST_CHECK_EQUAL(v3.type(), toml::value_t::Table);
BOOST_CHECK(v3.is(toml::value_t::Table)); BOOST_CHECK(v3.is(toml::value_t::Table));
BOOST_CHECK(v3.is<toml::Table>()); BOOST_CHECK(v3.is<toml::Table>());
BOOST_CHECK(v3.is_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);
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_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)
{
toml::value v1;
BOOST_CHECK(v1.is_uninitialized());
BOOST_CHECK(v1.is(toml::value_t::Empty));
} }

View File

@@ -34,8 +34,8 @@
#endif #endif
#include "toml/parser.hpp" #include "toml/parser.hpp"
#include "toml/literal.hpp"
#include "toml/serializer.hpp" #include "toml/serializer.hpp"
#include "toml/to_toml.hpp"
#include "toml/from_toml.hpp" #include "toml/from_toml.hpp"
#include "toml/get.hpp" #include "toml/get.hpp"

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,7 +56,8 @@ 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>, std::string>
invoke(location<Cont>& loc, const bool msg = false)
{ {
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`.");
@@ -61,11 +67,15 @@ struct character
const char c = *(loc.iter()); const char c = *(loc.iter());
if(c != target) if(c != target)
{
if(msg)
{ {
return err(concat_to_string("expected '", show_char(target), return err(concat_to_string("expected '", show_char(target),
"' but got '", show_char(c), "'.")); "' but got '", show_char(c), "'."));
} }
++(loc.iter()); // update location return err("");
}
loc.advance(); // update location
return ok(region<Cont>(loc, first, loc.iter())); return ok(region<Cont>(loc, first, loc.iter()));
} }
@@ -86,7 +96,8 @@ 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>, std::string>
invoke(location<Cont>& loc, const bool msg = false)
{ {
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`.");
@@ -96,13 +107,17 @@ struct in_range
const char c = *(loc.iter()); const char c = *(loc.iter());
if(c < lower || upper < c) if(c < lower || upper < c)
{
if(msg)
{ {
return err(concat_to_string("expected character in range " return err(concat_to_string("expected character in range "
"[", show_char(lower), ", ", show_char(upper), "] but got ", "[", show_char(lower), ", ", show_char(upper), "] but got ",
"'", show_char(c), "'.")); "'", show_char(c), "'."));
} }
return err("");
}
++(loc.iter()); loc.advance();
return ok(region<Cont>(loc, first, loc.iter())); return ok(region<Cont>(loc, first, loc.iter()));
} }
@@ -120,7 +135,8 @@ 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>, std::string>
invoke(location<Cont>& loc, const bool msg = false)
{ {
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`.");
@@ -128,15 +144,18 @@ struct exclude
if(loc.iter() == loc.end()) {return err("not sufficient characters");} if(loc.iter() == loc.end()) {return err("not sufficient characters");}
auto first = loc.iter(); auto first = loc.iter();
auto rslt = Combinator::invoke(loc); auto rslt = Combinator::invoke(loc, msg);
if(rslt.is_ok()) if(rslt.is_ok())
{ {
loc.iter() = first; // rollback loc.reset(first);
return err(concat_to_string( if(msg)
"invalid pattern (", Combinator::pattern(), ") appeared ", {
rslt.unwrap().str())); return err(concat_to_string("invalid pattern (",
Combinator::pattern(), ") appeared ", rslt.unwrap().str()));
} }
loc.iter() = std::next(first); return err("");
}
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()));
} }
@@ -151,12 +170,13 @@ 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>, std::string>
invoke(location<Cont>& loc, const bool msg = false)
{ {
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`.");
const auto rslt = Combinator::invoke(loc); const auto rslt = Combinator::invoke(loc, msg);
if(rslt.is_ok()) if(rslt.is_ok())
{ {
return rslt; return rslt;
@@ -177,34 +197,36 @@ 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>, std::string>
invoke(location<Cont>& loc, const bool msg = false)
{ {
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`.");
const auto first = loc.iter(); const auto first = loc.iter();
const auto rslt = Head::invoke(loc); const auto rslt = Head::invoke(loc, msg);
if(rslt.is_err()) if(rslt.is_err())
{ {
loc.iter() = first; loc.reset(first);
return err(rslt.unwrap_err()); return err(rslt.unwrap_err());
} }
return sequence<Tail...>::invoke(loc, std::move(rslt.unwrap()), first); return sequence<Tail...>::invoke(loc, std::move(rslt.unwrap()), first, msg);
} }
// 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>, std::string>
invoke(location<Cont>& loc, region<Cont> reg, Iterator first) invoke(location<Cont>& loc, region<Cont> reg, Iterator first,
const bool msg = false)
{ {
const auto rslt = Head::invoke(loc); const auto rslt = Head::invoke(loc, msg);
if(rslt.is_err()) if(rslt.is_err())
{ {
loc.iter() = first; loc.reset(first);
return err(rslt.unwrap_err()); return err(rslt.unwrap_err());
} }
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, msg);
} }
static std::string pattern() static std::string pattern()
@@ -219,12 +241,13 @@ 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>, std::string>
invoke(location<Cont>& loc, region<Cont> reg, Iterator first) invoke(location<Cont>& loc, region<Cont> reg, Iterator first,
const bool msg = false)
{ {
const auto rslt = Head::invoke(loc); const auto rslt = Head::invoke(loc, msg);
if(rslt.is_err()) if(rslt.is_err())
{ {
loc.iter() = first; loc.reset(first);
return err(rslt.unwrap_err()); return err(rslt.unwrap_err());
} }
reg += rslt.unwrap(); // concat regions reg += rslt.unwrap(); // concat regions
@@ -240,14 +263,15 @@ 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>, std::string>
invoke(location<Cont>& loc, const bool msg = false)
{ {
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`.");
const auto rslt = Head::invoke(loc); const auto rslt = Head::invoke(loc, msg);
if(rslt.is_ok()) {return rslt;} if(rslt.is_ok()) {return rslt;}
return either<Tail...>::invoke(loc); return either<Tail...>::invoke(loc, msg);
} }
static std::string pattern() static std::string pattern()
@@ -259,11 +283,12 @@ 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>, std::string>
invoke(location<Cont>& loc, const bool msg = false)
{ {
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, msg);
} }
static std::string pattern() static std::string pattern()
{ {
@@ -282,16 +307,17 @@ 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>, std::string>
invoke(location<Cont>& loc, const bool msg = false)
{ {
region<Cont> retval(loc); region<Cont> retval(loc);
const auto first = loc.iter(); const auto first = loc.iter();
for(std::size_t i=0; i<N; ++i) for(std::size_t i=0; i<N; ++i)
{ {
auto rslt = T::invoke(loc); auto rslt = T::invoke(loc, msg);
if(rslt.is_err()) if(rslt.is_err())
{ {
loc.iter() = first; loc.reset(first);
return err(rslt.unwrap_err()); return err(rslt.unwrap_err());
} }
retval += rslt.unwrap(); retval += rslt.unwrap();
@@ -308,24 +334,25 @@ 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>, std::string>
invoke(location<Cont>& loc, const bool msg = false)
{ {
region<Cont> retval(loc); region<Cont> retval(loc);
const auto first = loc.iter(); const auto first = loc.iter();
for(std::size_t i=0; i<N; ++i) for(std::size_t i=0; i<N; ++i)
{ {
auto rslt = T::invoke(loc); auto rslt = T::invoke(loc, msg);
if(rslt.is_err()) if(rslt.is_err())
{ {
loc.iter() = first; loc.reset(first);
return err(rslt.unwrap_err()); return err(rslt.unwrap_err());
} }
retval += rslt.unwrap(); retval += rslt.unwrap();
} }
while(true) while(true)
{ {
auto rslt = T::invoke(loc); auto rslt = T::invoke(loc, msg);
if(rslt.is_err()) if(rslt.is_err())
{ {
return ok(std::move(retval)); return ok(std::move(retval));
@@ -343,12 +370,13 @@ 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>, std::string>
invoke(location<Cont>& loc, const bool msg = false)
{ {
region<Cont> retval(loc); region<Cont> retval(loc);
while(true) while(true)
{ {
auto rslt = T::invoke(loc); auto rslt = T::invoke(loc, msg);
if(rslt.is_err()) if(rslt.is_err())
{ {
return ok(std::move(retval)); return ok(std::move(retval));

View File

@@ -1,7 +1,7 @@
// Copyright Toru Niina 2017. // Copyright Toru Niina 2017.
// Distributed under the MIT License. // Distributed under the MIT License.
#ifndef TOML11_DATETIME #ifndef TOML11_DATETIME_HPP
#define TOML11_DATETIME #define TOML11_DATETIME_HPP
#include <chrono> #include <chrono>
#include <tuple> #include <tuple>
#include <array> #include <array>
@@ -25,16 +25,18 @@ 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);
@@ -332,17 +334,10 @@ operator<<(std::basic_ostream<charT, traits>& os, const time_offset& offset)
os << 'Z'; os << 'Z';
return os; return os;
} }
if(static_cast<int>(offset.hour) * static_cast<int>(offset.minute) < 0) int minute = static_cast<int>(offset.hour) * 60 + offset.minute;
{ if(minute < 0){os << '-'; minute = std::abs(minute);} else {os << '+';}
const int min = static_cast<int>(offset.hour) * 60 + offset.minute; os << std::setfill('0') << std::setw(2) << minute / 60 << ':';
if(min < 0){os << '-';} else {os << '+';} os << std::setfill('0') << std::setw(2) << minute % 60;
os << std::setfill('0') << std::setw(2) << min / 60 << ':';
os << std::setfill('0') << std::setw(2) << min % 60;
return os;
}
if(offset.hour < 0){os << '-';} else {os << '+';}
os << std::setfill('0') << std::setw(2) << static_cast<int>(offset.hour) << ':';
os << std::setfill('0') << std::setw(2) << static_cast<int>(offset.minute);
return os; return os;
} }
@@ -367,12 +362,12 @@ struct local_datetime
// 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(&time));
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

@@ -1,7 +1,7 @@
// Copyright Toru Niina 2017. // Copyright Toru Niina 2017.
// Distributed under the MIT License. // Distributed under the MIT License.
#ifndef TOML11_EXCEPTION #ifndef TOML11_EXCEPTION_HPP
#define TOML11_EXCEPTION #define TOML11_EXCEPTION_HPP
#include <stdexcept> #include <stdexcept>
#include <string> #include <string>

20
toml/from.hpp Normal file
View File

@@ -0,0 +1,20 @@
// Copyright Toru Niina 2019.
// Distributed under the MIT License.
#ifndef TOML11_FROM_HPP
#define TOML11_FROM_HPP
#include "traits.hpp"
namespace toml
{
template<typename T>
struct from;
// {
// static T from_toml(const toml::value& v)
// {
// // User-defined conversions ...
// }
// };
} // toml
#endif // TOML11_FROM_HPP

View File

@@ -1,7 +1,7 @@
// Copyright Toru Niina 2017. // Copyright Toru Niina 2017.
// Distributed under the MIT License. // Distributed under the MIT License.
#ifndef TOML11_FROM_TOML #ifndef TOML11_FROM_TOML_HPP
#define TOML11_FROM_TOML #define TOML11_FROM_TOML_HPP
#include "get.hpp" #include "get.hpp"
namespace toml namespace toml
@@ -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

@@ -1,7 +1,8 @@
// Copyright Toru Niina 2017. // Copyright Toru Niina 2017.
// Distributed under the MIT License. // Distributed under the MIT License.
#ifndef TOML11_GET #ifndef TOML11_GET_HPP
#define TOML11_GET #define TOML11_GET_HPP
#include "from.hpp"
#include "result.hpp" #include "result.hpp"
#include "value.hpp" #include "value.hpp"
#include <algorithm> #include <algorithm>
@@ -107,12 +108,24 @@ 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.
template<typename T, typename std::enable_if< template<typename T, typename std::enable_if<
detail::is_chrono_duration<T>::value, std::nullptr_t>::type = nullptr> detail::is_chrono_duration<T>::value, std::nullptr_t>::type = nullptr>
inline T get(value& v) inline T get(const value& v)
{ {
return std::chrono::duration_cast<T>( return std::chrono::duration_cast<T>(
std::chrono::nanoseconds(v.cast<value_t::LocalTime>())); std::chrono::nanoseconds(v.cast<value_t::LocalTime>()));
@@ -124,7 +137,7 @@ inline T get(value& v)
template<typename T, typename std::enable_if< template<typename T, typename std::enable_if<
std::is_same<std::chrono::system_clock::time_point, T>::value, std::is_same<std::chrono::system_clock::time_point, T>::value,
std::nullptr_t>::type = nullptr> std::nullptr_t>::type = nullptr>
inline T get(value& v) inline T get(const value& v)
{ {
switch(v.type()) switch(v.type())
{ {
@@ -173,6 +186,20 @@ template<typename T, typename std::enable_if<detail::conjunction<
>::value, std::nullptr_t>::type = nullptr> >::value, std::nullptr_t>::type = nullptr>
T get(const toml::value& v); T get(const toml::value& v);
template<typename T, typename std::enable_if<detail::conjunction<
detail::negation<detail::is_exact_toml_type<T>>, // not a toml::value
detail::has_from_toml_method<T>, // but has from_toml(toml::value) memfn
std::is_default_constructible<T> // and default constructible
>::value, std::nullptr_t>::type = nullptr>
T get(const toml::value& v);
template<typename T, typename std::enable_if<detail::conjunction<
detail::negation<detail::is_exact_toml_type<T>> // not a toml::value
>::value, std::nullptr_t>::type = nullptr,
std::size_t = sizeof(::toml::from<T>) // and has from<T> specialization
>
T get(const toml::value& v);
// ============================================================================ // ============================================================================
// array-like types; most likely STL container, like std::vector, etc. // array-like types; most likely STL container, like std::vector, etc.
@@ -297,6 +324,29 @@ T get(const toml::value& v)
return map; return map;
} }
// ============================================================================
// user-defined, but compatible types.
template<typename T, typename std::enable_if<detail::conjunction<
detail::negation<detail::is_exact_toml_type<T>>, // not a toml::value
detail::has_from_toml_method<T>, // but has from_toml(toml::value) memfn
std::is_default_constructible<T> // and default constructible
>::value, std::nullptr_t>::type>
T get(const toml::value& v)
{
T ud;
ud.from_toml(v);
return ud;
}
template<typename T, typename std::enable_if<detail::conjunction<
detail::negation<detail::is_exact_toml_type<T>> // not a toml::value
>::value, std::nullptr_t>::type, std::size_t> // and has from<T>
T get(const toml::value& v)
{
return ::toml::from<T>::from_toml(v);
}
// ============================================================================ // ============================================================================
// find and get // find and get
@@ -382,13 +432,14 @@ find(toml::value&& v, const toml::key& ky)
// ============================================================================ // ============================================================================
// get_or // get_or(value, fallback)
template<typename T> // ----------------------------------------------------------------------------
decltype(::toml::get<typename std::remove_cv< // specialization for the exact toml types (return type becomes lvalue ref)
typename std::remove_reference<T>::type>::type>(
std::declval<const toml::value&>())) template<typename T, typename std::enable_if<
get_or(const toml::value& v, T&& opt) detail::is_exact_toml_type<T>::value, std::nullptr_t>::type = nullptr>
T const& get_or(const toml::value& v, const T& opt)
{ {
try try
{ {
@@ -397,14 +448,12 @@ get_or(const toml::value& v, T&& opt)
} }
catch(...) catch(...)
{ {
return std::forward<T>(opt); return opt;
} }
} }
template<typename T> template<typename T, typename std::enable_if<
decltype(::toml::get<typename std::remove_cv< detail::is_exact_toml_type<T>::value, std::nullptr_t>::type = nullptr>
typename std::remove_reference<T>::type>::type>( T& get_or(toml::value& v, T& opt)
std::declval<toml::value&>()))
get_or(toml::value& v, T&& opt)
{ {
try try
{ {
@@ -413,75 +462,270 @@ get_or(toml::value& v, T&& opt)
} }
catch(...) catch(...)
{ {
return std::forward<T>(opt); return opt;
} }
} }
template<typename T> template<typename T, typename std::enable_if<
decltype(::toml::get<typename std::remove_cv< detail::is_exact_toml_type<T>::value, std::nullptr_t>::type = nullptr>
typename std::remove_reference<T>::type>::type>( T&& get_or(toml::value&& v, T&& opt)
std::declval<toml::value&&>()))
get_or(toml::value&& v, T&& opt)
{ {
try try
{ {
return get<typename std::remove_cv< return get<typename std::remove_cv<
typename std::remove_reference<T>::type>::type>(std::move(v)); typename std::remove_reference<T>::type>::type>(v);
} }
catch(...) catch(...)
{ {
return std::forward<T>(opt); return opt;
} }
} }
// ----------------------------------------------------------------------------
// specialization for std::string (return type becomes lvalue ref)
template<typename T> template<typename T, typename std::enable_if<
auto get_or(const toml::table& tab, const toml::key& ky, T&& opt) std::is_same<T, std::string>::value, std::nullptr_t>::type = nullptr>
-> decltype(get_or(std::declval<value const&>(), std::forward<T>(opt))) std::string const& get_or(const toml::value& v, const T& opt)
{ {
if(tab.count(ky) == 0) {return std::forward<T>(opt);} try
return ::toml::get_or(tab.at(ky), std::forward<T>(opt)); {
return get<std::string>(v);
} }
template<typename T> catch(...)
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 std::forward<T>(opt);} return opt;
return ::toml::get_or(tab[ky], std::forward<T>(opt));
} }
template<typename T> }
auto get_or(toml::table&& tab, const toml::key& ky, T&& opt) template<typename T, typename std::enable_if<
-> decltype(get_or(std::declval<value&&>(), std::forward<T>(opt))) std::is_same<T, std::string>::value, std::nullptr_t>::type = nullptr>
std::string& get_or(toml::value& v, T& opt)
{ {
if(tab.count(ky) == 0) {return std::forward<T>(opt);} try
return ::toml::get_or(std::move(tab[ky]), std::forward<T>(opt)); {
return get<std::string>(v);
}
catch(...)
{
return opt;
}
}
template<typename T, typename std::enable_if<
std::is_same<T, std::string>::value, std::nullptr_t>::type = nullptr>
std::string get_or(toml::value&& v, T&& opt)
{
try
{
return get<std::string>(v);
}
catch(...)
{
return opt;
}
}
template<typename T, typename std::enable_if<
detail::is_string_literal<typename std::remove_reference<T>::type>::value,
std::nullptr_t>::type = nullptr>
std::string get_or(const toml::value& v, T&& opt)
{
try
{
return get<std::string>(v);
}
catch(...)
{
return std::string(opt);
}
} }
template<typename T> // ----------------------------------------------------------------------------
auto get_or(const toml::value& v, const toml::key& ky, T&& opt) // others (require type conversion and return type cannot be lvalue reference)
-> decltype(get_or(std::declval<value const&>(), std::forward<T>(opt)))
template<typename T, typename std::enable_if<detail::conjunction<
detail::negation<detail::is_exact_toml_type<T>>,
detail::negation<std::is_same<T, std::string>>,
detail::negation<detail::is_string_literal<typename std::remove_reference<T>::type>>
>::value, std::nullptr_t>::type = nullptr>
T get_or(const toml::value& v, T&& opt)
{ {
if(v.type() != toml::value_t::Table){return std::forward<T>(opt);} try
{
return get<typename std::remove_cv<
typename std::remove_reference<T>::type>::type>(v);
}
catch(...)
{
return opt;
}
}
// ===========================================================================
// find_or(value, key, fallback)
// ---------------------------------------------------------------------------
// exact types (return type can be a reference)
template<typename T, typename std::enable_if<
detail::is_exact_toml_type<T>::value, std::nullptr_t>::type = nullptr>
T const& find_or(const toml::value& v, const toml::key& ky, const T& opt)
{
if(!v.is_table()) {return opt;}
const auto& tab = toml::get<toml::table>(v); const auto& tab = toml::get<toml::table>(v);
if(tab.count(ky) == 0) {return std::forward<T>(opt);} if(tab.count(ky) == 0) {return opt;}
return ::toml::get_or(tab.at(ky), std::forward<T>(opt)); return get_or(tab.at(ky), opt);
} }
template<typename T>
auto get_or(toml::value& v, const toml::key& ky, T&& opt) template<typename T, typename std::enable_if<
-> decltype(get_or(std::declval<value&>(), std::forward<T>(opt))) detail::is_exact_toml_type<T>::value, std::nullptr_t>::type = nullptr>
T& find_or(toml::value& v, const toml::key& ky, T& opt)
{ {
if(v.type() != toml::value_t::Table){return std::forward<T>(opt);} if(!v.is_table()) {return opt;}
auto& tab = toml::get<toml::table>(v); auto& tab = toml::get<toml::table>(v);
if(tab.count(ky) == 0) {return std::forward<T>(opt);} if(tab.count(ky) == 0) {return opt;}
return ::toml::get_or(tab[ky], std::forward<T>(opt)); return get_or(tab[ky], opt);
} }
template<typename T>
auto get_or(toml::value&& v, const toml::key& ky, T&& opt) template<typename T, typename std::enable_if<
-> decltype(get_or(std::declval<value&&>(), std::forward<T>(opt))) detail::is_exact_toml_type<T>::value, std::nullptr_t>::type = nullptr>
T&& find_or(toml::value&& v, const toml::key& ky, T&& opt)
{ {
if(v.type() != toml::value_t::Table){return std::forward<T>(opt);} if(!v.is_table()) {return opt;}
auto tab = toml::get<toml::table>(std::move(v)); auto tab = toml::get<toml::table>(std::move(v));
if(tab.count(ky) == 0) {return std::forward<T>(opt);} if(tab.count(ky) == 0) {return opt;}
return ::toml::get_or(std::move(tab[ky]), std::forward<T>(opt)); return get_or(std::move(tab[ky]), std::forward<T>(opt));
}
// ---------------------------------------------------------------------------
// std::string (return type can be a reference)
template<typename T, typename std::enable_if<
std::is_same<T, std::string>::value, std::nullptr_t>::type = nullptr>
std::string const& find_or(const toml::value& v, const toml::key& ky, const T& opt)
{
if(!v.is_table()) {return opt;}
const auto& tab = toml::get<toml::table>(v);
if(tab.count(ky) == 0) {return opt;}
return get_or(tab.at(ky), opt);
}
template<typename T, typename std::enable_if<
std::is_same<T, std::string>::value, std::nullptr_t>::type = nullptr>
std::string& find_or(toml::value& v, const toml::key& ky, T& opt)
{
if(!v.is_table()) {return opt;}
auto& tab = toml::get<toml::table>(v);
if(tab.count(ky) == 0) {return opt;}
return get_or(tab[ky], opt);
}
template<typename T, typename std::enable_if<
std::is_same<T, std::string>::value, std::nullptr_t>::type = nullptr>
std::string find_or(toml::value&& v, const toml::key& ky, 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 get_or(std::move(tab[ky]), std::forward<T>(opt));
}
// ---------------------------------------------------------------------------
// string literal (deduced as std::string)
template<typename T, typename std::enable_if<
detail::is_string_literal<typename std::remove_reference<T>::type>::value,
std::nullptr_t>::type = nullptr>
std::string find_or(const toml::value& v, const toml::key& ky, T&& opt)
{
if(!v.is_table()) {return opt;}
const auto& tab = toml::get<toml::table>(v);
if(tab.count(ky) == 0) {return std::string(opt);}
return get_or(tab.at(ky), std::forward<T>(opt));
}
// ---------------------------------------------------------------------------
// others (require type conversion and return type cannot be lvalue reference)
template<typename T, typename std::enable_if<detail::conjunction<
detail::negation<detail::is_exact_toml_type<T>>,
detail::negation<std::is_same<T, std::string>>,
detail::negation<detail::is_string_literal<typename std::remove_reference<T>::type>>
>::value, std::nullptr_t>::type = nullptr>
T find_or(const toml::value& v, const toml::key& ky, T&& opt)
{
if(!v.is_table()) {return opt;}
const auto& tab = toml::get<toml::table>(v);
if(tab.count(ky) == 0) {return opt;}
return get_or(tab.at(ky), std::forward<T>(opt));
}
// ===========================================================================
// find_or(table, key, opt)
// ---------------------------------------------------------------------------
// exact types (return type can be a reference)
template<typename T, typename std::enable_if<
detail::is_exact_toml_type<T>::value, std::nullptr_t>::type = nullptr>
T const& find_or(const toml::table& tab, const toml::key& ky, const T& opt)
{
if(tab.count(ky) == 0) {return opt;}
return get_or(tab.at(ky), opt);
}
template<typename T, typename std::enable_if<
detail::is_exact_toml_type<T>::value, std::nullptr_t>::type = nullptr>
T& find_or(toml::table& tab, const toml::key& ky, T& opt)
{
if(tab.count(ky) == 0) {return opt;}
return get_or(tab[ky], opt);
}
template<typename T, typename std::enable_if<
detail::is_exact_toml_type<T>::value, std::nullptr_t>::type = nullptr>
T&& find_or(toml::table&& tab, const toml::key& ky, T&& opt)
{
if(tab.count(ky) == 0) {return opt;}
return get_or(std::move(tab[ky]), std::forward<T>(opt));
}
// ---------------------------------------------------------------------------
// std::string (return type can be a reference)
template<typename T, typename std::enable_if<
std::is_same<T, std::string>::value, std::nullptr_t>::type = nullptr>
std::string const& find_or(const toml::table& tab, const toml::key& ky, const T& opt)
{
if(tab.count(ky) == 0) {return opt;}
return get_or(tab.at(ky), opt);
}
template<typename T, typename std::enable_if<
std::is_same<T, std::string>::value, std::nullptr_t>::type = nullptr>
std::string& find_or(toml::table& tab, const toml::key& ky, T& opt)
{
if(tab.count(ky) == 0) {return opt;}
return get_or(tab[ky], opt);
}
template<typename T, typename std::enable_if<
std::is_same<T, std::string>::value, std::nullptr_t>::type = nullptr>
std::string find_or(toml::table&& tab, const toml::key& ky, T&& opt)
{
if(tab.count(ky) == 0) {return opt;}
return get_or(std::move(tab[ky]), std::forward<T>(opt));
}
// ---------------------------------------------------------------------------
// string literal (deduced as std::string)
template<typename T, typename std::enable_if<
detail::is_string_literal<typename std::remove_reference<T>::type>::value,
std::nullptr_t>::type = nullptr>
std::string find_or(const toml::table& tab, const toml::key& ky, T&& opt)
{
if(tab.count(ky) == 0) {return std::string(opt);}
return get_or(tab.at(ky), std::forward<T>(opt));
}
// ---------------------------------------------------------------------------
// others (require type conversion and return type cannot be lvalue reference)
template<typename T, typename std::enable_if<detail::conjunction<
detail::negation<detail::is_exact_toml_type<T>>,
detail::negation<std::is_same<T, std::string>>,
detail::negation<detail::is_string_literal<typename std::remove_reference<T>::type>>
>::value, std::nullptr_t>::type = nullptr>
T find_or(const toml::table& tab, const toml::key& ky, T&& opt)
{
if(tab.count(ky) == 0) {return opt;}
return get_or(tab.at(ky), std::forward<T>(opt));
} }
// ============================================================================ // ============================================================================

20
toml/into.hpp Normal file
View File

@@ -0,0 +1,20 @@
// Copyright Toru Niina 2019.
// Distributed under the MIT License.
#ifndef TOML11_INTO_HPP
#define TOML11_INTO_HPP
#include "traits.hpp"
namespace toml
{
template<typename T>
struct into;
// {
// static toml::value into_toml(const T& user_defined_type)
// {
// // User-defined conversions ...
// }
// };
} // toml
#endif // TOML11_INTO_HPP

91
toml/literal.hpp Normal file
View File

@@ -0,0 +1,91 @@
// Copyright Toru Niina 2019.
// Distributed under the MIT License.
#ifndef TOML11_LITERAL_HPP
#define TOML11_LITERAL_HPP
#include "parser.hpp"
namespace toml
{
inline namespace literals
{
inline namespace toml_literals
{
inline ::toml::value operator""_toml(const char* str, std::size_t len)
{
::toml::detail::location<std::vector<char>>
loc(/* filename = */ std::string("TOML literal encoded in a C++ code"),
/* contents = */ std::vector<char>(str, str + len));
// if there are some comments or empty lines, skip them.
using skip_line = ::toml::detail::repeat<toml::detail::sequence<
::toml::detail::maybe<::toml::detail::lex_ws>,
::toml::detail::maybe<::toml::detail::lex_comment>,
::toml::detail::lex_newline
>, ::toml::detail::at_least<1>>;
skip_line::invoke(loc);
// if there are some whitespaces before a value, skip them.
using skip_ws = ::toml::detail::repeat<
::toml::detail::lex_ws, ::toml::detail::at_least<1>>;
skip_ws::invoke(loc);
// to distinguish arrays and tables, first check it is a table or not.
//
// "[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)
{
if(auto data = ::toml::detail::parse_value(loc))
{
return data.unwrap();
}
}
// 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))
{
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()),
::toml::detail::region<std::vector<char>>(std::move(loc)));
}
else // none of them.
{
throw ::toml::syntax_error(data.unwrap_err());
}
}
} // toml_literals
} // literals
} // toml
#endif//TOML11_LITERAL_HPP

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"}}));
} }
@@ -250,11 +268,11 @@ std::string read_utf8_codepoint(const region<Container>& reg,
{ {
if(0xD800 <= codepoint && codepoint <= 0xDFFF) if(0xD800 <= codepoint && codepoint <= 0xDFFF)
{ {
std::cerr << format_underline("[warning] " throw syntax_error(format_underline("[error] "
"toml::read_utf8_codepoint: codepoints in the range " "toml::read_utf8_codepoint: codepoints in the range "
"[0xD800, 0xDFFF] are not valid UTF-8.", {{ "[0xD800, 0xDFFF] are not valid UTF-8.", {{
std::addressof(loc), "not a valid UTF-8 codepoint" std::addressof(loc), "not a valid UTF-8 codepoint"
}}) << std::endl; }}));
} }
assert(codepoint < 0xD800 || 0xDFFF < codepoint); assert(codepoint < 0xD800 || 0xDFFF < codepoint);
// 1110yyyy 10yxxxxx 10xxxxxx // 1110yyyy 10yxxxxx 10xxxxxx
@@ -262,15 +280,8 @@ std::string read_utf8_codepoint(const region<Container>& reg,
character += static_cast<unsigned char>(0x80|(codepoint >> 6 & 0x3F)); character += static_cast<unsigned char>(0x80|(codepoint >> 6 & 0x3F));
character += static_cast<unsigned char>(0x80|(codepoint & 0x3F)); character += static_cast<unsigned char>(0x80|(codepoint & 0x3F));
} }
else if(codepoint < 0x200000) // U+010000 ... U+1FFFFF else if(codepoint < 0x110000) // U+010000 ... U+10FFFF
{ {
if(0x10FFFF < codepoint) // out of Unicode region
{
std::cerr << format_underline("[error] "
"toml::read_utf8_codepoint: input codepoint is too large to "
"decode as a unicode character.", {{std::addressof(loc),
"should be in [0x00..0x10FFFF]"}}) << std::endl;
}
// 11110yyy 10yyxxxx 10xxxxxx 10xxxxxx // 11110yyy 10yyxxxx 10xxxxxx 10xxxxxx
character += static_cast<unsigned char>(0xF0| codepoint >> 18); character += static_cast<unsigned char>(0xF0| codepoint >> 18);
character += static_cast<unsigned char>(0x80|(codepoint >> 12 & 0x3F)); character += static_cast<unsigned char>(0x80|(codepoint >> 12 & 0x3F));
@@ -279,9 +290,9 @@ std::string read_utf8_codepoint(const region<Container>& reg,
} }
else // out of UTF-8 region else // out of UTF-8 region
{ {
throw std::range_error(format_underline(concat_to_string("[error] " throw syntax_error(format_underline("[error] toml::read_utf8_codepoint:"
"input codepoint (", str, ") is too large to encode as utf-8."), " input codepoint is too large.",
{{std::addressof(reg), "should be in [0x00..0x10FFFF]"}})); {{std::addressof(loc), "should be in [0x00..0x10FFFF]"}}));
} }
return character; return character;
} }
@@ -295,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))
@@ -315,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':
@@ -328,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"}}));
} }
} }
} }
@@ -338,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);
} }
@@ -350,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());
@@ -394,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"}}));
} }
} }
@@ -407,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)
@@ -443,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"}}));
} }
} }
@@ -482,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"}}));
} }
} }
@@ -519,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"}}));
} }
} }
@@ -528,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"}}));
} }
@@ -555,7 +594,7 @@ parse_local_date(location<Container>& loc)
"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), msg}}));
} }
++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() != '-')
{ {
@@ -566,7 +605,7 @@ parse_local_date(location<Container>& loc)
"toml::parse_local_date: invalid month format", "toml::parse_local_date: invalid month format",
{{std::addressof(inner_loc), msg}})); {{std::addressof(inner_loc), msg}}));
} }
++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)
{ {
@@ -583,7 +622,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"}}));
} }
@@ -608,7 +647,7 @@ parse_local_time(location<Container>& loc)
"toml::parse_local_time: invalid year format", "toml::parse_local_time: invalid year format",
{{std::addressof(inner_loc), msg}})); {{std::addressof(inner_loc), msg}}));
} }
++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() != ':')
{ {
@@ -619,7 +658,7 @@ parse_local_time(location<Container>& loc)
"toml::parse_local_time: invalid month format", "toml::parse_local_time: invalid month format",
{{std::addressof(inner_loc), msg}})); {{std::addressof(inner_loc), msg}}));
} }
++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)
{ {
@@ -668,7 +707,7 @@ parse_local_time(location<Container>& loc)
} }
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"}}));
} }
@@ -692,13 +731,14 @@ parse_local_datetime(location<Container>& loc)
"toml::parse_local_datetime: invalid datetime format", "toml::parse_local_datetime: invalid datetime format",
{{std::addressof(inner_loc), msg}})); {{std::addressof(inner_loc), msg}}));
} }
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)
{ {
@@ -712,7 +752,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"}}));
} }
@@ -762,9 +802,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"}}));
} }
} }
@@ -823,7 +863,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
{ {
@@ -834,7 +874,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))
@@ -863,7 +903,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>;
@@ -875,7 +915,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())));
} }
@@ -885,7 +925,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.", {
@@ -905,7 +945,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", {
@@ -921,14 +961,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", {
@@ -938,7 +978,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"}}));
@@ -957,7 +997,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 '='"}});
@@ -986,7 +1026,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));
} }
@@ -995,11 +1035,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"}});
@@ -1008,7 +1048,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()),
@@ -1035,6 +1075,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`.
@@ -1325,14 +1366,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())));
} }
@@ -1361,24 +1402,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)
{ {
@@ -1388,32 +1475,28 @@ 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)));}
switch(guess_value_type(loc))
{
case value_t::Boolean : {return parse_boolean(loc); }
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: " const auto msg = format_underline("[error] toml::parse_value: "
"unknown token appeared", {{std::addressof(loc), "unknown"}}); "unknown token appeared", {{std::addressof(loc), "unknown"}});
loc.iter() = first; loc.reset(first);
return err(msg); return err(msg);
} }
}
}
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>
@@ -1468,7 +1551,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"}}));
} }
} }
@@ -1476,7 +1560,7 @@ template<typename Container>
result<std::pair<std::vector<key>, region<Container>>, std::string> result<std::pair<std::vector<key>, region<Container>>, std::string>
parse_array_table_key(location<Container>& loc) parse_array_table_key(location<Container>& loc)
{ {
if(auto token = lex_array_table::invoke(loc)) if(auto token = lex_array_table::invoke(loc, true))
{ {
location<std::string> inner_loc(loc.name(), token.unwrap().str()); location<std::string> inner_loc(loc.name(), token.unwrap().str());
@@ -1521,7 +1605,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"}}));
} }
} }
@@ -1547,12 +1632,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);
} }
@@ -1592,7 +1677,7 @@ result<table, std::string> parse_ml_table(location<Container>& loc)
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(before);
return err(msg); return err(msg);
} }
@@ -1695,7 +1780,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

@@ -1,7 +1,7 @@
// Copyright Toru Niina 2017. // Copyright Toru Niina 2017.
// Distributed under the MIT License. // Distributed under the MIT License.
#ifndef TOML11_REGION_H #ifndef TOML11_REGION_HPP
#define TOML11_REGION_H #define TOML11_REGION_HPP
#include "exception.hpp" #include "exception.hpp"
#include <memory> #include <memory>
#include <vector> #include <vector>
@@ -15,7 +15,7 @@ namespace toml
namespace detail namespace detail
{ {
// helper function to avoid std::string(0, 'c') // helper function to avoid std::string(0, 'c') or std::string(iter, iter)
template<typename Iterator> template<typename Iterator>
std::string make_string(Iterator first, Iterator last) std::string make_string(Iterator first, Iterator last)
{ {
@@ -28,9 +28,7 @@ inline std::string make_string(std::size_t len, char c)
return std::string(len, c); return std::string(len, c);
} }
// region in a container, normally in a file content. // region_base is a base class of location and region that are defined below.
// shared_ptr points the resource that the iter points.
// combinators returns this.
// it will be used to generate better error messages. // it will be used to generate better error messages.
struct region_base struct region_base
{ {
@@ -48,25 +46,40 @@ struct region_base
virtual std::string line() const {return std::string("unknown line");} virtual std::string line() const {return std::string("unknown line");}
virtual std::string line_num() const {return std::string("?");} virtual std::string line_num() const {return std::string("?");}
virtual std::size_t before() const noexcept {return 0;} // length of the region
virtual std::size_t size() const noexcept {return 0;} virtual std::size_t size() const noexcept {return 0;}
// number of characters in the line before the region
virtual std::size_t before() const noexcept {return 0;}
// 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 in a container, normally in a file content. // location represents a position in a container, which contains a file content.
// shared_ptr points the resource that the iter points.
// it can be used not only for resource handling, but also error message.
//
// it can be considered as a region that contains only one character. // it can be considered as a region that contains only one character.
//
// it contains pointer to the file content and iterator that points the current
// location.
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;
@@ -77,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
@@ -127,17 +176,26 @@ 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_;
}; };
// region represents a range in a container, which contains a file content.
//
// it contains pointer to the file content and iterator that points the first
// and last location.
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;
@@ -230,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_;
@@ -293,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

@@ -1,7 +1,7 @@
// Copyright Toru Niina 2017. // Copyright Toru Niina 2017.
// Distributed under the MIT License. // Distributed under the MIT License.
#ifndef TOML11_RESULT_H #ifndef TOML11_RESULT_HPP
#define TOML11_RESULT_H #define TOML11_RESULT_HPP
#include "traits.hpp" #include "traits.hpp"
#include <type_traits> #include <type_traits>
#include <stdexcept> #include <stdexcept>

View File

@@ -36,7 +36,7 @@ struct serializer
std::vector<char> buf(bsz + 1, '\0'); // +1 for null character(\0) std::vector<char> buf(bsz + 1, '\0'); // +1 for null character(\0)
std::snprintf(buf.data(), buf.size(), fmt, this->float_prec_, f); std::snprintf(buf.data(), buf.size(), fmt, this->float_prec_, f);
std::string token(buf.begin(), buf.end()); std::string token(buf.begin(), std::prev(buf.end()));
if(token.back() == '.') // 1. => 1.0 if(token.back() == '.') // 1. => 1.0
{ {
token += '0'; token += '0';
@@ -483,15 +483,19 @@ struct serializer
inline std::string inline std::string
format(const value& v, std::size_t w = 80, format(const value& v, std::size_t w = 80,
int fprec = std::numeric_limits<toml::floating>::max_digits10) int fprec = std::numeric_limits<toml::floating>::max_digits10,
bool force_inline = false)
{ {
return visit(serializer(w, fprec, true), v); // if value is a table, it is considered to be a root object.
// the root object can't be an inline table. so pass false. otherwise, true.
return visit(serializer(w, fprec, (!v.is_table()) || force_inline), v);
} }
inline std::string inline std::string
format(const table& t, std::size_t w = 80, format(const table& t, std::size_t w = 80,
int fprec = std::numeric_limits<toml::floating>::max_digits10) int fprec = std::numeric_limits<toml::floating>::max_digits10,
bool force_inline = false)
{ {
return serializer(w, fprec, true)(t); return serializer(w, fprec, force_inline)(t);
} }
template<typename charT, typename traits> template<typename charT, typename traits>
@@ -506,6 +510,18 @@ operator<<(std::basic_ostream<charT, traits>& os, const value& v)
os << visit(serializer(w, fprec, false), v); os << visit(serializer(w, fprec, false), v);
return os; return os;
} }
template<typename charT, typename traits>
std::basic_ostream<charT, traits>&
operator<<(std::basic_ostream<charT, traits>& os, const table& v)
{
// get status of std::setw().
const std::size_t w = os.width();
const int fprec = os.precision();
os.width(0);
// the root object can't be an inline table. so pass `false`.
os << serializer(w, fprec, false)(v);
return os;
}
} // toml } // toml
#endif// TOML11_SERIALIZER_HPP #endif// TOML11_SERIALIZER_HPP

View File

@@ -1,9 +1,14 @@
// Copyright Toru Niina 2017. // Copyright Toru Niina 2017.
// Distributed under the MIT License. // Distributed under the MIT License.
#ifndef TOML11_STRING_H #ifndef TOML11_STRING_HPP
#define TOML11_STRING_H #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

@@ -1,49 +0,0 @@
// Copyright Toru Niina 2017.
// Distributed under the MIT License.
#ifndef TOML11_TO_TOML
#define TOML11_TO_TOML
#include "value.hpp"
namespace toml
{
template<typename T>
TOML11_MARK_AS_DEPRECATED
inline value to_toml(T&& x)
{
return value(std::forward<T>(x));
}
template<typename T>
TOML11_MARK_AS_DEPRECATED
inline value to_toml(T&& x, string_t kind)
{
return value(std::forward<T>(x), kind);
}
TOML11_MARK_AS_DEPRECATED
inline value to_toml(local_date d, local_time t)
{
return value(local_datetime(d, t));
}
TOML11_MARK_AS_DEPRECATED
inline value to_toml(local_date d, local_time t, time_offset ofs)
{
return value(offset_datetime(d, t, ofs));
}
template<typename ... Ts>
TOML11_MARK_AS_DEPRECATED
inline value to_toml(Ts&& ... xs)
{
return value(toml::array{toml::value(std::forward<Ts>(xs)) ... });
}
TOML11_MARK_AS_DEPRECATED
inline value to_toml(std::initializer_list<std::pair<std::string, toml::value>> xs)
{
return value(toml::table(xs.begin(), xs.end()));
}
} // toml
#endif // TOML11_TO_TOML

View File

@@ -1,7 +1,7 @@
// Copyright Toru Niina 2017. // Copyright Toru Niina 2017.
// Distributed under the MIT License. // Distributed under the MIT License.
#ifndef TOML11_TRAITS #ifndef TOML11_TRAITS_HPP
#define TOML11_TRAITS #define TOML11_TRAITS_HPP
#include <type_traits> #include <type_traits>
#include <utility> #include <utility>
#include <chrono> #include <chrono>
@@ -9,6 +9,9 @@
namespace toml namespace toml
{ {
class value; // forward decl
namespace detail namespace detail
{ {
@@ -45,6 +48,22 @@ struct has_resize_method_impl
template<typename T> static std::false_type check(...); template<typename T> static std::false_type check(...);
}; };
struct has_from_toml_method_impl
{
template<typename T>
static std::true_type check(
decltype(std::declval<T>().from_toml(std::declval<::toml::value>()))*);
template<typename T>
static std::false_type check(...);
};
struct has_into_toml_method_impl
{
template<typename T>
static std::true_type check(decltype(std::declval<T>().into_toml())*);
template<typename T>
static std::false_type check(...);
};
/// Intel C++ compiler can not use decltype in parent class declaration, here /// Intel C++ compiler can not use decltype in parent class declaration, here
/// is a hack to work around it. https://stackoverflow.com/a/23953090/4692076 /// is a hack to work around it. https://stackoverflow.com/a/23953090/4692076
#ifdef __INTEL_COMPILER #ifdef __INTEL_COMPILER
@@ -62,6 +81,14 @@ struct has_mapped_type : decltype(has_mapped_type_impl::check<T>(nullptr)){};
template<typename T> template<typename T>
struct has_resize_method : decltype(has_resize_method_impl::check<T>(nullptr)){}; struct has_resize_method : decltype(has_resize_method_impl::check<T>(nullptr)){};
template<typename T>
struct has_from_toml_method
: decltype(has_from_toml_method_impl::check<T>(nullptr)){};
template<typename T>
struct has_into_toml_method
: decltype(has_into_toml_method_impl::check<T>(nullptr)){};
#ifdef __INTEL_COMPILER #ifdef __INTEL_COMPILER
#undef decltype(...) #undef decltype(...)
#endif #endif
@@ -142,6 +169,21 @@ using return_type_of_t = typename std::result_of<F(Args...)>::type;
#endif #endif
// ---------------------------------------------------------------------------
// is_string_literal
//
// to use this, pass `typename remove_reference<T>::type` to T.
template<typename T>
struct is_string_literal:
disjunction<
std::is_same<const char*, T>,
conjunction<
std::is_array<T>,
std::is_same<const char, typename std::remove_extent<T>::type>
>
>{};
}// detail }// detail
}//toml }//toml
#endif // TOML_TRAITS #endif // TOML_TRAITS

View File

@@ -1,12 +1,17 @@
// Copyright Toru Niina 2017. // Copyright Toru Niina 2017.
// Distributed under the MIT License. // Distributed under the MIT License.
#ifndef TOML11_TYPES_H #ifndef TOML11_TYPES_HPP
#define TOML11_TYPES_H #define TOML11_TYPES_HPP
#include "datetime.hpp" #include "datetime.hpp"
#include "string.hpp" #include "string.hpp"
#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
{ {
@@ -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

@@ -1,18 +1,18 @@
// Copyright Toru Niina 2017. // Copyright Toru Niina 2017.
// Distributed under the MIT License. // Distributed under the MIT License.
#ifndef TOML11_UTILITY #ifndef TOML11_UTILITY_HPP
#define TOML11_UTILITY #define TOML11_UTILITY_HPP
#include "traits.hpp" #include "traits.hpp"
#include <utility> #include <utility>
#include <memory> #include <memory>
#include <sstream> #include <sstream>
#if __cplusplus >= 201402L #if __cplusplus >= 201402L
# define TOML11_MARK_AS_DEPRECATED [[deprecated]] # define TOML11_MARK_AS_DEPRECATED(msg) [[deprecated(msg)]]
#elif defined(__GNUC__) #elif defined(__GNUC__)
# define TOML11_MARK_AS_DEPRECATED __attribute__((deprecated)) # define TOML11_MARK_AS_DEPRECATED(msg) __attribute__((deprecated(msg)))
#elif defined(_MSC_VER) #elif defined(_MSC_VER)
# define TOML11_MARK_AS_DEPRECATED __declspec(deprecated) # define TOML11_MARK_AS_DEPRECATED(msg) __declspec(deprecated(msg))
#else #else
# define TOML11_MARK_AS_DEPRECATED # define TOML11_MARK_AS_DEPRECATED
#endif #endif

View File

@@ -1,8 +1,9 @@
// Copyright Toru Niina 2017. // Copyright Toru Niina 2017.
// Distributed under the MIT License. // Distributed under the MIT License.
#ifndef TOML11_VALUE #ifndef TOML11_VALUE_HPP
#define TOML11_VALUE #define TOML11_VALUE_HPP
#include "traits.hpp" #include "traits.hpp"
#include "into.hpp"
#include "utility.hpp" #include "utility.hpp"
#include "exception.hpp" #include "exception.hpp"
#include "storage.hpp" #include "storage.hpp"
@@ -13,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
{ {
@@ -292,6 +296,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)
@@ -473,7 +500,7 @@ class value
region_info_(std::make_shared<region_base>(region_base{})) region_info_(std::make_shared<region_base>(region_base{}))
{ {
array ary; ary.reserve(list.size()); array ary; ary.reserve(list.size());
for(auto& elem : list) {ary.emplace_back(std::move(elem));} for(const auto& elem : list) {ary.emplace_back(elem);}
assigner(this->array_, std::move(ary)); assigner(this->array_, std::move(ary));
} }
template<typename T, typename std::enable_if<detail::is_container<T>::value, template<typename T, typename std::enable_if<detail::is_container<T>::value,
@@ -485,7 +512,7 @@ class value
this->region_info_ = std::make_shared<region_base>(region_base{}); this->region_info_ = std::make_shared<region_base>(region_base{});
array ary; ary.reserve(list.size()); array ary; ary.reserve(list.size());
for(auto& elem : list) {ary.emplace_back(std::move(elem));} for(const auto& elem : list) {ary.emplace_back(elem);}
assigner(this->array_, std::move(ary)); assigner(this->array_, std::move(ary));
return *this; return *this;
} }
@@ -533,10 +560,70 @@ class value
return *this; return *this;
} }
// user-defined =========================================================
// convert using into_toml() method -------------------------------------
template<typename T, typename std::enable_if<detail::conjunction<
detail::negation<detail::is_exact_toml_type<T>>, // not a toml::value
detail::has_into_toml_method<T> // but has `into_toml` method
>::value, std::nullptr_t>::type = nullptr>
value(const T& ud): value(ud.into_toml()) {}
template<typename T, typename std::enable_if<detail::conjunction<
detail::negation<detail::is_exact_toml_type<T>>, // not a toml::value
detail::has_into_toml_method<T> // but has `into_toml` method
>::value, std::nullptr_t>::type = nullptr>
value& operator=(const T& ud)
{
*this = ud.into_toml();
return *this;
}
// convert using into<T> struct -----------------------------------------
template<typename T, typename std::enable_if<
detail::negation<detail::is_exact_toml_type<T>>::value,
std::nullptr_t>::type = nullptr,
std::size_t S = sizeof(::toml::into<T>)>
value(const T& ud): value(::toml::into<T>::into_toml(ud)) {}
template<typename T, typename std::enable_if<
detail::negation<detail::is_exact_toml_type<T>>::value,
std::nullptr_t>::type = nullptr,
std::size_t S = sizeof(::toml::into<T>)>
value& operator=(const T& ud)
{
*this = ::toml::into<T>::into_toml(ud);
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 ============================================
template<typename T> template<typename T>
bool is() const noexcept {return value_traits<T>::type_index == this->type_;} bool is() const noexcept {return value_traits<T>::type_index == this->type_;}
bool is(value_t t) const noexcept {return t == this->type_;} bool is(value_t t) const noexcept {return t == this->type_;}
bool is_uninitialized() const noexcept {return this->is(value_t::Empty );}
bool is_boolean() const noexcept {return this->is(value_t::Boolean );}
bool is_integer() const noexcept {return this->is(value_t::Integer );}
bool is_float() const noexcept {return this->is(value_t::Float );}
bool is_string() const noexcept {return this->is(value_t::String );}
bool is_offset_datetime() const noexcept {return this->is(value_t::OffsetDatetime);}
bool is_local_datetime() const noexcept {return this->is(value_t::LocalDatetime );}
bool is_local_date() const noexcept {return this->is(value_t::LocalDate );}
bool is_local_time() const noexcept {return this->is(value_t::LocalTime );}
bool is_array() const noexcept {return this->is(value_t::Array );}
bool is_table() const noexcept {return this->is(value_t::Table );}
value_t type() const {return type_;} value_t type() const {return type_;}
template<value_t T> template<value_t T>
@@ -546,6 +633,41 @@ 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 {return this->cast<value_t::Boolean >();}
integer const& as_integer() const {return this->cast<value_t::Integer >();}
floating const& as_float() const {return this->cast<value_t::Float >();}
string const& as_string() const {return this->cast<value_t::String >();}
offset_datetime const& as_offset_datetime() const {return this->cast<value_t::OffsetDatetime>();}
local_datetime const& as_local_datetime() const {return this->cast<value_t::LocalDatetime >();}
local_date const& as_local_date() const {return this->cast<value_t::LocalDate >();}
local_time const& as_local_time() const {return this->cast<value_t::LocalTime >();}
array const& as_array() const {return this->cast<value_t::Array >();}
table const& as_table() const {return this->cast<value_t::Table >();}
boolean& as_boolean() {return this->cast<value_t::Boolean >();}
integer& as_integer() {return this->cast<value_t::Integer >();}
floating& as_float() {return this->cast<value_t::Float >();}
string& as_string() {return this->cast<value_t::String >();}
offset_datetime& as_offset_datetime() {return this->cast<value_t::OffsetDatetime>();}
local_datetime& as_local_datetime() {return this->cast<value_t::LocalDatetime >();}
local_date& as_local_date() {return this->cast<value_t::LocalDate >();}
local_time& as_local_time() {return this->cast<value_t::LocalTime >();}
array& as_array() {return this->cast<value_t::Array >();}
table& as_table() {return this->cast<value_t::Table >();}
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
@@ -794,38 +916,40 @@ inline bool operator>=(const toml::value& lhs, const toml::value& rhs)
return !(lhs < rhs); return !(lhs < rhs);
} }
namespace detail inline std::string format_error(const std::string& err_msg,
{
inline std::string format_error_impl(const std::string& err_msg,
std::vector<std::pair<region_base const*, std::string>> val,
std::vector<std::string> hints)
{
return format_underline(err_msg, std::move(val), std::move(hints));
}
inline std::string format_error_impl(const std::string& err_msg,
std::vector<std::pair<region_base const*, std::string>> val)
{
return format_underline(err_msg, std::move(val));
}
template<typename ... Ts>
std::string format_error_impl(const std::string& err_msg,
std::vector<std::pair<region_base const*, std::string>> val,
const toml::value& v, const std::string& comment, const toml::value& v, const std::string& comment,
Ts&& ... args) std::vector<std::string> hints = {})
{ {
val.push_back(std::make_pair(std::addressof(get_region(v)), comment)); return detail::format_underline(err_msg,
return format_error_impl(err_msg, std::move(val), std::forward<Ts>(args)...); std::vector<std::pair<detail::region_base const*, std::string>>{
{std::addressof(detail::get_region(v)), comment}
}, std::move(hints));
} }
} // detail
template<typename ... Ts> inline std::string format_error(const std::string& err_msg,
std::string format_error(const std::string& err_msg, Ts&& ... args) const toml::value& v1, const std::string& comment1,
const toml::value& v2, const std::string& comment2,
std::vector<std::string> hints = {})
{ {
std::vector<std::pair<detail::region_base const*, std::string>> val; return detail::format_underline(err_msg,
val.reserve(sizeof...(args) / 2); std::vector<std::pair<detail::region_base const*, std::string>>{
return detail::format_error_impl(err_msg, std::move(val), {std::addressof(detail::get_region(v1)), comment1},
std::forward<Ts>(args)...); {std::addressof(detail::get_region(v2)), comment2}
}, std::move(hints));
}
inline std::string format_error(const std::string& err_msg,
const toml::value& v1, const std::string& comment1,
const toml::value& v2, const std::string& comment2,
const toml::value& v3, const std::string& comment3,
std::vector<std::string> hints = {})
{
return detail::format_underline(err_msg,
std::vector<std::pair<detail::region_base const*, std::string>>{
{std::addressof(detail::get_region(v1)), comment1},
{std::addressof(detail::get_region(v2)), comment2},
{std::addressof(detail::get_region(v3)), comment3}
}, std::move(hints));
} }
template<typename Visitor> template<typename Visitor>
@@ -877,7 +1001,7 @@ visit(Visitor&& visitor, toml::value& v)
} }
template<typename Visitor> template<typename Visitor>
detail::return_type_of_t<Visitor, toml::boolean&> detail::return_type_of_t<Visitor, toml::boolean&&>
visit(Visitor&& visitor, toml::value&& v) visit(Visitor&& visitor, toml::value&& v)
{ {
switch(v.type()) switch(v.type())