mirror of
https://github.com/ToruNiina/toml11.git
synced 2025-12-16 03:08:52 +08:00
Compare commits
69 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
f5079a7892 | ||
|
|
d90ffb63c6 | ||
|
|
b0ed122214 | ||
|
|
d88521d63c | ||
|
|
2accc9d22c | ||
|
|
363927f489 | ||
|
|
ae793fb631 | ||
|
|
944b83642a | ||
|
|
5a8e5dee73 | ||
|
|
7f870d5861 | ||
|
|
536b23dc84 | ||
|
|
0c9806e99f | ||
|
|
5a92932019 | ||
|
|
e929d2f00f | ||
|
|
1e1e4c06e8 | ||
|
|
d0726db473 | ||
|
|
4cdc15a824 | ||
|
|
b36fdf2f54 | ||
|
|
73ba6b385f | ||
|
|
30d1639aa4 | ||
|
|
d82814fc86 | ||
|
|
2220efd682 | ||
|
|
679b365cf7 | ||
|
|
83bf83b6dd | ||
|
|
321364c7c2 | ||
|
|
d8707d5867 | ||
|
|
2dd0a78c52 | ||
|
|
d7b8c3c78f | ||
|
|
2f0148a2df | ||
|
|
4accc29984 | ||
|
|
19b9af2494 | ||
|
|
0aa50e9439 | ||
|
|
a00a906482 | ||
|
|
19ad7d7c96 | ||
|
|
251e55da42 | ||
|
|
32f1b2060a | ||
|
|
b1c54532df | ||
|
|
38c67f16e8 | ||
|
|
24aefc52a1 | ||
|
|
ba8c205253 | ||
|
|
31193d99ba | ||
|
|
c4aecc8e4b | ||
|
|
60c81d06a0 | ||
|
|
46569da231 | ||
|
|
5e20a8ff16 | ||
|
|
dd9319245e | ||
|
|
4bbe42d105 | ||
|
|
5bdc022627 | ||
|
|
41e354f1ee | ||
|
|
d1c76709b0 | ||
|
|
64774a8db0 | ||
|
|
53f6b8268b | ||
|
|
32dcc35918 | ||
|
|
8c3854b28b | ||
|
|
75af9c79df | ||
|
|
1dfe32acd8 | ||
|
|
5dfdbe4bff | ||
|
|
4584eeb57a | ||
|
|
aa67069387 | ||
|
|
ee3424ad51 | ||
|
|
17def14ab6 | ||
|
|
51dd3abcae | ||
|
|
825b2c30a1 | ||
|
|
b5b8830c29 | ||
|
|
87a5c844c2 | ||
|
|
11c7ee4501 | ||
|
|
d24a188d4c | ||
|
|
29876221f8 | ||
|
|
7c03c446fe |
59
PROPOSAL.md
59
PROPOSAL.md
@@ -1,59 +0,0 @@
|
|||||||
|
|
||||||
### encoding user's data
|
|
||||||
|
|
||||||
You can encode your data to toml format.
|
|
||||||
|
|
||||||
```cpp
|
|
||||||
const toml::value integer(1);
|
|
||||||
const toml::value array{3.1, 3.14, 3.141, 3.1415};
|
|
||||||
const toml::value table{{"answer", 42}, {"pi", 3.14}, {"string", "foobar"}};
|
|
||||||
|
|
||||||
std::cout << toml::format("integer", integer) << std::endl;
|
|
||||||
std::cout << toml::format("array", array) << std::endl;
|
|
||||||
std::cout << toml::format("table", table) << std::endl;
|
|
||||||
```
|
|
||||||
|
|
||||||
this program will output as below.
|
|
||||||
|
|
||||||
```toml
|
|
||||||
integer = 1
|
|
||||||
array = [3.1, 3.14, 3.141, 3.1415]
|
|
||||||
[table]
|
|
||||||
answer = 42
|
|
||||||
pi = 3.14
|
|
||||||
string = "foobar"
|
|
||||||
```
|
|
||||||
|
|
||||||
Without key name, you can make string formatted as toml.
|
|
||||||
|
|
||||||
```cpp
|
|
||||||
const std::string integer_ = toml::format(integer); // "1"
|
|
||||||
const std::string array_ = toml::format(array); // "[3.1, 3.14, 3.141, 3.1415]"
|
|
||||||
const std::string table_ = toml::format(table); // "answer = 42\npi=3.14\nstring=foobar"
|
|
||||||
```
|
|
||||||
|
|
||||||
### inlinize
|
|
||||||
|
|
||||||
You can make `toml::Table` inline.
|
|
||||||
|
|
||||||
```cpp
|
|
||||||
const toml::value table{{"answer", 42}, {"pi", 3.14}, {"string", "foobar"}};
|
|
||||||
// if the inline-table format length is less than 80, the table will be inlined
|
|
||||||
std::cout << toml::format("table", table, toml::make_inline(80)) << std::endl;
|
|
||||||
// In any case, the table will be inlined.
|
|
||||||
std::cout << toml::format("table", table, toml::forceinline) << std::endl;
|
|
||||||
```
|
|
||||||
|
|
||||||
```toml
|
|
||||||
table = {answer = 42, pi = 3.14, string = "foobar"}
|
|
||||||
```
|
|
||||||
|
|
||||||
And there are some stream manipulators for toml format.
|
|
||||||
|
|
||||||
```cpp
|
|
||||||
const toml::value table{{"answer", 42}, {"pi", 3.14}, {"string", "foobar"}};
|
|
||||||
// if the inline-table format length is less than 80, the table will be inlined
|
|
||||||
std::cout << toml::make_inline(80) << table << std::endl;
|
|
||||||
// In any case, the table will be inlined.
|
|
||||||
std::cout << toml::forceinline << table << std::endl;
|
|
||||||
```
|
|
||||||
223
README.md
223
README.md
@@ -2,15 +2,20 @@ toml11
|
|||||||
======
|
======
|
||||||
|
|
||||||
[](https://travis-ci.org/ToruNiina/toml11)
|
[](https://travis-ci.org/ToruNiina/toml11)
|
||||||
[](https://ci.appveyor.com/project/ToruNiina/toml11)
|
[](https://ci.appveyor.com/project/ToruNiina/toml11/branch/master)
|
||||||
[](LICENSE)
|
[](https://github.com/ToruNiina/toml11/releases)
|
||||||
|
[](LICENSE)
|
||||||
[](https://doi.org/10.5281/zenodo.1209136)
|
[](https://doi.org/10.5281/zenodo.1209136)
|
||||||
|
|
||||||
c++11 header-only toml parser depending only on c++ standard library.
|
C++11 header-only toml parser depending only on C++ standard library.
|
||||||
|
|
||||||
compatible to the latest version of [TOML v0.5.0](https://github.com/toml-lang/toml/blob/master/versions/en/toml-v0.5.0.md) after version 2.0.0.
|
compatible to the latest version of
|
||||||
|
[TOML v0.5.0](https://github.com/toml-lang/toml/blob/master/versions/en/toml-v0.5.0.md)
|
||||||
|
after version 2.0.0.
|
||||||
|
|
||||||
Are you looking for pre-C++11 compatible toml parser? Try [Boost.toml](https://github.com/ToruNiina/Boost.toml)! It has almost the same functionality as this library and works with C++98 & Boost.
|
Are you looking for pre-C++11 compatible toml parser?
|
||||||
|
Try [Boost.toml](https://github.com/ToruNiina/Boost.toml)!
|
||||||
|
It has almost the same functionality as this library and works with C++98 & Boost.
|
||||||
|
|
||||||
## How to use
|
## How to use
|
||||||
|
|
||||||
@@ -42,19 +47,28 @@ In the case of file open error, it will throw `std::runtime_error`.
|
|||||||
|
|
||||||
You can also pass a `stream` to the `toml::parse` function after checking the status.
|
You can also pass a `stream` to the `toml::parse` function after checking the status.
|
||||||
|
|
||||||
|
Note that on __Windows OS__, stream that is opened as text-mode automatically converts
|
||||||
|
CRLF ("\r\n") into LF ("\n") and this leads inconsistency between file size and
|
||||||
|
the contents that would be read. This causes weird error. To use a file stream
|
||||||
|
with `toml::parse`, don't forget to pass binary mode flag when you open the
|
||||||
|
stream.
|
||||||
|
|
||||||
```cpp
|
```cpp
|
||||||
std::ifstream ifs("sample.toml");
|
std::ifstream ifs("sample.toml", std::ios_base::binary);
|
||||||
assert(ifs.good());
|
assert(ifs.good());
|
||||||
const auto data = toml::parse(ifs /*, "filename" (optional)*/);
|
const auto data = toml::parse(ifs /*, "filename" (optional)*/);
|
||||||
```
|
```
|
||||||
|
|
||||||
To show a better error message, it is recommended to pass a filename with `istream`. See also [in the case of syntax error](#in-the-case-of-syntax-error) and [passing invalid type to toml::get](#passing-invalid-type-to-tomlget).
|
To show a better error message, it is recommended to pass a filename with `istream`.
|
||||||
|
See also [in the case of syntax error](#in-the-case-of-syntax-error)
|
||||||
|
and [passing invalid type to toml::get](#passing-invalid-type-to-tomlget).
|
||||||
|
|
||||||
### In the case of syntax error
|
### In the case of syntax error
|
||||||
|
|
||||||
If there is a syntax error in a toml file, `toml::parse` will throw `toml::syntax_error`.
|
If there is a syntax error in a toml file, `toml::parse` will throw `toml::syntax_error`.
|
||||||
|
|
||||||
toml11 now has clean and informative error messages inspired by Rust and it looks like the following (comment after hash sign are actually not shown).
|
toml11 now has clean and informative error messages inspired by Rust and
|
||||||
|
it looks like the following (comment after hash sign are actually not shown).
|
||||||
|
|
||||||
```console
|
```console
|
||||||
terminate called after throwing an instance of 'toml::syntax_error'
|
terminate called after throwing an instance of 'toml::syntax_error'
|
||||||
@@ -64,7 +78,8 @@ terminate called after throwing an instance of 'toml::syntax_error'
|
|||||||
| ^------ expected newline, but got '='. # error reason
|
| ^------ expected newline, but got '='. # error reason
|
||||||
```
|
```
|
||||||
|
|
||||||
If you (mistakenly) duplicate tables and got an error, you may want to see where the other is. toml11 shows both at the same time.
|
If you (mistakenly) duplicate tables and got an error, you may want to see
|
||||||
|
where the other is. toml11 shows both at the same time.
|
||||||
|
|
||||||
```console
|
```console
|
||||||
terminate called after throwing an instance of 'toml::syntax_error'
|
terminate called after throwing an instance of 'toml::syntax_error'
|
||||||
@@ -77,11 +92,14 @@ terminate called after throwing an instance of 'toml::syntax_error'
|
|||||||
| ~~~~~~~ table defined twice
|
| ~~~~~~~ table defined twice
|
||||||
```
|
```
|
||||||
|
|
||||||
Since the error message generation is generally a difficult task, the current status is not ideal. toml11 needs your help. If you encounter a weird error message, please let us know and contribute to improve the quality!
|
Since the error message generation is generally a difficult task, the current
|
||||||
|
status is not ideal. toml11 needs your help. If you encounter a weird error message,
|
||||||
|
please let us know and contribute to improve the quality!
|
||||||
|
|
||||||
### Getting a toml value
|
### Getting a toml value
|
||||||
|
|
||||||
After parsing successfully, you can obtain the values from the result of `toml::parse` (here, `data`) using `toml::get` function.
|
After parsing successfully, you can obtain the values from the result of
|
||||||
|
`toml::parse` (here, `data`) using `toml::get` function.
|
||||||
|
|
||||||
```toml
|
```toml
|
||||||
answer = 42
|
answer = 42
|
||||||
@@ -101,18 +119,22 @@ const auto tab = toml::get<toml::Table>(data.at("tab"));
|
|||||||
const auto key = toml::get<std::string>( tab.at("key"));
|
const auto key = toml::get<std::string>( tab.at("key"));
|
||||||
```
|
```
|
||||||
|
|
||||||
When you pass an exact TOML type that does not require type conversion, `toml::get` returns also a reference through which you can modify the content.
|
When you pass an exact TOML type that does not require type conversion,
|
||||||
|
`toml::get` returns also a reference through which you can modify the content.
|
||||||
|
|
||||||
```cpp
|
```cpp
|
||||||
toml::get<toml::integer>(data["answer"]) = 6 * 9;
|
toml::get<toml::integer>(data["answer"]) = 6 * 9;
|
||||||
std::cout << toml::get<int>(data.at("answer")) << std::endl; // 54
|
std::cout << toml::get<int>(data.at("answer")) << std::endl; // 54
|
||||||
```
|
```
|
||||||
|
|
||||||
If the specified type requires conversion, you can't take a reference to the value. See also [underlying types](#underlying-types).
|
If the specified type requires conversion, you can't take a reference to the value.
|
||||||
|
See also [underlying types](#underlying-types).
|
||||||
|
|
||||||
#### Passing invalid type to toml::get
|
#### Passing invalid type to toml::get
|
||||||
|
|
||||||
If you choose the invalid type, `toml::type_error` will be thrown. Similar to the `syntax_error`, toml11 also displays informative error messages. The error message when you choose `int` to get `string` value would be like this.
|
If you choose the invalid type, `toml::type_error` will be thrown.
|
||||||
|
Similar to the `syntax_error`, toml11 also displays informative error messages.
|
||||||
|
The error message when you choose `int` to get `string` value would be like this.
|
||||||
|
|
||||||
```console
|
```console
|
||||||
terminate called after throwing an instance of 'toml::type_error'
|
terminate called after throwing an instance of 'toml::type_error'
|
||||||
@@ -122,7 +144,9 @@ terminate called after throwing an instance of 'toml::type_error'
|
|||||||
| ~~~~~~~~~~~~~~ the actual type is string
|
| ~~~~~~~~~~~~~~ the actual type is string
|
||||||
```
|
```
|
||||||
|
|
||||||
NOTE: In order to show this kind of error message, all the toml values have 1 shared_ptr that points the corresponding byte sequence and 2 iterators that point the range. It is recommended to destruct all the `toml::value` classes after configuring your application to save memory resources.
|
NOTE: In order to show this kind of error message, all the toml values have 1
|
||||||
|
`shared_ptr` that points the corresponding byte sequence and 2 iterators that point the range.
|
||||||
|
It is recommended to destruct all the `toml::value` classes after configuring your application to save memory resources.
|
||||||
|
|
||||||
### Getting arrays
|
### Getting arrays
|
||||||
|
|
||||||
@@ -157,20 +181,25 @@ const auto aofa = toml::get<
|
|||||||
>(data.at("aofa"));
|
>(data.at("aofa"));
|
||||||
```
|
```
|
||||||
|
|
||||||
If you don't know what the type is inside the array, you can use `toml::array`, which is a `std::vector` of `toml::value`, instead.
|
If you don't know what the type is inside the array, you can use `toml::array`,
|
||||||
|
which is a `std::vector` of `toml::value`, instead.
|
||||||
|
|
||||||
```cpp
|
```cpp
|
||||||
const auto aofa = toml::get<toml::array>(data.at("aofa"));
|
const auto aofa = toml::get<toml::array>(data.at("aofa"));
|
||||||
const auto first = toml::get<toml::array>(aofa.at(0));
|
const auto first = toml::get<toml::array>(aofa.at(0));
|
||||||
```
|
```
|
||||||
|
|
||||||
See also [expecting conversion](#expecting-conversion) and [checking-value-type](#checking-value-type).
|
See also [expecting conversion](#expecting-conversion)
|
||||||
|
and [checking-value-type](#checking-value-type).
|
||||||
|
|
||||||
### Getting tables
|
### Getting tables
|
||||||
|
|
||||||
`toml::table` is a key component of this library, which is an alias of a `std::unordered_map` from `toml::key (a.k.a. std::string)` to `toml::value`. `toml::parse` returns this as a result.
|
`toml::table` is a key component of this library, which is an alias of
|
||||||
|
a `std::unordered_map` from `toml::key (a.k.a. std::string)` to `toml::value`.
|
||||||
|
`toml::parse` returns this.
|
||||||
|
|
||||||
Since it is just an alias of `std::unordered_map`, it has all the functionalities that `std::unordered_map` has, e.g. `operator[]`, `count`, and `find`.
|
Since it is just an alias of `std::unordered_map`, it has all the functionalities
|
||||||
|
that `std::unordered_map` has, e.g. `operator[]`, `count`, and `find`.
|
||||||
|
|
||||||
```cpp
|
```cpp
|
||||||
toml::table data = toml::parse("example.toml");
|
toml::table data = toml::parse("example.toml");
|
||||||
@@ -180,7 +209,8 @@ if(data.count("title") != 0)
|
|||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
When all the values of the table have the same type, toml11 allows you to convert a `toml::table` to a `map` that contains the convertible type.
|
When all the values of the table have the same type, toml11 allows you to
|
||||||
|
convert a `toml::table` to a `map` that contains the convertible type.
|
||||||
|
|
||||||
```toml
|
```toml
|
||||||
[tab]
|
[tab]
|
||||||
@@ -196,7 +226,8 @@ std::cout << tab["key2"] << std::endl; // bar
|
|||||||
|
|
||||||
### Dotted keys
|
### Dotted keys
|
||||||
|
|
||||||
TOML v0.5.0 has a new feature named "dotted keys". You can chain keys to represent the structure of the data.
|
TOML v0.5.0 has a new feature named "dotted keys".
|
||||||
|
You can chain keys to represent the structure of the data.
|
||||||
|
|
||||||
```toml
|
```toml
|
||||||
physical.color = "orange"
|
physical.color = "orange"
|
||||||
@@ -220,7 +251,8 @@ const auto color = toml::get<std::string>(physical.at("color"));
|
|||||||
|
|
||||||
### An array of tables
|
### An array of tables
|
||||||
|
|
||||||
An array of tables is just an array of tables. You can get it completely in the same way as the other arrays and tables.
|
An array of tables is just an array of tables.
|
||||||
|
You can get it completely in the same way as the other arrays and tables.
|
||||||
|
|
||||||
```toml
|
```toml
|
||||||
array_of_inline_table = [{key = "value1"}, {key = "value2"}, {key = "value3"}]
|
array_of_inline_table = [{key = "value1"}, {key = "value2"}, {key = "value3"}]
|
||||||
@@ -240,7 +272,9 @@ const auto aot2 = toml::get<std::vector<toml::table>>(data.at("array_of_table"))
|
|||||||
|
|
||||||
### Cost of conversion
|
### Cost of conversion
|
||||||
|
|
||||||
Although `toml::get` is convenient, it has additional copy-cost because it copies data contained in `toml::value` to the user-specified type. Of course in some case this overhead is not ignorable.
|
Although `toml::get` is convenient, it has additional copy-cost because
|
||||||
|
it copies data contained in `toml::value` to the user-specified type.
|
||||||
|
Of course in some case this overhead is not ignorable.
|
||||||
|
|
||||||
By passing the exact types, `toml::get` returns reference that has nealy zero overhead.
|
By passing the exact types, `toml::get` returns reference that has nealy zero overhead.
|
||||||
|
|
||||||
@@ -249,7 +283,8 @@ const auto& tab = toml::get<toml::array>(data.at("tab"));
|
|||||||
const auto& numbers = toml::get<toml::table>(data.at("numbers"));
|
const auto& numbers = toml::get<toml::table>(data.at("numbers"));
|
||||||
```
|
```
|
||||||
|
|
||||||
Unfortunately, in this case you need to call `toml::get` each time you access to the element of `toml::array` because `toml::array` is an array of `toml::value`.
|
Unfortunately, in this case you need to call `toml::get` each time you access to
|
||||||
|
the element of `toml::array` because `toml::array` is an array of `toml::value`.
|
||||||
|
|
||||||
```cpp
|
```cpp
|
||||||
const auto& num0 = toml::get<toml::integer>(numbers.at(0));
|
const auto& num0 = toml::get<toml::integer>(numbers.at(0));
|
||||||
@@ -259,7 +294,9 @@ const auto& num2 = toml::get<toml::integer>(numbers.at(2));
|
|||||||
|
|
||||||
### Datetime and its variants
|
### Datetime and its variants
|
||||||
|
|
||||||
TOML v0.5.0 has 4 different datetime objects, `local_date`, `local_time`, `local_datetime`, and `offset_datetime`. With toml11, you can convert `local_time` to your favorite `std::chrono::duration` and others to `std::chrono::system_clock::time_point`.
|
TOML v0.5.0 has 4 different datetime objects, `local_date`, `local_time`,
|
||||||
|
`local_datetime`, and `offset_datetime`. With toml11, you can convert `local_time`
|
||||||
|
to your favorite `std::chrono::duration` and others to `std::chrono::system_clock::time_point`.
|
||||||
|
|
||||||
```toml
|
```toml
|
||||||
time = 12:30:00
|
time = 12:30:00
|
||||||
@@ -284,7 +321,8 @@ const auto value = toml::get_or(data, "key", 42); // value => int 42.
|
|||||||
|
|
||||||
### Expecting conversion
|
### Expecting conversion
|
||||||
|
|
||||||
By using `toml::expect`, you will get your expected value or an error message without throwing `toml::type_error`.
|
By using `toml::expect`, you will get your expected value or an error message
|
||||||
|
without throwing `toml::type_error`.
|
||||||
|
|
||||||
```cpp
|
```cpp
|
||||||
const auto value = toml::expect<std::string>(data.at("title"));
|
const auto value = toml::expect<std::string>(data.at("title"));
|
||||||
@@ -307,7 +345,9 @@ const auto value = toml::expect<int>(data.at("number"))
|
|||||||
|
|
||||||
### Finding value from table
|
### Finding value from table
|
||||||
|
|
||||||
toml11 provides utility function to find a value from `toml::table`. Of course, you can do this in your own way with `toml::get` because it just searches an `unordered_map` and returns a value if it exists.
|
toml11 provides utility function to find a value from `toml::table`.
|
||||||
|
Of course, you can do this in your own way with `toml::get` because
|
||||||
|
it just searches an `unordered_map` and returns a value if it exists.
|
||||||
|
|
||||||
```cpp
|
```cpp
|
||||||
const auto data = toml::parse("example.toml");
|
const auto data = toml::parse("example.toml");
|
||||||
@@ -321,7 +361,8 @@ terminate called after throwing an instance of 'std::out_of_range'
|
|||||||
what(): [error] key "num" not found in example.toml
|
what(): [error] key "num" not found in example.toml
|
||||||
```
|
```
|
||||||
|
|
||||||
You can use this with a `toml::value` that is expected to be a `toml::table`. It automatically casts the value to table.
|
You can use this with a `toml::value` that is expected to be a `toml::table`.
|
||||||
|
It automatically casts the value to table.
|
||||||
|
|
||||||
```cpp
|
```cpp
|
||||||
const auto data = toml::parse("example.toml");
|
const auto data = toml::parse("example.toml");
|
||||||
@@ -331,7 +372,8 @@ const auto num = toml::find<int>(data.at("table"), "num");
|
|||||||
// num = 42
|
// num = 42
|
||||||
```
|
```
|
||||||
|
|
||||||
In this case, because the value `data.at("table")` knows the locatoin of itself, you don't need to pass where you find the value. `toml::find` will show you a great error message.
|
In this case, because the value `data.at("table")` knows the locatoin of itself,
|
||||||
|
you don't need to pass where you find the value. `toml::find` will show you a great error message.
|
||||||
|
|
||||||
```console
|
```console
|
||||||
terminate called after throwing an instance of 'std::out_of_range'
|
terminate called after throwing an instance of 'std::out_of_range'
|
||||||
@@ -381,9 +423,26 @@ int i = 0;
|
|||||||
toml::from_toml(i, data.at("something"));
|
toml::from_toml(i, data.at("something"));
|
||||||
```
|
```
|
||||||
|
|
||||||
|
### visiting toml::value
|
||||||
|
|
||||||
|
TOML v2.1.0+ provides `toml::visit` to apply a function to `toml::value` in the
|
||||||
|
same way as `std::variant`.
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
const toml::value v(3.14);
|
||||||
|
toml::visit([](const auto& val) -> void {
|
||||||
|
std::cout << val << std::endl;
|
||||||
|
}, v);
|
||||||
|
```
|
||||||
|
|
||||||
|
The function object that would be passed to `toml::visit` must be able to
|
||||||
|
recieve all the possible TOML types. Also, the result types should be the same
|
||||||
|
each other.
|
||||||
|
|
||||||
### Sanitizing UTF-8 codepoints
|
### Sanitizing UTF-8 codepoints
|
||||||
|
|
||||||
toml11 shows warning if a value of an escape sequence used to represent unicode character exceeds the unicode range.
|
toml11 shows warning if a value of an escape sequence used
|
||||||
|
to represent unicode character exceeds the unicode range.
|
||||||
|
|
||||||
```console
|
```console
|
||||||
[warning] input codepoint (0011FFFF) is too large to decode as a unicode character. The result may not be able to render to your screen.
|
[warning] input codepoint (0011FFFF) is too large to decode as a unicode character. The result may not be able to render to your screen.
|
||||||
@@ -455,6 +514,97 @@ you will get an error message like this.
|
|||||||
| ~~ maximum number here
|
| ~~ maximum number here
|
||||||
```
|
```
|
||||||
|
|
||||||
|
### Serializing TOML data
|
||||||
|
|
||||||
|
toml11 v2.1.0 enables you to serialize data into toml format.
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
const auto data = toml::table{{"foo", 42}, {"bar", "baz"}};
|
||||||
|
|
||||||
|
const std::string serial = toml::format(data);
|
||||||
|
// serial == "{bar=\"baz\",foo=42}"
|
||||||
|
|
||||||
|
std::cout << data << std::endl;
|
||||||
|
// bar = "baz"
|
||||||
|
// foo = 42
|
||||||
|
```
|
||||||
|
|
||||||
|
toml11 automatically makes a tiny table and array inline.
|
||||||
|
You can specify the width to make them inline by `std::setw` for streams.
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
const auto data = toml::table{
|
||||||
|
{"qux", toml::table{{"foo", 42}, {"bar", "baz"}}},
|
||||||
|
{"quux", toml::array{"small", "array", "of", "strings"}},
|
||||||
|
{"foobar", toml::array{"this", "array", "of", "strings", "is", "too", "long",
|
||||||
|
"to", "print", "into", "single", "line", "isn't", "it?"}},
|
||||||
|
};
|
||||||
|
|
||||||
|
// the threshold becomes 80.
|
||||||
|
std::cout << std::setw(80) << data << std::endl;
|
||||||
|
// foobar = [
|
||||||
|
// "this","array","of","strings","is","too","long","to","print","into",
|
||||||
|
// "single","line","isn't","it?",
|
||||||
|
// ]
|
||||||
|
// quux = ["small","array","of","strings"]
|
||||||
|
// qux = {bar="baz",foo=42}
|
||||||
|
|
||||||
|
|
||||||
|
// the width is 0. nothing become inline.
|
||||||
|
std::cout << std::setw(0) << data << std::endl;
|
||||||
|
// foobar = [
|
||||||
|
// "this",
|
||||||
|
// ... (snip)
|
||||||
|
// "it?",
|
||||||
|
// ]
|
||||||
|
// quux = [
|
||||||
|
// "small",
|
||||||
|
// "array",
|
||||||
|
// "of",
|
||||||
|
// "strings",
|
||||||
|
// ]
|
||||||
|
// [qux]
|
||||||
|
// bar = "baz"
|
||||||
|
// foo = 42
|
||||||
|
```
|
||||||
|
|
||||||
|
It is recommended to set width before printing data. Some I/O functions changes
|
||||||
|
width to 0, and it makes all the stuff (including `toml::array`) multiline.
|
||||||
|
The resulting files becomes too long.
|
||||||
|
|
||||||
|
`toml::format` receives optional second argument to set the width.
|
||||||
|
By default, it is 80.
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
const auto data = toml::table{
|
||||||
|
{"qux", toml::table{{"foo", 42}, {"bar", "baz"}}}
|
||||||
|
};
|
||||||
|
|
||||||
|
const std::string serial = toml::format(data, /*width = */ 0);
|
||||||
|
// [qux]
|
||||||
|
// bar = "baz"
|
||||||
|
// foo = 42
|
||||||
|
```
|
||||||
|
|
||||||
|
To control the precision of floating point numbers, you need to pass
|
||||||
|
`std::setprecision` to stream or pass `int` to the optional third argument of
|
||||||
|
`toml::format` (by default, it is `std::numeric_limits<double>::max_digit10`).
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
const auto data = toml::table{
|
||||||
|
{"pi", 3.141592653589793},
|
||||||
|
{"e", 2.718281828459045}
|
||||||
|
};
|
||||||
|
std::cout << std::setprecision(17) << data << std::endl;
|
||||||
|
// e = 2.7182818284590451
|
||||||
|
// pi = 3.1415926535897931
|
||||||
|
std::cout << std::setprecision( 7) << data << std::endl;
|
||||||
|
// e = 2.718282
|
||||||
|
// pi = 3.141593
|
||||||
|
|
||||||
|
const std::string serial = toml::format(data, /*width = */ 0, /*prec = */ 17);
|
||||||
|
```
|
||||||
|
|
||||||
## Underlying types
|
## Underlying types
|
||||||
|
|
||||||
The toml types (can be used as `toml::*` in this library) and corresponding `enum` names are listed in the table below.
|
The toml types (can be used as `toml::*` in this library) and corresponding `enum` names are listed in the table below.
|
||||||
@@ -470,11 +620,16 @@ The toml types (can be used as `toml::*` in this library) and corresponding `enu
|
|||||||
| LocalDatetime | `toml::local_datetime` | `toml::value_t::LocalDatetime` |
|
| LocalDatetime | `toml::local_datetime` | `toml::value_t::LocalDatetime` |
|
||||||
| OffsetDatetime | `toml::offset_datetime` | `toml::value_t::offsetDatetime` |
|
| OffsetDatetime | `toml::offset_datetime` | `toml::value_t::offsetDatetime` |
|
||||||
| Array | `std::vector<toml::value>` | `toml::value_t::Array` |
|
| Array | `std::vector<toml::value>` | `toml::value_t::Array` |
|
||||||
| Table | `std::unordered_map<std::string, toml::key>` | `toml::value_t::Table` |
|
| Table | `std::unordered_map<toml::key, toml::value>` | `toml::value_t::Table` |
|
||||||
|
|
||||||
`toml::string` is effectively the same as `std::string` but has an additional flag that represents a kind of a string, `string_t::basic` and `string_t::literal`. Although `std::string` is not an exact toml type, still you can get a reference that points to internal `std::string` by using `toml::get<std::string>()` for convenience.
|
`toml::string` is effectively the same as `std::string` but has an additional
|
||||||
|
flag that represents a kind of a string, `string_t::basic` and `string_t::literal`.
|
||||||
|
Although `std::string` is not an exact toml type, still you can get a reference
|
||||||
|
that points to internal `std::string` by using `toml::get<std::string>()` for convenience.
|
||||||
|
|
||||||
`Datetime` variants are `struct` that are defined in this library. Because `std::chrono::system_clock::time_point` is a __time point__, not capable of representing a Local Time independent from a specific day.
|
`Datetime` variants are `struct` that are defined in this library.
|
||||||
|
Because `std::chrono::system_clock::time_point` is a __time point__,
|
||||||
|
not capable of representing a Local Time independent from a specific day.
|
||||||
|
|
||||||
It is recommended to get `Datetime`s as `std::chrono` classes through `toml::get`.
|
It is recommended to get `Datetime`s as `std::chrono` classes through `toml::get`.
|
||||||
|
|
||||||
@@ -485,6 +640,8 @@ I thank the contributor for providing great feature to this repository.
|
|||||||
- Guillaume Fraux (@Luthaf)
|
- Guillaume Fraux (@Luthaf)
|
||||||
- Windows support and CI on Appvayor
|
- Windows support and CI on Appvayor
|
||||||
- Intel Compiler support
|
- Intel Compiler support
|
||||||
|
- Quentin Khan (@xaxousis)
|
||||||
|
- Found & Fixed a bug around ODR
|
||||||
|
|
||||||
## Licensing terms
|
## Licensing terms
|
||||||
|
|
||||||
|
|||||||
@@ -25,6 +25,7 @@ set(TEST_NAMES
|
|||||||
test_to_toml
|
test_to_toml
|
||||||
test_from_toml
|
test_from_toml
|
||||||
test_parse_file
|
test_parse_file
|
||||||
|
test_serialize_file
|
||||||
test_parse_unicode
|
test_parse_unicode
|
||||||
test_error_detection
|
test_error_detection
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -194,3 +194,504 @@ BOOST_AUTO_TEST_CASE(test_hard_example)
|
|||||||
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);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
// after here, the test codes generate the content of a file.
|
||||||
|
|
||||||
|
BOOST_AUTO_TEST_CASE(test_file_with_BOM)
|
||||||
|
{
|
||||||
|
{
|
||||||
|
const std::string table(
|
||||||
|
"\xEF\xBB\xBF" // BOM
|
||||||
|
"key = \"value\"\n"
|
||||||
|
"[table]\n"
|
||||||
|
"key = \"value\"\n"
|
||||||
|
);
|
||||||
|
std::istringstream iss(table);
|
||||||
|
const auto data = toml::parse(iss, "test_file_with_BOM.toml");
|
||||||
|
|
||||||
|
BOOST_CHECK_EQUAL(toml::get <std::string>(data.at("key")), "value");
|
||||||
|
BOOST_CHECK_EQUAL(toml::find<std::string>(data.at("table"), "key"), "value");
|
||||||
|
}
|
||||||
|
{
|
||||||
|
const std::string table(
|
||||||
|
"\xEF\xBB\xBF" // BOM
|
||||||
|
"key = \"value\"\n"
|
||||||
|
"[table]\n"
|
||||||
|
"key = \"value\"\n"
|
||||||
|
);
|
||||||
|
{
|
||||||
|
std::ofstream ofs("tmp.toml");
|
||||||
|
ofs << table;
|
||||||
|
}
|
||||||
|
const auto data = toml::parse("tmp.toml");
|
||||||
|
|
||||||
|
BOOST_CHECK_EQUAL(toml::get <std::string>(data.at("key")), "value");
|
||||||
|
BOOST_CHECK_EQUAL(toml::find<std::string>(data.at("table"), "key"), "value");
|
||||||
|
}
|
||||||
|
{
|
||||||
|
const std::string table(
|
||||||
|
"\xEF\xBB\xBF" // BOM
|
||||||
|
"key = \"value\"\r\n"
|
||||||
|
"[table]\r\n"
|
||||||
|
"key = \"value\"\r\n"
|
||||||
|
);
|
||||||
|
std::istringstream iss(table);
|
||||||
|
const auto data = toml::parse(iss, "test_file_with_BOM_CRLF.toml");
|
||||||
|
|
||||||
|
BOOST_CHECK_EQUAL(toml::get <std::string>(data.at("key")), "value");
|
||||||
|
BOOST_CHECK_EQUAL(toml::find<std::string>(data.at("table"), "key"), "value");
|
||||||
|
}
|
||||||
|
{
|
||||||
|
const std::string table(
|
||||||
|
"\xEF\xBB\xBF" // BOM
|
||||||
|
"key = \"value\"\r\n"
|
||||||
|
"[table]\r\n"
|
||||||
|
"key = \"value\"\r\n"
|
||||||
|
);
|
||||||
|
{
|
||||||
|
// with text-mode, "\n" is converted to "\r\n" and the resulting
|
||||||
|
// value will be "\r\r\n". To avoid the additional "\r", use binary
|
||||||
|
// mode.
|
||||||
|
std::ofstream ofs("tmp.toml", std::ios_base::binary);
|
||||||
|
ofs.write(table.data(), table.size());
|
||||||
|
}
|
||||||
|
const auto data = toml::parse("tmp.toml");
|
||||||
|
|
||||||
|
BOOST_CHECK_EQUAL(toml::get <std::string>(data.at("key")), "value");
|
||||||
|
BOOST_CHECK_EQUAL(toml::find<std::string>(data.at("table"), "key"), "value");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
BOOST_AUTO_TEST_CASE(test_file_without_newline_at_the_end_of_file)
|
||||||
|
{
|
||||||
|
{
|
||||||
|
const std::string table(
|
||||||
|
"key = \"value\"\n"
|
||||||
|
"[table]\n"
|
||||||
|
"key = \"value\""
|
||||||
|
);
|
||||||
|
std::istringstream iss(table);
|
||||||
|
const auto data = toml::parse(iss,
|
||||||
|
"test_file_without_newline_at_the_end_of_file.toml");
|
||||||
|
|
||||||
|
BOOST_CHECK_EQUAL(toml::get <std::string>(data.at("key")), "value");
|
||||||
|
BOOST_CHECK_EQUAL(toml::find<std::string>(data.at("table"), "key"), "value");
|
||||||
|
}
|
||||||
|
{
|
||||||
|
const std::string table(
|
||||||
|
"key = \"value\"\r\n"
|
||||||
|
"[table]\r\n"
|
||||||
|
"key = \"value\""
|
||||||
|
);
|
||||||
|
std::istringstream iss(table);
|
||||||
|
const auto data = toml::parse(iss,
|
||||||
|
"test_file_without_newline_at_the_end_of_file_CRLF.toml");
|
||||||
|
|
||||||
|
BOOST_CHECK_EQUAL(toml::get <std::string>(data.at("key")), "value");
|
||||||
|
BOOST_CHECK_EQUAL(toml::find<std::string>(data.at("table"), "key"), "value");
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
const std::string table(
|
||||||
|
"key = \"value\"\n"
|
||||||
|
"[table]\n"
|
||||||
|
"key = \"value\" # comment"
|
||||||
|
);
|
||||||
|
std::istringstream iss(table);
|
||||||
|
const auto data = toml::parse(iss,
|
||||||
|
"test_file_without_newline_at_the_end_of_file_comment.toml");
|
||||||
|
|
||||||
|
BOOST_CHECK_EQUAL(toml::get <std::string>(data.at("key")), "value");
|
||||||
|
BOOST_CHECK_EQUAL(toml::find<std::string>(data.at("table"), "key"), "value");
|
||||||
|
}
|
||||||
|
{
|
||||||
|
const std::string table(
|
||||||
|
"key = \"value\"\r\n"
|
||||||
|
"[table]\r\n"
|
||||||
|
"key = \"value\" # comment"
|
||||||
|
);
|
||||||
|
std::istringstream iss(table);
|
||||||
|
const auto data = toml::parse(iss,
|
||||||
|
"test_file_without_newline_at_the_end_of_file_comment.toml");
|
||||||
|
|
||||||
|
BOOST_CHECK_EQUAL(toml::get <std::string>(data.at("key")), "value");
|
||||||
|
BOOST_CHECK_EQUAL(toml::find<std::string>(data.at("table"), "key"), "value");
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
const std::string table(
|
||||||
|
"key = \"value\"\n"
|
||||||
|
"[table]\n"
|
||||||
|
"key = \"value\" \t"
|
||||||
|
);
|
||||||
|
std::istringstream iss(table);
|
||||||
|
const auto data = toml::parse(iss,
|
||||||
|
"test_file_without_newline_at_the_end_of_file_ws.toml");
|
||||||
|
|
||||||
|
BOOST_CHECK_EQUAL(toml::get <std::string>(data.at("key")), "value");
|
||||||
|
BOOST_CHECK_EQUAL(toml::find<std::string>(data.at("table"), "key"), "value");
|
||||||
|
}
|
||||||
|
{
|
||||||
|
const std::string table(
|
||||||
|
"key = \"value\"\r\n"
|
||||||
|
"[table]\r\n"
|
||||||
|
"key = \"value\" \t"
|
||||||
|
);
|
||||||
|
std::istringstream iss(table);
|
||||||
|
const auto data = toml::parse(iss,
|
||||||
|
"test_file_without_newline_at_the_end_of_file_ws.toml");
|
||||||
|
|
||||||
|
BOOST_CHECK_EQUAL(toml::get <std::string>(data.at("key")), "value");
|
||||||
|
BOOST_CHECK_EQUAL(toml::find<std::string>(data.at("table"), "key"), "value");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
BOOST_AUTO_TEST_CASE(test_files_end_with_comment)
|
||||||
|
{
|
||||||
|
// comment w/o newline
|
||||||
|
{
|
||||||
|
const std::string table(
|
||||||
|
"key = \"value\"\n"
|
||||||
|
"[table]\n"
|
||||||
|
"key = \"value\"\n"
|
||||||
|
"# comment"
|
||||||
|
);
|
||||||
|
std::istringstream iss(table);
|
||||||
|
const auto data = toml::parse(iss,
|
||||||
|
"test_files_end_with_comment.toml");
|
||||||
|
|
||||||
|
BOOST_CHECK_EQUAL(toml::get <std::string>(data.at("key")), "value");
|
||||||
|
BOOST_CHECK_EQUAL(toml::find<std::string>(data.at("table"), "key"), "value");
|
||||||
|
}
|
||||||
|
{
|
||||||
|
const std::string table(
|
||||||
|
"key = \"value\"\n"
|
||||||
|
"[table]\n"
|
||||||
|
"key = \"value\"\n"
|
||||||
|
"# comment\n"
|
||||||
|
"# one more comment"
|
||||||
|
);
|
||||||
|
std::istringstream iss(table);
|
||||||
|
const auto data = toml::parse(iss,
|
||||||
|
"test_files_end_with_comment.toml");
|
||||||
|
|
||||||
|
BOOST_CHECK_EQUAL(toml::get <std::string>(data.at("key")), "value");
|
||||||
|
BOOST_CHECK_EQUAL(toml::find<std::string>(data.at("table"), "key"), "value");
|
||||||
|
}
|
||||||
|
|
||||||
|
// comment w/ newline
|
||||||
|
|
||||||
|
{
|
||||||
|
const std::string table(
|
||||||
|
"key = \"value\"\n"
|
||||||
|
"[table]\n"
|
||||||
|
"key = \"value\"\n"
|
||||||
|
"# comment\n"
|
||||||
|
);
|
||||||
|
std::istringstream iss(table);
|
||||||
|
const auto data = toml::parse(iss,
|
||||||
|
"test_files_end_with_comment.toml");
|
||||||
|
|
||||||
|
BOOST_CHECK_EQUAL(toml::get <std::string>(data.at("key")), "value");
|
||||||
|
BOOST_CHECK_EQUAL(toml::find<std::string>(data.at("table"), "key"), "value");
|
||||||
|
}
|
||||||
|
{
|
||||||
|
const std::string table(
|
||||||
|
"key = \"value\"\n"
|
||||||
|
"[table]\n"
|
||||||
|
"key = \"value\"\n"
|
||||||
|
"# comment\n"
|
||||||
|
"# one more comment\n"
|
||||||
|
);
|
||||||
|
std::istringstream iss(table);
|
||||||
|
const auto data = toml::parse(iss,
|
||||||
|
"test_files_end_with_comment.toml");
|
||||||
|
|
||||||
|
BOOST_CHECK_EQUAL(toml::get <std::string>(data.at("key")), "value");
|
||||||
|
BOOST_CHECK_EQUAL(toml::find<std::string>(data.at("table"), "key"), "value");
|
||||||
|
}
|
||||||
|
|
||||||
|
// CRLF version
|
||||||
|
|
||||||
|
{
|
||||||
|
const std::string table(
|
||||||
|
"key = \"value\"\r\n"
|
||||||
|
"[table]\r\n"
|
||||||
|
"key = \"value\"\r\n"
|
||||||
|
"# comment"
|
||||||
|
);
|
||||||
|
std::istringstream iss(table);
|
||||||
|
const auto data = toml::parse(iss,
|
||||||
|
"test_files_end_with_comment.toml");
|
||||||
|
|
||||||
|
BOOST_CHECK_EQUAL(toml::get <std::string>(data.at("key")), "value");
|
||||||
|
BOOST_CHECK_EQUAL(toml::find<std::string>(data.at("table"), "key"), "value");
|
||||||
|
}
|
||||||
|
{
|
||||||
|
const std::string table(
|
||||||
|
"key = \"value\"\r\n"
|
||||||
|
"[table]\r\n"
|
||||||
|
"key = \"value\"\r\n"
|
||||||
|
"# comment\r\n"
|
||||||
|
"# one more comment"
|
||||||
|
);
|
||||||
|
std::istringstream iss(table);
|
||||||
|
const auto data = toml::parse(iss,
|
||||||
|
"test_files_end_with_comment.toml");
|
||||||
|
|
||||||
|
BOOST_CHECK_EQUAL(toml::get <std::string>(data.at("key")), "value");
|
||||||
|
BOOST_CHECK_EQUAL(toml::find<std::string>(data.at("table"), "key"), "value");
|
||||||
|
}
|
||||||
|
{
|
||||||
|
const std::string table(
|
||||||
|
"key = \"value\"\r\n"
|
||||||
|
"[table]\r\n"
|
||||||
|
"key = \"value\"\r\n"
|
||||||
|
"# comment\r\n"
|
||||||
|
);
|
||||||
|
std::istringstream iss(table);
|
||||||
|
const auto data = toml::parse(iss,
|
||||||
|
"test_files_end_with_comment.toml");
|
||||||
|
|
||||||
|
BOOST_CHECK_EQUAL(toml::get <std::string>(data.at("key")), "value");
|
||||||
|
BOOST_CHECK_EQUAL(toml::find<std::string>(data.at("table"), "key"), "value");
|
||||||
|
}
|
||||||
|
{
|
||||||
|
const std::string table(
|
||||||
|
"key = \"value\"\r\n"
|
||||||
|
"[table]\r\n"
|
||||||
|
"key = \"value\"\r\n"
|
||||||
|
"# comment\r\n"
|
||||||
|
"# one more comment\r\n"
|
||||||
|
);
|
||||||
|
std::istringstream iss(table);
|
||||||
|
const auto data = toml::parse(iss,
|
||||||
|
"test_files_end_with_comment.toml");
|
||||||
|
|
||||||
|
BOOST_CHECK_EQUAL(toml::get <std::string>(data.at("key")), "value");
|
||||||
|
BOOST_CHECK_EQUAL(toml::find<std::string>(data.at("table"), "key"), "value");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
BOOST_AUTO_TEST_CASE(test_files_end_with_empty_lines)
|
||||||
|
{
|
||||||
|
{
|
||||||
|
const std::string table(
|
||||||
|
"key = \"value\"\n"
|
||||||
|
"[table]\n"
|
||||||
|
"key = \"value\"\n"
|
||||||
|
"\n"
|
||||||
|
);
|
||||||
|
std::istringstream iss(table);
|
||||||
|
const auto data = toml::parse(iss,
|
||||||
|
"test_files_end_with_newline.toml");
|
||||||
|
|
||||||
|
BOOST_CHECK_EQUAL(toml::get <std::string>(data.at("key")), "value");
|
||||||
|
BOOST_CHECK_EQUAL(toml::find<std::string>(data.at("table"), "key"), "value");
|
||||||
|
}
|
||||||
|
{
|
||||||
|
const std::string table(
|
||||||
|
"key = \"value\"\n"
|
||||||
|
"[table]\n"
|
||||||
|
"key = \"value\"\n"
|
||||||
|
"\n"
|
||||||
|
"\n"
|
||||||
|
);
|
||||||
|
std::istringstream iss(table);
|
||||||
|
const auto data = toml::parse(iss,
|
||||||
|
"test_files_end_with_newline.toml");
|
||||||
|
|
||||||
|
BOOST_CHECK_EQUAL(toml::get <std::string>(data.at("key")), "value");
|
||||||
|
BOOST_CHECK_EQUAL(toml::find<std::string>(data.at("table"), "key"), "value");
|
||||||
|
}
|
||||||
|
|
||||||
|
// with whitespaces
|
||||||
|
|
||||||
|
{
|
||||||
|
const std::string table(
|
||||||
|
"key = \"value\"\n"
|
||||||
|
"[table]\n"
|
||||||
|
"key = \"value\"\n"
|
||||||
|
" \n"
|
||||||
|
);
|
||||||
|
std::istringstream iss(table);
|
||||||
|
const auto data = toml::parse(iss,
|
||||||
|
"test_files_end_with_newline.toml");
|
||||||
|
|
||||||
|
BOOST_CHECK_EQUAL(toml::get <std::string>(data.at("key")), "value");
|
||||||
|
BOOST_CHECK_EQUAL(toml::find<std::string>(data.at("table"), "key"), "value");
|
||||||
|
}
|
||||||
|
{
|
||||||
|
const std::string table(
|
||||||
|
"key = \"value\"\n"
|
||||||
|
"[table]\n"
|
||||||
|
"key = \"value\"\n"
|
||||||
|
" \n"
|
||||||
|
" \n"
|
||||||
|
);
|
||||||
|
std::istringstream iss(table);
|
||||||
|
const auto data = toml::parse(iss,
|
||||||
|
"test_files_end_with_newline.toml");
|
||||||
|
|
||||||
|
BOOST_CHECK_EQUAL(toml::get <std::string>(data.at("key")), "value");
|
||||||
|
BOOST_CHECK_EQUAL(toml::find<std::string>(data.at("table"), "key"), "value");
|
||||||
|
}
|
||||||
|
{
|
||||||
|
const std::string table(
|
||||||
|
"key = \"value\"\n"
|
||||||
|
"[table]\n"
|
||||||
|
"key = \"value\"\n"
|
||||||
|
"\n"
|
||||||
|
" \n"
|
||||||
|
);
|
||||||
|
std::istringstream iss(table);
|
||||||
|
const auto data = toml::parse(iss,
|
||||||
|
"test_files_end_with_newline.toml");
|
||||||
|
|
||||||
|
BOOST_CHECK_EQUAL(toml::get <std::string>(data.at("key")), "value");
|
||||||
|
BOOST_CHECK_EQUAL(toml::find<std::string>(data.at("table"), "key"), "value");
|
||||||
|
}
|
||||||
|
{
|
||||||
|
const std::string table(
|
||||||
|
"key = \"value\"\n"
|
||||||
|
"[table]\n"
|
||||||
|
"key = \"value\"\n"
|
||||||
|
" \n"
|
||||||
|
"\n"
|
||||||
|
);
|
||||||
|
std::istringstream iss(table);
|
||||||
|
const auto data = toml::parse(iss,
|
||||||
|
"test_files_end_with_newline.toml");
|
||||||
|
|
||||||
|
BOOST_CHECK_EQUAL(toml::get <std::string>(data.at("key")), "value");
|
||||||
|
BOOST_CHECK_EQUAL(toml::find<std::string>(data.at("table"), "key"), "value");
|
||||||
|
}
|
||||||
|
|
||||||
|
// with whitespaces but no newline
|
||||||
|
{
|
||||||
|
const std::string table(
|
||||||
|
"key = \"value\"\n"
|
||||||
|
"[table]\n"
|
||||||
|
"key = \"value\"\n"
|
||||||
|
" "
|
||||||
|
);
|
||||||
|
std::istringstream iss(table);
|
||||||
|
const auto data = toml::parse(iss,
|
||||||
|
"test_files_end_with_newline.toml");
|
||||||
|
|
||||||
|
BOOST_CHECK_EQUAL(toml::get <std::string>(data.at("key")), "value");
|
||||||
|
BOOST_CHECK_EQUAL(toml::find<std::string>(data.at("table"), "key"), "value");
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// CRLF
|
||||||
|
|
||||||
|
{
|
||||||
|
const std::string table(
|
||||||
|
"key = \"value\"\r\n"
|
||||||
|
"[table]\r\n"
|
||||||
|
"key = \"value\"\r\n"
|
||||||
|
"\r\n"
|
||||||
|
);
|
||||||
|
std::istringstream iss(table);
|
||||||
|
const auto data = toml::parse(iss,
|
||||||
|
"test_files_end_with_newline.toml");
|
||||||
|
|
||||||
|
BOOST_CHECK_EQUAL(toml::get <std::string>(data.at("key")), "value");
|
||||||
|
BOOST_CHECK_EQUAL(toml::find<std::string>(data.at("table"), "key"), "value");
|
||||||
|
}
|
||||||
|
{
|
||||||
|
const std::string table(
|
||||||
|
"key = \"value\"\r\n"
|
||||||
|
"[table]\r\n"
|
||||||
|
"key = \"value\"\r\n"
|
||||||
|
"\r\n"
|
||||||
|
"\r\n"
|
||||||
|
);
|
||||||
|
std::istringstream iss(table);
|
||||||
|
const auto data = toml::parse(iss,
|
||||||
|
"test_files_end_with_newline.toml");
|
||||||
|
|
||||||
|
BOOST_CHECK_EQUAL(toml::get <std::string>(data.at("key")), "value");
|
||||||
|
BOOST_CHECK_EQUAL(toml::find<std::string>(data.at("table"), "key"), "value");
|
||||||
|
}
|
||||||
|
|
||||||
|
// with whitespaces
|
||||||
|
|
||||||
|
{
|
||||||
|
const std::string table(
|
||||||
|
"key = \"value\"\r\n"
|
||||||
|
"[table]\r\n"
|
||||||
|
"key = \"value\"\r\n"
|
||||||
|
" \r\n"
|
||||||
|
);
|
||||||
|
std::istringstream iss(table);
|
||||||
|
const auto data = toml::parse(iss,
|
||||||
|
"test_files_end_with_newline.toml");
|
||||||
|
|
||||||
|
BOOST_CHECK_EQUAL(toml::get <std::string>(data.at("key")), "value");
|
||||||
|
BOOST_CHECK_EQUAL(toml::find<std::string>(data.at("table"), "key"), "value");
|
||||||
|
}
|
||||||
|
{
|
||||||
|
const std::string table(
|
||||||
|
"key = \"value\"\r\n"
|
||||||
|
"[table]\r\n"
|
||||||
|
"key = \"value\"\r\n"
|
||||||
|
"\r\n"
|
||||||
|
" \r\n"
|
||||||
|
);
|
||||||
|
std::istringstream iss(table);
|
||||||
|
const auto data = toml::parse(iss,
|
||||||
|
"test_files_end_with_newline.toml");
|
||||||
|
|
||||||
|
BOOST_CHECK_EQUAL(toml::get <std::string>(data.at("key")), "value");
|
||||||
|
BOOST_CHECK_EQUAL(toml::find<std::string>(data.at("table"), "key"), "value");
|
||||||
|
}
|
||||||
|
{
|
||||||
|
const std::string table(
|
||||||
|
"key = \"value\"\r\n"
|
||||||
|
"[table]\r\n"
|
||||||
|
"key = \"value\"\r\n"
|
||||||
|
" \r\n"
|
||||||
|
"\r\n"
|
||||||
|
);
|
||||||
|
std::istringstream iss(table);
|
||||||
|
const auto data = toml::parse(iss,
|
||||||
|
"test_files_end_with_newline.toml");
|
||||||
|
|
||||||
|
BOOST_CHECK_EQUAL(toml::get <std::string>(data.at("key")), "value");
|
||||||
|
BOOST_CHECK_EQUAL(toml::find<std::string>(data.at("table"), "key"), "value");
|
||||||
|
}
|
||||||
|
{
|
||||||
|
const std::string table(
|
||||||
|
"key = \"value\"\r\n"
|
||||||
|
"[table]\r\n"
|
||||||
|
"key = \"value\"\r\n"
|
||||||
|
" \r\n"
|
||||||
|
" \r\n"
|
||||||
|
);
|
||||||
|
std::istringstream iss(table);
|
||||||
|
const auto data = toml::parse(iss,
|
||||||
|
"test_files_end_with_newline.toml");
|
||||||
|
|
||||||
|
BOOST_CHECK_EQUAL(toml::get <std::string>(data.at("key")), "value");
|
||||||
|
BOOST_CHECK_EQUAL(toml::find<std::string>(data.at("table"), "key"), "value");
|
||||||
|
}
|
||||||
|
{
|
||||||
|
const std::string table(
|
||||||
|
"key = \"value\"\r\n"
|
||||||
|
"[table]\r\n"
|
||||||
|
"key = \"value\"\r\n"
|
||||||
|
" "
|
||||||
|
);
|
||||||
|
std::istringstream iss(table);
|
||||||
|
const auto data = toml::parse(iss,
|
||||||
|
"test_files_end_with_newline.toml");
|
||||||
|
|
||||||
|
BOOST_CHECK_EQUAL(toml::get <std::string>(data.at("key")), "value");
|
||||||
|
BOOST_CHECK_EQUAL(toml::find<std::string>(data.at("table"), "key"), "value");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -13,23 +13,23 @@ using namespace detail;
|
|||||||
|
|
||||||
BOOST_AUTO_TEST_CASE(test_bare_key)
|
BOOST_AUTO_TEST_CASE(test_bare_key)
|
||||||
{
|
{
|
||||||
TOML11_TEST_PARSE_EQUAL_VALUE(parse_key, "barekey", std::vector<key>(1, "barekey"));
|
TOML11_TEST_PARSE_EQUAL(parse_key, "barekey", std::vector<key>(1, "barekey"));
|
||||||
TOML11_TEST_PARSE_EQUAL_VALUE(parse_key, "bare-key", std::vector<key>(1, "bare-key"));
|
TOML11_TEST_PARSE_EQUAL(parse_key, "bare-key", std::vector<key>(1, "bare-key"));
|
||||||
TOML11_TEST_PARSE_EQUAL_VALUE(parse_key, "bare_key", std::vector<key>(1, "bare_key"));
|
TOML11_TEST_PARSE_EQUAL(parse_key, "bare_key", std::vector<key>(1, "bare_key"));
|
||||||
TOML11_TEST_PARSE_EQUAL_VALUE(parse_key, "1234", std::vector<key>(1, "1234"));
|
TOML11_TEST_PARSE_EQUAL(parse_key, "1234", std::vector<key>(1, "1234"));
|
||||||
}
|
}
|
||||||
|
|
||||||
BOOST_AUTO_TEST_CASE(test_quoted_key)
|
BOOST_AUTO_TEST_CASE(test_quoted_key)
|
||||||
{
|
{
|
||||||
TOML11_TEST_PARSE_EQUAL_VALUE(parse_key, "\"127.0.0.1\"", std::vector<key>(1, "127.0.0.1" ));
|
TOML11_TEST_PARSE_EQUAL(parse_key, "\"127.0.0.1\"", std::vector<key>(1, "127.0.0.1" ));
|
||||||
TOML11_TEST_PARSE_EQUAL_VALUE(parse_key, "\"character encoding\"", std::vector<key>(1, "character encoding"));
|
TOML11_TEST_PARSE_EQUAL(parse_key, "\"character encoding\"", std::vector<key>(1, "character encoding"));
|
||||||
#if defined(_MSC_VER) || defined(__INTEL_COMPILER)
|
#if defined(_MSC_VER) || defined(__INTEL_COMPILER)
|
||||||
TOML11_TEST_PARSE_EQUAL_VALUE(parse_key, "\"\xCA\x8E\xC7\x9D\xCA\x9E\"", std::vector<key>(1, "\xCA\x8E\xC7\x9D\xCA\x9E"));
|
TOML11_TEST_PARSE_EQUAL(parse_key, "\"\xCA\x8E\xC7\x9D\xCA\x9E\"", std::vector<key>(1, "\xCA\x8E\xC7\x9D\xCA\x9E"));
|
||||||
#else
|
#else
|
||||||
TOML11_TEST_PARSE_EQUAL_VALUE(parse_key, "\"ʎǝʞ\"", std::vector<key>(1, "ʎǝʞ" ));
|
TOML11_TEST_PARSE_EQUAL(parse_key, "\"ʎǝʞ\"", std::vector<key>(1, "ʎǝʞ" ));
|
||||||
#endif
|
#endif
|
||||||
TOML11_TEST_PARSE_EQUAL_VALUE(parse_key, "'key2'", std::vector<key>(1, "key2" ));
|
TOML11_TEST_PARSE_EQUAL(parse_key, "'key2'", std::vector<key>(1, "key2" ));
|
||||||
TOML11_TEST_PARSE_EQUAL_VALUE(parse_key, "'quoted \"value\"'", std::vector<key>(1, "quoted \"value\"" ));
|
TOML11_TEST_PARSE_EQUAL(parse_key, "'quoted \"value\"'", std::vector<key>(1, "quoted \"value\"" ));
|
||||||
}
|
}
|
||||||
|
|
||||||
BOOST_AUTO_TEST_CASE(test_dotted_key)
|
BOOST_AUTO_TEST_CASE(test_dotted_key)
|
||||||
@@ -38,13 +38,13 @@ BOOST_AUTO_TEST_CASE(test_dotted_key)
|
|||||||
std::vector<key> keys(2);
|
std::vector<key> keys(2);
|
||||||
keys[0] = "physical";
|
keys[0] = "physical";
|
||||||
keys[1] = "color";
|
keys[1] = "color";
|
||||||
TOML11_TEST_PARSE_EQUAL_VALUE(parse_key, "physical.color", keys);
|
TOML11_TEST_PARSE_EQUAL(parse_key, "physical.color", keys);
|
||||||
}
|
}
|
||||||
{
|
{
|
||||||
std::vector<key> keys(2);
|
std::vector<key> keys(2);
|
||||||
keys[0] = "physical";
|
keys[0] = "physical";
|
||||||
keys[1] = "shape";
|
keys[1] = "shape";
|
||||||
TOML11_TEST_PARSE_EQUAL_VALUE(parse_key, "physical.shape", keys);
|
TOML11_TEST_PARSE_EQUAL(parse_key, "physical.shape", keys);
|
||||||
}
|
}
|
||||||
{
|
{
|
||||||
std::vector<key> keys(4);
|
std::vector<key> keys(4);
|
||||||
@@ -52,12 +52,12 @@ BOOST_AUTO_TEST_CASE(test_dotted_key)
|
|||||||
keys[1] = "y";
|
keys[1] = "y";
|
||||||
keys[2] = "z";
|
keys[2] = "z";
|
||||||
keys[3] = "w";
|
keys[3] = "w";
|
||||||
TOML11_TEST_PARSE_EQUAL_VALUE(parse_key, "x.y.z.w", keys);
|
TOML11_TEST_PARSE_EQUAL(parse_key, "x.y.z.w", keys);
|
||||||
}
|
}
|
||||||
{
|
{
|
||||||
std::vector<key> keys(2);
|
std::vector<key> keys(2);
|
||||||
keys[0] = "site";
|
keys[0] = "site";
|
||||||
keys[1] = "google.com";
|
keys[1] = "google.com";
|
||||||
TOML11_TEST_PARSE_EQUAL_VALUE(parse_key, "site.\"google.com\"", keys);
|
TOML11_TEST_PARSE_EQUAL(parse_key, "site.\"google.com\"", keys);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
54
tests/test_serialize_file.cpp
Normal file
54
tests/test_serialize_file.cpp
Normal file
@@ -0,0 +1,54 @@
|
|||||||
|
#define BOOST_TEST_MODULE "test_serialize_file"
|
||||||
|
#ifdef UNITTEST_FRAMEWORK_LIBRARY_EXIST
|
||||||
|
#include <boost/test/unit_test.hpp>
|
||||||
|
#else
|
||||||
|
#define BOOST_TEST_NO_LIB
|
||||||
|
#include <boost/test/included/unit_test.hpp>
|
||||||
|
#endif
|
||||||
|
#include <toml.hpp>
|
||||||
|
#include <iostream>
|
||||||
|
#include <fstream>
|
||||||
|
|
||||||
|
|
||||||
|
BOOST_AUTO_TEST_CASE(test_example)
|
||||||
|
{
|
||||||
|
const auto data = toml::parse("toml/tests/example.toml");
|
||||||
|
{
|
||||||
|
std::ofstream ofs("tmp1.toml");
|
||||||
|
ofs << std::setw(80) << data;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto serialized = toml::parse("tmp1.toml");
|
||||||
|
{
|
||||||
|
auto& owner = toml::get<toml::table>(serialized.at("owner"));
|
||||||
|
auto& bio = toml::get<std::string>(owner.at("bio"));
|
||||||
|
const auto CR = std::find(bio.begin(), bio.end(), '\r');
|
||||||
|
if(CR != bio.end())
|
||||||
|
{
|
||||||
|
bio.erase(CR);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
BOOST_CHECK(data == serialized);
|
||||||
|
}
|
||||||
|
|
||||||
|
BOOST_AUTO_TEST_CASE(test_fruit)
|
||||||
|
{
|
||||||
|
const auto data = toml::parse("toml/tests/fruit.toml");
|
||||||
|
{
|
||||||
|
std::ofstream ofs("tmp2.toml");
|
||||||
|
ofs << std::setw(80) << data;
|
||||||
|
}
|
||||||
|
const auto serialized = toml::parse("tmp2.toml");
|
||||||
|
BOOST_CHECK(data == serialized);
|
||||||
|
}
|
||||||
|
|
||||||
|
BOOST_AUTO_TEST_CASE(test_hard_example)
|
||||||
|
{
|
||||||
|
const auto data = toml::parse("toml/tests/hard_example.toml");
|
||||||
|
{
|
||||||
|
std::ofstream ofs("tmp3.toml");
|
||||||
|
ofs << std::setw(80) << data;
|
||||||
|
}
|
||||||
|
const auto serialized = toml::parse("tmp3.toml");
|
||||||
|
BOOST_CHECK(data == serialized);
|
||||||
|
}
|
||||||
1
toml.hpp
1
toml.hpp
@@ -34,6 +34,7 @@
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
#include "toml/parser.hpp"
|
#include "toml/parser.hpp"
|
||||||
|
#include "toml/serializer.hpp"
|
||||||
#include "toml/to_toml.hpp"
|
#include "toml/to_toml.hpp"
|
||||||
#include "toml/from_toml.hpp"
|
#include "toml/from_toml.hpp"
|
||||||
#include "toml/get.hpp"
|
#include "toml/get.hpp"
|
||||||
|
|||||||
@@ -39,7 +39,7 @@ inline std::string show_char(const char c)
|
|||||||
else
|
else
|
||||||
{
|
{
|
||||||
std::ostringstream oss;
|
std::ostringstream oss;
|
||||||
oss << std::hex << std::setfill('0') << std::setw(2) << "0x"
|
oss << "0x" << std::hex << std::setfill('0') << std::setw(2)
|
||||||
<< static_cast<int>(c);
|
<< static_cast<int>(c);
|
||||||
return oss.str();
|
return oss.str();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -14,6 +14,36 @@
|
|||||||
namespace toml
|
namespace toml
|
||||||
{
|
{
|
||||||
|
|
||||||
|
// To avoid non-threadsafe std::localtime. In C11 (not C++11!), localtime_s is
|
||||||
|
// provided in the absolutely same purpose, but C++11 is actually not compatible
|
||||||
|
// with C11. We need to dispatch the function depending on the OS.
|
||||||
|
namespace detail
|
||||||
|
{
|
||||||
|
// TODO: find more sophisticated way to handle this
|
||||||
|
#if _POSIX_C_SOURCE >= 1 || _XOPEN_SOURCE || _BSD_SOURCE || _SVID_SOURCE || _POSIX_SOURCE
|
||||||
|
inline std::tm localtime_s(const std::time_t* src)
|
||||||
|
{
|
||||||
|
std::tm dst;
|
||||||
|
const auto result = ::localtime_r(src, &dst);
|
||||||
|
if(!result)
|
||||||
|
{
|
||||||
|
throw std::runtime_error("localtime_r failed.");
|
||||||
|
}
|
||||||
|
return dst;
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
// XXX: On Windows, std::localtime is thread-safe because they uses thread-local
|
||||||
|
// storage to store the instance of std::tm. On the other platforms, it may not
|
||||||
|
// be thread-safe.
|
||||||
|
inline std::tm localtime_s(const std::time_t* src)
|
||||||
|
{
|
||||||
|
const auto result = std::localtime(src);
|
||||||
|
if(!result) {throw std::runtime_error("localtime failed.");}
|
||||||
|
return *result;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
} // detail
|
||||||
|
|
||||||
enum class month_t : std::int8_t
|
enum class month_t : std::int8_t
|
||||||
{
|
{
|
||||||
Jan = 0,
|
Jan = 0,
|
||||||
@@ -51,9 +81,7 @@ struct local_date
|
|||||||
explicit local_date(const std::chrono::system_clock::time_point& tp)
|
explicit local_date(const std::chrono::system_clock::time_point& tp)
|
||||||
{
|
{
|
||||||
const auto t = std::chrono::system_clock::to_time_t(tp);
|
const auto t = std::chrono::system_clock::to_time_t(tp);
|
||||||
const auto tmp = std::localtime(&t); //XXX: not threadsafe!
|
const auto time = detail::localtime_s(&t);
|
||||||
assert(tmp); // if std::localtime fails, tmp is nullptr
|
|
||||||
const std::tm time = *tmp;
|
|
||||||
*this = local_date(time);
|
*this = local_date(time);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -330,10 +358,8 @@ struct local_datetime
|
|||||||
explicit local_datetime(const std::chrono::system_clock::time_point& tp)
|
explicit local_datetime(const std::chrono::system_clock::time_point& tp)
|
||||||
{
|
{
|
||||||
const auto t = std::chrono::system_clock::to_time_t(tp);
|
const auto t = std::chrono::system_clock::to_time_t(tp);
|
||||||
const auto tmp = std::localtime(&t); //XXX: not threadsafe!
|
std::tm time = detail::localtime_s(&t);
|
||||||
assert(tmp); // if std::localtime fails, tmp is nullptr
|
|
||||||
|
|
||||||
std::tm time = *tmp;
|
|
||||||
this->date = local_date(time);
|
this->date = local_date(time);
|
||||||
this->time = local_time(time);
|
this->time = local_time(time);
|
||||||
|
|
||||||
@@ -483,9 +509,7 @@ struct offset_datetime
|
|||||||
{
|
{
|
||||||
// get current timezone
|
// get current timezone
|
||||||
const auto tmp1 = std::time(nullptr);
|
const auto tmp1 = std::time(nullptr);
|
||||||
const auto tmp2 = std::localtime(&tmp1); // XXX not threadsafe!
|
const auto t = detail::localtime_s(&tmp1);
|
||||||
assert(tmp2);
|
|
||||||
std::tm t = *tmp2;
|
|
||||||
|
|
||||||
std::array<char, 6> buf;
|
std::array<char, 6> buf;
|
||||||
const auto result = std::strftime(buf.data(), 6, "%z", &t); // +hhmm\0
|
const auto result = std::strftime(buf.data(), 6, "%z", &t); // +hhmm\0
|
||||||
|
|||||||
12
toml/get.hpp
12
toml/get.hpp
@@ -295,7 +295,7 @@ T get(const toml::value& v)
|
|||||||
// ============================================================================
|
// ============================================================================
|
||||||
// find and get
|
// find and get
|
||||||
|
|
||||||
template<typename T>
|
template<typename T = ::toml::value>
|
||||||
decltype(::toml::get<T>(std::declval<const ::toml::value&>()))
|
decltype(::toml::get<T>(std::declval<const ::toml::value&>()))
|
||||||
find(const toml::table& tab, const toml::key& ky,
|
find(const toml::table& tab, const toml::key& ky,
|
||||||
std::string tablename = "unknown table")
|
std::string tablename = "unknown table")
|
||||||
@@ -307,7 +307,7 @@ find(const toml::table& tab, const toml::key& ky,
|
|||||||
}
|
}
|
||||||
return ::toml::get<T>(tab.at(ky));
|
return ::toml::get<T>(tab.at(ky));
|
||||||
}
|
}
|
||||||
template<typename T>
|
template<typename T = ::toml::value>
|
||||||
decltype(::toml::get<T>(std::declval<::toml::value&>()))
|
decltype(::toml::get<T>(std::declval<::toml::value&>()))
|
||||||
find(toml::table& tab, const toml::key& ky,
|
find(toml::table& tab, const toml::key& ky,
|
||||||
std::string tablename = "unknown table")
|
std::string tablename = "unknown table")
|
||||||
@@ -319,7 +319,7 @@ find(toml::table& tab, const toml::key& ky,
|
|||||||
}
|
}
|
||||||
return ::toml::get<T>(tab[ky]);
|
return ::toml::get<T>(tab[ky]);
|
||||||
}
|
}
|
||||||
template<typename T>
|
template<typename T = ::toml::value>
|
||||||
decltype(::toml::get<T>(std::declval<::toml::value&&>()))
|
decltype(::toml::get<T>(std::declval<::toml::value&&>()))
|
||||||
find(toml::table&& tab, const toml::key& ky,
|
find(toml::table&& tab, const toml::key& ky,
|
||||||
std::string tablename = "unknown table")
|
std::string tablename = "unknown table")
|
||||||
@@ -332,7 +332,7 @@ find(toml::table&& tab, const toml::key& ky,
|
|||||||
return ::toml::get<T>(std::move(tab[ky]));
|
return ::toml::get<T>(std::move(tab[ky]));
|
||||||
}
|
}
|
||||||
|
|
||||||
template<typename T>
|
template<typename T = ::toml::value>
|
||||||
decltype(::toml::get<T>(std::declval<const ::toml::value&>()))
|
decltype(::toml::get<T>(std::declval<const ::toml::value&>()))
|
||||||
find(const toml::value& v, const toml::key& ky)
|
find(const toml::value& v, const toml::key& ky)
|
||||||
{
|
{
|
||||||
@@ -345,7 +345,7 @@ find(const toml::value& v, const toml::key& ky)
|
|||||||
}
|
}
|
||||||
return ::toml::get<T>(tab.at(ky));
|
return ::toml::get<T>(tab.at(ky));
|
||||||
}
|
}
|
||||||
template<typename T>
|
template<typename T = ::toml::value>
|
||||||
decltype(::toml::get<T>(std::declval<::toml::value&>()))
|
decltype(::toml::get<T>(std::declval<::toml::value&>()))
|
||||||
find(toml::value& v, const toml::key& ky)
|
find(toml::value& v, const toml::key& ky)
|
||||||
{
|
{
|
||||||
@@ -358,7 +358,7 @@ find(toml::value& v, const toml::key& ky)
|
|||||||
}
|
}
|
||||||
return ::toml::get<T>(tab.at(ky));
|
return ::toml::get<T>(tab.at(ky));
|
||||||
}
|
}
|
||||||
template<typename T>
|
template<typename T = ::toml::value>
|
||||||
decltype(::toml::get<T>(std::declval<::toml::value&&>()))
|
decltype(::toml::get<T>(std::declval<::toml::value&&>()))
|
||||||
find(toml::value&& v, const toml::key& ky)
|
find(toml::value&& v, const toml::key& ky)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -124,9 +124,9 @@ using lex_escape_unicode_short = sequence<character<'u'>,
|
|||||||
using lex_escape_unicode_long = sequence<character<'U'>,
|
using lex_escape_unicode_long = sequence<character<'U'>,
|
||||||
repeat<lex_hex_dig, exactly<8>>>;
|
repeat<lex_hex_dig, exactly<8>>>;
|
||||||
using lex_escape_seq_char = either<character<'"'>, character<'\\'>,
|
using lex_escape_seq_char = either<character<'"'>, character<'\\'>,
|
||||||
character<'/'>, character<'b'>,
|
character<'b'>, character<'f'>,
|
||||||
character<'f'>, character<'n'>,
|
character<'n'>, character<'r'>,
|
||||||
character<'r'>, character<'t'>,
|
character<'t'>,
|
||||||
lex_escape_unicode_short,
|
lex_escape_unicode_short,
|
||||||
lex_escape_unicode_long
|
lex_escape_unicode_long
|
||||||
>;
|
>;
|
||||||
|
|||||||
381
toml/parser.hpp
381
toml/parser.hpp
@@ -34,8 +34,8 @@ parse_boolean(location<Container>& loc)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
loc.iter() = first; //rollback
|
loc.iter() = first; //rollback
|
||||||
return err(format_underline("[error] toml::parse_boolean", loc,
|
return err(format_underline("[error] toml::parse_boolean: ", loc,
|
||||||
"token is not boolean", {"boolean is `true` or `false`"}));
|
"the next token is not a boolean"));
|
||||||
}
|
}
|
||||||
|
|
||||||
template<typename Container>
|
template<typename Container>
|
||||||
@@ -63,8 +63,8 @@ parse_binary_integer(location<Container>& loc)
|
|||||||
return ok(std::make_pair(retval, token.unwrap()));
|
return ok(std::make_pair(retval, token.unwrap()));
|
||||||
}
|
}
|
||||||
loc.iter() = first;
|
loc.iter() = first;
|
||||||
return err(format_underline("[error] toml::parse_binary_integer", loc,
|
return err(format_underline("[error] toml::parse_binary_integer:", loc,
|
||||||
"token is not binary integer", {"binary integer is like: 0b0011"}));
|
"the next token is not an integer"));
|
||||||
}
|
}
|
||||||
|
|
||||||
template<typename Container>
|
template<typename Container>
|
||||||
@@ -84,9 +84,8 @@ parse_octal_integer(location<Container>& loc)
|
|||||||
return ok(std::make_pair(retval, token.unwrap()));
|
return ok(std::make_pair(retval, token.unwrap()));
|
||||||
}
|
}
|
||||||
loc.iter() = first;
|
loc.iter() = first;
|
||||||
|
return err(format_underline("[error] toml::parse_octal_integer:", loc,
|
||||||
return err(format_underline("[error] toml::parse_octal_integer", loc,
|
"the next token is not an integer"));
|
||||||
"token is not octal integer", {"octal integer is like: 0o775"}));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
template<typename Container>
|
template<typename Container>
|
||||||
@@ -107,7 +106,7 @@ parse_hexadecimal_integer(location<Container>& loc)
|
|||||||
}
|
}
|
||||||
loc.iter() = first;
|
loc.iter() = first;
|
||||||
return err(format_underline("[error] toml::parse_hexadecimal_integer", loc,
|
return err(format_underline("[error] toml::parse_hexadecimal_integer", loc,
|
||||||
"token is not hex integer", {"hex integer is like: 0xC0FFEE"}));
|
"the next token is not an integer"));
|
||||||
}
|
}
|
||||||
|
|
||||||
template<typename Container>
|
template<typename Container>
|
||||||
@@ -134,10 +133,8 @@ parse_integer(location<Container>& loc)
|
|||||||
return ok(std::make_pair(retval, token.unwrap()));
|
return ok(std::make_pair(retval, token.unwrap()));
|
||||||
}
|
}
|
||||||
loc.iter() = first;
|
loc.iter() = first;
|
||||||
return err(format_underline("[error] toml::parse_integer", loc,
|
return err(format_underline("[error] toml::parse_integer: ", loc,
|
||||||
"token is not integer", {"integer is like: +42",
|
"the next token is not an integer"));
|
||||||
"hex integer is like: 0xC0FFEE", "octal integer is like: 0o775",
|
|
||||||
"binary integer is like: 0b0011"}));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
template<typename Container>
|
template<typename Container>
|
||||||
@@ -226,11 +223,12 @@ parse_floating(location<Container>& loc)
|
|||||||
}
|
}
|
||||||
loc.iter() = first;
|
loc.iter() = first;
|
||||||
return err(format_underline("[error] toml::parse_floating: ", loc,
|
return err(format_underline("[error] toml::parse_floating: ", loc,
|
||||||
"token is not a float", {"floating point is like: -3.14e+1"}));
|
"the next token is not a float"));
|
||||||
}
|
}
|
||||||
|
|
||||||
template<typename Container>
|
template<typename Container, typename Container2>
|
||||||
std::string read_utf8_codepoint(const region<Container>& reg)
|
std::string read_utf8_codepoint(const region<Container>& reg,
|
||||||
|
/* for err msg */ const location<Container2>& loc)
|
||||||
{
|
{
|
||||||
const auto str = reg.str().substr(1);
|
const auto str = reg.str().substr(1);
|
||||||
std::uint_least32_t codepoint;
|
std::uint_least32_t codepoint;
|
||||||
@@ -250,20 +248,27 @@ std::string read_utf8_codepoint(const region<Container>& reg)
|
|||||||
}
|
}
|
||||||
else if(codepoint < 0x10000) // U+0800...U+FFFF
|
else if(codepoint < 0x10000) // U+0800...U+FFFF
|
||||||
{
|
{
|
||||||
|
if(0xD800 <= codepoint && codepoint <= 0xDFFF)
|
||||||
|
{
|
||||||
|
std::cerr << format_underline("[warning] "
|
||||||
|
"toml::read_utf8_codepoint: codepoints in the range "
|
||||||
|
"[0xD800, 0xDFFF] are not valid UTF-8.",
|
||||||
|
loc, "not a valid UTF-8 codepoint") << std::endl;
|
||||||
|
}
|
||||||
|
assert(codepoint < 0xD800 || 0xDFFF < codepoint);
|
||||||
// 1110yyyy 10yxxxxx 10xxxxxx
|
// 1110yyyy 10yxxxxx 10xxxxxx
|
||||||
character += static_cast<unsigned char>(0xE0| codepoint >> 12);
|
character += static_cast<unsigned char>(0xE0| codepoint >> 12);
|
||||||
character += static_cast<unsigned char>(0x80|(codepoint >> 6 & 0x3F));
|
character += static_cast<unsigned char>(0x80|(codepoint >> 6 & 0x3F));
|
||||||
character += static_cast<unsigned char>(0x80|(codepoint & 0x3F));
|
character += static_cast<unsigned char>(0x80|(codepoint & 0x3F));
|
||||||
}
|
}
|
||||||
else if(codepoint < 0x200000) // U+10000 ... U+1FFFFF
|
else if(codepoint < 0x200000) // U+010000 ... U+1FFFFF
|
||||||
{
|
{
|
||||||
if(0x10FFFF < codepoint) // out of Unicode region
|
if(0x10FFFF < codepoint) // out of Unicode region
|
||||||
{
|
{
|
||||||
std::cerr << format_underline(concat_to_string("[warning] "
|
std::cerr << format_underline("[error] "
|
||||||
"input codepoint (", str, ") is too large to decode as "
|
"toml::read_utf8_codepoint: input codepoint is too large to "
|
||||||
"a unicode character. The result may not be able to render "
|
"decode as a unicode character.", loc,
|
||||||
"to your screen."), reg, "should be in [0x00..0x10FFFF]")
|
"should be in [0x00..0x10FFFF]") << std::endl;
|
||||||
<< std::endl;
|
|
||||||
}
|
}
|
||||||
// 11110yyy 10yyxxxx 10xxxxxx 10xxxxxx
|
// 11110yyy 10yyxxxx 10xxxxxx 10xxxxxx
|
||||||
character += static_cast<unsigned char>(0xF0| codepoint >> 18);
|
character += static_cast<unsigned char>(0xF0| codepoint >> 18);
|
||||||
@@ -286,9 +291,8 @@ result<std::string, std::string> parse_escape_sequence(location<Container>& loc)
|
|||||||
const auto first = loc.iter();
|
const auto first = loc.iter();
|
||||||
if(first == loc.end() || *first != '\\')
|
if(first == loc.end() || *first != '\\')
|
||||||
{
|
{
|
||||||
return err(format_underline("[error]: "
|
return err(format_underline("[error]: toml::parse_escape_sequence: ", loc,
|
||||||
"toml::parse_escape_sequence: location does not points \"\\\"",
|
"the next token is not an escape sequence \"\\\""));
|
||||||
loc, "should be \"\\\""));
|
|
||||||
}
|
}
|
||||||
++loc.iter();
|
++loc.iter();
|
||||||
switch(*loc.iter())
|
switch(*loc.iter())
|
||||||
@@ -304,7 +308,7 @@ result<std::string, std::string> parse_escape_sequence(location<Container>& loc)
|
|||||||
{
|
{
|
||||||
if(const auto token = lex_escape_unicode_short::invoke(loc))
|
if(const auto token = lex_escape_unicode_short::invoke(loc))
|
||||||
{
|
{
|
||||||
return ok(read_utf8_codepoint(token.unwrap()));
|
return ok(read_utf8_codepoint(token.unwrap(), loc));
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@@ -317,7 +321,7 @@ result<std::string, std::string> parse_escape_sequence(location<Container>& loc)
|
|||||||
{
|
{
|
||||||
if(const auto token = lex_escape_unicode_long::invoke(loc))
|
if(const auto token = lex_escape_unicode_long::invoke(loc))
|
||||||
{
|
{
|
||||||
return ok(read_utf8_codepoint(token.unwrap()));
|
return ok(read_utf8_codepoint(token.unwrap(), loc));
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@@ -527,8 +531,8 @@ parse_string(location<Container>& loc)
|
|||||||
if(const auto rslt = parse_ml_literal_string(loc)) {return rslt;}
|
if(const auto rslt = parse_ml_literal_string(loc)) {return rslt;}
|
||||||
if(const auto rslt = parse_basic_string(loc)) {return rslt;}
|
if(const auto rslt = parse_basic_string(loc)) {return rslt;}
|
||||||
if(const auto rslt = parse_literal_string(loc)) {return rslt;}
|
if(const auto rslt = parse_literal_string(loc)) {return rslt;}
|
||||||
return err(format_underline("[error] toml::parse_string: not a string",
|
return err(format_underline("[error] toml::parse_string: ", loc,
|
||||||
loc, "not a string"));
|
"the next token is not a string"));
|
||||||
}
|
}
|
||||||
|
|
||||||
template<typename Container>
|
template<typename Container>
|
||||||
@@ -576,11 +580,9 @@ parse_local_date(location<Container>& loc)
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
auto msg = format_underline("[error]: toml::parse_local_date: "
|
|
||||||
"invalid format", loc, token.unwrap_err(),
|
|
||||||
{"local date is like: 1979-05-27"});
|
|
||||||
loc.iter() = first;
|
loc.iter() = first;
|
||||||
return err(std::move(msg));
|
return err(format_underline("[error]: toml::parse_local_date: ", loc,
|
||||||
|
"the next token is not a local_date"));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -661,11 +663,9 @@ parse_local_time(location<Container>& loc)
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
auto msg = format_underline("[error]: toml::parse_local_time: "
|
|
||||||
"invalid format", loc, token.unwrap_err(),
|
|
||||||
{"local time is like: 00:32:00.999999"});
|
|
||||||
loc.iter() = first;
|
loc.iter() = first;
|
||||||
return err(std::move(msg));
|
return err(format_underline("[error]: toml::parse_local_time: ", loc,
|
||||||
|
"the next token is not a local_time"));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -706,11 +706,9 @@ parse_local_datetime(location<Container>& loc)
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
auto msg = format_underline("[error]: toml::parse_local_datetime: "
|
|
||||||
"invalid format", loc, token.unwrap_err(),
|
|
||||||
{"local datetime is like: 1979-05-27T00:32:00.999999"});
|
|
||||||
loc.iter() = first;
|
loc.iter() = first;
|
||||||
return err(std::move(msg));
|
return err(format_underline("[error]: toml::parse_local_datetime: ", loc,
|
||||||
|
"the next token is not a local_datetime"));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -757,43 +755,44 @@ parse_offset_datetime(location<Container>& loc)
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
auto msg = format_underline("[error]: toml::parse_offset_datetime: "
|
|
||||||
"invalid format", loc, token.unwrap_err(),
|
|
||||||
{"offset datetime is like: 1979-05-27T00:32:00-07:00",
|
|
||||||
"or in UTC (w/o offset) : 1979-05-27T00:32:00Z"});
|
|
||||||
loc.iter() = first;
|
loc.iter() = first;
|
||||||
return err(std::move(msg));
|
return err(format_underline("[error]: toml::parse_offset_datetime: ", loc,
|
||||||
|
"the next token is not a local_datetime"));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
template<typename Container>
|
template<typename Container>
|
||||||
result<key, std::string> parse_simple_key(location<Container>& loc)
|
result<std::pair<key, region<Container>>, std::string>
|
||||||
|
parse_simple_key(location<Container>& loc)
|
||||||
{
|
{
|
||||||
if(const auto bstr = parse_basic_string(loc))
|
if(const auto bstr = parse_basic_string(loc))
|
||||||
{
|
{
|
||||||
return ok(bstr.unwrap().first.str);
|
return ok(std::make_pair(bstr.unwrap().first.str, bstr.unwrap().second));
|
||||||
}
|
}
|
||||||
if(const auto lstr = parse_literal_string(loc))
|
if(const auto lstr = parse_literal_string(loc))
|
||||||
{
|
{
|
||||||
return ok(lstr.unwrap().first.str);
|
return ok(std::make_pair(lstr.unwrap().first.str, lstr.unwrap().second));
|
||||||
}
|
}
|
||||||
if(const auto bare = lex_unquoted_key::invoke(loc))
|
if(const auto bare = lex_unquoted_key::invoke(loc))
|
||||||
{
|
{
|
||||||
return ok(bare.unwrap().str());
|
const auto reg = bare.unwrap();
|
||||||
|
return ok(std::make_pair(reg.str(), reg));
|
||||||
}
|
}
|
||||||
return err(format_underline("[error] toml::parse_simple_key: "
|
return err(format_underline("[error] toml::parse_simple_key: ", loc,
|
||||||
"the next token is not a simple key", loc, "not a key"));
|
"the next token is not a simple key"));
|
||||||
}
|
}
|
||||||
|
|
||||||
// dotted key become vector of keys
|
// dotted key become vector of keys
|
||||||
template<typename Container>
|
template<typename Container>
|
||||||
result<std::vector<key>, std::string> parse_key(location<Container>& loc)
|
result<std::pair<std::vector<key>, region<Container>>, std::string>
|
||||||
|
parse_key(location<Container>& loc)
|
||||||
{
|
{
|
||||||
const auto first = loc.iter();
|
const auto first = loc.iter();
|
||||||
// dotted key -> foo.bar.baz whitespaces are allowed
|
// dotted key -> foo.bar.baz whitespaces are allowed
|
||||||
if(const auto token = lex_dotted_key::invoke(loc))
|
if(const auto token = lex_dotted_key::invoke(loc))
|
||||||
{
|
{
|
||||||
location<std::string> inner_loc(loc.name(), token.unwrap().str());
|
const auto reg = token.unwrap();
|
||||||
|
location<std::string> inner_loc(loc.name(), reg.str());
|
||||||
std::vector<key> keys;
|
std::vector<key> keys;
|
||||||
|
|
||||||
while(inner_loc.iter() != inner_loc.end())
|
while(inner_loc.iter() != inner_loc.end())
|
||||||
@@ -801,7 +800,7 @@ result<std::vector<key>, std::string> parse_key(location<Container>& loc)
|
|||||||
lex_ws::invoke(inner_loc);
|
lex_ws::invoke(inner_loc);
|
||||||
if(const auto k = parse_simple_key(inner_loc))
|
if(const auto k = parse_simple_key(inner_loc))
|
||||||
{
|
{
|
||||||
keys.push_back(k.unwrap());
|
keys.push_back(k.unwrap().first);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@@ -826,17 +825,17 @@ result<std::vector<key>, std::string> parse_key(location<Container>& loc)
|
|||||||
"should be `.`"));
|
"should be `.`"));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return ok(keys);
|
return ok(std::make_pair(keys, reg));
|
||||||
}
|
}
|
||||||
loc.iter() = first;
|
loc.iter() = first;
|
||||||
|
|
||||||
// simple key -> foo
|
// simple key -> foo
|
||||||
if(const auto smpl = parse_simple_key(loc))
|
if(const auto smpl = parse_simple_key(loc))
|
||||||
{
|
{
|
||||||
return ok(std::vector<key>(1, smpl.unwrap()));
|
return ok(std::make_pair(std::vector<key>(1, smpl.unwrap().first),
|
||||||
|
smpl.unwrap().second));
|
||||||
}
|
}
|
||||||
return err(format_underline("toml::parse_key: the next token is not a key",
|
return err(format_underline("[error] toml::parse_key: ", loc, "is not a valid key"));
|
||||||
loc, "not a key"));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// forward-decl to implement parse_array and parse_table
|
// forward-decl to implement parse_array and parse_table
|
||||||
@@ -854,8 +853,7 @@ parse_array(location<Container>& loc)
|
|||||||
}
|
}
|
||||||
if(*loc.iter() != '[')
|
if(*loc.iter() != '[')
|
||||||
{
|
{
|
||||||
return err(format_underline("[error] toml::parse_array: "
|
return err("[error] toml::parse_array: token is not an array");
|
||||||
"token is not an array", loc, "should be ["));
|
|
||||||
}
|
}
|
||||||
++loc.iter();
|
++loc.iter();
|
||||||
|
|
||||||
@@ -878,16 +876,39 @@ parse_array(location<Container>& loc)
|
|||||||
{
|
{
|
||||||
if(!retval.empty() && retval.front().type() != val.as_ok().type())
|
if(!retval.empty() && retval.front().type() != val.as_ok().type())
|
||||||
{
|
{
|
||||||
throw syntax_error(format_underline(
|
auto array_start_loc = loc;
|
||||||
"[error] toml::parse_array: type of elements should be the "
|
array_start_loc.iter() = first;
|
||||||
"same each other.", region<Container>(loc, first, loc.iter()),
|
|
||||||
"inhomogeneous types"));
|
throw syntax_error(format_underline("[error] toml::parse_array: "
|
||||||
|
"type of elements should be the same each other.",
|
||||||
|
std::vector<std::pair<region_base const*, std::string>>{
|
||||||
|
std::make_pair(
|
||||||
|
std::addressof(array_start_loc),
|
||||||
|
std::string("array starts here")
|
||||||
|
),
|
||||||
|
std::make_pair(
|
||||||
|
std::addressof(get_region(retval.front())),
|
||||||
|
std::string("value has type ") +
|
||||||
|
stringize(retval.front().type())
|
||||||
|
),
|
||||||
|
std::make_pair(
|
||||||
|
std::addressof(get_region(val.unwrap())),
|
||||||
|
std::string("value has different type, ") +
|
||||||
|
stringize(val.unwrap().type())
|
||||||
|
)
|
||||||
|
}));
|
||||||
}
|
}
|
||||||
retval.push_back(std::move(val.unwrap()));
|
retval.push_back(std::move(val.unwrap()));
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
return err(val.unwrap_err());
|
auto array_start_loc = loc;
|
||||||
|
array_start_loc.iter() = first;
|
||||||
|
|
||||||
|
throw syntax_error(format_underline("[error] toml::parse_array: "
|
||||||
|
"value having invalid format appeared in an array",
|
||||||
|
array_start_loc, "array starts here",
|
||||||
|
loc, "it is not a valid value."));
|
||||||
}
|
}
|
||||||
|
|
||||||
using lex_array_separator = sequence<maybe<lex_ws>, character<','>>;
|
using lex_array_separator = sequence<maybe<lex_ws>, character<','>>;
|
||||||
@@ -903,25 +924,29 @@ parse_array(location<Container>& loc)
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
return err(format_underline("[error] toml::parse_array: "
|
auto array_start_loc = loc;
|
||||||
"missing array separator `,`", loc, "should be `,`"));
|
array_start_loc.iter() = first;
|
||||||
|
|
||||||
|
throw syntax_error(format_underline("[error] toml::parse_array:"
|
||||||
|
" missing array separator `,` after a value",
|
||||||
|
array_start_loc, "array starts here", loc, "should be `,`"));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
loc.iter() = first;
|
loc.iter() = first;
|
||||||
return err(format_underline("[error] toml::parse_array: "
|
throw syntax_error(format_underline("[error] toml::parse_array: "
|
||||||
"array did not closed by `]`", loc, "should be closed"));
|
"array did not closed by `]`", loc, "should be closed"));
|
||||||
}
|
}
|
||||||
|
|
||||||
template<typename Container>
|
template<typename Container>
|
||||||
result<std::pair<std::vector<key>, value>, std::string>
|
result<std::pair<std::pair<std::vector<key>, region<Container>>, value>, std::string>
|
||||||
parse_key_value_pair(location<Container>& loc)
|
parse_key_value_pair(location<Container>& loc)
|
||||||
{
|
{
|
||||||
const auto first = loc.iter();
|
const auto first = loc.iter();
|
||||||
auto key = parse_key(loc);
|
auto key_reg = parse_key(loc);
|
||||||
if(!key)
|
if(!key_reg)
|
||||||
{
|
{
|
||||||
std::string msg = std::move(key.unwrap_err());
|
std::string msg = std::move(key_reg.unwrap_err());
|
||||||
// if the next token is keyvalue-separator, it means that there are no
|
// if the next token is keyvalue-separator, it means that there are no
|
||||||
// key. then we need to show error as "empty key is not allowed".
|
// key. then we need to show error as "empty key is not allowed".
|
||||||
if(const auto keyval_sep = lex_keyval_sep::invoke(loc))
|
if(const auto keyval_sep = lex_keyval_sep::invoke(loc))
|
||||||
@@ -962,6 +987,7 @@ parse_key_value_pair(location<Container>& loc)
|
|||||||
{
|
{
|
||||||
std::string msg;
|
std::string msg;
|
||||||
loc.iter() = after_kvsp;
|
loc.iter() = after_kvsp;
|
||||||
|
// check there is something not a comment/whitespace after `=`
|
||||||
if(sequence<maybe<lex_ws>, maybe<lex_comment>, lex_newline>::invoke(loc))
|
if(sequence<maybe<lex_ws>, maybe<lex_comment>, lex_newline>::invoke(loc))
|
||||||
{
|
{
|
||||||
loc.iter() = after_kvsp;
|
loc.iter() = after_kvsp;
|
||||||
@@ -969,15 +995,15 @@ parse_key_value_pair(location<Container>& loc)
|
|||||||
"missing value after key-value separator '='", loc,
|
"missing value after key-value separator '='", loc,
|
||||||
"expected value, but got nothing");
|
"expected value, but got nothing");
|
||||||
}
|
}
|
||||||
else
|
else // there is something not a comment/whitespace, so invalid format.
|
||||||
{
|
{
|
||||||
msg = format_underline("[error] toml::parse_key_value_pair: "
|
msg = std::move(val.unwrap_err());
|
||||||
"invalid value format", loc, val.unwrap_err());
|
|
||||||
}
|
}
|
||||||
loc.iter() = first;
|
loc.iter() = first;
|
||||||
return err(msg);
|
return err(msg);
|
||||||
}
|
}
|
||||||
return ok(std::make_pair(std::move(key.unwrap()), std::move(val.unwrap())));
|
return ok(std::make_pair(std::move(key_reg.unwrap()),
|
||||||
|
std::move(val.unwrap())));
|
||||||
}
|
}
|
||||||
|
|
||||||
// for error messages.
|
// for error messages.
|
||||||
@@ -996,10 +1022,80 @@ std::string format_dotted_keys(InputIterator first, const InputIterator last)
|
|||||||
return retval;
|
return retval;
|
||||||
}
|
}
|
||||||
|
|
||||||
template<typename InputIterator>
|
// forward decl for is_valid_forward_table_definition
|
||||||
|
template<typename Container>
|
||||||
|
result<std::pair<std::vector<key>, region<Container>>, std::string>
|
||||||
|
parse_table_key(location<Container>& loc);
|
||||||
|
// The following toml file is allowed.
|
||||||
|
// ```toml
|
||||||
|
// [a.b.c] # here, table `a` has element `b`.
|
||||||
|
// foo = "bar"
|
||||||
|
// [a] # merge a = {baz = "qux"} to a = {b = {...}}
|
||||||
|
// baz = "qux"
|
||||||
|
// ```
|
||||||
|
// But the following is not allowed.
|
||||||
|
// ```toml
|
||||||
|
// [a]
|
||||||
|
// b.c.foo = "bar"
|
||||||
|
// [a] # error! the same table [a] defined!
|
||||||
|
// baz = "qux"
|
||||||
|
// ```
|
||||||
|
// The following is neither allowed.
|
||||||
|
// ```toml
|
||||||
|
// a = { b.c.foo = "bar"}
|
||||||
|
// [a] # error! the same table [a] defined!
|
||||||
|
// baz = "qux"
|
||||||
|
// ```
|
||||||
|
// Here, it parses region of `tab->at(k)` as a table key and check the depth
|
||||||
|
// of the key. If the key region points deeper node, it would be allowed.
|
||||||
|
// Otherwise, the key points the same node. It would be rejected.
|
||||||
|
template<typename Iterator>
|
||||||
|
bool is_valid_forward_table_definition(const value& fwd,
|
||||||
|
Iterator key_first, Iterator key_curr, Iterator key_last)
|
||||||
|
{
|
||||||
|
location<std::string> def("internal", detail::get_region(fwd).str());
|
||||||
|
if(const auto tabkeys = parse_table_key(def))
|
||||||
|
{
|
||||||
|
// table keys always contains all the nodes from the root.
|
||||||
|
const auto& tks = tabkeys.unwrap().first;
|
||||||
|
if(std::distance(key_first, key_last) == tks.size() &&
|
||||||
|
std::equal(tks.begin(), tks.end(), key_first))
|
||||||
|
{
|
||||||
|
// the keys are equivalent. it is not allowed.
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
// the keys are not equivalent. it is allowed.
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if(const auto dotkeys = parse_key(def))
|
||||||
|
{
|
||||||
|
// consider the following case.
|
||||||
|
// [a]
|
||||||
|
// b.c = {d = 42}
|
||||||
|
// [a.b.c]
|
||||||
|
// e = 2.71
|
||||||
|
// this defines the table [a.b.c] twice. no?
|
||||||
|
|
||||||
|
// a dotted key starts from the node representing a table in which the
|
||||||
|
// dotted key belongs to.
|
||||||
|
const auto& dks = dotkeys.unwrap().first;
|
||||||
|
if(std::distance(key_curr, key_last) == dks.size() &&
|
||||||
|
std::equal(dks.begin(), dks.end(), key_curr))
|
||||||
|
{
|
||||||
|
// the keys are equivalent. it is not allowed.
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
// the keys are not equivalent. it is allowed.
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename InputIterator, typename Container>
|
||||||
result<bool, std::string>
|
result<bool, std::string>
|
||||||
insert_nested_key(table& root, const toml::value& v,
|
insert_nested_key(table& root, const toml::value& v,
|
||||||
InputIterator iter, const InputIterator last,
|
InputIterator iter, const InputIterator last,
|
||||||
|
region<Container> key_reg,
|
||||||
const bool is_array_of_table = false)
|
const bool is_array_of_table = false)
|
||||||
{
|
{
|
||||||
static_assert(std::is_same<key,
|
static_assert(std::is_same<key,
|
||||||
@@ -1081,25 +1177,39 @@ insert_nested_key(table& root, const toml::value& v,
|
|||||||
}
|
}
|
||||||
else // if not, we need to create the array of table
|
else // if not, we need to create the array of table
|
||||||
{
|
{
|
||||||
toml::value aot(v); // copy region info from table to array
|
toml::value aot(toml::array(1, v), key_reg);
|
||||||
// update content by an array with one element
|
|
||||||
detail::assign_keeping_region(aot,
|
|
||||||
::toml::value(toml::array(1, v)));
|
|
||||||
|
|
||||||
tab->insert(std::make_pair(k, aot));
|
tab->insert(std::make_pair(k, aot));
|
||||||
return ok(true);
|
return ok(true);
|
||||||
}
|
}
|
||||||
}
|
} // end if(array of table)
|
||||||
|
|
||||||
if(tab->count(k) == 1)
|
if(tab->count(k) == 1)
|
||||||
{
|
{
|
||||||
if(tab->at(k).is(value_t::Table) && v.is(value_t::Table))
|
if(tab->at(k).is(value_t::Table) && v.is(value_t::Table))
|
||||||
|
{
|
||||||
|
if(!is_valid_forward_table_definition(
|
||||||
|
tab->at(k), first, iter, last))
|
||||||
{
|
{
|
||||||
throw syntax_error(format_underline(concat_to_string(
|
throw syntax_error(format_underline(concat_to_string(
|
||||||
"[error] toml::insert_value: table (\"",
|
"[error] toml::insert_value: table (\"",
|
||||||
format_dotted_keys(first, last), "\") already exists."),
|
format_dotted_keys(first, last),
|
||||||
|
"\") already exists."),
|
||||||
get_region(tab->at(k)), "table already exists here",
|
get_region(tab->at(k)), "table already exists here",
|
||||||
get_region(v), "table defined twice"));
|
get_region(v), "table defined twice"));
|
||||||
}
|
}
|
||||||
|
// to allow the following toml file.
|
||||||
|
// [a.b.c]
|
||||||
|
// d = 42
|
||||||
|
// [a]
|
||||||
|
// e = 2.71
|
||||||
|
auto& t = tab->at(k).cast<value_t::Table>();
|
||||||
|
for(const auto& kv : v.cast<value_t::Table>())
|
||||||
|
{
|
||||||
|
t[kv.first] = kv.second;
|
||||||
|
}
|
||||||
|
detail::change_region(tab->at(k), key_reg);
|
||||||
|
return ok(true);
|
||||||
|
}
|
||||||
else if(v.is(value_t::Table) &&
|
else if(v.is(value_t::Table) &&
|
||||||
tab->at(k).is(value_t::Array) &&
|
tab->at(k).is(value_t::Array) &&
|
||||||
tab->at(k).cast<value_t::Array>().size() > 0 &&
|
tab->at(k).cast<value_t::Array>().size() > 0 &&
|
||||||
@@ -1118,7 +1228,7 @@ insert_nested_key(table& root, const toml::value& v,
|
|||||||
"[error] toml::insert_value: value (\"",
|
"[error] toml::insert_value: value (\"",
|
||||||
format_dotted_keys(first, last), "\") already exists."),
|
format_dotted_keys(first, last), "\") already exists."),
|
||||||
get_region(tab->at(k)), "value already exists here",
|
get_region(tab->at(k)), "value already exists here",
|
||||||
get_region(v), "value inserted twice"));
|
get_region(v), "value defined twice"));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
tab->insert(std::make_pair(k, v));
|
tab->insert(std::make_pair(k, v));
|
||||||
@@ -1134,10 +1244,7 @@ insert_nested_key(table& root, const toml::value& v,
|
|||||||
// [x.y.z]
|
// [x.y.z]
|
||||||
if(tab->count(k) == 0)
|
if(tab->count(k) == 0)
|
||||||
{
|
{
|
||||||
// the region of [x.y] is the same as [x.y.z].
|
(*tab)[k] = toml::value(toml::table{}, key_reg);
|
||||||
(*tab)[k] = v; // copy region_info_
|
|
||||||
detail::assign_keeping_region((*tab)[k],
|
|
||||||
::toml::value(::toml::table{}));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// type checking...
|
// type checking...
|
||||||
@@ -1181,10 +1288,11 @@ parse_inline_table(location<Container>& loc)
|
|||||||
table retval;
|
table retval;
|
||||||
if(!(loc.iter() != loc.end() && *loc.iter() == '{'))
|
if(!(loc.iter() != loc.end() && *loc.iter() == '{'))
|
||||||
{
|
{
|
||||||
return err(format_underline("[error] toml::parse_inline_table: "
|
return err(format_underline("[error] toml::parse_inline_table: ", loc,
|
||||||
"the next token is not an inline table", loc, "not `{`."));
|
"the next token is not an inline table"));
|
||||||
}
|
}
|
||||||
++loc.iter();
|
++loc.iter();
|
||||||
|
// it starts from "{". it should be formatted as inline-table
|
||||||
while(loc.iter() != loc.end())
|
while(loc.iter() != loc.end())
|
||||||
{
|
{
|
||||||
maybe<lex_ws>::invoke(loc);
|
maybe<lex_ws>::invoke(loc);
|
||||||
@@ -1200,11 +1308,12 @@ parse_inline_table(location<Container>& loc)
|
|||||||
{
|
{
|
||||||
return err(kv_r.unwrap_err());
|
return err(kv_r.unwrap_err());
|
||||||
}
|
}
|
||||||
const std::vector<key>& keys = kv_r.unwrap().first;
|
const std::vector<key>& keys = kv_r.unwrap().first.first;
|
||||||
|
const region<Container>& key_reg = kv_r.unwrap().first.second;
|
||||||
const value& val = kv_r.unwrap().second;
|
const value& val = kv_r.unwrap().second;
|
||||||
|
|
||||||
const auto inserted =
|
const auto inserted =
|
||||||
insert_nested_key(retval, val, keys.begin(), keys.end());
|
insert_nested_key(retval, val, keys.begin(), keys.end(), key_reg);
|
||||||
if(!inserted)
|
if(!inserted)
|
||||||
{
|
{
|
||||||
throw internal_error("[error] toml::parse_inline_table: "
|
throw internal_error("[error] toml::parse_inline_table: "
|
||||||
@@ -1224,13 +1333,14 @@ parse_inline_table(location<Container>& loc)
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
return err(format_underline("[error] toml:::parse_inline_table:"
|
throw syntax_error(format_underline("[error] "
|
||||||
" missing table separator `,` ", loc, "should be `,`"));
|
"toml:::parse_inline_table: missing table separator `,` ",
|
||||||
|
loc, "should be `,`"));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
loc.iter() = first;
|
loc.iter() = first;
|
||||||
return err(format_underline("[error] toml::parse_inline_table: "
|
throw syntax_error(format_underline("[error] toml::parse_inline_table: "
|
||||||
"inline table did not closed by `}`", loc, "should be closed"));
|
"inline table did not closed by `}`", loc, "should be closed"));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1240,7 +1350,7 @@ result<value, std::string> parse_value(location<Container>& loc)
|
|||||||
const auto first = loc.iter();
|
const auto first = loc.iter();
|
||||||
if(first == loc.end())
|
if(first == loc.end())
|
||||||
{
|
{
|
||||||
return err(std::string("[error] toml::parse_value: input is empty"));
|
return err(format_underline("[error] toml::parse_value: input is empty", loc, ""));
|
||||||
}
|
}
|
||||||
if(auto r = parse_string (loc))
|
if(auto r = parse_string (loc))
|
||||||
{return ok(value(std::move(r.unwrap().first), std::move(r.unwrap().second)));}
|
{return ok(value(std::move(r.unwrap().first), std::move(r.unwrap().second)));}
|
||||||
@@ -1301,7 +1411,21 @@ parse_table_key(location<Container>& loc)
|
|||||||
throw internal_error(format_underline("[error] "
|
throw internal_error(format_underline("[error] "
|
||||||
"toml::parse_table_key: no `]`", inner_loc, "should be `]`"));
|
"toml::parse_table_key: no `]`", inner_loc, "should be `]`"));
|
||||||
}
|
}
|
||||||
return ok(std::make_pair(keys.unwrap(), token.unwrap()));
|
|
||||||
|
// after [table.key], newline or EOF(empty table) requried.
|
||||||
|
if(loc.iter() != loc.end())
|
||||||
|
{
|
||||||
|
using lex_newline_after_table_key =
|
||||||
|
sequence<maybe<lex_ws>, maybe<lex_comment>, lex_newline>;
|
||||||
|
const auto nl = lex_newline_after_table_key::invoke(loc);
|
||||||
|
if(!nl)
|
||||||
|
{
|
||||||
|
throw syntax_error(format_underline("[error] "
|
||||||
|
"toml::parse_table_key: newline required after [table.key]",
|
||||||
|
loc, "expected newline"));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return ok(std::make_pair(keys.unwrap().first, token.unwrap()));
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@@ -1339,7 +1463,21 @@ parse_array_table_key(location<Container>& loc)
|
|||||||
throw internal_error(format_underline("[error] "
|
throw internal_error(format_underline("[error] "
|
||||||
"toml::parse_table_key: no `]]`", inner_loc, "should be `]]`"));
|
"toml::parse_table_key: no `]]`", inner_loc, "should be `]]`"));
|
||||||
}
|
}
|
||||||
return ok(std::make_pair(keys.unwrap(), token.unwrap()));
|
|
||||||
|
// after [[table.key]], newline or EOF(empty table) requried.
|
||||||
|
if(loc.iter() != loc.end())
|
||||||
|
{
|
||||||
|
using lex_newline_after_table_key =
|
||||||
|
sequence<maybe<lex_ws>, maybe<lex_comment>, lex_newline>;
|
||||||
|
const auto nl = lex_newline_after_table_key::invoke(loc);
|
||||||
|
if(!nl)
|
||||||
|
{
|
||||||
|
throw syntax_error(format_underline("[error] "
|
||||||
|
"toml::parse_array_table_key: newline required after "
|
||||||
|
"[[table.key]]", loc, "expected newline"));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return ok(std::make_pair(keys.unwrap().first, token.unwrap()));
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@@ -1354,10 +1492,10 @@ result<table, std::string> parse_ml_table(location<Container>& loc)
|
|||||||
const auto first = loc.iter();
|
const auto first = loc.iter();
|
||||||
if(first == loc.end())
|
if(first == loc.end())
|
||||||
{
|
{
|
||||||
return err(std::string("toml::parse_ml_table: input is empty"));
|
return ok(toml::table{});
|
||||||
}
|
}
|
||||||
|
|
||||||
// XXX at lest one newline is needed
|
// XXX at lest one newline is needed.
|
||||||
using skip_line = repeat<
|
using skip_line = repeat<
|
||||||
sequence<maybe<lex_ws>, maybe<lex_comment>, lex_newline>, at_least<1>>;
|
sequence<maybe<lex_ws>, maybe<lex_comment>, lex_newline>, at_least<1>>;
|
||||||
skip_line::invoke(loc);
|
skip_line::invoke(loc);
|
||||||
@@ -1377,12 +1515,14 @@ result<table, std::string> parse_ml_table(location<Container>& loc)
|
|||||||
loc.iter() = before;
|
loc.iter() = before;
|
||||||
return ok(tab);
|
return ok(tab);
|
||||||
}
|
}
|
||||||
|
|
||||||
if(const auto kv = parse_key_value_pair(loc))
|
if(const auto kv = parse_key_value_pair(loc))
|
||||||
{
|
{
|
||||||
const std::vector<key>& keys = kv.unwrap().first;
|
const std::vector<key>& keys = kv.unwrap().first.first;
|
||||||
|
const region<Container>& key_reg = kv.unwrap().first.second;
|
||||||
const value& val = kv.unwrap().second;
|
const value& val = kv.unwrap().second;
|
||||||
const auto inserted =
|
const auto inserted =
|
||||||
insert_nested_key(tab, val, keys.begin(), keys.end());
|
insert_nested_key(tab, val, keys.begin(), keys.end(), key_reg);
|
||||||
if(!inserted)
|
if(!inserted)
|
||||||
{
|
{
|
||||||
return err(inserted.unwrap_err());
|
return err(inserted.unwrap_err());
|
||||||
@@ -1393,6 +1533,17 @@ result<table, std::string> parse_ml_table(location<Container>& loc)
|
|||||||
return err(kv.unwrap_err());
|
return err(kv.unwrap_err());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// comment lines are skipped by the above function call.
|
||||||
|
// However, since the `skip_line` requires at least 1 newline, it fails
|
||||||
|
// if the file ends with ws and/or comment without newline.
|
||||||
|
// `skip_line` matches `ws? + comment? + newline`, not `ws` or `comment`
|
||||||
|
// itself. To skip the last ws and/or comment, call lexers.
|
||||||
|
// It does not matter if these fails, so the return value is discarded.
|
||||||
|
lex_ws::invoke(loc);
|
||||||
|
lex_comment::invoke(loc);
|
||||||
|
|
||||||
|
// skip_line is (whitespace? comment? newline)_{1,}. multiple empty lines
|
||||||
|
// and comments after the last key-value pairs are allowed.
|
||||||
const auto newline = skip_line::invoke(loc);
|
const auto newline = skip_line::invoke(loc);
|
||||||
if(!newline && loc.iter() != loc.end())
|
if(!newline && loc.iter() != loc.end())
|
||||||
{
|
{
|
||||||
@@ -1405,11 +1556,10 @@ result<table, std::string> parse_ml_table(location<Container>& loc)
|
|||||||
return err(msg);
|
return err(msg);
|
||||||
}
|
}
|
||||||
|
|
||||||
// comment lines are skipped by the above function call.
|
// the skip_lines only matches with lines that includes newline.
|
||||||
// However, if the file ends with comment without newline,
|
// to skip the last line that includes comment and/or whitespace
|
||||||
// it might cause parsing error because skip_line matches
|
// but no newline, call them one more time.
|
||||||
// `comment + newline`, not `comment` itself. to skip the
|
lex_ws::invoke(loc);
|
||||||
// last comment, call lex_comment one more time.
|
|
||||||
lex_comment::invoke(loc);
|
lex_comment::invoke(loc);
|
||||||
}
|
}
|
||||||
return ok(tab);
|
return ok(tab);
|
||||||
@@ -1421,11 +1571,11 @@ result<table, std::string> parse_toml_file(location<Container>& loc)
|
|||||||
const auto first = loc.iter();
|
const auto first = loc.iter();
|
||||||
if(first == loc.end())
|
if(first == loc.end())
|
||||||
{
|
{
|
||||||
return err(std::string("toml::detail::parse_toml_file: input is empty"));
|
return ok(toml::table{});
|
||||||
}
|
}
|
||||||
|
|
||||||
table data;
|
table data;
|
||||||
/* root object is also table, but without [tablename] */
|
// root object is also a table, but without [tablename]
|
||||||
if(auto tab = parse_ml_table(loc))
|
if(auto tab = parse_ml_table(loc))
|
||||||
{
|
{
|
||||||
data = std::move(tab.unwrap());
|
data = std::move(tab.unwrap());
|
||||||
@@ -1450,7 +1600,8 @@ result<table, std::string> parse_toml_file(location<Container>& loc)
|
|||||||
|
|
||||||
const auto inserted = insert_nested_key(data,
|
const auto inserted = insert_nested_key(data,
|
||||||
toml::value(tab.unwrap(), reg),
|
toml::value(tab.unwrap(), reg),
|
||||||
keys.begin(), keys.end(), /*is_array_of_table=*/ true);
|
keys.begin(), keys.end(), reg,
|
||||||
|
/*is_array_of_table=*/ true);
|
||||||
if(!inserted) {return err(inserted.unwrap_err());}
|
if(!inserted) {return err(inserted.unwrap_err());}
|
||||||
|
|
||||||
continue;
|
continue;
|
||||||
@@ -1464,7 +1615,7 @@ result<table, std::string> parse_toml_file(location<Container>& loc)
|
|||||||
const auto& reg = tabkey.unwrap().second;
|
const auto& reg = tabkey.unwrap().second;
|
||||||
|
|
||||||
const auto inserted = insert_nested_key(data,
|
const auto inserted = insert_nested_key(data,
|
||||||
toml::value(tab.unwrap(), reg), keys.begin(), keys.end());
|
toml::value(tab.unwrap(), reg), keys.begin(), keys.end(), reg);
|
||||||
if(!inserted) {return err(inserted.unwrap_err());}
|
if(!inserted) {return err(inserted.unwrap_err());}
|
||||||
|
|
||||||
continue;
|
continue;
|
||||||
@@ -1498,10 +1649,10 @@ inline table parse(std::istream& is, std::string fname = "unknown file")
|
|||||||
// be compared to char. However, since we are always out of luck, we need to
|
// be compared to char. However, since we are always out of luck, we need to
|
||||||
// check our chars are equivalent to BOM. To do this, first we need to
|
// check our chars are equivalent to BOM. To do this, first we need to
|
||||||
// convert char to unsigned char to guarantee the comparability.
|
// convert char to unsigned char to guarantee the comparability.
|
||||||
if(letters.size() >= 3)
|
if(loc.source()->size() >= 3)
|
||||||
{
|
{
|
||||||
std::array<unsigned char, 3> BOM;
|
std::array<unsigned char, 3> BOM;
|
||||||
std::memcpy(BOM.data(), letters.data(), 3);
|
std::memcpy(BOM.data(), loc.source()->data(), 3);
|
||||||
if(BOM[0] == 0xEF && BOM[1] == 0xBB && BOM[2] == 0xBF)
|
if(BOM[0] == 0xEF && BOM[1] == 0xBB && BOM[2] == 0xBF)
|
||||||
{
|
{
|
||||||
loc.iter() += 3; // BOM found. skip.
|
loc.iter() += 3; // BOM found. skip.
|
||||||
@@ -1518,7 +1669,7 @@ inline table parse(std::istream& is, std::string fname = "unknown file")
|
|||||||
|
|
||||||
inline table parse(const std::string& fname)
|
inline table parse(const std::string& fname)
|
||||||
{
|
{
|
||||||
std::ifstream ifs(fname.c_str());
|
std::ifstream ifs(fname.c_str(), std::ios_base::binary);
|
||||||
if(!ifs.good())
|
if(!ifs.good())
|
||||||
{
|
{
|
||||||
throw std::runtime_error("toml::parse: file open error -> " + fname);
|
throw std::runtime_error("toml::parse: file open error -> " + fname);
|
||||||
|
|||||||
251
toml/region.hpp
251
toml/region.hpp
@@ -28,44 +28,6 @@ inline std::string make_string(std::size_t len, char c)
|
|||||||
return std::string(len, c);
|
return std::string(len, c);
|
||||||
}
|
}
|
||||||
|
|
||||||
// location in a container, normally in a file content.
|
|
||||||
// shared_ptr points the resource that the iter points.
|
|
||||||
// it can be used not only for resource handling, but also error message.
|
|
||||||
template<typename Container>
|
|
||||||
struct location
|
|
||||||
{
|
|
||||||
static_assert(std::is_same<char, typename Container::value_type>::value,"");
|
|
||||||
using const_iterator = typename Container::const_iterator;
|
|
||||||
using source_ptr = std::shared_ptr<const Container>;
|
|
||||||
|
|
||||||
location(std::string name, Container cont)
|
|
||||||
: source_(std::make_shared<Container>(std::move(cont))),
|
|
||||||
source_name_(std::move(name)), iter_(source_->cbegin())
|
|
||||||
{}
|
|
||||||
location(const location&) = default;
|
|
||||||
location(location&&) = default;
|
|
||||||
location& operator=(const location&) = default;
|
|
||||||
location& operator=(location&&) = default;
|
|
||||||
~location() = default;
|
|
||||||
|
|
||||||
const_iterator& iter() noexcept {return iter_;}
|
|
||||||
const_iterator iter() const noexcept {return iter_;}
|
|
||||||
|
|
||||||
const_iterator begin() const noexcept {return source_->cbegin();}
|
|
||||||
const_iterator end() const noexcept {return source_->cend();}
|
|
||||||
|
|
||||||
source_ptr const& source() const& noexcept {return source_;}
|
|
||||||
source_ptr&& source() && noexcept {return std::move(source_);}
|
|
||||||
|
|
||||||
std::string const& name() const noexcept {return source_name_;}
|
|
||||||
|
|
||||||
private:
|
|
||||||
|
|
||||||
source_ptr source_;
|
|
||||||
std::string source_name_;
|
|
||||||
const_iterator iter_;
|
|
||||||
};
|
|
||||||
|
|
||||||
// region in a container, normally in a file content.
|
// region in a container, normally in a file content.
|
||||||
// shared_ptr points the resource that the iter points.
|
// shared_ptr points the resource that the iter points.
|
||||||
// combinators returns this.
|
// combinators returns this.
|
||||||
@@ -86,12 +48,89 @@ struct region_base
|
|||||||
virtual std::string line() const {return std::string("unknown line");}
|
virtual std::string line() const {return std::string("unknown line");}
|
||||||
virtual std::string line_num() const {return std::string("?");}
|
virtual std::string line_num() const {return std::string("?");}
|
||||||
|
|
||||||
|
|
||||||
virtual std::size_t before() const noexcept {return 0;}
|
virtual std::size_t before() const noexcept {return 0;}
|
||||||
virtual std::size_t size() const noexcept {return 0;}
|
virtual std::size_t size() const noexcept {return 0;}
|
||||||
virtual std::size_t after() const noexcept {return 0;}
|
virtual std::size_t after() const noexcept {return 0;}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// location in a container, normally in a file content.
|
||||||
|
// shared_ptr points the resource that the iter points.
|
||||||
|
// it can be used not only for resource handling, but also error message.
|
||||||
|
//
|
||||||
|
// it can be considered as a region that contains only one character.
|
||||||
|
template<typename Container>
|
||||||
|
struct location final : public region_base
|
||||||
|
{
|
||||||
|
static_assert(std::is_same<char, typename Container::value_type>::value,"");
|
||||||
|
using const_iterator = typename Container::const_iterator;
|
||||||
|
using source_ptr = std::shared_ptr<const Container>;
|
||||||
|
|
||||||
|
location(std::string name, Container cont)
|
||||||
|
: source_(std::make_shared<Container>(std::move(cont))),
|
||||||
|
source_name_(std::move(name)), iter_(source_->cbegin())
|
||||||
|
{}
|
||||||
|
location(const location&) = default;
|
||||||
|
location(location&&) = default;
|
||||||
|
location& operator=(const location&) = default;
|
||||||
|
location& operator=(location&&) = default;
|
||||||
|
~location() = default;
|
||||||
|
|
||||||
|
bool is_ok() const noexcept override {return static_cast<bool>(source_);}
|
||||||
|
|
||||||
|
const_iterator& iter() noexcept {return iter_;}
|
||||||
|
const_iterator iter() const noexcept {return iter_;}
|
||||||
|
|
||||||
|
const_iterator begin() const noexcept {return source_->cbegin();}
|
||||||
|
const_iterator end() const noexcept {return source_->cend();}
|
||||||
|
|
||||||
|
std::string str() const override {return make_string(1, *this->iter());}
|
||||||
|
std::string name() const override {return source_name_;}
|
||||||
|
|
||||||
|
std::string line_num() const override
|
||||||
|
{
|
||||||
|
return std::to_string(1+std::count(this->begin(), this->iter(), '\n'));
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string line() const override
|
||||||
|
{
|
||||||
|
return make_string(this->line_begin(), this->line_end());
|
||||||
|
}
|
||||||
|
|
||||||
|
const_iterator line_begin() const noexcept
|
||||||
|
{
|
||||||
|
using reverse_iterator = std::reverse_iterator<const_iterator>;
|
||||||
|
return std::find(reverse_iterator(this->iter()),
|
||||||
|
reverse_iterator(this->begin()), '\n').base();
|
||||||
|
}
|
||||||
|
const_iterator line_end() const noexcept
|
||||||
|
{
|
||||||
|
return std::find(this->iter(), this->end(), '\n');
|
||||||
|
}
|
||||||
|
|
||||||
|
// location is always points a character. so the size is 1.
|
||||||
|
std::size_t size() const noexcept override
|
||||||
|
{
|
||||||
|
return 1u;
|
||||||
|
}
|
||||||
|
std::size_t before() const noexcept override
|
||||||
|
{
|
||||||
|
return std::distance(this->line_begin(), this->iter());
|
||||||
|
}
|
||||||
|
std::size_t after() const noexcept override
|
||||||
|
{
|
||||||
|
return std::distance(this->iter(), this->line_end());
|
||||||
|
}
|
||||||
|
|
||||||
|
source_ptr const& source() const& noexcept {return source_;}
|
||||||
|
source_ptr&& source() && noexcept {return std::move(source_);}
|
||||||
|
|
||||||
|
private:
|
||||||
|
|
||||||
|
source_ptr source_;
|
||||||
|
std::string source_name_;
|
||||||
|
const_iterator iter_;
|
||||||
|
};
|
||||||
|
|
||||||
template<typename Container>
|
template<typename Container>
|
||||||
struct region final : public region_base
|
struct region final : public region_base
|
||||||
{
|
{
|
||||||
@@ -225,7 +264,19 @@ inline std::string format_underline(const std::string& message,
|
|||||||
retval += make_string(line_number.size() + 1, ' ');
|
retval += make_string(line_number.size() + 1, ' ');
|
||||||
retval += " | ";
|
retval += " | ";
|
||||||
retval += make_string(reg.before(), ' ');
|
retval += make_string(reg.before(), ' ');
|
||||||
|
if(reg.size() == 1)
|
||||||
|
{
|
||||||
|
// invalid
|
||||||
|
// ^------
|
||||||
|
retval += '^';
|
||||||
|
retval += make_string(reg.after(), '-');
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// invalid
|
||||||
|
// ~~~~~~~
|
||||||
retval += make_string(reg.size(), '~');
|
retval += make_string(reg.size(), '~');
|
||||||
|
}
|
||||||
retval += ' ';
|
retval += ' ';
|
||||||
retval += comment_for_underline;
|
retval += comment_for_underline;
|
||||||
if(helps.size() != 0)
|
if(helps.size() != 0)
|
||||||
@@ -270,7 +321,19 @@ inline std::string format_underline(const std::string& message,
|
|||||||
retval << make_string(line_num_width + 1, ' ');
|
retval << make_string(line_num_width + 1, ' ');
|
||||||
retval << " | ";
|
retval << " | ";
|
||||||
retval << make_string(reg1.before(), ' ');
|
retval << make_string(reg1.before(), ' ');
|
||||||
|
if(reg1.size() == 1)
|
||||||
|
{
|
||||||
|
// invalid
|
||||||
|
// ^------
|
||||||
|
retval << '^';
|
||||||
|
retval << make_string(reg1.after(), '-');
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// invalid
|
||||||
|
// ~~~~~~~
|
||||||
retval << make_string(reg1.size(), '~');
|
retval << make_string(reg1.size(), '~');
|
||||||
|
}
|
||||||
retval << ' ';
|
retval << ' ';
|
||||||
retval << comment_for_underline1 << newline;
|
retval << comment_for_underline1 << newline;
|
||||||
// ---------------------------------------
|
// ---------------------------------------
|
||||||
@@ -287,7 +350,19 @@ inline std::string format_underline(const std::string& message,
|
|||||||
retval << make_string(line_num_width + 1, ' ');
|
retval << make_string(line_num_width + 1, ' ');
|
||||||
retval << " | ";
|
retval << " | ";
|
||||||
retval << make_string(reg2.before(), ' ');
|
retval << make_string(reg2.before(), ' ');
|
||||||
|
if(reg2.size() == 1)
|
||||||
|
{
|
||||||
|
// invalid
|
||||||
|
// ^------
|
||||||
|
retval << '^';
|
||||||
|
retval << make_string(reg2.after(), '-');
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// invalid
|
||||||
|
// ~~~~~~~
|
||||||
retval << make_string(reg2.size(), '~');
|
retval << make_string(reg2.size(), '~');
|
||||||
|
}
|
||||||
retval << ' ';
|
retval << ' ';
|
||||||
retval << comment_for_underline2;
|
retval << comment_for_underline2;
|
||||||
if(helps.size() != 0)
|
if(helps.size() != 0)
|
||||||
@@ -305,62 +380,84 @@ inline std::string format_underline(const std::string& message,
|
|||||||
return retval.str();
|
return retval.str();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// to show a better error message.
|
// to show a better error message.
|
||||||
template<typename Container>
|
inline std::string format_underline(const std::string& message,
|
||||||
std::string
|
std::vector<std::pair<region_base const*, std::string>> reg_com,
|
||||||
format_underline(const std::string& message, const location<Container>& loc,
|
|
||||||
const std::string& comment_for_underline,
|
|
||||||
std::vector<std::string> helps = {})
|
std::vector<std::string> helps = {})
|
||||||
{
|
{
|
||||||
|
assert(!reg_com.empty());
|
||||||
|
|
||||||
#ifdef _WIN32
|
#ifdef _WIN32
|
||||||
const auto newline = "\r\n";
|
const auto newline = "\r\n";
|
||||||
#else
|
#else
|
||||||
const char newline = '\n';
|
const char newline = '\n';
|
||||||
#endif
|
#endif
|
||||||
using const_iterator = typename location<Container>::const_iterator;
|
|
||||||
using reverse_iterator = std::reverse_iterator<const_iterator>;
|
|
||||||
const auto line_begin = std::find(reverse_iterator(loc.iter()),
|
|
||||||
reverse_iterator(loc.begin()),
|
|
||||||
'\n').base();
|
|
||||||
const auto line_end = std::find(loc.iter(), loc.end(), '\n');
|
|
||||||
|
|
||||||
const auto line_number = std::to_string(
|
const auto line_num_width = std::max_element(reg_com.begin(), reg_com.end(),
|
||||||
1 + std::count(loc.begin(), loc.iter(), '\n'));
|
[](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;
|
||||||
|
retval << message << newline;
|
||||||
|
|
||||||
|
for(std::size_t i=0; i<reg_com.size(); ++i)
|
||||||
|
{
|
||||||
|
if(i!=0 && reg_com.at(i-1).first->name() == reg_com.at(i).first->name())
|
||||||
|
{
|
||||||
|
retval << " ..." << newline;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
retval << " --> " << reg_com.at(i).first->name() << newline;
|
||||||
|
}
|
||||||
|
|
||||||
|
const region_base* const reg = reg_com.at(i).first;
|
||||||
|
const std::string& comment = reg_com.at(i).second;
|
||||||
|
|
||||||
|
|
||||||
|
retval << ' ' << std::setw(line_num_width) << reg->line_num();
|
||||||
|
retval << " | " << reg->line() << newline;
|
||||||
|
retval << make_string(line_num_width + 1, ' ');
|
||||||
|
retval << " | " << make_string(reg->before(), ' ');
|
||||||
|
|
||||||
|
if(reg->size() == 1)
|
||||||
|
{
|
||||||
|
// invalid
|
||||||
|
// ^------
|
||||||
|
retval << '^';
|
||||||
|
retval << make_string(reg->after(), '-');
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// invalid
|
||||||
|
// ~~~~~~~
|
||||||
|
retval << make_string(reg->size(), '~');
|
||||||
|
}
|
||||||
|
|
||||||
|
retval << ' ';
|
||||||
|
retval << comment << newline;
|
||||||
|
}
|
||||||
|
|
||||||
std::string retval;
|
|
||||||
retval += message;
|
|
||||||
retval += newline;
|
|
||||||
retval += " --> ";
|
|
||||||
retval += loc.name();
|
|
||||||
retval += newline;
|
|
||||||
retval += ' ';
|
|
||||||
retval += line_number;
|
|
||||||
retval += " | ";
|
|
||||||
retval += make_string(line_begin, line_end);
|
|
||||||
retval += newline;
|
|
||||||
retval += make_string(line_number.size() + 1, ' ');
|
|
||||||
retval += " | ";
|
|
||||||
retval += make_string(std::distance(line_begin, loc.iter()),' ');
|
|
||||||
retval += '^';
|
|
||||||
retval += make_string(std::distance(loc.iter(), line_end), '-');
|
|
||||||
retval += ' ';
|
|
||||||
retval += comment_for_underline;
|
|
||||||
if(helps.size() != 0)
|
if(helps.size() != 0)
|
||||||
{
|
{
|
||||||
retval += newline;
|
retval << newline;
|
||||||
retval += make_string(line_number.size() + 1, ' ');
|
retval << make_string(line_num_width + 1, ' ');
|
||||||
retval += " | ";
|
retval << " | ";
|
||||||
for(const auto help : helps)
|
for(const auto help : helps)
|
||||||
{
|
{
|
||||||
retval += newline;
|
retval << newline;
|
||||||
retval += "Hint: ";
|
retval << "Hint: ";
|
||||||
retval += help;
|
retval << help;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return retval;
|
return retval.str();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
} // detail
|
} // detail
|
||||||
} // toml
|
} // toml
|
||||||
#endif// TOML11_REGION_H
|
#endif// TOML11_REGION_H
|
||||||
|
|||||||
@@ -2,6 +2,7 @@
|
|||||||
// Distributed under the MIT License.
|
// Distributed under the MIT License.
|
||||||
#ifndef TOML11_RESULT_H
|
#ifndef TOML11_RESULT_H
|
||||||
#define TOML11_RESULT_H
|
#define TOML11_RESULT_H
|
||||||
|
#include "traits.hpp"
|
||||||
#include <type_traits>
|
#include <type_traits>
|
||||||
#include <stdexcept>
|
#include <stdexcept>
|
||||||
#include <utility>
|
#include <utility>
|
||||||
@@ -13,19 +14,6 @@
|
|||||||
namespace toml
|
namespace toml
|
||||||
{
|
{
|
||||||
|
|
||||||
#if __cplusplus >= 201703L
|
|
||||||
|
|
||||||
template<typename F, typename ... Args>
|
|
||||||
using return_type_of_t = std::invoke_result_t<F, Args...>;
|
|
||||||
|
|
||||||
#else
|
|
||||||
// result_of is deprecated after C++17
|
|
||||||
template<typename F, typename ... Args>
|
|
||||||
using return_type_of_t = typename std::result_of<F(Args...)>::type;
|
|
||||||
|
|
||||||
#endif
|
|
||||||
|
|
||||||
|
|
||||||
template<typename T>
|
template<typename T>
|
||||||
struct success
|
struct success
|
||||||
{
|
{
|
||||||
@@ -441,21 +429,21 @@ struct result
|
|||||||
// F: T -> U
|
// F: T -> U
|
||||||
// retval: result<U, E>
|
// retval: result<U, E>
|
||||||
template<typename F>
|
template<typename F>
|
||||||
result<return_type_of_t<F, value_type&>, error_type>
|
result<detail::return_type_of_t<F, value_type&>, error_type>
|
||||||
map(F&& f) &
|
map(F&& f) &
|
||||||
{
|
{
|
||||||
if(this->is_ok()){return ok(f(this->as_ok()));}
|
if(this->is_ok()){return ok(f(this->as_ok()));}
|
||||||
return err(this->as_err());
|
return err(this->as_err());
|
||||||
}
|
}
|
||||||
template<typename F>
|
template<typename F>
|
||||||
result<return_type_of_t<F, value_type const&>, error_type>
|
result<detail::return_type_of_t<F, value_type const&>, error_type>
|
||||||
map(F&& f) const&
|
map(F&& f) const&
|
||||||
{
|
{
|
||||||
if(this->is_ok()){return ok(f(this->as_ok()));}
|
if(this->is_ok()){return ok(f(this->as_ok()));}
|
||||||
return err(this->as_err());
|
return err(this->as_err());
|
||||||
}
|
}
|
||||||
template<typename F>
|
template<typename F>
|
||||||
result<return_type_of_t<F, value_type &&>, error_type>
|
result<detail::return_type_of_t<F, value_type &&>, error_type>
|
||||||
map(F&& f) &&
|
map(F&& f) &&
|
||||||
{
|
{
|
||||||
if(this->is_ok()){return ok(f(std::move(this->as_ok())));}
|
if(this->is_ok()){return ok(f(std::move(this->as_ok())));}
|
||||||
@@ -466,21 +454,21 @@ struct result
|
|||||||
// F: E -> F
|
// F: E -> F
|
||||||
// retval: result<T, F>
|
// retval: result<T, F>
|
||||||
template<typename F>
|
template<typename F>
|
||||||
result<value_type, return_type_of_t<F, error_type&>>
|
result<value_type, detail::return_type_of_t<F, error_type&>>
|
||||||
map_err(F&& f) &
|
map_err(F&& f) &
|
||||||
{
|
{
|
||||||
if(this->is_err()){return err(f(this->as_err()));}
|
if(this->is_err()){return err(f(this->as_err()));}
|
||||||
return ok(this->as_ok());
|
return ok(this->as_ok());
|
||||||
}
|
}
|
||||||
template<typename F>
|
template<typename F>
|
||||||
result<value_type, return_type_of_t<F, error_type const&>>
|
result<value_type, detail::return_type_of_t<F, error_type const&>>
|
||||||
map_err(F&& f) const&
|
map_err(F&& f) const&
|
||||||
{
|
{
|
||||||
if(this->is_err()){return err(f(this->as_err()));}
|
if(this->is_err()){return err(f(this->as_err()));}
|
||||||
return ok(this->as_ok());
|
return ok(this->as_ok());
|
||||||
}
|
}
|
||||||
template<typename F>
|
template<typename F>
|
||||||
result<value_type, return_type_of_t<F, error_type&&>>
|
result<value_type, detail::return_type_of_t<F, error_type&&>>
|
||||||
map_err(F&& f) &&
|
map_err(F&& f) &&
|
||||||
{
|
{
|
||||||
if(this->is_err()){return err(f(std::move(this->as_err())));}
|
if(this->is_err()){return err(f(std::move(this->as_err())));}
|
||||||
@@ -491,21 +479,21 @@ struct result
|
|||||||
// F: T -> U
|
// F: T -> U
|
||||||
// retval: U
|
// retval: U
|
||||||
template<typename F, typename U>
|
template<typename F, typename U>
|
||||||
return_type_of_t<F, value_type&>
|
detail::return_type_of_t<F, value_type&>
|
||||||
map_or_else(F&& f, U&& opt) &
|
map_or_else(F&& f, U&& opt) &
|
||||||
{
|
{
|
||||||
if(this->is_err()){return std::forward<U>(opt);}
|
if(this->is_err()){return std::forward<U>(opt);}
|
||||||
return f(this->as_ok());
|
return f(this->as_ok());
|
||||||
}
|
}
|
||||||
template<typename F, typename U>
|
template<typename F, typename U>
|
||||||
return_type_of_t<F, value_type const&>
|
detail::return_type_of_t<F, value_type const&>
|
||||||
map_or_else(F&& f, U&& opt) const&
|
map_or_else(F&& f, U&& opt) const&
|
||||||
{
|
{
|
||||||
if(this->is_err()){return std::forward<U>(opt);}
|
if(this->is_err()){return std::forward<U>(opt);}
|
||||||
return f(this->as_ok());
|
return f(this->as_ok());
|
||||||
}
|
}
|
||||||
template<typename F, typename U>
|
template<typename F, typename U>
|
||||||
return_type_of_t<F, value_type&&>
|
detail::return_type_of_t<F, value_type&&>
|
||||||
map_or_else(F&& f, U&& opt) &&
|
map_or_else(F&& f, U&& opt) &&
|
||||||
{
|
{
|
||||||
if(this->is_err()){return std::forward<U>(opt);}
|
if(this->is_err()){return std::forward<U>(opt);}
|
||||||
@@ -516,21 +504,21 @@ struct result
|
|||||||
// F: E -> U
|
// F: E -> U
|
||||||
// retval: U
|
// retval: U
|
||||||
template<typename F, typename U>
|
template<typename F, typename U>
|
||||||
return_type_of_t<F, error_type&>
|
detail::return_type_of_t<F, error_type&>
|
||||||
map_err_or_else(F&& f, U&& opt) &
|
map_err_or_else(F&& f, U&& opt) &
|
||||||
{
|
{
|
||||||
if(this->is_ok()){return std::forward<U>(opt);}
|
if(this->is_ok()){return std::forward<U>(opt);}
|
||||||
return f(this->as_err());
|
return f(this->as_err());
|
||||||
}
|
}
|
||||||
template<typename F, typename U>
|
template<typename F, typename U>
|
||||||
return_type_of_t<F, error_type const&>
|
detail::return_type_of_t<F, error_type const&>
|
||||||
map_err_or_else(F&& f, U&& opt) const&
|
map_err_or_else(F&& f, U&& opt) const&
|
||||||
{
|
{
|
||||||
if(this->is_ok()){return std::forward<U>(opt);}
|
if(this->is_ok()){return std::forward<U>(opt);}
|
||||||
return f(this->as_err());
|
return f(this->as_err());
|
||||||
}
|
}
|
||||||
template<typename F, typename U>
|
template<typename F, typename U>
|
||||||
return_type_of_t<F, error_type&&>
|
detail::return_type_of_t<F, error_type&&>
|
||||||
map_err_or_else(F&& f, U&& opt) &&
|
map_err_or_else(F&& f, U&& opt) &&
|
||||||
{
|
{
|
||||||
if(this->is_ok()){return std::forward<U>(opt);}
|
if(this->is_ok()){return std::forward<U>(opt);}
|
||||||
@@ -542,21 +530,21 @@ struct result
|
|||||||
// toml::err(error_type) should be convertible to U.
|
// toml::err(error_type) should be convertible to U.
|
||||||
// normally, type U is another result<S, F> and E is convertible to F
|
// normally, type U is another result<S, F> and E is convertible to F
|
||||||
template<typename F>
|
template<typename F>
|
||||||
return_type_of_t<F, value_type&>
|
detail::return_type_of_t<F, value_type&>
|
||||||
and_then(F&& f) &
|
and_then(F&& f) &
|
||||||
{
|
{
|
||||||
if(this->is_ok()){return f(this->as_ok());}
|
if(this->is_ok()){return f(this->as_ok());}
|
||||||
return err(this->as_err());
|
return err(this->as_err());
|
||||||
}
|
}
|
||||||
template<typename F>
|
template<typename F>
|
||||||
return_type_of_t<F, value_type const&>
|
detail::return_type_of_t<F, value_type const&>
|
||||||
and_then(F&& f) const&
|
and_then(F&& f) const&
|
||||||
{
|
{
|
||||||
if(this->is_ok()){return f(this->as_ok());}
|
if(this->is_ok()){return f(this->as_ok());}
|
||||||
return err(this->as_err());
|
return err(this->as_err());
|
||||||
}
|
}
|
||||||
template<typename F>
|
template<typename F>
|
||||||
return_type_of_t<F, value_type&&>
|
detail::return_type_of_t<F, value_type&&>
|
||||||
and_then(F&& f) &&
|
and_then(F&& f) &&
|
||||||
{
|
{
|
||||||
if(this->is_ok()){return f(std::move(this->as_ok()));}
|
if(this->is_ok()){return f(std::move(this->as_ok()));}
|
||||||
@@ -568,21 +556,21 @@ struct result
|
|||||||
// toml::ok(value_type) should be convertible to U.
|
// toml::ok(value_type) should be convertible to U.
|
||||||
// normally, type U is another result<S, F> and T is convertible to S
|
// normally, type U is another result<S, F> and T is convertible to S
|
||||||
template<typename F>
|
template<typename F>
|
||||||
return_type_of_t<F, error_type&>
|
detail::return_type_of_t<F, error_type&>
|
||||||
or_else(F&& f) &
|
or_else(F&& f) &
|
||||||
{
|
{
|
||||||
if(this->is_err()){return f(this->as_err());}
|
if(this->is_err()){return f(this->as_err());}
|
||||||
return ok(this->as_ok());
|
return ok(this->as_ok());
|
||||||
}
|
}
|
||||||
template<typename F>
|
template<typename F>
|
||||||
return_type_of_t<F, error_type const&>
|
detail::return_type_of_t<F, error_type const&>
|
||||||
or_else(F&& f) const&
|
or_else(F&& f) const&
|
||||||
{
|
{
|
||||||
if(this->is_err()){return f(this->as_err());}
|
if(this->is_err()){return f(this->as_err());}
|
||||||
return ok(this->as_ok());
|
return ok(this->as_ok());
|
||||||
}
|
}
|
||||||
template<typename F>
|
template<typename F>
|
||||||
return_type_of_t<F, error_type&&>
|
detail::return_type_of_t<F, error_type&&>
|
||||||
or_else(F&& f) &&
|
or_else(F&& f) &&
|
||||||
{
|
{
|
||||||
if(this->is_err()){return f(std::move(this->as_err()));}
|
if(this->is_err()){return f(std::move(this->as_err()));}
|
||||||
|
|||||||
511
toml/serializer.hpp
Normal file
511
toml/serializer.hpp
Normal file
@@ -0,0 +1,511 @@
|
|||||||
|
// Copyright Toru Niina 2019.
|
||||||
|
// Distributed under the MIT License.
|
||||||
|
#ifndef TOML11_SERIALIZER_HPP
|
||||||
|
#define TOML11_SERIALIZER_HPP
|
||||||
|
#include "value.hpp"
|
||||||
|
#include "lexer.hpp"
|
||||||
|
#include <limits>
|
||||||
|
|
||||||
|
namespace toml
|
||||||
|
{
|
||||||
|
|
||||||
|
struct serializer
|
||||||
|
{
|
||||||
|
serializer(const std::size_t w = 80,
|
||||||
|
const int float_prec = std::numeric_limits<toml::floating>::max_digits10,
|
||||||
|
const bool can_be_inlined = false,
|
||||||
|
std::vector<toml::key> ks = {})
|
||||||
|
: can_be_inlined_(can_be_inlined), float_prec_(float_prec), width_(w),
|
||||||
|
keys_(std::move(ks))
|
||||||
|
{}
|
||||||
|
~serializer() = default;
|
||||||
|
|
||||||
|
std::string operator()(const toml::boolean& b) const
|
||||||
|
{
|
||||||
|
return b ? "true" : "false";
|
||||||
|
}
|
||||||
|
std::string operator()(const integer i) const
|
||||||
|
{
|
||||||
|
return std::to_string(i);
|
||||||
|
}
|
||||||
|
std::string operator()(const toml::floating f) const
|
||||||
|
{
|
||||||
|
std::string token = [=] {
|
||||||
|
// every float value needs decimal point (or exponent).
|
||||||
|
std::ostringstream oss;
|
||||||
|
oss << std::setprecision(float_prec_) << std::showpoint << f;
|
||||||
|
return oss.str();
|
||||||
|
}();
|
||||||
|
|
||||||
|
if(token.back() == '.') // 1. => 1.0
|
||||||
|
{
|
||||||
|
token += '0';
|
||||||
|
}
|
||||||
|
const auto e = std::find_if(token.cbegin(), token.cend(),
|
||||||
|
[](const char c) -> bool {
|
||||||
|
return c == 'E' || c == 'e';
|
||||||
|
});
|
||||||
|
if(e == token.cend())
|
||||||
|
{
|
||||||
|
return token; // there is no exponent part. just return it.
|
||||||
|
}
|
||||||
|
|
||||||
|
// zero-prefix in an exponent is not allowed in TOML.
|
||||||
|
// 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(offset, zero_prefix);
|
||||||
|
}
|
||||||
|
return token;
|
||||||
|
}
|
||||||
|
std::string operator()(const string& s) const
|
||||||
|
{
|
||||||
|
if(s.kind == string_t::basic)
|
||||||
|
{
|
||||||
|
if(std::find(s.str.cbegin(), s.str.cend(), '\n') != s.str.cend())
|
||||||
|
{
|
||||||
|
// if linefeed is contained, make it multiline-string.
|
||||||
|
const std::string open("\"\"\"\n");
|
||||||
|
const std::string close("\\\n\"\"\"");
|
||||||
|
return open + this->escape_ml_basic_string(s.str) + close;
|
||||||
|
}
|
||||||
|
|
||||||
|
// no linefeed. try to make it oneline-string.
|
||||||
|
std::string oneline = this->escape_basic_string(s.str);
|
||||||
|
if(oneline.size() + 2 < width_ || width_ < 2)
|
||||||
|
{
|
||||||
|
const std::string quote("\"");
|
||||||
|
return quote + oneline + quote;
|
||||||
|
}
|
||||||
|
|
||||||
|
// the line is too long compared to the specified width.
|
||||||
|
// split it into multiple lines.
|
||||||
|
std::string token("\"\"\"\n");
|
||||||
|
while(!oneline.empty())
|
||||||
|
{
|
||||||
|
if(oneline.size() < width_)
|
||||||
|
{
|
||||||
|
token += oneline;
|
||||||
|
oneline.clear();
|
||||||
|
}
|
||||||
|
else if(oneline.at(width_-2) == '\\')
|
||||||
|
{
|
||||||
|
token += oneline.substr(0, width_-2);
|
||||||
|
token += "\\\n";
|
||||||
|
oneline.erase(0, width_-2);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
token += oneline.substr(0, width_-1);
|
||||||
|
token += "\\\n";
|
||||||
|
oneline.erase(0, width_-1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return token + std::string("\\\n\"\"\"");
|
||||||
|
}
|
||||||
|
else // the string `s` is literal-string.
|
||||||
|
{
|
||||||
|
if(std::find(s.str.cbegin(), s.str.cend(), '\n') != s.str.cend() ||
|
||||||
|
std::find(s.str.cbegin(), s.str.cend(), '\'') != s.str.cend() )
|
||||||
|
{
|
||||||
|
const std::string open("'''\n");
|
||||||
|
const std::string close("'''");
|
||||||
|
return open + s.str + close;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
const std::string quote("'");
|
||||||
|
return quote + s.str + quote;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string operator()(const local_date& d) const
|
||||||
|
{
|
||||||
|
std::ostringstream oss;
|
||||||
|
oss << d;
|
||||||
|
return oss.str();
|
||||||
|
}
|
||||||
|
std::string operator()(const local_time& t) const
|
||||||
|
{
|
||||||
|
std::ostringstream oss;
|
||||||
|
oss << t;
|
||||||
|
return oss.str();
|
||||||
|
}
|
||||||
|
std::string operator()(const local_datetime& dt) const
|
||||||
|
{
|
||||||
|
std::ostringstream oss;
|
||||||
|
oss << dt;
|
||||||
|
return oss.str();
|
||||||
|
}
|
||||||
|
std::string operator()(const offset_datetime& odt) const
|
||||||
|
{
|
||||||
|
std::ostringstream oss;
|
||||||
|
oss << odt;
|
||||||
|
return oss.str();
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string operator()(const array& v) const
|
||||||
|
{
|
||||||
|
if(!v.empty() && v.front().is(value_t::Table))// v is an array of tables
|
||||||
|
{
|
||||||
|
// if it's not inlined, we need to add `[[table.key]]`.
|
||||||
|
// but if it can be inlined, we need `table.key = [...]`.
|
||||||
|
if(this->can_be_inlined_)
|
||||||
|
{
|
||||||
|
std::string token;
|
||||||
|
if(!keys_.empty())
|
||||||
|
{
|
||||||
|
token += this->serialize_key(keys_.back());
|
||||||
|
token += " = ";
|
||||||
|
}
|
||||||
|
bool width_exceeds = false;
|
||||||
|
token += "[\n";
|
||||||
|
for(const auto& item : v)
|
||||||
|
{
|
||||||
|
const auto t =
|
||||||
|
this->make_inline_table(item.cast<value_t::Table>());
|
||||||
|
|
||||||
|
if(t.size() + 1 > width_ || // +1 for the last comma {...},
|
||||||
|
std::find(t.cbegin(), t.cend(), '\n') != t.cend())
|
||||||
|
{
|
||||||
|
width_exceeds = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
token += t;
|
||||||
|
token += ",\n";
|
||||||
|
}
|
||||||
|
if(!width_exceeds)
|
||||||
|
{
|
||||||
|
token += "]\n";
|
||||||
|
return token;
|
||||||
|
}
|
||||||
|
// if width_exceeds, serialize it as [[array.of.tables]].
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string token;
|
||||||
|
for(const auto& item : v)
|
||||||
|
{
|
||||||
|
token += "[[";
|
||||||
|
token += this->serialize_dotted_key(keys_);
|
||||||
|
token += "]]\n";
|
||||||
|
token += this->make_multiline_table(item.cast<value_t::Table>());
|
||||||
|
}
|
||||||
|
return token;
|
||||||
|
}
|
||||||
|
if(v.empty())
|
||||||
|
{
|
||||||
|
return std::string("[]");
|
||||||
|
}
|
||||||
|
|
||||||
|
// not an array of tables. normal array. first, try to make it inline.
|
||||||
|
{
|
||||||
|
const auto inl = this->make_inline_array(v);
|
||||||
|
if(inl.size() < this->width_ &&
|
||||||
|
std::find(inl.cbegin(), inl.cend(), '\n') == inl.cend())
|
||||||
|
{
|
||||||
|
return inl;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// if the length exceeds this->width_, print multiline array
|
||||||
|
std::string token;
|
||||||
|
std::string current_line;
|
||||||
|
token += "[\n";
|
||||||
|
for(const auto& item : v)
|
||||||
|
{
|
||||||
|
auto next_elem = toml::visit(*this, item);
|
||||||
|
// newline between array-value and comma is not allowed
|
||||||
|
if(next_elem.back() == '\n'){next_elem.pop_back();}
|
||||||
|
|
||||||
|
if(current_line.size() + next_elem.size() + 1 < this->width_)
|
||||||
|
{
|
||||||
|
current_line += next_elem;
|
||||||
|
current_line += ',';
|
||||||
|
}
|
||||||
|
else if(current_line.empty())
|
||||||
|
{
|
||||||
|
// the next elem cannot be within the width.
|
||||||
|
token += next_elem;
|
||||||
|
token += ",\n";
|
||||||
|
// keep current line empty
|
||||||
|
}
|
||||||
|
else // current_line has some tokens and it exceeds width
|
||||||
|
{
|
||||||
|
assert(current_line.back() == ',');
|
||||||
|
token += current_line;
|
||||||
|
token += '\n';
|
||||||
|
current_line = next_elem;
|
||||||
|
current_line += ',';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if(!current_line.empty())
|
||||||
|
{
|
||||||
|
if(current_line.back() != '\n') {current_line += '\n';}
|
||||||
|
token += current_line;
|
||||||
|
}
|
||||||
|
token += "]\n";
|
||||||
|
return token;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string operator()(const table& v) const
|
||||||
|
{
|
||||||
|
if(this->can_be_inlined_)
|
||||||
|
{
|
||||||
|
std::string token;
|
||||||
|
if(!this->keys_.empty())
|
||||||
|
{
|
||||||
|
token += this->serialize_key(this->keys_.back());
|
||||||
|
token += " = ";
|
||||||
|
}
|
||||||
|
token += this->make_inline_table(v);
|
||||||
|
if(token.size() < this->width_)
|
||||||
|
{
|
||||||
|
return token;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string token;
|
||||||
|
if(!keys_.empty())
|
||||||
|
{
|
||||||
|
token += '[';
|
||||||
|
token += this->serialize_dotted_key(keys_);
|
||||||
|
token += "]\n";
|
||||||
|
}
|
||||||
|
token += this->make_multiline_table(v);
|
||||||
|
return token;
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
|
||||||
|
std::string serialize_key(const toml::key& key) const
|
||||||
|
{
|
||||||
|
detail::location<toml::key> loc(key, key);
|
||||||
|
detail::lex_unquoted_key::invoke(loc);
|
||||||
|
if(loc.iter() == loc.end())
|
||||||
|
{
|
||||||
|
return key; // all the tokens are consumed. the key is unquoted-key.
|
||||||
|
}
|
||||||
|
std::string token("\"");
|
||||||
|
token += this->escape_basic_string(key);
|
||||||
|
token += "\"";
|
||||||
|
return token;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string serialize_dotted_key(const std::vector<toml::key>& keys) const
|
||||||
|
{
|
||||||
|
std::string token;
|
||||||
|
if(keys.empty()){return token;}
|
||||||
|
|
||||||
|
for(const auto& k : keys)
|
||||||
|
{
|
||||||
|
token += this->serialize_key(k);
|
||||||
|
token += '.';
|
||||||
|
}
|
||||||
|
token.erase(token.size() - 1, 1); // remove trailing `.`
|
||||||
|
return token;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string escape_basic_string(const std::string& s) const
|
||||||
|
{
|
||||||
|
//XXX assuming `s` is a valid utf-8 sequence.
|
||||||
|
std::string retval;
|
||||||
|
for(const char c : s)
|
||||||
|
{
|
||||||
|
switch(c)
|
||||||
|
{
|
||||||
|
case '\\': {retval += "\\\\"; break;}
|
||||||
|
case '\"': {retval += "\\\""; break;}
|
||||||
|
case '\b': {retval += "\\b"; break;}
|
||||||
|
case '\t': {retval += "\\t"; break;}
|
||||||
|
case '\f': {retval += "\\f"; break;}
|
||||||
|
case '\n': {retval += "\\n"; break;}
|
||||||
|
case '\r': {retval += "\\r"; break;}
|
||||||
|
default : {retval += c; break;}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return retval;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string escape_ml_basic_string(const std::string& s) const
|
||||||
|
{
|
||||||
|
std::string retval;
|
||||||
|
for(auto i=s.cbegin(), e=s.cend(); i!=e; ++i)
|
||||||
|
{
|
||||||
|
switch(*i)
|
||||||
|
{
|
||||||
|
case '\\': {retval += "\\\\"; break;}
|
||||||
|
case '\"': {retval += "\\\""; break;}
|
||||||
|
case '\b': {retval += "\\b"; break;}
|
||||||
|
case '\t': {retval += "\\t"; break;}
|
||||||
|
case '\f': {retval += "\\f"; break;}
|
||||||
|
case '\n': {retval += "\n"; break;}
|
||||||
|
case '\r':
|
||||||
|
{
|
||||||
|
if(std::next(i) != e && *std::next(i) == '\n')
|
||||||
|
{
|
||||||
|
retval += "\r\n";
|
||||||
|
++i;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
retval += "\\r";
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
default: {retval += *i; break;}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return retval;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string make_inline_array(const array& v) const
|
||||||
|
{
|
||||||
|
std::string token;
|
||||||
|
token += '[';
|
||||||
|
bool is_first = true;
|
||||||
|
for(const auto& item : v)
|
||||||
|
{
|
||||||
|
if(is_first) {is_first = false;} else {token += ',';}
|
||||||
|
token += visit(serializer(std::numeric_limits<std::size_t>::max(),
|
||||||
|
this->float_prec_, true), item);
|
||||||
|
}
|
||||||
|
token += ']';
|
||||||
|
return token;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string make_inline_table(const table& v) const
|
||||||
|
{
|
||||||
|
assert(this->can_be_inlined_);
|
||||||
|
std::string token;
|
||||||
|
token += '{';
|
||||||
|
bool is_first = true;
|
||||||
|
for(const auto& kv : v)
|
||||||
|
{
|
||||||
|
// in inline tables, trailing comma is not allowed (toml-lang #569).
|
||||||
|
if(is_first) {is_first = false;} else {token += ',';}
|
||||||
|
token += this->serialize_key(kv.first);
|
||||||
|
token += '=';
|
||||||
|
token += visit(serializer(std::numeric_limits<std::size_t>::max(),
|
||||||
|
this->float_prec_, true), kv.second);
|
||||||
|
}
|
||||||
|
token += '}';
|
||||||
|
return token;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string make_multiline_table(const table& v) const
|
||||||
|
{
|
||||||
|
std::string token;
|
||||||
|
|
||||||
|
// print non-table stuff first. because after printing [foo.bar], the
|
||||||
|
// remaining non-table values will be assigned into [foo.bar], not [foo]
|
||||||
|
for(const auto kv : v)
|
||||||
|
{
|
||||||
|
if(kv.second.is(value_t::Table) || is_array_of_tables(kv.second))
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
const auto key_and_sep = this->serialize_key(kv.first) + " = ";
|
||||||
|
const auto residual_width = (this->width_ > key_and_sep.size()) ?
|
||||||
|
this->width_ - key_and_sep.size() : 0;
|
||||||
|
token += key_and_sep;
|
||||||
|
token += visit(serializer(residual_width, this->float_prec_, true),
|
||||||
|
kv.second);
|
||||||
|
if(token.back() != '\n')
|
||||||
|
{
|
||||||
|
token += '\n';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// normal tables / array of tables
|
||||||
|
|
||||||
|
// after multiline table appeared, the other tables cannot be inline
|
||||||
|
// because the table would be assigned into the table.
|
||||||
|
// [foo]
|
||||||
|
// ...
|
||||||
|
// bar = {...} # <- bar will be a member of [foo].
|
||||||
|
bool multiline_table_printed = false;
|
||||||
|
for(const auto& kv : v)
|
||||||
|
{
|
||||||
|
if(!kv.second.is(value_t::Table) && !is_array_of_tables(kv.second))
|
||||||
|
{
|
||||||
|
continue; // other stuff are already serialized. skip them.
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<toml::key> ks(this->keys_);
|
||||||
|
ks.push_back(kv.first);
|
||||||
|
|
||||||
|
auto tmp = visit(serializer(
|
||||||
|
this->width_, this->float_prec_, !multiline_table_printed, ks),
|
||||||
|
kv.second);
|
||||||
|
|
||||||
|
if((!multiline_table_printed) &&
|
||||||
|
std::find(tmp.cbegin(), tmp.cend(), '\n') != tmp.cend())
|
||||||
|
{
|
||||||
|
multiline_table_printed = true;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// still inline tables only.
|
||||||
|
tmp += '\n';
|
||||||
|
}
|
||||||
|
token += tmp;
|
||||||
|
}
|
||||||
|
return token;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool is_array_of_tables(const value& v) const
|
||||||
|
{
|
||||||
|
if(!v.is(value_t::Array)) {return false;}
|
||||||
|
|
||||||
|
const auto& a = v.cast<value_t::Array>();
|
||||||
|
return !a.empty() && a.front().is(value_t::Table);
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
|
||||||
|
bool can_be_inlined_;
|
||||||
|
int float_prec_;
|
||||||
|
std::size_t width_;
|
||||||
|
std::vector<toml::key> keys_;
|
||||||
|
};
|
||||||
|
|
||||||
|
inline std::string
|
||||||
|
format(const value& v, std::size_t w = 80,
|
||||||
|
int fprec = std::numeric_limits<toml::floating>::max_digits10)
|
||||||
|
{
|
||||||
|
return visit(serializer(w, fprec, true), v);
|
||||||
|
}
|
||||||
|
inline std::string
|
||||||
|
format(const table& t, std::size_t w = 80,
|
||||||
|
int fprec = std::numeric_limits<toml::floating>::max_digits10)
|
||||||
|
{
|
||||||
|
return serializer(w, fprec, true)(t);
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename charT, typename traits>
|
||||||
|
std::basic_ostream<charT, traits>&
|
||||||
|
operator<<(std::basic_ostream<charT, traits>& os, const value& v)
|
||||||
|
{
|
||||||
|
// get status of std::setw().
|
||||||
|
const std::size_t w = os.width();
|
||||||
|
const int fprec = os.precision();
|
||||||
|
os.width(0);
|
||||||
|
// the root object can't be an inline table. so pass `false`.
|
||||||
|
os << visit(serializer(w, fprec, false), v);
|
||||||
|
return os;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // toml
|
||||||
|
#endif// TOML11_SERIALIZER_HPP
|
||||||
@@ -15,6 +15,9 @@ namespace detail
|
|||||||
template<typename T>
|
template<typename T>
|
||||||
using unwrap_t = typename std::decay<T>::type;
|
using unwrap_t = typename std::decay<T>::type;
|
||||||
|
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
// check whether type T is a kind of container/map class
|
||||||
|
|
||||||
struct has_iterator_impl
|
struct has_iterator_impl
|
||||||
{
|
{
|
||||||
template<typename T> static std::true_type check(typename T::iterator*);
|
template<typename T> static std::true_type check(typename T::iterator*);
|
||||||
@@ -63,6 +66,9 @@ struct has_resize_method : decltype(has_resize_method_impl::check<T>(nullptr)){}
|
|||||||
#undef decltype(...)
|
#undef decltype(...)
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
// C++17 and/or/not
|
||||||
|
|
||||||
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>
|
||||||
@@ -80,6 +86,9 @@ 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)>{};
|
||||||
|
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
// normal type checker
|
||||||
|
|
||||||
template<typename T> struct is_std_pair : std::false_type{};
|
template<typename T> struct is_std_pair : std::false_type{};
|
||||||
template<typename T1, typename T2>
|
template<typename T1, typename T2>
|
||||||
struct is_std_pair<std::pair<T1, T2>> : std::true_type{};
|
struct is_std_pair<std::pair<T1, T2>> : std::true_type{};
|
||||||
@@ -92,7 +101,9 @@ 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{};
|
||||||
|
|
||||||
// to use toml::get<std::tuple<T1, T2, ...>> in C++11
|
// ---------------------------------------------------------------------------
|
||||||
|
// C++14 index_sequence
|
||||||
|
|
||||||
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{};
|
||||||
@@ -116,6 +127,21 @@ 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;
|
||||||
|
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
// return_type_of_t
|
||||||
|
|
||||||
|
#if __cplusplus >= 201703L
|
||||||
|
|
||||||
|
template<typename F, typename ... Args>
|
||||||
|
using return_type_of_t = std::invoke_result_t<F, Args...>;
|
||||||
|
|
||||||
|
#else
|
||||||
|
// result_of is deprecated after C++17
|
||||||
|
template<typename F, typename ... Args>
|
||||||
|
using return_type_of_t = typename std::result_of<F(Args...)>::type;
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
}// detail
|
}// detail
|
||||||
}//toml
|
}//toml
|
||||||
#endif // TOML_TRAITS
|
#endif // TOML_TRAITS
|
||||||
|
|||||||
@@ -137,17 +137,6 @@ template<> struct toml_value_t<LocalDate >{static constexpr value_t value =
|
|||||||
template<> struct toml_value_t<LocalTime >{static constexpr value_t value = value_t::LocalTime ;};
|
template<> struct toml_value_t<LocalTime >{static constexpr value_t value = value_t::LocalTime ;};
|
||||||
template<> struct toml_value_t<Array >{static constexpr value_t value = value_t::Array ;};
|
template<> struct toml_value_t<Array >{static constexpr value_t value = value_t::Array ;};
|
||||||
template<> struct toml_value_t<Table >{static constexpr value_t value = value_t::Table ;};
|
template<> struct toml_value_t<Table >{static constexpr value_t value = value_t::Table ;};
|
||||||
template<typename T> constexpr value_t toml_value_t<T>::value;
|
|
||||||
constexpr value_t toml_value_t<Boolean >::value;
|
|
||||||
constexpr value_t toml_value_t<Integer >::value;
|
|
||||||
constexpr value_t toml_value_t<Float >::value;
|
|
||||||
constexpr value_t toml_value_t<String >::value;
|
|
||||||
constexpr value_t toml_value_t<OffsetDatetime>::value;
|
|
||||||
constexpr value_t toml_value_t<LocalDatetime >::value;
|
|
||||||
constexpr value_t toml_value_t<LocalDate >::value;
|
|
||||||
constexpr value_t toml_value_t<LocalTime >::value;
|
|
||||||
constexpr value_t toml_value_t<Array >::value;
|
|
||||||
constexpr value_t toml_value_t<Table >::value;
|
|
||||||
|
|
||||||
template<typename T>
|
template<typename T>
|
||||||
struct is_exact_toml_type : disjunction<
|
struct is_exact_toml_type : disjunction<
|
||||||
|
|||||||
@@ -29,8 +29,9 @@ inline void resize_impl(T& container, std::size_t N, std::true_type)
|
|||||||
template<typename T>
|
template<typename T>
|
||||||
inline void resize_impl(T& container, std::size_t N, std::false_type)
|
inline void resize_impl(T& container, std::size_t N, std::false_type)
|
||||||
{
|
{
|
||||||
if(container.size() >= N) return;
|
if(container.size() >= N) {return;}
|
||||||
else throw std::invalid_argument("not resizable type");
|
|
||||||
|
throw std::invalid_argument("not resizable type");
|
||||||
}
|
}
|
||||||
|
|
||||||
} // detail
|
} // detail
|
||||||
@@ -38,8 +39,9 @@ inline void resize_impl(T& container, std::size_t N, std::false_type)
|
|||||||
template<typename T>
|
template<typename T>
|
||||||
inline void resize(T& container, std::size_t N)
|
inline void resize(T& container, std::size_t N)
|
||||||
{
|
{
|
||||||
if(container.size() == N) return;
|
if(container.size() == N) {return;}
|
||||||
else return detail::resize_impl(container, N, detail::has_resize_method<T>());
|
|
||||||
|
return detail::resize_impl(container, N, detail::has_resize_method<T>());
|
||||||
}
|
}
|
||||||
|
|
||||||
namespace detail
|
namespace detail
|
||||||
@@ -73,7 +75,5 @@ T from_string(const std::string& str, U&& opt)
|
|||||||
return v;
|
return v;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
}// toml
|
}// toml
|
||||||
#endif // TOML11_UTILITY
|
#endif // TOML11_UTILITY
|
||||||
|
|||||||
130
toml/value.hpp
130
toml/value.hpp
@@ -21,8 +21,8 @@ namespace detail
|
|||||||
{
|
{
|
||||||
// to show error messages. not recommended for users.
|
// to show error messages. not recommended for users.
|
||||||
region_base const& get_region(const value&);
|
region_base const& get_region(const value&);
|
||||||
// ditto.
|
template<typename Region>
|
||||||
void assign_keeping_region(value&, value);
|
void change_region(value&, Region&&);
|
||||||
}// detail
|
}// detail
|
||||||
|
|
||||||
template<typename T>
|
template<typename T>
|
||||||
@@ -562,8 +562,8 @@ class value
|
|||||||
// for error messages
|
// for error messages
|
||||||
friend region_base const& detail::get_region(const value&);
|
friend region_base const& detail::get_region(const value&);
|
||||||
|
|
||||||
// to see why it's here, see detail::insert_nested_key.
|
template<typename Region>
|
||||||
friend void detail::assign_keeping_region(value&, value);
|
friend void detail::change_region(value&, Region&&);
|
||||||
|
|
||||||
template<value_t T>
|
template<value_t T>
|
||||||
struct switch_cast;
|
struct switch_cast;
|
||||||
@@ -599,35 +599,21 @@ inline region_base const& get_region(const value& v)
|
|||||||
{
|
{
|
||||||
return *(v.region_info_);
|
return *(v.region_info_);
|
||||||
}
|
}
|
||||||
// If we keep region information after assigning another toml::* types, the
|
|
||||||
// error message become different from the actual value contained.
|
template<typename Region>
|
||||||
// To avoid this kind of confusing phenomena, the default assigners clear the
|
void change_region(value& v, Region&& reg)
|
||||||
// old region_info_. But this functionality is actually needed deep inside of
|
|
||||||
// parser, so if you want to see the usecase, see toml::detail::insert_nested_key
|
|
||||||
// defined in toml/parser.hpp.
|
|
||||||
inline void assign_keeping_region(value& v, value other)
|
|
||||||
{
|
{
|
||||||
v.cleanup(); // this keeps region info
|
using region_type = typename std::remove_reference<
|
||||||
// keep region_info_ intact
|
typename std::remove_cv<Region>::type
|
||||||
v.type_ = other.type();
|
>::type;
|
||||||
switch(v.type())
|
|
||||||
{
|
std::shared_ptr<region_base> new_reg =
|
||||||
case value_t::Boolean : ::toml::value::assigner(v.boolean_ , other.boolean_ ); break;
|
std::make_shared<region_type>(std::forward<region_type>(reg));
|
||||||
case value_t::Integer : ::toml::value::assigner(v.integer_ , other.integer_ ); break;
|
v.region_info_ = new_reg;
|
||||||
case value_t::Float : ::toml::value::assigner(v.floating_ , other.floating_ ); break;
|
|
||||||
case value_t::String : ::toml::value::assigner(v.string_ , other.string_ ); break;
|
|
||||||
case value_t::OffsetDatetime: ::toml::value::assigner(v.offset_datetime_, other.offset_datetime_); break;
|
|
||||||
case value_t::LocalDatetime : ::toml::value::assigner(v.local_datetime_ , other.local_datetime_ ); break;
|
|
||||||
case value_t::LocalDate : ::toml::value::assigner(v.local_date_ , other.local_date_ ); break;
|
|
||||||
case value_t::LocalTime : ::toml::value::assigner(v.local_time_ , other.local_time_ ); break;
|
|
||||||
case value_t::Array : ::toml::value::assigner(v.array_ , other.array_ ); break;
|
|
||||||
case value_t::Table : ::toml::value::assigner(v.table_ , other.table_ ); break;
|
|
||||||
default: break;
|
|
||||||
}
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}// detail
|
|
||||||
|
|
||||||
|
}// detail
|
||||||
|
|
||||||
template<> struct value::switch_cast<value_t::Boolean>
|
template<> struct value::switch_cast<value_t::Boolean>
|
||||||
{
|
{
|
||||||
@@ -767,6 +753,8 @@ inline bool operator<(const toml::value& lhs, const toml::value& rhs)
|
|||||||
return lhs.cast<value_t::Float >() < rhs.cast<value_t::Float >();
|
return lhs.cast<value_t::Float >() < rhs.cast<value_t::Float >();
|
||||||
case value_t::String :
|
case value_t::String :
|
||||||
return lhs.cast<value_t::String >() < rhs.cast<value_t::String >();
|
return lhs.cast<value_t::String >() < rhs.cast<value_t::String >();
|
||||||
|
case value_t::OffsetDatetime:
|
||||||
|
return lhs.cast<value_t::OffsetDatetime>() < rhs.cast<value_t::OffsetDatetime>();
|
||||||
case value_t::LocalDatetime:
|
case value_t::LocalDatetime:
|
||||||
return lhs.cast<value_t::LocalDatetime>() < rhs.cast<value_t::LocalDatetime>();
|
return lhs.cast<value_t::LocalDatetime>() < rhs.cast<value_t::LocalDatetime>();
|
||||||
case value_t::LocalDate:
|
case value_t::LocalDate:
|
||||||
@@ -801,17 +789,93 @@ inline bool operator>=(const toml::value& lhs, const toml::value& rhs)
|
|||||||
}
|
}
|
||||||
|
|
||||||
inline std::string format_error(const std::string& err_msg,
|
inline std::string format_error(const std::string& err_msg,
|
||||||
const toml::value& v, const std::string& comment)
|
const toml::value& v, const std::string& comment,
|
||||||
|
std::vector<std::string> hints = {})
|
||||||
{
|
{
|
||||||
return detail::format_underline(err_msg, detail::get_region(v), comment);
|
return detail::format_underline(err_msg, detail::get_region(v), comment,
|
||||||
|
std::move(hints));
|
||||||
}
|
}
|
||||||
|
|
||||||
inline std::string format_error(const std::string& err_msg,
|
inline std::string format_error(const std::string& err_msg,
|
||||||
const toml::value& v1, const std::string& comment1,
|
const toml::value& v1, const std::string& comment1,
|
||||||
const toml::value& v2, const std::string& comment2)
|
const toml::value& v2, const std::string& comment2,
|
||||||
|
std::vector<std::string> hints = {})
|
||||||
{
|
{
|
||||||
return detail::format_underline(err_msg, detail::get_region(v1), comment1,
|
return detail::format_underline(err_msg, detail::get_region(v1), comment1,
|
||||||
detail::get_region(v2), comment2);
|
detail::get_region(v2), comment2,
|
||||||
|
std::move(hints));
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename Visitor>
|
||||||
|
detail::return_type_of_t<Visitor, const toml::boolean&>
|
||||||
|
visit(Visitor&& visitor, const toml::value& v)
|
||||||
|
{
|
||||||
|
switch(v.type())
|
||||||
|
{
|
||||||
|
case value_t::Boolean : {return visitor(v.cast<value_t::Boolean >());}
|
||||||
|
case value_t::Integer : {return visitor(v.cast<value_t::Integer >());}
|
||||||
|
case value_t::Float : {return visitor(v.cast<value_t::Float >());}
|
||||||
|
case value_t::String : {return visitor(v.cast<value_t::String >());}
|
||||||
|
case value_t::OffsetDatetime: {return visitor(v.cast<value_t::OffsetDatetime>());}
|
||||||
|
case value_t::LocalDatetime : {return visitor(v.cast<value_t::LocalDatetime >());}
|
||||||
|
case value_t::LocalDate : {return visitor(v.cast<value_t::LocalDate >());}
|
||||||
|
case value_t::LocalTime : {return visitor(v.cast<value_t::LocalTime >());}
|
||||||
|
case value_t::Array : {return visitor(v.cast<value_t::Array >());}
|
||||||
|
case value_t::Table : {return visitor(v.cast<value_t::Table >());}
|
||||||
|
case value_t::Empty : break;
|
||||||
|
case value_t::Unknown : break;
|
||||||
|
default: break;
|
||||||
|
}
|
||||||
|
throw std::runtime_error(format_error("[error] toml::visit: toml::value "
|
||||||
|
"does not have any valid value.", v, "here"));
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename Visitor>
|
||||||
|
detail::return_type_of_t<Visitor, toml::boolean&>
|
||||||
|
visit(Visitor&& visitor, toml::value& v)
|
||||||
|
{
|
||||||
|
switch(v.type())
|
||||||
|
{
|
||||||
|
case value_t::Boolean : {return visitor(v.cast<value_t::Boolean >());}
|
||||||
|
case value_t::Integer : {return visitor(v.cast<value_t::Integer >());}
|
||||||
|
case value_t::Float : {return visitor(v.cast<value_t::Float >());}
|
||||||
|
case value_t::String : {return visitor(v.cast<value_t::String >());}
|
||||||
|
case value_t::OffsetDatetime: {return visitor(v.cast<value_t::OffsetDatetime>());}
|
||||||
|
case value_t::LocalDatetime : {return visitor(v.cast<value_t::LocalDatetime >());}
|
||||||
|
case value_t::LocalDate : {return visitor(v.cast<value_t::LocalDate >());}
|
||||||
|
case value_t::LocalTime : {return visitor(v.cast<value_t::LocalTime >());}
|
||||||
|
case value_t::Array : {return visitor(v.cast<value_t::Array >());}
|
||||||
|
case value_t::Table : {return visitor(v.cast<value_t::Table >());}
|
||||||
|
case value_t::Empty : break;
|
||||||
|
case value_t::Unknown : break;
|
||||||
|
default: break;
|
||||||
|
}
|
||||||
|
throw std::runtime_error(format_error("[error] toml::visit: toml::value "
|
||||||
|
"does not have any valid value.", v, "here"));
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename Visitor>
|
||||||
|
detail::return_type_of_t<Visitor, toml::boolean&>
|
||||||
|
visit(Visitor&& visitor, toml::value&& v)
|
||||||
|
{
|
||||||
|
switch(v.type())
|
||||||
|
{
|
||||||
|
case value_t::Boolean : {return visitor(std::move(v.cast<value_t::Boolean >()));}
|
||||||
|
case value_t::Integer : {return visitor(std::move(v.cast<value_t::Integer >()));}
|
||||||
|
case value_t::Float : {return visitor(std::move(v.cast<value_t::Float >()));}
|
||||||
|
case value_t::String : {return visitor(std::move(v.cast<value_t::String >()));}
|
||||||
|
case value_t::OffsetDatetime: {return visitor(std::move(v.cast<value_t::OffsetDatetime>()));}
|
||||||
|
case value_t::LocalDatetime : {return visitor(std::move(v.cast<value_t::LocalDatetime >()));}
|
||||||
|
case value_t::LocalDate : {return visitor(std::move(v.cast<value_t::LocalDate >()));}
|
||||||
|
case value_t::LocalTime : {return visitor(std::move(v.cast<value_t::LocalTime >()));}
|
||||||
|
case value_t::Array : {return visitor(std::move(v.cast<value_t::Array >()));}
|
||||||
|
case value_t::Table : {return visitor(std::move(v.cast<value_t::Table >()));}
|
||||||
|
case value_t::Empty : break;
|
||||||
|
case value_t::Unknown : break;
|
||||||
|
default: break;
|
||||||
|
}
|
||||||
|
throw std::runtime_error(format_error("[error] toml::visit: toml::value "
|
||||||
|
"does not have any valid value.", v, "here"));
|
||||||
}
|
}
|
||||||
|
|
||||||
}// toml
|
}// toml
|
||||||
|
|||||||
Reference in New Issue
Block a user