Compare commits

...

177 Commits

Author SHA1 Message Date
ToruNiina
c037913b2c doc: update link to the TOML spec 2020-09-20 19:24:48 +09:00
ToruNiina
6a328fe890 doc: recommend to set /Zc:__cplusplus 2020-09-20 18:07:58 +09:00
ToruNiina
7c18cbb1d9 doc: update section "contributors" 2020-09-19 20:35:37 +09:00
ToruNiina
ba7d49f452 test: use normal string literal
as a workaround for older version of gcc
2020-09-19 19:08:20 +09:00
ToruNiina
b0784ce286 test: in case of comment-before-comma 2020-09-19 18:24:23 +09:00
ToruNiina
670186fac7 Merge branch 'master' into allow-comment-before-comma 2020-09-19 18:10:45 +09:00
ToruNiina
5005998709 Merge branch 'master' into cpp20-mode-u8literal-workaround 2020-09-19 13:42:12 +09:00
ToruNiina
84fb703e04 ci: add utf-8 option to MSVC 2020-09-19 00:41:05 +09:00
ToruNiina
8c2560761b chore: enable to use __cplusplus on MSVC
related: https://github.com/ToruNiina/toml11/issues/112
2020-09-19 00:40:44 +09:00
ToruNiina
07ea5e52e2 ci: pass REQ_FS_LIB=ON in case of g++-8 & C++20 2020-09-16 22:16:20 +09:00
ToruNiina
d2b1e962c9 ci: add std=20 to some compilers on github actions 2020-09-16 21:28:19 +09:00
ToruNiina
528031012d test: add test for u8""_toml literals 2020-09-16 21:25:38 +09:00
ToruNiina
c205c762fe test: remove needless u8s from ascii characters 2020-09-16 21:25:04 +09:00
ToruNiina
a32cd6cb61 feat: enable to use u8""_toml literal in C++20 2020-09-16 21:24:03 +09:00
ToruNiina
38e113d2dc ci: set BUILD_TEST=ON on appveyor 2020-09-15 22:40:24 +09:00
Toru Niina
f15480ae4d Merge pull request #130 from MoAlyousef/master
Make toml11_BUILD_TEST Off by default
2020-09-15 22:28:34 +09:00
MoAlyousef
00bec8ae45 update Running Tests heading 2020-09-14 22:05:15 +03:00
MoAlyousef
d599edd1d4 make testing optional 2020-09-14 20:34:19 +03:00
MoAlyousef
a9534579c6 make testing optional 2020-09-14 20:25:38 +03:00
ToruNiina
c8ff302c94 test: add test for no-eof-newline cases 2020-09-14 16:39:05 +09:00
ToruNiina
003bc16c1b fix: skip the last zero in the file 2020-09-14 16:35:51 +09:00
Toru Niina
9132abc5c4 Merge pull request #127 from kenichiice/fix-include
Fix include path in README
2020-09-07 16:19:29 +09:00
OGAWA KenIchi
99d565bcc4 doc: fix include path
* see #72
2020-09-07 15:32:28 +09:00
ToruNiina
5f38127692 feat: allow comments before comma
replace ws by ws_comment_newline, as suggested.
discussed here: toml-lang/toml/issues/766
2020-08-16 11:03:58 +09:00
ToruNiina
3c3ebd88b4 feat: improve error message about invalid keys 2020-08-09 18:38:50 +09:00
ToruNiina
08f7ea9c56 refactor: remove extraneous whitespaces in errmsg 2020-08-09 18:38:21 +09:00
ToruNiina
cde29399f4 fix: use 1 in source_location as the default pos 2020-08-07 22:24:01 +09:00
ToruNiina
eec429e31b ci: add REQUIRE_FILESYSTEM_LIBRARY on CI 2020-08-06 16:35:49 +09:00
ToruNiina
79ddcaece6 chore: add CMake option to link with (std)c++fs 2020-08-06 16:29:24 +09:00
ToruNiina
8398b9a08b test: use array for char*
forgot to delete
2020-08-05 20:43:48 +09:00
ToruNiina
9c5abf0bfd test: check each overload compiles 2020-08-05 20:29:07 +09:00
ToruNiina
4fa94d45b3 fix: use const char* instead of &char[N]
to enable to pass char*, not only string literal
2020-08-04 20:08:58 +09:00
ToruNiina
46e84a9cc2 refactor: Merge branch 'refactor-region' 2020-07-31 12:45:52 +09:00
ToruNiina
4e6ae9a994 refactor: avoid string construct in format_ul 2020-07-30 16:11:35 +09:00
ToruNiina
f23c003d2f fix: add missing namespace specifier 2020-07-28 00:04:25 +09:00
ToruNiina
4b719f0806 refactor: use location() instead of get_region 2020-07-27 23:15:14 +09:00
ToruNiina
22ace027de refactor: rm template from detail::change_region 2020-07-27 23:04:24 +09:00
ToruNiina
bc219af5b5 refactor: use location() member instead of ctor 2020-07-27 23:03:33 +09:00
ToruNiina
68e8a31659 refactor: remove needless addressof() call 2020-07-27 23:00:40 +09:00
ToruNiina
32a5341d09 refactor: use source_location, not region_base* 2020-07-27 22:29:18 +09:00
ToruNiina
ce68f6f4c2 refactor: check (always-valid) ptr before deref 2020-07-27 21:32:35 +09:00
ToruNiina
e696aabd11 refactor: change internal interface to reduce code
to remove `std::addressof` calls, get_region(toml::value) now
returns a pointer to region.
2020-07-27 00:48:04 +09:00
ToruNiina
7fb93e2f54 fix: add missing explicit to detail::region 2020-07-27 00:20:26 +09:00
ToruNiina
19cc9a2edf refactor: remove template from detail::region 2020-07-25 22:01:34 +09:00
ToruNiina
72f5afb6af refactor: remove template from detail::location 2020-07-25 21:06:26 +09:00
ToruNiina
a8fa14d159 refactor: remove vec() method, use a constructor 2020-07-21 20:55:18 +09:00
ToruNiina
75999aa9ad refactor: add a constructor to location
By adding the constructor, vec() would not be not needed. But inserting
Container = std::string makes the constructor ambiguous, so it breaks
the current code.
2020-07-21 20:53:44 +09:00
ToruNiina
259da54edb refactor: always use vector<char> in location
`location` and `region` have a (shared_ptr to the) container of TOML
contents. Those take a template argument to allow both std::vector<char>
and std::string as an interanal container. But since those are internal
feature, i.e. it should not be used by a user directly, this template
can be removed by re-writing a parser a bit. This introduces a
complexity to toml11 error reporting system, so I'm removing this.
First, remove all the location<std::string> from the parser. Then the
template argument can be removed because everyone uses std::vector<char>
now.
2020-07-20 19:52:11 +09:00
ToruNiina
b461f363da refactor: add a method to reduce complexity later 2020-07-20 19:40:55 +09:00
ToruNiina
d43139a471 doc: update Contributors section 2020-07-19 19:50:02 +09:00
ToruNiina
a344668fa2 doc: update version to 3.5.0 2020-07-19 19:12:18 +09:00
ToruNiina
25aa97a435 doc: add actions status badge to README 2020-07-19 19:09:47 +09:00
ToruNiina
af70d3dfed ci [skip ci]: add github actions workflow file 2020-07-19 17:13:39 +09:00
ToruNiina
8b5cfb4105 test: add missing binary flag to ifstream 2020-07-19 16:57:20 +09:00
ToruNiina
4e0624aa60 feat: make sure the last null is removed 2020-07-19 16:56:31 +09:00
ToruNiina
3ac2c065eb Merge branch 'reorder-headers' to master 2020-07-17 15:17:40 +09:00
ToruNiina
470f81dc94 fix: #123 merge branch 'windows-nominmax' 2020-07-10 20:55:53 +09:00
ToruNiina
93a9f2711c test: add windows.h test 2020-07-10 18:32:59 +09:00
ToruNiina
761e576991 fix: workaround for windows.h that defines min/max
related to #123
2020-07-10 15:07:13 +09:00
ToruNiina
e6e84714c5 Merge branch 'master' into reorder-headers 2020-07-10 00:06:22 +09:00
Toru Niina
1efc99e11c Merge pull request #121 from SeverinLeonhardt/fix_msvc_c4866
Fix MSVC warning C4866
2020-07-03 21:37:22 +09:00
Marius Maaß
92aa42a58e Fix MSVC warning C4866
This fixes the warning "compiler may not enforce left-to-right
evaluation order for call to" that is caused by Visual Studio if this is
compiled with a target of C++17.
2020-07-03 08:00:47 +02:00
ToruNiina
b1c9df8998 feat: reorder headers following google c++ style
related to: #115
2020-06-28 00:58:20 +09:00
ToruNiina
9633e5fe5a doc: add iteration examples into as_xxx section
related to #120
2020-06-21 14:11:26 +09:00
ToruNiina
2164fd39f7 doc: explain about the type of the top-level value
fix #120.
2020-06-21 14:04:20 +09:00
ToruNiina
c22a3fd227 feat: support parse(std::filesystem::path) #113 2020-06-07 15:11:48 +09:00
ToruNiina
57c6652360 Merge branch 'master' into std-filesystem 2020-06-06 17:25:26 +09:00
ToruNiina
defde33544 fix: avoid ambiguity in overload resolution
Since both `std::string` and `std::filesystem::path` can be convertible
from `const char &[N]` (like, `parse("file.toml")`), after adding
`parse(std::filesystem::path)`, the overload resolution of
`parse("file.toml")` becomes ambiguous. By adding `parse(...)` that
exactly matches to `parse("file.toml")`, we can remove this ambiguity.
2020-06-06 17:18:02 +09:00
ToruNiina
46ed051740 fix: pass path.string as a filename 2020-06-05 23:15:19 +09:00
ToruNiina
2963d9a25b feat: add std::filesystem::path support 2020-06-05 19:43:23 +09:00
Toru Niina
531f335417 Merge pull request #119 from halfelf/fix/readme_finding_value_in_table
fix: "Finding a value in an array" example in README
2020-05-20 00:27:33 +09:00
Shu Wang
f29f42277e fix: "Finding a value in an array" example in README 2020-05-18 13:53:48 +08:00
Toru Niina
b03cde566a Merge pull request #117 from usefulcat/master
when parsing a local_time, parse up to 9 digits worth (nanoseconds) o…
2020-05-11 13:36:23 +09:00
Scott McCaskill
57d4e196a3 when parsing a local_time, parse up to 9 digits worth (nanoseconds) of fractional seconds 2020-05-10 16:06:52 -05:00
ToruNiina
deb3ab6617 ci: add DISALLOW_HETEROGENEOUS_ARRAYS 2020-04-03 23:57:52 +09:00
ToruNiina
bf992e8f94 doc: update README for v1-rc1 2020-04-03 23:45:45 +09:00
ToruNiina
7c07f4382c ci: add DISALLOW_HETEROGENEOUS_ARRAYS to toml-test 2020-04-03 23:43:59 +09:00
ToruNiina
125f608fa5 feat: remove TOML11_UNRELEASED_FEATURES.
v1.0.0-rc.1 has been released
2020-04-03 23:42:58 +09:00
ToruNiina
4d0ed847f9 test: remove default ctor from test code 2020-03-30 15:04:51 +09:00
ToruNiina
79594709fe fix: don't use default ctor when converting to map 2020-03-30 15:02:26 +09:00
ToruNiina
55a738c11f Merge branch 'do-not-require-default-ctor-108'
fix #108.
2020-03-28 23:19:52 +09:00
ToruNiina
eebe1f87e6 fix: update cmake version 3.4.0 2020-03-28 17:59:10 +09:00
ToruNiina
95c3b5f538 feat: use push_back instead of resize 2020-03-27 18:06:26 +09:00
ToruNiina
e2790c9e7b test: remove test_resize and add test_try_reserve 2020-03-27 18:06:02 +09:00
ToruNiina
9b52dc0131 feat: remove resize and add try_reserve 2020-03-27 18:05:31 +09:00
ToruNiina
5212992f05 feat: add is_std_forward_list
std::forward_list does not have push_back, insert, or emplace but
push_front, insert_after, and emplace_after. We need to distinguish it
from other continers.
2020-03-27 18:02:37 +09:00
ToruNiina
fcd6e47500 feat: add meta funcs, has_reserve/push_back_method 2020-03-27 18:01:47 +09:00
ToruNiina
31826b55ce feat: avoid double checking in helper methods 2020-03-25 22:49:19 +09:00
ToruNiina
e3fc354e8d Merge branch 'shorten-switch-cast' 2020-03-24 22:43:09 +09:00
ToruNiina
ea87f92358 doc: update exception section in README (fix #107) 2020-03-23 20:57:36 +09:00
ToruNiina
c259456282 ci: fix Travis.CI OS X build 2020-03-22 20:40:02 +09:00
ToruNiina
d7662347f2 refactor: shorten switch_cast definition by macro 2020-03-21 17:44:23 +09:00
ToruNiina
5f5539d402 feat: throw informative error from value.at(...) 2020-03-21 17:09:04 +09:00
ToruNiina
c2151cab0b refactor: show func name in bad_cast from helpers 2020-03-21 17:06:34 +09:00
ToruNiina
653c87592c feat: enable to show function name in bad_cast 2020-03-21 17:04:05 +09:00
ToruNiina
bdf4e75122 refactor: move helper function from get to value 2020-03-21 16:57:12 +09:00
ToruNiina
60d23116ba Merge branch 'master' of github.com:ToruNiina/toml11 2020-03-13 14:38:33 +09:00
ToruNiina
af8cf9ddc5 refactor: remove redundant functions in serializer 2020-03-13 13:55:14 +09:00
ToruNiina
f125cca010 refactor: simplify serializer's template argument 2020-03-12 13:46:17 +09:00
ToruNiina
a20a2c0b80 doc: update README 2020-03-01 00:35:27 +09:00
ToruNiina
9694afbe32 Merge branch 'improve-error-message' 2020-02-29 23:43:23 +09:00
ToruNiina
d11e42ca7e fix: explicitly say the table is top-level
The top-level table has its region at the first character of the file.
That means that, in the case when a key is not found in the top-level
table, the error message points to the first character. If the file has
its first table at the first line, the error message would be like this.
```console
[error] key "a" not found
 --> example.toml
   |
 1 | [table]
   | ^------ in this table
```
It actually points to the top-level table at the first character,
not `[table]`. But it is too confusing. To avoid the confusion, the
error message should explicitly say "key not found in the top-level
table".
2020-02-29 22:56:29 +09:00
ToruNiina
128b66bda9 refactor: add missing whitespace 2020-02-29 22:54:50 +09:00
ToruNiina
d1af42f151 refactor: add throw_key_not_found_error
and replace related throw statements with it
2020-02-29 22:23:15 +09:00
ToruNiina
8acf105b56 doc: update contributor list and test commands 2020-02-27 19:30:34 +09:00
Toru Niina
b86b5364ba Merge pull request #103 from jwillikers/fix_tests
Use FetchContent to retrieve TOML test data
2020-02-27 19:13:02 +09:00
Jordan Williams
bfe57340f4 no longer explicitly clone the TOML repository in CI builds 2020-02-24 08:03:29 -06:00
Jordan Williams
02a6f029ad use ExternalProject to download the toml tests
In order to evaluate the relative paths correctly, add_test should use the current binary directory as the working directory.
2020-02-24 07:59:59 -06:00
Jordan Williams
9017900ff3 set version directly from CMake project command
This silences the warning for CMake policy CMP0048: https://cmake.org/cmake/help/v3.0/policy/CMP0048.html
2020-02-24 07:35:59 -06:00
Jordan Williams
3c5ebd73d7 require CMake version 3.1 2020-02-24 07:31:48 -06:00
Jordan Williams
2223eb4f62 Revert "no longer explicitly clone the TOML repository in CI builds"
This reverts commit fe644ea4b7.
2020-02-24 07:31:15 -06:00
Jordan Williams
a655a71cef Revert "use FetchContent to retrieve TOML test data"
This reverts commit 4c34986db0.
2020-02-24 07:31:06 -06:00
ToruNiina
c34001725c Merge branch 'workaround-gcc-48x' 2020-02-20 11:59:49 +09:00
ToruNiina
5e3ffb70dd fix: check clang macro when checking gcc is used 2020-02-19 17:00:22 +09:00
ToruNiina
2265ca41c6 ci: test with gcc 4.8 and 4.9 on CI 2020-02-19 15:47:34 +09:00
ToruNiina
82fec38e37 refactor: simplify internally-used function 2020-02-19 15:46:25 +09:00
ToruNiina
189b910384 fix: solve #97 in the naivest way, macros 2020-02-19 15:44:38 +09:00
Jordan Williams
fe644ea4b7 no longer explicitly clone the TOML repository in CI builds 2020-02-18 20:21:02 -06:00
Jordan Williams
4c34986db0 use FetchContent to retrieve TOML test data 2020-02-18 19:37:28 -06:00
ToruNiina
ac1130f9f4 tag: update patch version v3.3.1 2020-02-16 22:02:40 +09:00
ToruNiina
d290c3b7e5 doc: add contributor to README 2020-02-14 19:25:41 +09:00
Toru Niina
e4140ac1fd Merge pull request #102 from jwillikers/cmake_cache_variables
Set CMake Standard Cache Variables. Fixes #101
2020-02-13 13:22:25 +09:00
Jordan Williams
ef33c10ba8 use cache variables for the CMake standard and extensions settings 2020-02-12 07:44:47 -06:00
Toru Niina
ced710bb4c Merge pull request #100 from jwillikers/clang_warnings
Silence Clang Warnings. Fixes #98 & #99
2020-02-12 13:37:58 +09:00
Jordan Williams
6b5944e839 fix -Wundef warnings 2020-02-11 06:30:18 -06:00
Jordan Williams
76cae8c057 enable -Wundef flag for tests 2020-02-11 06:25:10 -06:00
Jordan Williams
3930a44ccd enable range-loop-analysis flag for tests 2020-02-11 06:17:54 -06:00
Jordan Williams
3b6417de00 fix clang range-loop-analysis warnings 2020-02-11 06:13:55 -06:00
ToruNiina
573a6f1d81 test: use JSON format_key to format a key in JSON
The rule to format a basic string and a key is different. `"""` cannot
be used with JSON keys. Also, toml key is basically not wrapped by `"`.
So we need a function to format a key in JSON.
2020-02-06 01:28:37 +09:00
ToruNiina
f6a41d986c feat: handle quotes in strings in the better way
- if a basic string contains any double quote, make it multiline.
  - because 1 or 2 consecutive "s do not require escape sequence in it.
- if a basic string will be sufficiently long, make it multiline.
- if 3 consecutive "s appeared, insert backslash to break it down.
2020-02-05 22:42:10 +09:00
ToruNiina
16fc172b21 feat: check string length before adding newline
In literal strings, only the first newline will be trimmed.
```toml
str = '''
The first newline will be trimmed.'''
```
The previous code always adds this first-newline, but after this commit
it checks the length of the string and adds newline if the string is
sufficiently long.
2020-02-05 22:39:08 +09:00
ToruNiina
7d03eb489a test: add test cases with quotes in ml-string
some of these example strings are copied from toml-lang/toml:README.md
and some are modified.
2020-02-04 22:37:11 +09:00
ToruNiina
0582e1535b fix: handle edge-cases with quotes in ml-string
See comments in the code for detail.
2020-02-04 22:36:39 +09:00
ToruNiina
d495df93a6 refactor: remove trailing whitespaces 2020-02-04 22:21:37 +09:00
ToruNiina
5ca3a3c262 refactor: change ifdef UNRELEASED_FEATURE region
to eliminate dead code after returning from a function
2020-02-04 21:05:03 +09:00
ToruNiina
aa8d574dfe chore: update minor version 2020-01-24 22:08:12 +09:00
ToruNiina
49fdb61731 refactor: add explicit to ctors of internal types 2020-01-24 15:58:24 +09:00
ToruNiina
b2bb21a473 doc: update year of copyright notice 2020-01-23 22:18:04 +09:00
ToruNiina
0c58549fc6 Merge branch 'master' of github.com:ToruNiina/toml11 2020-01-22 12:20:34 +09:00
ToruNiina
b7b5e847d3 ci: test with C++14, not only 11/17 2020-01-22 12:19:53 +09:00
ToruNiina
22d630fec1 feat: replace detail::stuff by std if possible 2020-01-20 12:18:05 +09:00
ToruNiina
f7bf341452 fix: add missing noexcept specifier 2020-01-19 21:06:10 +09:00
ToruNiina
0934d90f90 refactor: move ctors that are only used internally 2020-01-19 18:30:27 +09:00
ToruNiina
f2c8d0e279 refactor: add missing explicit to toml::exception 2020-01-19 17:51:24 +09:00
ToruNiina
8c7d83d985 Merge branch 'add-value-member-methods' 2020-01-17 20:30:26 +09:00
ToruNiina
5ce44adbdc doc: add description about toml::value memfuns 2020-01-17 20:26:36 +09:00
ToruNiina
5c5b1320d0 test: add test for map/vector methods 2020-01-16 20:58:36 +09:00
ToruNiina
8b737dc21f feat: add member methods to toml::value 2020-01-16 20:58:10 +09:00
ToruNiina
ee654b6c3f chore: add -Werror when building test codes 2020-01-13 11:31:03 +09:00
ToruNiina
c59782d180 fix: remove useless conversions in the test codes 2020-01-13 11:29:17 +09:00
ToruNiina
9bef715ccd fix: use u32 as a result of binary operation 2020-01-13 11:26:53 +09:00
ToruNiina
d2b1cf5123 refactor: just use a constructor
to remove conversions
2020-01-13 11:26:29 +09:00
ToruNiina
9f92916d1d fix: suppress -Wuseless-conversion
`{integer} + 1` will automatically be an int, so static_cast<int>(a+1)
will be useless conversion.
2020-01-13 11:24:48 +09:00
ToruNiina
666e4cf9dc fix: suppress sign-conversion warnings 2020-01-13 00:46:21 +09:00
ToruNiina
cafee29c64 test: add some combinations of types in toml::find 2020-01-13 00:27:39 +09:00
ToruNiina
a7a2272b29 chore: turn more diagnostic flags on 2020-01-13 00:16:33 +09:00
ToruNiina
dc0bca2bb6 fix: update patch version ... 2020-01-12 23:18:03 +09:00
ToruNiina
490abe04fd refactor: remove redundant template argument 2020-01-12 23:07:17 +09:00
ToruNiina
81ed4c0e9d Merge branch 'master' of github.com:ToruNiina/toml11 2020-01-10 21:17:17 +09:00
ToruNiina
1b07baf184 doc: add toml::get specialization using ctor 2020-01-10 20:46:54 +09:00
ToruNiina
9073d52159 test: check get<foo> works with constructor 2020-01-10 20:39:56 +09:00
ToruNiina
55260654bf feat: get user-defined value by constructor
If a user-defined constructor has constructor(const toml::value&),
then it should be convertible in `toml::get` and `toml::find`.
2020-01-10 20:38:52 +09:00
ToruNiina
aa6271af75 doc: update README 2020-01-09 01:40:05 +09:00
ToruNiina
c54a03f189 Merge branch 'master' into find-idx 2020-01-09 00:27:51 +09:00
ToruNiina
c153c0e8c3 ci: test with sanitizers 2020-01-08 23:28:17 +09:00
ToruNiina
1f90af8e67 ci: refactor list of env vars 2020-01-08 23:17:38 +09:00
ToruNiina
a0c5192b74 chore: add option to use sanitizers 2020-01-08 23:07:05 +09:00
ToruNiina
7f020f3f44 refactor: remove error prefix
that will automatically be added in format_underline
2020-01-07 22:27:13 +09:00
ToruNiina
827b433389 Merge branch 'master' into find-idx 2020-01-07 22:13:15 +09:00
ToruNiina
b1827e6fca test: check immutability of inline tables 2019-12-20 19:46:54 +09:00
ToruNiina
18f84088b4 perf: avoid tmp str construction while checking 2019-12-19 22:13:47 +09:00
ToruNiina
c199bd8b49 feat: enable to access the 1st char of region 2019-12-19 22:13:33 +09:00
ToruNiina
5b35c1a74e fix: prohibit modification on inline table
According to toml-lang/toml:36d3091b3 "Clarify that inline tables are
immutable", check if it adds key-value pair to an inline table.
  This is one of the unreleased (after-0.5.0) toml feature. But this is
marked as "Clarify", so TOML-lang intended that inline tables are
immutable in all version.
2019-12-19 22:02:17 +09:00
ToruNiina
d3513c0f84 fix: fmt line num in err msg correctly 2019-12-17 19:35:26 +09:00
ToruNiina
1148d01c70 Merge branch 'master' into find-idx 2019-10-07 11:11:08 +09:00
ToruNiina
17d78553ff test: add test cases for find(v, idx)
- check whether find(v, idx) throws
- check find(v, ks...) works with both indices and strings
2019-10-03 15:48:04 +09:00
ToruNiina
4c12dad51f feat: add find<T>(value, idx) for arrays (#79) 2019-10-03 15:27:25 +09:00
48 changed files with 2960 additions and 1413 deletions

View File

@@ -12,7 +12,7 @@ jobs:
command: | command: |
g++ --version g++ --version
cd tests/ cd tests/
g++ -std=c++11 -O2 -Wall -Wextra -Werror -DTOML11_COLORIZE_ERROR_MESSAGE -I../ check_toml_test.cpp -o check_toml_test g++ -std=c++11 -O2 -Wall -Wextra -Werror -DTOML11_DISALLOW_HETEROGENEOUS_ARRAYS -I../ check_toml_test.cpp -o check_toml_test
go get github.com/BurntSushi/toml-test go get github.com/BurntSushi/toml-test
$GOPATH/bin/toml-test ./check_toml_test $GOPATH/bin/toml-test ./check_toml_test
test_serialization: test_serialization:
@@ -24,7 +24,7 @@ jobs:
command: | command: |
g++ --version g++ --version
cd tests/ cd tests/
g++ -std=c++11 -O2 -Wall -Wextra -Wpedantic -Werror -DTOML11_COLORIZE_ERROR_MESSAGE -I../ check_serialization.cpp -o check_serialization g++ -std=c++11 -O2 -Wall -Wextra -Wpedantic -Werror -DTOML11_DISALLOW_HETEROGENEOUS_ARRAYS -I../ check_serialization.cpp -o check_serialization
git clone https://github.com/BurntSushi/toml-test.git git clone https://github.com/BurntSushi/toml-test.git
cp check_serialization toml-test/tests/valid cp check_serialization toml-test/tests/valid
cd toml-test/tests/valid cd toml-test/tests/valid
@@ -47,7 +47,7 @@ jobs:
command: | command: |
g++ --version g++ --version
cd tests/ cd tests/
g++ -std=c++11 -O2 -Wall -Wextra -Wpedantic -Werror -DTOML11_COLORIZE_ERROR_MESSAGE -I../ check.cpp -o check g++ -std=c++11 -O2 -Wall -Wextra -Wpedantic -Werror -DTOML11_DISALLOW_HETEROGENEOUS_ARRAYS -I../ check.cpp -o check
git clone https://github.com/BurntSushi/toml-test.git git clone https://github.com/BurntSushi/toml-test.git
cp check toml-test/tests/invalid cp check toml-test/tests/invalid
cp check toml-test/tests/valid cp check toml-test/tests/valid

117
.github/workflows/main.yml vendored Normal file
View File

@@ -0,0 +1,117 @@
name: build
on: [push, pull_request]
jobs:
build-linux-gcc:
runs-on: Ubuntu-18.04
strategy:
matrix:
# g++-4.8 and 4.9 are tested on Travis.CI.
compiler: ['g++-9', 'g++-8', 'g++-7', 'g++-6', 'g++-5']
standard: ['11', '14', '17', '20']
exclude:
- {compiler: 'g++-5', standard: '17'}
- {compiler: 'g++-6', standard: '17'}
- {compiler: 'g++-5', standard: '20'}
- {compiler: 'g++-6', standard: '20'}
- {compiler: 'g++-7', standard: '20'}
steps:
- name: Checkout
uses: actions/checkout@v2
with:
submodules: true
- name: Install
run: |
sudo apt-add-repository ppa:mhier/libboost-latest
sudo apt-get update
sudo apt-get install boost1.70
if [[ "${{ matrix.compiler }}" == "g++-6" || "${{ matrix.compiler }}" == "g++-5" ]] ; then
sudo apt-add-repository ppa:ubuntu-toolchain-r/test
sudo apt-get update
sudo apt-get install ${{ matrix.compiler }}
fi
- name: Configure
run: |
mkdir build && cd build
if [[ "${{ matrix.compiler }}" == "g++-8" && ( "${{ matrix.standard }}" == "17" || "${{ matrix.standard }}" == "20" ) ]] ; then
cmake .. -Dtoml11_BUILD_TEST=ON -DCMAKE_CXX_COMPILER=${{ matrix.compiler }} -DCMAKE_CXX_STANDARD=${{ matrix.standard }} -DTOML11_REQUIRE_FILESYSTEM_LIBRARY=ON
else
cmake .. -Dtoml11_BUILD_TEST=ON -DCMAKE_CXX_COMPILER=${{ matrix.compiler }} -DCMAKE_CXX_STANDARD=${{ matrix.standard }}
fi
- name: Build
run: |
cd build && cmake --build .
- name: Test
run: |
cd build && ctest --output-on-failure
build-linux-clang:
runs-on: Ubuntu-18.04
strategy:
matrix:
compiler: ['10', '9', '8', '7', '6.0', '5.0', '4.0', '3.9']
standard: ['11', '14', '17', '20']
exclude:
- {compiler: '3.9', standard: '17'}
- {compiler: '4.0', standard: '17'}
- {compiler: '5.0', standard: '17'}
- {compiler: '3.9', standard: '20'}
- {compiler: '4.0', standard: '20'}
- {compiler: '5.0', standard: '20'}
steps:
- name: Checkout
uses: actions/checkout@v2
with:
submodules: true
- name: Install
run: |
sudo apt-add-repository ppa:mhier/libboost-latest
sudo apt-get update
sudo apt-get install boost1.70
if [[ "${{ matrix.compiler }}" != "6" && "${{ matrix.compiler }}" != "8" && "${{ matrix.compiler }}" != "9" ]] ; then
sudo apt-add-repository ppa:ubuntu-toolchain-r/test
sudo apt-get update
sudo apt-get install clang-${{ matrix.compiler }}
fi
- name: Configure
run: |
mkdir build && cd build
cmake .. -Dtoml11_BUILD_TEST=ON -DCMAKE_CXX_COMPILER=clang++-${{ matrix.compiler }} -DCMAKE_CXX_STANDARD=${{ matrix.standard }}
- name: Build
run: |
cd build && cmake --build .
- name: Test
run: |
cd build && ctest --output-on-failure
build-windows-msvc:
runs-on: windows-2019
strategy:
matrix:
standard: ['11', '14', '17', '20']
config: ['Release', 'Debug']
steps:
- name: Checkout
uses: actions/checkout@v2
with:
submodules: true
- uses: ilammy/msvc-dev-cmd@v1
- name: Configure
shell: cmd
run: |
file --mime-encoding tests/test_literals.cpp
mkdir build
cd build
cmake ../ -G "NMake Makefiles" -Dtoml11_BUILD_TEST=ON -DCMAKE_CXX_STANDARD=${{ matrix.standard }} -DBoost_ADDITIONAL_VERSIONS=1.72.0 -DBoost_USE_MULTITHREADED=ON -DBoost_ARCHITECTURE=-x64 -DBoost_NO_BOOST_CMAKE=ON -DBOOST_ROOT=%BOOST_ROOT_1_72_0%
- name: Build
working-directory: ./build
run: |
cmake --build . --config "${{ matrix.config }}"
- name: Test
working-directory: ./build
run: |
./tests/test_literals --log_level=all
file --mime-encoding tests/toml/tests/example.toml
file --mime-encoding tests/toml/tests/fruit.toml
file --mime-encoding tests/toml/tests/hard_example.toml
file --mime-encoding tests/toml/tests/hard_example_unicode.toml
ctest --build-config "${{ matrix.config }}" --output-on-failure

View File

@@ -5,7 +5,31 @@ matrix:
- os: linux - os: linux
language: cpp language: cpp
compiler: gcc compiler: gcc
env: COMPILER="g++-5" CXX_STANDARD=11 TOML_HEAD=OFF env: COMPILER="g++-4.8" CXX_STANDARD=11
addons:
apt:
sources:
- sourceline: 'ppa:ubuntu-toolchain-r/test'
- sourceline: 'ppa:mhier/libboost-latest'
packages:
- g++-4.8
- boost1.70
- os: linux
language: cpp
compiler: gcc
env: COMPILER="g++-4.9" CXX_STANDARD=11
addons:
apt:
sources:
- sourceline: 'ppa:ubuntu-toolchain-r/test'
- sourceline: 'ppa:mhier/libboost-latest'
packages:
- g++-4.9
- boost1.70
- os: linux
language: cpp
compiler: gcc
env: COMPILER="g++-5" CXX_STANDARD=11
addons: addons:
apt: apt:
sources: sources:
@@ -17,7 +41,7 @@ matrix:
- os: linux - os: linux
language: cpp language: cpp
compiler: gcc compiler: gcc
env: COMPILER="g++-6" CXX_STANDARD=11 TOML_HEAD=OFF env: COMPILER="g++-6" CXX_STANDARD=11
addons: addons:
apt: apt:
sources: sources:
@@ -29,7 +53,7 @@ matrix:
- os: linux - os: linux
language: cpp language: cpp
compiler: gcc compiler: gcc
env: COMPILER="g++-7" CXX_STANDARD=11 TOML_HEAD=OFF env: COMPILER="g++-7" CXX_STANDARD=11
addons: addons:
apt: apt:
sources: sources:
@@ -41,7 +65,7 @@ matrix:
- os: linux - os: linux
language: cpp language: cpp
compiler: gcc compiler: gcc
env: COMPILER="g++-8" CXX_STANDARD=11 TOML_HEAD=OFF env: COMPILER="g++-8" CXX_STANDARD=11
addons: addons:
apt: apt:
sources: sources:
@@ -65,7 +89,7 @@ matrix:
- os: linux - os: linux
language: cpp language: cpp
compiler: gcc compiler: gcc
env: COMPILER="g++-8" CXX_STANDARD=17 TOML_HEAD=OFF env: COMPILER="g++-8" CXX_STANDARD=14
addons: addons:
apt: apt:
sources: sources:
@@ -77,7 +101,19 @@ matrix:
- os: linux - os: linux
language: cpp language: cpp
compiler: gcc compiler: gcc
env: COMPILER="g++-8" CXX_STANDARD=17 TOML_HEAD=ON env: COMPILER="g++-8" CXX_STANDARD=17 REQUIRE_FILESYSTEM_LIBRARY=ON
addons:
apt:
sources:
- sourceline: 'ppa:ubuntu-toolchain-r/test'
- sourceline: 'ppa:mhier/libboost-latest'
packages:
- g++-8
- boost1.70
- os: linux
language: cpp
compiler: gcc
env: COMPILER="g++-8" CXX_STANDARD=17 TOML_HEAD=ON REQUIRE_FILESYSTEM_LIBRARY=ON
addons: addons:
apt: apt:
sources: sources:
@@ -89,7 +125,7 @@ matrix:
- os: linux - os: linux
language: cpp language: cpp
compiler: clang compiler: clang
env: COMPILER="clang++-3.9" CXX_STANDARD=11 TOML_HEAD=OFF env: COMPILER="clang++-3.9" CXX_STANDARD=11
addons: addons:
apt: apt:
sources: sources:
@@ -103,7 +139,7 @@ matrix:
- os: linux - os: linux
language: cpp language: cpp
compiler: clang compiler: clang
env: COMPILER="clang++-4.0" CXX_STANDARD=11 TOML_HEAD=OFF env: COMPILER="clang++-4.0" CXX_STANDARD=11
addons: addons:
apt: apt:
sources: sources:
@@ -117,7 +153,7 @@ matrix:
- os: linux - os: linux
language: cpp language: cpp
compiler: clang compiler: clang
env: COMPILER="clang++-5.0" CXX_STANDARD=11 TOML_HEAD=OFF env: COMPILER="clang++-5.0" CXX_STANDARD=11
addons: addons:
apt: apt:
sources: sources:
@@ -131,7 +167,7 @@ matrix:
- os: linux - os: linux
language: cpp language: cpp
compiler: clang compiler: clang
env: COMPILER="clang++-6.0" CXX_STANDARD=11 TOML_HEAD=OFF env: COMPILER="clang++-6.0" CXX_STANDARD=11
addons: addons:
apt: apt:
sources: sources:
@@ -145,7 +181,7 @@ matrix:
- os: linux - os: linux
language: cpp language: cpp
compiler: clang compiler: clang
env: COMPILER="clang++-7" CXX_STANDARD=11 TOML_HEAD=OFF env: COMPILER="clang++-7" CXX_STANDARD=11
addons: addons:
apt: apt:
sources: sources:
@@ -159,7 +195,7 @@ matrix:
- os: linux - os: linux
language: cpp language: cpp
compiler: clang compiler: clang
env: COMPILER="clang++-8" CXX_STANDARD=11 TOML_HEAD=OFF env: COMPILER="clang++-8" CXX_STANDARD=11
addons: addons:
apt: apt:
sources: sources:
@@ -187,7 +223,7 @@ matrix:
- os: linux - os: linux
language: cpp language: cpp
compiler: clang compiler: clang
env: COMPILER="clang++-8" CXX_STANDARD=17 TOML_HEAD=OFF env: COMPILER="clang++-8" CXX_STANDARD=14
addons: addons:
apt: apt:
sources: sources:
@@ -201,7 +237,49 @@ matrix:
- os: linux - os: linux
language: cpp language: cpp
compiler: clang compiler: clang
env: COMPILER="clang++-8" CXX_STANDARD=17 TOML_HEAD=ON env: COMPILER="clang++-8" CXX_STANDARD=17 REQUIRE_FILESYSTEM_LIBRARY=ON
addons:
apt:
sources:
- sourceline: 'ppa:ubuntu-toolchain-r/test'
- sourceline: 'ppa:mhier/libboost-latest'
- llvm-toolchain-trusty-8
packages:
- clang-8
- g++-8
- boost1.70
- os: linux
language: cpp
compiler: clang
env: COMPILER="clang++-8" CXX_STANDARD=17 TOML_HEAD=ON REQUIRE_FILESYSTEM_LIBRARY=ON
addons:
apt:
sources:
- sourceline: 'ppa:ubuntu-toolchain-r/test'
- sourceline: 'ppa:mhier/libboost-latest'
- llvm-toolchain-trusty-8
packages:
- clang-8
- g++-8
- boost1.70
- os: linux
language: cpp
compiler: clang
env: COMPILER="clang++-8" CXX_STANDARD=11 WITH_ASAN=ON
addons:
apt:
sources:
- sourceline: 'ppa:ubuntu-toolchain-r/test'
- sourceline: 'ppa:mhier/libboost-latest'
- llvm-toolchain-trusty-8
packages:
- clang-8
- g++-8
- boost1.70
- os: linux
language: cpp
compiler: clang
env: COMPILER="clang++-8" CXX_STANDARD=11 WITH_UBSAN=ON
addons: addons:
apt: apt:
sources: sources:
@@ -215,16 +293,6 @@ matrix:
- os: osx - os: osx
language: cpp language: cpp
compiler: clang compiler: clang
env: CXX_STANDARD=11
cache:
directories:
- $HOME/Library/Caches/Homebrew
addons:
homebrew:
update: true
packages:
- cmake
- boost
script: script:
- | - |
@@ -234,17 +302,36 @@ script:
tar xf cmake-3.14.5-Linux-x86_64.tar.gz -C cmake --strip-components=1 tar xf cmake-3.14.5-Linux-x86_64.tar.gz -C cmake --strip-components=1
export PATH=${TRAVIS_BUILD_DIR}/cmake/bin:${PATH} export PATH=${TRAVIS_BUILD_DIR}/cmake/bin:${PATH}
fi fi
- |
if [[ "${CXX_STANDARD}" == "" ]]; then
export CXX_STANDARD="11"
fi
- |
if [[ "${TOML_HEAD}" != "ON" ]]; then
export TOML_HEAD="OFF"
fi
- echo "TOML_HEAD = ${TOML_HEAD}"
- |
if [[ "${WITH_ASAN}" != "ON" ]]; then
export WITH_ASAN="OFF"
fi
- echo "WITH_ASAN = ${WITH_ASAN}"
- |
if [[ "${WITH_UBSAN}" != "ON" ]]; then
export WITH_UBSAN="OFF"
fi
- echo "WITH_UBSAN = ${WITH_UBSAN}"
- echo "REQUIRE_FILESYSTEM_LIBRARY = ${REQUIRE_FILESYSTEM_LIBRARY}"
- |
if [[ "${REQUIRE_FILESYSTEM_LIBRARY}" != "ON" ]]; then
export REQUIRE_FILESYSTEM_LIBRARY="OFF"
fi
- echo "REQUIRE_FILESYSTEM_LIBRARY = ${REQUIRE_FILESYSTEM_LIBRARY}"
- cmake --version - cmake --version
- mkdir build - mkdir build
- cd build - cd build
- git clone https://github.com/toml-lang/toml.git - echo "COMPILER = ${COMPILER}"
- cmake -DCMAKE_CXX_COMPILER=$COMPILER -DCMAKE_CXX_STANDARD=$CXX_STANDARD -DTOML11_USE_UNRELEASED_TOML_FEATURES=${TOML_HEAD} .. - echo "CXX_STANDARD = ${CXX_STANDARD}"
- cmake -DCMAKE_CXX_COMPILER=$COMPILER -DCMAKE_CXX_STANDARD=$CXX_STANDARD -DTOML11_REQUIRE_FILESYSTEM_LIBRARY=${REQUIRE_FILESYSTEM_LIBRARY} -Dtoml11_BUILD_TEST=ON -DTOML11_USE_UNRELEASED_TOML_FEATURES=${TOML_HEAD} -Dtoml11_TEST_WITH_ASAN=${WITH_ASAN} -Dtoml11_TEST_WITH_UBSAN=${WITH_UBSAN} ..
- make - make
- ctest --output-on-failure - ctest --output-on-failure
# https://stackoverflow.com/a/53331571
before_cache:
- |
if [[ "${TRAVIS_OS_NAME}" == "osx" ]]; then
brew cleanup
fi

View File

@@ -1,24 +1,17 @@
cmake_minimum_required(VERSION 2.8) cmake_minimum_required(VERSION 3.1)
enable_testing() enable_testing()
project(toml11) project(toml11 VERSION 3.5.0)
set(toml11_VERSION_MAYOR 3) option(toml11_BUILD_TEST "Build toml tests" OFF)
set(toml11_VERSION_MINOR 2) option(toml11_TEST_WITH_ASAN "use LLVM address sanitizer" OFF)
set(toml11_VERSION_PATCH 0) option(toml11_TEST_WITH_UBSAN "use LLVM undefined behavior sanitizer" OFF)
set(toml11_VERSION
"${toml11_VERSION_MAYOR}.${toml11_VERSION_MINOR}.${toml11_VERSION_PATCH}"
)
option(toml11_BUILD_TEST "Build toml tests" ON)
include(CheckCXXCompilerFlag) include(CheckCXXCompilerFlag)
if("${CMAKE_VERSION}" VERSION_GREATER 3.1) if("${CMAKE_VERSION}" VERSION_GREATER 3.1)
set(CMAKE_CXX_EXTENSIONS OFF) set(CMAKE_CXX_STANDARD 11 CACHE STRING "The C++ standard whose features are requested to build all targets.")
if(NOT DEFINED CMAKE_CXX_STANDARD) set(CMAKE_CXX_STANDARD_REQUIRED ON CACHE BOOL "Boolean describing whether the value of CXX_STANDARD is a requirement.")
set(CMAKE_CXX_STANDARD 11) set(CMAKE_CXX_EXTENSIONS OFF CACHE BOOL "Boolean specifying whether compiler specific extensions are requested.")
endif()
set(CXX_STANDARD_REQUIRED ON)
else() else()
# Manually check for C++11 compiler flag. # Manually check for C++11 compiler flag.
CHECK_CXX_COMPILER_FLAG("-std=c++11" COMPILER_SUPPORTS_CXX11) CHECK_CXX_COMPILER_FLAG("-std=c++11" COMPILER_SUPPORTS_CXX11)
@@ -44,6 +37,11 @@ else()
endif() endif()
endif() endif()
if(MSVC)
add_definitions("/Zc:__cplusplus") # define __cplusplus value correctly
add_definitions("/utf-8") # enable to use u8"" literal
endif()
# Set some common directories # Set some common directories
include(GNUInstallDirs) include(GNUInstallDirs)
set(toml11_install_cmake_dir ${CMAKE_INSTALL_LIBDIR}/cmake/toml11) set(toml11_install_cmake_dir ${CMAKE_INSTALL_LIBDIR}/cmake/toml11)

234
README.md
View File

@@ -1,6 +1,7 @@
toml11 toml11
====== ======
[![Build Status on GitHub Actions](https://github.com/ToruNiina/toml11/workflows/build/badge.svg)](https://github.com/ToruNiina/toml11/actions)
[![Build Status on TravisCI](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 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 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) [![Build status on CircleCI](https://circleci.com/gh/ToruNiina/toml11/tree/master.svg?style=svg)](https://circleci.com/gh/ToruNiina/toml11/tree/master)
@@ -8,16 +9,16 @@ toml11
[![License](https://img.shields.io/github/license/ToruNiina/toml11.svg?style=flat)](LICENSE) [![License](https://img.shields.io/github/license/ToruNiina/toml11.svg?style=flat)](LICENSE)
[![DOI](https://zenodo.org/badge/DOI/10.5281/zenodo.1209136.svg)](https://doi.org/10.5281/zenodo.1209136) [![DOI](https://zenodo.org/badge/DOI/10.5281/zenodo.1209136.svg)](https://doi.org/10.5281/zenodo.1209136)
toml11 is a C++11 header-only toml parser/encoder depending only on C++ standard library. toml11 is a C++11 (or later) header-only toml parser/encoder depending only on C++ standard library.
compatible to the latest version of - It is compatible to the latest version of [TOML v1.0.0-rc.2](https://toml.io/en/v1.0.0-rc.2).
[TOML v0.5.0](https://github.com/toml-lang/toml/blob/master/versions/en/toml-v0.5.0.md) - It is one of the most TOML standard compliant libraries, tested with [the language agnostic test suite for TOML parsers by BurntSushi](https://github.com/BurntSushi/toml-test).
after version 2.0.0. - It shows highly informative error messages. You can see the error messages about invalid files at [CircleCI](https://circleci.com/gh/ToruNiina/toml11).
- It has configurable container. You can use any random-access containers and key-value maps as backend containers.
It passes [the language agnostic test suite for TOML parsers by BurntSushi](https://github.com/BurntSushi/toml-test). - It optionally preserves comments without any overhead.
Not only the test suite itself, a TOML reader/encoder also runs on [CircleCI](https://circleci.com/gh/ToruNiina/toml11). - It has configurable serializer that supports comments, inline tables, literal strings and multiline strings.
You can see the error messages about invalid files and serialization results of valid files at - It supports user-defined type conversion from/into toml values.
[CircleCI](https://circleci.com/gh/ToruNiina/toml11). - It correctly handles UTF-8 sequences, with or without BOM, both on posix and Windows.
## Example ## Example
@@ -27,15 +28,25 @@ You can see the error messages about invalid files and serialization results of
int main() int main()
{ {
const auto data = toml::parse("example.toml"); auto data = toml::parse("example.toml");
// title = "an example toml file" // find a value with the specified type from a table
std::string title = toml::find<std::string>(data, "title"); std::string title = toml::find<std::string>(data, "title");
std::cout << "the title is " << title << std::endl;
// nums = [1, 2, 3, 4, 5] // convert the whole array into any container automatically
std::vector<int> nums = toml::find<std::vector<int>>(data, "nums"); std::vector<int> nums = toml::find<std::vector<int>>(data, "nums");
std::cout << "the length of `nums` is" << nums.size() << std::endl;
// access with STL-like manner
if(not data.at("a").contains("b"))
{
data["a"]["b"] = "c";
}
// pass a fallback
std::string name = toml::find_or<std::string>(data, "name", "not found");
// width-dependent formatting
std::cout << std::setw(80) << data << std::endl;
return 0; return 0;
} }
@@ -84,7 +95,7 @@ int main()
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 <toml.hpp> // that's all! now you can use it.
#include <iostream> #include <iostream>
int main() int main()
@@ -99,6 +110,8 @@ int main()
The convenient way is to add this repository as a git-submodule or to install The convenient way is to add this repository as a git-submodule or to install
it in your system by CMake. it in your system by CMake.
Note for MSVC: We recommend to set `/Zc:__cplusplus` to detect C++ version correctly.
## Decoding a toml file ## Decoding a toml file
To parse a toml file, the only thing you have to do is To parse a toml file, the only thing you have to do is
@@ -109,6 +122,9 @@ const std::string fname("sample.toml");
const toml::value data = toml::parse(fname); const toml::value data = toml::parse(fname);
``` ```
As required by the TOML specification, the top-level value is always a table.
You can find a value inside it, cast it into a table explicitly, and insert it as a value into other `toml::value`.
If it encounters an error while opening a file, it will throw `std::runtime_error`. If it encounters an error while opening a file, it will throw `std::runtime_error`.
You can also pass a `std::istream` to the `toml::parse` function. You can also pass a `std::istream` to the `toml::parse` function.
@@ -166,7 +182,7 @@ what(): [error] bad time: should be HH:MM:SS.subsec
--> ./datetime-malformed-no-secs.toml --> ./datetime-malformed-no-secs.toml
1 | no-secs = 1987-07-05T17:45Z 1 | no-secs = 1987-07-05T17:45Z
| ^------- HH:MM:SS.subsec | ^------- HH:MM:SS.subsec
| |
Hint: pass: 1979-05-27T07:32:00, 1979-05-27 07:32:00.999999 Hint: pass: 1979-05-27T07:32:00, 1979-05-27 07:32:00.999999
Hint: fail: 1979-05-27T7:32:00, 1979-05-27 17:32 Hint: fail: 1979-05-27T7:32:00, 1979-05-27 17:32
``` ```
@@ -256,11 +272,11 @@ shape = "round"
``` cpp ``` cpp
const auto data = toml::parse("fruit.toml"); const auto data = toml::parse("fruit.toml");
const auto& fruit = toml::find(data, "fruit"); const auto& fruit = toml::find(data, "fruit");
const auto name = toml::find<std::string>(fruit, "apple"); const auto name = toml::find<std::string>(fruit, "name");
const auto& physical = toml::find(fruit, "physical"); const auto& physical = toml::find(fruit, "physical");
const auto color = toml::find<std::string>(fruit, "color"); const auto color = toml::find<std::string>(physical, "color");
const auto shape = toml::find<std::string>(fruit, "shape"); const auto shape = toml::find<std::string>(physical, "shape");
``` ```
Here, variable `fruit` is a `toml::value` and can be used as the first argument Here, variable `fruit` is a `toml::value` and can be used as the first argument
@@ -274,10 +290,44 @@ const auto color = toml::find<std::string>(data, "fruit", "physical", "color");
const auto shape = toml::find<std::string>(data, "fruit", "physical", "shape"); const auto shape = toml::find<std::string>(data, "fruit", "physical", "shape");
``` ```
### Finding a value in an array
You can find n-th value in an array by `toml::find`.
```toml
values = ["foo", "bar", "baz"]
```
``` cpp
const auto data = toml::parse("sample.toml");
const auto values = toml::find(data, "values");
const auto bar = toml::find<std::string>(values, 1);
```
`toml::find` can also search array recursively.
```cpp
const auto data = toml::parse("fruit.toml");
const auto bar = toml::find<std::string>(data, "values", 1);
```
Before calling `toml::find`, you can check if a value corresponding to a key
exists. You can use both `bool toml::value::contains(const key&) const` and
`std::size_t toml::value::count(const key&) const`. Those behaves like the
`std::map::contains` and `std::map::count`.
```cpp
const auto data = toml::parse("fruit.toml");
if(data.contains("fruit") && data.at("fruit").count("physical") != 0)
{
// ...
}
```
### In case of error ### In case of error
If the value does not exist, `toml::find` throws an error with the location of If the value does not exist, `toml::find` throws `std::out_of_range` with the
the table. location of the table.
```console ```console
terminate called after throwing an instance of 'std::out_of_range' terminate called after throwing an instance of 'std::out_of_range'
@@ -287,11 +337,6 @@ terminate called after throwing an instance of 'std::out_of_range'
| ~~~~~ in this table | ~~~~~ in this table
``` ```
**Note**: It is recommended to find a table as `toml::value` because it has much information
compared to `toml::table`, which is an alias of
`std::unordered_map<std::string, toml::value>`. Since `toml::table` does not have
any information about toml file, such as where the table was defined in the file.
---- ----
If the specified type differs from the actual value contained, it throws If the specified type differs from the actual value contained, it throws
@@ -423,6 +468,24 @@ if(answer.is_integer() && answer.as_integer(std::nothrow) == 42)
If `std::nothrow` is passed, the functions are marked as noexcept. If `std::nothrow` is passed, the functions are marked as noexcept.
By casting a `toml::value` into an array or a table, you can iterate over the
elements.
```cpp
const auto data = toml::parse("example.toml");
std::cout << "keys in the top-level table are the following: \n";
for(const auto& [k, v] : data.as_table())
{
std::cout << k << '\n';
}
const auto& fruits = toml::find(data, "fruits");
for(const auto& v : fruits.as_array())
{
std::cout << toml::find<std::string>(v, "name") << '\n';
}
```
The full list of the functions is below. The full list of the functions is below.
```cpp ```cpp
@@ -869,14 +932,25 @@ toml::value v(toml::local_time(std::chrono::hours(10)));
``` ```
You can construct an array object not only from `initializer_list`, but also You can construct an array object not only from `initializer_list`, but also
from STL containers. from STL containers. In that case, the element type must be convertible to
`toml::value`.
```cpp ```cpp
std::vector<int> vec{1,2,3,4,5}; std::vector<int> vec{1,2,3,4,5};
toml::value v = vec; toml::value v(vec);
``` ```
All the elements of `initializer_list` should be convertible into `toml::value`. When you construct an array value, all the elements of `initializer_list`
must be convertible into `toml::value`.
If a `toml::value` has an array, you can `push_back` an element in it.
```cpp
toml::value v{1,2,3,4,5};
v.push_back(6);
```
`emplace_back` also works.
## Preserving comments ## Preserving comments
@@ -1093,7 +1167,7 @@ const auto data = toml::parse("example.toml");
const foo f = toml::find<ext::foo>(data, "foo"); const foo f = toml::find<ext::foo>(data, "foo");
``` ```
There are 2 ways to use `toml::get` with the types that you defined. There are 3 ways to use `toml::get` with the types that you defined.
The first one is to implement `from_toml(const toml::value&)` member function. The first one is to implement `from_toml(const toml::value&)` member function.
@@ -1120,7 +1194,31 @@ struct foo
In this way, because `toml::get` first constructs `foo` without arguments, In this way, because `toml::get` first constructs `foo` without arguments,
the type should be default-constructible. the type should be default-constructible.
The second is to implement specialization of `toml::from` for your type. The second is to implement `constructor(const toml::value&)`.
```cpp
namespace ext
{
struct foo
{
explicit foo(const toml::value& v)
: a(toml::find<int>(v, "a")), b(toml::find<double>(v, "b")),
c(toml::find<std::string>(v, "c"))
{}
int a;
double b;
std::string c;
};
} // ext
```
Note that implicit default constructor declaration will be suppressed
when a constructor is defined. If you want to use the struct (here, `foo`)
in a container (e.g. `std::vector<foo>`), you may need to define default
constructor explicitly.
The third is to implement specialization of `toml::from` for your type.
```cpp ```cpp
namespace ext namespace ext
@@ -1349,7 +1447,7 @@ const toml::source_location loc = v.location();
## Exceptions ## Exceptions
All the exceptions thrown by toml11 inherits `toml::exception` that inherits The following `exception` classes inherits `toml::exception` that inherits
`std::exception`. `std::exception`.
```cpp ```cpp
@@ -1376,6 +1474,16 @@ struct exception : public std::exception
It represents where the error occurs. It represents where the error occurs.
`syntax_error` will be thrown from `toml::parse` and `_toml` literal.
`type_error` will be thrown from `toml::get/find`, `toml::value::as_xxx()`, and
other functions that takes a content inside of `toml::value`.
Note that, currently, from `toml::value::at()` and `toml::find(value, key)`
may throw an `std::out_of_range` that does not inherits `toml::exception`.
Also, in some cases, most likely in the file open error, it will throw an
`std::runtime_error`.
## Colorize Error Messages ## Colorize Error Messages
By defining `TOML11_COLORIZE_ERROR_MESSAGE`, the error messages from By defining `TOML11_COLORIZE_ERROR_MESSAGE`, the error messages from
@@ -1567,13 +1675,8 @@ not capable of representing a Local Time independent from a specific day.
## Unreleased TOML features ## Unreleased TOML features
There are some unreleased features in toml-lang/toml:master. Since TOML v1.0.0-rc.1 has been released, those features are now activated by
Currently, the following features are available after defining default. We no longer need to define `TOML11_USE_UNRELEASED_FEATURES`.
`TOML11_USE_UNRELEASED_TOML_FEATURES` macro flag.
To use those features, `#define` `TOML11_USE_UNRELEASED_TOML_FEATURES` before
including `toml.hpp` or pass `-DTOML11_USE_UNRELEASED_TOML_FEATURES` to your
compiler.
- Leading zeroes in exponent parts of floats are permitted. - Leading zeroes in exponent parts of floats are permitted.
- e.g. `1.0e+01`, `5e+05` - e.g. `1.0e+01`, `5e+05`
@@ -1583,10 +1686,10 @@ compiler.
- Allow heterogeneous arrays - Allow heterogeneous arrays
- [toml-lang/toml/PR/676](https://github.com/toml-lang/toml/pull/676) - [toml-lang/toml/PR/676](https://github.com/toml-lang/toml/pull/676)
### Note about heterogeneous arrays ## Note about heterogeneous arrays
Although `toml::parse` allows heterogeneous arrays, constructor of `toml::value` Although `toml::parse` allows heterogeneous arrays, constructor of `toml::value`
does not. does not. Here the reason is explained.
```cpp ```cpp
// this won't be compiled // this won't be compiled
@@ -1595,8 +1698,10 @@ toml::value v{
} }
``` ```
There is a workaround for this issue. By explicitly converting values into There is a workaround for this. By explicitly converting values into
`toml::value`, you can initialize `toml::value` with a heterogeneous array. `toml::value`, you can initialize `toml::value` with a heterogeneous array.
Also, you can first initialize a `toml::value` with an array and then
`push_back` into it.
```cpp ```cpp
// OK! // OK!
@@ -1604,6 +1709,17 @@ toml::value v{
toml::value("foo"), toml::value(3.14), toml::value(42), toml::value("foo"), toml::value(3.14), toml::value(42),
toml::value{1,2,3,4,5}, toml::value{{"key", "value"}} toml::value{1,2,3,4,5}, toml::value{{"key", "value"}}
} }
// OK!
toml::value v(toml::array{});
v.push_back("foo");
v.push_back(3.14);
// OK!
toml::array a;
a.push_back("foo");
a.push_back(3.14);
toml::value v(std::move(a));
``` ```
The reason why the first example is not allowed is the following. The reason why the first example is not allowed is the following.
@@ -1632,15 +1748,14 @@ This means that the above C++ code makes constructor's overload resolution
ambiguous. So a constructor that allows both "table as an initializer-list" and ambiguous. So a constructor that allows both "table as an initializer-list" and
"heterogeneous array as an initializer-list" cannot be implemented. "heterogeneous array as an initializer-list" cannot be implemented.
Thus, although it is painful, you need to explicitly cast values into Thus, although it is painful, we need to explicitly cast values into
`toml::value` when you initialize heterogeneous array in C++ code. `toml::value` when you initialize heterogeneous array in a C++ code.
```cpp ```cpp
// You need to do this when you want to initialize hetero array.
toml::value v{ toml::value v{
toml::value("foo"), toml::value(3.14), toml::value(42), toml::value("foo"), toml::value(3.14), toml::value(42),
toml::value{1,2,3,4,5}, toml::value{{"key", "value"}} toml::value{1,2,3,4,5}, toml::value{{"key", "value"}}
} };
``` ```
## Breaking Changes from v2 ## Breaking Changes from v2
@@ -1679,14 +1794,13 @@ Such a big change will not happen in the coming years.
## Running Tests ## Running Tests
To run test codes, you need to clone toml-lang/toml repository under `build/` directory After cloning this repository, run the following command (thank you @jwillikers
because some of the test codes read a file in the repository. for automating test set fetching!).
```sh ```sh
$ mkdir build $ mkdir build
$ cd build $ cd build
$ git clone https://github.com/toml-lang/toml.git $ cmake .. -Dtoml11_BUILD_TEST=ON
$ cmake ..
$ make $ make
$ make test $ make test
``` ```
@@ -1719,11 +1833,27 @@ I appreciate the help of the contributors who introduced the great feature to th
- Suppress warnings in Debug mode - Suppress warnings in Debug mode
- OGAWA Kenichi (@kenichiice) - OGAWA Kenichi (@kenichiice)
- Suppress warnings on intel compiler - Suppress warnings on intel compiler
- Jordan Williams (@jwillikers)
- Fixed clang range-loop-analysis warnings
- Fixed feature test macro to suppress -Wundef
- Use cache variables in CMakeLists.txt
- Automate test set fetching, update and refactor CMakeLists.txt
- Scott McCaskill
- Parse 9 digits (nanoseconds) of fractional seconds in a `local_time`
- Shu Wang (@halfelf)
- fix "Finding a value in an array" example in README
- @maass-tv and @SeverinLeonhardt
- Fix MSVC warning C4866
- OGAWA KenIchi (@kenichiice)
- Fix include path in README
- Mohammed Alyousef (@MoAlyousef)
- Made testing optional in CMake
## 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-2019 Toru Niina - Copyright (c) 2017-2020 Toru Niina
All rights reserved. All rights reserved.

View File

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

View File

@@ -1,3 +1,12 @@
include(ExternalProject)
ExternalProject_Add(toml
SOURCE_DIR ${CMAKE_CURRENT_BINARY_DIR}/toml
GIT_REPOSITORY https://github.com/toml-lang/toml
GIT_TAG v0.5.0
CONFIGURE_COMMAND ""
BUILD_COMMAND ""
INSTALL_COMMAND "")
set(TEST_NAMES set(TEST_NAMES
test_datetime test_datetime
test_string test_string
@@ -39,6 +48,17 @@ set(TEST_NAMES
CHECK_CXX_COMPILER_FLAG("-Wall" COMPILER_SUPPORTS_WALL) CHECK_CXX_COMPILER_FLAG("-Wall" COMPILER_SUPPORTS_WALL)
CHECK_CXX_COMPILER_FLAG("-Wextra" COMPILER_SUPPORTS_WEXTRA) CHECK_CXX_COMPILER_FLAG("-Wextra" COMPILER_SUPPORTS_WEXTRA)
CHECK_CXX_COMPILER_FLAG("-Wpedantic" COMPILER_SUPPORTS_WPEDANTIC) CHECK_CXX_COMPILER_FLAG("-Wpedantic" COMPILER_SUPPORTS_WPEDANTIC)
CHECK_CXX_COMPILER_FLAG("-Werror" COMPILER_SUPPORTS_WERROR)
CHECK_CXX_COMPILER_FLAG("-Wsign-conversion" COMPILER_SUPPORTS_WSIGN_CONVERSION)
CHECK_CXX_COMPILER_FLAG("-Wconversion" COMPILER_SUPPORTS_WCONVERSION)
CHECK_CXX_COMPILER_FLAG("-Wduplicated-cond" COMPILER_SUPPORTS_WDUPLICATED_COND)
CHECK_CXX_COMPILER_FLAG("-Wduplicated-branches" COMPILER_SUPPORTS_WDUPLICATED_BRANCHES)
CHECK_CXX_COMPILER_FLAG("-Wlogical-op" COMPILER_SUPPORTS_WLOGICAL_OP)
CHECK_CXX_COMPILER_FLAG("-Wuseless-cast" COMPILER_SUPPORTS_WUSELESS_CAST)
CHECK_CXX_COMPILER_FLAG("-Wdouble-promotion" COMPILER_SUPPORTS_WDOUBLE_PROMOTION)
CHECK_CXX_COMPILER_FLAG("-Wrange-loop-analysis" COMPILER_SUPPORTS_WRANGE_LOOP_ANALYSIS)
CHECK_CXX_COMPILER_FLAG("-Wundef" COMPILER_SUPPORTS_WUNDEF)
if(COMPILER_SUPPORTS_WALL) if(COMPILER_SUPPORTS_WALL)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall") set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall")
@@ -49,6 +69,36 @@ endif()
if(COMPILER_SUPPORTS_WPEDANTIC) if(COMPILER_SUPPORTS_WPEDANTIC)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wpedantic") set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wpedantic")
endif() endif()
if(COMPILER_SUPPORTS_WERROR)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Werror")
endif()
if(COMPILER_SUPPORTS_WSIGN_CONVERSION)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wsign-conversion")
endif()
if(COMPILER_SUPPORTS_WCONVERSION)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wconversion")
endif()
if(COMPILER_SUPPORTS_WDUPLICATED_COND)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wduplicated-cond")
endif()
if(COMPILER_SUPPORTS_WDUPLICATED_BRANCHES)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wduplicated-branches")
endif()
if(COMPILER_SUPPORTS_WLOGICAL_OP)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wlogical-op")
endif()
if(COMPILER_SUPPORTS_WUSELESS_CAST)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wuseless-cast")
endif()
if(COMPILER_SUPPORTS_WDOUBLE_PROMOTION)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wdouble-promotion")
endif()
if(COMPILER_SUPPORTS_WRANGE_LOOP_ANALYSIS)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wrange-loop-analysis")
endif()
if(COMPILER_SUPPORTS_WUNDEF)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wundef")
endif()
option(TOML11_USE_UNRELEASED_TOML_FEATURES option(TOML11_USE_UNRELEASED_TOML_FEATURES
"use features in toml-lang/toml master while testing" OFF) "use features in toml-lang/toml master while testing" OFF)
@@ -100,11 +150,61 @@ find_package(Boost COMPONENTS unit_test_framework REQUIRED)
add_definitions(-DBOOST_TEST_DYN_LINK) add_definitions(-DBOOST_TEST_DYN_LINK)
add_definitions(-DUNITTEST_FRAMEWORK_LIBRARY_EXIST) add_definitions(-DUNITTEST_FRAMEWORK_LIBRARY_EXIST)
# check which standard library implementation is used. If libstdc++ is used,
# it will fail to compile. It compiles if libc++ is used.
include(CheckCXXSourceCompiles)
check_cxx_source_compiles("
#include <cstddef>
#ifdef __GLIBCXX__
static_assert(false);
#endif
int main() {
return 0;
}" TOML11_WITH_LIBCXX_LIBRARY)
# LLVM 8 requires -lc++fs if compiled with libc++ to use <filesystem>.
# LLVM 9+ does not require any special library.
# GCC 8 requires -lstdc++fs. GCC 9+ does not require it.
#
# Yes, we can check the version of the compiler used in the current build
# directly in CMake. But, in most cases, clang build uses libstdc++ as the
# standard library implementation and it makes the condition complicated.
# In many environment, the default installed C++ compiler is GCC and libstdc++
# is installed along with it. In most build on such an environment, even if we
# chose clang as the C++ compiler, still libstdc++ is used. Checking default
# gcc version makes the condition complicated.
# The purpose of this file is to compile tests. We know the environment on which
# the tests run. We can set this option and, I think, it is easier and better.
option(TOML11_REQUIRE_FILESYSTEM_LIBRARY "need to link -lstdc++fs or -lc++fs" OFF)
foreach(TEST_NAME ${TEST_NAMES}) foreach(TEST_NAME ${TEST_NAMES})
add_executable(${TEST_NAME} ${TEST_NAME}.cpp) add_executable(${TEST_NAME} ${TEST_NAME}.cpp)
target_link_libraries(${TEST_NAME} ${Boost_UNIT_TEST_FRAMEWORK_LIBRARY} toml11::toml11) target_link_libraries(${TEST_NAME} ${Boost_UNIT_TEST_FRAMEWORK_LIBRARY} toml11::toml11)
# to compile tests with <filesystem>...
if(TOML11_REQUIRE_FILESYSTEM_LIBRARY)
if(TOML11_WITH_LIBCXX_LIBRARY)
target_link_libraries(${TEST_NAME} "c++fs")
else()
target_link_libraries(${TEST_NAME} "stdc++fs")
endif()
endif()
target_include_directories(${TEST_NAME} PRIVATE ${Boost_INCLUDE_DIRS}) target_include_directories(${TEST_NAME} PRIVATE ${Boost_INCLUDE_DIRS})
add_test(NAME ${TEST_NAME} COMMAND ${TEST_NAME} WORKING_DIRECTORY ${PROJECT_BINARY_DIR})
if(CMAKE_CXX_COMPILER_ID STREQUAL "Clang")
if(toml11_TEST_WITH_ASAN)
set_target_properties(${TEST_NAME} PROPERTIES
COMPILE_FLAGS "-fsanitize=address -fno-omit-frame-pointer"
LINK_FLAGS "-fsanitize=address -fno-omit-frame-pointer")
elseif(toml11_TEST_WITH_UBSAN)
set_target_properties(${TEST_NAME} PROPERTIES
COMPILE_FLAGS "-fsanitize=undefined"
LINK_FLAGS "-fsanitize=undefined")
endif()
endif()
add_test(NAME ${TEST_NAME} COMMAND ${TEST_NAME} WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR})
# Set the PATH to be able to find Boost DLL # Set the PATH to be able to find Boost DLL
if(WIN32) if(WIN32)
@@ -115,8 +215,15 @@ foreach(TEST_NAME ${TEST_NAMES})
endif() endif()
endforeach(TEST_NAME) endforeach(TEST_NAME)
# this test is to check it compiles. it will not run # this test is to check it compiles. it will not run
add_executable(test_multiple_translation_unit add_executable(test_multiple_translation_unit
test_multiple_translation_unit_1.cpp test_multiple_translation_unit_1.cpp
test_multiple_translation_unit_2.cpp) test_multiple_translation_unit_2.cpp)
target_link_libraries(test_multiple_translation_unit toml11::toml11) target_link_libraries(test_multiple_translation_unit toml11::toml11)
if(WIN32)
add_executable(test_windows test_windows.cpp)
target_link_libraries(test_windows toml11::toml11)
endif()

View File

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

View File

@@ -11,7 +11,7 @@
BOOST_AUTO_TEST_CASE(test_comment_before) BOOST_AUTO_TEST_CASE(test_comment_before)
{ {
{ {
const std::string file = u8R"( const std::string file = R"(
# comment for a. # comment for a.
a = 42 a = 42
# comment for b. # comment for b.
@@ -24,12 +24,12 @@ BOOST_AUTO_TEST_CASE(test_comment_before)
const auto& b = toml::find(v, "b"); const auto& b = toml::find(v, "b");
BOOST_TEST(a.comments().size() == 1u); BOOST_TEST(a.comments().size() == 1u);
BOOST_TEST(a.comments().front() == u8" comment for a."); BOOST_TEST(a.comments().front() == " comment for a.");
BOOST_TEST(b.comments().size() == 1u); BOOST_TEST(b.comments().size() == 1u);
BOOST_TEST(b.comments().front() == u8" comment for b."); BOOST_TEST(b.comments().front() == " comment for b.");
} }
{ {
const std::string file = u8R"( const std::string file = R"(
# comment for a. # comment for a.
# another comment for a. # another comment for a.
a = 42 a = 42
@@ -45,18 +45,18 @@ BOOST_AUTO_TEST_CASE(test_comment_before)
const auto& b = toml::find(v, "b"); const auto& b = toml::find(v, "b");
BOOST_TEST(a.comments().size() == 2u); BOOST_TEST(a.comments().size() == 2u);
BOOST_TEST(a.comments().front() == u8" comment for a."); BOOST_TEST(a.comments().front() == " comment for a.");
BOOST_TEST(a.comments().back() == u8" another comment for a."); BOOST_TEST(a.comments().back() == " another comment for a.");
BOOST_TEST(b.comments().size() == 2u); BOOST_TEST(b.comments().size() == 2u);
BOOST_TEST(b.comments().front() == u8" comment for b."); BOOST_TEST(b.comments().front() == " comment for b.");
BOOST_TEST(b.comments().back() == u8" also comment for b."); BOOST_TEST(b.comments().back() == " also comment for b.");
} }
} }
BOOST_AUTO_TEST_CASE(test_comment_inline) BOOST_AUTO_TEST_CASE(test_comment_inline)
{ {
{ {
const std::string file = u8R"( const std::string file = R"(
a = 42 # comment for a. a = 42 # comment for a.
b = "baz" # comment for b. b = "baz" # comment for b.
)"; )";
@@ -68,12 +68,12 @@ BOOST_AUTO_TEST_CASE(test_comment_inline)
const auto& b = toml::find(v, "b"); const auto& b = toml::find(v, "b");
BOOST_TEST(a.comments().size() == 1u); BOOST_TEST(a.comments().size() == 1u);
BOOST_TEST(a.comments().front() == u8" comment for a."); BOOST_TEST(a.comments().front() == " comment for a.");
BOOST_TEST(b.comments().size() == 1u); BOOST_TEST(b.comments().size() == 1u);
BOOST_TEST(b.comments().front() == u8" comment for b."); BOOST_TEST(b.comments().front() == " comment for b.");
} }
{ {
const std::string file = u8R"( const std::string file = R"(
a = [ a = [
42, 42,
] # comment for a. ] # comment for a.
@@ -90,18 +90,18 @@ BOOST_AUTO_TEST_CASE(test_comment_inline)
const auto& b0 = b.as_array().at(0); const auto& b0 = b.as_array().at(0);
BOOST_TEST(a.comments().size() == 1u); BOOST_TEST(a.comments().size() == 1u);
BOOST_TEST(a.comments().front() == u8" comment for a."); BOOST_TEST(a.comments().front() == " comment for a.");
BOOST_TEST(b.comments().size() == 1u); BOOST_TEST(b.comments().size() == 1u);
BOOST_TEST(b.comments().front() == u8" this is a comment for b."); BOOST_TEST(b.comments().front() == " this is a comment for b.");
BOOST_TEST(b0.comments().size() == 1u); BOOST_TEST(b0.comments().size() == 1u);
BOOST_TEST(b0.comments().front() == u8" this is not a comment for b, but \"bar\""); BOOST_TEST(b0.comments().front() == " this is not a comment for b, but \"bar\"");
} }
} }
BOOST_AUTO_TEST_CASE(test_comment_both) BOOST_AUTO_TEST_CASE(test_comment_both)
{ {
{ {
const std::string file = u8R"( const std::string file = R"(
# comment for a. # comment for a.
a = 42 # inline comment for a. a = 42 # inline comment for a.
# comment for b. # comment for b.
@@ -122,25 +122,25 @@ BOOST_AUTO_TEST_CASE(test_comment_both)
const auto& c0 = c.as_array().at(0); const auto& c0 = c.as_array().at(0);
BOOST_TEST(a.comments().size() == 2u); BOOST_TEST(a.comments().size() == 2u);
BOOST_TEST(a.comments().front() == u8" comment for a."); BOOST_TEST(a.comments().front() == " comment for a.");
BOOST_TEST(a.comments().back() == u8" inline comment for a."); BOOST_TEST(a.comments().back() == " inline comment for a.");
BOOST_TEST(b.comments().size() == 2u); BOOST_TEST(b.comments().size() == 2u);
BOOST_TEST(b.comments().front() == u8" comment for b."); BOOST_TEST(b.comments().front() == " comment for b.");
BOOST_TEST(b.comments().back() == u8" inline comment for b."); BOOST_TEST(b.comments().back() == " inline comment for b.");
BOOST_TEST(c.comments().size() == 2u); BOOST_TEST(c.comments().size() == 2u);
BOOST_TEST(c.comments().front() == u8" comment for c."); BOOST_TEST(c.comments().front() == " comment for c.");
BOOST_TEST(c.comments().back() == u8" another comment for c."); BOOST_TEST(c.comments().back() == " another comment for c.");
BOOST_TEST(c0.comments().size() == 2u); BOOST_TEST(c0.comments().size() == 2u);
BOOST_TEST(c0.comments().front() == u8" comment for the first element."); BOOST_TEST(c0.comments().front() == " comment for the first element.");
BOOST_TEST(c0.comments().back() == u8" this also."); BOOST_TEST(c0.comments().back() == " this also.");
} }
} }
BOOST_AUTO_TEST_CASE(test_discard_comment) BOOST_AUTO_TEST_CASE(test_discard_comment)
{ {
const std::string file = u8R"( const std::string file = R"(
# comment for a. # comment for a.
a = 42 # inline comment for a. a = 42 # inline comment for a.
# comment for b. # comment for b.

View File

@@ -80,13 +80,13 @@ BOOST_AUTO_TEST_CASE(test_detect_conflicting_value)
BOOST_AUTO_TEST_CASE(test_detect_inhomogeneous_array) BOOST_AUTO_TEST_CASE(test_detect_inhomogeneous_array)
{ {
#ifdef TOML11_USE_UNRELEASED_TOML_FEATURES #ifdef TOML11_DISALLOW_HETEROGENEOUS_ARRAYS
BOOST_TEST_MESSAGE("heterogeneous array will be allowed in the next release");
#else
std::istringstream stream(std::string( std::istringstream stream(std::string(
"a = [1, 1.0]\n" "a = [1, 1.0]\n"
)); ));
BOOST_CHECK_THROW(toml::parse(stream), toml::syntax_error); BOOST_CHECK_THROW(toml::parse(stream), toml::syntax_error);
#else
BOOST_TEST_MESSAGE("After v1.0.0-rc.1, heterogeneous arrays are allowed");
#endif #endif
} }

View File

@@ -44,6 +44,16 @@ struct qux
int a; int a;
std::string b; std::string b;
}; };
struct foobar
{
// via constructor
explicit foobar(const toml::value& v)
: a(toml::find<int>(v, "a")), b(toml::find<std::string>(v, "b"))
{}
int a;
std::string b;
};
} // extlib } // extlib
namespace toml namespace toml
@@ -122,6 +132,17 @@ struct qux
int a; int a;
std::string b; std::string b;
}; };
struct foobar
{
template<typename C, template<typename ...> class M, template<typename ...> class A>
explicit foobar(const toml::basic_value<C, M, A>& v)
: a(toml::find<int>(v, "a")), b(toml::find<std::string>(v, "b"))
{}
int a;
std::string b;
};
} // extlib2 } // extlib2
namespace toml namespace toml
@@ -284,6 +305,27 @@ BOOST_AUTO_TEST_CASE(test_conversion_one_way)
} }
} }
BOOST_AUTO_TEST_CASE(test_conversion_via_constructor)
{
{
const toml::value v{{"a", 42}, {"b", "foobar"}};
const auto foobar = toml::get<extlib::foobar>(v);
BOOST_TEST(foobar.a == 42);
BOOST_TEST(foobar.b == "foobar");
}
{
const toml::basic_value<toml::discard_comments, std::map> v{
{"a", 42}, {"b", "foobar"}
};
const auto foobar = toml::get<extlib2::foobar>(v);
BOOST_TEST(foobar.a == 42);
BOOST_TEST(foobar.b == "foobar");
}
}
BOOST_AUTO_TEST_CASE(test_recursive_conversion) BOOST_AUTO_TEST_CASE(test_recursive_conversion)
{ {
{ {
@@ -384,5 +426,122 @@ BOOST_AUTO_TEST_CASE(test_recursive_conversion)
BOOST_TEST(bars.at(2).b == "quux"); BOOST_TEST(bars.at(2).b == "quux");
BOOST_TEST(bars.at(3).b == "foobar"); BOOST_TEST(bars.at(3).b == "foobar");
} }
// via constructor
{
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 foobars = toml::get<std::vector<extlib::foobar>>(v);
BOOST_TEST(foobars.size() == 4ul);
BOOST_TEST(foobars.at(0).a == 42);
BOOST_TEST(foobars.at(1).a == 43);
BOOST_TEST(foobars.at(2).a == 44);
BOOST_TEST(foobars.at(3).a == 45);
BOOST_TEST(foobars.at(0).b == "baz");
BOOST_TEST(foobars.at(1).b == "qux");
BOOST_TEST(foobars.at(2).b == "quux");
BOOST_TEST(foobars.at(3).b == "foobar");
}
{
const auto foobars = toml::get<std::vector<extlib2::foobar>>(v);
BOOST_TEST(foobars.size() == 4ul);
BOOST_TEST(foobars.at(0).a == 42);
BOOST_TEST(foobars.at(1).a == 43);
BOOST_TEST(foobars.at(2).a == 44);
BOOST_TEST(foobars.at(3).a == 45);
BOOST_TEST(foobars.at(0).b == "baz");
BOOST_TEST(foobars.at(1).b == "qux");
BOOST_TEST(foobars.at(2).b == "quux");
BOOST_TEST(foobars.at(3).b == "foobar");
}
}
{
const toml::basic_value<toml::discard_comments, std::map, std::deque>
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 foobars = toml::get<std::vector<extlib2::foobar>>(v);
BOOST_TEST(foobars.size() == 4ul);
BOOST_TEST(foobars.at(0).a == 42);
BOOST_TEST(foobars.at(1).a == 43);
BOOST_TEST(foobars.at(2).a == 44);
BOOST_TEST(foobars.at(3).a == 45);
BOOST_TEST(foobars.at(0).b == "baz");
BOOST_TEST(foobars.at(1).b == "qux");
BOOST_TEST(foobars.at(2).b == "quux");
BOOST_TEST(foobars.at(3).b == "foobar");
}
// via constructor
{
const toml::value v{
{"0", toml::table{{"a", 42}, {"b", "baz"}}},
{"1", toml::table{{"a", 43}, {"b", "qux"}}},
{"2", toml::table{{"a", 44}, {"b", "quux"}}},
{"3", toml::table{{"a", 45}, {"b", "foobar"}}}
};
{
const auto foobars = toml::get<std::map<std::string, extlib::foobar>>(v);
BOOST_TEST(foobars.size() == 4ul);
BOOST_TEST(foobars.at("0").a == 42);
BOOST_TEST(foobars.at("1").a == 43);
BOOST_TEST(foobars.at("2").a == 44);
BOOST_TEST(foobars.at("3").a == 45);
BOOST_TEST(foobars.at("0").b == "baz");
BOOST_TEST(foobars.at("1").b == "qux");
BOOST_TEST(foobars.at("2").b == "quux");
BOOST_TEST(foobars.at("3").b == "foobar");
}
{
const auto foobars = toml::get<std::map<std::string, extlib2::foobar>>(v);
BOOST_TEST(foobars.size() == 4ul);
BOOST_TEST(foobars.at("0").a == 42);
BOOST_TEST(foobars.at("1").a == 43);
BOOST_TEST(foobars.at("2").a == 44);
BOOST_TEST(foobars.at("3").a == 45);
BOOST_TEST(foobars.at("0").b == "baz");
BOOST_TEST(foobars.at("1").b == "qux");
BOOST_TEST(foobars.at("2").b == "quux");
BOOST_TEST(foobars.at("3").b == "foobar");
}
}
{
const toml::basic_value<toml::discard_comments, std::map, std::deque>
v{
{"0", toml::table{{"a", 42}, {"b", "baz"}}},
{"1", toml::table{{"a", 43}, {"b", "qux"}}},
{"2", toml::table{{"a", 44}, {"b", "quux"}}},
{"3", toml::table{{"a", 45}, {"b", "foobar"}}}
};
const auto foobars = toml::get<std::map<std::string, extlib::foobar>>(v);
BOOST_TEST(foobars.size() == 4ul);
BOOST_TEST(foobars.at("0").a == 42);
BOOST_TEST(foobars.at("1").a == 43);
BOOST_TEST(foobars.at("2").a == 44);
BOOST_TEST(foobars.at("3").a == 45);
BOOST_TEST(foobars.at("0").b == "baz");
BOOST_TEST(foobars.at("1").b == "qux");
BOOST_TEST(foobars.at("2").b == "quux");
BOOST_TEST(foobars.at("3").b == "foobar");
}
} }

View File

@@ -101,6 +101,78 @@ BOOST_AUTO_TEST_CASE(test_find_throws)
} }
} }
BOOST_AUTO_TEST_CASE(test_find_array_throws)
{
// -----------------------------------------------------------------------
// const-reference version
{
// value is not an array
const toml::value v(true);
BOOST_CHECK_THROW(toml::find<toml::boolean>(v, 0), toml::type_error);
}
{
// the value corresponding to the key is not the expected type
const toml::value v{1, 2, 3, 4, 5};
BOOST_CHECK_THROW(toml::find<toml::boolean>(v, 0), toml::type_error);
}
{
// the value corresponding to the key is not found
const toml::value v{1, 2, 3, 4, 5};
BOOST_CHECK_THROW(toml::find<toml::integer>(v, 6), std::out_of_range);
}
{
// the positive control.
const toml::value v{1, 2, 3, 4, 5};
BOOST_TEST(3 == toml::find<int>(v, 2));
}
// -----------------------------------------------------------------------
// non-const reference version
{
// value is not an array
toml::value v(true);
BOOST_CHECK_THROW(toml::find<toml::boolean>(v, 0), toml::type_error);
}
{
// the value corresponding to the key is not the expected type
toml::value v{1, 2, 3, 4, 5};
BOOST_CHECK_THROW(toml::find<toml::boolean>(v, 0), toml::type_error);
}
{
// the value corresponding to the key is not found
toml::value v{1, 2, 3, 4, 5};
BOOST_CHECK_THROW(toml::find<toml::integer>(v, 6), std::out_of_range);
}
{
// the positive control.
toml::value v{1, 2, 3, 4, 5};
BOOST_TEST(3 == toml::find<int>(v, 2));
}
// -----------------------------------------------------------------------
// move version
{
// value is not an array
toml::value v(true);
BOOST_CHECK_THROW(toml::find<toml::boolean>(std::move(v), 0), toml::type_error);
}
{
// the value corresponding to the key is not the expected type
toml::value v{1, 2, 3, 4, 5};
BOOST_CHECK_THROW(toml::find<toml::boolean>(std::move(v), 0), toml::type_error);
}
{
// the value corresponding to the key is not found
toml::value v{1, 2, 3, 4, 5};
BOOST_CHECK_THROW(toml::find<toml::integer>(std::move(v), 6), std::out_of_range);
}
{
// the positive control.
toml::value v{1, 2, 3, 4, 5};
BOOST_TEST(3 == toml::find<int>(std::move(v), 2));
}
}
BOOST_AUTO_TEST_CASE(test_find_recursive) BOOST_AUTO_TEST_CASE(test_find_recursive)
{ {
// recursively search tables // recursively search tables
@@ -126,8 +198,76 @@ BOOST_AUTO_TEST_CASE(test_find_recursive)
num2 = 42; num2 = 42;
BOOST_TEST(42 == toml::find<int>(v, a, b, c, d)); BOOST_TEST(42 == toml::find<int>(v, a, b, c, d));
auto num3 = toml::find<toml::integer>(std::move(v), a, b, c, d); auto num3 = toml::find<toml::integer>(v, a, "b", c, "d");
BOOST_TEST(42 == num3); BOOST_TEST(42 == num3);
auto num4 = toml::find<toml::integer>(std::move(v), a, b, c, d);
BOOST_TEST(42 == num4);
}
// recursively search arrays
{
toml::value v{
toml::array{"array", "of", "string"},
toml::array{toml::array{1, 2, 3}, toml::array{3.14, 2.71}}
};
BOOST_TEST("array" == toml::find<std::string>(v, 0, 0));
BOOST_TEST("of" == toml::find<std::string>(v, 0, 1));
BOOST_TEST("string" == toml::find<std::string>(v, 0, 2));
BOOST_TEST(1 == toml::find<int>(v, 1, 0, 0));
BOOST_TEST(2 == toml::find<int>(v, 1, 0, 1));
BOOST_TEST(3 == toml::find<int>(v, 1, 0, 2));
BOOST_TEST(3.14 == toml::find<double>(v, 1, 1, 0));
BOOST_TEST(2.71 == toml::find<double>(v, 1, 1, 1));
// reference that can be used to modify the content
auto& num = toml::find<toml::integer>(v, 1, 0, 2);
num = 42;
BOOST_TEST( 1 == toml::find<int>(v, 1, 0, 0));
BOOST_TEST( 2 == toml::find<int>(v, 1, 0, 1));
BOOST_TEST(42 == toml::find<int>(v, 1, 0, 2));
// move value
auto num2 = toml::find<toml::integer>(std::move(v), 1, 0, 2);
BOOST_TEST(42 == num2);
}
// recursively search mixtures
{
toml::value v = toml::table{{"array", toml::array{
toml::array{1, 2, 3},
toml::array{
toml::table{{"foo", "bar"}, {"baz", "qux"}},
toml::table{{"pi", 3.14}, {"e", 2.71}}
}}
}};
BOOST_TEST(1 == toml::find<int>(v, "array", 0, 0));
BOOST_TEST(2 == toml::find<int>(v, "array", 0, 1));
BOOST_TEST(3 == toml::find<int>(v, "array", 0, 2));
BOOST_TEST("bar" == toml::find<std::string>(v, "array", 1, 0, "foo"));
BOOST_TEST("qux" == toml::find<std::string>(v, "array", 1, 0, "baz"));
BOOST_TEST(3.14 == toml::find<double>(v, "array", 1, 1, "pi"));
BOOST_TEST(2.71 == toml::find<double>(v, "array", 1, 1, "e"));
const std::string ar("array");
const auto ar_c = "array";
const std::string pi("pi");
const auto pi_c = "pi";
BOOST_TEST(3.14 == toml::find<double>(v, ar, 1, 1, "pi"));
BOOST_TEST(3.14 == toml::find<double>(v, ar, 1, 1, pi));
BOOST_TEST(3.14 == toml::find<double>(v, ar, 1, 1, pi_c));
BOOST_TEST(3.14 == toml::find<double>(v, ar_c, 1, 1, "pi"));
BOOST_TEST(3.14 == toml::find<double>(v, ar_c, 1, 1, pi));
BOOST_TEST(3.14 == toml::find<double>(v, ar_c, 1, 1, pi_c));
BOOST_TEST(3.14 == toml::find<double>(v, "array", 1, 1, pi));
BOOST_TEST(3.14 == toml::find<double>(v, "array", 1, 1, pi_c));
} }
} }
@@ -309,10 +449,11 @@ BOOST_AUTO_TEST_CASE_TEMPLATE(test_find_floating_type, value_type, test_value_ty
{ {
{ {
value_type v{{"key", 3.14}}; value_type v{{"key", 3.14}};
BOOST_TEST(static_cast<float >(3.14) == toml::find<float >(v, "key")); const double ref(3.14);
BOOST_TEST(static_cast<double >(3.14) == toml::find<double >(v, "key")); BOOST_TEST(static_cast<float >(ref) == toml::find<float >(v, "key"));
BOOST_TEST(static_cast<long double>(3.14) == toml::find<long double>(v, "key")); BOOST_TEST( ref == toml::find<double >(v, "key"));
BOOST_TEST(static_cast<float >(3.14) == toml::find<float >(std::move(v), "key")); BOOST_TEST(static_cast<long double>(ref) == toml::find<long double>(v, "key"));
BOOST_TEST(static_cast<float >(ref) == toml::find<float >(std::move(v), "key"));
} }
} }
@@ -373,14 +514,14 @@ BOOST_AUTO_TEST_CASE_TEMPLATE(test_find_toml_array, value_type, test_value_types
BOOST_TEST(static_cast<std::int64_t>(72) == deq.at(3)); BOOST_TEST(static_cast<std::int64_t>(72) == deq.at(3));
std::array<int, 4> ary = toml::find<std::array<int, 4>>(v, "key"); std::array<int, 4> ary = toml::find<std::array<int, 4>>(v, "key");
BOOST_TEST(static_cast<int>(42) == ary.at(0)); BOOST_TEST(42 == ary.at(0));
BOOST_TEST(static_cast<int>(54) == ary.at(1)); BOOST_TEST(54 == ary.at(1));
BOOST_TEST(static_cast<int>(69) == ary.at(2)); BOOST_TEST(69 == ary.at(2));
BOOST_TEST(static_cast<int>(72) == ary.at(3)); BOOST_TEST(72 == ary.at(3));
std::tuple<int, short, unsigned, long> tpl = std::tuple<int, short, unsigned, long> tpl =
toml::find<std::tuple<int, short, unsigned, long>>(v, "key"); toml::find<std::tuple<int, short, unsigned, long>>(v, "key");
BOOST_TEST(static_cast<int >(42) == std::get<0>(tpl)); BOOST_TEST( 42 == std::get<0>(tpl));
BOOST_TEST(static_cast<short >(54) == std::get<1>(tpl)); BOOST_TEST(static_cast<short >(54) == std::get<1>(tpl));
BOOST_TEST(static_cast<unsigned>(69) == std::get<2>(tpl)); BOOST_TEST(static_cast<unsigned>(69) == std::get<2>(tpl));
BOOST_TEST(static_cast<long >(72) == std::get<3>(tpl)); BOOST_TEST(static_cast<long >(72) == std::get<3>(tpl));
@@ -420,14 +561,14 @@ BOOST_AUTO_TEST_CASE_TEMPLATE(test_find_move_toml_array, value_type, test_value_
BOOST_TEST(static_cast<std::int64_t>(72) == deq.at(3)); BOOST_TEST(static_cast<std::int64_t>(72) == deq.at(3));
std::array<int, 4> ary = toml::find<std::array<int, 4>>(std::move(v4), "key"); std::array<int, 4> ary = toml::find<std::array<int, 4>>(std::move(v4), "key");
BOOST_TEST(static_cast<int>(42) == ary.at(0)); BOOST_TEST(42 == ary.at(0));
BOOST_TEST(static_cast<int>(54) == ary.at(1)); BOOST_TEST(54 == ary.at(1));
BOOST_TEST(static_cast<int>(69) == ary.at(2)); BOOST_TEST(69 == ary.at(2));
BOOST_TEST(static_cast<int>(72) == ary.at(3)); BOOST_TEST(72 == ary.at(3));
std::tuple<int, short, unsigned, long> tpl = std::tuple<int, short, unsigned, long> tpl =
toml::find<std::tuple<int, short, unsigned, long>>(std::move(v5), "key"); toml::find<std::tuple<int, short, unsigned, long>>(std::move(v5), "key");
BOOST_TEST(static_cast<int >(42) == std::get<0>(tpl)); BOOST_TEST( 42 == std::get<0>(tpl));
BOOST_TEST(static_cast<short >(54) == std::get<1>(tpl)); BOOST_TEST(static_cast<short >(54) == std::get<1>(tpl));
BOOST_TEST(static_cast<unsigned>(69) == std::get<2>(tpl)); BOOST_TEST(static_cast<unsigned>(69) == std::get<2>(tpl));
BOOST_TEST(static_cast<long >(72) == std::get<3>(tpl)); BOOST_TEST(static_cast<long >(72) == std::get<3>(tpl));

View File

@@ -375,7 +375,8 @@ BOOST_AUTO_TEST_CASE(test_find_or_floating)
toml::value v1{{"key", 42}}; toml::value v1{{"key", 42}};
toml::value v2{{"key", 3.14}}; toml::value v2{{"key", 3.14}};
BOOST_TEST(2.71f == toml::find_or(v1, "key", 2.71f)); BOOST_TEST(2.71f == toml::find_or(v1, "key", 2.71f));
BOOST_TEST(static_cast<float>(double(3.14)) == toml::find_or(v2, "key", 2.71f)); const double ref(3.14);
BOOST_TEST(static_cast<float>(ref) == toml::find_or(v2, "key", 2.71f));
} }
{ {
toml::value v1{{"key", 42}}; toml::value v1{{"key", 42}};
@@ -383,7 +384,8 @@ BOOST_AUTO_TEST_CASE(test_find_or_floating)
const auto moved1 = toml::find_or(std::move(v1), "key", 2.71f); const auto moved1 = toml::find_or(std::move(v1), "key", 2.71f);
const auto moved2 = toml::find_or(std::move(v2), "key", 2.71f); const auto moved2 = toml::find_or(std::move(v2), "key", 2.71f);
BOOST_TEST(2.71f == moved1); BOOST_TEST(2.71f == moved1);
BOOST_TEST(static_cast<float>(double(3.14)) == moved2); const double ref(3.14);
BOOST_TEST(static_cast<float>(ref) == moved2);
} }
} }

View File

@@ -195,11 +195,11 @@ BOOST_AUTO_TEST_CASE_TEMPLATE(test_get_floating_type, value_type, test_value_typ
{ {
{ {
value_type v(3.14); value_type v(3.14);
BOOST_TEST(static_cast<float >(3.14) == toml::get<float >(v)); const double ref(3.14);
BOOST_TEST(static_cast<double >(3.14) == toml::get<double >(v)); BOOST_TEST(static_cast<float >(ref) == toml::get<float >(v));
BOOST_TEST(static_cast<long double>(3.14) == toml::get<long double>(v)); BOOST_TEST( ref == toml::get<double >(v));
BOOST_TEST(static_cast<long double>(ref) == toml::get<long double>(v));
BOOST_TEST(3.14f == toml::get<float>(std::move(v))); BOOST_TEST(static_cast<float >(ref) == toml::get<float>(std::move(v)));
} }
} }
@@ -262,14 +262,14 @@ BOOST_AUTO_TEST_CASE_TEMPLATE(test_get_toml_array, value_type, test_value_types)
BOOST_TEST(static_cast<std::int64_t>(72) == deq.at(3)); BOOST_TEST(static_cast<std::int64_t>(72) == deq.at(3));
std::array<int, 4> ary = toml::get<std::array<int, 4>>(v); std::array<int, 4> ary = toml::get<std::array<int, 4>>(v);
BOOST_TEST(static_cast<int>(42) == ary.at(0)); BOOST_TEST(42 == ary.at(0));
BOOST_TEST(static_cast<int>(54) == ary.at(1)); BOOST_TEST(54 == ary.at(1));
BOOST_TEST(static_cast<int>(69) == ary.at(2)); BOOST_TEST(69 == ary.at(2));
BOOST_TEST(static_cast<int>(72) == ary.at(3)); BOOST_TEST(72 == ary.at(3));
std::tuple<int, short, unsigned, long> tpl = std::tuple<int, short, unsigned, long> tpl =
toml::get<std::tuple<int, short, unsigned, long>>(v); toml::get<std::tuple<int, short, unsigned, long>>(v);
BOOST_TEST(static_cast<int >(42) == std::get<0>(tpl)); BOOST_TEST( 42 == std::get<0>(tpl));
BOOST_TEST(static_cast<short >(54) == std::get<1>(tpl)); BOOST_TEST(static_cast<short >(54) == std::get<1>(tpl));
BOOST_TEST(static_cast<unsigned>(69) == std::get<2>(tpl)); BOOST_TEST(static_cast<unsigned>(69) == std::get<2>(tpl));
BOOST_TEST(static_cast<long >(72) == std::get<3>(tpl)); BOOST_TEST(static_cast<long >(72) == std::get<3>(tpl));
@@ -308,16 +308,16 @@ BOOST_AUTO_TEST_CASE_TEMPLATE(test_get_toml_array, value_type, test_value_types)
{ {
value_type v{42, 54, 69, 72}; value_type v{42, 54, 69, 72};
std::array<int, 4> ary = toml::get<std::array<int, 4>>(std::move(v)); std::array<int, 4> ary = toml::get<std::array<int, 4>>(std::move(v));
BOOST_TEST(static_cast<int>(42) == ary.at(0)); BOOST_TEST(42 == ary.at(0));
BOOST_TEST(static_cast<int>(54) == ary.at(1)); BOOST_TEST(54 == ary.at(1));
BOOST_TEST(static_cast<int>(69) == ary.at(2)); BOOST_TEST(69 == ary.at(2));
BOOST_TEST(static_cast<int>(72) == ary.at(3)); BOOST_TEST(72 == ary.at(3));
} }
{ {
value_type v{42, 54, 69, 72}; value_type v{42, 54, 69, 72};
std::tuple<int, short, unsigned, long> tpl = std::tuple<int, short, unsigned, long> tpl =
toml::get<std::tuple<int, short, unsigned, long>>(std::move(v)); toml::get<std::tuple<int, short, unsigned, long>>(std::move(v));
BOOST_TEST(static_cast<int >(42) == std::get<0>(tpl)); BOOST_TEST( 42 == std::get<0>(tpl));
BOOST_TEST(static_cast<short >(54) == std::get<1>(tpl)); BOOST_TEST(static_cast<short >(54) == std::get<1>(tpl));
BOOST_TEST(static_cast<unsigned>(69) == std::get<2>(tpl)); BOOST_TEST(static_cast<unsigned>(69) == std::get<2>(tpl));
BOOST_TEST(static_cast<long >(72) == std::get<3>(tpl)); BOOST_TEST(static_cast<long >(72) == std::get<3>(tpl));

View File

@@ -8,7 +8,7 @@
do { \ do { \
const std::string token (tkn); \ const std::string token (tkn); \
const std::string expected(expct); \ const std::string expected(expct); \
toml::detail::location<std::string> loc("test", token); \ toml::detail::location loc("test", token); \
const auto result = lxr::invoke(loc); \ const auto result = lxr::invoke(loc); \
BOOST_TEST(result.is_ok()); \ BOOST_TEST(result.is_ok()); \
if(result.is_ok()){ \ if(result.is_ok()){ \
@@ -28,7 +28,7 @@ do { \
#define TOML11_TEST_LEX_REJECT(lxr, tkn) \ #define TOML11_TEST_LEX_REJECT(lxr, tkn) \
do { \ do { \
const std::string token (tkn); \ const std::string token (tkn); \
toml::detail::location<std::string> loc("test", token); \ toml::detail::location loc("test", token); \
const auto result = lxr::invoke(loc); \ const auto result = lxr::invoke(loc); \
BOOST_TEST(result.is_err()); \ BOOST_TEST(result.is_err()); \
const bool loc_same = (loc.begin() == loc.iter()); \ const bool loc_same = (loc.begin() == loc.iter()); \

View File

@@ -18,12 +18,11 @@ BOOST_AUTO_TEST_CASE(test_quoted_key)
{ {
TOML11_TEST_LEX_ACCEPT(lex_key, "\"127.0.0.1\"", "\"127.0.0.1\""); TOML11_TEST_LEX_ACCEPT(lex_key, "\"127.0.0.1\"", "\"127.0.0.1\"");
TOML11_TEST_LEX_ACCEPT(lex_key, "\"character encoding\"", "\"character encoding\""); TOML11_TEST_LEX_ACCEPT(lex_key, "\"character encoding\"", "\"character encoding\"");
#if defined(_MSC_VER) || defined(__INTEL_COMPILER)
// UTF-8 codepoint of characters that looks like "key" written upside down
TOML11_TEST_LEX_ACCEPT(lex_key, "\"\xCA\x8E\xC7\x9D\xCA\x9E\"", TOML11_TEST_LEX_ACCEPT(lex_key, "\"\xCA\x8E\xC7\x9D\xCA\x9E\"",
"\"\xCA\x8E\xC7\x9D\xCA\x9E\""); "\"\xCA\x8E\xC7\x9D\xCA\x9E\"");
#else
TOML11_TEST_LEX_ACCEPT(lex_key, u8"\"ʎǝʞ\"", u8"\"ʎǝʞ\"");
#endif
TOML11_TEST_LEX_ACCEPT(lex_key, "'key2'", "'key2'"); TOML11_TEST_LEX_ACCEPT(lex_key, "'key2'", "'key2'");
TOML11_TEST_LEX_ACCEPT(lex_key, "'quoted \"value\"'", "'quoted \"value\"'"); TOML11_TEST_LEX_ACCEPT(lex_key, "'quoted \"value\"'", "'quoted \"value\"'");
} }

View File

@@ -31,15 +31,9 @@ BOOST_AUTO_TEST_CASE(test_basic_string)
"\"192.168.1.1\"", "\"192.168.1.1\"",
"\"192.168.1.1\""); "\"192.168.1.1\"");
#if defined(_MSC_VER) || defined(__INTEL_COMPILER)
TOML11_TEST_LEX_ACCEPT(lex_string, TOML11_TEST_LEX_ACCEPT(lex_string,
"\"\xE4\xB8\xAD\xE5\x9B\xBD\"", "\"\xE4\xB8\xAD\xE5\x9B\xBD\"", // UTF-8 string (means "China" in
"\"\xE4\xB8\xAD\xE5\x9B\xBD\""); "\"\xE4\xB8\xAD\xE5\x9B\xBD\""); // Chinese characters)
#else
TOML11_TEST_LEX_ACCEPT(lex_string,
u8"\"中国\"",
u8"\"中国\"");
#endif
TOML11_TEST_LEX_ACCEPT(lex_string, TOML11_TEST_LEX_ACCEPT(lex_string,
"\"You'll hate me after this - #\"", "\"You'll hate me after this - #\"",
@@ -57,6 +51,22 @@ BOOST_AUTO_TEST_CASE(test_ml_basic_string)
TOML11_TEST_LEX_ACCEPT(lex_string, TOML11_TEST_LEX_ACCEPT(lex_string,
"\"\"\"\\\n The quick brown \\\n\n fox jumps over \\\n the lazy dog.\\\n \"\"\"", "\"\"\"\\\n The quick brown \\\n\n fox jumps over \\\n the lazy dog.\\\n \"\"\"",
"\"\"\"\\\n The quick brown \\\n\n fox jumps over \\\n the lazy dog.\\\n \"\"\""); "\"\"\"\\\n The quick brown \\\n\n fox jumps over \\\n the lazy dog.\\\n \"\"\"");
TOML11_TEST_LEX_ACCEPT(lex_string,
"\"\"\"Here are two quotation marks: \"\". Simple enough.\"\"\"",
"\"\"\"Here are two quotation marks: \"\". Simple enough.\"\"\"");
TOML11_TEST_LEX_ACCEPT(lex_string,
"\"\"\"Here are three quotation marks: \"\"\\\".\"\"\"",
"\"\"\"Here are three quotation marks: \"\"\\\".\"\"\"");
TOML11_TEST_LEX_ACCEPT(lex_string,
"\"\"\"Here are fifteen quotation marks: \"\"\\\"\"\"\\\"\"\"\\\"\"\"\\\"\"\"\\\".\"\"\"",
"\"\"\"Here are fifteen quotation marks: \"\"\\\"\"\"\\\"\"\"\\\"\"\"\\\"\"\"\\\".\"\"\"");
TOML11_TEST_LEX_ACCEPT(lex_string,
"\"\"\"\"This,\" she said, \"is just a pointless statement.\"\"\"\"",
"\"\"\"\"This,\" she said, \"is just a pointless statement.\"\"\"\"");
} }
BOOST_AUTO_TEST_CASE(test_literal_string) BOOST_AUTO_TEST_CASE(test_literal_string)
@@ -83,4 +93,16 @@ BOOST_AUTO_TEST_CASE(test_ml_literal_string)
TOML11_TEST_LEX_ACCEPT(lex_string, TOML11_TEST_LEX_ACCEPT(lex_string,
"'''\nThe first newline is\ntrimmed in raw strings.\n All other whitespace\n is preserved.\n'''", "'''\nThe first newline is\ntrimmed in raw strings.\n All other whitespace\n is preserved.\n'''",
"'''\nThe first newline is\ntrimmed in raw strings.\n All other whitespace\n is preserved.\n'''"); "'''\nThe first newline is\ntrimmed in raw strings.\n All other whitespace\n is preserved.\n'''");
TOML11_TEST_LEX_ACCEPT(lex_string,
"''''That's still pointless', she said.'''",
"''''That's still pointless', she said.'''");
TOML11_TEST_LEX_ACCEPT(lex_string,
"'''Here are fifteen quotation marks: \"\"\"\"\"\"\"\"\"\"\"\"\"\"\".'''",
"'''Here are fifteen quotation marks: \"\"\"\"\"\"\"\"\"\"\"\"\"\"\".'''");
TOML11_TEST_LEX_ACCEPT(lex_string,
"''''This,' she said, 'is just a pointless statement.''''",
"''''This,' she said, 'is just a pointless statement.''''");
} }

View File

@@ -12,6 +12,170 @@ BOOST_AUTO_TEST_CASE(test_file_as_literal)
{ {
using namespace toml::literals::toml_literals; using namespace toml::literals::toml_literals;
{
const toml::value r{{"a", 42}, {"b", "baz"}};
const toml::value v = R"(
a = 42
b = "baz"
)"_toml;
BOOST_TEST(r == v);
}
{
const toml::value r{
{"c", 3.14},
{"table", toml::table{{"a", 42}, {"b", "baz"}}}
};
const toml::value v = R"(
c = 3.14
[table]
a = 42
b = "baz"
)"_toml;
BOOST_TEST(r == v);
}
{
const toml::value r{
{"table", toml::table{{"a", 42}, {"b", "baz"}}}
};
const toml::value v = R"(
[table]
a = 42
b = "baz"
)"_toml;
BOOST_TEST(r == v);
}
{
const toml::value r{
{"array_of_tables", toml::array{toml::table{}}}
};
const toml::value v = R"(
[[array_of_tables]]
)"_toml;
BOOST_TEST(r == v);
}
}
BOOST_AUTO_TEST_CASE(test_value_as_literal)
{
using namespace toml::literals::toml_literals;
{
const toml::value v1 = "true"_toml;
const toml::value v2 = "false"_toml;
BOOST_TEST(v1.is_boolean());
BOOST_TEST(v2.is_boolean());
BOOST_TEST(toml::get<bool>(v1));
BOOST_TEST(!toml::get<bool>(v2));
}
{
const toml::value v1 = "123_456"_toml;
const toml::value v2 = "0b0010"_toml;
const toml::value v3 = "0xDEADBEEF"_toml;
BOOST_TEST(v1.is_integer());
BOOST_TEST(v2.is_integer());
BOOST_TEST(v3.is_integer());
BOOST_TEST(toml::get<toml::integer>(v1) == 123456);
BOOST_TEST(toml::get<toml::integer>(v2) == 2);
BOOST_TEST(toml::get<toml::integer>(v3) == 0xDEADBEEF);
}
{
const toml::value v1 = "3.1415"_toml;
const toml::value v2 = "6.02e+23"_toml;
BOOST_TEST(v1.is_floating());
BOOST_TEST(v2.is_floating());
BOOST_TEST(toml::get<double>(v1) == 3.1415, boost::test_tools::tolerance(0.00001));
BOOST_TEST(toml::get<double>(v2) == 6.02e23, boost::test_tools::tolerance(0.0001));
}
{
const toml::value v1 = R"("foo")"_toml;
const toml::value v2 = R"('foo')"_toml;
const toml::value v3 = R"("""foo""")"_toml;
const toml::value v4 = R"('''foo''')"_toml;
BOOST_TEST(v1.is_string());
BOOST_TEST(v2.is_string());
BOOST_TEST(v3.is_string());
BOOST_TEST(v4.is_string());
BOOST_TEST(toml::get<std::string>(v1) == "foo");
BOOST_TEST(toml::get<std::string>(v2) == "foo");
BOOST_TEST(toml::get<std::string>(v3) == "foo");
BOOST_TEST(toml::get<std::string>(v4) == "foo");
}
{
{
const toml::value v1 = R"([1,2,3])"_toml;
BOOST_TEST(v1.is_array());
const bool result = (toml::get<std::vector<int>>(v1) == std::vector<int>{1,2,3});
BOOST_TEST(result);
}
{
const toml::value v2 = R"([1,])"_toml;
BOOST_TEST(v2.is_array());
const bool result = (toml::get<std::vector<int>>(v2) == std::vector<int>{1});
BOOST_TEST(result);
}
{
const toml::value v3 = R"([[1,]])"_toml;
BOOST_TEST(v3.is_array());
const bool result = (toml::get<std::vector<int>>(toml::get<toml::array>(v3).front()) == std::vector<int>{1});
BOOST_TEST(result);
}
{
const toml::value v4 = R"([[1],])"_toml;
BOOST_TEST(v4.is_array());
const bool result = (toml::get<std::vector<int>>(toml::get<toml::array>(v4).front()) == std::vector<int>{1});
BOOST_TEST(result);
}
}
{
const toml::value v1 = R"({a = 42})"_toml;
BOOST_TEST(v1.is_table());
const bool result = toml::get<std::map<std::string,int>>(v1) ==
std::map<std::string,int>{{"a", 42}};
BOOST_TEST(result);
}
{
const toml::value v1 = "1979-05-27"_toml;
BOOST_TEST(v1.is_local_date());
BOOST_TEST(toml::get<toml::local_date>(v1) ==
toml::local_date(1979, toml::month_t::May, 27));
}
{
const toml::value v1 = "12:00:00"_toml;
BOOST_TEST(v1.is_local_time());
const bool result = toml::get<std::chrono::hours>(v1) == std::chrono::hours(12);
BOOST_TEST(result);
}
{
const toml::value v1 = "1979-05-27T07:32:00"_toml;
BOOST_TEST(v1.is_local_datetime());
BOOST_TEST(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_TEST(v1.is_offset_datetime());
BOOST_TEST(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)));
}
}
BOOST_AUTO_TEST_CASE(test_file_as_u8_literal)
{
using namespace toml::literals::toml_literals;
{ {
const toml::value r{{"a", 42}, {"b", "baz"}}; const toml::value r{{"a", 42}, {"b", "baz"}};
const toml::value v = u8R"( const toml::value v = u8R"(
@@ -59,7 +223,7 @@ BOOST_AUTO_TEST_CASE(test_file_as_literal)
} }
} }
BOOST_AUTO_TEST_CASE(test_value_as_literal) BOOST_AUTO_TEST_CASE(test_value_as_u8_literal)
{ {
using namespace toml::literals::toml_literals; using namespace toml::literals::toml_literals;
@@ -98,15 +262,18 @@ BOOST_AUTO_TEST_CASE(test_value_as_literal)
const toml::value v2 = u8R"('foo')"_toml; const toml::value v2 = u8R"('foo')"_toml;
const toml::value v3 = u8R"("""foo""")"_toml; const toml::value v3 = u8R"("""foo""")"_toml;
const toml::value v4 = u8R"('''foo''')"_toml; const toml::value v4 = u8R"('''foo''')"_toml;
const toml::value v5 = u8R"("")"_toml;
BOOST_TEST(v1.is_string()); BOOST_TEST(v1.is_string());
BOOST_TEST(v2.is_string()); BOOST_TEST(v2.is_string());
BOOST_TEST(v3.is_string()); BOOST_TEST(v3.is_string());
BOOST_TEST(v4.is_string()); BOOST_TEST(v4.is_string());
BOOST_TEST(v5.is_string());
BOOST_TEST(toml::get<std::string>(v1) == "foo"); BOOST_TEST(toml::get<std::string>(v1) == "foo");
BOOST_TEST(toml::get<std::string>(v2) == "foo"); BOOST_TEST(toml::get<std::string>(v2) == "foo");
BOOST_TEST(toml::get<std::string>(v3) == "foo"); BOOST_TEST(toml::get<std::string>(v3) == "foo");
BOOST_TEST(toml::get<std::string>(v4) == "foo"); BOOST_TEST(toml::get<std::string>(v4) == "foo");
BOOST_TEST(toml::get<std::string>(v5) == "\xE3\x81\xB2\xE3\x82\x89\xE3\x81\x8C\xE3\x81\xAA");
} }
{ {
{ {
@@ -164,7 +331,7 @@ BOOST_AUTO_TEST_CASE(test_value_as_literal)
toml::local_time(7, 32, 0))); toml::local_time(7, 32, 0)));
} }
{ {
const toml::value v1 = "1979-05-27T07:32:00Z"_toml; const toml::value v1 = u8"1979-05-27T07:32:00Z"_toml;
BOOST_TEST(v1.is_offset_datetime()); BOOST_TEST(v1.is_offset_datetime());
BOOST_TEST(toml::get<toml::offset_datetime>(v1) == BOOST_TEST(toml::get<toml::offset_datetime>(v1) ==
toml::offset_datetime(toml::local_date(1979, toml::month_t::May, 27), toml::offset_datetime(toml::local_date(1979, toml::month_t::May, 27),

View File

@@ -172,3 +172,18 @@ BOOST_AUTO_TEST_CASE(test_heterogeneous_array)
} }
#endif #endif
} }
BOOST_AUTO_TEST_CASE(test_comments_after_comma)
{
{
array a;
a.push_back("foo");
a.push_back("bar");
a.push_back("baz");
TOML11_TEST_PARSE_EQUAL_VALUE(parse_value<toml::value>,
"[ \"foo\" # comment\n"
", \"bar\" # comment\n"
", \"baz\" # comment\n"
"]", toml::value(a));
}
}

View File

@@ -7,7 +7,7 @@
#define TOML11_TEST_PARSE_EQUAL(psr, tkn, expct) \ #define TOML11_TEST_PARSE_EQUAL(psr, tkn, expct) \
do { \ do { \
const std::string token(tkn); \ const std::string token(tkn); \
toml::detail::location<std::string> loc("test", token); \ toml::detail::location loc("test", token); \
const auto result = psr(loc); \ const auto result = psr(loc); \
BOOST_TEST(result.is_ok()); \ BOOST_TEST(result.is_ok()); \
if(result.is_ok()){ \ if(result.is_ok()){ \
@@ -23,7 +23,7 @@ do { \
#define TOML11_TEST_PARSE_EQUAL_VALUE(psr, tkn, expct) \ #define TOML11_TEST_PARSE_EQUAL_VALUE(psr, tkn, expct) \
do { \ do { \
const std::string token(tkn); \ const std::string token(tkn); \
toml::detail::location<std::string> loc("test", token); \ toml::detail::location loc("test", token); \
const auto result = psr(loc); \ const auto result = psr(loc); \
BOOST_TEST(result.is_ok()); \ BOOST_TEST(result.is_ok()); \
if(result.is_ok()){ \ if(result.is_ok()){ \

View File

@@ -76,7 +76,7 @@ BOOST_AUTO_TEST_CASE(test_example)
BOOST_AUTO_TEST_CASE(test_example_stream) BOOST_AUTO_TEST_CASE(test_example_stream)
{ {
std::ifstream ifs("toml/tests/example.toml"); std::ifstream ifs("toml/tests/example.toml", std::ios::binary);
const auto data = toml::parse(ifs); const auto data = toml::parse(ifs);
BOOST_TEST(toml::find<std::string>(data, "title") == "TOML Example"); BOOST_TEST(toml::find<std::string>(data, "title") == "TOML Example");
@@ -779,6 +779,22 @@ BOOST_AUTO_TEST_CASE(test_files_end_with_empty_lines)
BOOST_TEST(toml::find<std::string>(toml::find(data, "table"), "key") == "value"); BOOST_TEST(toml::find<std::string>(toml::find(data, "table"), "key") == "value");
} }
// without newline
{
const std::string table(
"key = \"value\"\n"
"[table]\n"
"key = \"value\"\n"
"a = 0"
);
std::istringstream iss(table);
const auto data = toml::parse(iss,
"test_files_end_with_newline.toml");
BOOST_TEST(toml::find<std::string>(data, "key") == "value");
BOOST_TEST(toml::find<std::string>(toml::find(data, "table"), "key") == "value");
}
// CRLF // CRLF
@@ -889,3 +905,41 @@ BOOST_AUTO_TEST_CASE(test_files_end_with_empty_lines)
} }
} }
BOOST_AUTO_TEST_CASE(test_parse_function_compiles)
{
// toml::parse("");
const auto string_literal = toml::parse("toml/tests/example.toml");
BOOST_TEST_MESSAGE("string_literal");
const char* fname_cstring = "toml/tests/example.toml";
// toml::parse(const char*);
const auto cstring = toml::parse(fname_cstring);
BOOST_TEST_MESSAGE("const char*");
// toml::parse(char*);
std::array<char, 24> fname_char_ptr;
std::strncpy(fname_char_ptr.data(), fname_cstring, 24);
const auto char_ptr = toml::parse(fname_char_ptr.data());
BOOST_TEST_MESSAGE("char*");
// toml::parse(const std::string&);
const std::string fname_string("toml/tests/example.toml");
const auto string = toml::parse(fname_string);
std::string fname_string_mut("toml/tests/example.toml");
// toml::parse(std::string&);
const auto string_mutref = toml::parse(fname_string_mut);
// toml::parse(std::string&&);
const auto string_rref = toml::parse(std::move(fname_string_mut));
BOOST_TEST_MESSAGE("strings");
#ifdef TOML11_HAS_STD_FILESYSTEM
const std::filesystem::path fname_path(fname_string.begin(), fname_string.end());
const auto filesystem_path = toml::parse(fname_path);
BOOST_TEST_MESSAGE("path");
#endif
}

View File

@@ -128,7 +128,7 @@ BOOST_AUTO_TEST_CASE(test_inf)
{ {
{ {
const std::string token("inf"); const std::string token("inf");
toml::detail::location<std::string> loc("test", token); toml::detail::location loc("test", token);
const auto r = parse_floating(loc); const auto r = parse_floating(loc);
BOOST_CHECK(r.is_ok()); BOOST_CHECK(r.is_ok());
BOOST_CHECK(std::isinf(r.unwrap().first)); BOOST_CHECK(std::isinf(r.unwrap().first));
@@ -136,7 +136,7 @@ BOOST_AUTO_TEST_CASE(test_inf)
} }
{ {
const std::string token("+inf"); const std::string token("+inf");
toml::detail::location<std::string> loc("test", token); toml::detail::location loc("test", token);
const auto r = parse_floating(loc); const auto r = parse_floating(loc);
BOOST_CHECK(r.is_ok()); BOOST_CHECK(r.is_ok());
BOOST_CHECK(std::isinf(r.unwrap().first)); BOOST_CHECK(std::isinf(r.unwrap().first));
@@ -144,7 +144,7 @@ BOOST_AUTO_TEST_CASE(test_inf)
} }
{ {
const std::string token("-inf"); const std::string token("-inf");
toml::detail::location<std::string> loc("test", token); toml::detail::location loc("test", token);
const auto r = parse_floating(loc); const auto r = parse_floating(loc);
BOOST_CHECK(r.is_ok()); BOOST_CHECK(r.is_ok());
BOOST_CHECK(std::isinf(r.unwrap().first)); BOOST_CHECK(std::isinf(r.unwrap().first));
@@ -156,21 +156,21 @@ BOOST_AUTO_TEST_CASE(test_nan)
{ {
{ {
const std::string token("nan"); const std::string token("nan");
toml::detail::location<std::string> loc("test", token); toml::detail::location loc("test", token);
const auto r = parse_floating(loc); const auto r = parse_floating(loc);
BOOST_CHECK(r.is_ok()); BOOST_CHECK(r.is_ok());
BOOST_CHECK(std::isnan(r.unwrap().first)); BOOST_CHECK(std::isnan(r.unwrap().first));
} }
{ {
const std::string token("+nan"); const std::string token("+nan");
toml::detail::location<std::string> loc("test", token); toml::detail::location loc("test", token);
const auto r = parse_floating(loc); const auto r = parse_floating(loc);
BOOST_CHECK(r.is_ok()); BOOST_CHECK(r.is_ok());
BOOST_CHECK(std::isnan(r.unwrap().first)); BOOST_CHECK(std::isnan(r.unwrap().first));
} }
{ {
const std::string token("-nan"); const std::string token("-nan");
toml::detail::location<std::string> loc("test", token); toml::detail::location loc("test", token);
const auto r = parse_floating(loc); const auto r = parse_floating(loc);
BOOST_CHECK(r.is_ok()); BOOST_CHECK(r.is_ok());
BOOST_CHECK(std::isnan(r.unwrap().first)); BOOST_CHECK(std::isnan(r.unwrap().first));

View File

@@ -46,3 +46,19 @@ BOOST_AUTO_TEST_CASE(test_inline_table_value)
TOML11_TEST_PARSE_EQUAL_VALUE(parse_value<toml::value>, "{type.name = \"pug\"}", value(t)); TOML11_TEST_PARSE_EQUAL_VALUE(parse_value<toml::value>, "{type.name = \"pug\"}", value(t));
} }
} }
BOOST_AUTO_TEST_CASE(test_inline_table_immutability)
{
{
std::istringstream stream(std::string(
"a = {b = 1}\n"
"a.c = 2\n"));
BOOST_CHECK_THROW(toml::parse(stream), toml::syntax_error);
}
{
std::istringstream stream(std::string(
"a = {b = {c = 1}}\n"
"a.b.d = 2\n"));
BOOST_CHECK_THROW(toml::parse(stream), toml::syntax_error);
}
}

View File

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

View File

@@ -19,7 +19,7 @@ BOOST_AUTO_TEST_CASE(test_normal_table)
"key2 = 42\n" "key2 = 42\n"
"key3 = 3.14\n" "key3 = 3.14\n"
); );
location<std::string> loc("test", table); location loc("test", table);
const auto result = toml::detail::parse_ml_table<toml::value>(loc); const auto result = toml::detail::parse_ml_table<toml::value>(loc);
BOOST_TEST(result.is_ok()); BOOST_TEST(result.is_ok());
@@ -36,7 +36,7 @@ BOOST_AUTO_TEST_CASE(test_nested_table)
"a.b = \"value\"\n" "a.b = \"value\"\n"
"a.c.d = 42\n" "a.c.d = 42\n"
); );
location<std::string> loc("test", table); location loc("test", table);
const auto result = toml::detail::parse_ml_table<toml::value>(loc); const auto result = toml::detail::parse_ml_table<toml::value>(loc);
BOOST_TEST(result.is_ok()); BOOST_TEST(result.is_ok());

View File

@@ -9,7 +9,6 @@
#include <iostream> #include <iostream>
#include <fstream> #include <fstream>
#if defined(_MSC_VER) || defined(__INTEL_COMPILER)
BOOST_AUTO_TEST_CASE(test_hard_example_unicode) BOOST_AUTO_TEST_CASE(test_hard_example_unicode)
{ {
const auto data = toml::parse("toml/tests/hard_example_unicode.toml"); const auto data = toml::parse("toml/tests/hard_example_unicode.toml");
@@ -40,35 +39,3 @@ BOOST_AUTO_TEST_CASE(test_hard_example_unicode)
BOOST_CHECK(toml::get<std::vector<std::string>>(bit.at("multi_line_array")) == BOOST_CHECK(toml::get<std::vector<std::string>>(bit.at("multi_line_array")) ==
expected_multi_line_array); expected_multi_line_array);
} }
#else
BOOST_AUTO_TEST_CASE(test_hard_example_unicode)
{
const auto data = toml::parse("toml/tests/hard_example_unicode.toml");
const auto the = toml::find<toml::table>(data, "the");
BOOST_TEST(toml::get<std::string>(the.at("test_string")) ==
std::string(u8"Ýôú' λáƭè ₥è áƒƭèř ƭλïƨ - #"));
const auto hard = toml::get<toml::table>(the.at("hard"));
const std::vector<std::string> expected_the_hard_test_array{"] ", " # "};
BOOST_CHECK(toml::get<std::vector<std::string>>(hard.at("test_array")) ==
expected_the_hard_test_array);
const std::vector<std::string> expected_the_hard_test_array2{
std::string(u8"Tèƨƭ #11 ]ƥřôƲèδ ƭλáƭ"),
std::string(u8"Éжƥèřï₥èñƭ #9 ωáƨ á ƨúççèƨƨ")};
BOOST_CHECK(toml::get<std::vector<std::string>>(hard.at("test_array2")) ==
expected_the_hard_test_array2);
BOOST_TEST(toml::get<std::string>(hard.at("another_test_string")) ==
std::string(u8"§á₥è ƭλïñϱ, βúƭ ωïƭλ á ƨƭřïñϱ #"));
BOOST_TEST(toml::get<std::string>(hard.at("harder_test_string")) ==
std::string(u8" Âñδ ωλèñ \"'ƨ ářè ïñ ƭλè ƨƭřïñϱ, áℓôñϱ ωïƭλ # \""));
const auto bit = toml::get<toml::table>(hard.at(std::string(u8"βïƭ#")));
BOOST_TEST(toml::get<std::string>(bit.at(std::string(u8"ωλáƭ?"))) ==
std::string(u8"Ýôú δôñ'ƭ ƭλïñƙ ƨô₥è úƨèř ωôñ'ƭ δô ƭλáƭ?"));
const std::vector<std::string> expected_multi_line_array{"]"};
BOOST_CHECK(toml::get<std::vector<std::string>>(bit.at("multi_line_array")) ==
expected_multi_line_array);
}
#endif

View File

@@ -9,27 +9,24 @@
#include <vector> #include <vector>
#include <array> #include <array>
BOOST_AUTO_TEST_CASE(test_resize) BOOST_AUTO_TEST_CASE(test_try_reserve)
{ {
{ {
typedef std::vector<int> resizable_type; // since BOOST_TEST is a macro, it cannot handle commas correctly.
typedef std::array<int,1> non_resizable_type; // When toml::detail::has_reserve_method<std::array<int, 1>>::value
BOOST_TEST(toml::detail::has_resize_method<resizable_type>::value); // is passed to a macro, C preprocessor considers
BOOST_TEST(!toml::detail::has_resize_method<non_resizable_type>::value); // toml::detail::has_reserve_method<std::array<int as the first argument
// and 1>>::value as the second argument. We need an alias to avoid
// this problem.
using reservable_type = std::vector<int> ;
using nonreservable_type = std::array<int, 1>;
BOOST_TEST( toml::detail::has_reserve_method<reservable_type >::value);
BOOST_TEST(!toml::detail::has_reserve_method<nonreservable_type>::value);
} }
{ {
std::vector<int> v; std::vector<int> v;
toml::resize(v, 10); toml::try_reserve(v, 100);
BOOST_TEST(v.size() == 10u); BOOST_TEST(v.capacity() == 100u);
}
{
std::array<int, 15> a;
toml::resize(a, 10);
BOOST_TEST(a.size() == 15u);
}
{
std::array<int, 15> a;
BOOST_CHECK_THROW(toml::resize(a, 20), std::invalid_argument);
} }
} }

View File

@@ -966,3 +966,54 @@ BOOST_AUTO_TEST_CASE(test_value_bracket)
BOOST_CHECK_THROW(v1["foo"], toml::type_error); BOOST_CHECK_THROW(v1["foo"], toml::type_error);
} }
} }
BOOST_AUTO_TEST_CASE(test_value_map_methods)
{
{
toml::value v1{{"foo", 42}, {"bar", 3.14}, {"baz", "qux"}};
BOOST_TEST(v1.count("foo") == 1u);
BOOST_TEST(v1.count("bar") == 1u);
BOOST_TEST(v1.count("baz") == 1u);
BOOST_TEST(v1.count("qux") == 0u);
BOOST_TEST( v1.contains("foo"));
BOOST_TEST( v1.contains("bar"));
BOOST_TEST( v1.contains("baz"));
BOOST_TEST(!v1.contains("qux"));
BOOST_TEST(v1.size() == 3);
v1["qux"] = 54;
BOOST_TEST(v1.count("qux") == 1u);
BOOST_TEST(v1.contains("qux"));
BOOST_TEST(v1.size() == 4);
}
{
toml::value v1(42);
BOOST_CHECK_THROW(v1.size() , toml::type_error);
BOOST_CHECK_THROW(v1.count("k") , toml::type_error);
BOOST_CHECK_THROW(v1.contains("k"), toml::type_error);
}
}
BOOST_AUTO_TEST_CASE(test_value_vector_methods)
{
{
toml::value v1{1, 2, 3, 4, 5};
BOOST_TEST(v1.size() == 5);
v1.push_back(6);
BOOST_TEST(v1.size() == 6);
v1.emplace_back(6);
BOOST_TEST(v1.size() == 7);
}
{
toml::value v1(42);
BOOST_CHECK_THROW(v1.size(), toml::type_error);
BOOST_CHECK_THROW(v1.push_back(1), toml::type_error);
BOOST_CHECK_THROW(v1.emplace_back(1), toml::type_error);
}
}

12
tests/test_windows.cpp Normal file
View File

@@ -0,0 +1,12 @@
#include <windows.h>
#include <toml.hpp>
#include <iostream>
int main()
{
using namespace toml::literals::toml_literals;
const auto data = R"(windows = "defines min and max as a macro")"_toml;
std::cout << toml::find<std::string>(data, "windows") << std::endl;
return 0;
}

View File

@@ -33,6 +33,10 @@
# error "toml11 requires C++11 or later." # error "toml11 requires C++11 or later."
#endif #endif
#define TOML11_VERSION_MAJOR 3
#define TOML11_VERSION_MINOR 5
#define TOML11_VERSION_PATCH 0
#include "toml/parser.hpp" #include "toml/parser.hpp"
#include "toml/literal.hpp" #include "toml/literal.hpp"
#include "toml/serializer.hpp" #include "toml/serializer.hpp"

View File

@@ -1,7 +1,7 @@
#ifndef TOML11_COLOR_HPP #ifndef TOML11_COLOR_HPP
#define TOML11_COLOR_HPP #define TOML11_COLOR_HPP
#include <ostream>
#include <cstdint> #include <cstdint>
#include <ostream>
#ifdef TOML11_COLORIZE_ERROR_MESSAGE #ifdef TOML11_COLORIZE_ERROR_MESSAGE
#define TOML11_ERROR_MESSAGE_COLORIZED true #define TOML11_ERROR_MESSAGE_COLORIZED true

View File

@@ -2,18 +2,20 @@
// Distributed under the MIT License. // Distributed under the MIT License.
#ifndef TOML11_COMBINATOR_HPP #ifndef TOML11_COMBINATOR_HPP
#define TOML11_COMBINATOR_HPP #define TOML11_COMBINATOR_HPP
#include "traits.hpp"
#include "result.hpp"
#include "utility.hpp"
#include "region.hpp"
#include <type_traits>
#include <iterator>
#include <limits>
#include <array>
#include <iomanip>
#include <cstdio>
#include <cassert> #include <cassert>
#include <cctype> #include <cctype>
#include <cstdio>
#include <array>
#include <iomanip>
#include <iterator>
#include <limits>
#include <type_traits>
#include "region.hpp"
#include "result.hpp"
#include "traits.hpp"
#include "utility.hpp"
// they scans characters and returns region if it matches to the condition. // they scans characters and returns region if it matches to the condition.
// when they fail, it does not change the location. // when they fail, it does not change the location.
@@ -56,13 +58,9 @@ struct character
{ {
static constexpr char target = C; static constexpr char target = C;
template<typename Cont> static result<region, none_t>
static result<region<Cont>, none_t> invoke(location& loc)
invoke(location<Cont>& loc)
{ {
static_assert(std::is_same<char, typename Cont::value_type>::value,
"internal error: container::value_type should be `char`.");
if(loc.iter() == loc.end()) {return none();} if(loc.iter() == loc.end()) {return none();}
const auto first = loc.iter(); const auto first = loc.iter();
@@ -73,7 +71,7 @@ struct character
} }
loc.advance(); // update location loc.advance(); // update location
return ok(region<Cont>(loc, first, loc.iter())); return ok(region(loc, first, loc.iter()));
} }
}; };
template<char C> template<char C>
@@ -89,13 +87,9 @@ struct in_range
static constexpr char upper = Up; static constexpr char upper = Up;
static constexpr char lower = Low; static constexpr char lower = Low;
template<typename Cont> static result<region, none_t>
static result<region<Cont>, none_t> invoke(location& loc)
invoke(location<Cont>& loc)
{ {
static_assert(std::is_same<char, typename Cont::value_type>::value,
"internal error: container::value_type should be `char`.");
if(loc.iter() == loc.end()) {return none();} if(loc.iter() == loc.end()) {return none();}
const auto first = loc.iter(); const auto first = loc.iter();
@@ -106,7 +100,7 @@ struct in_range
} }
loc.advance(); loc.advance();
return ok(region<Cont>(loc, first, loc.iter())); return ok(region(loc, first, loc.iter()));
} }
}; };
template<char L, char U> constexpr char in_range<L, U>::upper; template<char L, char U> constexpr char in_range<L, U>::upper;
@@ -117,13 +111,9 @@ template<char L, char U> constexpr char in_range<L, U>::lower;
template<typename Combinator> template<typename Combinator>
struct exclude struct exclude
{ {
template<typename Cont> static result<region, none_t>
static result<region<Cont>, none_t> invoke(location& loc)
invoke(location<Cont>& loc)
{ {
static_assert(std::is_same<char, typename Cont::value_type>::value,
"internal error: container::value_type should be `char`.");
if(loc.iter() == loc.end()) {return none();} if(loc.iter() == loc.end()) {return none();}
auto first = loc.iter(); auto first = loc.iter();
@@ -134,7 +124,7 @@ struct exclude
return none(); return none();
} }
loc.reset(std::next(first)); // XXX maybe loc.advance() is okay but... loc.reset(std::next(first)); // XXX maybe loc.advance() is okay but...
return ok(region<Cont>(loc, first, loc.iter())); return ok(region(loc, first, loc.iter()));
} }
}; };
@@ -142,19 +132,15 @@ struct exclude
template<typename Combinator> template<typename Combinator>
struct maybe struct maybe
{ {
template<typename Cont> static result<region, none_t>
static result<region<Cont>, none_t> invoke(location& loc)
invoke(location<Cont>& loc)
{ {
static_assert(std::is_same<char, typename Cont::value_type>::value,
"internal error: container::value_type should be `char`.");
const auto rslt = Combinator::invoke(loc); const auto rslt = Combinator::invoke(loc);
if(rslt.is_ok()) if(rslt.is_ok())
{ {
return rslt; return rslt;
} }
return ok(region<Cont>(loc)); return ok(region(loc));
} }
}; };
@@ -164,13 +150,9 @@ struct sequence;
template<typename Head, typename ... Tail> template<typename Head, typename ... Tail>
struct sequence<Head, Tail...> struct sequence<Head, Tail...>
{ {
template<typename Cont> static result<region, none_t>
static result<region<Cont>, none_t> invoke(location& loc)
invoke(location<Cont>& loc)
{ {
static_assert(std::is_same<char, typename Cont::value_type>::value,
"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);
if(rslt.is_err()) if(rslt.is_err())
@@ -182,9 +164,9 @@ struct sequence<Head, Tail...>
} }
// called from the above function only, recursively. // called from the above function only, recursively.
template<typename Cont, typename Iterator> template<typename Iterator>
static result<region<Cont>, none_t> static result<region, none_t>
invoke(location<Cont>& loc, region<Cont> reg, Iterator first) invoke(location& loc, region reg, Iterator first)
{ {
const auto rslt = Head::invoke(loc); const auto rslt = Head::invoke(loc);
if(rslt.is_err()) if(rslt.is_err())
@@ -201,9 +183,9 @@ template<typename Head>
struct sequence<Head> 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 Iterator>
static result<region<Cont>, none_t> static result<region, none_t>
invoke(location<Cont>& loc, region<Cont> reg, Iterator first) invoke(location& loc, region reg, Iterator first)
{ {
const auto rslt = Head::invoke(loc); const auto rslt = Head::invoke(loc);
if(rslt.is_err()) if(rslt.is_err())
@@ -222,13 +204,9 @@ struct either;
template<typename Head, typename ... Tail> template<typename Head, typename ... Tail>
struct either<Head, Tail...> struct either<Head, Tail...>
{ {
template<typename Cont> static result<region, none_t>
static result<region<Cont>, none_t> invoke(location& loc)
invoke(location<Cont>& loc)
{ {
static_assert(std::is_same<char, typename Cont::value_type>::value,
"internal error: container::value_type should be `char`.");
const auto rslt = Head::invoke(loc); const auto rslt = Head::invoke(loc);
if(rslt.is_ok()) {return rslt;} if(rslt.is_ok()) {return rslt;}
return either<Tail...>::invoke(loc); return either<Tail...>::invoke(loc);
@@ -237,12 +215,9 @@ struct either<Head, Tail...>
template<typename Head> template<typename Head>
struct either<Head> struct either<Head>
{ {
template<typename Cont> static result<region, none_t>
static result<region<Cont>, none_t> invoke(location& loc)
invoke(location<Cont>& loc)
{ {
static_assert(std::is_same<char, typename Cont::value_type>::value,
"internal error: container::value_type should be `char`.");
return Head::invoke(loc); return Head::invoke(loc);
} }
}; };
@@ -257,11 +232,10 @@ struct unlimited{};
template<typename T, std::size_t N> template<typename T, std::size_t N>
struct repeat<T, exactly<N>> struct repeat<T, exactly<N>>
{ {
template<typename Cont> static result<region, none_t>
static result<region<Cont>, none_t> invoke(location& loc)
invoke(location<Cont>& loc)
{ {
region<Cont> retval(loc); region 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)
{ {
@@ -280,11 +254,10 @@ struct repeat<T, exactly<N>>
template<typename T, std::size_t N> template<typename T, std::size_t N>
struct repeat<T, at_least<N>> struct repeat<T, at_least<N>>
{ {
template<typename Cont> static result<region, none_t>
static result<region<Cont>, none_t> invoke(location& loc)
invoke(location<Cont>& loc)
{ {
region<Cont> retval(loc); region 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)
@@ -312,11 +285,10 @@ struct repeat<T, at_least<N>>
template<typename T> template<typename T>
struct repeat<T, unlimited> struct repeat<T, unlimited>
{ {
template<typename Cont> static result<region, none_t>
static result<region<Cont>, none_t> invoke(location& loc)
invoke(location<Cont>& loc)
{ {
region<Cont> retval(loc); region retval(loc);
while(true) while(true)
{ {
auto rslt = T::invoke(loc); auto rslt = T::invoke(loc);

View File

@@ -2,12 +2,12 @@
// Distributed under the MIT License. // Distributed under the MIT License.
#ifndef TOML11_COMMENTS_HPP #ifndef TOML11_COMMENTS_HPP
#define TOML11_COMMENTS_HPP #define TOML11_COMMENTS_HPP
#include <initializer_list>
#include <iterator>
#include <string>
#include <type_traits> #include <type_traits>
#include <utility> #include <utility>
#include <iterator>
#include <initializer_list>
#include <vector> #include <vector>
#include <string>
// This file provides mainly two classes, `preserve_comments` and `discard_comments`. // This file provides mainly two classes, `preserve_comments` and `discard_comments`.
// Those two are a container that have the same interface as `std::vector<std::string>` // Those two are a container that have the same interface as `std::vector<std::string>`
@@ -81,6 +81,54 @@ struct preserve_comments
void assign(std::initializer_list<std::string> ini) {comments.assign(ini);} void assign(std::initializer_list<std::string> ini) {comments.assign(ini);}
void assign(size_type n, const std::string& val) {comments.assign(n, val);} void assign(size_type n, const std::string& val) {comments.assign(n, val);}
// Related to the issue #97.
//
// It is known that `std::vector::insert` and `std::vector::erase` in
// the standard library implementation included in GCC 4.8.5 takes
// `std::vector::iterator` instead of `std::vector::const_iterator`.
// Because of the const-correctness, we cannot convert a `const_iterator` to
// an `iterator`. It causes compilation error in GCC 4.8.5.
#if defined(__GNUC__) && defined(__GNUC_MINOR__) && defined(__GNUC_PATCHLEVEL__) && !defined(__clang__)
# if (__GNUC__ * 10000 + __GNUC_MINOR__ * 100 + __GNUC_PATCHLEVEL__) <= 40805
# define TOML11_WORKAROUND_GCC_4_8_X_STANDARD_LIBRARY_IMPLEMENTATION
# endif
#endif
#ifdef TOML11_WORKAROUND_GCC_4_8_X_STANDARD_LIBRARY_IMPLEMENTATION
iterator insert(iterator p, const std::string& x)
{
return comments.insert(p, x);
}
iterator insert(iterator p, std::string&& x)
{
return comments.insert(p, std::move(x));
}
void insert(iterator p, size_type n, const std::string& x)
{
return comments.insert(p, n, x);
}
template<typename InputIterator>
void insert(iterator p, InputIterator first, InputIterator last)
{
return comments.insert(p, first, last);
}
void insert(iterator p, std::initializer_list<std::string> ini)
{
return comments.insert(p, ini);
}
template<typename ... Ts>
iterator emplace(iterator p, Ts&& ... args)
{
return comments.emplace(p, std::forward<Ts>(args)...);
}
iterator erase(iterator pos) {return comments.erase(pos);}
iterator erase(iterator first, iterator last)
{
return comments.erase(first, last);
}
#else
iterator insert(const_iterator p, const std::string& x) iterator insert(const_iterator p, const std::string& x)
{ {
return comments.insert(p, x); return comments.insert(p, x);
@@ -114,6 +162,7 @@ struct preserve_comments
{ {
return comments.erase(first, last); return comments.erase(first, last);
} }
#endif
void swap(preserve_comments& other) {comments.swap(other.comments);} void swap(preserve_comments& other) {comments.swap(other.comments);}

View File

@@ -2,15 +2,16 @@
// Distributed under the MIT License. // Distributed under the MIT License.
#ifndef TOML11_DATETIME_HPP #ifndef TOML11_DATETIME_HPP
#define TOML11_DATETIME_HPP #define TOML11_DATETIME_HPP
#include <chrono>
#include <tuple>
#include <array>
#include <ostream>
#include <iomanip>
#include <cstdint> #include <cstdint>
#include <cstdlib> #include <cstdlib>
#include <ctime> #include <ctime>
#include <array>
#include <chrono>
#include <iomanip>
#include <ostream>
#include <tuple>
namespace toml namespace toml
{ {
@@ -20,7 +21,7 @@ namespace toml
namespace detail namespace detail
{ {
// TODO: find more sophisticated way to handle this // TODO: find more sophisticated way to handle this
#if _POSIX_C_SOURCE >= 1 || defined(_XOPEN_SOURCE) || defined(_BSD_SOURCE) || defined(_SVID_SOURCE) || defined(_POSIX_SOURCE) #if (defined(_POSIX_C_SOURCE) && _POSIX_C_SOURCE >= 1) || defined(_XOPEN_SOURCE) || defined(_BSD_SOURCE) || defined(_SVID_SOURCE) || defined(_POSIX_SOURCE)
inline std::tm localtime_s(const std::time_t* src) inline std::tm localtime_s(const std::time_t* src)
{ {
std::tm dst; std::tm dst;
@@ -35,7 +36,7 @@ inline std::tm gmtime_s(const std::time_t* src)
if (!result) { throw std::runtime_error("gmtime_r failed."); } if (!result) { throw std::runtime_error("gmtime_r failed."); }
return dst; return dst;
} }
#elif _MSC_VER #elif defined(_MSC_VER)
inline std::tm localtime_s(const std::time_t* src) inline std::tm localtime_s(const std::time_t* src)
{ {
std::tm dst; std::tm dst;
@@ -172,9 +173,9 @@ template<typename charT, typename traits>
std::basic_ostream<charT, traits>& std::basic_ostream<charT, traits>&
operator<<(std::basic_ostream<charT, traits>& os, const local_date& date) operator<<(std::basic_ostream<charT, traits>& os, const local_date& date)
{ {
os << std::setfill('0') << std::setw(4) << static_cast<int>(date.year ) << '-'; os << std::setfill('0') << std::setw(4) << static_cast<int>(date.year ) << '-';
os << std::setfill('0') << std::setw(2) << static_cast<int>(date.month + 1) << '-'; os << std::setfill('0') << std::setw(2) << static_cast<int>(date.month) + 1 << '-';
os << std::setfill('0') << std::setw(2) << static_cast<int>(date.day ); os << std::setfill('0') << std::setw(2) << static_cast<int>(date.day ) ;
return os; return os;
} }

View File

@@ -2,17 +2,18 @@
// Distributed under the MIT License. // Distributed under the MIT License.
#ifndef TOML11_EXCEPTION_HPP #ifndef TOML11_EXCEPTION_HPP
#define TOML11_EXCEPTION_HPP #define TOML11_EXCEPTION_HPP
#include "source_location.hpp"
#include <stdexcept> #include <stdexcept>
#include <string> #include <string>
#include "source_location.hpp"
namespace toml namespace toml
{ {
struct exception : public std::exception struct exception : public std::exception
{ {
public: public:
exception(const source_location& loc): loc_(loc) {} explicit exception(const source_location& loc): loc_(loc) {}
virtual ~exception() noexcept override = default; virtual ~exception() noexcept override = default;
virtual const char* what() const noexcept override {return "";} virtual const char* what() const noexcept override {return "";}
virtual source_location const& location() const noexcept {return loc_;} virtual source_location const& location() const noexcept {return loc_;}

View File

@@ -2,10 +2,11 @@
// Distributed under the MIT License. // Distributed under the MIT License.
#ifndef TOML11_GET_HPP #ifndef TOML11_GET_HPP
#define TOML11_GET_HPP #define TOML11_GET_HPP
#include <algorithm>
#include "from.hpp" #include "from.hpp"
#include "result.hpp" #include "result.hpp"
#include "value.hpp" #include "value.hpp"
#include <algorithm>
namespace toml namespace toml
{ {
@@ -188,8 +189,7 @@ get(const basic_value<C, M, V>& v)
{ {
throw type_error(detail::format_underline("toml::value: " throw type_error(detail::format_underline("toml::value: "
"bad_cast to std::chrono::system_clock::time_point", { "bad_cast to std::chrono::system_clock::time_point", {
{std::addressof(detail::get_region(v)), {v.location(), concat_to_string("the actual type is ", v.type())}
concat_to_string("the actual type is ", v.type())}
}), v.location()); }), v.location());
} }
} }
@@ -198,24 +198,24 @@ get(const basic_value<C, M, V>& v)
// ============================================================================ // ============================================================================
// forward declaration to use this recursively. ignore this and go ahead. // forward declaration to use this recursively. ignore this and go ahead.
// array-like type with resize(N) method // array-like type with push_back(value) method
template<typename T, typename C, template<typename T, typename C,
template<typename ...> class M, template<typename ...> class V> template<typename ...> class M, template<typename ...> class V>
detail::enable_if_t<detail::conjunction< detail::enable_if_t<detail::conjunction<
detail::is_container<T>, // T is container detail::is_container<T>, // T is a container
detail::has_resize_method<T>, // T::resize(N) works detail::has_push_back_method<T>, // T::push_back(value) works
detail::negation< // but not toml::array detail::negation< // but not toml::array
detail::is_exact_toml_type<T, basic_value<C, M, V>>> detail::is_exact_toml_type<T, basic_value<C, M, V>>>
>::value, T> >::value, T>
get(const basic_value<C, M, V>&); get(const basic_value<C, M, V>&);
// array-like type with resize(N) method // array-like type without push_back(value) method
template<typename T, typename C, template<typename T, typename C,
template<typename ...> class M, template<typename ...> class V> template<typename ...> class M, template<typename ...> class V>
detail::enable_if_t<detail::conjunction< detail::enable_if_t<detail::conjunction<
detail::is_container<T>, // T is container detail::is_container<T>, // T is a container
detail::negation<detail::has_resize_method<T>>, // no T::resize() exists detail::negation<detail::has_push_back_method<T>>, // w/o push_back(...)
detail::negation< // not toml::array detail::negation< // not toml::array
detail::is_exact_toml_type<T, basic_value<C, M, V>>> detail::is_exact_toml_type<T, basic_value<C, M, V>>>
>::value, T> >::value, T>
get(const basic_value<C, M, V>&); get(const basic_value<C, M, V>&);
@@ -259,37 +259,69 @@ template<typename T, typename C,
std::size_t S = sizeof(::toml::from<T>)> std::size_t S = sizeof(::toml::from<T>)>
T get(const basic_value<C, M, V>&); T get(const basic_value<C, M, V>&);
// T(const toml::value&) and T is not toml::basic_value
template<typename T, typename C,
template<typename ...> class M, template<typename ...> class V>
detail::enable_if_t<detail::conjunction<
detail::negation<detail::is_basic_value<T>>,
std::is_constructible<T, const basic_value<C, M, V>&>
>::value, T>
get(const basic_value<C, M, V>&);
// ============================================================================ // ============================================================================
// array-like types; most likely STL container, like std::vector, etc. // array-like types; most likely STL container, like std::vector, etc.
template<typename T, typename C, template<typename T, typename C,
template<typename ...> class M, template<typename ...> class V> template<typename ...> class M, template<typename ...> class V>
detail::enable_if_t<detail::conjunction< detail::enable_if_t<detail::conjunction<
detail::is_container<T>, // T is container detail::is_container<T>, // T is a container
detail::has_resize_method<T>, // T::resize(N) works detail::has_push_back_method<T>, // container.push_back(elem) works
detail::negation< // but not toml::array detail::negation< // but not toml::array
detail::is_exact_toml_type<T, basic_value<C, M, V>>> detail::is_exact_toml_type<T, basic_value<C, M, V>>>
>::value, T> >::value, T>
get(const basic_value<C, M, V>& v) get(const basic_value<C, M, V>& v)
{ {
using value_type = typename T::value_type; using value_type = typename T::value_type;
const auto& ar = v.as_array(); const auto& ary = v.as_array();
T container; T container;
container.resize(ar.size()); try_reserve(container, ary.size());
std::transform(ar.cbegin(), ar.cend(), container.begin(),
[](const value& x){return ::toml::get<value_type>(x);}); for(const auto& elem : ary)
{
container.push_back(get<value_type>(elem));
}
return container; return container;
} }
// ============================================================================ // ============================================================================
// array-like types; but does not have resize(); most likely std::array. // std::forward_list does not have push_back, insert, or emplace.
// It has insert_after, emplace_after, push_front.
template<typename T, typename C,
template<typename ...> class M, template<typename ...> class V>
detail::enable_if_t<detail::is_std_forward_list<T>::value, T>
get(const basic_value<C, M, V>& v)
{
using value_type = typename T::value_type;
T container;
for(const auto& elem : v.as_array())
{
container.push_front(get<value_type>(elem));
}
container.reverse();
return container;
}
// ============================================================================
// array-like types, without push_back(). most likely [std|boost]::array.
template<typename T, typename C, template<typename T, typename C,
template<typename ...> class M, template<typename ...> class V> template<typename ...> class M, template<typename ...> class V>
detail::enable_if_t<detail::conjunction< detail::enable_if_t<detail::conjunction<
detail::is_container<T>, // T is container detail::is_container<T>, // T is a container
detail::negation<detail::has_resize_method<T>>, // no T::resize() exists detail::negation<detail::has_push_back_method<T>>, // w/o push_back
detail::negation< // but not toml::array detail::negation< // T is not toml::array
detail::is_exact_toml_type<T, basic_value<C, M, V>>> detail::is_exact_toml_type<T, basic_value<C, M, V>>>
>::value, T> >::value, T>
get(const basic_value<C, M, V>& v) get(const basic_value<C, M, V>& v)
@@ -303,7 +335,7 @@ get(const basic_value<C, M, V>& v)
throw std::out_of_range(detail::format_underline(concat_to_string( throw std::out_of_range(detail::format_underline(concat_to_string(
"toml::get: specified container size is ", container.size(), "toml::get: specified container size is ", container.size(),
" but there are ", ar.size(), " elements in toml array."), { " but there are ", ar.size(), " elements in toml array."), {
{std::addressof(detail::get_region(v)), "here"} {v.location(), "here"}
})); }));
} }
std::transform(ar.cbegin(), ar.cend(), container.begin(), std::transform(ar.cbegin(), ar.cend(), container.begin(),
@@ -327,9 +359,7 @@ get(const basic_value<C, M, V>& v)
{ {
throw std::out_of_range(detail::format_underline(concat_to_string( throw std::out_of_range(detail::format_underline(concat_to_string(
"toml::get: specified std::pair but there are ", ar.size(), "toml::get: specified std::pair but there are ", ar.size(),
" elements in toml array."), { " elements in toml array."), {{v.location(), "here"}}));
{std::addressof(detail::get_region(v)), "here"}
}));
} }
return std::make_pair(::toml::get<first_type >(ar.at(0)), return std::make_pair(::toml::get<first_type >(ar.at(0)),
::toml::get<second_type>(ar.at(1))); ::toml::get<second_type>(ar.at(1)));
@@ -359,9 +389,7 @@ get(const basic_value<C, M, V>& v)
throw std::out_of_range(detail::format_underline(concat_to_string( throw std::out_of_range(detail::format_underline(concat_to_string(
"toml::get: specified std::tuple with ", "toml::get: specified std::tuple with ",
std::tuple_size<T>::value, " elements, but there are ", ar.size(), std::tuple_size<T>::value, " elements, but there are ", ar.size(),
" elements in toml array."), { " elements in toml array."), {{v.location(), "here"}}));
{std::addressof(detail::get_region(v)), "here"}
}));
} }
return detail::get_tuple_impl<T>(ar, return detail::get_tuple_impl<T>(ar,
detail::make_index_sequence<std::tuple_size<T>::value>{}); detail::make_index_sequence<std::tuple_size<T>::value>{});
@@ -387,7 +415,7 @@ get(const basic_value<C, M, V>& v)
T map; T map;
for(const auto& kv : v.as_table()) for(const auto& kv : v.as_table())
{ {
map[key_type(kv.first)] = ::toml::get<mapped_type>(kv.second); map.emplace(key_type(kv.first), get<mapped_type>(kv.second));
} }
return map; return map;
} }
@@ -417,6 +445,17 @@ T get(const basic_value<C, M, V>& v)
return ::toml::from<T>::from_toml(v); return ::toml::from<T>::from_toml(v);
} }
template<typename T, typename C,
template<typename ...> class M, template<typename ...> class V>
detail::enable_if_t<detail::conjunction<
detail::negation<detail::is_basic_value<T>>,
std::is_constructible<T, const basic_value<C, M, V>&>
>::value, T>
get(const basic_value<C, M, V>& v)
{
return T(v);
}
// ============================================================================ // ============================================================================
// find // find
@@ -429,10 +468,7 @@ basic_value<C, M, V> const& find(const basic_value<C, M, V>& v, const key& ky)
const auto& tab = v.as_table(); const auto& tab = v.as_table();
if(tab.count(ky) == 0) if(tab.count(ky) == 0)
{ {
throw std::out_of_range(detail::format_underline(concat_to_string( detail::throw_key_not_found_error(v, ky);
"key \"", ky, "\" not found"), {
{std::addressof(detail::get_region(v)), "in this table"}
}));
} }
return tab.at(ky); return tab.at(ky);
} }
@@ -443,10 +479,7 @@ basic_value<C, M, V>& find(basic_value<C, M, V>& v, const key& ky)
auto& tab = v.as_table(); auto& tab = v.as_table();
if(tab.count(ky) == 0) if(tab.count(ky) == 0)
{ {
throw std::out_of_range(detail::format_underline(concat_to_string( detail::throw_key_not_found_error(v, ky);
"key \"", ky, "\" not found"), {
{std::addressof(detail::get_region(v)), "in this table"}
}));
} }
return tab.at(ky); return tab.at(ky);
} }
@@ -457,14 +490,51 @@ basic_value<C, M, V> find(basic_value<C, M, V>&& v, const key& ky)
typename basic_value<C, M, V>::table_type tab = std::move(v).as_table(); typename basic_value<C, M, V>::table_type tab = std::move(v).as_table();
if(tab.count(ky) == 0) if(tab.count(ky) == 0)
{ {
throw std::out_of_range(detail::format_underline(concat_to_string( detail::throw_key_not_found_error(v, ky);
"key \"", ky, "\" not found"), {
{std::addressof(detail::get_region(v)), "in this table"}
}));
} }
return basic_value<C, M, V>(std::move(tab.at(ky))); return basic_value<C, M, V>(std::move(tab.at(ky)));
} }
// ----------------------------------------------------------------------------
// find(value, idx)
template<typename C,
template<typename ...> class M, template<typename ...> class V>
basic_value<C, M, V> const&
find(const basic_value<C, M, V>& v, const std::size_t idx)
{
const auto& ary = v.as_array();
if(ary.size() <= idx)
{
throw std::out_of_range(detail::format_underline(concat_to_string(
"index ", idx, " is out of range"), {{v.location(), "in this array"}}));
}
return ary.at(idx);
}
template<typename C,
template<typename ...> class M, template<typename ...> class V>
basic_value<C, M, V>& find(basic_value<C, M, V>& v, const std::size_t idx)
{
auto& ary = v.as_array();
if(ary.size() <= idx)
{
throw std::out_of_range(detail::format_underline(concat_to_string(
"index ", idx, " is out of range"), {{v.location(), "in this array"}}));
}
return ary.at(idx);
}
template<typename C,
template<typename ...> class M, template<typename ...> class V>
basic_value<C, M, V> find(basic_value<C, M, V>&& v, const std::size_t idx)
{
auto& ary = v.as_array();
if(ary.size() <= idx)
{
throw std::out_of_range(detail::format_underline(concat_to_string(
"index ", idx, " is out of range"), {{v.location(), "in this array"}}));
}
return basic_value<C, M, V>(std::move(ary.at(idx)));
}
// ---------------------------------------------------------------------------- // ----------------------------------------------------------------------------
// find<T>(value, key); // find<T>(value, key);
@@ -476,10 +546,7 @@ find(const basic_value<C, M, V>& v, const key& ky)
const auto& tab = v.as_table(); const auto& tab = v.as_table();
if(tab.count(ky) == 0) if(tab.count(ky) == 0)
{ {
throw std::out_of_range(detail::format_underline(concat_to_string( detail::throw_key_not_found_error(v, ky);
"key \"", ky, "\" not found"), {
{std::addressof(detail::get_region(v)), "in this table"}
}));
} }
return ::toml::get<T>(tab.at(ky)); return ::toml::get<T>(tab.at(ky));
} }
@@ -492,10 +559,7 @@ find(basic_value<C, M, V>& v, const key& ky)
auto& tab = v.as_table(); auto& tab = v.as_table();
if(tab.count(ky) == 0) if(tab.count(ky) == 0)
{ {
throw std::out_of_range(detail::format_underline(concat_to_string( detail::throw_key_not_found_error(v, ky);
"key \"", ky, "\" not found"), {
{std::addressof(detail::get_region(v)), "in this table"}
}));
} }
return ::toml::get<T>(tab.at(ky)); return ::toml::get<T>(tab.at(ky));
} }
@@ -508,65 +572,151 @@ find(basic_value<C, M, V>&& v, const key& ky)
typename basic_value<C, M, V>::table_type tab = std::move(v).as_table(); typename basic_value<C, M, V>::table_type tab = std::move(v).as_table();
if(tab.count(ky) == 0) if(tab.count(ky) == 0)
{ {
throw std::out_of_range(detail::format_underline(concat_to_string( detail::throw_key_not_found_error(v, ky);
"key \"", ky, "\" not found"), {
{std::addressof(detail::get_region(v)), "in this table"}
}));
} }
return ::toml::get<T>(std::move(tab.at(ky))); return ::toml::get<T>(std::move(tab.at(ky)));
} }
// ----------------------------------------------------------------------------
// find<T>(value, idx)
template<typename T, typename C,
template<typename ...> class M, template<typename ...> class V>
decltype(::toml::get<T>(std::declval<basic_value<C, M, V> const&>()))
find(const basic_value<C, M, V>& v, const std::size_t idx)
{
const auto& ary = v.as_array();
if(ary.size() <= idx)
{
throw std::out_of_range(detail::format_underline(concat_to_string(
"index ", idx, " is out of range"), {{v.location(), "in this array"}}));
}
return ::toml::get<T>(ary.at(idx));
}
template<typename T, typename C,
template<typename ...> class M, template<typename ...> class V>
decltype(::toml::get<T>(std::declval<basic_value<C, M, V>&>()))
find(basic_value<C, M, V>& v, const std::size_t idx)
{
auto& ary = v.as_array();
if(ary.size() <= idx)
{
throw std::out_of_range(detail::format_underline(concat_to_string(
"index ", idx, " is out of range"), {{v.location(), "in this array"}}));
}
return ::toml::get<T>(ary.at(idx));
}
template<typename T, typename C,
template<typename ...> class M, template<typename ...> class V>
decltype(::toml::get<T>(std::declval<basic_value<C, M, V>&&>()))
find(basic_value<C, M, V>&& v, const std::size_t idx)
{
typename basic_value<C, M, V>::array_type ary = std::move(v).as_array();
if(ary.size() <= idx)
{
throw std::out_of_range(detail::format_underline(concat_to_string(
"index ", idx, " is out of range"), {{v.location(), "in this array"}}));
}
return ::toml::get<T>(std::move(ary.at(idx)));
}
// -------------------------------------------------------------------------- // --------------------------------------------------------------------------
// toml::find(toml::value, toml::key, Ts&& ... keys) // toml::find(toml::value, toml::key, Ts&& ... keys)
namespace detail
{
// It suppresses warnings by -Wsign-conversion. Let's say we have the following
// code.
// ```cpp
// const auto x = toml::find<std::string>(data, "array", 0);
// ```
// Here, the type of literal number `0` is `int`. `int` is a signed integer.
// `toml::find` takes `std::size_t` as an index. So it causes implicit sign
// conversion and `-Wsign-conversion` warns about it. Using `0u` instead of `0`
// suppresses the warning, but it makes user code messy.
// To suppress this warning, we need to be aware of type conversion caused
// by `toml::find(v, key1, key2, ... keys)`. But the thing is that the types of
// keys can be any combination of {string-like, size_t-like}. Of course we can't
// write down all the combinations. Thus we need to use some function that
// recognize the type of argument and cast it into `std::string` or
// `std::size_t` depending on the context.
// `key_cast` does the job. It has 2 overloads. One is invoked when the
// argument type is an integer and cast the argument into `std::size_t`. The
// other is invoked when the argument type is not an integer, possibly one of
// std::string, const char[N] or const char*, and construct std::string from
// the argument.
// `toml::find(v, k1, k2, ... ks)` uses `key_cast` before passing `ks` to
// `toml::find(v, k)` to suppress -Wsign-conversion.
template<typename T>
enable_if_t<conjunction<std::is_integral<remove_cvref_t<T>>,
negation<std::is_same<remove_cvref_t<T>, bool>>>::value, std::size_t>
key_cast(T&& v) noexcept
{
return std::size_t(v);
}
template<typename T>
enable_if_t<negation<conjunction<std::is_integral<remove_cvref_t<T>>,
negation<std::is_same<remove_cvref_t<T>, bool>>>>::value, std::string>
key_cast(T&& v) noexcept
{
return std::string(std::forward<T>(v));
}
} // detail
template<typename C, template<typename C,
template<typename ...> class M, template<typename ...> class V, template<typename ...> class M, template<typename ...> class V,
typename ... Ts> typename Key1, typename Key2, typename ... Keys>
const basic_value<C, M, V>& const basic_value<C, M, V>&
find(const basic_value<C, M, V>& v, const ::toml::key& ky, Ts&& ... keys) find(const basic_value<C, M, V>& v, Key1&& k1, Key2&& k2, Keys&& ... keys)
{ {
return ::toml::find(::toml::find(v, ky), std::forward<Ts>(keys)...); return ::toml::find(::toml::find(v, detail::key_cast(k1)),
detail::key_cast(k2), std::forward<Keys>(keys)...);
} }
template<typename C, template<typename C,
template<typename ...> class M, template<typename ...> class V, template<typename ...> class M, template<typename ...> class V,
typename ... Ts> typename Key1, typename Key2, typename ... Keys>
basic_value<C, M, V>& basic_value<C, M, V>&
find(basic_value<C, M, V>& v, const ::toml::key& ky, Ts&& ... keys) find(basic_value<C, M, V>& v, Key1&& k1, Key2&& k2, Keys&& ... keys)
{ {
return ::toml::find(::toml::find(v, ky), std::forward<Ts>(keys)...); return ::toml::find(::toml::find(v, detail::key_cast(k1)),
detail::key_cast(k2), std::forward<Keys>(keys)...);
} }
template<typename C, template<typename C,
template<typename ...> class M, template<typename ...> class V, template<typename ...> class M, template<typename ...> class V,
typename ... Ts> typename Key1, typename Key2, typename ... Keys>
basic_value<C, M, V> basic_value<C, M, V>
find(basic_value<C, M, V>&& v, const ::toml::key& ky, Ts&& ... keys) find(basic_value<C, M, V>&& v, Key1&& k1, Key2&& k2, Keys&& ... keys)
{ {
return ::toml::find(::toml::find(std::move(v), ky), std::forward<Ts>(keys)...); return ::toml::find(::toml::find(std::move(v), std::forward<Key1>(k1)),
detail::key_cast(k2), std::forward<Keys>(keys)...);
} }
template<typename T, typename C, template<typename T, typename C,
template<typename ...> class M, template<typename ...> class V, template<typename ...> class M, template<typename ...> class V,
typename ... Ts> typename Key1, typename Key2, typename ... Keys>
decltype(::toml::get<T>(std::declval<const basic_value<C, M, V>&>())) decltype(::toml::get<T>(std::declval<const basic_value<C, M, V>&>()))
find(const basic_value<C, M, V>& v, const ::toml::key& ky, Ts&& ... keys) find(const basic_value<C, M, V>& v, Key1&& k1, Key2&& k2, Keys&& ... keys)
{ {
return ::toml::find<T>(::toml::find(v, ky), std::forward<Ts>(keys)...); return ::toml::find<T>(::toml::find(v, detail::key_cast(k1)),
detail::key_cast(k2), std::forward<Keys>(keys)...);
} }
template<typename T, typename C, template<typename T, typename C,
template<typename ...> class M, template<typename ...> class V, template<typename ...> class M, template<typename ...> class V,
typename ... Ts> typename Key1, typename Key2, typename ... Keys>
decltype(::toml::get<T>(std::declval<basic_value<C, M, V>&>())) decltype(::toml::get<T>(std::declval<basic_value<C, M, V>&>()))
find(basic_value<C, M, V>& v, const ::toml::key& ky, Ts&& ... keys) find(basic_value<C, M, V>& v, Key1&& k1, Key2&& k2, Keys&& ... keys)
{ {
return ::toml::find<T>(::toml::find(v, ky), std::forward<Ts>(keys)...); return ::toml::find<T>(::toml::find(v, detail::key_cast(k1)),
detail::key_cast(k2), std::forward<Keys>(keys)...);
} }
template<typename T, typename C, template<typename T, typename C,
template<typename ...> class M, template<typename ...> class V, template<typename ...> class M, template<typename ...> class V,
typename ... Ts> typename Key1, typename Key2, typename ... Keys>
decltype(::toml::get<T>(std::declval<basic_value<C, M, V>&&>())) decltype(::toml::get<T>(std::declval<basic_value<C, M, V>&&>()))
find(basic_value<C, M, V>&& v, const ::toml::key& ky, Ts&& ... keys) find(basic_value<C, M, V>&& v, Key1&& k1, Key2&& k2, Keys&& ... keys)
{ {
return ::toml::find<T>(::toml::find(std::move(v), ky), std::forward<Ts>(keys)...); return ::toml::find<T>(::toml::find(std::move(v), detail::key_cast(k1)),
detail::key_cast(k2), std::forward<Keys>(keys)...);
} }
// ============================================================================ // ============================================================================

View File

@@ -2,12 +2,13 @@
// Distributed under the MIT License. // Distributed under the MIT License.
#ifndef TOML11_LEXER_HPP #ifndef TOML11_LEXER_HPP
#define TOML11_LEXER_HPP #define TOML11_LEXER_HPP
#include "combinator.hpp"
#include <stdexcept>
#include <istream> #include <istream>
#include <sstream> #include <sstream>
#include <stdexcept>
#include <fstream> #include <fstream>
#include "combinator.hpp"
namespace toml namespace toml
{ {
namespace detail namespace detail
@@ -69,15 +70,8 @@ using lex_zero_prefixable_int = sequence<lex_digit, repeat<either<lex_digit,
using lex_fractional_part = sequence<character<'.'>, lex_zero_prefixable_int>; using lex_fractional_part = sequence<character<'.'>, lex_zero_prefixable_int>;
#ifdef TOML11_USE_UNRELEASED_TOML_FEATURES
// use toml-lang/toml HEAD
using lex_exponent_part = sequence<either<character<'e'>, character<'E'>>, using lex_exponent_part = sequence<either<character<'e'>, character<'E'>>,
maybe<lex_sign>, lex_zero_prefixable_int>; maybe<lex_sign>, lex_zero_prefixable_int>;
#else
// strictly TOML v0.5.0
using lex_exponent_part = sequence<either<character<'e'>, character<'E'>>,
lex_dec_int>;
#endif
using lex_float = either<lex_special_float, using lex_float = either<lex_special_float,
sequence<lex_dec_int, either<lex_exponent_part, sequence<lex_dec_int, either<lex_exponent_part,
@@ -125,17 +119,11 @@ using lex_local_time = lex_partial_time;
// =========================================================================== // ===========================================================================
using lex_quotation_mark = character<'"'>; using lex_quotation_mark = character<'"'>;
#ifdef TOML11_USE_UNRELEASED_TOML_FEATURES
using lex_basic_unescaped = exclude<either<in_range<0x00, 0x08>, // 0x09 (tab) using lex_basic_unescaped = exclude<either<in_range<0x00, 0x08>, // 0x09 (tab)
in_range<0x0a, 0x1F>, // is allowed in_range<0x0a, 0x1F>, // is allowed
character<0x22>, character<0x5C>, character<0x22>, character<0x5C>,
character<0x7F>>>; character<0x7F>>>;
#else
using lex_basic_unescaped = exclude<either<in_range<0x00, 0x1F>,
character<0x22>, character<0x5C>,
character<0x7F>>>;
#endif
using lex_escape = character<'\\'>; using lex_escape = character<'\\'>;
using lex_escape_unicode_short = sequence<character<'u'>, using lex_escape_unicode_short = sequence<character<'u'>,
repeat<lex_hex_dig, exactly<4>>>; repeat<lex_hex_dig, exactly<4>>>;
@@ -154,19 +142,47 @@ using lex_basic_string = sequence<lex_quotation_mark,
repeat<lex_basic_char, unlimited>, repeat<lex_basic_char, unlimited>,
lex_quotation_mark>; lex_quotation_mark>;
// After toml post-v0.5.0, it is explicitly clarified how quotes in ml-strings
// are allowed to be used.
// After this, the following strings are *explicitly* allowed.
// - One or two `"`s in a multi-line basic string is allowed wherever it is.
// - Three consecutive `"`s in a multi-line basic string is considered as a delimiter.
// - One or two `"`s can appear just before or after the delimiter.
// ```toml
// str4 = """Here are two quotation marks: "". Simple enough."""
// str5 = """Here are three quotation marks: ""\"."""
// str6 = """Here are fifteen quotation marks: ""\"""\"""\"""\"""\"."""
// str7 = """"This," she said, "is just a pointless statement.""""
// ```
// In the current implementation (v3.3.0), it is difficult to parse `str7` in
// the above example. It is difficult to recognize `"` at the end of string body
// collectly. It will be misunderstood as a `"""` delimiter and an additional,
// invalid `"`. Like this:
// ```console
// what(): [error] toml::parse_table: invalid line format
// --> hoge.toml
// |
// 13 | str7 = """"This," she said, "is just a pointless statement.""""
// | ^- expected newline, but got '"'.
// ```
// As a quick workaround for this problem, `lex_ml_basic_string_delim` was
// splitted into two, `lex_ml_basic_string_open` and `lex_ml_basic_string_close`.
// `lex_ml_basic_string_open` allows only `"""`. `_close` allows 3-5 `"`s.
// In parse_ml_basic_string() function, the trailing `"`s will be attached to
// the string body.
//
using lex_ml_basic_string_delim = repeat<lex_quotation_mark, exactly<3>>; using lex_ml_basic_string_delim = repeat<lex_quotation_mark, exactly<3>>;
#ifdef TOML11_USE_UNRELEASED_TOML_FEATURES using lex_ml_basic_string_open = lex_ml_basic_string_delim;
using lex_ml_basic_string_close = sequence<
repeat<lex_quotation_mark, exactly<3>>,
maybe<lex_quotation_mark>, maybe<lex_quotation_mark>
>;
using lex_ml_basic_unescaped = exclude<either<in_range<0x00, 0x08>, // 0x09 using lex_ml_basic_unescaped = exclude<either<in_range<0x00, 0x08>, // 0x09
in_range<0x0a, 0x1F>, // is tab in_range<0x0a, 0x1F>, // is tab
character<0x5C>, character<0x5C>, // backslash
character<0x7F>, character<0x7F>, // DEL
lex_ml_basic_string_delim>>; lex_ml_basic_string_delim>>;
#else // TOML v0.5.0
using lex_ml_basic_unescaped = exclude<either<in_range<0x00,0x1F>,
character<0x5C>,
character<0x7F>,
lex_ml_basic_string_delim>>;
#endif
using lex_ml_basic_escaped_newline = sequence< using lex_ml_basic_escaped_newline = sequence<
lex_escape, maybe<lex_ws>, lex_newline, lex_escape, maybe<lex_ws>, lex_newline,
@@ -176,9 +192,9 @@ using lex_ml_basic_char = either<lex_ml_basic_unescaped, lex_escaped>;
using lex_ml_basic_body = repeat<either<lex_ml_basic_char, lex_newline, using lex_ml_basic_body = repeat<either<lex_ml_basic_char, lex_newline,
lex_ml_basic_escaped_newline>, lex_ml_basic_escaped_newline>,
unlimited>; unlimited>;
using lex_ml_basic_string = sequence<lex_ml_basic_string_delim, using lex_ml_basic_string = sequence<lex_ml_basic_string_open,
lex_ml_basic_body, lex_ml_basic_body,
lex_ml_basic_string_delim>; lex_ml_basic_string_close>;
using lex_literal_char = exclude<either<in_range<0x00, 0x08>, using lex_literal_char = exclude<either<in_range<0x00, 0x08>,
in_range<0x10, 0x19>, character<0x27>>>; in_range<0x10, 0x19>, character<0x27>>>;
@@ -187,7 +203,13 @@ using lex_literal_string = sequence<lex_apostrophe,
repeat<lex_literal_char, unlimited>, repeat<lex_literal_char, unlimited>,
lex_apostrophe>; lex_apostrophe>;
// the same reason as above.
using lex_ml_literal_string_delim = repeat<lex_apostrophe, exactly<3>>; using lex_ml_literal_string_delim = repeat<lex_apostrophe, exactly<3>>;
using lex_ml_literal_string_open = lex_ml_literal_string_delim;
using lex_ml_literal_string_close = sequence<
repeat<lex_apostrophe, exactly<3>>,
maybe<lex_apostrophe>, maybe<lex_apostrophe>
>;
using lex_ml_literal_char = exclude<either<in_range<0x00, 0x08>, using lex_ml_literal_char = exclude<either<in_range<0x00, 0x08>,
in_range<0x10, 0x1F>, in_range<0x10, 0x1F>,
@@ -195,9 +217,9 @@ using lex_ml_literal_char = exclude<either<in_range<0x00, 0x08>,
lex_ml_literal_string_delim>>; lex_ml_literal_string_delim>>;
using lex_ml_literal_body = repeat<either<lex_ml_literal_char, lex_newline>, using lex_ml_literal_body = repeat<either<lex_ml_literal_char, lex_newline>,
unlimited>; unlimited>;
using lex_ml_literal_string = sequence<lex_ml_literal_string_delim, using lex_ml_literal_string = sequence<lex_ml_literal_string_open,
lex_ml_literal_body, lex_ml_literal_body,
lex_ml_literal_string_delim>; lex_ml_literal_string_close>;
using lex_string = either<lex_ml_basic_string, lex_basic_string, using lex_string = either<lex_ml_basic_string, lex_basic_string,
lex_ml_literal_string, lex_literal_string>; lex_ml_literal_string, lex_literal_string>;

View File

@@ -11,12 +11,9 @@ inline namespace literals
inline namespace toml_literals inline namespace toml_literals
{ {
inline ::toml::value operator"" _toml(const char* str, std::size_t len) // implementation
inline ::toml::value literal_internal_impl(::toml::detail::location loc)
{ {
::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. // if there are some comments or empty lines, skip them.
using skip_line = ::toml::detail::repeat<toml::detail::sequence< using skip_line = ::toml::detail::repeat<toml::detail::sequence<
::toml::detail::maybe<::toml::detail::lex_ws>, ::toml::detail::maybe<::toml::detail::lex_ws>,
@@ -76,11 +73,34 @@ inline ::toml::value operator"" _toml(const char* str, std::size_t len)
} }
else // none of them. else // none of them.
{ {
throw ::toml::syntax_error(data.unwrap_err(), throw ::toml::syntax_error(data.unwrap_err(), source_location(loc));
source_location(std::addressof(loc)));
} }
} }
inline ::toml::value operator"" _toml(const char* str, std::size_t len)
{
::toml::detail::location loc(
std::string("TOML literal encoded in a C++ code"),
std::vector<char>(str, str + len));
return literal_internal_impl(std::move(loc));
}
// value of __cplusplus in C++2a/20 mode is not fixed yet along compilers.
// So here we use the feature test macro for `char8_t` itself.
#if defined(__cpp_char8_t) && __cpp_char8_t >= 201811L
// value of u8"" literal has been changed from char to char8_t and char8_t is
// NOT compatible to char
inline ::toml::value operator"" _toml(const char8_t* str, std::size_t len)
{
::toml::detail::location loc(
std::string("TOML literal encoded in a C++ code"),
std::vector<char>(reinterpret_cast<const char*>(str),
reinterpret_cast<const char*>(str) + len));
return literal_internal_impl(std::move(loc));
}
#endif
} // toml_literals } // toml_literals
} // literals } // literals
} // toml } // toml

File diff suppressed because it is too large Load Diff

View File

@@ -41,6 +41,7 @@ struct region_base
region_base& operator=(region_base&& ) = default; region_base& operator=(region_base&& ) = default;
virtual bool is_ok() const noexcept {return false;} virtual bool is_ok() const noexcept {return false;}
virtual char front() const noexcept {return '\0';}
virtual std::string str() const {return std::string("unknown region");} virtual std::string str() const {return std::string("unknown region");}
virtual std::string name() const {return std::string("unknown file");} virtual std::string name() const {return std::string("unknown file");}
@@ -54,7 +55,7 @@ struct region_base
// number of characters in the line after the region // number of characters in the line after the region
virtual std::size_t after() const noexcept {return 0;} virtual std::size_t after() const noexcept {return 0;}
virtual std::vector<std::string> comments()const {return {};} virtual std::vector<std::string> comments() const {return {};}
// ```toml // ```toml
// # comment_before // # comment_before
// key = "value" # comment_inline // key = "value" # comment_inline
@@ -66,22 +67,21 @@ struct region_base
// //
// it contains pointer to the file content and iterator that points the current // it contains pointer to the file content and iterator that points the current
// location. // location.
template<typename Container>
struct location final : public region_base struct location final : public region_base
{ {
using const_iterator = typename Container::const_iterator; using const_iterator = typename std::vector<char>::const_iterator;
using difference_type = typename const_iterator::difference_type; using difference_type = typename const_iterator::difference_type;
using source_ptr = std::shared_ptr<const Container>; using source_ptr = std::shared_ptr<const std::vector<char>>;
static_assert(std::is_same<char, typename Container::value_type>::value,""); location(std::string name, std::vector<char> cont)
static_assert(std::is_same<std::random_access_iterator_tag, : source_(std::make_shared<std::vector<char>>(std::move(cont))),
typename std::iterator_traits<const_iterator>::iterator_category>::value, line_number_(1), source_name_(std::move(name)), iter_(source_->cbegin())
"container should be randomly accessible");
location(std::string name, Container cont)
: source_(std::make_shared<Container>(std::move(cont))), line_number_(1),
source_name_(std::move(name)), iter_(source_->cbegin())
{} {}
location(std::string name, const std::string& cont)
: source_(std::make_shared<std::vector<char>>(cont.begin(), cont.end())),
line_number_(1), source_name_(std::move(name)), iter_(source_->cbegin())
{}
location(const location&) = default; location(const location&) = default;
location(location&&) = default; location(location&&) = default;
location& operator=(const location&) = default; location& operator=(const location&) = default;
@@ -89,6 +89,7 @@ struct location final : public region_base
~location() = default; ~location() = default;
bool is_ok() const noexcept override {return static_cast<bool>(source_);} bool is_ok() const noexcept override {return static_cast<bool>(source_);}
char front() const noexcept override {return *iter_;}
// this const prohibits codes like `++(loc.iter())`. // this const prohibits codes like `++(loc.iter())`.
const const_iterator iter() const noexcept {return iter_;} const const_iterator iter() const noexcept {return iter_;}
@@ -193,33 +194,27 @@ struct location final : public region_base
// //
// it contains pointer to the file content and iterator that points the first // it contains pointer to the file content and iterator that points the first
// and last location. // and last location.
template<typename Container>
struct region final : public region_base struct region final : public region_base
{ {
using const_iterator = typename Container::const_iterator; using const_iterator = typename std::vector<char>::const_iterator;
using source_ptr = std::shared_ptr<const Container>; using source_ptr = std::shared_ptr<const std::vector<char>>;
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;
region(const location<Container>& loc) explicit region(const location& loc)
: source_(loc.source()), source_name_(loc.name()), : source_(loc.source()), source_name_(loc.name()),
first_(loc.iter()), last_(loc.iter()) first_(loc.iter()), last_(loc.iter())
{} {}
region(location<Container>&& loc) explicit region(location&& loc)
: source_(loc.source()), source_name_(loc.name()), : source_(loc.source()), source_name_(loc.name()),
first_(loc.iter()), last_(loc.iter()) first_(loc.iter()), last_(loc.iter())
{} {}
region(const location<Container>& loc, const_iterator f, const_iterator l) region(const location& loc, const_iterator f, const_iterator l)
: source_(loc.source()), source_name_(loc.name()), first_(f), last_(l) : source_(loc.source()), source_name_(loc.name()), first_(f), last_(l)
{} {}
region(location<Container>&& loc, const_iterator f, const_iterator l) region(location&& loc, const_iterator f, const_iterator l)
: source_(loc.source()), source_name_(loc.name()), first_(f), last_(l) : source_(loc.source()), source_name_(loc.name()), first_(f), last_(l)
{} {}
@@ -240,6 +235,7 @@ struct region 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_);}
char front() const noexcept override {return *first_;}
std::string str() const override {return make_string(first_, last_);} std::string str() const override {return make_string(first_, last_);}
std::string line() const override std::string line() const override
@@ -416,106 +412,6 @@ struct region final : public region_base
const_iterator first_, last_; const_iterator first_, last_;
}; };
// to show a better error message.
inline std::string format_underline(const std::string& message,
const std::vector<std::pair<region_base const*, std::string>>& reg_com,
const std::vector<std::string>& helps = {},
const bool colorize = TOML11_ERROR_MESSAGE_COLORIZED)
{
assert(!reg_com.empty());
const auto line_num_width = static_cast<int>(std::max_element(
reg_com.begin(), reg_com.end(),
[](std::pair<region_base const*, std::string> const& lhs,
std::pair<region_base const*, std::string> const& rhs)
{
return lhs.first->line_num().size() < rhs.first->line_num().size();
}
)->first->line_num().size());
std::ostringstream retval;
if(colorize)
{
retval << color::colorize; // turn on ANSI color
}
// XXX
// Here, before `colorize` support, it does not output `[error]` prefix
// automatically. So some user may output it manually and this change may
// duplicate the prefix. To avoid it, check the first 7 characters and
// if it is "[error]", it removes that part from the message shown.
if(message.size() > 7 && message.substr(0, 7) == "[error]")
{
retval << color::bold << color::red << "[error]" << color::reset
<< color::bold << message.substr(7) << color::reset << '\n';
}
else
{
retval << color::bold << color::red << "[error] " << color::reset
<< color::bold << message << color::reset << '\n';
}
for(auto iter = reg_com.begin(); iter != reg_com.end(); ++iter)
{
// if the filenames are the same, print "..."
if(iter != reg_com.begin() &&
std::prev(iter)->first->name() == iter->first->name())
{
retval << color::bold << color::blue << "\n ...\n" << color::reset;
}
else // if filename differs, print " --> filename.toml"
{
if(iter != reg_com.begin()) {retval << '\n';}
retval << color::bold << color::blue << " --> " << color::reset
<< iter->first->name() << '\n';
// add one almost-empty line for readability
retval << make_string(static_cast<std::size_t>(line_num_width + 1), ' ')
<< color::bold << color::blue << " | " << color::reset << '\n';
}
const region_base* const reg = iter->first;
const std::string& comment = iter->second;
retval << ' ' << std::setw(line_num_width) << color::bold << color::blue
<< reg->line_num() << " | " << color::reset << reg->line() << '\n';
retval << make_string(static_cast<std::size_t>(line_num_width + 1), ' ')
<< color::bold << color::blue << " | " << color::reset
<< make_string(reg->before(), ' ');
if(reg->size() == 1)
{
// invalid
// ^------
retval << color::bold << color::red
<< '^' << make_string(reg->after(), '-') << color::reset;
}
else
{
// invalid
// ~~~~~~~
const auto underline_len = std::min(reg->size(), reg->line().size());
retval << color::bold << color::red
<< make_string(underline_len, '~') << color::reset;
}
retval << ' ';
retval << comment;
}
if(!helps.empty())
{
retval << '\n';
retval << make_string(static_cast<std::size_t>(line_num_width + 1), ' ');
retval << color::bold << color::blue << " | " << color::reset;
for(const auto help : helps)
{
retval << color::bold << "\nHint: " << color::reset;
retval << help;
}
}
return retval.str();
}
} // detail } // detail
} // toml } // toml
#endif// TOML11_REGION_H #endif// TOML11_REGION_H

View File

@@ -2,11 +2,13 @@
// Distributed under the MIT License. // Distributed under the MIT License.
#ifndef TOML11_SERIALIZER_HPP #ifndef TOML11_SERIALIZER_HPP
#define TOML11_SERIALIZER_HPP #define TOML11_SERIALIZER_HPP
#include "value.hpp"
#include "lexer.hpp"
#include <limits>
#include <cstdio> #include <cstdio>
#include <limits>
#include "lexer.hpp"
#include "value.hpp"
namespace toml namespace toml
{ {
@@ -22,39 +24,62 @@ namespace toml
// Since toml restricts characters available in a bare key, generally a string // Since toml restricts characters available in a bare key, generally a string
// should be escaped. But checking whether a string needs to be surrounded by // should be escaped. But checking whether a string needs to be surrounded by
// a `"` and escaping some special character is boring. // a `"` and escaping some special character is boring.
inline std::string format_key(const toml::key& key) template<typename charT, typename traits, typename Alloc>
std::basic_string<charT, traits, Alloc>
format_key(const std::basic_string<charT, traits, Alloc>& key)
{ {
detail::location<toml::key> loc(key, key); // check the key can be a bare (unquoted) key
detail::location loc(key, std::vector<char>(key.begin(), key.end()));
detail::lex_unquoted_key::invoke(loc); detail::lex_unquoted_key::invoke(loc);
if(loc.iter() == loc.end()) if(loc.iter() == loc.end())
{ {
return key; // all the tokens are consumed. the key is unquoted-key. return key; // all the tokens are consumed. the key is unquoted-key.
} }
std::string token("\"");
//if it includes special characters, then format it in a "quoted" key.
std::basic_string<charT, traits, Alloc> serialized("\"");
for(const char c : key) for(const char c : key)
{ {
switch(c) switch(c)
{ {
case '\\': {token += "\\\\"; break;} case '\\': {serialized += "\\\\"; break;}
case '\"': {token += "\\\""; break;} case '\"': {serialized += "\\\""; break;}
case '\b': {token += "\\b"; break;} case '\b': {serialized += "\\b"; break;}
case '\t': {token += "\\t"; break;} case '\t': {serialized += "\\t"; break;}
case '\f': {token += "\\f"; break;} case '\f': {serialized += "\\f"; break;}
case '\n': {token += "\\n"; break;} case '\n': {serialized += "\\n"; break;}
case '\r': {token += "\\r"; break;} case '\r': {serialized += "\\r"; break;}
default : {token += c; break;} default : {serialized += c; break;}
} }
} }
token += "\""; serialized += "\"";
return token; return serialized;
} }
template<typename Comment, template<typename charT, typename traits, typename Alloc>
template<typename ...> class Table, std::basic_string<charT, traits, Alloc>
template<typename ...> class Array> format_keys(const std::vector<std::basic_string<charT, traits, Alloc>>& keys)
{
std::basic_string<charT, traits, Alloc> serialized;
if(keys.empty()) {return serialized;}
for(const auto& ky : keys)
{
serialized += format_key(ky);
serialized += charT('.');
}
serialized.pop_back(); // remove the last dot '.'
return serialized;
}
template<typename Value>
struct serializer struct serializer
{ {
using value_type = basic_value<Comment, Table, Array>; static_assert(detail::is_basic_value<Value>::value,
"toml::serializer is for toml::value and its variants, "
"toml::basic_value<...>.");
using value_type = Value;
using key_type = typename value_type::key_type ; using key_type = typename value_type::key_type ;
using comment_type = typename value_type::comment_type ; using comment_type = typename value_type::comment_type ;
using boolean_type = typename value_type::boolean_type ; using boolean_type = typename value_type::boolean_type ;
@@ -112,35 +137,6 @@ struct serializer
{ {
// the resulting value does not have any float specific part! // the resulting value does not have any float specific part!
token += ".0"; token += ".0";
return token;
}
if(!has_exponent)
{
return token; // there is no exponent part. just return it.
}
#ifdef TOML11_USE_UNRELEASED_TOML_FEATURES
// Although currently it is not released yet, TOML will allow
// zero-prefix in an exponent part such as 1.234e+01.
// The following code removes the zero prefixes.
// If the feature is activated, the following codes can be skipped.
return token;
#endif
// zero-prefix in an exponent is NOT allowed in TOML v0.5.0.
// remove it if it exists.
bool sign_exists = false;
std::size_t zero_prefix = 0;
for(auto iter = std::next(e), iend = token.cend(); iter != iend; ++iter)
{
if(*iter == '+' || *iter == '-'){sign_exists = true; continue;}
if(*iter == '0'){zero_prefix += 1;}
else {break;}
}
if(zero_prefix != 0)
{
const auto offset = std::distance(token.cbegin(), e) +
(sign_exists ? 2 : 1);
token.erase(static_cast<typename std::string::size_type>(offset),
zero_prefix);
} }
return token; return token;
} }
@@ -148,12 +144,23 @@ struct serializer
{ {
if(s.kind == string_t::basic) if(s.kind == string_t::basic)
{ {
if(std::find(s.str.cbegin(), s.str.cend(), '\n') != s.str.cend()) if(std::find(s.str.cbegin(), s.str.cend(), '\n') != s.str.cend() ||
std::find(s.str.cbegin(), s.str.cend(), '\"') != s.str.cend())
{ {
// if linefeed is contained, make it multiline-string. // if linefeed or double-quote is contained,
const std::string open("\"\"\"\n"); // make it multiline basic string.
const std::string close("\\\n\"\"\""); const auto escaped = this->escape_ml_basic_string(s.str);
return open + this->escape_ml_basic_string(s.str) + close; std::string open("\"\"\"");
std::string close("\"\"\"");
if(escaped.find('\n') != std::string::npos ||
this->width_ < escaped.size() + 6)
{
// if the string body contains newline or is enough long,
// add newlines after and before delimiters.
open += "\n";
close = std::string("\\\n") + close;
}
return open + escaped + close;
} }
// no linefeed. try to make it oneline-string. // no linefeed. try to make it oneline-string.
@@ -194,7 +201,11 @@ struct serializer
if(std::find(s.str.cbegin(), s.str.cend(), '\n') != s.str.cend() || if(std::find(s.str.cbegin(), s.str.cend(), '\n') != s.str.cend() ||
std::find(s.str.cbegin(), s.str.cend(), '\'') != s.str.cend() ) std::find(s.str.cbegin(), s.str.cend(), '\'') != s.str.cend() )
{ {
const std::string open("'''\n"); std::string open("'''");
if(this->width_ + 6 < s.str.size())
{
open += '\n'; // the first newline is ignored by TOML spec
}
const std::string close("'''"); const std::string close("'''");
return open + s.str + close; return open + s.str + close;
} }
@@ -249,7 +260,7 @@ struct serializer
std::string token; std::string token;
if(!keys_.empty()) if(!keys_.empty())
{ {
token += this->serialize_key(keys_.back()); token += format_key(keys_.back());
token += " = "; token += " = ";
} }
bool failed = false; bool failed = false;
@@ -305,7 +316,7 @@ struct serializer
} }
} }
token += "[["; token += "[[";
token += this->serialize_dotted_key(keys_); token += format_keys(keys_);
token += "]]\n"; token += "]]\n";
token += this->make_multiline_table(item.as_table()); token += this->make_multiline_table(item.as_table());
} }
@@ -417,7 +428,7 @@ struct serializer
std::string token; std::string token;
if(!this->keys_.empty()) if(!this->keys_.empty())
{ {
token += this->serialize_key(this->keys_.back()); token += format_key(this->keys_.back());
token += " = "; token += " = ";
} }
token += this->make_inline_table(v); token += this->make_inline_table(v);
@@ -432,7 +443,7 @@ struct serializer
if(!keys_.empty()) if(!keys_.empty())
{ {
token += '['; token += '[';
token += this->serialize_dotted_key(keys_); token += format_keys(keys_);
token += "]\n"; token += "]\n";
} }
token += this->make_multiline_table(v); token += this->make_multiline_table(v);
@@ -441,25 +452,6 @@ struct serializer
private: private:
std::string serialize_key(const toml::key& key) const
{
return ::toml::format_key(key);
}
std::string serialize_dotted_key(const std::vector<toml::key>& keys) const
{
std::string token;
if(keys.empty()){return token;}
for(const auto& k : keys)
{
token += this->serialize_key(k);
token += '.';
}
token.erase(token.size() - 1, 1); // remove trailing `.`
return token;
}
std::string escape_basic_string(const std::string& s) const std::string escape_basic_string(const std::string& s) const
{ {
//XXX assuming `s` is a valid utf-8 sequence. //XXX assuming `s` is a valid utf-8 sequence.
@@ -489,7 +481,9 @@ struct serializer
switch(*i) switch(*i)
{ {
case '\\': {retval += "\\\\"; break;} case '\\': {retval += "\\\\"; break;}
case '\"': {retval += "\\\""; break;} // One or two consecutive "s are allowed.
// Later we will check there are no three consecutive "s.
// case '\"': {retval += "\\\""; break;}
case '\b': {retval += "\\b"; break;} case '\b': {retval += "\\b"; break;}
case '\t': {retval += "\\t"; break;} case '\t': {retval += "\\t"; break;}
case '\f': {retval += "\\f"; break;} case '\f': {retval += "\\f"; break;}
@@ -510,6 +504,23 @@ struct serializer
default: {retval += *i; break;} default: {retval += *i; break;}
} }
} }
// Only 1 or 2 consecutive `"`s are allowed in multiline basic string.
// 3 consecutive `"`s are considered as a closing delimiter.
// We need to check if there are 3 or more consecutive `"`s and insert
// backslash to break them down into several short `"`s like the `str6`
// in the following example.
// ```toml
// str4 = """Here are two quotation marks: "". Simple enough."""
// # str5 = """Here are three quotation marks: """.""" # INVALID
// str5 = """Here are three quotation marks: ""\"."""
// str6 = """Here are fifteen quotation marks: ""\"""\"""\"""\"""\"."""
// ```
auto found_3_quotes = retval.find("\"\"\"");
while(found_3_quotes != std::string::npos)
{
retval.replace(found_3_quotes, 3, "\"\"\\\"");
found_3_quotes = retval.find("\"\"\"");
}
return retval; return retval;
} }
@@ -546,7 +557,7 @@ struct serializer
for(const auto& item : v) for(const auto& item : v)
{ {
if(is_first) {is_first = false;} else {token += ',';} if(is_first) {is_first = false;} else {token += ',';}
token += visit(serializer(std::numeric_limits<std::size_t>::max(), token += visit(serializer((std::numeric_limits<std::size_t>::max)(),
this->float_prec_, true), item); this->float_prec_, true), item);
} }
token += ']'; token += ']';
@@ -564,9 +575,9 @@ struct serializer
{ {
// in inline tables, trailing comma is not allowed (toml-lang #569). // in inline tables, trailing comma is not allowed (toml-lang #569).
if(is_first) {is_first = false;} else {token += ',';} if(is_first) {is_first = false;} else {token += ',';}
token += this->serialize_key(kv.first); token += format_key(kv.first);
token += '='; token += '=';
token += visit(serializer(std::numeric_limits<std::size_t>::max(), token += visit(serializer((std::numeric_limits<std::size_t>::max)(),
this->float_prec_, true), kv.second); this->float_prec_, true), kv.second);
} }
token += '}'; token += '}';
@@ -579,7 +590,7 @@ struct serializer
// print non-table stuff first. because after printing [foo.bar], the // print non-table stuff first. because after printing [foo.bar], the
// remaining non-table values will be assigned into [foo.bar], not [foo] // remaining non-table values will be assigned into [foo.bar], not [foo]
for(const auto kv : v) for(const auto& kv : v)
{ {
if(kv.second.is_table() || is_array_of_tables(kv.second)) if(kv.second.is_table() || is_array_of_tables(kv.second))
{ {
@@ -595,7 +606,7 @@ struct serializer
token += '\n'; token += '\n';
} }
} }
const auto key_and_sep = this->serialize_key(kv.first) + " = "; const auto key_and_sep = format_key(kv.first) + " = ";
const auto residual_width = (this->width_ > key_and_sep.size()) ? const auto residual_width = (this->width_ > key_and_sep.size()) ?
this->width_ - key_and_sep.size() : 0; this->width_ - key_and_sep.size() : 0;
token += key_and_sep; token += key_and_sep;
@@ -677,6 +688,7 @@ format(const basic_value<C, M, V>& v, std::size_t w = 80u,
int fprec = std::numeric_limits<toml::floating>::max_digits10, int fprec = std::numeric_limits<toml::floating>::max_digits10,
bool no_comment = false, bool force_inline = false) bool no_comment = false, bool force_inline = false)
{ {
using value_type = basic_value<C, M, V>;
// if value is a table, it is considered to be a root object. // if value is a table, it is considered to be a root object.
// the root object can't be an inline table. // the root object can't be an inline table.
if(v.is_table()) if(v.is_table())
@@ -687,10 +699,11 @@ format(const basic_value<C, M, V>& v, std::size_t w = 80u,
oss << v.comments(); oss << v.comments();
oss << '\n'; // to split the file comment from the first element oss << '\n'; // to split the file comment from the first element
} }
oss << visit(serializer<C, M, V>(w, fprec, no_comment, false), v); const auto serialized = visit(serializer<value_type>(w, fprec, no_comment, false), v);
oss << serialized;
return oss.str(); return oss.str();
} }
return visit(serializer<C, M, V>(w, fprec, force_inline), v); return visit(serializer<value_type>(w, fprec, force_inline), v);
} }
namespace detail namespace detail
@@ -726,6 +739,8 @@ template<typename charT, typename traits, typename C,
std::basic_ostream<charT, traits>& std::basic_ostream<charT, traits>&
operator<<(std::basic_ostream<charT, traits>& os, const basic_value<C, M, V>& v) operator<<(std::basic_ostream<charT, traits>& os, const basic_value<C, M, V>& v)
{ {
using value_type = basic_value<C, M, V>;
// get status of std::setw(). // get status of std::setw().
const auto w = static_cast<std::size_t>(os.width()); const auto w = static_cast<std::size_t>(os.width());
const int fprec = static_cast<int>(os.precision()); const int fprec = static_cast<int>(os.precision());
@@ -741,7 +756,8 @@ operator<<(std::basic_ostream<charT, traits>& os, const basic_value<C, M, V>& v)
os << '\n'; // to split the file comment from the first element os << '\n'; // to split the file comment from the first element
} }
// the root object can't be an inline table. so pass `false`. // the root object can't be an inline table. so pass `false`.
os << visit(serializer<C, M, V>(w, fprec, false, no_comment), v); const auto serialized = visit(serializer<value_type>(w, fprec, no_comment, false), v);
os << serialized;
// if v is a non-table value, and has only one comment, then // if v is a non-table value, and has only one comment, then
// put a comment just after a value. in the following way. // put a comment just after a value. in the following way.

View File

@@ -2,9 +2,10 @@
// Distributed under the MIT License. // Distributed under the MIT License.
#ifndef TOML11_SOURCE_LOCATION_HPP #ifndef TOML11_SOURCE_LOCATION_HPP
#define TOML11_SOURCE_LOCATION_HPP #define TOML11_SOURCE_LOCATION_HPP
#include "region.hpp"
#include <cstdint> #include <cstdint>
#include "region.hpp"
namespace toml namespace toml
{ {
@@ -38,12 +39,12 @@ struct source_location
public: public:
source_location() source_location()
: line_num_(0), column_num_(0), region_size_(0), : line_num_(1), column_num_(1), region_size_(1),
file_name_("unknown file"), line_str_("") file_name_("unknown file"), line_str_("")
{} {}
explicit source_location(const detail::region_base* reg) explicit source_location(const detail::region_base* reg)
: line_num_(0), column_num_(0), region_size_(0), : line_num_(1), column_num_(1), region_size_(1),
file_name_("unknown file"), line_str_("") file_name_("unknown file"), line_str_("")
{ {
if(reg) if(reg)
@@ -60,6 +61,21 @@ struct source_location
} }
} }
explicit source_location(const detail::region& reg)
: line_num_(static_cast<std::uint_least32_t>(std::stoul(reg.line_num()))),
column_num_(static_cast<std::uint_least32_t>(reg.before() + 1)),
region_size_(static_cast<std::uint_least32_t>(reg.size())),
file_name_(reg.name()),
line_str_ (reg.line())
{}
explicit source_location(const detail::location& loc)
: line_num_(static_cast<std::uint_least32_t>(std::stoul(loc.line_num()))),
column_num_(static_cast<std::uint_least32_t>(loc.before() + 1)),
region_size_(static_cast<std::uint_least32_t>(loc.size())),
file_name_(loc.name()),
line_str_ (loc.line())
{}
~source_location() = default; ~source_location() = default;
source_location(source_location const&) = default; source_location(source_location const&) = default;
source_location(source_location &&) = default; source_location(source_location &&) = default;
@@ -82,5 +98,135 @@ struct source_location
std::string line_str_; std::string line_str_;
}; };
namespace detail
{
// internal error message generation.
inline std::string format_underline(const std::string& message,
const std::vector<std::pair<source_location, std::string>>& loc_com,
const std::vector<std::string>& helps = {},
const bool colorize = TOML11_ERROR_MESSAGE_COLORIZED)
{
std::size_t line_num_width = 0;
for(const auto& lc : loc_com)
{
std::uint_least32_t line = lc.first.line();
std::size_t digit = 0;
while(line != 0)
{
line /= 10;
digit += 1;
}
line_num_width = (std::max)(line_num_width, digit);
}
// 1 is the minimum width
line_num_width = std::max<std::size_t>(line_num_width, 1);
std::ostringstream retval;
if(colorize)
{
retval << color::colorize; // turn on ANSI color
}
// XXX
// Here, before `colorize` support, it does not output `[error]` prefix
// automatically. So some user may output it manually and this change may
// duplicate the prefix. To avoid it, check the first 7 characters and
// if it is "[error]", it removes that part from the message shown.
if(message.size() > 7 && message.substr(0, 7) == "[error]")
{
retval << color::bold << color::red << "[error]" << color::reset
<< color::bold << message.substr(7) << color::reset << '\n';
}
else
{
retval << color::bold << color::red << "[error] " << color::reset
<< color::bold << message << color::reset << '\n';
}
const auto format_one_location = [line_num_width]
(std::ostringstream& oss,
const source_location& loc, const std::string& comment) -> void
{
oss << ' ' << color::bold << color::blue
<< std::setw(static_cast<int>(line_num_width))
<< std::right << loc.line() << " | " << color::reset
<< loc.line_str() << '\n';
oss << make_string(line_num_width + 1, ' ')
<< color::bold << color::blue << " | " << color::reset
<< make_string(loc.column()-1 /*1-origin*/, ' ');
if(loc.region() == 1)
{
// invalid
// ^------
oss << color::bold << color::red << "^---" << color::reset;
}
else
{
// invalid
// ~~~~~~~
const auto underline_len = (std::min)(
static_cast<std::size_t>(loc.region()), loc.line_str().size());
oss << color::bold << color::red
<< make_string(underline_len, '~') << color::reset;
}
oss << ' ';
oss << comment;
return;
};
assert(!loc_com.empty());
// --> example.toml
// |
retval << color::bold << color::blue << " --> " << color::reset
<< loc_com.front().first.file_name() << '\n';
retval << make_string(line_num_width + 1, ' ')
<< color::bold << color::blue << " |\n" << color::reset;
// 1 | key value
// | ^--- missing =
format_one_location(retval, loc_com.front().first, loc_com.front().second);
// process the rest of the locations
for(std::size_t i=1; i<loc_com.size(); ++i)
{
const auto& prev = loc_com.at(i-1);
const auto& curr = loc_com.at(i);
retval << '\n';
// if the filenames are the same, print "..."
if(prev.first.file_name() == curr.first.file_name())
{
retval << color::bold << color::blue << " ...\n" << color::reset;
}
else // if filename differs, print " --> filename.toml" again
{
retval << color::bold << color::blue << " --> " << color::reset
<< curr.first.file_name() << '\n';
retval << make_string(line_num_width + 1, ' ')
<< color::bold << color::blue << " |\n" << color::reset;
}
format_one_location(retval, curr.first, curr.second);
}
if(!helps.empty())
{
retval << '\n';
retval << make_string(line_num_width + 1, ' ');
retval << color::bold << color::blue << " |" << color::reset;
for(const auto& help : helps)
{
retval << color::bold << "\nHint: " << color::reset;
retval << help;
}
}
return retval.str();
}
} // detail
} // toml } // toml
#endif// TOML11_SOURCE_LOCATION_HPP #endif// TOML11_SOURCE_LOCATION_HPP

View File

@@ -16,10 +16,9 @@ struct storage
{ {
using value_type = T; using value_type = T;
storage(value_type const& v): ptr(toml::make_unique<T>(v)) {} explicit storage(value_type const& v): ptr(toml::make_unique<T>(v)) {}
storage(value_type&& v): ptr(toml::make_unique<T>(std::move(v))) {} explicit storage(value_type&& v): ptr(toml::make_unique<T>(std::move(v))) {}
~storage() = default; ~storage() = default;
storage(const storage& rhs): ptr(toml::make_unique<T>(*rhs.ptr)) {} storage(const storage& rhs): ptr(toml::make_unique<T>(*rhs.ptr)) {}
storage& operator=(const storage& rhs) storage& operator=(const storage& rhs)
{ {

View File

@@ -2,9 +2,11 @@
// Distributed under the MIT License. // Distributed under the MIT License.
#ifndef TOML11_STRING_HPP #ifndef TOML11_STRING_HPP
#define TOML11_STRING_HPP #define TOML11_STRING_HPP
#include <cstdint>
#include <algorithm> #include <algorithm>
#include <string> #include <string>
#include <cstdint>
#if __cplusplus >= 201703L #if __cplusplus >= 201703L
#if __has_include(<string_view>) #if __has_include(<string_view>)
#include <string_view> #include <string_view>

View File

@@ -2,11 +2,13 @@
// Distributed under the MIT License. // Distributed under the MIT License.
#ifndef TOML11_TRAITS_HPP #ifndef TOML11_TRAITS_HPP
#define TOML11_TRAITS_HPP #define TOML11_TRAITS_HPP
#include <chrono>
#include <forward_list>
#include <string>
#include <tuple>
#include <type_traits> #include <type_traits>
#include <utility> #include <utility>
#include <chrono>
#include <tuple>
#include <string>
#if __cplusplus >= 201703L #if __cplusplus >= 201703L
#if __has_include(<string_view>) #if __has_include(<string_view>)
#include <string_view> #include <string_view>
@@ -43,17 +45,23 @@ struct has_mapped_type_impl
template<typename T> static std::true_type check(typename T::mapped_type*); template<typename T> static std::true_type check(typename T::mapped_type*);
template<typename T> static std::false_type check(...); template<typename T> static std::false_type check(...);
}; };
struct has_resize_method_impl struct has_reserve_method_impl
{ {
constexpr static std::size_t dummy=0;
template<typename T> static std::true_type check(decltype(std::declval<T>().resize(dummy))*);
template<typename T> static std::false_type check(...); template<typename T> static std::false_type check(...);
template<typename T> static std::true_type check(
decltype(std::declval<T>().reserve(std::declval<std::size_t>()))*);
};
struct has_push_back_method_impl
{
template<typename T> static std::false_type check(...);
template<typename T> static std::true_type check(
decltype(std::declval<T>().push_back(std::declval<typename T::value_type>()))*);
}; };
struct is_comparable_impl struct is_comparable_impl
{ {
template<typename T> static std::true_type check(decltype(std::declval<T>() < std::declval<T>())*);
template<typename T> static std::false_type check(...); template<typename T> static std::false_type check(...);
template<typename T> static std::true_type check(
decltype(std::declval<T>() < std::declval<T>())*);
}; };
struct has_from_toml_method_impl struct has_from_toml_method_impl
@@ -91,7 +99,9 @@ struct has_key_type : decltype(has_key_type_impl::check<T>(nullptr)){};
template<typename T> template<typename T>
struct has_mapped_type : decltype(has_mapped_type_impl::check<T>(nullptr)){}; 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_reserve_method : decltype(has_reserve_method_impl::check<T>(nullptr)){};
template<typename T>
struct has_push_back_method : decltype(has_push_back_method_impl::check<T>(nullptr)){};
template<typename T> template<typename T>
struct is_comparable : decltype(is_comparable_impl::check<T>(nullptr)){}; struct is_comparable : decltype(is_comparable_impl::check<T>(nullptr)){};
@@ -111,6 +121,14 @@ struct has_into_toml_method
// --------------------------------------------------------------------------- // ---------------------------------------------------------------------------
// C++17 and/or/not // C++17 and/or/not
#if __cplusplus >= 201703L
using std::conjunction;
using std::disjunction;
using std::negation;
#else
template<typename ...> struct conjunction : std::true_type{}; template<typename ...> struct conjunction : std::true_type{};
template<typename T> struct conjunction<T> : T{}; template<typename T> struct conjunction<T> : T{};
template<typename T, typename ... Ts> template<typename T, typename ... Ts>
@@ -128,6 +146,8 @@ struct disjunction<T, Ts...> :
template<typename T> template<typename T>
struct negation : std::integral_constant<bool, !static_cast<bool>(T::value)>{}; struct negation : std::integral_constant<bool, !static_cast<bool>(T::value)>{};
#endif
// --------------------------------------------------------------------------- // ---------------------------------------------------------------------------
// type checkers // type checkers
@@ -139,6 +159,10 @@ template<typename T> struct is_std_tuple : std::false_type{};
template<typename ... Ts> template<typename ... Ts>
struct is_std_tuple<std::tuple<Ts...>> : std::true_type{}; struct is_std_tuple<std::tuple<Ts...>> : std::true_type{};
template<typename T> struct is_std_forward_list : std::false_type{};
template<typename T>
struct is_std_forward_list<std::forward_list<T>> : std::true_type{};
template<typename T> struct is_chrono_duration: std::false_type{}; template<typename T> struct is_chrono_duration: std::false_type{};
template<typename Rep, typename Period> template<typename Rep, typename Period>
struct is_chrono_duration<std::chrono::duration<Rep, Period>>: std::true_type{}; struct is_chrono_duration<std::chrono::duration<Rep, Period>>: std::true_type{};
@@ -182,6 +206,13 @@ struct is_basic_value<::toml::basic_value<C, M, V>>: std::true_type{};
// --------------------------------------------------------------------------- // ---------------------------------------------------------------------------
// C++14 index_sequence // C++14 index_sequence
#if __cplusplus >= 201402L
using std::index_sequence;
using std::make_index_sequence;
#else
template<std::size_t ... Ns> struct index_sequence{}; template<std::size_t ... Ns> struct index_sequence{};
template<typename IS, std::size_t N> struct push_back_index_sequence{}; template<typename IS, std::size_t N> struct push_back_index_sequence{};
@@ -205,11 +236,22 @@ struct index_sequence_maker<0>
template<std::size_t N> template<std::size_t N>
using make_index_sequence = typename index_sequence_maker<N-1>::type; using make_index_sequence = typename index_sequence_maker<N-1>::type;
#endif // __cplusplus >= 2014
// --------------------------------------------------------------------------- // ---------------------------------------------------------------------------
// C++14 enable_if_t // C++14 enable_if_t
#if __cplusplus >= 201402L
using std::enable_if_t;
#else
template<bool B, typename T> template<bool B, typename T>
using enable_if_t = typename std::enable_if<B, T>::type; using enable_if_t = typename std::enable_if<B, T>::type;
#endif // __cplusplus >= 2014
// --------------------------------------------------------------------------- // ---------------------------------------------------------------------------
// return_type_of_t // return_type_of_t

View File

@@ -2,12 +2,13 @@
// Distributed under the MIT License. // Distributed under the MIT License.
#ifndef TOML11_TYPES_HPP #ifndef TOML11_TYPES_HPP
#define TOML11_TYPES_HPP #define TOML11_TYPES_HPP
#include <unordered_map>
#include <vector>
#include "comments.hpp"
#include "datetime.hpp" #include "datetime.hpp"
#include "string.hpp" #include "string.hpp"
#include "traits.hpp" #include "traits.hpp"
#include "comments.hpp"
#include <vector>
#include <unordered_map>
namespace toml namespace toml
{ {

View File

@@ -2,10 +2,11 @@
// Distributed under the MIT License. // Distributed under the MIT License.
#ifndef TOML11_UTILITY_HPP #ifndef TOML11_UTILITY_HPP
#define TOML11_UTILITY_HPP #define TOML11_UTILITY_HPP
#include "traits.hpp"
#include <utility>
#include <memory> #include <memory>
#include <sstream> #include <sstream>
#include <utility>
#include "traits.hpp"
#if __cplusplus >= 201402L #if __cplusplus >= 201402L
# define TOML11_MARK_AS_DEPRECATED(msg) [[deprecated(msg)]] # define TOML11_MARK_AS_DEPRECATED(msg) [[deprecated(msg)]]
@@ -20,38 +21,41 @@
namespace toml namespace toml
{ {
#if __cplusplus >= 201402L
using std::make_unique;
#else
template<typename T, typename ... Ts> template<typename T, typename ... Ts>
inline std::unique_ptr<T> make_unique(Ts&& ... args) inline std::unique_ptr<T> make_unique(Ts&& ... args)
{ {
return std::unique_ptr<T>(new T(std::forward<Ts>(args)...)); return std::unique_ptr<T>(new T(std::forward<Ts>(args)...));
} }
#endif // __cplusplus >= 2014
namespace detail namespace detail
{ {
template<typename Container>
template<typename T> void try_reserve_impl(Container& container, std::size_t N, std::true_type)
inline void resize_impl(T& container, std::size_t N, std::true_type)
{ {
container.resize(N); container.reserve(N);
return ; return;
} }
template<typename Container>
template<typename T> void try_reserve_impl(Container&, std::size_t, std::false_type) noexcept
inline void resize_impl(T& container, std::size_t N, std::false_type)
{ {
if(container.size() >= N) {return;} return;
throw std::invalid_argument("not resizable type");
} }
} // detail } // detail
template<typename T> template<typename Container>
inline void resize(T& container, std::size_t N) void try_reserve(Container& container, std::size_t N)
{ {
if(container.size() == N) {return;} if(N <= container.size()) {return;}
detail::try_reserve_impl(container, N, detail::has_reserve_method<Container>{});
return detail::resize_impl(container, N, detail::has_resize_method<T>()); return;
} }
namespace detail namespace detail
@@ -76,10 +80,10 @@ std::string concat_to_string(Ts&& ... args)
return detail::concat_to_string_impl(oss, std::forward<Ts>(args) ...); return detail::concat_to_string_impl(oss, std::forward<Ts>(args) ...);
} }
template<typename T, typename U> template<typename T>
T from_string(const std::string& str, U&& opt) T from_string(const std::string& str, T opt)
{ {
T v(static_cast<T>(std::forward<U>(opt))); T v(opt);
std::istringstream iss(str); std::istringstream iss(str);
iss >> v; iss >> v;
return v; return v;

File diff suppressed because it is too large Load Diff