diff --git a/README.md b/README.md index e157ef41..f796abb6 100644 --- a/README.md +++ b/README.md @@ -6,199 +6,208 @@ toml11 [![MIT License](http://img.shields.io/badge/license-MIT-blue.svg?style=flat)](LICENSE) [![DOI](https://zenodo.org/badge/DOI/10.5281/zenodo.1209136.svg)](https://doi.org/10.5281/zenodo.1209136) -c++11 header-only toml parser depending only on c++11 standard library. +c++11 header-only toml parser depending only on c++ standard library. -compatible to TOML v0.4.0. +compatible to the latest version of TOML v0.5.0 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. ## How to use -Just include the file after adding correct include path. +## Installation + +Just include the file after adding it to the include path. ```cpp -#include +#include // that's all! now you can use it. int main() { - /* do something ... */ + const auto data = toml::parse("example.toml"); + const auto title = toml::get(data.at("title")); + std::cout << "the title is " << title << std::endl; } ``` -### decoding toml file +### Decoding toml file -The only thing you have to do is passing filename to `toml::parse` function. +The only thing you have to do is to pass a filename to the `toml::parse` function. ```cpp const std::string fname("sample.toml"); -const auto data = toml::parse(fname); +const toml::table data = toml::parse(fname); ``` In the case of file open error, it will throw `std::runtime_error`. -You can pass also `stream` to `toml::parse` function. +You can also pass a `stream` to the `toml::parse` function after checking the status. ```cpp std::ifstream ifs("sample.toml"); assert(ifs.good()); -const auto data = toml::parse(ifs); +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). + +### In the case of 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). + +```console +terminate called after throwing an instance of 'toml::syntax_error' + what(): [error] toml::parse_table: invalid line format # error description + --> example.toml # file name + 3 | a = 42 = true # line num and content + | ^------ expected newline, but got '='. # error reason ``` -If there are syntax error in the toml file, -`toml::parse` will throw `toml::syntax_error`. +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! -#### toml::get() +### Getting a toml value -Then you can obtain the various value from the `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 -answer = 42 -pi = 3.14 +answer = 42 +pi = 3.14 numbers = [1,2,3] -time = 1979-05-27T07:32:00Z +time = 1979-05-27T07:32:00Z [tab] key = "value" ``` ``` cpp -const auto answer = toml::get(data.at("answer")); -const auto pi = toml::get(data.at("pi")); +const auto answer = toml::get(data.at("answer")); +const auto pi = toml::get(data.at("pi")); const auto numbers = toml::get>(data.at("numbers")); const auto timepoint = toml::get(data.at("time")); const auto tab = toml::get(data.at("tab")); -const auto key = toml::get(tab.at("key")); +const auto key = toml::get( tab.at("key")); ``` -You can set any kind of `container` class to obtain `toml::Array` except for -`map`-like class. +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 -const auto vc = toml::get>(data.at("numbers")); -const auto ls = toml::get>(data.at("numbers")); -const auto dq = toml::get>(data.at("numbers")); -const auto ar = toml::get>(data.at("numbers")); -// if size of data.at("numbers") is larger than 3, it will throw toml::type_error -// because std::array is not resizable. +```cpp +toml::get(data["answer"]) = 6 * 9; +std::cout << toml::get(data.at("answer")) << std::endl; // 54 ``` -If the type you passed as a template parameter is incorrect, -it will throw `toml::type_error`. +If the specified type requires conversion, you can't take a reference to the value. See also [underlying types](#underlying-types). -``` cpp -const auto wrong1 = toml::get(data.at("integer")); // exception thrown! -const auto wrong2 = toml::get(data.at("integer")); // ditto -const auto wrong3 = toml::get(data.at("array")); // ditto +#### 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. + +```console +terminate called after throwing an instance of 'toml::type_error' + what(): [error] toml::value bad_cast to integer + --> example.toml + 3 | title = "TOML Example" + | ~~~~~~~~~~~~~~ the actual type is string ``` -Although `toml::get` is convenient, it has additional copy-cost because it -copies data contained in `toml::value` to user-specified type. -Of course in some case this overhead is not ignorable. +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 -You can get reference pointing to contained value using `toml::value::cast()` like this. +You can set any kind of `container` class to obtain a `toml::array` except for `map`-like classes. ``` cpp -const auto& pi = data.at("pi").cast(); -const auto& tab = data.at("tab").cast(); -const auto& numbers = data.at("numbers").cast(); +const auto vc = toml::get >(data.at("numbers")); +const auto ls = toml::get >(data.at("numbers")); +const auto dq = toml::get >(data.at("numbers")); +const auto ar = toml::get>(data.at("numbers")); +// if the size of data.at("numbers") is larger than that of std::array, +// it will throw toml::type_error because std::array is not resizable. ``` -Unfortunately, if you use `toml::value::cast` to get an array, you would need to -`cast` each element in `toml::Array` because `toml::Array` is represented as -an array of `toml::value`. +Surprisingly, you can also get a `toml::array` as `std::pair` and `std::tuple.` ```cpp -const auto& num0 = numbers.at(0).cast(); -const auto& num1 = numbers.at(1).cast(); -const auto& num2 = numbers.at(2).cast(); +const auto tp = toml::get>(data.at("numbers")); ``` -#### toml::get\_or +The case when you need this functionality is to get an array of arrays. + +```toml +aofa = [[1,2,3], ["foo", "bar", "baz"]] # toml allows this +``` -You can also set default value for `toml::get`. +What is the corresponding C++ type? Obviously, it is a `std::pair` of `std::vector`s. ```cpp -toml::Table data; // empty table! +const auto aofa = toml::get< + std::pair, std::vector> + >(data.at("aofa")); +``` -const auto value1 = toml::get_or(data, "key1", 42); // value1 => int 42. +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. -toml::Integer i(123); -const auto value2 = toml::get_or(data, "key1", i); // value2 => toml::Integer 42. +```cpp +const auto aofa = toml::get(data.at("aofa")); +const auto first = toml::get(aofa.at(0)); ``` -#### toml::value\_t +See also [expecting conversion](#expecting-conversion) and [checking-value-type](#checking-value-type). + +### Getting tables -When you don't know the exact type of toml-value, you can get `enum` type from -`toml::value`. +`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. + +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 -int i; -double d; -std::string s; -std::vector a; -const auto t = data.at("something").type(); -switch(t) +toml::table data = toml::parse("example.toml"); +if(data.count("title") != 0) { - case toml::value_t::Integer: i = toml::get(data.at("something")); break; - case toml::value_t::Float : d = toml::get(data.at("something")); break; - case toml::value_t::String : s = toml::get(data.at("something")); break; - case toml::value_t::Array : a = toml::get>(data.at("something")); break; - default : throw std::runtime_error("unexpected type : " + toml::stringize(t)); + data["title"] = std::string("TOML example"); } ``` -Okey, but it is painful to write `switch-case` for many time. +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::from\_toml() - -The more sophisticated way is using `toml::from_toml` and `std::tie`. +```toml +[tab] +key1 = "foo" # all the values are +key2 = "bar" # toml String +``` ```cpp -int i = 0; -double d = 0.; -std::string s; -std::vector a; - -toml::from_toml(std::tie(i, d, s, a), data.at("something")); +const auto tab = toml::get>(data.at("tab")); +std::cout << tab["key1"] << std::endl; // foo +std::cout << tab["key2"] << std::endl; // bar ``` -Here, only matched value will be filled. -The others are left intact after calling `from_toml`. -It should be noted that `toml::from_toml` returns as usual even if there are no -matched type. +### Dotted keys +TOML v0.5.0 has a new feature named "dotted keys". You can chain keys to represent the structure of the data. -`from_toml` can be used also for single type. - -```cpp -int i; -toml::from_toml(i, data.at("something")); +```toml +physical.color = "orange" +physical.shape = "round" ``` -Unlike `toml::get`, `toml::from_toml` does not require to specify the type -through the template argument because the type can be deduced from argument. - -#### toml::value - -In toml, `Array` is capable of having `Array` s and each of them possibly have -different types like this. +This is equivalent to the following. ```toml -array_of_array = [[1,2,3,4,5], ["foo", "bar", "baz"]] +[physical] +color = "orange" +shape = "round" ``` -In this case, you can use `toml::value` directly. +You can get both of the above formats with the same c++ code. ```cpp -// use toml::value in a container -const auto a = toml::get>(data.at("array_of_array")); -// or you can use default toml::Array. -const auto a_ = toml::get(data.at("array_of_array")); -// you can obtain values from toml::value in the same way as described above. -const auto ns = toml::get>(a.at(0)); -const auto ss = toml::get>(a.at(1)); +const auto physical = toml::get(data.at("physical")); +const auto color = toml::get(physical.at("color")); ``` -#### Array of Table +### An array of tables -Of course, you can obtain `array of table` in the same way. +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 array_of_inline_table = [{key = "value1"}, {key = "value2"}, {key = "value3"}] @@ -212,61 +221,196 @@ key = "value6" ``` ```cpp -const auto aot1 = toml::get>(data.at("array_of_inline_table")) -const auto aot2 = toml::get>(data.at("array_of_table")) +const auto aot1 = toml::get>(data.at("array_of_inline_table")); +const auto aot2 = toml::get>(data.at("array_of_table")); +``` + +### 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. + +By passing the exact types, `toml::get` returns reference that has nealy zero overhead. + +``` cpp +const auto& tab = toml::get(data.at("tab")); +const auto& numbers = toml::get(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`. + +```cpp +const auto& num0 = toml::get(numbers.at(0)); +const auto& num1 = toml::get(numbers.at(1)); +const auto& num2 = toml::get(numbers.at(2)); ``` -## Documentation +### Datetime and its variants -The toml types and corresponding `enum` name are listed in the table below. -`value_t` is a scoped-enum defined in the namespace toml. +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-type | c++ type | enum | -| --------- | ---------------------------------------------- | ------------------------- | -| Boolean | `bool` | `toml::value_t::Boolean` | -| Integer | `std::int64_t` | `toml::value_t::Integer` | -| Float | `double` | `toml::value_t::Float` | -| String | `std::string` | `toml::value_t::String` | -| Datetime | `toml::Datetime` | `toml::value_t::Datetime` | -| Array | `std::vector` | `toml::value_t::Array` | -| Table | `std::unordered_map` | `toml::value_t::Table` | +```toml +time = 12:30:00 +date = 2018-12-23 +``` -`Datetime` is the `struct` that is 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. +```cpp +const auto dur = toml::get(data.at("time")); // 12 * 60 + 30 min +const auto tp = toml::get(data.at("date")); +``` -For user-convenience, `toml::Datetime` is _implicitly_ convertible to -`std::chrono::system_clock::time_point`. If `toml::Datetime` does not have any -Date information, the information will be generated from -`std::chrono::system_clock::now()` when cast is performed. +### Getting with a fallback -The definition of Datetime struct is below. + `toml::get_or` returns a default value if `toml::get` failed. ```cpp -namespace toml -{ -template -struct basic_datetime +toml::table data; // empty table! +const auto value = toml::get_or(data, "key", 42); // value => int 42. +``` + +`toml::get_or` automatically deduces what type you want to get from the default value you passed. + +### Expecting conversion + +By using `toml::expect`, you will get your expected value or an error message without throwing `toml::type_error`. + +```cpp +const auto value = toml::expect(data.at("title")); +if(value.is_ok()) { + std::cout << value.unwrap() << std::endl; +} else { + std::cout << value.unwrap_err() << std::endl; +} +``` + +Also, you can pass a function object to modify the expected value. + +```cpp +const auto value = toml::expect(data.at("number")) + .map(// function that receives expected type (here, int) + [](const int number) -> double { + return number * 1.5 + 1.0; + }).unwrap_or(/*default value =*/ 3.14); +``` + +### 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. + +```cpp +const auto data = toml::parse("example.toml"); +const auto num = toml::find(data, "num", /*for err msg*/"example.toml"); +``` + +If the value does not exist, it throws `std::out_of_range` with informative error message. + +```console +terminate called after throwing an instance of 'std::out_of_range' + 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. + +```cpp +const auto data = toml::parse("example.toml"); +const auto num = toml::find(data.at("table"), "num"); +// expecting the following example.toml +// [table] +// 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. + +```console +terminate called after throwing an instance of 'std::out_of_range' + what(): [error] key "num" not found + --> example.toml + 3 | [table] + | ~~~~~~~ in this table +``` + +If it's not a `toml::table`, the same error as "invalid type" would be thrown. + +### Checking value type + +When you don't know the exact type of toml-value, you can get `enum` type from `toml::value`. + +```cpp +switch(data.at("something").type()) { - uintT year; // since 0. - uintT month; // [1-12] - uintT day; // [1-31] - uintT hour; // [0-23] - uintT minite; // [0-59] - uintT second; // [0-59] - uintT millisecond // [0-999] - uintT microsecond // [0-999] - intT offset_hour; // [-12 - +12] - intT offset_minute; // [-59 - +59] -}; - -typedef basic_datetime Datetime; + case toml::value_t::Integer: /* do some stuff */; break; + case toml::value_t::Float : /* do some stuff */; break; + case toml::value_t::String : /* do some stuff */; break; + default : throw std::runtime_error( + "unexpected type : " + toml::stringize(data.at("something").type())); } ``` -It must be noted that the range of some values in `basic_datetime` is different -from `std::tm`. For example, month is in the range of `[1,12]` and year starts -from 0 (not 1900). +### Fill only the matched value + +The more sophisticated way is using `toml::from_toml` and `std::tie`. + +```cpp +toml::table data{{"something", toml::value("foo")}}; +int i = 0; +double d = 0.; +std::string s; +toml::from_toml(std::tie(i, d, s), data.at("something")); +std::cout << i << ", " << d << ", " << s << std::endl; // 0, 0, foo +``` + +Here, only matched value will be filled. The others are left intact after calling `from_toml`. +It should be noted that `toml::from_toml` returns as usual even if there are no matched type. + +`from_toml` can be used also for single type. + +```cpp +int i = 0; +toml::from_toml(i, data.at("something")); +``` + +### Sanitizing UTF-8 codepoints + +toml11 shows warning if a value of an escape sequence used to represent unicode character exceeds the unicode range. + +```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. + --> example.toml + 3 | exceeds_unicode = "\U0011FFFF example" + | ~~~~~~~~~ should be in [0x00..0x10FFFF] +``` + +Also, toml11 throws `std::domain_error` if the code point exceeds the range that can be represented by utf-8. + +```console +terminate called after throwing an instance of 'std::range_error' + what(): [error] input codepoint (0020FFFF) is too large to encode as utf-8. + --> example.toml + 3 | exceeds_utf8 = "\U0020FFFF example" + | ~~~~~~~~~ should be in [0x00..0x10FFFF] +``` + +## Underlying types + +The toml types (can be used as `toml::*` in this library) and corresponding `enum` names are listed in the table below. + +| toml::type | underlying c++ type | enum | +| -------------- | -------------------------------------------- | ------------------------------- | +| Boolean | `bool` | `toml::value_t::Boolean` | +| Integer | `std::int64_t` | `toml::value_t::Integer` | +| Float | `double` | `toml::value_t::Float` | +| String | `toml::string` | `toml::value_t::String` | +| LocalDate | `toml::local_date` | `toml::value_t::LocalDate` | +| LocalTime | `toml::local_time` | `toml::value_t::LocalTime` | +| LocalDatetime | `toml::local_datetime` | `toml::value_t::LocalDatetime` | +| OffsetDatetime | `toml::offset_datetime` | `toml::value_t::offsetDatetime` | +| Array | `std::vector` | `toml::value_t::Array` | +| Table | `std::unordered_map` | `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()` 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. + +It is recommended to get `Datetime`s as `std::chrono` classes through `toml::get`. ## Contributors diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 51ca6096..e9eabba0 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -1,15 +1,29 @@ set(TEST_NAMES - test_traits + test_datetime test_utility + test_result + test_traits test_value + test_lex_boolean + test_lex_integer + test_lex_floating + test_lex_datetime + test_lex_string + test_lex_key_comment + test_parse_boolean + test_parse_integer + test_parse_floating + test_parse_string + test_parse_datetime + test_parse_array + test_parse_table + test_parse_inline_table + test_parse_key + test_parse_table_key + test_get + test_get_related_func test_to_toml test_from_toml - test_get - test_get_or - test_value_operator - test_datetime - test_acceptor - test_parser test_parse_file test_parse_unicode ) diff --git a/tests/test_acceptor.cpp b/tests/test_acceptor.cpp deleted file mode 100644 index dfce2eb3..00000000 --- a/tests/test_acceptor.cpp +++ /dev/null @@ -1,592 +0,0 @@ -#define BOOST_TEST_MODULE "test_acceptor" -#ifdef UNITTEST_FRAMEWORK_LIBRARY_EXIST -#include -#else -#define BOOST_TEST_NO_LIB -#include -#endif -#include -#include -#include - -BOOST_AUTO_TEST_CASE(test_conditions) -{ - { - const std::string tmp(" "); - const std::string dummy("dummy"); - BOOST_CHECK(toml::is_space::invoke(tmp.begin(), tmp.end()) == tmp.end()); - BOOST_CHECK(toml::is_space::invoke(dummy.begin(), dummy.end()) == dummy.begin()); - } - - { - const std::string tmp("\t"); - const std::string dummy("dummy"); - BOOST_CHECK(toml::is_tab::invoke(tmp.begin(), tmp.end()) == tmp.end()); - BOOST_CHECK(toml::is_tab::invoke(dummy.begin(), dummy.end()) == dummy.begin()); - } - - { - for(int i=0; i<10; ++i) - { - const std::string tmp = std::to_string(i); - const std::string dummy("dummy"); - BOOST_CHECK(toml::is_number::invoke(tmp.begin(), tmp.end()) == tmp.end()); - BOOST_CHECK(toml::is_number::invoke(dummy.begin(), dummy.end()) == dummy.begin()); - } - } - - { - for(char c='a'; c <= 'z'; ++c) - { - const std::string tmp(1, c); - const std::string dummy(1, std::toupper(c)); - BOOST_CHECK(toml::is_lowercase::invoke(tmp.begin(), tmp.end()) == tmp.end()); - BOOST_CHECK(toml::is_lowercase::invoke(dummy.begin(), dummy.end()) == dummy.begin()); - } - } - { - for(char c='A'; c <= 'Z'; ++c) - { - const std::string tmp(1, c); - const std::string dummy(1, std::tolower(c)); - BOOST_CHECK(toml::is_uppercase::invoke(tmp.begin(), tmp.end()) == tmp.end()); - BOOST_CHECK(toml::is_uppercase::invoke(dummy.begin(), dummy.end()) == dummy.begin()); - } - } - { - const std::string tmp(" "); - const std::string dummy("a"); - BOOST_CHECK(toml::is_whitespace::invoke(tmp.begin(), tmp.end()) == tmp.end()); - BOOST_CHECK(toml::is_whitespace::invoke(dummy.begin(), dummy.end()) == dummy.begin()); - } - { - const std::string tmp("\t"); - const std::string dummy("a"); - BOOST_CHECK(toml::is_whitespace::invoke(tmp.begin(), tmp.end()) == tmp.end()); - BOOST_CHECK(toml::is_whitespace::invoke(dummy.begin(), dummy.end()) == dummy.begin()); - } - { - const std::string tmp("hoge1-piyo2_fuga3"); - const std::string dummy(" \t"); - BOOST_CHECK(toml::is_barekey::invoke(tmp.begin(), tmp.end()) != tmp.begin()); - BOOST_CHECK(toml::is_barekey::invoke(dummy.begin(), dummy.end()) == dummy.begin()); - } -} - -BOOST_AUTO_TEST_CASE(test_basic_inline_string) -{ - using is_valid = toml::is_basic_inline_string; - { - const std::string simple("\"hoge1-piyo2_fuga3\""); - BOOST_CHECK(is_valid::invoke(simple.cbegin(), simple.cend()) == simple.cend()); - } - { - const std::string quote("\"hoge1-\\\"piyo2\\\"_fuga3\""); - BOOST_CHECK(is_valid::invoke(quote.cbegin(), quote.cend()) == quote.cend()); - } - { - const std::string escape("\"I'm a string. \\\"You can quote me\\\". Name\\tJos\\u00E9\\nLocation\\tSF.\""); - BOOST_CHECK(is_valid::invoke(escape.cbegin(), escape.cend()) == escape.cend()); - } - { - const std::string empty("\"\""); - BOOST_CHECK(is_valid::invoke(empty.cbegin(), empty.cend()) == empty.cend()); - } - - { - const std::string newline("\"newline\r\nhoge\""); - BOOST_CHECK(is_valid::invoke(newline.cbegin(), newline.cend()) == newline.cbegin()); - } - { - const std::string invalid_escape("\"foo\\abar\""); - BOOST_CHECK(is_valid::invoke(invalid_escape.cbegin(), invalid_escape.cend()) == invalid_escape.cbegin()); - } - { - const std::string invalid_character("\"foo\10bar\""); - BOOST_CHECK(is_valid::invoke(invalid_character.cbegin(), invalid_character.cend()) == invalid_character.cbegin()); - } - { - const std::string multi("\"\"\"multiline\"\"\""); - BOOST_CHECK(is_valid::invoke(multi.cbegin(), multi.cend()) == multi.cbegin()); - } -} - -BOOST_AUTO_TEST_CASE(test_basic_multiline_string) -{ - using is_valid = toml::is_basic_multiline_string; - { - const std::string simple("\"\"\"foobar\"\"\""); - BOOST_CHECK(is_valid::invoke(simple.cbegin(), simple.cend()) == simple.cend()); - } - { - const std::string quote("\"\"\"hoge1-\"piyo2\"_fuga3\"\"\""); - BOOST_CHECK(is_valid::invoke(quote.cbegin(), quote.cend()) == quote.cend()); - } - { - const std::string newline("\"\"\"hoge1-\npiyo2_\r\nfuga3\"\"\""); - BOOST_CHECK(is_valid::invoke(newline.cbegin(), newline.cend()) == newline.cend()); - } - { - const std::string escape("\"\"\"I'm a string. \"You can quote me\". Name\\tJos\\u00E9\\nLocation\\tSF.\"\"\""); - BOOST_CHECK(is_valid::invoke(escape.cbegin(), escape.cend()) == escape.cend()); - } - { - const std::string empty("\"\"\"\"\"\""); - BOOST_CHECK(is_valid::invoke(empty.cbegin(), empty.cend()) == empty.cend()); - } - { - const std::string ending_backslash("\"\"\"hoge\\\n piyo\\\n\"\"\""); - BOOST_CHECK(is_valid::invoke(ending_backslash.cbegin(), ending_backslash.cend()) == ending_backslash.cend()); - } - - { - const std::string invalid_escape("\"\"\"foo\\abar\"\"\""); - BOOST_CHECK(is_valid::invoke(invalid_escape.cbegin(), invalid_escape.cend()) == invalid_escape.cbegin()); - } - { - const std::string invalid_character("\"\"\"foo\10bar\"\"\""); - BOOST_CHECK(is_valid::invoke(invalid_character.cbegin(), invalid_character.cend()) == invalid_character.cbegin()); - } - { - const std::string single("\"singleline\""); - BOOST_CHECK(is_valid::invoke(single.cbegin(), single.cend()) == single.cbegin()); - } -} - -BOOST_AUTO_TEST_CASE(test_literal_inline_string) -{ - using is_valid = toml::is_literal_inline_string; - { - const std::string simple("'foobar'"); - BOOST_CHECK(is_valid::invoke(simple.cbegin(), simple.cend()) == simple.cend()); - } - { - const std::string nonescape("'C:\\Users\\nodejs\\templates'"); - BOOST_CHECK(is_valid::invoke(nonescape.cbegin(), nonescape.cend()) == nonescape.cend()); - } - { - const std::string empty("''"); - BOOST_CHECK(is_valid::invoke(empty.cbegin(), empty.cend()) == empty.cend()); - } - - { - const std::string quote("'hoge1-'piyo2'_fuga3'"); - BOOST_CHECK(is_valid::invoke(quote.cbegin(), quote.cend()) == quote.cbegin() + 8); - } - { - const std::string newline("'hoge1-\npiyo2_\r\nfuga3'"); - BOOST_CHECK(is_valid::invoke(newline.cbegin(), newline.cend()) == newline.cbegin()); - } - { - const std::string invalid_character("'foo\10bar'"); - BOOST_CHECK(is_valid::invoke(invalid_character.cbegin(), invalid_character.cend()) == invalid_character.cbegin()); - } - { - const std::string multi("'''multiline'''"); - BOOST_CHECK(is_valid::invoke(multi.cbegin(), multi.cend()) == multi.cbegin()); - } -} - -BOOST_AUTO_TEST_CASE(test_literal_multiline_string) -{ - using is_valid = toml::is_literal_multiline_string; - { - const std::string simple("'''foobar'''"); - BOOST_CHECK(is_valid::invoke(simple.cbegin(), simple.cend()) == simple.cend()); - } - { - const std::string quote("'''hoge1-'piyo2'_fuga3'''"); - BOOST_CHECK(is_valid::invoke(quote.cbegin(), quote.cend()) == quote.cend()); - } - { - const std::string nonescape("'''C:\\Users\\nodejs\\templates'''"); - BOOST_CHECK(is_valid::invoke(nonescape.cbegin(), nonescape.cend()) == nonescape.cend()); - } - { - const std::string newline("'''hoge1-\npiyo2_\r\nfuga3'''"); - BOOST_CHECK(is_valid::invoke(newline.cbegin(), newline.cend()) == newline.cend()); - } - { - const std::string empty("''''''"); - BOOST_CHECK(is_valid::invoke(empty.cbegin(), empty.cend()) == empty.cend()); - } - - { - const std::string invalid_character("'''foo\10bar'''"); - BOOST_CHECK(is_valid::invoke(invalid_character.cbegin(), invalid_character.cend()) == invalid_character.cbegin()); - } - { - const std::string single("'singleline'"); - BOOST_CHECK(is_valid::invoke(single.cbegin(), single.cend()) == single.cbegin()); - } -} - -BOOST_AUTO_TEST_CASE(test_integer) -{ - using is_valid = toml::is_integer; - { - const std::string simple("1"); - BOOST_CHECK(is_valid::invoke(simple.cbegin(), simple.cend()) == simple.cend()); - } - { - const std::string psign("+1234"); - BOOST_CHECK(is_valid::invoke(psign.cbegin(), psign.cend()) == psign.cend()); - const std::string nsign("-1234"); - BOOST_CHECK(is_valid::invoke(nsign.cbegin(), nsign.cend()) == nsign.cend()); - } - { - const std::string zero("0"); - BOOST_CHECK(is_valid::invoke(zero.cbegin(), zero.cend()) == zero.cend()); - } - { - const std::string us("1_2_3_4_5"); - BOOST_CHECK(is_valid::invoke(us.cbegin(), us.cend()) == us.cend()); - } - - { - const std::string f("12.34"); - BOOST_CHECK(is_valid::invoke(f.cbegin(), f.cend()) == f.cbegin()+2); - } - { - const std::string f("12e34"); - BOOST_CHECK(is_valid::invoke(f.cbegin(), f.cend()) == f.cbegin()+2); - } - { - const std::string ascii("1234a"); - BOOST_CHECK(is_valid::invoke(ascii.cbegin(), ascii.cend()) == ascii.cbegin()+4); - } -} - -BOOST_AUTO_TEST_CASE(test_float) -{ - using is_valid = toml::is_float; - { - const std::string simplef("1.0"); - BOOST_CHECK(is_valid::invoke(simplef.cbegin(), simplef.cend()) == simplef.cend()); - const std::string simplee("1e0"); - BOOST_CHECK(is_valid::invoke(simplee.cbegin(), simplee.cend()) == simplee.cend()); - const std::string both("6.626e-34"); - BOOST_CHECK(is_valid::invoke(both.cbegin(), both.cend()) == both.cend()); - } - { - const std::string psign("+1.0"); - BOOST_CHECK(is_valid::invoke(psign.cbegin(), psign.cend()) == psign.cend()); - const std::string nsign("-1.0"); - BOOST_CHECK(is_valid::invoke(nsign.cbegin(), nsign.cend()) == nsign.cend()); - } - { - const std::string psmall("+0.001"); - BOOST_CHECK(is_valid::invoke(psmall.cbegin(), psmall.cend()) == psmall.cend()); - const std::string nsmall("-0.001"); - BOOST_CHECK(is_valid::invoke(nsmall.cbegin(), nsmall.cend()) == nsmall.cend()); - } - { - const std::string zero("0.0"); - BOOST_CHECK(is_valid::invoke(zero.cbegin(), zero.cend()) == zero.cend()); - } - { - const std::string us("9_224_617.445_991_228_313"); - BOOST_CHECK(is_valid::invoke(us.cbegin(), us.cend()) == us.cend()); - } -} - -BOOST_AUTO_TEST_CASE(test_boolean) -{ - using is_valid = toml::is_boolean; - { - const std::string t("true"); - BOOST_CHECK(is_valid::invoke(t.cbegin(), t.cend()) == t.cend()); - const std::string f("false"); - BOOST_CHECK(is_valid::invoke(f.cbegin(), f.cend()) == f.cend()); - } - { - const std::string t("True"); - BOOST_CHECK(is_valid::invoke(t.cbegin(), t.cend()) == t.cbegin()); - const std::string f("False"); - BOOST_CHECK(is_valid::invoke(f.cbegin(), f.cend()) == f.cbegin()); - } -} - -BOOST_AUTO_TEST_CASE(test_localtime) -{ - using is_valid = toml::is_local_time; - { - const std::string t("07:32:00"); - BOOST_CHECK(is_valid::invoke(t.cbegin(), t.cend()) == t.cend()); - const std::string tf("07:32:00.0000"); - BOOST_CHECK(is_valid::invoke(tf.cbegin(), tf.cend()) == tf.cend()); - } - { - const std::string d("1907-32-00"); - BOOST_CHECK(is_valid::invoke(d.cbegin(), d.cend()) == d.cbegin()); - const std::string f("1907:32:00"); - BOOST_CHECK(is_valid::invoke(f.cbegin(), f.cend()) == f.cbegin()); - } -} - -BOOST_AUTO_TEST_CASE(test_localdate) -{ - using is_valid = toml::is_local_date; - { - const std::string d("1907-32-00"); - BOOST_CHECK(is_valid::invoke(d.cbegin(), d.cend()) == d.cend()); - } - { - const std::string t("07:32:00"); - BOOST_CHECK(is_valid::invoke(t.cbegin(), t.cend()) == t.cbegin()); - const std::string f("1907:32:00"); - BOOST_CHECK(is_valid::invoke(f.cbegin(),f.cend()) == f.cbegin()); - } -} - -BOOST_AUTO_TEST_CASE(test_localdatetime) -{ - using is_valid = toml::is_local_date_time; - { - const std::string dt("1907-32-00T07:32:00"); - BOOST_CHECK(is_valid::invoke(dt.cbegin(), dt.cend()) == dt.cend()); - const std::string dtf("1907-32-00T07:32:00.0000"); - BOOST_CHECK(is_valid::invoke(dtf.cbegin(), dtf.cend()) == dtf.cend()); - } - { - const std::string d("1907-32-00"); - BOOST_CHECK(is_valid::invoke(d.cbegin(), d.cend()) == d.cbegin()); - const std::string t("07:32:00"); - BOOST_CHECK(is_valid::invoke(t.cbegin(), t.cend()) == t.cbegin()); - const std::string f("1907-32-00 07:32:00"); - BOOST_CHECK(is_valid::invoke(f.cbegin(), f.cend()) == f.cbegin()); - } -} - -BOOST_AUTO_TEST_CASE(test_offsetdatetime) -{ - using is_valid = toml::is_offset_date_time; - { - const std::string dtZ("1907-32-00T07:32:00Z"); - BOOST_CHECK(is_valid::invoke(dtZ.cbegin(), dtZ.cend()) == dtZ.cend()); - const std::string dtfZ("1907-32-00T07:32:00.0000Z"); - BOOST_CHECK(is_valid::invoke(dtfZ.cbegin(), dtfZ.cend()) == dtfZ.cend()); - const std::string dtp("1907-32-00T07:32:00+12:34"); - BOOST_CHECK(is_valid::invoke(dtp.cbegin(), dtp.cend()) == dtp.cend()); - const std::string dtfp("1907-32-00T07:32:00.0000+12:34"); - BOOST_CHECK(is_valid::invoke(dtfp.cbegin(), dtfp.cend()) == dtfp.cend()); - const std::string dtn("1907-32-00T07:32:00-12:34"); - BOOST_CHECK(is_valid::invoke(dtn.cbegin(), dtn.cend()) == dtn.cend()); - const std::string dtfn("1907-32-00T07:32:00.0000-12:34"); - BOOST_CHECK(is_valid::invoke(dtfn.cbegin(), dtfn.cend()) == dtfn.cend()); - } - { - const std::string d("1907-32-00"); - BOOST_CHECK(is_valid::invoke(d.cbegin(), d.cend()) == d.cbegin()); - const std::string t("07:32:00"); - BOOST_CHECK(is_valid::invoke(t.cbegin(), t.cend()) == t.cbegin()); - const std::string l("1907-32-00T07:32:00"); - BOOST_CHECK(is_valid::invoke(l.cbegin(), l.cend()) == l.cbegin()); - } -} - -BOOST_AUTO_TEST_CASE(test_array) -{ - using is_valid = toml::is_array; - { - const std::string arr0("[]"); - BOOST_CHECK(is_valid::invoke(arr0.cbegin(), arr0.cend()) == arr0.cend()); - const std::string arr1("[1,2,3]"); - BOOST_CHECK(is_valid::invoke(arr1.cbegin(), arr1.cend()) == arr1.cend()); - const std::string arr2("[ 1,2,3 ]"); - BOOST_CHECK(is_valid::invoke(arr2.cbegin(), arr2.cend()) == arr2.cend()); - const std::string arr3("[ 1, 2, 3 ]"); - BOOST_CHECK(is_valid::invoke(arr3.cbegin(), arr3.cend()) == arr3.cend()); - const std::string arr4("[ 1, 2, 3, ]"); - BOOST_CHECK(is_valid::invoke(arr4.cbegin(), arr4.cend()) == arr4.cend()); - const std::string arr5("[ 1, 2, 3,]"); - BOOST_CHECK(is_valid::invoke(arr5.cbegin(), arr5.cend()) == arr5.cend()); - const std::string arr6("[ 1 , 2 , 3 ,]"); - BOOST_CHECK(is_valid::invoke(arr6.cbegin(), arr6.cend()) == arr6.cend()); - } - { - const std::string arr1("[\"red\", \"yellow\", \"green\"]"); - BOOST_CHECK(is_valid::invoke(arr1.cbegin(), arr1.cend()) == arr1.cend()); - const std::string arr2("[\"]\", \"#\", \" \"]"); - BOOST_CHECK(is_valid::invoke(arr2.cbegin(), arr2.cend()) == arr2.cend()); - const std::string arr3("[[1, 2, 3], ['a', 'b', 'c']]"); - BOOST_CHECK(is_valid::invoke(arr3.cbegin(), arr3.cend()) == arr3.cend()); - const std::string arr4("[{hoge = 1}, {piyo = 'a'}, {fuga = [1,2,3]}]"); - BOOST_CHECK(is_valid::invoke(arr4.cbegin(), arr4.cend()) == arr4.cend()); - } - { - const std::string arr1("[1,\n2,#comment\n3]"); - BOOST_CHECK(is_valid::invoke(arr1.cbegin(), arr1.cend()) == arr1.cend()); - const std::string arr2("[#c\n1,\n2,#comment\r\n3]"); - BOOST_CHECK(is_valid::invoke(arr2.cbegin(), arr2.cend()) == arr2.cend()); - } - - { - const std::string invalid("[1, 3.14, 'string']"); - BOOST_CHECK(is_valid::invoke(invalid.cbegin(), invalid.cend()) == invalid.cbegin()); - const std::string valid("[[1,2,3], [3.14, 2.71, 1.414], ['foo', 'bar']]"); - BOOST_CHECK(is_valid::invoke(valid.cbegin(), valid.cend()) == valid.cend()); - } - -} - -BOOST_AUTO_TEST_CASE(test_inline_table) -{ - using is_valid = toml::is_inline_table; - { - const std::string tab0("{}"); - BOOST_CHECK(is_valid::invoke(tab0.cbegin(), tab0.cend()) == tab0.cend()); - const std::string tab1("{hoge=1,piyo=2,fuga=3}"); - BOOST_CHECK(is_valid::invoke(tab1.cbegin(), tab1.cend()) == tab1.cend()); - const std::string tab2("{hoge=1, piyo=2, fuga=3}"); - BOOST_CHECK(is_valid::invoke(tab2.cbegin(), tab2.cend()) == tab2.cend()); - const std::string tab3("{ hoge=1, piyo=2, fuga=3 }"); - BOOST_CHECK(is_valid::invoke(tab3.cbegin(), tab3.cend()) == tab3.cend()); - const std::string tab4("{ hoge = 1, piyo = 2, fuga = 3 }"); - BOOST_CHECK(is_valid::invoke(tab4.cbegin(), tab4.cend()) == tab4.cend()); - const std::string tab5("{hoge = 1, piyo = 2, fuga = 3}"); - BOOST_CHECK(is_valid::invoke(tab5.cbegin(), tab5.cend()) == tab5.cend()); - const std::string tab6("{hoge = 1, piyo = 2, fuga = 3,}"); - BOOST_CHECK(is_valid::invoke(tab6.cbegin(), tab6.cend()) == tab6.cend()); - const std::string tab7("{hoge = 1, piyo = 2, fuga = 3, }"); - BOOST_CHECK(is_valid::invoke(tab7.cbegin(), tab7.cend()) == tab7.cend()); - } - { - const std::string tab0("{hoge = 1, piyo = 2.0}"); - BOOST_CHECK(is_valid::invoke(tab0.cbegin(), tab0.cend()) == tab0.cend()); - const std::string tab1("{hoge = [1,2,3], piyo = {fuga = {}}}"); - BOOST_CHECK(is_valid::invoke(tab1.cbegin(), tab1.cend()) == tab1.cend()); - const std::string tab2("{hoge = \"}\", piyo = \"#\"}"); - BOOST_CHECK(is_valid::invoke(tab2.cbegin(), tab2.cend()) == tab2.cend()); - const std::string tab3("{b=true, i=1, f=2.0, d=1907-03-02T07:32:00, s='str', a=[1,2,3], t={foo=1}}"); - BOOST_CHECK(is_valid::invoke(tab3.cbegin(), tab3.cend()) == tab3.cend()); - } - { - const std::string tab0("{hoge = \"}\",\n piyo = \"#\"}"); - BOOST_CHECK(is_valid::invoke(tab0.cbegin(), tab0.cend()) == tab0.cbegin()); - } -} - -BOOST_AUTO_TEST_CASE(test_table_definition) -{ - using is_valid = toml::is_table_definition; - { - const std::string simple("[hoge]"); - BOOST_CHECK(is_valid::invoke(simple.cbegin(), simple.cend()) == simple.cend()); - const std::string dotted("[hoge.piyo.fuga]"); - BOOST_CHECK(is_valid::invoke(dotted.cbegin(), dotted.cend()) == dotted.cend()); - const std::string spaced_dotted("[hoge . piyo .fuga. foo]"); - BOOST_CHECK(is_valid::invoke(spaced_dotted.cbegin(), spaced_dotted.cend()) == spaced_dotted.cend()); - const std::string quoted("[\"hoge\"]"); - BOOST_CHECK(is_valid::invoke(quoted.cbegin(), quoted.cend()) == quoted.cend()); - const std::string quoted_dot("[\"hoge\".'piyo'.fuga]"); - BOOST_CHECK(is_valid::invoke(quoted_dot.cbegin(), quoted_dot.cend()) == quoted_dot.cend()); - } -} - -BOOST_AUTO_TEST_CASE(test_array_of_table_definition) -{ - using is_valid = toml::is_array_of_table_definition; - { - const std::string simple("[[hoge]]"); - BOOST_CHECK(is_valid::invoke(simple.cbegin(), simple.cend()) == simple.cend()); - const std::string dotted("[[hoge.piyo.fuga]]"); - BOOST_CHECK(is_valid::invoke(dotted.cbegin(), dotted.cend()) == dotted.cend()); - const std::string spaced_dotted("[[hoge . piyo .fuga. foo]]"); - BOOST_CHECK(is_valid::invoke(spaced_dotted.cbegin(), spaced_dotted.cend()) == spaced_dotted.cend()); - const std::string quoted("[[\"hoge\"]]"); - BOOST_CHECK(is_valid::invoke(quoted.cbegin(), quoted.cend()) == quoted.cend()); - const std::string quoted_dot("[[\"hoge\".'piyo'.fuga]]"); - BOOST_CHECK(is_valid::invoke(quoted_dot.cbegin(), quoted_dot.cend()) == quoted_dot.cend()); - } -} - -BOOST_AUTO_TEST_CASE(test_key) -{ - using is_valid = toml::is_key; - { - const std::string simple("foobar"); - BOOST_CHECK(is_valid::invoke(simple.cbegin(), simple.cend()) == simple.cend()); - const std::string quoted("\"foo#bar.baz\\n\""); - BOOST_CHECK(is_valid::invoke(quoted.cbegin(), quoted.cend()) == quoted.cend()); - } -} - -BOOST_AUTO_TEST_CASE(test_value) -{ - using is_valid = toml::is_value; - { - const std::string boolean("true"); - BOOST_CHECK(is_valid::invoke(boolean.cbegin(), boolean.cend()) == boolean.cend()); - const std::string integer("-42"); - BOOST_CHECK(is_valid::invoke(integer.cbegin(), integer.cend()) == integer.cend()); - const std::string floating("-42e0"); - BOOST_CHECK(is_valid::invoke(floating.cbegin(), floating.cend()) == floating.cend()); - const std::string string("\"string\""); - BOOST_CHECK(is_valid::invoke(string.cbegin(), string.cend()) == string.cend()); - const std::string datetime("1901-01-01T00:00:00"); - BOOST_CHECK(is_valid::invoke(datetime.cbegin(), datetime.cend()) == datetime.cend()); - const std::string array("[1,2,3]"); - BOOST_CHECK(is_valid::invoke(array.cbegin(), array.cend()) == array.cend()); - const std::string table("{foo=1,bar=2.0,baz='3'}"); - BOOST_CHECK(is_valid::invoke(table.cbegin(), table.cend()) == table.cend()); - } -} - -BOOST_AUTO_TEST_CASE(test_key_value_pair) -{ - using is_valid = toml::is_key_value_pair; - { - const std::string kv("key=1"); - BOOST_CHECK(is_valid::invoke(kv.cbegin(), kv.cend()) == kv.cend()); - } - { - const std::string kv("key = 1"); - BOOST_CHECK(is_valid::invoke(kv.cbegin(), kv.cend()) == kv.cend()); - } - { - const std::string kv(" key = 1"); - BOOST_CHECK(is_valid::invoke(kv.cbegin(), kv.cend()) == kv.cend()); - } - { - const std::string kv(" key = 1 "); - BOOST_CHECK(is_valid::invoke(kv.cbegin(), kv.cend()) == kv.cend()); - } - { - const std::string boolean("key = true"); - BOOST_CHECK(is_valid::invoke(boolean.cbegin(), boolean.cend()) == boolean.cend()); - const std::string integer("key = -42"); - BOOST_CHECK(is_valid::invoke(integer.cbegin(), integer.cend()) == integer.cend()); - const std::string floating("key = -42.0"); - BOOST_CHECK(is_valid::invoke(floating.cbegin(), floating.cend()) == floating.cend()); - const std::string string("key = \"string\""); - BOOST_CHECK(is_valid::invoke(string.cbegin(), string.cend()) == string.cend()); - const std::string datetime("key = 1901-01-01T00:00:00"); - BOOST_CHECK(is_valid::invoke(datetime.cbegin(), datetime.cend()) == datetime.cend()); - const std::string array("key = [1,2,3]"); - BOOST_CHECK(is_valid::invoke(array.cbegin(), array.cend()) == array.cend()); - const std::string table("key = {foo=1,bar=2.0,baz='3'}"); - BOOST_CHECK(is_valid::invoke(table.cbegin(), table.cend()) == table.cend()); - } -} - -BOOST_AUTO_TEST_CASE(test_empty_line) -{ - using is_valid = toml::is_empty_line; - { - const std::string empty("\n"); - BOOST_CHECK(is_valid::invoke(empty.cbegin(), empty.cend()) == empty.cend()); - } - { - const std::string empty(" \n"); - BOOST_CHECK(is_valid::invoke(empty.cbegin(), empty.cend()) == empty.cend()); - } - { - const std::string empty("#comment\n"); - BOOST_CHECK(is_valid::invoke(empty.cbegin(), empty.cend()) == empty.cend()); - } - { - const std::string empty(" #comment\n"); - BOOST_CHECK(is_valid::invoke(empty.cbegin(), empty.cend()) == empty.cend()); - } -} diff --git a/tests/test_datetime.cpp b/tests/test_datetime.cpp index b96bbb3f..ae5ae515 100644 --- a/tests/test_datetime.cpp +++ b/tests/test_datetime.cpp @@ -5,26 +5,117 @@ #define BOOST_TEST_NO_LIB #include #endif -#include -#include +#include -BOOST_AUTO_TEST_CASE(test_datetime_convertible) +BOOST_AUTO_TEST_CASE(test_local_date) { - const auto now = std::chrono::system_clock::now(); - toml::Datetime d1(now); - const std::chrono::system_clock::time_point cvt(d1); - toml::Datetime d2(cvt); + const toml::local_date date(2018, toml::month_t::Jan, 1); + const toml::local_date date1(date); + BOOST_CHECK_EQUAL(date, date1); - BOOST_CHECK_EQUAL(d1, d2); + const std::chrono::system_clock::time_point tp(date); + const toml::local_date date2(tp); + BOOST_CHECK_EQUAL(date, date2); - const auto time = std::chrono::system_clock::to_time_t(now); - toml::Datetime d3(time); - toml::Datetime d4(std::chrono::system_clock::from_time_t(time)); + const toml::local_date date3(2017, toml::month_t::Dec, 31); + BOOST_CHECK(date > date3); - BOOST_CHECK_EQUAL(d3, d4); + std::ostringstream oss; + oss << date; + BOOST_CHECK_EQUAL(oss.str(), std::string("2018-01-01")); +} + +BOOST_AUTO_TEST_CASE(test_local_time) +{ + const toml::local_time time(12, 30, 45); + const toml::local_time time1(time); + BOOST_CHECK_EQUAL(time, time1); + + const std::chrono::nanoseconds dur(time); + std::chrono::nanoseconds ns(0); + ns += std::chrono::hours (12); + ns += std::chrono::minutes(30); + ns += std::chrono::seconds(45); + BOOST_CHECK_EQUAL(dur.count(), ns.count()); + + const toml::local_time time3(12, 15, 45); + BOOST_CHECK(time > time3); + + { + std::ostringstream oss; + oss << time; + BOOST_CHECK_EQUAL(oss.str(), std::string("12:30:45")); + } + + { + const toml::local_time time4(12, 30, 45, 123, 456); + std::ostringstream oss; + oss << time4; + BOOST_CHECK_EQUAL(oss.str(), std::string("12:30:45.123456")); + } +} + +BOOST_AUTO_TEST_CASE(test_time_offset) +{ + const toml::time_offset time(9, 30); + const toml::time_offset time1(time); + BOOST_CHECK_EQUAL(time, time1); + + const std::chrono::minutes dur(time); + std::chrono::minutes m(0); + m += std::chrono::hours (9); + m += std::chrono::minutes(30); + BOOST_CHECK_EQUAL(dur.count(), m.count()); + + const toml::time_offset time2(9, 0); + BOOST_CHECK(time2 < time); + + std::ostringstream oss; + oss << time; + BOOST_CHECK_EQUAL(oss.str(), std::string("+09:30")); +} + +BOOST_AUTO_TEST_CASE(test_local_datetime) +{ + const toml::local_datetime dt(toml::local_date(2018, toml::month_t::Jan, 1), + toml::local_time(12, 30, 45)); + const toml::local_datetime dt1(dt); + BOOST_CHECK_EQUAL(dt, dt1); + + const std::chrono::system_clock::time_point tp(dt); + const toml::local_datetime dt2(tp); + BOOST_CHECK_EQUAL(dt, dt2); + + std::ostringstream oss; + oss << dt; + BOOST_CHECK_EQUAL(oss.str(), std::string("2018-01-01T12:30:45")); +} + +BOOST_AUTO_TEST_CASE(test_offset_datetime) +{ + const toml::offset_datetime dt(toml::local_date(2018, toml::month_t::Jan, 1), + toml::local_time(12, 30, 45), + toml::time_offset(9, 30)); + const toml::offset_datetime dt1(dt); + BOOST_CHECK_EQUAL(dt, dt1); + + const std::chrono::system_clock::time_point tp1(dt); + const toml::offset_datetime dt2(tp1); + const std::chrono::system_clock::time_point tp2(dt2); + BOOST_CHECK(tp1 == tp2); - std::this_thread::sleep_for(std::chrono::seconds(1)); - const auto later = std::chrono::system_clock::now(); - toml::Datetime d5(later); - BOOST_CHECK(d1 < d5); + { + std::ostringstream oss; + oss << dt; + BOOST_CHECK_EQUAL(oss.str(), std::string("2018-01-01T12:30:45+09:30")); + } + { + const toml::offset_datetime dt3( + toml::local_date(2018, toml::month_t::Jan, 1), + toml::local_time(12, 30, 45), + toml::time_offset(0, 0)); + std::ostringstream oss; + oss << dt3; + BOOST_CHECK_EQUAL(oss.str(), std::string("2018-01-01T12:30:45Z")); + } } diff --git a/tests/test_from_toml.cpp b/tests/test_from_toml.cpp index b3953b3f..45cfac5d 100644 --- a/tests/test_from_toml.cpp +++ b/tests/test_from_toml.cpp @@ -12,180 +12,52 @@ #include #include -BOOST_AUTO_TEST_CASE(test_from_toml_exact) +BOOST_AUTO_TEST_CASE(test_from_toml) { - toml::Boolean b(true); - toml::Integer i(42); - toml::Float f(3.14); - toml::String s("hoge"); - toml::Datetime d(std::chrono::system_clock::now()); - toml::Array a; - a.emplace_back(2); - a.emplace_back(7); - a.emplace_back(1); - a.emplace_back(8); - a.emplace_back(2); - toml::Table t; - t.emplace("val1", true); - t.emplace("val2", 42); - t.emplace("val3", 3.14); - t.emplace("val4", "piyo"); - - toml::value v1(b); - toml::value v2(i); - toml::value v3(f); - toml::value v4(s); - toml::value v5(d); - toml::value v6(a); - toml::value v7(t); - - toml::Boolean u1; - toml::Integer u2; - toml::Float u3; - toml::String u4; - toml::Datetime u5; - toml::Array u6; - toml::Table u7; - - toml::from_toml(u1, v1); - toml::from_toml(u2, v2); - toml::from_toml(u3, v3); - toml::from_toml(u4, v4); - toml::from_toml(u5, v5); - toml::from_toml(u6, v6); - toml::from_toml(u7, v7); - - BOOST_CHECK_EQUAL(u1, b); - BOOST_CHECK_EQUAL(u2, i); - BOOST_CHECK_EQUAL(u3, f); - BOOST_CHECK_EQUAL(u4, s); - BOOST_CHECK_EQUAL(u6.at(0).cast(), a.at(0).cast()); - BOOST_CHECK_EQUAL(u6.at(1).cast(), a.at(1).cast()); - BOOST_CHECK_EQUAL(u6.at(2).cast(), a.at(2).cast()); - BOOST_CHECK_EQUAL(u6.at(3).cast(), a.at(3).cast()); - BOOST_CHECK_EQUAL(u6.at(4).cast(), a.at(4).cast()); - BOOST_CHECK_EQUAL(u7.at("val1").cast(), true); - BOOST_CHECK_EQUAL(u7.at("val2").cast(), 42); - BOOST_CHECK_CLOSE_FRACTION(u7.at("val3").cast(),3.14, 1e-3); - BOOST_CHECK_EQUAL(u7.at("val4").cast(), "piyo"); -} - -BOOST_AUTO_TEST_CASE(test_from_toml_cast) -{ - toml::Integer i(42); - toml::Float f(3.14); - toml::Array a{2, 7, 1, 8, 2}; - toml::Table t{{"val1", true}, {"val2", 42}, {"val3", 3.14}, {"val4", "piyo"}}; - - toml::value vi(i); - toml::value vf(f); - toml::value va(a); - toml::value vt(t); - - int u1; - std::size_t u2; - float u3; - std::list u4; - std::deque u5; - std::array u6; - std::map u7; - - std::list expect_list{2,7,1,8,2}; - std::deque expect_deque{2,7,1,8,2}; - std::array expect_array{{2,7,1,8,2}}; - - toml::from_toml(u1, vi); - toml::from_toml(u2, vi); - toml::from_toml(u3, vf); - toml::from_toml(u4, va); - toml::from_toml(u5, va); - toml::from_toml(u6, va); - toml::from_toml(u7, vt); - - BOOST_CHECK_EQUAL(u1, 42); - BOOST_CHECK_EQUAL(u2, 42ul); - BOOST_CHECK_CLOSE_FRACTION(u3, 3.14, 1e-3); - - const bool same_list = (u4 == expect_list); - const bool same_deque = (u5 == expect_deque); - const bool same_array = (u6 == expect_array); - BOOST_CHECK(same_list); - BOOST_CHECK(same_deque); - BOOST_CHECK(same_array); - - BOOST_CHECK_EQUAL(u7["val1"].cast(), true); - BOOST_CHECK_EQUAL(u7["val2"].cast(), 42); - BOOST_CHECK_CLOSE_FRACTION(u7["val3"].cast(), 3.14, 1e-3); - BOOST_CHECK_EQUAL(u7["val4"].cast(), "piyo"); - + toml::boolean b = false; + toml::integer i = 0; + toml::floating f = 0.; + toml::string s; + toml::local_date dt; + toml::array a; + toml::table t; + { + toml::value v(true); + toml::from_toml(std::tie(b, i, f, s, dt, a, t), v); + BOOST_CHECK_EQUAL(b, true); + } + { + toml::value v(42); + toml::from_toml(std::tie(b, i, f, s, dt, a, t), v); + BOOST_CHECK_EQUAL(i, 42); + } + { + toml::value v(3.14); + toml::from_toml(std::tie(b, i, f, s, dt, a, t), v); + BOOST_CHECK_EQUAL(f, 3.14); + } + { + toml::value v("foo"); + + toml::from_toml(std::tie(b, i, f, s, dt, a, t), v); + BOOST_CHECK_EQUAL(s, "foo"); + } + { + toml::value v(toml::local_date(2018, toml::month_t::Apr, 22)); + toml::from_toml(std::tie(b, i, f, s, dt, a, t), v); + BOOST_CHECK(dt == toml::local_date(2018, toml::month_t::Apr, 22)); + } + { + toml::array ref{toml::value(42), toml::value(54)}; + toml::value v(ref); + toml::from_toml(std::tie(b, i, f, s, dt, a, t), v); + BOOST_CHECK(ref == a); + } + { + toml::table ref{{"key1", 42}, {"key2", 3.14}}; + toml::value v(ref); + toml::from_toml(std::tie(b, i, f, s, dt, a, t), v); + BOOST_CHECK(ref == t); + } } -BOOST_AUTO_TEST_CASE(test_from_toml_tie) -{ - toml::Boolean b(42); - toml::Integer i(42); - toml::Float f(3.14); - toml::Array a; - a.emplace_back(2); - a.emplace_back(7); - a.emplace_back(1); - a.emplace_back(8); - a.emplace_back(2); - toml::Table t; - t.emplace("val1", true); - t.emplace("val2", 42); - t.emplace("val3", 3.14); - t.emplace("val4", "piyo"); - - toml::value vb(b); - toml::value vi(i); - toml::value vf(f); - toml::value va(a); - toml::value vt(t); - - bool ub; - int ui; - float uf; - std::deque ua; - std::map ut; - - toml::from_toml(std::tie(ub, ui, uf, ua, ut), vb); - toml::from_toml(std::tie(ub, ui, uf, ua, ut), vi); - toml::from_toml(std::tie(ub, ui, uf, ua, ut), vf); - toml::from_toml(std::tie(ub, ui, uf, ua, ut), va); - toml::from_toml(std::tie(ub, ui, uf, ua, ut), va); - toml::from_toml(std::tie(ub, ui, uf, ua, ut), vt); - - BOOST_CHECK_EQUAL(ub, true); - BOOST_CHECK_EQUAL(ui, 42); - BOOST_CHECK_CLOSE_FRACTION(uf, 3.14, 1e-3); - BOOST_CHECK_EQUAL(ua.at(0), 2); - BOOST_CHECK_EQUAL(ua.at(1), 7); - BOOST_CHECK_EQUAL(ua.at(2), 1); - BOOST_CHECK_EQUAL(ua.at(3), 8); - BOOST_CHECK_EQUAL(ua.at(4), 2); - BOOST_CHECK_EQUAL(ut["val1"].cast(), true); - BOOST_CHECK_EQUAL(ut["val2"].cast(), 42); - BOOST_CHECK_CLOSE_FRACTION(ut["val3"].cast(), 3.14, 1e-3); - BOOST_CHECK_EQUAL(ut["val4"].cast(), "piyo"); -} - -BOOST_AUTO_TEST_CASE(test_from_toml_tuple) -{ - toml::Array a; - a.emplace_back(2); - a.emplace_back(7); - a.emplace_back(1); - a.emplace_back(8); - a.emplace_back(2); - toml::value v(a); - - std::tuple t; - toml::from_toml(t, v); - - BOOST_CHECK_EQUAL(std::get<0>(t), 2); - BOOST_CHECK_EQUAL(std::get<1>(t), 7); - BOOST_CHECK_EQUAL(std::get<2>(t), 1); - BOOST_CHECK_EQUAL(std::get<3>(t), 8); - BOOST_CHECK_EQUAL(std::get<4>(t), 2); -} diff --git a/tests/test_get.cpp b/tests/test_get.cpp index 728efabb..3dc753c6 100644 --- a/tests/test_get.cpp +++ b/tests/test_get.cpp @@ -5,7 +5,8 @@ #define BOOST_TEST_NO_LIB #include #endif -#include +#include +#include #include #include #include @@ -15,98 +16,350 @@ BOOST_AUTO_TEST_CASE(test_get_exact) { - toml::Boolean b(true); - toml::Integer i(42); - toml::Float f(3.14); - toml::String s("hoge"); - toml::Array a; - a.emplace_back(2); - a.emplace_back(7); - a.emplace_back(1); - a.emplace_back(8); - a.emplace_back(2); - toml::Table t; - t.emplace("val1", true); - t.emplace("val2", 42); - t.emplace("val3", 3.14); - t.emplace("val4", "piyo"); - - toml::value v1(b); - toml::value v2(i); - toml::value v3(f); - toml::value v4(s); - toml::value v6(a); - toml::value v7(t); - - toml::Boolean u1 = toml::get(v1); - toml::Integer u2 = toml::get(v2); - toml::Float u3 = toml::get(v3); - toml::String u4 = toml::get(v4); - toml::Array u6 = toml::get(v6); - toml::Table u7 = toml::get(v7); - - BOOST_CHECK_EQUAL(u1, b); - BOOST_CHECK_EQUAL(u2, i); - BOOST_CHECK_EQUAL(u3, f); - BOOST_CHECK_EQUAL(u4, s); - BOOST_CHECK_EQUAL(u6.at(0).cast(), a.at(0).cast()); - BOOST_CHECK_EQUAL(u6.at(1).cast(), a.at(1).cast()); - BOOST_CHECK_EQUAL(u6.at(2).cast(), a.at(2).cast()); - BOOST_CHECK_EQUAL(u6.at(3).cast(), a.at(3).cast()); - BOOST_CHECK_EQUAL(u6.at(4).cast(), a.at(4).cast()); - BOOST_CHECK_EQUAL(u7.at("val1").cast(), true); - BOOST_CHECK_EQUAL(u7.at("val2").cast(), 42); - BOOST_CHECK_CLOSE_FRACTION(u7.at("val3").cast(),3.14, 1e-3); - BOOST_CHECK_EQUAL(u7.at("val4").cast(), "piyo"); + { + toml::value v(true); + BOOST_CHECK_EQUAL(true, toml::get(v)); + + toml::get(v) = false; + BOOST_CHECK_EQUAL(false, toml::get(v)); + } + { + toml::value v(42); + BOOST_CHECK_EQUAL(toml::integer(42), toml::get(v)); + + toml::get(v) = 54; + BOOST_CHECK_EQUAL(toml::integer(54), toml::get(v)); + } + { + toml::value v(3.14); + BOOST_CHECK_EQUAL(toml::floating(3.14), toml::get(v)); + + toml::get(v) = 2.71; + BOOST_CHECK_EQUAL(toml::floating(2.71), toml::get(v)); + } + { + toml::value v("foo"); + BOOST_CHECK_EQUAL(toml::string("foo", toml::string_t::basic), + toml::get(v)); + + toml::get(v).str += "bar"; + BOOST_CHECK_EQUAL(toml::string("foobar", toml::string_t::basic), + toml::get(v)); + } + { + toml::value v("foo", toml::string_t::literal); + BOOST_CHECK_EQUAL(toml::string("foo", toml::string_t::literal), + toml::get(v)); + + toml::get(v).str += "bar"; + BOOST_CHECK_EQUAL(toml::string("foobar", toml::string_t::literal), + toml::get(v)); + } + { + toml::local_date d(2018, toml::month_t::Apr, 22); + toml::value v(d); + BOOST_CHECK(d == toml::get(v)); + + toml::get(v).year = 2017; + d.year = 2017; + BOOST_CHECK(d == toml::get(v)); + } + { + toml::local_time t(12, 30, 45); + toml::value v(t); + BOOST_CHECK(t == toml::get(v)); + + toml::get(v).hour = 9; + t.hour = 9; + BOOST_CHECK(t == toml::get(v)); + } + { + toml::local_datetime dt(toml::local_date(2018, toml::month_t::Apr, 22), + toml::local_time(12, 30, 45)); + toml::value v(dt); + BOOST_CHECK(dt == toml::get(v)); + + toml::get(v).date.year = 2017; + dt.date.year = 2017; + BOOST_CHECK(dt == toml::get(v)); + } + { + toml::offset_datetime dt(toml::local_datetime( + toml::local_date(2018, toml::month_t::Apr, 22), + toml::local_time(12, 30, 45)), toml::time_offset(9, 0)); + toml::value v(dt); + BOOST_CHECK(dt == toml::get(v)); + + toml::get(v).date.year = 2017; + dt.date.year = 2017; + BOOST_CHECK(dt == toml::get(v)); + } + { + toml::array vec; + vec.push_back(toml::value(42)); + vec.push_back(toml::value(54)); + toml::value v(vec); + BOOST_CHECK(vec == toml::get(v)); + + toml::get(v).push_back(toml::value(123)); + vec.push_back(toml::value(123)); + BOOST_CHECK(vec == toml::get(v)); + } + { + toml::table tab; + tab["key1"] = toml::value(42); + tab["key2"] = toml::value(3.14); + toml::value v(tab); + BOOST_CHECK(tab == toml::get(v)); + + toml::get(v)["key3"] = toml::value(123); + tab["key3"] = toml::value(123); + BOOST_CHECK(tab == toml::get(v)); + } +} + +BOOST_AUTO_TEST_CASE(test_get_integer_type) +{ + { + toml::value v(42); + BOOST_CHECK_EQUAL(int(42), toml::get(v)); + BOOST_CHECK_EQUAL(short(42), toml::get(v)); + BOOST_CHECK_EQUAL(char(42), toml::get(v)); + BOOST_CHECK_EQUAL(unsigned(42), toml::get(v)); + BOOST_CHECK_EQUAL(long(42), toml::get(v)); + BOOST_CHECK_EQUAL(std::int64_t(42), toml::get(v)); + BOOST_CHECK_EQUAL(std::uint64_t(42), toml::get(v)); + BOOST_CHECK_EQUAL(std::int16_t(42), toml::get(v)); + BOOST_CHECK_EQUAL(std::uint16_t(42), toml::get(v)); + } +} + +BOOST_AUTO_TEST_CASE(test_get_floating_type) +{ + { + toml::value v(3.14); + BOOST_CHECK_EQUAL(static_cast(3.14), toml::get(v)); + BOOST_CHECK_EQUAL(static_cast(3.14), toml::get(v)); + BOOST_CHECK_EQUAL(static_cast(3.14), toml::get(v)); + } +} + +BOOST_AUTO_TEST_CASE(test_get_string_type) +{ + { + toml::value v("foo", toml::string_t::basic); + BOOST_CHECK_EQUAL("foo", toml::get(v)); + toml::get(v) += "bar"; + BOOST_CHECK_EQUAL("foobar", toml::get(v)); + } + { + toml::value v("foo", toml::string_t::literal); + BOOST_CHECK_EQUAL("foo", toml::get(v)); + toml::get(v) += "bar"; + BOOST_CHECK_EQUAL("foobar", toml::get(v)); + } +} + +BOOST_AUTO_TEST_CASE(test_get_toml_array) +{ + toml::value v(toml::array(0)); + toml::get(v).push_back(toml::value(42)); + toml::get(v).push_back(toml::value(54)); + toml::get(v).push_back(toml::value(69)); + toml::get(v).push_back(toml::value(72)); + + const std::vector vec = toml::get>(v); + const std::list lst = toml::get>(v); + const std::deque deq = toml::get>(v); + + BOOST_CHECK_EQUAL(42, vec.at(0)); + BOOST_CHECK_EQUAL(54, vec.at(1)); + BOOST_CHECK_EQUAL(69, vec.at(2)); + BOOST_CHECK_EQUAL(72, vec.at(3)); + + std::list::const_iterator iter = lst.begin(); + BOOST_CHECK_EQUAL(static_cast(42), *(iter++)); + BOOST_CHECK_EQUAL(static_cast(54), *(iter++)); + BOOST_CHECK_EQUAL(static_cast(69), *(iter++)); + BOOST_CHECK_EQUAL(static_cast(72), *(iter++)); + + BOOST_CHECK_EQUAL(static_cast(42), deq.at(0)); + BOOST_CHECK_EQUAL(static_cast(54), deq.at(1)); + BOOST_CHECK_EQUAL(static_cast(69), deq.at(2)); + BOOST_CHECK_EQUAL(static_cast(72), deq.at(3)); + + std::array ary = toml::get>(v); + BOOST_CHECK_EQUAL(static_cast(42), ary.at(0)); + BOOST_CHECK_EQUAL(static_cast(54), ary.at(1)); + BOOST_CHECK_EQUAL(static_cast(69), ary.at(2)); + BOOST_CHECK_EQUAL(static_cast(72), ary.at(3)); + + std::tuple tpl = + toml::get>(v); + BOOST_CHECK_EQUAL(static_cast(42), std::get<0>(tpl)); + BOOST_CHECK_EQUAL(static_cast(54), std::get<1>(tpl)); + BOOST_CHECK_EQUAL(static_cast(69), std::get<2>(tpl)); + BOOST_CHECK_EQUAL(static_cast(72), std::get<3>(tpl)); + + toml::value p(toml::array{}); + toml::get(p).push_back(toml::value(3.14)); + toml::get(p).push_back(toml::value(2.71)); + std::pair pr = toml::get >(p); + BOOST_CHECK_EQUAL(3.14, pr.first); + BOOST_CHECK_EQUAL(2.71, pr.second); +} + +BOOST_AUTO_TEST_CASE(test_get_toml_array_of_array) +{ + toml::value v1(toml::array{}); + toml::get(v1).push_back(toml::value(42)); + toml::get(v1).push_back(toml::value(54)); + toml::get(v1).push_back(toml::value(69)); + toml::get(v1).push_back(toml::value(72)); + + toml::value v2(toml::array{}); + toml::get(v2).push_back(toml::value("foo")); + toml::get(v2).push_back(toml::value("bar")); + toml::get(v2).push_back(toml::value("baz")); + + toml::value v(toml::array(2)); + toml::get(v).at(0) = v1; + toml::get(v).at(1) = v2; + + std::pair, std::vector> p = + toml::get, std::vector>>(v); + + BOOST_CHECK_EQUAL(p.first.at(0), 42); + BOOST_CHECK_EQUAL(p.first.at(1), 54); + BOOST_CHECK_EQUAL(p.first.at(2), 69); + BOOST_CHECK_EQUAL(p.first.at(3), 72); + + BOOST_CHECK_EQUAL(p.second.at(0), "foo"); + BOOST_CHECK_EQUAL(p.second.at(1), "bar"); + BOOST_CHECK_EQUAL(p.second.at(2), "baz"); + + std::tuple, std::vector> t = + toml::get, std::vector>>(v); + + BOOST_CHECK_EQUAL(std::get<0>(t).at(0), 42); + BOOST_CHECK_EQUAL(std::get<0>(t).at(1), 54); + BOOST_CHECK_EQUAL(std::get<0>(t).at(2), 69); + BOOST_CHECK_EQUAL(std::get<0>(t).at(3), 72); + + BOOST_CHECK_EQUAL(std::get<1>(t).at(0), "foo"); + BOOST_CHECK_EQUAL(std::get<1>(t).at(1), "bar"); + BOOST_CHECK_EQUAL(std::get<1>(t).at(2), "baz"); +} + +BOOST_AUTO_TEST_CASE(test_get_toml_table) +{ + toml::value v1(toml::table{ + {"key1", 1}, + {"key2", 2}, + {"key3", 3}, + {"key4", 4} + }); + + const auto v = toml::get>(v1); + BOOST_CHECK_EQUAL(v.at("key1"), 1); + BOOST_CHECK_EQUAL(v.at("key2"), 2); + BOOST_CHECK_EQUAL(v.at("key3"), 3); + BOOST_CHECK_EQUAL(v.at("key4"), 4); } -BOOST_AUTO_TEST_CASE(test_get_cast) +BOOST_AUTO_TEST_CASE(test_get_toml_local_date) { - toml::Integer i(42); - toml::Float f(3.14); - toml::String s("hoge"); - toml::Datetime d(std::chrono::system_clock::now()); - toml::Array a; - a.emplace_back(2); - a.emplace_back(7); - a.emplace_back(1); - a.emplace_back(8); - a.emplace_back(2); - toml::Table t; - t.emplace("val1", true); - t.emplace("val2", 42); - t.emplace("val3", 3.14); - t.emplace("val4", "piyo"); - - toml::value v2(i); - toml::value v3(f); - toml::value v4(s); - toml::value v5(d); - toml::value v6(a); - toml::value v7(t); - - const auto u2 = toml::get(v2); - const auto u3 = toml::get(v3); - const auto u4 = toml::get>(v6); - const auto u5 = toml::get >(v6); - const auto u6 = toml::get>(v6); - std::map u7 = toml::get>(v7); - - std::deque r4{2,7,1,8,2}; - std::list r5{2,7,1,8,2}; - std::array r6{{2,7,1,8,2}}; - - BOOST_CHECK_EQUAL(u2, 42ul); - BOOST_CHECK_CLOSE_FRACTION(u3, 3.14, 1e-3); - const bool dq = r4 == u4; - const bool ls = r5 == u5; - const bool ar = r6 == u6; - BOOST_CHECK(dq); - BOOST_CHECK(ls); - BOOST_CHECK(ar); - BOOST_CHECK_EQUAL(u7.at("val1").cast(), true); - BOOST_CHECK_EQUAL(u7.at("val2").cast(), 42); - BOOST_CHECK_CLOSE_FRACTION(u7.at("val3").cast(),3.14, 1e-3); - BOOST_CHECK_EQUAL(u7.at("val4").cast(), "piyo"); + toml::value v1(toml::local_date{2018, toml::month_t::Apr, 1}); + const auto date = std::chrono::system_clock::to_time_t( + toml::get(v1)); + + std::tm t; + t.tm_year = 2018 - 1900; + t.tm_mon = 4 - 1; + t.tm_mday = 1; + t.tm_hour = 0; + t.tm_min = 0; + t.tm_sec = 0; + t.tm_isdst = -1; + const auto c = std::mktime(&t); + BOOST_CHECK_EQUAL(c, date); } + +BOOST_AUTO_TEST_CASE(test_get_toml_local_time) +{ + toml::value v1(toml::local_time{12, 30, 45}); + const auto time = toml::get(v1); + BOOST_CHECK(time == std::chrono::hours(12) + + std::chrono::minutes(30) + std::chrono::seconds(45)); +} + +BOOST_AUTO_TEST_CASE(test_get_toml_local_datetime) +{ + toml::value v1(toml::local_datetime( + toml::local_date{2018, toml::month_t::Apr, 1}, + toml::local_time{12, 30, 45})); + + const auto date = std::chrono::system_clock::to_time_t( + toml::get(v1)); + std::tm t; + t.tm_year = 2018 - 1900; + t.tm_mon = 4 - 1; + t.tm_mday = 1; + t.tm_hour = 12; + t.tm_min = 30; + t.tm_sec = 45; + t.tm_isdst = -1; + const auto c = std::mktime(&t); + BOOST_CHECK_EQUAL(c, date); +} + +BOOST_AUTO_TEST_CASE(test_get_toml_offset_datetime) +{ + { + toml::value v1(toml::offset_datetime( + toml::local_date{2018, toml::month_t::Apr, 1}, + toml::local_time{12, 30, 0}, + toml::time_offset{9, 0})); + // 2018-04-01T12:30:00+09:00 + // == 2018-04-01T03:30:00Z + + const auto date = toml::get(v1); + const auto timet = std::chrono::system_clock::to_time_t(date); + + // get time_t as gmtime (2018-04-01T03:30:00Z) + const auto tmp = std::gmtime(std::addressof(timet)); // XXX not threadsafe! + BOOST_CHECK(tmp); + const auto tm = *tmp; + BOOST_CHECK_EQUAL(tm.tm_year + 1900, 2018); + BOOST_CHECK_EQUAL(tm.tm_mon + 1, 4); + BOOST_CHECK_EQUAL(tm.tm_mday, 1); + BOOST_CHECK_EQUAL(tm.tm_hour, 3); + BOOST_CHECK_EQUAL(tm.tm_min, 30); + BOOST_CHECK_EQUAL(tm.tm_sec, 0); + } + + { + toml::value v1(toml::offset_datetime( + toml::local_date{2018, toml::month_t::Apr, 1}, + toml::local_time{12, 30, 0}, + toml::time_offset{-8, 0})); + // 2018-04-01T12:30:00-08:00 + // == 2018-04-01T20:30:00Z + + const auto date = toml::get(v1); + const auto timet = std::chrono::system_clock::to_time_t(date); + + // get time_t as gmtime (2018-04-01T03:30:00Z) + const auto tmp = std::gmtime(std::addressof(timet)); // XXX not threadsafe! + BOOST_CHECK(tmp); + const auto tm = *tmp; + BOOST_CHECK_EQUAL(tm.tm_year + 1900, 2018); + BOOST_CHECK_EQUAL(tm.tm_mon + 1, 4); + BOOST_CHECK_EQUAL(tm.tm_mday, 1); + BOOST_CHECK_EQUAL(tm.tm_hour, 20); + BOOST_CHECK_EQUAL(tm.tm_min, 30); + BOOST_CHECK_EQUAL(tm.tm_sec, 0); + } +} + diff --git a/tests/test_get_or.cpp b/tests/test_get_or.cpp deleted file mode 100644 index 4ca863f0..00000000 --- a/tests/test_get_or.cpp +++ /dev/null @@ -1,88 +0,0 @@ -#define BOOST_TEST_MODULE "test_get_or" -#ifdef UNITTEST_FRAMEWORK_LIBRARY_EXIST -#include -#else -#define BOOST_TEST_NO_LIB -#include -#endif -#include -#include -#include -#include -#include -#include - - -BOOST_AUTO_TEST_CASE(test_get_or_exist) -{ - toml::Boolean raw_v1(true); - toml::Integer raw_v2(42); - toml::Float raw_v3(3.14); - toml::String raw_v4("hoge"); - toml::Array raw_v5{2,7,1,8,2}; - toml::Table raw_v6{{"key", 42}}; - - toml::value v1(raw_v1); - toml::value v2(raw_v2); - toml::value v3(raw_v3); - toml::value v4(raw_v4); - toml::value v5(raw_v5); - toml::value v6(raw_v6); - - toml::Table table{ - {"value1", v1}, - {"value2", v2}, - {"value3", v3}, - {"value4", v4}, - {"value5", v5}, - {"value6", v6} - }; - - toml::Boolean u1 = toml::get_or(table, "value1", raw_v1); - toml::Integer u2 = toml::get_or(table, "value2", raw_v2); - toml::Float u3 = toml::get_or(table, "value3", raw_v3); - toml::String u4 = toml::get_or(table, "value4", raw_v4); - toml::Array u5 = toml::get_or(table, "value5", raw_v5); - toml::Table u6 = toml::get_or(table, "value6", raw_v6); - - BOOST_CHECK_EQUAL(u1, raw_v1); - BOOST_CHECK_EQUAL(u2, raw_v2); - BOOST_CHECK_EQUAL(u3, raw_v3); - BOOST_CHECK_EQUAL(u4, raw_v4); - BOOST_CHECK_EQUAL(u5.at(0).cast(), raw_v5.at(0).cast()); - BOOST_CHECK_EQUAL(u5.at(1).cast(), raw_v5.at(1).cast()); - BOOST_CHECK_EQUAL(u5.at(2).cast(), raw_v5.at(2).cast()); - BOOST_CHECK_EQUAL(u5.at(3).cast(), raw_v5.at(3).cast()); - BOOST_CHECK_EQUAL(u5.at(4).cast(), raw_v5.at(4).cast()); - BOOST_CHECK_EQUAL(u6.at("key").cast(), 42); -} - -BOOST_AUTO_TEST_CASE(test_get_or_empty) -{ - toml::Boolean raw_v1(true); - toml::Integer raw_v2(42); - toml::Float raw_v3(3.14); - toml::String raw_v4("hoge"); - toml::Array raw_v5{2,7,1,8,2}; - toml::Table raw_v6{{"key", 42}}; - - toml::Table table; // empty! - - toml::Boolean u1 = toml::get_or(table, std::string("value1"), raw_v1); - toml::Integer u2 = toml::get_or(table, std::string("value2"), raw_v2); - toml::Float u3 = toml::get_or(table, std::string("value3"), raw_v3); - toml::String u4 = toml::get_or(table, std::string("value4"), raw_v4); - toml::Array u5 = toml::get_or(table, std::string("value5"), raw_v5); - toml::Table u6 = toml::get_or(table, std::string("value6"), raw_v6); - - BOOST_CHECK_EQUAL(u1, raw_v1); - BOOST_CHECK_EQUAL(u2, raw_v2); - BOOST_CHECK_EQUAL(u3, raw_v3); - BOOST_CHECK_EQUAL(u4, raw_v4); - BOOST_CHECK_EQUAL(u5.at(0).cast(), raw_v5.at(0).cast()); - BOOST_CHECK_EQUAL(u5.at(1).cast(), raw_v5.at(1).cast()); - BOOST_CHECK_EQUAL(u5.at(2).cast(), raw_v5.at(2).cast()); - BOOST_CHECK_EQUAL(u5.at(3).cast(), raw_v5.at(3).cast()); - BOOST_CHECK_EQUAL(u5.at(4).cast(), raw_v5.at(4).cast()); - BOOST_CHECK_EQUAL(u6.at("key").cast(), 42); -} diff --git a/tests/test_get_related_func.cpp b/tests/test_get_related_func.cpp new file mode 100644 index 00000000..2feebdde --- /dev/null +++ b/tests/test_get_related_func.cpp @@ -0,0 +1,76 @@ +#define BOOST_TEST_MODULE "test_get_or" +#ifdef UNITTEST_FRAMEWORK_LIBRARY_EXIST +#include +#else +#define BOOST_TEST_NO_LIB +#include +#endif +#include +#include +#include +#include +#include +#include + +BOOST_AUTO_TEST_CASE(test_find) +{ + { + toml::value v(true); + bool thrown = false; + try + { + toml::find(v, "key"); + } + catch(toml::type_error const& te) + { + thrown = true; + } + BOOST_CHECK(thrown); + } + + { + toml::table v{{"num", 42}}; + BOOST_CHECK_EQUAL(42, toml::find(v, "num")); + toml::find(v, "num") = 54; + BOOST_CHECK_EQUAL(54, toml::find(v, "num")); + } + + { + toml::value v = toml::table{{"num", 42}}; + BOOST_CHECK_EQUAL(42, toml::find(v, "num")); + toml::find(v, "num") = 54; + BOOST_CHECK_EQUAL(54, toml::find(v, "num")); + } +} + +BOOST_AUTO_TEST_CASE(test_get_or) +{ + { + toml::table v{{"num", 42}}; + BOOST_CHECK_EQUAL(42, toml::get_or(v, "num", 0)); + BOOST_CHECK_EQUAL(0, toml::get_or(v, "foo", 0)); + } + { + toml::value v = toml::table{{"num", 42}}; + BOOST_CHECK_EQUAL(42, toml::get_or(v, "num", 0)); + BOOST_CHECK_EQUAL(0, toml::get_or(v, "foo", 0)); + } + { + toml::value v1(42); + toml::value v2(3.14); + BOOST_CHECK_EQUAL(42, toml::get_or(v1, 0)); + BOOST_CHECK_EQUAL(0, toml::get_or(v2, 0)); + } +} + +BOOST_AUTO_TEST_CASE(test_expect) +{ + { + toml::value v1(42); + toml::value v2(3.14); + BOOST_CHECK_EQUAL(42, toml::expect(v1).unwrap_or(0)); + BOOST_CHECK_EQUAL( 0, toml::expect(v2).unwrap_or(0)); + BOOST_CHECK_EQUAL("42", toml::expect(v1).map([](int i){return std::to_string(i);}).unwrap_or(std::string("none"))); + BOOST_CHECK_EQUAL("none", toml::expect(v2).map([](int i){return std::to_string(i);}).unwrap_or(std::string("none"))); + } +} diff --git a/tests/test_lex_aux.hpp b/tests/test_lex_aux.hpp new file mode 100644 index 00000000..3778f275 --- /dev/null +++ b/tests/test_lex_aux.hpp @@ -0,0 +1,35 @@ +#include +#include +#include +#include +#include + +#define TOML11_TEST_LEX_ACCEPT(lxr, tkn, expct) \ +do { \ + const std::string token (tkn); \ + const std::string expected(expct); \ + toml::detail::location loc("test", token); \ + const auto result = lxr::invoke(loc); \ + BOOST_CHECK(result.is_ok()); \ + if(result.is_ok()){ \ + const auto region = result.unwrap(); \ + BOOST_CHECK_EQUAL(region.str(), expected); \ + BOOST_CHECK_EQUAL(region.str().size(), expected.size()); \ + BOOST_CHECK_EQUAL(static_cast(std::distance( \ + loc.begin(), loc.iter())), region.size()); \ + } else { \ + std::cerr << "lexer " << lxr::pattern() << " failed with input `"; \ + std::cerr << token << "`. expected `" << expected << "`\n"; \ + std::cerr << "reason: " << result.unwrap_err() << '\n'; \ + } \ +} while(false); \ +/**/ + +#define TOML11_TEST_LEX_REJECT(lxr, tkn) \ +do { \ + const std::string token (tkn); \ + toml::detail::location loc("test", token); \ + const auto result = lxr::invoke(loc); \ + BOOST_CHECK(result.is_err()); \ + BOOST_CHECK(loc.begin() == loc.iter()); \ +} while(false); /**/ diff --git a/tests/test_lex_boolean.cpp b/tests/test_lex_boolean.cpp new file mode 100644 index 00000000..c2e43a87 --- /dev/null +++ b/tests/test_lex_boolean.cpp @@ -0,0 +1,23 @@ +#define BOOST_TEST_MODULE "test_lex_boolean" +#include +#include +#include "test_lex_aux.hpp" + +using namespace toml; +using namespace detail; + +BOOST_AUTO_TEST_CASE(test_correct) +{ + TOML11_TEST_LEX_ACCEPT(lex_boolean, "true", "true"); + TOML11_TEST_LEX_ACCEPT(lex_boolean, "false", "false"); + TOML11_TEST_LEX_ACCEPT(lex_boolean, "true # trailing", "true"); + TOML11_TEST_LEX_ACCEPT(lex_boolean, "false # trailing", "false"); +} + +BOOST_AUTO_TEST_CASE(test_invalid) +{ + TOML11_TEST_LEX_REJECT(lex_boolean, "TRUE"); + TOML11_TEST_LEX_REJECT(lex_boolean, "FALSE"); + TOML11_TEST_LEX_REJECT(lex_boolean, "True"); + TOML11_TEST_LEX_REJECT(lex_boolean, "False"); +} diff --git a/tests/test_lex_datetime.cpp b/tests/test_lex_datetime.cpp new file mode 100644 index 00000000..a790eebc --- /dev/null +++ b/tests/test_lex_datetime.cpp @@ -0,0 +1,57 @@ +#define BOOST_TEST_MODULE "test_lex_datetime" +#include +#include +#include "test_lex_aux.hpp" + +using namespace toml; +using namespace detail; + +BOOST_AUTO_TEST_CASE(test_offset_datetime) +{ + TOML11_TEST_LEX_ACCEPT(lex_offset_date_time, + "1979-05-27T07:32:00Z", + "1979-05-27T07:32:00Z"); + TOML11_TEST_LEX_ACCEPT(lex_offset_date_time, + "1979-05-27T07:32:00-07:00", + "1979-05-27T07:32:00-07:00"); + TOML11_TEST_LEX_ACCEPT(lex_offset_date_time, + "1979-05-27T07:32:00.999999-07:00", + "1979-05-27T07:32:00.999999-07:00"); + + TOML11_TEST_LEX_ACCEPT(lex_offset_date_time, + "1979-05-27 07:32:00Z", + "1979-05-27 07:32:00Z"); + TOML11_TEST_LEX_ACCEPT(lex_offset_date_time, + "1979-05-27 07:32:00-07:00", + "1979-05-27 07:32:00-07:00"); + TOML11_TEST_LEX_ACCEPT(lex_offset_date_time, + "1979-05-27 07:32:00.999999-07:00", + "1979-05-27 07:32:00.999999-07:00"); +} + +BOOST_AUTO_TEST_CASE(test_local_datetime) +{ + TOML11_TEST_LEX_ACCEPT(lex_local_date_time, + "1979-05-27T07:32:00", + "1979-05-27T07:32:00"); + TOML11_TEST_LEX_ACCEPT(lex_local_date_time, + "1979-05-27T07:32:00.999999", + "1979-05-27T07:32:00.999999"); + + TOML11_TEST_LEX_ACCEPT(lex_local_date_time, + "1979-05-27 07:32:00", + "1979-05-27 07:32:00"); + TOML11_TEST_LEX_ACCEPT(lex_local_date_time, + "1979-05-27 07:32:00.999999", + "1979-05-27 07:32:00.999999"); +} + +BOOST_AUTO_TEST_CASE(test_local_date) +{ + TOML11_TEST_LEX_ACCEPT(lex_local_date, "1979-05-27", "1979-05-27"); +} +BOOST_AUTO_TEST_CASE(test_local_time) +{ + TOML11_TEST_LEX_ACCEPT(lex_local_time, "07:32:00", "07:32:00"); + TOML11_TEST_LEX_ACCEPT(lex_local_time, "07:32:00.999999", "07:32:00.999999"); +} diff --git a/tests/test_lex_floating.cpp b/tests/test_lex_floating.cpp new file mode 100644 index 00000000..79ce157d --- /dev/null +++ b/tests/test_lex_floating.cpp @@ -0,0 +1,84 @@ +#define BOOST_TEST_MODULE "test_lex_floating" +#include +#include +#include +#include "test_lex_aux.hpp" + +using namespace toml; +using namespace detail; + +BOOST_AUTO_TEST_CASE(test_fractional_valid) +{ + TOML11_TEST_LEX_ACCEPT(lex_float, "1.0", "1.0" ); + TOML11_TEST_LEX_ACCEPT(lex_float, "0.1", "0.1" ); + TOML11_TEST_LEX_ACCEPT(lex_float, "0.001", "0.001" ); + TOML11_TEST_LEX_ACCEPT(lex_float, "0.100", "0.100" ); + TOML11_TEST_LEX_ACCEPT(lex_float, "+3.14", "+3.14" ); + TOML11_TEST_LEX_ACCEPT(lex_float, "-3.14", "-3.14" ); + TOML11_TEST_LEX_ACCEPT(lex_float, "3.1415_9265_3589", "3.1415_9265_3589" ); + TOML11_TEST_LEX_ACCEPT(lex_float, "+3.1415_9265_3589", "+3.1415_9265_3589"); + TOML11_TEST_LEX_ACCEPT(lex_float, "-3.1415_9265_3589", "-3.1415_9265_3589"); + TOML11_TEST_LEX_ACCEPT(lex_float, "123_456.789", "123_456.789" ); + TOML11_TEST_LEX_ACCEPT(lex_float, "+123_456.789", "+123_456.789" ); + TOML11_TEST_LEX_ACCEPT(lex_float, "-123_456.789", "-123_456.789" ); +} + +BOOST_AUTO_TEST_CASE(test_fractional_invalid) +{ + TOML11_TEST_LEX_REJECT(lex_float, "0."); + TOML11_TEST_LEX_REJECT(lex_float, ".0"); + TOML11_TEST_LEX_REJECT(lex_float, "01.0"); + TOML11_TEST_LEX_REJECT(lex_float, "3,14"); + TOML11_TEST_LEX_REJECT(lex_float, "+-1.0"); + TOML11_TEST_LEX_REJECT(lex_float, "1._0"); +} + +BOOST_AUTO_TEST_CASE(test_exponential_valid) +{ + TOML11_TEST_LEX_ACCEPT(lex_float, "1e10", "1e10"); + TOML11_TEST_LEX_ACCEPT(lex_float, "1e+10", "1e+10"); + TOML11_TEST_LEX_ACCEPT(lex_float, "1e-10", "1e-10"); + TOML11_TEST_LEX_ACCEPT(lex_float, "+1e10", "+1e10"); + TOML11_TEST_LEX_ACCEPT(lex_float, "+1e+10", "+1e+10"); + TOML11_TEST_LEX_ACCEPT(lex_float, "+1e-10", "+1e-10"); + TOML11_TEST_LEX_ACCEPT(lex_float, "-1e10", "-1e10"); + TOML11_TEST_LEX_ACCEPT(lex_float, "-1e+10", "-1e+10"); + TOML11_TEST_LEX_ACCEPT(lex_float, "-1e-10", "-1e-10"); + TOML11_TEST_LEX_ACCEPT(lex_float, "123e-10", "123e-10"); + TOML11_TEST_LEX_ACCEPT(lex_float, "1E10", "1E10"); + TOML11_TEST_LEX_ACCEPT(lex_float, "1E+10", "1E+10"); + TOML11_TEST_LEX_ACCEPT(lex_float, "1E-10", "1E-10"); + TOML11_TEST_LEX_ACCEPT(lex_float, "123E-10", "123E-10"); + TOML11_TEST_LEX_ACCEPT(lex_float, "1_2_3E-10", "1_2_3E-10"); + TOML11_TEST_LEX_ACCEPT(lex_float, "1_2_3E-1_0", "1_2_3E-1_0"); +} + +BOOST_AUTO_TEST_CASE(test_exponential_invalid) +{ + TOML11_TEST_LEX_ACCEPT(lex_float, "1e1E0", "1e1"); + TOML11_TEST_LEX_ACCEPT(lex_float, "1E1e0", "1E1"); +} + +BOOST_AUTO_TEST_CASE(test_both_valid) +{ + TOML11_TEST_LEX_ACCEPT(lex_float, "6.02e23", "6.02e23"); + TOML11_TEST_LEX_ACCEPT(lex_float, "6.02e+23", "6.02e+23"); + TOML11_TEST_LEX_ACCEPT(lex_float, "1.112_650_06e-17", "1.112_650_06e-17"); +} + +BOOST_AUTO_TEST_CASE(test_both_invalid) +{ + TOML11_TEST_LEX_ACCEPT(lex_float, "1e1.0", "1e1"); + TOML11_TEST_LEX_REJECT(lex_float, "01e1.0"); +} + +BOOST_AUTO_TEST_CASE(test_special_floating_point) +{ + TOML11_TEST_LEX_ACCEPT(lex_float, "inf", "inf"); + TOML11_TEST_LEX_ACCEPT(lex_float, "+inf", "+inf"); + TOML11_TEST_LEX_ACCEPT(lex_float, "-inf", "-inf"); + + TOML11_TEST_LEX_ACCEPT(lex_float, "nan", "nan"); + TOML11_TEST_LEX_ACCEPT(lex_float, "+nan", "+nan"); + TOML11_TEST_LEX_ACCEPT(lex_float, "-nan", "-nan"); +} diff --git a/tests/test_lex_integer.cpp b/tests/test_lex_integer.cpp new file mode 100644 index 00000000..bb6dbb7a --- /dev/null +++ b/tests/test_lex_integer.cpp @@ -0,0 +1,101 @@ +#define BOOST_TEST_MODULE "test_lex_integer" +#include +#include +#include "test_lex_aux.hpp" + +using namespace toml; +using namespace detail; + +BOOST_AUTO_TEST_CASE(test_decimal_correct) +{ + TOML11_TEST_LEX_ACCEPT(lex_integer, "1234", "1234" ); + TOML11_TEST_LEX_ACCEPT(lex_integer, "+1234", "+1234" ); + TOML11_TEST_LEX_ACCEPT(lex_integer, "-1234", "-1234" ); + TOML11_TEST_LEX_ACCEPT(lex_integer, "0", "0" ); + TOML11_TEST_LEX_ACCEPT(lex_integer, "1_2_3_4", "1_2_3_4" ); + TOML11_TEST_LEX_ACCEPT(lex_integer, "+1_2_3_4", "+1_2_3_4" ); + TOML11_TEST_LEX_ACCEPT(lex_integer, "-1_2_3_4", "-1_2_3_4" ); + TOML11_TEST_LEX_ACCEPT(lex_integer, "123_456_789", "123_456_789"); +} + +BOOST_AUTO_TEST_CASE(test_decimal_invalid) +{ + TOML11_TEST_LEX_ACCEPT(lex_integer, "123+45", "123"); + TOML11_TEST_LEX_ACCEPT(lex_integer, "123-45", "123"); + TOML11_TEST_LEX_ACCEPT(lex_integer, "01234", "0"); + TOML11_TEST_LEX_ACCEPT(lex_integer, "123__45", "123"); + + TOML11_TEST_LEX_REJECT(lex_integer, "_1234"); +} + +BOOST_AUTO_TEST_CASE(test_hex_correct) +{ + TOML11_TEST_LEX_ACCEPT(lex_integer, "0xDEADBEEF", "0xDEADBEEF" ); + TOML11_TEST_LEX_ACCEPT(lex_integer, "0xdeadbeef", "0xdeadbeef" ); + TOML11_TEST_LEX_ACCEPT(lex_integer, "0xDEADbeef", "0xDEADbeef" ); + TOML11_TEST_LEX_ACCEPT(lex_integer, "0xDEAD_BEEF", "0xDEAD_BEEF"); + TOML11_TEST_LEX_ACCEPT(lex_integer, "0xdead_beef", "0xdead_beef"); + TOML11_TEST_LEX_ACCEPT(lex_integer, "0xdead_BEEF", "0xdead_BEEF"); + + TOML11_TEST_LEX_ACCEPT(lex_integer, "0xFF", "0xFF" ); + TOML11_TEST_LEX_ACCEPT(lex_integer, "0x00FF", "0x00FF" ); + TOML11_TEST_LEX_ACCEPT(lex_integer, "0x0000FF", "0x0000FF"); +} + +BOOST_AUTO_TEST_CASE(test_hex_invalid) +{ + TOML11_TEST_LEX_ACCEPT(lex_integer, "0xAPPLE", "0xA"); + TOML11_TEST_LEX_ACCEPT(lex_integer, "0xDEAD+BEEF", "0xDEAD"); + TOML11_TEST_LEX_ACCEPT(lex_integer, "0xDEAD__BEEF", "0xDEAD"); + + TOML11_TEST_LEX_REJECT(lex_hex_int, "0x_DEADBEEF"); + TOML11_TEST_LEX_REJECT(lex_hex_int, "0x+DEADBEEF"); + TOML11_TEST_LEX_REJECT(lex_hex_int, "-0xFF" ); + TOML11_TEST_LEX_REJECT(lex_hex_int, "-0x00FF" ); + + TOML11_TEST_LEX_ACCEPT(lex_integer, "0x_DEADBEEF", "0" ); + TOML11_TEST_LEX_ACCEPT(lex_integer, "0x+DEADBEEF", "0" ); + TOML11_TEST_LEX_ACCEPT(lex_integer, "-0xFF" , "-0" ); + TOML11_TEST_LEX_ACCEPT(lex_integer, "-0x00FF" , "-0" ); +} + +BOOST_AUTO_TEST_CASE(test_oct_correct) +{ + TOML11_TEST_LEX_ACCEPT(lex_integer, "0o777", "0o777" ); + TOML11_TEST_LEX_ACCEPT(lex_integer, "0o7_7_7", "0o7_7_7"); + TOML11_TEST_LEX_ACCEPT(lex_integer, "0o007", "0o007" ); + +} + +BOOST_AUTO_TEST_CASE(test_oct_invalid) +{ + TOML11_TEST_LEX_ACCEPT(lex_integer, "0o77+7", "0o77"); + TOML11_TEST_LEX_ACCEPT(lex_integer, "0o1__0", "0o1"); + + TOML11_TEST_LEX_REJECT(lex_oct_int, "0o800" ); + TOML11_TEST_LEX_REJECT(lex_oct_int, "-0o777"); + TOML11_TEST_LEX_REJECT(lex_oct_int, "0o+777"); + TOML11_TEST_LEX_REJECT(lex_oct_int, "0o_10" ); + + TOML11_TEST_LEX_ACCEPT(lex_integer, "0o800", "0"); + TOML11_TEST_LEX_ACCEPT(lex_integer, "-0o777", "-0"); + TOML11_TEST_LEX_ACCEPT(lex_integer, "0o+777", "0"); + TOML11_TEST_LEX_ACCEPT(lex_integer, "0o_10", "0"); +} + +BOOST_AUTO_TEST_CASE(test_bin_correct) +{ + TOML11_TEST_LEX_ACCEPT(lex_integer, "0b10000", "0b10000" ); + TOML11_TEST_LEX_ACCEPT(lex_integer, "0b010000", "0b010000" ); + TOML11_TEST_LEX_ACCEPT(lex_integer, "0b01_00_00", "0b01_00_00"); + TOML11_TEST_LEX_ACCEPT(lex_integer, "0b111111", "0b111111" ); +} + +BOOST_AUTO_TEST_CASE(test_bin_invalid) +{ + TOML11_TEST_LEX_ACCEPT(lex_bin_int, "0b11__11", "0b11"); + TOML11_TEST_LEX_ACCEPT(lex_bin_int, "0b11+11" , "0b11"); + + TOML11_TEST_LEX_REJECT(lex_bin_int, "-0b10000"); + TOML11_TEST_LEX_REJECT(lex_bin_int, "0b_1111" ); +} diff --git a/tests/test_lex_key_comment.cpp b/tests/test_lex_key_comment.cpp new file mode 100644 index 00000000..be4b1762 --- /dev/null +++ b/tests/test_lex_key_comment.cpp @@ -0,0 +1,53 @@ +#define BOOST_TEST_MODULE "lex_key_comment_test" +#include +#include +#include "test_lex_aux.hpp" + +using namespace toml; +using namespace detail; + +BOOST_AUTO_TEST_CASE(test_bare_key) +{ + TOML11_TEST_LEX_ACCEPT(lex_key, "barekey", "barekey"); + TOML11_TEST_LEX_ACCEPT(lex_key, "bare-key", "bare-key"); + TOML11_TEST_LEX_ACCEPT(lex_key, "bare_key", "bare_key"); + TOML11_TEST_LEX_ACCEPT(lex_key, "1234", "1234"); +} + +BOOST_AUTO_TEST_CASE(test_quoted_key) +{ + TOML11_TEST_LEX_ACCEPT(lex_key, "\"127.0.0.1\"", "\"127.0.0.1\""); + TOML11_TEST_LEX_ACCEPT(lex_key, "\"character encoding\"", "\"character encoding\""); +#if defined(_MSC_VER) || defined(__INTEL_COMPILER) + TOML11_TEST_LEX_ACCEPT(lex_key, "\"\xCA\x8E\xC7\x9D\xCA\x9E\"", + "\"\xCA\x8E\xC7\x9D\xCA\x9E\""); +#else + TOML11_TEST_LEX_ACCEPT(lex_key, u8"\"ʎǝʞ\"", u8"\"ʎǝʞ\""); +#endif + TOML11_TEST_LEX_ACCEPT(lex_key, "'key2'", "'key2'"); + TOML11_TEST_LEX_ACCEPT(lex_key, "'quoted \"value\"'", "'quoted \"value\"'"); +} + +BOOST_AUTO_TEST_CASE(test_dotted_key) +{ + TOML11_TEST_LEX_ACCEPT(lex_key, "physical.color", "physical.color"); + TOML11_TEST_LEX_ACCEPT(lex_key, "physical.shape", "physical.shape"); + TOML11_TEST_LEX_ACCEPT(lex_key, "x.y", "x.y"); + TOML11_TEST_LEX_ACCEPT(lex_key, "x . y", "x . y"); + TOML11_TEST_LEX_ACCEPT(lex_key, "x.y.z", "x.y.z"); + TOML11_TEST_LEX_ACCEPT(lex_key, "x. y .z", "x. y .z"); + TOML11_TEST_LEX_ACCEPT(lex_key, "x .y. z", "x .y. z"); + TOML11_TEST_LEX_ACCEPT(lex_key, "x . y . z", "x . y . z"); + TOML11_TEST_LEX_ACCEPT(lex_key, "x.y.z.w", "x.y.z.w"); + TOML11_TEST_LEX_ACCEPT(lex_key, "x. y .z. w", "x. y .z. w"); + TOML11_TEST_LEX_ACCEPT(lex_key, "x . y . z . w", "x . y . z . w"); + TOML11_TEST_LEX_ACCEPT(lex_key, "site.\"google.com\"", "site.\"google.com\""); +} + +BOOST_AUTO_TEST_CASE(test_comment) +{ + TOML11_TEST_LEX_ACCEPT(lex_comment, "# hoge", "# hoge"); + TOML11_TEST_LEX_ACCEPT(lex_comment, "# \n", "# "); + TOML11_TEST_LEX_ACCEPT(lex_comment, "# \r\n", "# "); + TOML11_TEST_LEX_ACCEPT(lex_comment, "# # \n", "# # "); +} diff --git a/tests/test_lex_string.cpp b/tests/test_lex_string.cpp new file mode 100644 index 00000000..fcf07272 --- /dev/null +++ b/tests/test_lex_string.cpp @@ -0,0 +1,86 @@ +#define BOOST_TEST_MODULE "test_lex_string" +#include +#include +#include "test_lex_aux.hpp" + +using namespace toml; +using namespace detail; + +BOOST_AUTO_TEST_CASE(test_string) +{ + TOML11_TEST_LEX_ACCEPT(lex_string, + "\"The quick brown fox jumps over the lazy dog\"", + "\"The quick brown fox jumps over the lazy dog\""); + TOML11_TEST_LEX_ACCEPT(lex_string, + "\'The quick brown fox jumps over the lazy dog\'", + "\'The quick brown fox jumps over the lazy dog\'"); + TOML11_TEST_LEX_ACCEPT(lex_ml_basic_string, + "\"\"\"The quick brown fox \\\njumps over the lazy dog\"\"\"", + "\"\"\"The quick brown fox \\\njumps over the lazy dog\"\"\""); + TOML11_TEST_LEX_ACCEPT(lex_ml_literal_string, + "'''The quick brown fox \njumps over the lazy dog'''", + "'''The quick brown fox \njumps over the lazy dog'''"); +} + +BOOST_AUTO_TEST_CASE(test_basic_string) +{ + TOML11_TEST_LEX_ACCEPT(lex_string, + "\"GitHub Cofounder & CEO\\nLikes tater tots and beer.\"", + "\"GitHub Cofounder & CEO\\nLikes tater tots and beer.\""); + TOML11_TEST_LEX_ACCEPT(lex_string, + "\"192.168.1.1\"", + "\"192.168.1.1\""); + +#if defined(_MSC_VER) || defined(__INTEL_COMPILER) + TOML11_TEST_LEX_ACCEPT(lex_string, + "\"\xE4\xB8\xAD\xE5\x9B\xBD\"", + "\"\xE4\xB8\xAD\xE5\x9B\xBD\""); +#else + TOML11_TEST_LEX_ACCEPT(lex_string, + u8"\"中国\"", + u8"\"中国\""); +#endif + + TOML11_TEST_LEX_ACCEPT(lex_string, + "\"You'll hate me after this - #\"", + "\"You'll hate me after this - #\""); + TOML11_TEST_LEX_ACCEPT(lex_string, + "\" And when \\\"'s are in the string, along with # \\\"\"", + "\" And when \\\"'s are in the string, along with # \\\"\""); +} + +BOOST_AUTO_TEST_CASE(test_ml_basic_string) +{ + TOML11_TEST_LEX_ACCEPT(lex_string, + "\"\"\"\nThe quick brown \\\n\n fox jumps over \\\n the lazy dog.\"\"\"", + "\"\"\"\nThe quick brown \\\n\n fox jumps over \\\n the lazy dog.\"\"\""); + TOML11_TEST_LEX_ACCEPT(lex_string, + "\"\"\"\\\n The quick brown \\\n\n fox jumps over \\\n the lazy dog.\\\n \"\"\"", + "\"\"\"\\\n The quick brown \\\n\n fox jumps over \\\n the lazy dog.\\\n \"\"\""); +} + +BOOST_AUTO_TEST_CASE(test_literal_string) +{ + TOML11_TEST_LEX_ACCEPT(lex_string, + "'C:\\Users\\nodejs\\templates'", + "'C:\\Users\\nodejs\\templates'"); + TOML11_TEST_LEX_ACCEPT(lex_string, + "'\\\\ServerX\\admin$\\system32\\'", + "'\\\\ServerX\\admin$\\system32\\'"); + TOML11_TEST_LEX_ACCEPT(lex_string, + "'Tom \"Dubs\" Preston-Werner'", + "'Tom \"Dubs\" Preston-Werner'"); + TOML11_TEST_LEX_ACCEPT(lex_string, + "'<\\i\\c*\\s*>'", + "'<\\i\\c*\\s*>'"); +} + +BOOST_AUTO_TEST_CASE(test_ml_literal_string) +{ + TOML11_TEST_LEX_ACCEPT(lex_string, + "'''I [dw]on't need \\d{2} apples'''", + "'''I [dw]on't need \\d{2} apples'''"); + TOML11_TEST_LEX_ACCEPT(lex_string, + "'''\nThe first newline is\ntrimmed in raw strings.\n All other whitespace\n is preserved.\n'''", + "'''\nThe first newline is\ntrimmed in raw strings.\n All other whitespace\n is preserved.\n'''"); +} diff --git a/tests/test_parse_array.cpp b/tests/test_parse_array.cpp new file mode 100644 index 00000000..6350c3a0 --- /dev/null +++ b/tests/test_parse_array.cpp @@ -0,0 +1,130 @@ +#define BOOST_TEST_MODULE "parse_array_test" +#ifdef UNITTEST_FRAMEWORK_LIBRARY_EXIST +#include +#else +#define BOOST_TEST_NO_LIB +#include +#endif +#include +#include "test_parse_aux.hpp" + +using namespace toml; +using namespace detail; + +BOOST_AUTO_TEST_CASE(test_oneline_array) +{ + TOML11_TEST_PARSE_EQUAL(parse_array, "[]", array()); + { + array a(5); + a[0] = toml::value(3); a[1] = toml::value(1); a[2] = toml::value(4); + a[3] = toml::value(1); a[4] = toml::value(5); + TOML11_TEST_PARSE_EQUAL(parse_array, "[3,1,4,1,5]", a); + } + { + array a(3); + a[0] = toml::value("foo"); a[1] = toml::value("bar"); + a[2] = toml::value("baz"); + TOML11_TEST_PARSE_EQUAL(parse_array, "[\"foo\", \"bar\", \"baz\"]", a); + } + { + array a(5); + a[0] = toml::value(3); a[1] = toml::value(1); a[2] = toml::value(4); + a[3] = toml::value(1); a[4] = toml::value(5); + TOML11_TEST_PARSE_EQUAL(parse_array, "[3,1,4,1,5,]", a); + } + { + array a(3); + a[0] = toml::value("foo"); a[1] = toml::value("bar"); + a[2] = toml::value("baz"); + TOML11_TEST_PARSE_EQUAL(parse_array, "[\"foo\", \"bar\", \"baz\",]", a); + } +} + +BOOST_AUTO_TEST_CASE(test_oneline_array_value) +{ + TOML11_TEST_PARSE_EQUAL_VALUE(parse_value, "[]", toml::value(array())); + { + array a(5); + a[0] = toml::value(3); a[1] = toml::value(1); a[2] = toml::value(4); + a[3] = toml::value(1); a[4] = toml::value(5); + TOML11_TEST_PARSE_EQUAL_VALUE(parse_value, "[3,1,4,1,5]", toml::value(a)); + } + { + array a(3); + a[0] = toml::value("foo"); a[1] = toml::value("bar"); + a[2] = toml::value("baz"); + TOML11_TEST_PARSE_EQUAL_VALUE(parse_value, "[\"foo\", \"bar\", \"baz\"]", toml::value(a)); + } + { + array a(5); + a[0] = toml::value(3); a[1] = toml::value(1); a[2] = toml::value(4); + a[3] = toml::value(1); a[4] = toml::value(5); + TOML11_TEST_PARSE_EQUAL_VALUE(parse_value, "[3,1,4,1,5,]", toml::value(a)); + } + { + array a(3); + a[0] = toml::value("foo"); a[1] = toml::value("bar"); + a[2] = toml::value("baz"); + TOML11_TEST_PARSE_EQUAL_VALUE(parse_value, "[\"foo\", \"bar\", \"baz\",]", toml::value(a)); + } +} + +BOOST_AUTO_TEST_CASE(test_multiline_array) +{ + TOML11_TEST_PARSE_EQUAL(parse_array, "[\n#comment\n]", array()); + { + array a(5); + a[0] = toml::value(3); a[1] = toml::value(1); a[2] = toml::value(4); + a[3] = toml::value(1); a[4] = toml::value(5); + TOML11_TEST_PARSE_EQUAL(parse_array, "[3,\n1,\n4,\n1,\n5]", a); + } + { + array a(3); + a[0] = toml::value("foo"); a[1] = toml::value("bar"); + a[2] = toml::value("baz"); + TOML11_TEST_PARSE_EQUAL(parse_array, "[\"foo\",\n\"bar\",\n\"baz\"]", a); + } + + { + array a(5); + a[0] = toml::value(3); a[1] = toml::value(1); a[2] = toml::value(4); + a[3] = toml::value(1); a[4] = toml::value(5); + TOML11_TEST_PARSE_EQUAL(parse_array, "[3,#comment\n1,#comment\n4,#comment\n1,#comment\n5]", a); + } + { + array a(3); + a[0] = toml::value("foo"); a[1] = toml::value("b#r"); + a[2] = toml::value("b#z"); + TOML11_TEST_PARSE_EQUAL(parse_array, "[\"foo\",#comment\n\"b#r\",#comment\n\"b#z\"#comment\n]", a); + } +} + +BOOST_AUTO_TEST_CASE(test_multiline_array_value) +{ + TOML11_TEST_PARSE_EQUAL_VALUE(parse_value, "[\n#comment\n]", toml::value(array())); + { + array a(5); + a[0] = toml::value(3); a[1] = toml::value(1); a[2] = toml::value(4); + a[3] = toml::value(1); a[4] = toml::value(5); + TOML11_TEST_PARSE_EQUAL_VALUE(parse_value, "[3,\n1,\n4,\n1,\n5]", toml::value(a)); + } + { + array a(3); + a[0] = toml::value("foo"); a[1] = toml::value("bar"); + a[2] = toml::value("baz"); + TOML11_TEST_PARSE_EQUAL_VALUE(parse_value, "[\"foo\",\n\"bar\",\n\"baz\"]", toml::value(a)); + } + + { + array a(5); + a[0] = toml::value(3); a[1] = toml::value(1); a[2] = toml::value(4); + a[3] = toml::value(1); a[4] = toml::value(5); + TOML11_TEST_PARSE_EQUAL_VALUE(parse_value, "[3,#comment\n1,#comment\n4,#comment\n1,#comment\n5]", toml::value(a)); + } + { + array a(3); + a[0] = toml::value("foo"); a[1] = toml::value("b#r"); + a[2] = toml::value("b#z"); + TOML11_TEST_PARSE_EQUAL_VALUE(parse_value, "[\"foo\",#comment\n\"b#r\",#comment\n\"b#z\"#comment\n]", toml::value(a)); + } +} diff --git a/tests/test_parse_aux.hpp b/tests/test_parse_aux.hpp new file mode 100644 index 00000000..4ef9541c --- /dev/null +++ b/tests/test_parse_aux.hpp @@ -0,0 +1,38 @@ +#include +#include +#include +#include +#include + +// some of the parsers returns not only a value but also a region. +#define TOML11_TEST_PARSE_EQUAL(psr, tkn, expct) \ +do { \ + const std::string token(tkn); \ + toml::detail::location loc("test", token); \ + const auto result = psr(loc); \ + BOOST_CHECK(result.is_ok()); \ + if(result.is_ok()){ \ + BOOST_CHECK(result.unwrap().first == expct); \ + } else { \ + std::cerr << "parser " << #psr << " failed with input `"; \ + std::cerr << token << "`.\n"; \ + std::cerr << "reason: " << result.unwrap_err() << '\n'; \ + } \ +} while(false); \ +/**/ + +#define TOML11_TEST_PARSE_EQUAL_VALUE(psr, tkn, expct) \ +do { \ + const std::string token(tkn); \ + toml::detail::location loc("test", token); \ + const auto result = psr(loc); \ + BOOST_CHECK(result.is_ok()); \ + if(result.is_ok()){ \ + BOOST_CHECK(result.unwrap() == expct); \ + } else { \ + std::cerr << "parse_value failed with input `"; \ + std::cerr << token << "`.\n"; \ + std::cerr << "reason: " << result.unwrap_err() << '\n'; \ + } \ +} while(false); \ +/**/ diff --git a/tests/test_parse_boolean.cpp b/tests/test_parse_boolean.cpp new file mode 100644 index 00000000..5224a930 --- /dev/null +++ b/tests/test_parse_boolean.cpp @@ -0,0 +1,24 @@ +#define BOOST_TEST_MODULE "test_parse_boolean" +#ifdef UNITTEST_FRAMEWORK_LIBRARY_EXIST +#include +#else +#define BOOST_TEST_NO_LIB +#include +#endif +#include +#include "test_parse_aux.hpp" + +using namespace toml; +using namespace detail; + +BOOST_AUTO_TEST_CASE(test_boolean) +{ + TOML11_TEST_PARSE_EQUAL(parse_boolean, "true", true); + TOML11_TEST_PARSE_EQUAL(parse_boolean, "false", false); +} + +BOOST_AUTO_TEST_CASE(test_boolean_value) +{ + TOML11_TEST_PARSE_EQUAL_VALUE(parse_value, "true", toml::value( true)); + TOML11_TEST_PARSE_EQUAL_VALUE(parse_value, "false", toml::value(false)); +} diff --git a/tests/test_parse_datetime.cpp b/tests/test_parse_datetime.cpp new file mode 100644 index 00000000..7a4e2e7c --- /dev/null +++ b/tests/test_parse_datetime.cpp @@ -0,0 +1,133 @@ +#define BOOST_TEST_MODULE "parse_datetime_test" +#ifdef UNITTEST_FRAMEWORK_LIBRARY_EXIST +#include +#else +#define BOOST_TEST_NO_LIB +#include +#endif +#include +#include "test_parse_aux.hpp" + +using namespace toml; +using namespace detail; + +BOOST_AUTO_TEST_CASE(test_time) +{ + TOML11_TEST_PARSE_EQUAL(parse_local_time, "07:32:00", toml::local_time(7, 32, 0)); + TOML11_TEST_PARSE_EQUAL(parse_local_time, "07:32:00.99", toml::local_time(7, 32, 0, 990, 0)); + TOML11_TEST_PARSE_EQUAL(parse_local_time, "07:32:00.999", toml::local_time(7, 32, 0, 999, 0)); + TOML11_TEST_PARSE_EQUAL(parse_local_time, "07:32:00.999999", toml::local_time(7, 32, 0, 999, 999)); +} + +BOOST_AUTO_TEST_CASE(test_time_value) +{ + TOML11_TEST_PARSE_EQUAL_VALUE(parse_value, "07:32:00", toml::value(toml::local_time(7, 32, 0))); + TOML11_TEST_PARSE_EQUAL_VALUE(parse_value, "07:32:00.99", toml::value(toml::local_time(7, 32, 0, 990, 0))); + TOML11_TEST_PARSE_EQUAL_VALUE(parse_value, "07:32:00.999", toml::value(toml::local_time(7, 32, 0, 999, 0))); + TOML11_TEST_PARSE_EQUAL_VALUE(parse_value, "07:32:00.999999", toml::value(toml::local_time(7, 32, 0, 999, 999))); +} + +BOOST_AUTO_TEST_CASE(test_date) +{ + TOML11_TEST_PARSE_EQUAL(parse_local_date, "1979-05-27", + toml::local_date(1979, toml::month_t::May, 27)); +} +BOOST_AUTO_TEST_CASE(test_date_value) +{ + TOML11_TEST_PARSE_EQUAL_VALUE(parse_value, "1979-05-27", + value(toml::local_date(1979, toml::month_t::May, 27))); +} + +BOOST_AUTO_TEST_CASE(test_datetime) +{ + TOML11_TEST_PARSE_EQUAL(parse_local_datetime, "1979-05-27T07:32:00", + toml::local_datetime(toml::local_date(1979, toml::month_t::May, 27), toml::local_time(7, 32, 0))); + TOML11_TEST_PARSE_EQUAL(parse_local_datetime, "1979-05-27T07:32:00.99", + toml::local_datetime(toml::local_date(1979, toml::month_t::May, 27), toml::local_time(7, 32, 0, 990, 0))); + TOML11_TEST_PARSE_EQUAL(parse_local_datetime, "1979-05-27T07:32:00.999999", + toml::local_datetime(toml::local_date(1979, toml::month_t::May, 27), toml::local_time(7, 32, 0, 999, 999))); + + TOML11_TEST_PARSE_EQUAL(parse_local_datetime, "1979-05-27t07:32:00", + toml::local_datetime(toml::local_date(1979, toml::month_t::May, 27), toml::local_time(7, 32, 0))); + TOML11_TEST_PARSE_EQUAL(parse_local_datetime, "1979-05-27t07:32:00.99", + toml::local_datetime(toml::local_date(1979, toml::month_t::May, 27), toml::local_time(7, 32, 0, 990, 0))); + TOML11_TEST_PARSE_EQUAL(parse_local_datetime, "1979-05-27t07:32:00.999999", + toml::local_datetime(toml::local_date(1979, toml::month_t::May, 27), toml::local_time(7, 32, 0, 999, 999))); + + TOML11_TEST_PARSE_EQUAL(parse_local_datetime, "1979-05-27 07:32:00", + toml::local_datetime(toml::local_date(1979, toml::month_t::May, 27), toml::local_time(7, 32, 0))); + TOML11_TEST_PARSE_EQUAL(parse_local_datetime, "1979-05-27 07:32:00.99", + toml::local_datetime(toml::local_date(1979, toml::month_t::May, 27), toml::local_time(7, 32, 0, 990, 0))); + TOML11_TEST_PARSE_EQUAL(parse_local_datetime, "1979-05-27 07:32:00.999999", + toml::local_datetime(toml::local_date(1979, toml::month_t::May, 27), toml::local_time(7, 32, 0, 999, 999))); +} + +BOOST_AUTO_TEST_CASE(test_datetime_value) +{ + TOML11_TEST_PARSE_EQUAL_VALUE(parse_value, "1979-05-27T07:32:00", + toml::value(toml::local_datetime(toml::local_date(1979, toml::month_t::May, 27), toml::local_time(7, 32, 0)))); + TOML11_TEST_PARSE_EQUAL_VALUE(parse_value, "1979-05-27T07:32:00.99", + toml::value(toml::local_datetime(toml::local_date(1979, toml::month_t::May, 27), toml::local_time(7, 32, 0, 990, 0)))); + TOML11_TEST_PARSE_EQUAL_VALUE(parse_value, "1979-05-27T07:32:00.999999", + toml::value(toml::local_datetime(toml::local_date(1979, toml::month_t::May, 27), toml::local_time(7, 32, 0, 999, 999)))); + + TOML11_TEST_PARSE_EQUAL_VALUE(parse_value, "1979-05-27t07:32:00", + toml::value(toml::local_datetime(toml::local_date(1979, toml::month_t::May, 27), toml::local_time(7, 32, 0)))); + TOML11_TEST_PARSE_EQUAL_VALUE(parse_value, "1979-05-27t07:32:00.99", + toml::value(toml::local_datetime(toml::local_date(1979, toml::month_t::May, 27), toml::local_time(7, 32, 0, 990, 0)))); + TOML11_TEST_PARSE_EQUAL_VALUE(parse_value, "1979-05-27t07:32:00.999999", + toml::value(toml::local_datetime(toml::local_date(1979, toml::month_t::May, 27), toml::local_time(7, 32, 0, 999, 999)))); + + TOML11_TEST_PARSE_EQUAL_VALUE(parse_value, "1979-05-27 07:32:00", + toml::value(toml::local_datetime(toml::local_date(1979, toml::month_t::May, 27), toml::local_time(7, 32, 0)))); + TOML11_TEST_PARSE_EQUAL_VALUE(parse_value, "1979-05-27 07:32:00.99", + toml::value(toml::local_datetime(toml::local_date(1979, toml::month_t::May, 27), toml::local_time(7, 32, 0, 990, 0)))); + TOML11_TEST_PARSE_EQUAL_VALUE(parse_value, "1979-05-27 07:32:00.999999", + toml::value(toml::local_datetime(toml::local_date(1979, toml::month_t::May, 27), toml::local_time(7, 32, 0, 999, 999)))); +} + +BOOST_AUTO_TEST_CASE(test_offset_datetime) +{ + TOML11_TEST_PARSE_EQUAL(parse_offset_datetime, "1979-05-27T07:32:00Z", + toml::offset_datetime(toml::local_date(1979, toml::month_t::May, 27), + toml::local_time(7, 32, 0), toml::time_offset(0, 0))); + TOML11_TEST_PARSE_EQUAL(parse_offset_datetime, "1979-05-27T07:32:00.99Z", + toml::offset_datetime(toml::local_date(1979, toml::month_t::May, 27), + toml::local_time(7, 32, 0, 990, 0), toml::time_offset(0, 0))); + TOML11_TEST_PARSE_EQUAL(parse_offset_datetime, "1979-05-27T07:32:00.999999Z", + toml::offset_datetime(toml::local_date(1979, toml::month_t::May, 27), + toml::local_time(7, 32, 0, 999, 999), toml::time_offset(0, 0))); + + TOML11_TEST_PARSE_EQUAL(parse_offset_datetime, "1979-05-27T07:32:00+09:00", + toml::offset_datetime(toml::local_date(1979, toml::month_t::May, 27), + toml::local_time(7, 32, 0), toml::time_offset(9, 0))); + TOML11_TEST_PARSE_EQUAL(parse_offset_datetime, "1979-05-27T07:32:00.99+09:00", + toml::offset_datetime(toml::local_date(1979, toml::month_t::May, 27), + toml::local_time(7, 32, 0, 990, 0), toml::time_offset(9, 0))); + TOML11_TEST_PARSE_EQUAL(parse_offset_datetime, "1979-05-27T07:32:00.999999+09:00", + toml::offset_datetime(toml::local_date(1979, toml::month_t::May, 27), + toml::local_time(7, 32, 0, 999, 999), toml::time_offset(9, 0))); +} + +BOOST_AUTO_TEST_CASE(test_offset_datetime_value) +{ + TOML11_TEST_PARSE_EQUAL_VALUE(parse_value, "1979-05-27T07:32:00Z", + toml::value(toml::offset_datetime(toml::local_date(1979, toml::month_t::May, 27), + toml::local_time(7, 32, 0), toml::time_offset(0, 0)))); + TOML11_TEST_PARSE_EQUAL_VALUE(parse_value, "1979-05-27T07:32:00.99Z", + toml::value(toml::offset_datetime(toml::local_date(1979, toml::month_t::May, 27), + toml::local_time(7, 32, 0, 990, 0), toml::time_offset(0, 0)))); + TOML11_TEST_PARSE_EQUAL_VALUE(parse_value, "1979-05-27T07:32:00.999999Z", + toml::value(toml::offset_datetime(toml::local_date(1979, toml::month_t::May, 27), + toml::local_time(7, 32, 0, 999, 999), toml::time_offset(0, 0)))); + + TOML11_TEST_PARSE_EQUAL_VALUE(parse_value, "1979-05-27T07:32:00+09:00", + toml::value(toml::offset_datetime(toml::local_date(1979, toml::month_t::May, 27), + toml::local_time(7, 32, 0), toml::time_offset(9, 0)))); + TOML11_TEST_PARSE_EQUAL_VALUE(parse_value, "1979-05-27T07:32:00.99+09:00", + toml::value(toml::offset_datetime(toml::local_date(1979, toml::month_t::May, 27), + toml::local_time(7, 32, 0, 990, 0), toml::time_offset(9, 0)))); + TOML11_TEST_PARSE_EQUAL_VALUE(parse_value, "1979-05-27T07:32:00.999999+09:00", + toml::value(toml::offset_datetime(toml::local_date(1979, toml::month_t::May, 27), + toml::local_time(7, 32, 0, 999, 999), toml::time_offset(9, 0)))); +} diff --git a/tests/test_parse_file.cpp b/tests/test_parse_file.cpp index 4222921c..95f178cf 100644 --- a/tests/test_parse_file.cpp +++ b/tests/test_parse_file.cpp @@ -22,7 +22,8 @@ BOOST_AUTO_TEST_CASE(test_example) BOOST_CHECK_EQUAL(toml::get(owner.at("bio")), "GitHub Cofounder & CEO\nLikes tater tots and beer."); BOOST_CHECK_EQUAL(toml::get(owner.at("dob")), - toml::Datetime(1979, 5, 27, 7, 32, 0, 0, 0, 0, 0)); + toml::offset_datetime(toml::local_date(1979, toml::month_t::May, 27), + toml::local_time(7, 32, 0), toml::time_offset(0, 0))); } toml::Table database = toml::get(data.at("database")); @@ -90,7 +91,8 @@ BOOST_AUTO_TEST_CASE(test_example_stream) BOOST_CHECK_EQUAL(toml::get(owner.at("bio")), "GitHub Cofounder & CEO\nLikes tater tots and beer."); BOOST_CHECK_EQUAL(toml::get(owner.at("dob")), - toml::Datetime(1979, 5, 27, 7, 32, 0, 0, 0, 0, 0)); + toml::offset_datetime(toml::local_date(1979, toml::month_t::May, 27), + toml::local_time(7, 32, 0), toml::time_offset(0, 0))); } toml::Table database = toml::get(data.at("database")); diff --git a/tests/test_parse_floating.cpp b/tests/test_parse_floating.cpp new file mode 100644 index 00000000..e98dbe7a --- /dev/null +++ b/tests/test_parse_floating.cpp @@ -0,0 +1,158 @@ +#define BOOST_TEST_MODULE "parse_floating_test" +#ifdef UNITTEST_FRAMEWORK_LIBRARY_EXIST +#include +#else +#define BOOST_TEST_NO_LIB +#include +#endif +#include +#include +#include "test_parse_aux.hpp" + +using namespace toml; +using namespace detail; + +BOOST_AUTO_TEST_CASE(test_fractional) +{ + TOML11_TEST_PARSE_EQUAL(parse_floating, "1.0", 1.0); + TOML11_TEST_PARSE_EQUAL(parse_floating, "0.1", 0.1); + TOML11_TEST_PARSE_EQUAL(parse_floating, "0.001", 0.001); + TOML11_TEST_PARSE_EQUAL(parse_floating, "0.100", 0.1); + TOML11_TEST_PARSE_EQUAL(parse_floating, "+3.14", 3.14); + TOML11_TEST_PARSE_EQUAL(parse_floating, "-3.14", -3.14); + TOML11_TEST_PARSE_EQUAL(parse_floating, "3.1415_9265_3589", 3.141592653589); + TOML11_TEST_PARSE_EQUAL(parse_floating, "+3.1415_9265_3589", 3.141592653589); + TOML11_TEST_PARSE_EQUAL(parse_floating, "-3.1415_9265_3589", -3.141592653589); + TOML11_TEST_PARSE_EQUAL(parse_floating, "123_456.789", 123456.789); + TOML11_TEST_PARSE_EQUAL(parse_floating, "+123_456.789", 123456.789); + TOML11_TEST_PARSE_EQUAL(parse_floating, "-123_456.789", -123456.789); + TOML11_TEST_PARSE_EQUAL(parse_floating, "+0.0", 0.0); + TOML11_TEST_PARSE_EQUAL(parse_floating, "-0.0", -0.0); +} + +BOOST_AUTO_TEST_CASE(test_fractional_value) +{ + TOML11_TEST_PARSE_EQUAL_VALUE(parse_value, "1.0", value( 1.0)); + TOML11_TEST_PARSE_EQUAL_VALUE(parse_value, "0.1", value( 0.1)); + TOML11_TEST_PARSE_EQUAL_VALUE(parse_value, "0.001", value( 0.001)); + TOML11_TEST_PARSE_EQUAL_VALUE(parse_value, "0.100", value( 0.1)); + TOML11_TEST_PARSE_EQUAL_VALUE(parse_value, "+3.14", value( 3.14)); + TOML11_TEST_PARSE_EQUAL_VALUE(parse_value, "-3.14", value(-3.14)); + TOML11_TEST_PARSE_EQUAL_VALUE(parse_value, "3.1415_9265_3589", value( 3.141592653589)); + TOML11_TEST_PARSE_EQUAL_VALUE(parse_value, "+3.1415_9265_3589", value( 3.141592653589)); + TOML11_TEST_PARSE_EQUAL_VALUE(parse_value, "-3.1415_9265_3589", value(-3.141592653589)); + TOML11_TEST_PARSE_EQUAL_VALUE(parse_value, "123_456.789", value( 123456.789)); + TOML11_TEST_PARSE_EQUAL_VALUE(parse_value, "+123_456.789", value( 123456.789)); + TOML11_TEST_PARSE_EQUAL_VALUE(parse_value, "-123_456.789", value(-123456.789)); + TOML11_TEST_PARSE_EQUAL_VALUE(parse_value, "+0.0", value( 0.0)); + TOML11_TEST_PARSE_EQUAL_VALUE(parse_value, "-0.0", value(-0.0)); +} + +BOOST_AUTO_TEST_CASE(test_exponential) +{ + TOML11_TEST_PARSE_EQUAL(parse_floating, "1e10", 1e10); + TOML11_TEST_PARSE_EQUAL(parse_floating, "1e+10", 1e10); + TOML11_TEST_PARSE_EQUAL(parse_floating, "1e-10", 1e-10); + TOML11_TEST_PARSE_EQUAL(parse_floating, "+1e10", 1e10); + TOML11_TEST_PARSE_EQUAL(parse_floating, "+1e+10", 1e10); + TOML11_TEST_PARSE_EQUAL(parse_floating, "+1e-10", 1e-10); + TOML11_TEST_PARSE_EQUAL(parse_floating, "-1e10", -1e10); + TOML11_TEST_PARSE_EQUAL(parse_floating, "-1e+10", -1e10); + TOML11_TEST_PARSE_EQUAL(parse_floating, "-1e-10", -1e-10); + TOML11_TEST_PARSE_EQUAL(parse_floating, "123e-10", 123e-10); + TOML11_TEST_PARSE_EQUAL(parse_floating, "1E10", 1e10); + TOML11_TEST_PARSE_EQUAL(parse_floating, "1E+10", 1e10); + TOML11_TEST_PARSE_EQUAL(parse_floating, "1E-10", 1e-10); + TOML11_TEST_PARSE_EQUAL(parse_floating, "123E-10", 123e-10); + TOML11_TEST_PARSE_EQUAL(parse_floating, "1_2_3E-10", 123e-10); + TOML11_TEST_PARSE_EQUAL(parse_floating, "1_2_3E-1_0", 123e-10); + TOML11_TEST_PARSE_EQUAL(parse_floating, "+0e0", 0.0); + TOML11_TEST_PARSE_EQUAL(parse_floating, "-0e0", -0.0); +} + +BOOST_AUTO_TEST_CASE(test_exponential_value) +{ + TOML11_TEST_PARSE_EQUAL_VALUE(parse_value, "1e10", value(1e10)); + TOML11_TEST_PARSE_EQUAL_VALUE(parse_value, "1e+10", value(1e10)); + TOML11_TEST_PARSE_EQUAL_VALUE(parse_value, "1e-10", value(1e-10)); + TOML11_TEST_PARSE_EQUAL_VALUE(parse_value, "+1e10", value(1e10)); + TOML11_TEST_PARSE_EQUAL_VALUE(parse_value, "+1e+10", value(1e10)); + TOML11_TEST_PARSE_EQUAL_VALUE(parse_value, "+1e-10", value(1e-10)); + TOML11_TEST_PARSE_EQUAL_VALUE(parse_value, "-1e10", value(-1e10)); + TOML11_TEST_PARSE_EQUAL_VALUE(parse_value, "-1e+10", value(-1e10)); + TOML11_TEST_PARSE_EQUAL_VALUE(parse_value, "-1e-10", value(-1e-10)); + TOML11_TEST_PARSE_EQUAL_VALUE(parse_value, "123e-10", value(123e-10)); + TOML11_TEST_PARSE_EQUAL_VALUE(parse_value, "1E10", value(1e10)); + TOML11_TEST_PARSE_EQUAL_VALUE(parse_value, "1E+10", value(1e10)); + TOML11_TEST_PARSE_EQUAL_VALUE(parse_value, "1E-10", value(1e-10)); + TOML11_TEST_PARSE_EQUAL_VALUE(parse_value, "123E-10", value(123e-10)); + TOML11_TEST_PARSE_EQUAL_VALUE(parse_value, "1_2_3E-10", value(123e-10)); + TOML11_TEST_PARSE_EQUAL_VALUE(parse_value, "1_2_3E-1_0", value(123e-10)); + TOML11_TEST_PARSE_EQUAL_VALUE(parse_value, "+0e0", value( 0.0)); + TOML11_TEST_PARSE_EQUAL_VALUE(parse_value, "-0e0", value(-0.0)); +} +BOOST_AUTO_TEST_CASE(test_fe) +{ + TOML11_TEST_PARSE_EQUAL(parse_floating, "6.02e23", 6.02e23); + TOML11_TEST_PARSE_EQUAL(parse_floating, "6.02e+23", 6.02e23); + TOML11_TEST_PARSE_EQUAL(parse_floating, "1.112_650_06e-17", 1.11265006e-17); +} +BOOST_AUTO_TEST_CASE(test_fe_vaule) +{ + TOML11_TEST_PARSE_EQUAL_VALUE(parse_value, "6.02e23", value(6.02e23)); + TOML11_TEST_PARSE_EQUAL_VALUE(parse_value, "6.02e+23", value(6.02e23)); + TOML11_TEST_PARSE_EQUAL_VALUE(parse_value, "1.112_650_06e-17", value(1.11265006e-17)); +} + +BOOST_AUTO_TEST_CASE(test_inf) +{ + { + const std::string token("inf"); + toml::detail::location loc("test", token); + const auto r = parse_floating(loc); + BOOST_CHECK(r.is_ok()); + BOOST_CHECK(std::isinf(r.unwrap().first)); + BOOST_CHECK(r.unwrap().first > 0.0); + } + { + const std::string token("+inf"); + toml::detail::location loc("test", token); + const auto r = parse_floating(loc); + BOOST_CHECK(r.is_ok()); + BOOST_CHECK(std::isinf(r.unwrap().first)); + BOOST_CHECK(r.unwrap().first > 0.0); + } + { + const std::string token("-inf"); + toml::detail::location loc("test", token); + const auto r = parse_floating(loc); + BOOST_CHECK(r.is_ok()); + BOOST_CHECK(std::isinf(r.unwrap().first)); + BOOST_CHECK(r.unwrap().first < 0.0); + } +} + +BOOST_AUTO_TEST_CASE(test_nan) +{ + { + const std::string token("nan"); + toml::detail::location loc("test", token); + const auto r = parse_floating(loc); + BOOST_CHECK(r.is_ok()); + BOOST_CHECK(std::isnan(r.unwrap().first)); + } + { + const std::string token("+nan"); + toml::detail::location loc("test", token); + const auto r = parse_floating(loc); + BOOST_CHECK(r.is_ok()); + BOOST_CHECK(std::isnan(r.unwrap().first)); + } + { + const std::string token("-nan"); + toml::detail::location loc("test", token); + const auto r = parse_floating(loc); + BOOST_CHECK(r.is_ok()); + BOOST_CHECK(std::isnan(r.unwrap().first)); + } +} diff --git a/tests/test_parse_inline_table.cpp b/tests/test_parse_inline_table.cpp new file mode 100644 index 00000000..57045bd1 --- /dev/null +++ b/tests/test_parse_inline_table.cpp @@ -0,0 +1,48 @@ +#define BOOST_TEST_MODULE "parse_inline_table_test" +#ifdef UNITTEST_FRAMEWORK_LIBRARY_EXIST +#include +#else +#define BOOST_TEST_NO_LIB +#include +#endif +#include +#include "test_parse_aux.hpp" + +using namespace toml; +using namespace detail; + +BOOST_AUTO_TEST_CASE(test_inline_table) +{ + TOML11_TEST_PARSE_EQUAL(parse_inline_table, "{}", table()); + { + table t; + t["foo"] = toml::value(42); + t["bar"] = toml::value("baz"); + TOML11_TEST_PARSE_EQUAL(parse_inline_table, "{foo = 42, bar = \"baz\"}", t); + } + { + table t; + table t_sub; + t_sub["name"] = toml::value("pug"); + t["type"] = toml::value(t_sub); + TOML11_TEST_PARSE_EQUAL(parse_inline_table, "{type.name = \"pug\"}", t); + } +} + +BOOST_AUTO_TEST_CASE(test_inline_table_value) +{ + TOML11_TEST_PARSE_EQUAL_VALUE(parse_value, "{}", value(table())); + { + table t; + t["foo"] = toml::value(42); + t["bar"] = toml::value("baz"); + TOML11_TEST_PARSE_EQUAL_VALUE(parse_value, "{foo = 42, bar = \"baz\"}", value(t)); + } + { + table t; + table t_sub; + t_sub["name"] = toml::value("pug"); + t["type"] = toml::value(t_sub); + TOML11_TEST_PARSE_EQUAL_VALUE(parse_value, "{type.name = \"pug\"}", value(t)); + } +} diff --git a/tests/test_parse_integer.cpp b/tests/test_parse_integer.cpp new file mode 100644 index 00000000..6b5c515a --- /dev/null +++ b/tests/test_parse_integer.cpp @@ -0,0 +1,92 @@ +#define BOOST_TEST_MODULE "parse_integer_test" +#ifdef UNITTEST_FRAMEWORK_LIBRARY_EXIST +#include +#else +#define BOOST_TEST_NO_LIB +#include +#endif +#include +#include "test_parse_aux.hpp" + +using namespace toml; +using namespace detail; + +BOOST_AUTO_TEST_CASE(test_decimal) +{ + TOML11_TEST_PARSE_EQUAL(parse_integer, "1234", 1234); + TOML11_TEST_PARSE_EQUAL(parse_integer, "+1234", 1234); + TOML11_TEST_PARSE_EQUAL(parse_integer, "-1234", -1234); + TOML11_TEST_PARSE_EQUAL(parse_integer, "0", 0); + TOML11_TEST_PARSE_EQUAL(parse_integer, "1_2_3_4", 1234); + TOML11_TEST_PARSE_EQUAL(parse_integer, "+1_2_3_4", +1234); + TOML11_TEST_PARSE_EQUAL(parse_integer, "-1_2_3_4", -1234); + TOML11_TEST_PARSE_EQUAL(parse_integer, "123_456_789", 123456789); +} + +BOOST_AUTO_TEST_CASE(test_decimal_value) +{ + TOML11_TEST_PARSE_EQUAL_VALUE(parse_value, "1234", toml::value( 1234)); + TOML11_TEST_PARSE_EQUAL_VALUE(parse_value, "+1234", toml::value( 1234)); + TOML11_TEST_PARSE_EQUAL_VALUE(parse_value, "-1234", toml::value( -1234)); + TOML11_TEST_PARSE_EQUAL_VALUE(parse_value, "0", toml::value( 0)); + TOML11_TEST_PARSE_EQUAL_VALUE(parse_value, "1_2_3_4", toml::value( 1234)); + TOML11_TEST_PARSE_EQUAL_VALUE(parse_value, "+1_2_3_4", toml::value( +1234)); + TOML11_TEST_PARSE_EQUAL_VALUE(parse_value, "-1_2_3_4", toml::value( -1234)); + TOML11_TEST_PARSE_EQUAL_VALUE(parse_value, "123_456_789", toml::value(123456789)); +} + +BOOST_AUTO_TEST_CASE(test_hex) +{ + TOML11_TEST_PARSE_EQUAL(parse_integer, "0xDEADBEEF", 0xDEADBEEF); + TOML11_TEST_PARSE_EQUAL(parse_integer, "0xdeadbeef", 0xDEADBEEF); + TOML11_TEST_PARSE_EQUAL(parse_integer, "0xDEADbeef", 0xDEADBEEF); + TOML11_TEST_PARSE_EQUAL(parse_integer, "0xDEAD_BEEF", 0xDEADBEEF); + TOML11_TEST_PARSE_EQUAL(parse_integer, "0xdead_beef", 0xDEADBEEF); + TOML11_TEST_PARSE_EQUAL(parse_integer, "0xdead_BEEF", 0xDEADBEEF); + TOML11_TEST_PARSE_EQUAL(parse_integer, "0xFF", 0xFF); + TOML11_TEST_PARSE_EQUAL(parse_integer, "0x00FF", 0xFF); + TOML11_TEST_PARSE_EQUAL(parse_integer, "0x0000FF", 0xFF); +} + +BOOST_AUTO_TEST_CASE(test_hex_value) +{ + TOML11_TEST_PARSE_EQUAL_VALUE(parse_value, "0xDEADBEEF", value(0xDEADBEEF)); + TOML11_TEST_PARSE_EQUAL_VALUE(parse_value, "0xdeadbeef", value(0xDEADBEEF)); + TOML11_TEST_PARSE_EQUAL_VALUE(parse_value, "0xDEADbeef", value(0xDEADBEEF)); + TOML11_TEST_PARSE_EQUAL_VALUE(parse_value, "0xDEAD_BEEF", value(0xDEADBEEF)); + TOML11_TEST_PARSE_EQUAL_VALUE(parse_value, "0xdead_beef", value(0xDEADBEEF)); + TOML11_TEST_PARSE_EQUAL_VALUE(parse_value, "0xdead_BEEF", value(0xDEADBEEF)); + TOML11_TEST_PARSE_EQUAL_VALUE(parse_value, "0xFF", value(0xFF)); + TOML11_TEST_PARSE_EQUAL_VALUE(parse_value, "0x00FF", value(0xFF)); + TOML11_TEST_PARSE_EQUAL_VALUE(parse_value, "0x0000FF", value(0xFF)); +} + +BOOST_AUTO_TEST_CASE(test_oct) +{ + TOML11_TEST_PARSE_EQUAL(parse_integer, "0o777", 64*7+8*7+7); + TOML11_TEST_PARSE_EQUAL(parse_integer, "0o7_7_7", 64*7+8*7+7); + TOML11_TEST_PARSE_EQUAL(parse_integer, "0o007", 7); +} + +BOOST_AUTO_TEST_CASE(test_oct_value) +{ + TOML11_TEST_PARSE_EQUAL_VALUE(parse_value, "0o777", value(64*7+8*7+7)); + TOML11_TEST_PARSE_EQUAL_VALUE(parse_value, "0o7_7_7", value(64*7+8*7+7)); + TOML11_TEST_PARSE_EQUAL_VALUE(parse_value, "0o007", value(7)); +} + +BOOST_AUTO_TEST_CASE(test_bin) +{ + TOML11_TEST_PARSE_EQUAL(parse_integer, "0b10000", 16); + TOML11_TEST_PARSE_EQUAL(parse_integer, "0b010000", 16); + TOML11_TEST_PARSE_EQUAL(parse_integer, "0b01_00_00", 16); + TOML11_TEST_PARSE_EQUAL(parse_integer, "0b111111", 63); +} + +BOOST_AUTO_TEST_CASE(test_bin_value) +{ + TOML11_TEST_PARSE_EQUAL_VALUE(parse_value, "0b10000", value(16)); + TOML11_TEST_PARSE_EQUAL_VALUE(parse_value, "0b010000", value(16)); + TOML11_TEST_PARSE_EQUAL_VALUE(parse_value, "0b01_00_00", value(16)); + TOML11_TEST_PARSE_EQUAL_VALUE(parse_value, "0b111111", value(63)); +} diff --git a/tests/test_parse_key.cpp b/tests/test_parse_key.cpp new file mode 100644 index 00000000..b7fcf5b6 --- /dev/null +++ b/tests/test_parse_key.cpp @@ -0,0 +1,63 @@ +#define BOOST_TEST_MODULE "parse_key_test" +#ifdef UNITTEST_FRAMEWORK_LIBRARY_EXIST +#include +#else +#define BOOST_TEST_NO_LIB +#include +#endif +#include +#include "test_parse_aux.hpp" + +using namespace toml; +using namespace detail; + +BOOST_AUTO_TEST_CASE(test_bare_key) +{ + TOML11_TEST_PARSE_EQUAL_VALUE(parse_key, "barekey", std::vector(1, "barekey")); + TOML11_TEST_PARSE_EQUAL_VALUE(parse_key, "bare-key", std::vector(1, "bare-key")); + TOML11_TEST_PARSE_EQUAL_VALUE(parse_key, "bare_key", std::vector(1, "bare_key")); + TOML11_TEST_PARSE_EQUAL_VALUE(parse_key, "1234", std::vector(1, "1234")); +} + +BOOST_AUTO_TEST_CASE(test_quoted_key) +{ + TOML11_TEST_PARSE_EQUAL_VALUE(parse_key, "\"127.0.0.1\"", std::vector(1, "127.0.0.1" )); + TOML11_TEST_PARSE_EQUAL_VALUE(parse_key, "\"character encoding\"", std::vector(1, "character encoding")); +#if defined(_MSC_VER) || defined(__INTEL_COMPILER) + TOML11_TEST_PARSE_EQUAL_VALUE(parse_key, "\"\xCA\x8E\xC7\x9D\xCA\x9E\"", std::vector(1, "\xCA\x8E\xC7\x9D\xCA\x9E")); +#else + TOML11_TEST_PARSE_EQUAL_VALUE(parse_key, "\"ʎǝʞ\"", std::vector(1, "ʎǝʞ" )); +#endif + TOML11_TEST_PARSE_EQUAL_VALUE(parse_key, "'key2'", std::vector(1, "key2" )); + TOML11_TEST_PARSE_EQUAL_VALUE(parse_key, "'quoted \"value\"'", std::vector(1, "quoted \"value\"" )); +} + +BOOST_AUTO_TEST_CASE(test_dotted_key) +{ + { + std::vector keys(2); + keys[0] = "physical"; + keys[1] = "color"; + TOML11_TEST_PARSE_EQUAL_VALUE(parse_key, "physical.color", keys); + } + { + std::vector keys(2); + keys[0] = "physical"; + keys[1] = "shape"; + TOML11_TEST_PARSE_EQUAL_VALUE(parse_key, "physical.shape", keys); + } + { + std::vector keys(4); + keys[0] = "x"; + keys[1] = "y"; + keys[2] = "z"; + keys[3] = "w"; + TOML11_TEST_PARSE_EQUAL_VALUE(parse_key, "x.y.z.w", keys); + } + { + std::vector keys(2); + keys[0] = "site"; + keys[1] = "google.com"; + TOML11_TEST_PARSE_EQUAL_VALUE(parse_key, "site.\"google.com\"", keys); + } +} diff --git a/tests/test_parse_string.cpp b/tests/test_parse_string.cpp new file mode 100644 index 00000000..60f99917 --- /dev/null +++ b/tests/test_parse_string.cpp @@ -0,0 +1,188 @@ +#define BOOST_TEST_MODULE "parse_string_test" +#ifdef UNITTEST_FRAMEWORK_LIBRARY_EXIST +#include +#else +#define BOOST_TEST_NO_LIB +#include +#endif +#include +#include "test_parse_aux.hpp" + +using namespace toml; +using namespace detail; + +BOOST_AUTO_TEST_CASE(test_string) +{ + TOML11_TEST_PARSE_EQUAL(parse_string, + "\"The quick brown fox jumps over the lazy dog\"", + string("The quick brown fox jumps over the lazy dog", string_t::basic)); + TOML11_TEST_PARSE_EQUAL(parse_string, + "\'The quick brown fox jumps over the lazy dog\'", + string("The quick brown fox jumps over the lazy dog", string_t::literal)); + TOML11_TEST_PARSE_EQUAL(parse_string, + "\"\"\"The quick brown fox \\\njumps over the lazy dog\"\"\"", + string("The quick brown fox jumps over the lazy dog", string_t::basic)); + TOML11_TEST_PARSE_EQUAL(parse_string, + "'''The quick brown fox \njumps over the lazy dog'''", + string("The quick brown fox \njumps over the lazy dog", string_t::literal)); +} + +BOOST_AUTO_TEST_CASE(test_string_value) +{ + TOML11_TEST_PARSE_EQUAL_VALUE(parse_value, + "\"The quick brown fox jumps over the lazy dog\"", + toml::value("The quick brown fox jumps over the lazy dog", string_t::basic)); + TOML11_TEST_PARSE_EQUAL_VALUE(parse_value, + "\'The quick brown fox jumps over the lazy dog\'", + toml::value("The quick brown fox jumps over the lazy dog", string_t::literal)); + TOML11_TEST_PARSE_EQUAL_VALUE(parse_value, + "\"\"\"The quick brown fox \\\njumps over the lazy dog\"\"\"", + toml::value("The quick brown fox jumps over the lazy dog", string_t::basic)); + TOML11_TEST_PARSE_EQUAL_VALUE(parse_value, + "'''The quick brown fox \njumps over the lazy dog'''", + toml::value("The quick brown fox \njumps over the lazy dog", string_t::literal)); +} + + +BOOST_AUTO_TEST_CASE(test_basic_string) +{ + TOML11_TEST_PARSE_EQUAL(parse_string, + "\"GitHub Cofounder & CEO\\nLikes tater tots and beer.\"", + string("GitHub Cofounder & CEO\nLikes tater tots and beer.", string_t::basic)); + TOML11_TEST_PARSE_EQUAL(parse_string, + "\"192.168.1.1\"", + string("192.168.1.1", string_t::basic)); + +#if defined(_MSC_VER) || defined(__INTEL_COMPILER) + TOML11_TEST_PARSE_EQUAL(parse_string, + "\"\xE4\xB8\xAD\xE5\x9B\xBD\"", + string("\xE4\xB8\xAD\xE5\x9B\xBD", string_t::basic)); +#else + TOML11_TEST_PARSE_EQUAL(parse_string, + "\"中国\"", + string("中国", string_t::basic)); +#endif + + TOML11_TEST_PARSE_EQUAL(parse_string, + "\"You'll hate me after this - #\"", + string("You'll hate me after this - #", string_t::basic)); + TOML11_TEST_PARSE_EQUAL(parse_string, + "\" And when \\\"'s are in the along with # \\\"\"", + string(" And when \"'s are in the along with # \"", string_t::basic)); +} + +BOOST_AUTO_TEST_CASE(test_basic_string_value) +{ + TOML11_TEST_PARSE_EQUAL_VALUE(parse_value, + "\"GitHub Cofounder & CEO\\nLikes tater tots and beer.\"", + value("GitHub Cofounder & CEO\nLikes tater tots and beer.", string_t::basic)); + TOML11_TEST_PARSE_EQUAL_VALUE(parse_value, + "\"192.168.1.1\"", + value("192.168.1.1", string_t::basic)); +#if defined(_MSC_VER) || defined(__INTEL_COMPILER) + TOML11_TEST_PARSE_EQUAL_VALUE(parse_value, + "\"\xE4\xB8\xAD\xE5\x9B\xBD\"", + value("\xE4\xB8\xAD\xE5\x9B\xBD", string_t::basic)); +#else + TOML11_TEST_PARSE_EQUAL_VALUE(parse_value, + "\"中国\"", + value("中国", string_t::basic)); +#endif + TOML11_TEST_PARSE_EQUAL_VALUE(parse_value, + "\"You'll hate me after this - #\"", + value("You'll hate me after this - #", string_t::basic)); + TOML11_TEST_PARSE_EQUAL_VALUE(parse_value, + "\" And when \\\"'s are in the along with # \\\"\"", + value(" And when \"'s are in the along with # \"", string_t::basic)); +} + +BOOST_AUTO_TEST_CASE(test_ml_basic_string) +{ + TOML11_TEST_PARSE_EQUAL(parse_string, + "\"\"\"\nThe quick brown \\\n\n fox jumps over \\\n the lazy dog.\"\"\"", + string("The quick brown fox jumps over the lazy dog.", string_t::basic)); + TOML11_TEST_PARSE_EQUAL(parse_string, + "\"\"\"\\\n The quick brown \\\n\n fox jumps over \\\n the lazy dog.\\\n \"\"\"", + string("The quick brown fox jumps over the lazy dog.", string_t::basic)); +} + +BOOST_AUTO_TEST_CASE(test_ml_basic_string_value) +{ + TOML11_TEST_PARSE_EQUAL_VALUE(parse_value, + "\"\"\"\nThe quick brown \\\n\n fox jumps over \\\n the lazy dog.\"\"\"", + value("The quick brown fox jumps over the lazy dog.", string_t::basic)); + TOML11_TEST_PARSE_EQUAL_VALUE(parse_value, + "\"\"\"\\\n The quick brown \\\n\n fox jumps over \\\n the lazy dog.\\\n \"\"\"", + value("The quick brown fox jumps over the lazy dog.", string_t::basic)); +} + +BOOST_AUTO_TEST_CASE(test_literal_string) +{ + TOML11_TEST_PARSE_EQUAL(parse_string, + "'C:\\Users\\nodejs\\templates'", + string("C:\\Users\\nodejs\\templates", string_t::literal)); + TOML11_TEST_PARSE_EQUAL(parse_string, + "'\\\\ServerX\\admin$\\system32\\'", + string("\\\\ServerX\\admin$\\system32\\", string_t::literal)); + TOML11_TEST_PARSE_EQUAL(parse_string, + "'Tom \"Dubs\" Preston-Werner'", + string("Tom \"Dubs\" Preston-Werner", string_t::literal)); + TOML11_TEST_PARSE_EQUAL(parse_string, + "'<\\i\\c*\\s*>'", + string("<\\i\\c*\\s*>", string_t::literal)); +} + +BOOST_AUTO_TEST_CASE(test_literal_string_value) +{ + TOML11_TEST_PARSE_EQUAL_VALUE(parse_value, + "'C:\\Users\\nodejs\\templates'", + value("C:\\Users\\nodejs\\templates", string_t::literal)); + TOML11_TEST_PARSE_EQUAL_VALUE(parse_value, + "'\\\\ServerX\\admin$\\system32\\'", + value("\\\\ServerX\\admin$\\system32\\", string_t::literal)); + TOML11_TEST_PARSE_EQUAL_VALUE(parse_value, + "'Tom \"Dubs\" Preston-Werner'", + value("Tom \"Dubs\" Preston-Werner", string_t::literal)); + TOML11_TEST_PARSE_EQUAL_VALUE(parse_value, + "'<\\i\\c*\\s*>'", + value("<\\i\\c*\\s*>", string_t::literal)); +} + +BOOST_AUTO_TEST_CASE(test_ml_literal_string) +{ + TOML11_TEST_PARSE_EQUAL(parse_string, + "'''I [dw]on't need \\d{2} apples'''", + string("I [dw]on't need \\d{2} apples", string_t::literal)); + TOML11_TEST_PARSE_EQUAL(parse_string, + "'''\nThe first newline is\ntrimmed in raw strings.\n All other whitespace\n is preserved.\n'''", + string("The first newline is\ntrimmed in raw strings.\n All other whitespace\n is preserved.\n", string_t::literal)); +} + +BOOST_AUTO_TEST_CASE(test_ml_literal_string_value) +{ + TOML11_TEST_PARSE_EQUAL_VALUE(parse_value, + "'''I [dw]on't need \\d{2} apples'''", + value("I [dw]on't need \\d{2} apples", string_t::literal)); + TOML11_TEST_PARSE_EQUAL_VALUE(parse_value, + "'''\nThe first newline is\ntrimmed in raw strings.\n All other whitespace\n is preserved.\n'''", + value("The first newline is\ntrimmed in raw strings.\n All other whitespace\n is preserved.\n", string_t::literal)); +} + +BOOST_AUTO_TEST_CASE(test_unicode_escape_sequence) +{ +#if defined(_MSC_VER) || defined(__INTEL_COMPILER) + TOML11_TEST_PARSE_EQUAL(parse_string, + "\"\\u03B1\\u03B2\\u03B3\"", + string("\xCE\xB1\xCE\xB2\xCE\xB3", string_t::basic)); + TOML11_TEST_PARSE_EQUAL(parse_string, + "\"\\U0001D7AA\"", + string("\xF0\x9D\x9E\xAA", string_t::basic)); +#else + TOML11_TEST_PARSE_EQUAL(parse_string, + "\"\\u03B1\\u03B2\\u03B3\"", + string("αβγ", string_t::basic)); + TOML11_TEST_PARSE_EQUAL(parse_string, + "\"\\U0001D7AA\"", + string("𝞪", string_t::basic)); +#endif +} diff --git a/tests/test_parse_table.cpp b/tests/test_parse_table.cpp new file mode 100644 index 00000000..13207b50 --- /dev/null +++ b/tests/test_parse_table.cpp @@ -0,0 +1,50 @@ +#define BOOST_TEST_MODULE "parse_table_test" +#ifdef UNITTEST_FRAMEWORK_LIBRARY_EXIST +#include +#else +#define BOOST_TEST_NO_LIB +#include +#endif +#include +#include +#include "test_parse_aux.hpp" + +using namespace toml; +using namespace detail; + +BOOST_AUTO_TEST_CASE(test_normal_table) +{ + std::string table( + "key1 = \"value\"\n" + "key2 = 42\n" + "key3 = 3.14\n" + ); + location loc("test", table); + + const auto result = toml::detail::parse_ml_table(loc); + BOOST_CHECK(result.is_ok()); + const auto data = result.unwrap(); + + BOOST_CHECK_EQUAL(toml::get(data.at("key1")), "value"); + BOOST_CHECK_EQUAL(toml::get(data.at("key2")), 42); + BOOST_CHECK_EQUAL(toml::get(data.at("key3")), 3.14); +} + +BOOST_AUTO_TEST_CASE(test_nested_table) +{ + std::string table( + "a.b = \"value\"\n" + "a.c.d = 42\n" + ); + location loc("test", table); + + const auto result = toml::detail::parse_ml_table(loc); + BOOST_CHECK(result.is_ok()); + const auto data = result.unwrap(); + + const auto a = toml::get(data.at("a")); + const auto c = toml::get(a.at("c")); + + BOOST_CHECK_EQUAL(toml::get(a.at("b")), "value"); + BOOST_CHECK_EQUAL(toml::get(c.at("d")), 42); +} diff --git a/tests/test_parse_table_key.cpp b/tests/test_parse_table_key.cpp new file mode 100644 index 00000000..47df434a --- /dev/null +++ b/tests/test_parse_table_key.cpp @@ -0,0 +1,117 @@ +#define BOOST_TEST_MODULE "parse_table_key_test" +#ifdef UNITTEST_FRAMEWORK_LIBRARY_EXIST +#include +#else +#define BOOST_TEST_NO_LIB +#include +#endif +#include +#include "test_parse_aux.hpp" + +using namespace toml; +using namespace detail; + +BOOST_AUTO_TEST_CASE(test_table_bare_key) +{ + TOML11_TEST_PARSE_EQUAL(parse_table_key, "[barekey]", std::vector(1, "barekey")); + TOML11_TEST_PARSE_EQUAL(parse_table_key, "[bare-key]", std::vector(1, "bare-key")); + TOML11_TEST_PARSE_EQUAL(parse_table_key, "[bare_key]", std::vector(1, "bare_key")); + TOML11_TEST_PARSE_EQUAL(parse_table_key, "[1234]", std::vector(1, "1234")); +} + +BOOST_AUTO_TEST_CASE(test_table_quoted_key) +{ + TOML11_TEST_PARSE_EQUAL(parse_table_key, "[\"127.0.0.1\"]", std::vector(1, "127.0.0.1" )); + TOML11_TEST_PARSE_EQUAL(parse_table_key, "[\"character encoding\"]", std::vector(1, "character encoding")); + TOML11_TEST_PARSE_EQUAL(parse_table_key, "[\"ʎǝʞ\"]", std::vector(1, "ʎǝʞ" )); + TOML11_TEST_PARSE_EQUAL(parse_table_key, "['key2']", std::vector(1, "key2" )); + TOML11_TEST_PARSE_EQUAL(parse_table_key, "['quoted \"value\"']", std::vector(1, "quoted \"value\"" )); +} + +BOOST_AUTO_TEST_CASE(test_table_dotted_key) +{ + { + std::vector keys(2); + keys[0] = "physical"; + keys[1] = "color"; + TOML11_TEST_PARSE_EQUAL(parse_table_key, "[physical.color]", keys); + } + { + std::vector keys(2); + keys[0] = "physical"; + keys[1] = "shape"; + TOML11_TEST_PARSE_EQUAL(parse_table_key, "[physical.shape]", keys); + } + { + std::vector keys(4); + keys[0] = "x"; + keys[1] = "y"; + keys[2] = "z"; + keys[3] = "w"; + TOML11_TEST_PARSE_EQUAL(parse_table_key, "[x.y.z.w]", keys); + TOML11_TEST_PARSE_EQUAL(parse_table_key, "[x . y . z . w]", keys); + TOML11_TEST_PARSE_EQUAL(parse_table_key, "[x. y .z. w]", keys); + TOML11_TEST_PARSE_EQUAL(parse_table_key, "[x .y. z .w]", keys); + TOML11_TEST_PARSE_EQUAL(parse_table_key, "[ x. y .z . w ]", keys); + TOML11_TEST_PARSE_EQUAL(parse_table_key, "[ x . y . z . w ]", keys); + } + { + std::vector keys(2); + keys[0] = "site"; + keys[1] = "google.com"; + TOML11_TEST_PARSE_EQUAL(parse_table_key, "[site.\"google.com\"]", keys); + } +} + +BOOST_AUTO_TEST_CASE(test_array_of_table_bare_key) +{ + TOML11_TEST_PARSE_EQUAL(parse_array_table_key, "[[barekey]]", std::vector(1, "barekey")); + TOML11_TEST_PARSE_EQUAL(parse_array_table_key, "[[bare-key]]", std::vector(1, "bare-key")); + TOML11_TEST_PARSE_EQUAL(parse_array_table_key, "[[bare_key]]", std::vector(1, "bare_key")); + TOML11_TEST_PARSE_EQUAL(parse_array_table_key, "[[1234]]", std::vector(1, "1234")); +} + +BOOST_AUTO_TEST_CASE(test_array_of_table_quoted_key) +{ + TOML11_TEST_PARSE_EQUAL(parse_array_table_key, "[[\"127.0.0.1\"]]", std::vector(1, "127.0.0.1" )); + TOML11_TEST_PARSE_EQUAL(parse_array_table_key, "[[\"character encoding\"]]", std::vector(1, "character encoding")); + TOML11_TEST_PARSE_EQUAL(parse_array_table_key, "[[\"ʎǝʞ\"]]", std::vector(1, "ʎǝʞ" )); + TOML11_TEST_PARSE_EQUAL(parse_array_table_key, "[['key2']]", std::vector(1, "key2" )); + TOML11_TEST_PARSE_EQUAL(parse_array_table_key, "[['quoted \"value\"']]", std::vector(1, "quoted \"value\"" )); +} + +BOOST_AUTO_TEST_CASE(test_array_of_table_dotted_key) +{ + { + std::vector keys(2); + keys[0] = "physical"; + keys[1] = "color"; + TOML11_TEST_PARSE_EQUAL(parse_array_table_key, "[[physical.color]]", keys); + } + { + std::vector keys(2); + keys[0] = "physical"; + keys[1] = "shape"; + TOML11_TEST_PARSE_EQUAL(parse_array_table_key, "[[physical.shape]]", keys); + } + { + std::vector keys(4); + keys[0] = "x"; + keys[1] = "y"; + keys[2] = "z"; + keys[3] = "w"; + TOML11_TEST_PARSE_EQUAL(parse_array_table_key, "[[x.y.z.w]]", keys); + TOML11_TEST_PARSE_EQUAL(parse_array_table_key, "[[x . y . z . w]]", keys); + TOML11_TEST_PARSE_EQUAL(parse_array_table_key, "[[x. y .z. w]]", keys); + TOML11_TEST_PARSE_EQUAL(parse_array_table_key, "[[x .y. z .w]]", keys); + TOML11_TEST_PARSE_EQUAL(parse_array_table_key, "[[ x. y .z . w ]]", keys); + TOML11_TEST_PARSE_EQUAL(parse_array_table_key, "[[ x . y . z . w ]]", keys); + + } + { + std::vector keys(2); + keys[0] = "site"; + keys[1] = "google.com"; + TOML11_TEST_PARSE_EQUAL(parse_array_table_key, "[[site.\"google.com\"]]", keys); + } +} diff --git a/tests/test_parser.cpp b/tests/test_parser.cpp deleted file mode 100644 index 3a04c13d..00000000 --- a/tests/test_parser.cpp +++ /dev/null @@ -1,935 +0,0 @@ -#define BOOST_TEST_MODULE "test_parser" -#ifdef UNITTEST_FRAMEWORK_LIBRARY_EXIST -#include -#else -#define BOOST_TEST_NO_LIB -#include -#endif -#include -#include -#include -#include - - -BOOST_AUTO_TEST_CASE(test_parse_basic_inline_string) -{ - typedef toml::parse_basic_inline_string parser; - typedef toml::is_basic_inline_string acceptor; - { - const std::string source("\"simple\""); - const std::string expected("simple"); - const auto result = parser::invoke(source.cbegin(), source.cend()); - BOOST_CHECK(result.first.ok()); - BOOST_CHECK_EQUAL(result.first.get(), expected); - BOOST_CHECK(result.second == acceptor::invoke(source.begin(), source.end())); - } - { - const std::string source("\"I'm a string. \\\"You can quote me\\\". Name\\tJos\\u00E9\\nLocation\\tSF.\""); - const std::string expected("I'm a string. \"You can quote me\". Name\tJosé\nLocation\tSF."); - const auto result = parser::invoke(source.cbegin(), source.cend()); - BOOST_CHECK(result.first.ok()); - BOOST_CHECK_EQUAL(result.first.get(), expected); - BOOST_CHECK(result.second == acceptor::invoke(source.begin(), source.end())); - } - { - const std::string source("dummy"); - const auto result = parser::invoke(source.cbegin(), source.cend()); - BOOST_CHECK(!result.first.ok()); - BOOST_CHECK(result.second == source.begin()); - } -} - -BOOST_AUTO_TEST_CASE(test_parse_basic_multiline_string) -{ - typedef toml::parse_basic_multiline_string parser; - typedef toml::is_basic_multiline_string acceptor; - { - //XXX ifdef windows platform - const std::string source("\"\"\"\nRoses are red\nViolets are blue\"\"\""); - const std::string expected("Roses are red\nViolets are blue"); - const auto result = parser::invoke(source.cbegin(), source.cend()); - BOOST_CHECK(result.first.ok()); - BOOST_CHECK_EQUAL(result.first.get(), expected); - BOOST_CHECK(result.second == acceptor::invoke(source.begin(), source.end())); - } - { - const std::string source("\"\"\"\nThe quick brown \\\n\n fox jumps over \\\n the lazy dog.\"\"\""); - const std::string expected("The quick brown fox jumps over the lazy dog."); - const auto result = parser::invoke(source.cbegin(), source.cend()); - BOOST_CHECK(result.first.ok()); - BOOST_CHECK_EQUAL(result.first.get(), expected); - BOOST_CHECK(result.second == acceptor::invoke(source.begin(), source.end())); - } - { - const std::string source("\"\"\"\nThe quick brown \\\n fox jumps over \\\n the lazy dog.\\\n \"\"\""); - const std::string expected("The quick brown fox jumps over the lazy dog."); - const auto result = parser::invoke(source.cbegin(), source.cend()); - BOOST_CHECK(result.first.ok()); - BOOST_CHECK_EQUAL(result.first.get(), expected); - BOOST_CHECK(result.second == acceptor::invoke(source.begin(), source.end())); - } - { - const std::string source("dummy"); - const auto result = parser::invoke(source.cbegin(), source.cend()); - BOOST_CHECK(!result.first.ok()); - BOOST_CHECK(result.second == source.begin()); - } -} - -BOOST_AUTO_TEST_CASE(test_parse_literal_inline_string) -{ - typedef toml::parse_literal_inline_string parser; - typedef toml::is_literal_inline_string acceptor; - { - const std::string source("'C:\\Users\\nodejs\\templates'"); - const std::string expected("C:\\Users\\nodejs\\templates"); - const auto result = parser::invoke(source.cbegin(), source.cend()); - BOOST_CHECK(result.first.ok()); - BOOST_CHECK_EQUAL(result.first.get(), expected); - BOOST_CHECK(result.second == acceptor::invoke(source.begin(), source.end())); - } - { - const std::string source("'\\\\ServerX\\admin$\\system32\\'"); - const std::string expected("\\\\ServerX\\admin$\\system32\\"); - const auto result = parser::invoke(source.cbegin(), source.cend()); - BOOST_CHECK(result.first.ok()); - BOOST_CHECK_EQUAL(result.first.get(), expected); - BOOST_CHECK(result.second == acceptor::invoke(source.begin(), source.end())); - } - { - const std::string source("'Tom \"Dubs\" Preston-Werner'"); - const std::string expected("Tom \"Dubs\" Preston-Werner"); - const auto result = parser::invoke(source.cbegin(), source.cend()); - BOOST_CHECK(result.first.ok()); - BOOST_CHECK_EQUAL(result.first.get(), expected); - BOOST_CHECK(result.second == acceptor::invoke(source.begin(), source.end())); - } - { - const std::string source("'<\\i\\c*\\s*>'"); - const std::string expected("<\\i\\c*\\s*>"); - const auto result = parser::invoke(source.cbegin(), source.cend()); - BOOST_CHECK(result.first.ok()); - BOOST_CHECK_EQUAL(result.first.get(), expected); - BOOST_CHECK(result.second == acceptor::invoke(source.begin(), source.end())); - } - { - const std::string source("dummy"); - const auto result = parser::invoke(source.cbegin(), source.cend()); - BOOST_CHECK(!result.first.ok()); - BOOST_CHECK(result.second == source.begin()); - } -} - -BOOST_AUTO_TEST_CASE(test_parse_literal_multiline_string) -{ - typedef toml::parse_literal_multiline_string parser; - typedef toml::is_literal_multiline_string acceptor; - { - const std::string source("'''I [dw]on't need \\d{2} apples'''"); - const std::string expected("I [dw]on't need \\d{2} apples"); - const auto result = parser::invoke(source.cbegin(), source.cend()); - BOOST_CHECK(result.first.ok()); - BOOST_CHECK_EQUAL(result.first.get(), expected); - BOOST_CHECK(result.second == acceptor::invoke(source.begin(), source.end())); - } - { - const std::string source("'''\nThe first newline is \ntrimmed in raw strings.\n All other whitespace\n is preserved.'''"); - const std::string expected("The first newline is \ntrimmed in raw strings.\n All other whitespace\n is preserved."); - const auto result = parser::invoke(source.cbegin(), source.cend()); - BOOST_CHECK(result.first.ok()); - BOOST_CHECK_EQUAL(result.first.get(), expected); - BOOST_CHECK(result.second == acceptor::invoke(source.begin(), source.end())); - } - { - const std::string source("dummy"); - const auto result = parser::invoke(source.cbegin(), source.cend()); - BOOST_CHECK(!result.first.ok()); - BOOST_CHECK(result.second == source.begin()); - } -} - -BOOST_AUTO_TEST_CASE(test_parse_string) -{ - typedef toml::parse_string parser; - typedef toml::is_string acceptor; - { - const std::string source("\"string\""); - const std::string expected("string"); - const auto result = parser::invoke(source.cbegin(), source.cend()); - BOOST_CHECK(result.first.ok()); - BOOST_CHECK_EQUAL(result.first.get(), expected); - BOOST_CHECK(result.second == acceptor::invoke(source.begin(), source.end())); - } - { - const std::string source("\"\"\"string\"\"\""); - const std::string expected("string"); - const auto result = parser::invoke(source.cbegin(), source.cend()); - BOOST_CHECK(result.first.ok()); - BOOST_CHECK_EQUAL(result.first.get(), expected); - BOOST_CHECK(result.second == acceptor::invoke(source.begin(), source.end())); - } - { - const std::string source("'string'"); - const std::string expected("string"); - const auto result = parser::invoke(source.cbegin(), source.cend()); - BOOST_CHECK(result.first.ok()); - BOOST_CHECK_EQUAL(result.first.get(), expected); - BOOST_CHECK(result.second == acceptor::invoke(source.begin(), source.end())); - } - { - const std::string source("'''string'''"); - const std::string expected("string"); - const auto result = parser::invoke(source.cbegin(), source.cend()); - BOOST_CHECK(result.first.ok()); - BOOST_CHECK_EQUAL(result.first.get(), expected); - BOOST_CHECK(result.second == acceptor::invoke(source.begin(), source.end())); - } - { - const std::string source("dummy"); - const auto result = parser::invoke(source.cbegin(), source.cend()); - BOOST_CHECK(!result.first.ok()); - BOOST_CHECK(result.second == source.begin()); - } -} - -BOOST_AUTO_TEST_CASE(test_integer) -{ - typedef toml::parse_integer parser; - typedef toml::is_integer acceptor; - { - const std::string source("42"); - const toml::Integer expected(42); - const auto result = parser::invoke(source.cbegin(), source.cend()); - BOOST_CHECK(result.first.ok()); - BOOST_CHECK_EQUAL(result.first.get(), expected); - BOOST_CHECK(result.second == acceptor::invoke(source.begin(), source.end())); - } - { - const std::string source("+42"); - const toml::Integer expected(42); - const auto result = parser::invoke(source.cbegin(), source.cend()); - BOOST_CHECK(result.first.ok()); - BOOST_CHECK_EQUAL(result.first.get(), expected); - BOOST_CHECK(result.second == acceptor::invoke(source.begin(), source.end())); - } - { - const std::string source("-42"); - const toml::Integer expected(-42); - const auto result = parser::invoke(source.cbegin(), source.cend()); - BOOST_CHECK(result.first.ok()); - BOOST_CHECK_EQUAL(result.first.get(), expected); - BOOST_CHECK(result.second == acceptor::invoke(source.begin(), source.end())); - } - { - const std::string source("-4_2"); - const toml::Integer expected(-42); - const auto result = parser::invoke(source.cbegin(), source.cend()); - BOOST_CHECK(result.first.ok()); - BOOST_CHECK_EQUAL(result.first.get(), expected); - BOOST_CHECK(result.second == acceptor::invoke(source.begin(), source.end())); - } - { - const std::string source("dummy"); - const auto result = parser::invoke(source.cbegin(), source.cend()); - BOOST_CHECK(!result.first.ok()); - BOOST_CHECK(result.second == source.begin()); - } -} - -BOOST_AUTO_TEST_CASE(test_float) -{ - typedef toml::parse_float parser; - typedef toml::is_float acceptor; - { - const std::string source("42.0"); - const toml::Float expected(42.0); - const auto result = parser::invoke(source.cbegin(), source.cend()); - BOOST_CHECK(result.first.ok()); - BOOST_CHECK_EQUAL(result.first.get(), expected); - BOOST_CHECK(result.second == acceptor::invoke(source.begin(), source.end())); - } - { - const std::string source("+42.0"); - const toml::Float expected(42.0); - const auto result = parser::invoke(source.cbegin(), source.cend()); - BOOST_CHECK(result.first.ok()); - BOOST_CHECK_EQUAL(result.first.get(), expected); - BOOST_CHECK(result.second == acceptor::invoke(source.begin(), source.end())); - } - { - const std::string source("-42.0"); - const toml::Float expected(-42.0); - const auto result = parser::invoke(source.cbegin(), source.cend()); - BOOST_CHECK(result.first.ok()); - BOOST_CHECK_EQUAL(result.first.get(), expected); - BOOST_CHECK(result.second == acceptor::invoke(source.begin(), source.end())); - } - { - const std::string source("-4_2.0"); - const toml::Float expected(-42.0); - const auto result = parser::invoke(source.cbegin(), source.cend()); - BOOST_CHECK(result.first.ok()); - BOOST_CHECK_EQUAL(result.first.get(), expected); - BOOST_CHECK(result.second == acceptor::invoke(source.begin(), source.end())); - } - { - const std::string source("-42e0"); - const toml::Float expected(-42.0); - const auto result = parser::invoke(source.cbegin(), source.cend()); - BOOST_CHECK(result.first.ok()); - BOOST_CHECK_EQUAL(result.first.get(), expected); - BOOST_CHECK(result.second == acceptor::invoke(source.begin(), source.end())); - } - { - const std::string source("-42.0e0"); - const toml::Float expected(-42.0); - const auto result = parser::invoke(source.cbegin(), source.cend()); - BOOST_CHECK(result.first.ok()); - BOOST_CHECK_EQUAL(result.first.get(), expected); - BOOST_CHECK(result.second == acceptor::invoke(source.begin(), source.end())); - } - { - const std::string source("dummy"); - const auto result = parser::invoke(source.cbegin(), source.cend()); - BOOST_CHECK(!result.first.ok()); - BOOST_CHECK(result.second == source.begin()); - } - { - const std::string source("42"); - const auto result = parser::invoke(source.cbegin(), source.cend()); - BOOST_CHECK(!result.first.ok()); - BOOST_CHECK(result.second == source.begin()); - } -} - -BOOST_AUTO_TEST_CASE(test_parse_boolean) -{ - typedef toml::parse_boolean parser; - typedef toml::is_boolean acceptor; - { - const std::string source("true"); - const toml::Boolean expected(true); - const auto result = parser::invoke(source.cbegin(), source.cend()); - BOOST_CHECK(result.first.ok()); - BOOST_CHECK_EQUAL(result.first.get(), expected); - BOOST_CHECK(result.second == acceptor::invoke(source.begin(), source.end())); - } - { - const std::string source("false"); - const toml::Boolean expected(false); - const auto result = parser::invoke(source.cbegin(), source.cend()); - BOOST_CHECK(result.first.ok()); - BOOST_CHECK_EQUAL(result.first.get(), expected); - BOOST_CHECK(result.second == acceptor::invoke(source.begin(), source.end())); - } - { - const std::string source("dummy"); - const auto result = parser::invoke(source.cbegin(), source.cend()); - BOOST_CHECK(!result.first.ok()); - BOOST_CHECK(result.second == source.begin()); - } -} - - - -BOOST_AUTO_TEST_CASE(test_parse_local_time) -{ - typedef toml::parse_local_time parser; - typedef toml::is_local_time acceptor; - { - const std::string source("12:34:56"); - const toml::Datetime expected(12, 34, 56, 0, 0); - const auto result = parser::invoke(source.cbegin(), source.cend()); - BOOST_CHECK(result.first.ok()); - BOOST_CHECK_EQUAL(result.first.get(), expected); - BOOST_CHECK(result.second == acceptor::invoke(source.begin(), source.end())); - } - { - const std::string source("12:34:56.7"); - const toml::Datetime expected(12, 34, 56, 700, 0); - const auto result = parser::invoke(source.cbegin(), source.cend()); - BOOST_CHECK(result.first.ok()); - BOOST_CHECK_EQUAL(result.first.get(), expected); - BOOST_CHECK(result.second == acceptor::invoke(source.begin(), source.end())); - } - { - const std::string source("12:34:56.7891"); - const toml::Datetime expected(12, 34, 56, 789, 100); - const auto result = parser::invoke(source.cbegin(), source.cend()); - BOOST_CHECK(result.first.ok()); - BOOST_CHECK_EQUAL(result.first.get(), expected); - BOOST_CHECK(result.second == acceptor::invoke(source.begin(), source.end())); - } - { - const std::string source("10"); - const auto result = parser::invoke(source.cbegin(), source.cend()); - BOOST_CHECK(!result.first.ok()); - BOOST_CHECK(result.second == source.begin()); - } -} - -BOOST_AUTO_TEST_CASE(test_parse_local_date) -{ - typedef toml::parse_local_date parser; - typedef toml::is_local_date acceptor; - { - const std::string source("1979-09-27"); - const toml::Datetime expected(1979, 9, 27); - const auto result = parser::invoke(source.cbegin(), source.cend()); - BOOST_CHECK(result.first.ok()); - BOOST_CHECK_EQUAL(result.first.get(), expected); - BOOST_CHECK(result.second == acceptor::invoke(source.begin(), source.end())); - } - { - const std::string source("10"); - const auto result = parser::invoke(source.cbegin(), source.cend()); - BOOST_CHECK(!result.first.ok()); - BOOST_CHECK(result.second == source.begin()); - } -} - -BOOST_AUTO_TEST_CASE(test_parse_local_date_time) -{ - typedef toml::parse_local_date_time parser; - typedef toml::is_local_date_time acceptor; - { - const std::string source("1979-09-27T12:34:56"); - const toml::Datetime expected(1979, 9, 27, 12, 34, 56, 0, 0); - const auto result = parser::invoke(source.cbegin(), source.cend()); - BOOST_CHECK(result.first.ok()); - BOOST_CHECK_EQUAL(result.first.get(), expected); - BOOST_CHECK(result.second == acceptor::invoke(source.begin(), source.end())); - } - { - const std::string source("1979-09-27T12:34:56.789000"); - const toml::Datetime expected(1979, 9, 27, 12, 34, 56, 789, 0); - const auto result = parser::invoke(source.cbegin(), source.cend()); - BOOST_CHECK(result.first.ok()); - BOOST_CHECK_EQUAL(result.first.get(), expected); - BOOST_CHECK(result.second == acceptor::invoke(source.begin(), source.end())); - } - { - const std::string source("1000-11-11"); - const auto result = parser::invoke(source.cbegin(), source.cend()); - BOOST_CHECK(!result.first.ok()); - BOOST_CHECK(result.second == source.begin()); - } -} - -BOOST_AUTO_TEST_CASE(test_parse_offset_date_time) -{ - typedef toml::parse_offset_date_time parser; - typedef toml::is_offset_date_time acceptor; - { - const std::string source("1979-09-27T12:34:56Z"); - const toml::Datetime expected(1979, 9, 27, 12, 34, 56, 0, 0, 0, 0); - const auto result = parser::invoke(source.cbegin(), source.cend()); - BOOST_CHECK(result.first.ok()); - BOOST_CHECK_EQUAL(result.first.get(), expected); - BOOST_CHECK(result.second == acceptor::invoke(source.begin(), source.end())); - } - { - const std::string source("1979-09-27T12:34:56.789000Z"); - const toml::Datetime expected(1979, 9, 27, 12, 34, 56, 789, 0, 0, 0); - const auto result = parser::invoke(source.cbegin(), source.cend()); - BOOST_CHECK(result.first.ok()); - BOOST_CHECK_EQUAL(result.first.get(), expected); - BOOST_CHECK(result.second == acceptor::invoke(source.begin(), source.end())); - } - - { - const std::string source("1979-09-27T12:34:56+07:30"); - const toml::Datetime expected(1979, 9, 27, 12, 34, 56, 0, 0, 7, 30); - const auto result = parser::invoke(source.cbegin(), source.cend()); - BOOST_CHECK(result.first.ok()); - BOOST_CHECK_EQUAL(result.first.get(), expected); - BOOST_CHECK(result.second == acceptor::invoke(source.begin(), source.end())); - } - { - const std::string source("1979-09-27T12:34:56.789000+07:30"); - const toml::Datetime expected(1979, 9, 27, 12, 34, 56, 789, 0, 7, 30); - const auto result = parser::invoke(source.cbegin(), source.cend()); - BOOST_CHECK(result.first.ok()); - BOOST_CHECK_EQUAL(result.first.get(), expected); - BOOST_CHECK(result.second == acceptor::invoke(source.begin(), source.end())); - } - - { - const std::string source("1979-09-27T12:34:56-07:30"); - const toml::Datetime expected(1979, 9, 27, 12, 34, 56, 0, 0, -7, -30); - const auto result = parser::invoke(source.cbegin(), source.cend()); - BOOST_CHECK(result.first.ok()); - BOOST_CHECK_EQUAL(result.first.get(), expected); - BOOST_CHECK(result.second == acceptor::invoke(source.begin(), source.end())); - } - { - const std::string source("1979-09-27T12:34:56.789000-07:30"); - const toml::Datetime expected(1979, 9, 27, 12, 34, 56, 789, 0, -7, -30); - const auto result = parser::invoke(source.cbegin(), source.cend()); - BOOST_CHECK(result.first.ok()); - BOOST_CHECK_EQUAL(result.first.get(), expected); - BOOST_CHECK(result.second == acceptor::invoke(source.begin(), source.end())); - } - { - const std::string source("1000-11-11"); - const auto result = parser::invoke(source.cbegin(), source.cend()); - BOOST_CHECK(!result.first.ok()); - BOOST_CHECK(result.second == source.begin()); - } -} - -BOOST_AUTO_TEST_CASE(test_parse_datetime) -{ - typedef toml::parse_datetime parser; - typedef toml::is_datetime acceptor; - { - const std::string source("1979-09-27T12:34:56Z"); - const toml::Datetime expected(1979, 9, 27, 12, 34, 56, 0, 0, 0, 0); - const auto result = parser::invoke(source.cbegin(), source.cend()); - BOOST_CHECK(result.first.ok()); - BOOST_CHECK_EQUAL(result.first.get(), expected); - BOOST_CHECK(result.second == acceptor::invoke(source.begin(), source.end())); - } - { - const std::string source("1979-09-27T12:34:56"); - const toml::Datetime expected(1979, 9, 27, 12, 34, 56, 0, 0); - const auto result = parser::invoke(source.cbegin(), source.cend()); - BOOST_CHECK(result.first.ok()); - BOOST_CHECK_EQUAL(result.first.get(), expected); - BOOST_CHECK(result.second == acceptor::invoke(source.begin(), source.end())); - } - { - const std::string source("1979-09-27"); - const toml::Datetime expected(1979, 9, 27); - const auto result = parser::invoke(source.cbegin(), source.cend()); - BOOST_CHECK(result.first.ok()); - BOOST_CHECK_EQUAL(result.first.get(), expected); - BOOST_CHECK(result.second == acceptor::invoke(source.begin(), source.end())); - } - { - const std::string source("12:34:56"); - const toml::Datetime expected(12, 34, 56, 0, 0); - const auto result = parser::invoke(source.cbegin(), source.cend()); - BOOST_CHECK(result.first.ok()); - BOOST_CHECK_EQUAL(result.first.get(), expected); - BOOST_CHECK(result.second == acceptor::invoke(source.begin(), source.end())); - } - { - const std::string source("12"); - const auto result = parser::invoke(source.cbegin(), source.cend()); - BOOST_CHECK(!result.first.ok()); - BOOST_CHECK(result.second == source.begin()); - } -} - - -BOOST_AUTO_TEST_CASE(test_parse_array) -{ - typedef toml::parse_array parser; - typedef toml::is_array acceptor; - { - const std::string source("[1,2,3]"); - const toml::Array expected{1, 2, 3}; - const auto result = parser::invoke(source.cbegin(), source.cend()); - BOOST_CHECK(result.first.ok()); - BOOST_CHECK(result.first.get() == expected); - BOOST_CHECK(result.second == acceptor::invoke(source.begin(), source.end())); - } - { - const std::string source("[1, 2, 3]"); - const toml::Array expected{1, 2, 3}; - const auto result = parser::invoke(source.cbegin(), source.cend()); - BOOST_CHECK(result.first.ok()); - BOOST_CHECK(result.first.get() == expected); - BOOST_CHECK(result.second == acceptor::invoke(source.begin(), source.end())); - } - { - const std::string source("[ 1,2,3 ]"); - const toml::Array expected{1, 2, 3}; - const auto result = parser::invoke(source.cbegin(), source.cend()); - BOOST_CHECK(result.first.ok()); - BOOST_CHECK(result.first.get() == expected); - BOOST_CHECK(result.second == acceptor::invoke(source.begin(), source.end())); - } - { - const std::string source("[ 1 , 2 , 3 ]"); - const toml::Array expected{1, 2, 3}; - const auto result = parser::invoke(source.cbegin(), source.cend()); - BOOST_CHECK(result.first.ok()); - BOOST_CHECK(result.first.get() == expected); - BOOST_CHECK(result.second == acceptor::invoke(source.begin(), source.end())); - } - { - const std::string source("[ 1 \n,#comment\n 2 ,\n 3\n ]"); - const toml::Array expected{1, 2, 3}; - const auto result = parser::invoke(source.cbegin(), source.cend()); - BOOST_CHECK(result.first.ok()); - BOOST_CHECK(result.first.get() == expected); - BOOST_CHECK(result.second == acceptor::invoke(source.begin(), source.end())); - } - { - const std::string source("[ # empty array\n ]"); - const toml::Array expected{}; - const auto result = parser::invoke(source.cbegin(), source.cend()); - BOOST_CHECK(result.first.ok()); - BOOST_CHECK(result.first.get() == expected); - BOOST_CHECK(result.second == acceptor::invoke(source.begin(), source.end())); - } - { - const std::string source("[ \"] \", ' # ', \n']', # ] \n]"); - const toml::Array expected{"] ", " # ", "]"}; - const auto result = parser::invoke(source.cbegin(), source.cend()); - BOOST_CHECK(result.first.ok()); - BOOST_CHECK(result.first.get() == expected); - BOOST_CHECK(result.second == acceptor::invoke(source.begin(), source.end())); - } - - { - const std::string source("[ \"Test #11 ]proved that\", 'Experiment #9 was a success' ]"); - const toml::Array expected{"Test #11 ]proved that", "Experiment #9 was a success"}; - const auto result = parser::invoke(source.cbegin(), source.cend()); - BOOST_CHECK(result.first.ok()); - BOOST_CHECK(result.first.get() == expected); - BOOST_CHECK(result.second == acceptor::invoke(source.begin(), source.end())); - } - - { - const std::string source("[ \"Test #11 ]proved that\", 'Experiment #9 was a success' ]"); - const toml::Array expected{"Test #11 ]proved that", "Experiment #9 was a success"}; - const auto result = parser::invoke(source.cbegin(), source.cend()); - BOOST_CHECK(result.first.ok()); - BOOST_CHECK(result.first.get() == expected); - BOOST_CHECK(result.second == acceptor::invoke(source.begin(), source.end())); - } - - { - const std::string source("[ [1,2,3] , ['a', 'b', 'c'] ]"); - const toml::Array expected{{1,2,3}, {"a", "b", "c"}}; - const auto result = parser::invoke(source.cbegin(), source.cend()); - BOOST_CHECK(result.first.ok()); - BOOST_CHECK(result.first.get() == expected); - BOOST_CHECK(result.second == acceptor::invoke(source.begin(), source.end())); - } - - { - const std::string source("[ {foo=1}, {foo=1, bar=2.0}, {foo=1, bar=2.0, baz='str'} ]"); - const toml::Array expected{{{"foo", 1}}, {{"foo", 1}, {"bar", 2.0}}, {{"foo", 1}, {"bar", 2.0}, {"baz", "str"}}}; - const auto result = parser::invoke(source.cbegin(), source.cend()); - BOOST_CHECK(result.first.ok()); - BOOST_CHECK(result.first.get() == expected); - BOOST_CHECK(result.second == acceptor::invoke(source.begin(), source.end())); - } - { - const std::string source("[dummy]"); - const auto result = parser::invoke(source.cbegin(), source.cend()); - BOOST_CHECK(!result.first.ok()); - BOOST_CHECK(result.second == source.begin()); - } -} - - -BOOST_AUTO_TEST_CASE(test_parse_inline_table) -{ - typedef toml::parse_inline_table parser; - typedef toml::is_inline_table acceptor; - { - const std::string source("{foo=1,bar=2.0,baz='str'}"); - const toml::Table expected{{"foo", 1}, {"bar", 2.0}, {"baz", "str"}}; - const auto result = parser::invoke(source.cbegin(), source.cend()); - BOOST_CHECK(result.first.ok()); - BOOST_CHECK(result.first.get() == expected); - BOOST_CHECK(result.second == acceptor::invoke(source.begin(), source.end())); - } - { - const std::string source("{ foo=1, bar=2.0, baz='str' }"); - const toml::Table expected{{"foo", 1}, {"bar", 2.0}, {"baz", "str"}}; - const auto result = parser::invoke(source.cbegin(), source.cend()); - BOOST_CHECK(result.first.ok()); - BOOST_CHECK(result.first.get() == expected); - BOOST_CHECK(result.second == acceptor::invoke(source.begin(), source.end())); - } - { - const std::string source("{ foo = 1, bar = 2.0, baz = 'str' }"); - const toml::Table expected{{"foo", 1}, {"bar", 2.0}, {"baz", "str"}}; - const auto result = parser::invoke(source.cbegin(), source.cend()); - BOOST_CHECK(result.first.ok()); - BOOST_CHECK(result.first.get() == expected); - BOOST_CHECK(result.second == acceptor::invoke(source.begin(), source.end())); - } - - { - const std::string source("{b=true, i=1, f=2.0, d=1907-03-02T07:32:00, s='str', a=[1,2,3], t={foo=1}}"); - const toml::Table expected{{"b", true}, {"i", 1}, {"f", 2.0}, - {"d", toml::Datetime(1907,3,2,7,32,0,0,0)}, - {"s", "str"}, {"a", {1, 2, 3}}, - {"t", {{"foo", 1}}}}; - const auto result = parser::invoke(source.cbegin(), source.cend()); - BOOST_CHECK(result.first.ok()); - BOOST_CHECK(result.first.get() == expected); - BOOST_CHECK(result.second == acceptor::invoke(source.begin(), source.end())); - } - { - const std::string source("{dummy}"); - const auto result = parser::invoke(source.cbegin(), source.cend()); - BOOST_CHECK(!result.first.ok()); - BOOST_CHECK(result.second == source.begin()); - } -} - -BOOST_AUTO_TEST_CASE(test_parse_barekey) -{ - typedef toml::parse_barekey parser; - typedef toml::is_barekey acceptor; - { - const std::string source("hoge"); - const toml::key expected("hoge"); - const auto result = parser::invoke(source.cbegin(), source.cend()); - BOOST_CHECK(result.first.ok()); - BOOST_CHECK(result.first.get() == expected); - BOOST_CHECK(result.second == acceptor::invoke(source.begin(), source.end())); - } - { - const std::string source("bare-key"); - const toml::key expected("bare-key"); - const auto result = parser::invoke(source.cbegin(), source.cend()); - BOOST_CHECK(result.first.ok()); - BOOST_CHECK(result.first.get() == expected); - BOOST_CHECK(result.second == acceptor::invoke(source.begin(), source.end())); - } - { - const std::string source("bare_key"); - const toml::key expected("bare_key"); - const auto result = parser::invoke(source.cbegin(), source.cend()); - BOOST_CHECK(result.first.ok()); - BOOST_CHECK(result.first.get() == expected); - BOOST_CHECK(result.second == acceptor::invoke(source.begin(), source.end())); - } - { - const std::string source("42"); - const toml::key expected("42"); - const auto result = parser::invoke(source.cbegin(), source.cend()); - BOOST_CHECK(result.first.ok()); - BOOST_CHECK(result.first.get() == expected); - BOOST_CHECK(result.second == acceptor::invoke(source.begin(), source.end())); - } -} - - - -BOOST_AUTO_TEST_CASE(test_key_value_pair) -{ - typedef toml::parse_key_value_pair parser; - typedef toml::is_key_value_pair acceptor; - { - const std::string source("key=1"); - const std::pair expected{"key", 1}; - const auto result = parser::invoke(source.cbegin(), source.cend()); - BOOST_CHECK(result.first.ok()); - BOOST_CHECK(result.first.get() == expected); - BOOST_CHECK(result.second == acceptor::invoke(source.begin(), source.end())); - } - { - const std::string source("key =\t1"); - const std::pair expected{"key", 1}; - const auto result = parser::invoke(source.cbegin(), source.cend()); - BOOST_CHECK(result.first.ok()); - BOOST_CHECK(result.first.get() == expected); - BOOST_CHECK(result.second == acceptor::invoke(source.begin(), source.end())); - } - { - const std::string source("key = true"); - const std::pair expected{"key", true}; - const auto result = parser::invoke(source.cbegin(), source.cend()); - BOOST_CHECK(result.first.ok()); - BOOST_CHECK(result.first.get() == expected); - BOOST_CHECK(result.second == acceptor::invoke(source.begin(), source.end())); - } - { - const std::string source("key = -42"); - const std::pair expected{"key", -42}; - const auto result = parser::invoke(source.cbegin(), source.cend()); - BOOST_CHECK(result.first.ok()); - BOOST_CHECK(result.first.get() == expected); - BOOST_CHECK(result.second == acceptor::invoke(source.begin(), source.end())); - } - { - const std::string source("key = -42.0"); - const std::pair expected{"key", -42.}; - const auto result = parser::invoke(source.cbegin(), source.cend()); - BOOST_CHECK(result.first.ok()); - BOOST_CHECK(result.first.get() == expected); - BOOST_CHECK(result.second == acceptor::invoke(source.begin(), source.end())); - } - { - const std::string source("key = \"string\""); - const std::pair expected{"key", "string"}; - const auto result = parser::invoke(source.cbegin(), source.cend()); - BOOST_CHECK(result.first.ok()); - BOOST_CHECK(result.first.get() == expected); - BOOST_CHECK(result.second == acceptor::invoke(source.begin(), source.end())); - } - { - const std::string source("key = 1901-01-01T00:00:00"); - const std::pair expected{"key", toml::Datetime(1901, 1,1,0,0,0,0,0)}; - const auto result = parser::invoke(source.cbegin(), source.cend()); - BOOST_CHECK(result.first.ok()); - BOOST_CHECK(result.first.get() == expected); - BOOST_CHECK(result.second == acceptor::invoke(source.begin(), source.end())); - } - { - const std::string source("key = [1,2,3]"); - const std::pair expected{"key", {1,2,3}}; - const auto result = parser::invoke(source.cbegin(), source.cend()); - BOOST_CHECK(result.first.ok()); - BOOST_CHECK(result.first.get() == expected); - BOOST_CHECK(result.second == acceptor::invoke(source.begin(), source.end())); - } - { - const std::string source("key = {foo=1,bar=2.0,baz='3'}"); - const std::pair expected{"key", - {{"foo", 1}, {"bar", 2.0}, {"baz", "3"}}}; - const auto result = parser::invoke(source.cbegin(), source.cend()); - BOOST_CHECK(result.first.ok()); - BOOST_CHECK(result.first.get() == expected); - BOOST_CHECK(result.second == acceptor::invoke(source.begin(), source.end())); - } -} - -BOOST_AUTO_TEST_CASE(test_table_definition) -{ - typedef toml::parse_table_definition parser; - typedef toml::is_table_definition acceptor; - { - const std::string source("[foo]"); - const std::vector expected{"foo"}; - const auto result = parser::invoke(source.cbegin(), source.cend()); - BOOST_CHECK(result.first.ok()); - BOOST_CHECK(result.first.get() == expected); - BOOST_CHECK(result.second == acceptor::invoke(source.begin(), source.end())); - } - { - const std::string source("[foo.bar.baz]"); - const std::vector expected{"foo", "bar", "baz"}; - const auto result = parser::invoke(source.cbegin(), source.cend()); - BOOST_CHECK(result.first.ok()); - BOOST_CHECK(result.first.get() == expected); - BOOST_CHECK(result.second == acceptor::invoke(source.begin(), source.end())); - } - { - const std::string source("[foo . bar. baz]"); - const std::vector expected{"foo", "bar", "baz"}; - const auto result = parser::invoke(source.cbegin(), source.cend()); - BOOST_CHECK(result.first.ok()); - BOOST_CHECK(result.first.get() == expected); - BOOST_CHECK(result.second == acceptor::invoke(source.begin(), source.end())); - } - { - const std::string source("[foo . \"bar\" . baz]"); - const std::vector expected{"foo", "bar", "baz"}; - const auto result = parser::invoke(source.cbegin(), source.cend()); - BOOST_CHECK(result.first.ok()); - BOOST_CHECK(result.first.get() == expected); - BOOST_CHECK(result.second == acceptor::invoke(source.begin(), source.end())); - } - { - const std::string source("[foo . \"b\\tar\" . baz]"); - const std::vector expected{"foo", "b\tar", "baz"}; - const auto result = parser::invoke(source.cbegin(), source.cend()); - BOOST_CHECK(result.first.ok()); - BOOST_CHECK(result.first.get() == expected); - BOOST_CHECK(result.second == acceptor::invoke(source.begin(), source.end())); - } -} - -BOOST_AUTO_TEST_CASE(test_array_of_table_definition) -{ - typedef toml::parse_array_of_table_definition parser; - typedef toml::is_array_of_table_definition acceptor; - { - const std::string source("[[foo]]"); - const std::vector expected{"foo"}; - const auto result = parser::invoke(source.cbegin(), source.cend()); - BOOST_CHECK(result.first.ok()); - BOOST_CHECK(result.first.get() == expected); - BOOST_CHECK(result.second == acceptor::invoke(source.begin(), source.end())); - } - { - const std::string source("[[foo.bar.baz]]"); - const std::vector expected{"foo", "bar", "baz"}; - const auto result = parser::invoke(source.cbegin(), source.cend()); - BOOST_CHECK(result.first.ok()); - BOOST_CHECK(result.first.get() == expected); - BOOST_CHECK(result.second == acceptor::invoke(source.begin(), source.end())); - } - { - const std::string source("[[foo . bar. baz]]"); - const std::vector expected{"foo", "bar", "baz"}; - const auto result = parser::invoke(source.cbegin(), source.cend()); - BOOST_CHECK(result.first.ok()); - BOOST_CHECK(result.first.get() == expected); - BOOST_CHECK(result.second == acceptor::invoke(source.begin(), source.end())); - } - { - const std::string source("[[foo . \"bar\" . baz]]"); - const std::vector expected{"foo", "bar", "baz"}; - const auto result = parser::invoke(source.cbegin(), source.cend()); - BOOST_CHECK(result.first.ok()); - BOOST_CHECK(result.first.get() == expected); - BOOST_CHECK(result.second == acceptor::invoke(source.begin(), source.end())); - } - { - const std::string source("[[foo . \"b\\tar\" . baz]]"); - const std::vector expected{"foo", "b\tar", "baz"}; - const auto result = parser::invoke(source.cbegin(), source.cend()); - BOOST_CHECK(result.first.ok()); - BOOST_CHECK(result.first.get() == expected); - BOOST_CHECK(result.second == acceptor::invoke(source.begin(), source.end())); - } -} - -BOOST_AUTO_TEST_CASE(test_parse_data) -{ - typedef toml::parse_data parser; - { - const std::string source("#hogehoge"); - const toml::Table expected{}; - const auto result = parser::invoke(source.cbegin(), source.cend()); - BOOST_CHECK(result == expected); - } - { - const std::string source("key = 'value'"); - const toml::Table expected{{"key", "value"}}; - const auto result = parser::invoke(source.cbegin(), source.cend()); - BOOST_CHECK(result == expected); - } - { - const std::string source("key = 'value' #hoge"); - const toml::Table expected{{"key", "value"}}; - const auto result = parser::invoke(source.cbegin(), source.cend()); - BOOST_CHECK(result == expected); - } - { - const std::string source("[table]\nkey = 'value' #hoge"); - const toml::Table expected{{"table", {{"key", "value"}} }}; - const auto result = parser::invoke(source.cbegin(), source.cend()); - BOOST_CHECK(result == expected); - } - { - const std::string source("[table]\n\tkey = 'value'\n\t#hoge"); - const toml::Table expected{{"table", {{"key", "value"}} }}; - const auto result = parser::invoke(source.cbegin(), source.cend()); - BOOST_CHECK(result == expected); - } - { - const std::string source("[table]\n\tkey = 'value'\n\t#hoge"); - const toml::Table expected{{"table", {{"key", "value"}} }}; - const auto result = parser::invoke(source.cbegin(), source.cend()); - BOOST_CHECK(result == expected); - } - { - const std::string source("[nested.table]\n\tkey = 'value'\n\t#hoge"); - const toml::Table expected{{"nested", {{"table", {{"key", "value"}}}}}}; - const auto result = parser::invoke(source.cbegin(), source.cend()); - BOOST_CHECK(result == expected); - } -} diff --git a/tests/test_result.cpp b/tests/test_result.cpp new file mode 100644 index 00000000..5d7bcdd1 --- /dev/null +++ b/tests/test_result.cpp @@ -0,0 +1,411 @@ +#define BOOST_TEST_MODULE "test_result" +#include +#include +#include + +BOOST_AUTO_TEST_CASE(test_construct) +{ + { + auto s = toml::ok(42); + toml::result result(s); + BOOST_CHECK(!!result); + BOOST_CHECK(result.is_ok()); + BOOST_CHECK(!result.is_err()); + BOOST_CHECK_EQUAL(result.unwrap(), 42); + } + { + const auto s = toml::ok(42); + toml::result result(s); + BOOST_CHECK(!!result); + BOOST_CHECK(result.is_ok()); + BOOST_CHECK(!result.is_err()); + BOOST_CHECK_EQUAL(result.unwrap(), 42); + } + { + toml::result result(toml::ok(42)); + BOOST_CHECK(!!result); + BOOST_CHECK(result.is_ok()); + BOOST_CHECK(!result.is_err()); + BOOST_CHECK_EQUAL(result.unwrap(), 42); + } + + { + auto f = toml::err("foobar"); + toml::result result(f); + BOOST_CHECK(!result); + BOOST_CHECK(!result.is_ok()); + BOOST_CHECK(result.is_err()); + BOOST_CHECK_EQUAL(result.unwrap_err(), "foobar"); + } + { + const auto f = toml::err("foobar"); + toml::result result(f); + BOOST_CHECK(!result); + BOOST_CHECK(!result.is_ok()); + BOOST_CHECK(result.is_err()); + BOOST_CHECK_EQUAL(result.unwrap_err(), "foobar"); + } + { + toml::result result(toml::err("foobar")); + BOOST_CHECK(!result); + BOOST_CHECK(!result.is_ok()); + BOOST_CHECK(result.is_err()); + BOOST_CHECK_EQUAL(result.unwrap_err(), "foobar"); + } +} + +BOOST_AUTO_TEST_CASE(test_assignment) +{ + { + toml::result result(toml::err("foobar")); + result = toml::ok(42); + BOOST_CHECK(!!result); + BOOST_CHECK(result.is_ok()); + BOOST_CHECK(!result.is_err()); + BOOST_CHECK_EQUAL(result.unwrap(), 42); + } + { + toml::result result(toml::err("foobar")); + auto s = toml::ok(42); + result = s; + BOOST_CHECK(!!result); + BOOST_CHECK(result.is_ok()); + BOOST_CHECK(!result.is_err()); + BOOST_CHECK_EQUAL(result.unwrap(), 42); + } + { + toml::result result(toml::err("foobar")); + const auto s = toml::ok(42); + result = s; + BOOST_CHECK(!!result); + BOOST_CHECK(result.is_ok()); + BOOST_CHECK(!result.is_err()); + BOOST_CHECK_EQUAL(result.unwrap(), 42); + } + { + toml::result result(toml::err("foobar")); + result = toml::err("hoge"); + BOOST_CHECK(!result); + BOOST_CHECK(!result.is_ok()); + BOOST_CHECK(result.is_err()); + BOOST_CHECK_EQUAL(result.unwrap_err(), "hoge"); + } + { + toml::result result(toml::err("foobar")); + auto f = toml::err("hoge"); + result = f; + BOOST_CHECK(!result); + BOOST_CHECK(!result.is_ok()); + BOOST_CHECK(result.is_err()); + BOOST_CHECK_EQUAL(result.unwrap_err(), "hoge"); + } + { + toml::result result(toml::err("foobar")); + const auto f = toml::err("hoge"); + result = f; + BOOST_CHECK(!result); + BOOST_CHECK(!result.is_ok()); + BOOST_CHECK(result.is_err()); + BOOST_CHECK_EQUAL(result.unwrap_err(), "hoge"); + } +} + +BOOST_AUTO_TEST_CASE(test_map) +{ + { + const toml::result result(toml::ok(42)); + const auto mapped = result.map( + [](const int i) -> int { + return i * 2; + }); + + BOOST_CHECK(!!mapped); + BOOST_CHECK(mapped.is_ok()); + BOOST_CHECK(!mapped.is_err()); + BOOST_CHECK_EQUAL(mapped.unwrap(), 42 * 2); + } + { + toml::result, std::string> + result(toml::ok(std::unique_ptr(new int(42)))); + const auto mapped = std::move(result).map( + [](std::unique_ptr i) -> int { + return *i; + }); + + BOOST_CHECK(!!mapped); + BOOST_CHECK(mapped.is_ok()); + BOOST_CHECK(!mapped.is_err()); + BOOST_CHECK_EQUAL(mapped.unwrap(), 42); + } + { + const toml::result result(toml::err("hoge")); + const auto mapped = result.map( + [](const int i) -> int { + return i * 2; + }); + + BOOST_CHECK(!mapped); + BOOST_CHECK(!mapped.is_ok()); + BOOST_CHECK(mapped.is_err()); + BOOST_CHECK_EQUAL(mapped.unwrap_err(), "hoge"); + } + { + toml::result, std::string> + result(toml::err("hoge")); + const auto mapped = std::move(result).map( + [](std::unique_ptr i) -> int { + return *i; + }); + + BOOST_CHECK(!mapped); + BOOST_CHECK(!mapped.is_ok()); + BOOST_CHECK(mapped.is_err()); + BOOST_CHECK_EQUAL(mapped.unwrap_err(), "hoge"); + } +} + +BOOST_AUTO_TEST_CASE(test_map_err) +{ + { + const toml::result result(toml::ok(42)); + const auto mapped = result.map_err( + [](const std::string s) -> std::string { + return s + s; + }); + + BOOST_CHECK(!!mapped); + BOOST_CHECK(mapped.is_ok()); + BOOST_CHECK(!mapped.is_err()); + BOOST_CHECK_EQUAL(mapped.unwrap(), 42); + } + { + toml::result, std::string> + result(toml::ok(std::unique_ptr(new int(42)))); + const auto mapped = std::move(result).map_err( + [](const std::string s) -> std::string { + return s + s; + }); + + BOOST_CHECK(!!mapped); + BOOST_CHECK(mapped.is_ok()); + BOOST_CHECK(!mapped.is_err()); + BOOST_CHECK_EQUAL(*(mapped.unwrap()), 42); + } + { + const toml::result result(toml::err("hoge")); + const auto mapped = result.map_err( + [](const std::string s) -> std::string { + return s + s; + }); + BOOST_CHECK(!mapped); + BOOST_CHECK(!mapped.is_ok()); + BOOST_CHECK(mapped.is_err()); + BOOST_CHECK_EQUAL(mapped.unwrap_err(), "hogehoge"); + } + { + toml::result> + result(toml::err(std::unique_ptr(new std::string("hoge")))); + const auto mapped = std::move(result).map_err( + [](std::unique_ptr p) -> std::string { + return *p; + }); + + BOOST_CHECK(!mapped); + BOOST_CHECK(!mapped.is_ok()); + BOOST_CHECK(mapped.is_err()); + BOOST_CHECK_EQUAL(mapped.unwrap_err(), "hoge"); + } +} + +BOOST_AUTO_TEST_CASE(test_map_or_else) +{ + { + const toml::result result(toml::ok(42)); + const auto mapped = result.map_or_else( + [](const int i) -> int { + return i * 2; + }, 54); + + BOOST_CHECK_EQUAL(mapped, 42 * 2); + } + { + toml::result, std::string> + result(toml::ok(std::unique_ptr(new int(42)))); + const auto mapped = std::move(result).map_or_else( + [](std::unique_ptr i) -> int { + return *i; + }, 54); + + BOOST_CHECK_EQUAL(mapped, 42); + } + { + const toml::result result(toml::err("hoge")); + const auto mapped = result.map_or_else( + [](const int i) -> int { + return i * 2; + }, 54); + + BOOST_CHECK_EQUAL(mapped, 54); + } + { + toml::result, std::string> + result(toml::err("hoge")); + const auto mapped = std::move(result).map_or_else( + [](std::unique_ptr i) -> int { + return *i; + }, 54); + + BOOST_CHECK_EQUAL(mapped, 54); + } +} + +BOOST_AUTO_TEST_CASE(test_map_err_or_else) +{ + { + const toml::result result(toml::ok(42)); + const auto mapped = result.map_err_or_else( + [](const std::string i) -> std::string { + return i + i; + }, "foobar"); + + BOOST_CHECK_EQUAL(mapped, "foobar"); + } + { + toml::result, std::string> + result(toml::ok(std::unique_ptr(new int(42)))); + const auto mapped = std::move(result).map_err_or_else( + [](const std::string i) -> std::string { + return i + i; + }, "foobar"); + + BOOST_CHECK_EQUAL(mapped, "foobar"); + } + { + const toml::result result(toml::err("hoge")); + const auto mapped = result.map_err_or_else( + [](const std::string i) -> std::string { + return i + i; + }, "foobar"); + + BOOST_CHECK_EQUAL(mapped, "hogehoge"); + } + { + toml::result, std::string> + result(toml::err("hoge")); + const auto mapped = result.map_err_or_else( + [](const std::string i) -> std::string { + return i + i; + }, "foobar"); + + BOOST_CHECK_EQUAL(mapped, "hogehoge"); + } +} + + +BOOST_AUTO_TEST_CASE(test_and_then) +{ + { + const toml::result result(toml::ok(42)); + const auto mapped = result.and_then( + [](const int i) -> toml::result { + return toml::ok(i * 2); + }); + + BOOST_CHECK(!!mapped); + BOOST_CHECK(mapped.is_ok()); + BOOST_CHECK(!mapped.is_err()); + BOOST_CHECK_EQUAL(mapped.unwrap(), 42 * 2); + } + { + toml::result, std::string> + result(toml::ok(std::unique_ptr(new int(42)))); + const auto mapped = std::move(result).and_then( + [](std::unique_ptr i) -> toml::result { + return toml::ok(*i); + }); + + BOOST_CHECK(!!mapped); + BOOST_CHECK(mapped.is_ok()); + BOOST_CHECK(!mapped.is_err()); + BOOST_CHECK_EQUAL(mapped.unwrap(), 42); + } + { + const toml::result result(toml::err("hoge")); + const auto mapped = result.and_then( + [](const int i) -> toml::result { + return toml::ok(i * 2); + }); + + BOOST_CHECK(!mapped); + BOOST_CHECK(!mapped.is_ok()); + BOOST_CHECK(mapped.is_err()); + BOOST_CHECK_EQUAL(mapped.unwrap_err(), "hoge"); + } + { + toml::result, std::string> + result(toml::err("hoge")); + const auto mapped = std::move(result).and_then( + [](std::unique_ptr i) -> toml::result { + return toml::ok(*i); + }); + + BOOST_CHECK(!mapped); + BOOST_CHECK(!mapped.is_ok()); + BOOST_CHECK(mapped.is_err()); + BOOST_CHECK_EQUAL(mapped.unwrap_err(), "hoge"); + } +} + +BOOST_AUTO_TEST_CASE(test_or_else) +{ + { + const toml::result result(toml::ok(42)); + const auto mapped = result.or_else( + [](const std::string& s) -> toml::result { + return toml::err(s + s); + }); + + BOOST_CHECK(!!mapped); + BOOST_CHECK(mapped.is_ok()); + BOOST_CHECK(!mapped.is_err()); + BOOST_CHECK_EQUAL(mapped.unwrap(), 42); + } + { + toml::result, std::string> + result(toml::ok(std::unique_ptr(new int(42)))); + const auto mapped = std::move(result).or_else( + [](const std::string& s) -> toml::result, std::string> { + return toml::err(s + s); + }); + + BOOST_CHECK(!!mapped); + BOOST_CHECK(mapped.is_ok()); + BOOST_CHECK(!mapped.is_err()); + BOOST_CHECK_EQUAL(*mapped.unwrap(), 42); + } + { + const toml::result result(toml::err("hoge")); + const auto mapped = result.or_else( + [](const std::string& s) -> toml::result { + return toml::err(s + s); + }); + + BOOST_CHECK(!mapped); + BOOST_CHECK(!mapped.is_ok()); + BOOST_CHECK(mapped.is_err()); + BOOST_CHECK_EQUAL(mapped.unwrap_err(), "hogehoge"); + } + { + toml::result, std::string> + result(toml::err("hoge")); + const auto mapped = std::move(result).or_else( + [](const std::string& s) -> toml::result, std::string> { + return toml::err(s + s); + }); + + BOOST_CHECK(!mapped); + BOOST_CHECK(!mapped.is_ok()); + BOOST_CHECK(mapped.is_err()); + BOOST_CHECK_EQUAL(mapped.unwrap_err(), "hogehoge"); + } +} diff --git a/tests/test_to_toml.cpp b/tests/test_to_toml.cpp index dca9799b..464a760d 100644 --- a/tests/test_to_toml.cpp +++ b/tests/test_to_toml.cpp @@ -9,97 +9,181 @@ #include #include -BOOST_AUTO_TEST_CASE(test_to_toml_exact) +BOOST_AUTO_TEST_CASE(test_value_boolean) { - toml::Boolean b(true); - toml::Integer i(42); - toml::Float f(3.14); - toml::String s("hoge"); - toml::Datetime d(std::chrono::system_clock::now()); - toml::Array a; - a.emplace_back(2); - a.emplace_back(7); - a.emplace_back(1); - a.emplace_back(8); - a.emplace_back(2); - toml::Table t; - t.emplace("val1", true); - t.emplace("val2", 42); - t.emplace("val3", 3.14); - t.emplace("val4", "piyo"); - - auto v1 = toml::to_toml(b); - auto v2 = toml::to_toml(i); - auto v3 = toml::to_toml(f); - auto v4 = toml::to_toml(s); - auto v5 = toml::to_toml(d); - auto v6 = toml::to_toml(a); - auto v7 = toml::to_toml(t); + toml::value v1 = toml::to_toml(true); + toml::value v2 = toml::to_toml(false); BOOST_CHECK_EQUAL(v1.type(), toml::value_t::Boolean); - BOOST_CHECK_EQUAL(v2.type(), toml::value_t::Integer); - BOOST_CHECK_EQUAL(v3.type(), toml::value_t::Float); - BOOST_CHECK_EQUAL(v4.type(), toml::value_t::String); - BOOST_CHECK_EQUAL(v5.type(), toml::value_t::Datetime); - BOOST_CHECK_EQUAL(v6.type(), toml::value_t::Array); - BOOST_CHECK_EQUAL(v7.type(), toml::value_t::Table); - - BOOST_CHECK_EQUAL(v1.cast(), b); - BOOST_CHECK_EQUAL(v2.cast(), i); - BOOST_CHECK_EQUAL(v3.cast(), f); - BOOST_CHECK_EQUAL(v4.cast(), s); - const auto& ar = v6.cast(); - BOOST_CHECK_EQUAL(ar.at(0).cast(), a.at(0).cast()); - BOOST_CHECK_EQUAL(ar.at(1).cast(), a.at(1).cast()); - BOOST_CHECK_EQUAL(ar.at(2).cast(), a.at(2).cast()); - BOOST_CHECK_EQUAL(ar.at(3).cast(), a.at(3).cast()); - BOOST_CHECK_EQUAL(ar.at(4).cast(), a.at(4).cast()); + BOOST_CHECK_EQUAL(v2.type(), toml::value_t::Boolean); + BOOST_CHECK(v1.is(toml::value_t::Boolean)); + BOOST_CHECK(v2.is(toml::value_t::Boolean)); + BOOST_CHECK(v1.is()); + BOOST_CHECK(v2.is()); + + BOOST_CHECK_EQUAL(v1.cast(), true); + BOOST_CHECK_EQUAL(v2.cast(), false); } -BOOST_AUTO_TEST_CASE(test_to_toml_castable) +BOOST_AUTO_TEST_CASE(test_value_integer) { - auto v1 = toml::to_toml(true); - auto v2 = toml::to_toml(42ul); - auto v3 = toml::to_toml(3.14f); - auto v4 = toml::to_toml("hoge"); + toml::value v1 = toml::to_toml(-42); + toml::value v2 = toml::to_toml(42u); - BOOST_CHECK_EQUAL(v1.type(), toml::value_t::Boolean); + BOOST_CHECK_EQUAL(v1.type(), toml::value_t::Integer); BOOST_CHECK_EQUAL(v2.type(), toml::value_t::Integer); - BOOST_CHECK_EQUAL(v3.type(), toml::value_t::Float); - BOOST_CHECK_EQUAL(v4.type(), toml::value_t::String); + BOOST_CHECK(v1.is(toml::value_t::Integer)); + BOOST_CHECK(v2.is(toml::value_t::Integer)); + BOOST_CHECK(v1.is()); + BOOST_CHECK(v2.is()); - BOOST_CHECK_EQUAL(v1.cast(), true); - BOOST_CHECK_EQUAL(v2.cast(), 42); - BOOST_CHECK_CLOSE_FRACTION(v3.cast(), 3.14, 1e-5); - BOOST_CHECK_EQUAL(v4.cast(), "hoge"); + BOOST_CHECK_EQUAL(v1.cast(), -42); + BOOST_CHECK_EQUAL(v2.cast(), 42u); } -BOOST_AUTO_TEST_CASE(test_to_toml_initializer_list) +BOOST_AUTO_TEST_CASE(test_value_float) { - toml::value v1 = toml::to_toml({3,1,4,1,5}); - toml::value v2 = toml::to_toml({{"hoge", 1}, {"piyo", 3.14}, {"fuga", "string"}}); + toml::value v1 = toml::to_toml(3.14); + toml::value v2 = toml::to_toml(3.14f); + + BOOST_CHECK_EQUAL(v1.type(), toml::value_t::Float); + BOOST_CHECK_EQUAL(v2.type(), toml::value_t::Float); + BOOST_CHECK(v1.is(toml::value_t::Float)); + BOOST_CHECK(v2.is(toml::value_t::Float)); + BOOST_CHECK(v1.is()); + BOOST_CHECK(v2.is()); + + BOOST_CHECK_EQUAL(v1.cast(), 3.14); + BOOST_CHECK_CLOSE_FRACTION(v2.cast(), 3.14, 1e-2); +} - BOOST_CHECK_EQUAL(v1.type(), toml::value_t::Array); - BOOST_CHECK_EQUAL(v2.type(), toml::value_t::Table); +BOOST_AUTO_TEST_CASE(test_value_string) +{ + toml::value v1 = toml::to_toml(std::string("foo")); + toml::value v2 = toml::to_toml(std::string("foo"), toml::string_t::literal); + toml::value v3 = toml::to_toml("foo"); + + BOOST_CHECK_EQUAL(v1.type(), toml::value_t::String); + BOOST_CHECK_EQUAL(v2.type(), toml::value_t::String); + BOOST_CHECK_EQUAL(v3.type(), toml::value_t::String); + BOOST_CHECK(v1.is(toml::value_t::String)); + BOOST_CHECK(v2.is(toml::value_t::String)); + BOOST_CHECK(v3.is(toml::value_t::String)); + BOOST_CHECK(v1.is()); + BOOST_CHECK(v2.is()); + BOOST_CHECK(v3.is()); + + BOOST_CHECK_EQUAL(v1.cast(), "foo"); + BOOST_CHECK_EQUAL(v2.cast(), "foo"); + BOOST_CHECK_EQUAL(v3.cast(), "foo"); +} - const auto& ar = v1.cast(); - BOOST_CHECK_EQUAL(ar.at(0).cast(), 3); - BOOST_CHECK_EQUAL(ar.at(1).cast(), 1); - BOOST_CHECK_EQUAL(ar.at(2).cast(), 4); - BOOST_CHECK_EQUAL(ar.at(3).cast(), 1); - BOOST_CHECK_EQUAL(ar.at(4).cast(), 5); +BOOST_AUTO_TEST_CASE(test_value_local_date) +{ + toml::value v1 = toml::to_toml(toml::local_date(2018, toml::month_t::Jan, 31)); - const auto& tb = v2.cast(); + BOOST_CHECK_EQUAL(v1.type(), toml::value_t::LocalDate); + BOOST_CHECK(v1.is(toml::value_t::LocalDate)); + BOOST_CHECK(v1.is()); - BOOST_CHECK_EQUAL(tb.at("hoge").type(), toml::value_t::Integer); - BOOST_CHECK_EQUAL(tb.at("piyo").type(), toml::value_t::Float); - BOOST_CHECK_EQUAL(tb.at("fuga").type(), toml::value_t::String); + BOOST_CHECK_EQUAL(v1.cast(), + toml::local_date(2018, toml::month_t::Jan, 31)); +} - BOOST_CHECK_EQUAL(tb.at("hoge").cast(), 1); - BOOST_CHECK_CLOSE_FRACTION(tb.at("piyo").cast(), 3.14, 1e-3); - BOOST_CHECK_EQUAL(tb.at("fuga").cast(), "string"); +BOOST_AUTO_TEST_CASE(test_value_local_time) +{ + toml::value v1 = toml::to_toml(toml::local_time(12, 30, 45)); + toml::value v2 = toml::to_toml(std::chrono::hours(12) + std::chrono::minutes(30) + + std::chrono::seconds(45)); + + BOOST_CHECK_EQUAL(v1.type(), toml::value_t::LocalTime); + BOOST_CHECK_EQUAL(v2.type(), toml::value_t::LocalTime); + BOOST_CHECK(v1.is(toml::value_t::LocalTime)); + BOOST_CHECK(v2.is(toml::value_t::LocalTime)); + BOOST_CHECK(v1.is()); + BOOST_CHECK(v2.is()); + + BOOST_CHECK_EQUAL(v1.cast(), + toml::local_time(12, 30, 45)); + BOOST_CHECK_EQUAL(v2.cast(), + toml::local_time(12, 30, 45)); + BOOST_CHECK_EQUAL(v1.cast(), + v2.cast()); +} +BOOST_AUTO_TEST_CASE(test_value_local_datetime) +{ + toml::value v1 = toml::to_toml(toml::local_datetime( + toml::local_date(2018, toml::month_t::Jan, 31), + toml::local_time(12, 30, 45) + )); + + BOOST_CHECK_EQUAL(v1.type(), toml::value_t::LocalDatetime); + BOOST_CHECK(v1.is(toml::value_t::LocalDatetime)); + BOOST_CHECK(v1.is()); + + BOOST_CHECK_EQUAL(v1.cast(), + toml::local_datetime( + toml::local_date(2018, toml::month_t::Jan, 31), + toml::local_time(12, 30, 45))); } +BOOST_AUTO_TEST_CASE(test_value_offset_datetime) +{ + toml::value v1 = toml::to_toml(toml::offset_datetime( + toml::local_date(2018, toml::month_t::Jan, 31), + toml::local_time(12, 30, 45), + toml::time_offset(9, 0) + )); + + BOOST_CHECK_EQUAL(v1.type(), toml::value_t::OffsetDatetime); + BOOST_CHECK(v1.is(toml::value_t::OffsetDatetime)); + BOOST_CHECK(v1.is()); + + BOOST_CHECK_EQUAL(v1.cast(), + toml::offset_datetime( + toml::local_date(2018, toml::month_t::Jan, 31), + toml::local_time(12, 30, 45), + toml::time_offset(9, 0) + )); +} + +BOOST_AUTO_TEST_CASE(test_value_array) +{ + std::vector v{1,2,3,4,5}; + toml::value v1 = toml::to_toml(v); + toml::value v2 = toml::to_toml(6,7,8,9,0); + + BOOST_CHECK_EQUAL(v1.type(), toml::value_t::Array); + BOOST_CHECK(v1.is(toml::value_t::Array)); + BOOST_CHECK(v1.is()); + + BOOST_CHECK_EQUAL(v2.type(), toml::value_t::Array); + BOOST_CHECK(v2.is(toml::value_t::Array)); + BOOST_CHECK(v2.is()); + + BOOST_CHECK_EQUAL(v1.cast().at(0).cast(), 1); + BOOST_CHECK_EQUAL(v1.cast().at(1).cast(), 2); + BOOST_CHECK_EQUAL(v1.cast().at(2).cast(), 3); + BOOST_CHECK_EQUAL(v1.cast().at(3).cast(), 4); + BOOST_CHECK_EQUAL(v1.cast().at(4).cast(), 5); + + BOOST_CHECK_EQUAL(v2.cast().at(0).cast(), 6); + BOOST_CHECK_EQUAL(v2.cast().at(1).cast(), 7); + BOOST_CHECK_EQUAL(v2.cast().at(2).cast(), 8); + BOOST_CHECK_EQUAL(v2.cast().at(3).cast(), 9); + BOOST_CHECK_EQUAL(v2.cast().at(4).cast(), 0); +} +BOOST_AUTO_TEST_CASE(test_value_table) +{ + toml::value v1 = toml::to_toml({{"foo", 42}, {"bar", 3.14}, {"baz", "qux"}}); + BOOST_CHECK_EQUAL(v1.type(), toml::value_t::Table); + BOOST_CHECK(v1.is(toml::value_t::Table)); + BOOST_CHECK(v1.is()); + + BOOST_CHECK_EQUAL(v1.cast().at("foo").cast(), 42); + BOOST_CHECK_EQUAL(v1.cast().at("bar").cast(), 3.14); + BOOST_CHECK_EQUAL(v1.cast().at("baz").cast().str, "qux"); +} diff --git a/tests/test_traits.cpp b/tests/test_traits.cpp index b40f6268..70521ea7 100644 --- a/tests/test_traits.cpp +++ b/tests/test_traits.cpp @@ -5,7 +5,7 @@ #define BOOST_TEST_NO_LIB #include #endif -#include +#include #include #include diff --git a/tests/test_utility.cpp b/tests/test_utility.cpp index 1512e4f0..b879c545 100644 --- a/tests/test_utility.cpp +++ b/tests/test_utility.cpp @@ -30,7 +30,7 @@ BOOST_AUTO_TEST_CASE(test_resize) thrown = true; } BOOST_CHECK(!thrown); - BOOST_CHECK_EQUAL(v.size(), 10); + BOOST_CHECK_EQUAL(v.size(), 10u); } { @@ -45,7 +45,7 @@ BOOST_AUTO_TEST_CASE(test_resize) thrown = true; } BOOST_CHECK(!thrown); - BOOST_CHECK_EQUAL(a.size(), 15); + BOOST_CHECK_EQUAL(a.size(), 15u); } { @@ -62,3 +62,21 @@ BOOST_AUTO_TEST_CASE(test_resize) BOOST_CHECK(thrown); } } + +BOOST_AUTO_TEST_CASE(test_concat_to_string) +{ + const std::string cat = toml::concat_to_string("foo", "bar", 42); + BOOST_CHECK(cat == "foobar42"); +} + +BOOST_AUTO_TEST_CASE(test_from_string) +{ + { + const std::string str("123"); + BOOST_CHECK_EQUAL(toml::from_string(str, 0), 123); + } + { + const std::string str("01"); + BOOST_CHECK_EQUAL(toml::from_string(str, 0), 1); + } +} diff --git a/tests/test_value.cpp b/tests/test_value.cpp index 1e24088d..702fa8de 100644 --- a/tests/test_value.cpp +++ b/tests/test_value.cpp @@ -5,231 +5,604 @@ #define BOOST_TEST_NO_LIB #include #endif -#include +#include #include #include -BOOST_AUTO_TEST_CASE(test_value_exact_constructor) +BOOST_AUTO_TEST_CASE(test_value_boolean) { - toml::Boolean b(true); - toml::Integer i(42); - toml::Float f(3.14); - toml::String s("hoge"); - toml::Datetime d(std::chrono::system_clock::now()); - toml::Array a; - a.emplace_back(2); - a.emplace_back(7); - a.emplace_back(1); - a.emplace_back(8); - a.emplace_back(2); - toml::Table t; - t.emplace("val1", true); - t.emplace("val2", 42); - t.emplace("val3", 3.14); - t.emplace("val4", "piyo"); - - toml::value v1(b); - toml::value v2(i); - toml::value v3(f); - toml::value v4(s); - toml::value v5(d); - toml::value v6(a); - toml::value v7(t); + toml::value v1(true); + toml::value v2(false); BOOST_CHECK_EQUAL(v1.type(), toml::value_t::Boolean); - BOOST_CHECK_EQUAL(v2.type(), toml::value_t::Integer); - BOOST_CHECK_EQUAL(v3.type(), toml::value_t::Float); - BOOST_CHECK_EQUAL(v4.type(), toml::value_t::String); - BOOST_CHECK_EQUAL(v5.type(), toml::value_t::Datetime); - BOOST_CHECK_EQUAL(v6.type(), toml::value_t::Array); - BOOST_CHECK_EQUAL(v7.type(), toml::value_t::Table); - - BOOST_CHECK_EQUAL(v1.cast(), b); - BOOST_CHECK_EQUAL(v2.cast(), i); - BOOST_CHECK_EQUAL(v3.cast(), f); - BOOST_CHECK_EQUAL(v4.cast(), s); - const auto& ar = v6.cast(); - BOOST_CHECK_EQUAL(ar.at(0).cast(), a.at(0).cast()); - BOOST_CHECK_EQUAL(ar.at(1).cast(), a.at(1).cast()); - BOOST_CHECK_EQUAL(ar.at(2).cast(), a.at(2).cast()); - BOOST_CHECK_EQUAL(ar.at(3).cast(), a.at(3).cast()); - BOOST_CHECK_EQUAL(ar.at(4).cast(), a.at(4).cast()); -} + BOOST_CHECK_EQUAL(v2.type(), toml::value_t::Boolean); + BOOST_CHECK(v1.is(toml::value_t::Boolean)); + BOOST_CHECK(v2.is(toml::value_t::Boolean)); + BOOST_CHECK(v1.is()); + BOOST_CHECK(v2.is()); -BOOST_AUTO_TEST_CASE(test_value_convertible_constructor) -{ - int i(42); - float f(3.14); - const char* s = "hoge"; + BOOST_CHECK_EQUAL(v1.cast(), true); + BOOST_CHECK_EQUAL(v2.cast(), false); + + v1 = false; + v2 = true; + + BOOST_CHECK_EQUAL(v1.type(), toml::value_t::Boolean); + BOOST_CHECK_EQUAL(v2.type(), toml::value_t::Boolean); + BOOST_CHECK(v1.is(toml::value_t::Boolean)); + BOOST_CHECK(v2.is(toml::value_t::Boolean)); + BOOST_CHECK(v1.is()); + BOOST_CHECK(v2.is()); + + BOOST_CHECK_EQUAL(v1.cast(), false); + BOOST_CHECK_EQUAL(v2.cast(), true); - toml::value v1(i); - toml::value v2(f); - toml::value v3(s); + toml::value v3(v1); + toml::value v4(v2); + BOOST_CHECK(v3 == v1); + BOOST_CHECK(v4 == v2); + + BOOST_CHECK_EQUAL(v3.type(), toml::value_t::Boolean); + BOOST_CHECK_EQUAL(v4.type(), toml::value_t::Boolean); + BOOST_CHECK(v3.is(toml::value_t::Boolean)); + BOOST_CHECK(v4.is(toml::value_t::Boolean)); + BOOST_CHECK(v3.is()); + BOOST_CHECK(v4.is()); + + BOOST_CHECK_EQUAL(v3.cast(), false); + BOOST_CHECK_EQUAL(v4.cast(), true); + + toml::value v5(std::move(v1)); + toml::value v6(std::move(v2)); + + BOOST_CHECK_EQUAL(v5.type(), toml::value_t::Boolean); + BOOST_CHECK_EQUAL(v6.type(), toml::value_t::Boolean); + BOOST_CHECK(v5.is(toml::value_t::Boolean)); + BOOST_CHECK(v6.is(toml::value_t::Boolean)); + BOOST_CHECK(v5.is()); + BOOST_CHECK(v6.is()); + + BOOST_CHECK_EQUAL(v5.cast(), false); + BOOST_CHECK_EQUAL(v6.cast(), true); + + v1 = 42; + v2 = 3.14; BOOST_CHECK_EQUAL(v1.type(), toml::value_t::Integer); BOOST_CHECK_EQUAL(v2.type(), toml::value_t::Float); - BOOST_CHECK_EQUAL(v3.type(), toml::value_t::String); + BOOST_CHECK(v1.is(toml::value_t::Integer)); + BOOST_CHECK(v2.is(toml::value_t::Float)); + BOOST_CHECK(v1.is()); + BOOST_CHECK(v2.is()); - BOOST_CHECK_EQUAL(v1.cast(), i); - BOOST_CHECK_EQUAL(v2.cast(), f); - BOOST_CHECK_EQUAL(v3.cast(), s); + BOOST_CHECK_EQUAL(v1.cast(), 42); + BOOST_CHECK_EQUAL(v2.cast(), 3.14); } -BOOST_AUTO_TEST_CASE(test_value_copy_move_constructor) +BOOST_AUTO_TEST_CASE(test_value_integer) { - toml::Array a; - toml::Table t; - toml::value v1(true); - toml::value v2(42); - toml::value v3(3.14); - toml::value v4("hoge"); - toml::value v5(std::chrono::system_clock::now()); - toml::value v6(a); - toml::value v7(t); - - toml::value u1(v1); - toml::value u2(v2); - toml::value u3(v3); - toml::value u4(v4); - toml::value u5(v5); - toml::value u6(v6); - toml::value u7(v7); - - BOOST_CHECK_EQUAL(u1.type(), toml::value_t::Boolean); - BOOST_CHECK_EQUAL(u2.type(), toml::value_t::Integer); - BOOST_CHECK_EQUAL(u3.type(), toml::value_t::Float); - BOOST_CHECK_EQUAL(u4.type(), toml::value_t::String); - BOOST_CHECK_EQUAL(u5.type(), toml::value_t::Datetime); - BOOST_CHECK_EQUAL(u6.type(), toml::value_t::Array); - BOOST_CHECK_EQUAL(u7.type(), toml::value_t::Table); - - BOOST_CHECK_EQUAL(u1.cast(), true); - BOOST_CHECK_EQUAL(u2.cast(), 42); - BOOST_CHECK_EQUAL(u3.cast(), 3.14); - BOOST_CHECK_EQUAL(u4.cast(), "hoge"); - - toml::value w1(std::move(v1)); - toml::value w2(std::move(v2)); - toml::value w3(std::move(v3)); - toml::value w4(std::move(v4)); - toml::value w5(std::move(v5)); - toml::value w6(std::move(v6)); - toml::value w7(std::move(v7)); - - BOOST_CHECK_EQUAL(w1.type(), toml::value_t::Boolean); - BOOST_CHECK_EQUAL(w2.type(), toml::value_t::Integer); - BOOST_CHECK_EQUAL(w3.type(), toml::value_t::Float); - BOOST_CHECK_EQUAL(w4.type(), toml::value_t::String); - BOOST_CHECK_EQUAL(w5.type(), toml::value_t::Datetime); - BOOST_CHECK_EQUAL(w6.type(), toml::value_t::Array); - BOOST_CHECK_EQUAL(w7.type(), toml::value_t::Table); - - BOOST_CHECK_EQUAL(w1.cast(), true); - BOOST_CHECK_EQUAL(w2.cast(), 42); - BOOST_CHECK_EQUAL(w3.cast(), 3.14); - BOOST_CHECK_EQUAL(w4.cast(), "hoge"); + toml::value v1(-42); + toml::value v2(42u); + + BOOST_CHECK_EQUAL(v1.type(), toml::value_t::Integer); + BOOST_CHECK_EQUAL(v2.type(), toml::value_t::Integer); + BOOST_CHECK(v1.is(toml::value_t::Integer)); + BOOST_CHECK(v2.is(toml::value_t::Integer)); + BOOST_CHECK(v1.is()); + BOOST_CHECK(v2.is()); + + BOOST_CHECK_EQUAL(v1.cast(), -42); + BOOST_CHECK_EQUAL(v2.cast(), 42u); + + v1 = 54; + v2 = -54; + + BOOST_CHECK_EQUAL(v1.type(), toml::value_t::Integer); + BOOST_CHECK_EQUAL(v2.type(), toml::value_t::Integer); + BOOST_CHECK(v1.is(toml::value_t::Integer)); + BOOST_CHECK(v2.is(toml::value_t::Integer)); + BOOST_CHECK(v1.is()); + BOOST_CHECK(v2.is()); + + BOOST_CHECK_EQUAL(v1.cast(), 54); + BOOST_CHECK_EQUAL(v2.cast(), -54); + + toml::value v3(v1); + toml::value v4(v2); + BOOST_CHECK(v3 == v1); + BOOST_CHECK(v4 == v2); + + BOOST_CHECK_EQUAL(v3.type(), toml::value_t::Integer); + BOOST_CHECK_EQUAL(v4.type(), toml::value_t::Integer); + BOOST_CHECK(v3.is(toml::value_t::Integer)); + BOOST_CHECK(v4.is(toml::value_t::Integer)); + BOOST_CHECK(v3.is()); + BOOST_CHECK(v4.is()); + + BOOST_CHECK_EQUAL(v3.cast(), 54); + BOOST_CHECK_EQUAL(v4.cast(), -54); + + toml::value v5(std::move(v1)); + toml::value v6(std::move(v2)); + + BOOST_CHECK_EQUAL(v5.type(), toml::value_t::Integer); + BOOST_CHECK_EQUAL(v6.type(), toml::value_t::Integer); + BOOST_CHECK(v5.is(toml::value_t::Integer)); + BOOST_CHECK(v6.is(toml::value_t::Integer)); + BOOST_CHECK(v5.is()); + BOOST_CHECK(v6.is()); + + BOOST_CHECK_EQUAL(v5.cast(), 54); + BOOST_CHECK_EQUAL(v6.cast(), -54); + + v1 = true; + v2 = false; + + BOOST_CHECK_EQUAL(v1.type(), toml::value_t::Boolean); + BOOST_CHECK_EQUAL(v2.type(), toml::value_t::Boolean); + BOOST_CHECK(v1.is(toml::value_t::Boolean)); + BOOST_CHECK(v2.is(toml::value_t::Boolean)); + BOOST_CHECK(v1.is()); + BOOST_CHECK(v2.is()); + + BOOST_CHECK_EQUAL(v1.cast(), true); + BOOST_CHECK_EQUAL(v2.cast(), false); } -BOOST_AUTO_TEST_CASE(test_value_copy_move_substitution) +BOOST_AUTO_TEST_CASE(test_value_float) { - toml::Boolean b(true); - toml::Integer i(42); - toml::Float f(3.14); - toml::String s("hoge"); - toml::Datetime d(std::chrono::system_clock::now()); - toml::Array a; - a.emplace_back(2); - a.emplace_back(7); - a.emplace_back(1); - a.emplace_back(8); - a.emplace_back(2); - toml::Table t; - t.emplace("val1", true); - t.emplace("val2", 42); - t.emplace("val3", 3.14); - t.emplace("val4", "piyo"); - - toml::value v1(b); - toml::value v2(i); - toml::value v3(f); - toml::value v4(s); - toml::value v5(d); - toml::value v6(a); - toml::value v7(t); - - v1 = i; - v2 = f; - v3 = s; - v4 = d; - v5 = a; - v6 = t; - v7 = b; + toml::value v1(3.14); + toml::value v2(3.14f); - BOOST_CHECK_EQUAL(v1.type(), toml::value_t::Integer); + BOOST_CHECK_EQUAL(v1.type(), toml::value_t::Float); BOOST_CHECK_EQUAL(v2.type(), toml::value_t::Float); - BOOST_CHECK_EQUAL(v3.type(), toml::value_t::String); - BOOST_CHECK_EQUAL(v4.type(), toml::value_t::Datetime); - BOOST_CHECK_EQUAL(v5.type(), toml::value_t::Array); - BOOST_CHECK_EQUAL(v6.type(), toml::value_t::Table); - BOOST_CHECK_EQUAL(v7.type(), toml::value_t::Boolean); - - BOOST_CHECK_EQUAL(v7.cast(), b); - BOOST_CHECK_EQUAL(v1.cast(), i); - BOOST_CHECK_EQUAL(v2.cast(), f); - BOOST_CHECK_EQUAL(v3.cast(), s); - - const auto f_ = f; - const auto s_ = s; - const auto d_ = d; - const auto a_ = a; - const auto t_ = t; - const auto b_ = b; - const auto i_ = i; - - v1 = std::move(f); - v2 = std::move(s); - v3 = std::move(d); - v4 = std::move(a); - v5 = std::move(t); - v6 = std::move(b); - v7 = std::move(i); + BOOST_CHECK(v1.is(toml::value_t::Float)); + BOOST_CHECK(v2.is(toml::value_t::Float)); + BOOST_CHECK(v1.is()); + BOOST_CHECK(v2.is()); + + BOOST_CHECK_EQUAL(v1.cast(), 3.14); + BOOST_CHECK_CLOSE_FRACTION(v2.cast(), 3.14, 1e-2); + + v1 = 2.718f; + v2 = 2.718; BOOST_CHECK_EQUAL(v1.type(), toml::value_t::Float); + BOOST_CHECK_EQUAL(v2.type(), toml::value_t::Float); + BOOST_CHECK(v1.is(toml::value_t::Float)); + BOOST_CHECK(v2.is(toml::value_t::Float)); + BOOST_CHECK(v1.is()); + BOOST_CHECK(v2.is()); + + BOOST_CHECK_CLOSE_FRACTION(v1.cast(), 2.718, 1e-3); + BOOST_CHECK_EQUAL(v2.cast(), 2.718); + + toml::value v3(v1); + toml::value v4(v2); + BOOST_CHECK(v3 == v1); + BOOST_CHECK(v4 == v2); + + BOOST_CHECK_EQUAL(v3.type(), toml::value_t::Float); + BOOST_CHECK_EQUAL(v4.type(), toml::value_t::Float); + BOOST_CHECK(v3.is(toml::value_t::Float)); + BOOST_CHECK(v4.is(toml::value_t::Float)); + BOOST_CHECK(v3.is()); + BOOST_CHECK(v4.is()); + + BOOST_CHECK_CLOSE_FRACTION(v3.cast(), 2.718, 1e-3); + BOOST_CHECK_EQUAL(v4.cast(), 2.718); + + toml::value v5(std::move(v1)); + toml::value v6(std::move(v2)); + + BOOST_CHECK_EQUAL(v5.type(), toml::value_t::Float); + BOOST_CHECK_EQUAL(v6.type(), toml::value_t::Float); + BOOST_CHECK(v5.is(toml::value_t::Float)); + BOOST_CHECK(v6.is(toml::value_t::Float)); + BOOST_CHECK(v5.is()); + BOOST_CHECK(v6.is()); + + BOOST_CHECK_CLOSE_FRACTION(v5.cast(), 2.718, 1e-3); + BOOST_CHECK_EQUAL(v6.cast(), 2.718); + + v1 = true; + v2 = false; + + BOOST_CHECK_EQUAL(v1.type(), toml::value_t::Boolean); + BOOST_CHECK_EQUAL(v2.type(), toml::value_t::Boolean); + BOOST_CHECK(v1.is(toml::value_t::Boolean)); + BOOST_CHECK(v2.is(toml::value_t::Boolean)); + BOOST_CHECK(v1.is()); + BOOST_CHECK(v2.is()); + + BOOST_CHECK_EQUAL(v1.cast(), true); + BOOST_CHECK_EQUAL(v2.cast(), false); +} + +BOOST_AUTO_TEST_CASE(test_value_string) +{ + toml::value v1(std::string("foo")); + toml::value v2(std::string("foo"), toml::string_t::literal); + toml::value v3("foo"); + + BOOST_CHECK_EQUAL(v1.type(), toml::value_t::String); BOOST_CHECK_EQUAL(v2.type(), toml::value_t::String); - BOOST_CHECK_EQUAL(v3.type(), toml::value_t::Datetime); - BOOST_CHECK_EQUAL(v4.type(), toml::value_t::Array); - BOOST_CHECK_EQUAL(v5.type(), toml::value_t::Table); - BOOST_CHECK_EQUAL(v6.type(), toml::value_t::Boolean); - BOOST_CHECK_EQUAL(v7.type(), toml::value_t::Integer); + BOOST_CHECK_EQUAL(v3.type(), toml::value_t::String); + BOOST_CHECK(v1.is(toml::value_t::String)); + BOOST_CHECK(v2.is(toml::value_t::String)); + BOOST_CHECK(v3.is(toml::value_t::String)); + BOOST_CHECK(v1.is()); + BOOST_CHECK(v2.is()); + BOOST_CHECK(v3.is()); + + BOOST_CHECK_EQUAL(v1.cast(), "foo"); + BOOST_CHECK_EQUAL(v2.cast(), "foo"); + BOOST_CHECK_EQUAL(v3.cast(), "foo"); + + v1 = "bar"; + v2 = "bar"; + v3 = "bar"; + + BOOST_CHECK_EQUAL(v1.type(), toml::value_t::String); + BOOST_CHECK_EQUAL(v2.type(), toml::value_t::String); + BOOST_CHECK_EQUAL(v3.type(), toml::value_t::String); + BOOST_CHECK(v1.is(toml::value_t::String)); + BOOST_CHECK(v2.is(toml::value_t::String)); + BOOST_CHECK(v3.is(toml::value_t::String)); + BOOST_CHECK(v1.is()); + BOOST_CHECK(v2.is()); + BOOST_CHECK(v3.is()); + + BOOST_CHECK_EQUAL(v1.cast(), "bar"); + BOOST_CHECK_EQUAL(v2.cast(), "bar"); + BOOST_CHECK_EQUAL(v3.cast(), "bar"); + + toml::value v4(v1); + toml::value v5(v2); + toml::value v6(v3); + BOOST_CHECK(v4 == v1); + BOOST_CHECK(v5 == v2); + BOOST_CHECK(v6 == v3); + + BOOST_CHECK_EQUAL(v4.type(), toml::value_t::String); + BOOST_CHECK_EQUAL(v5.type(), toml::value_t::String); + BOOST_CHECK_EQUAL(v6.type(), toml::value_t::String); + BOOST_CHECK(v4.is(toml::value_t::String)); + BOOST_CHECK(v5.is(toml::value_t::String)); + BOOST_CHECK(v6.is(toml::value_t::String)); + BOOST_CHECK(v4.is()); + BOOST_CHECK(v5.is()); + BOOST_CHECK(v6.is()); + + BOOST_CHECK_EQUAL(v4.cast(), "bar"); + BOOST_CHECK_EQUAL(v5.cast(), "bar"); + BOOST_CHECK_EQUAL(v6.cast(), "bar"); + + v4.cast().str.at(2) = 'z'; + v5.cast().str.at(2) = 'z'; + v6.cast().str.at(2) = 'z'; + + BOOST_CHECK_EQUAL(v4.type(), toml::value_t::String); + BOOST_CHECK_EQUAL(v5.type(), toml::value_t::String); + BOOST_CHECK_EQUAL(v6.type(), toml::value_t::String); + BOOST_CHECK(v4.is(toml::value_t::String)); + BOOST_CHECK(v5.is(toml::value_t::String)); + BOOST_CHECK(v6.is(toml::value_t::String)); + BOOST_CHECK(v4.is()); + BOOST_CHECK(v5.is()); + BOOST_CHECK(v6.is()); + + BOOST_CHECK_EQUAL(v4.cast(), "baz"); + BOOST_CHECK_EQUAL(v5.cast(), "baz"); + BOOST_CHECK_EQUAL(v6.cast(), "baz"); - BOOST_CHECK_EQUAL(v6.cast(), b_); - BOOST_CHECK_EQUAL(v7.cast(), i_); - BOOST_CHECK_EQUAL(v1.cast(), f_); - BOOST_CHECK_EQUAL(v2.cast(), s_); + v1 = true; + v2 = true; + v3 = true; + + BOOST_CHECK_EQUAL(v1.type(), toml::value_t::Boolean); + BOOST_CHECK_EQUAL(v2.type(), toml::value_t::Boolean); + BOOST_CHECK_EQUAL(v3.type(), toml::value_t::Boolean); + BOOST_CHECK(v1.is(toml::value_t::Boolean)); + BOOST_CHECK(v2.is(toml::value_t::Boolean)); + BOOST_CHECK(v3.is(toml::value_t::Boolean)); + BOOST_CHECK(v1.is()); + BOOST_CHECK(v2.is()); + BOOST_CHECK(v3.is()); + + BOOST_CHECK_EQUAL(v1.cast(), true); + BOOST_CHECK_EQUAL(v2.cast(), true); + BOOST_CHECK_EQUAL(v3.cast(), true); +} + +BOOST_AUTO_TEST_CASE(test_value_local_date) +{ + toml::value v1(toml::local_date(2018, toml::month_t::Jan, 31)); + + BOOST_CHECK_EQUAL(v1.type(), toml::value_t::LocalDate); + BOOST_CHECK(v1.is(toml::value_t::LocalDate)); + BOOST_CHECK(v1.is()); + + BOOST_CHECK_EQUAL(v1.cast(), + toml::local_date(2018, toml::month_t::Jan, 31)); + + v1 = toml::local_date(2018, toml::month_t::Apr, 1); + + BOOST_CHECK_EQUAL(v1.type(), toml::value_t::LocalDate); + BOOST_CHECK(v1.is(toml::value_t::LocalDate)); + BOOST_CHECK(v1.is()); + + BOOST_CHECK_EQUAL(v1.cast(), + toml::local_date(2018, toml::month_t::Apr, 1)); + + toml::value v2(v1); + BOOST_CHECK(v2 == v1); + + BOOST_CHECK_EQUAL(v2.type(), toml::value_t::LocalDate); + BOOST_CHECK(v2.is(toml::value_t::LocalDate)); + BOOST_CHECK(v2.is()); + + BOOST_CHECK_EQUAL(v2.cast(), + toml::local_date(2018, toml::month_t::Apr, 1)); + + v1 = true; + BOOST_CHECK_EQUAL(v1.type(), toml::value_t::Boolean); + BOOST_CHECK(v1.is(toml::value_t::Boolean)); + BOOST_CHECK(v1.is()); + BOOST_CHECK_EQUAL(v1.cast(), true); +} + +BOOST_AUTO_TEST_CASE(test_value_local_time) +{ + toml::value v1(toml::local_time(12, 30, 45)); + toml::value v2(std::chrono::hours(12) + std::chrono::minutes(30) + + std::chrono::seconds(45)); + + BOOST_CHECK_EQUAL(v1.type(), toml::value_t::LocalTime); + BOOST_CHECK_EQUAL(v2.type(), toml::value_t::LocalTime); + BOOST_CHECK(v1.is(toml::value_t::LocalTime)); + BOOST_CHECK(v2.is(toml::value_t::LocalTime)); + BOOST_CHECK(v1.is()); + BOOST_CHECK(v2.is()); + + BOOST_CHECK_EQUAL(v1.cast(), + toml::local_time(12, 30, 45)); + BOOST_CHECK_EQUAL(v2.cast(), + toml::local_time(12, 30, 45)); + BOOST_CHECK_EQUAL(v1.cast(), + v2.cast()); + + v1 = toml::local_time(1, 30, 0, /*ms*/ 100, /*us*/ 0); + + BOOST_CHECK_EQUAL(v1.type(), toml::value_t::LocalTime); + BOOST_CHECK(v1.is(toml::value_t::LocalTime)); + BOOST_CHECK(v1.is()); + BOOST_CHECK_EQUAL(v1.cast(), + toml::local_time(1, 30, 0, 100, 0)); + + toml::value v3(v1); + BOOST_CHECK(v3 == v1); + + BOOST_CHECK_EQUAL(v3.type(), toml::value_t::LocalTime); + BOOST_CHECK(v3.is(toml::value_t::LocalTime)); + BOOST_CHECK(v3.is()); + + BOOST_CHECK_EQUAL(v3.cast(), + toml::local_time(1, 30, 0, 100, 0)); + + v1 = true; + BOOST_CHECK_EQUAL(v1.type(), toml::value_t::Boolean); + BOOST_CHECK(v1.is(toml::value_t::Boolean)); + BOOST_CHECK(v1.is()); + BOOST_CHECK_EQUAL(v1.cast(), true); +} + +BOOST_AUTO_TEST_CASE(test_value_local_datetime) +{ + toml::value v1(toml::local_datetime( + toml::local_date(2018, toml::month_t::Jan, 31), + toml::local_time(12, 30, 45) + )); + + BOOST_CHECK_EQUAL(v1.type(), toml::value_t::LocalDatetime); + BOOST_CHECK(v1.is(toml::value_t::LocalDatetime)); + BOOST_CHECK(v1.is()); + + BOOST_CHECK_EQUAL(v1.cast(), + toml::local_datetime( + toml::local_date(2018, toml::month_t::Jan, 31), + toml::local_time(12, 30, 45))); + + v1 = toml::local_datetime( + toml::local_date(2018, toml::month_t::Apr, 1), + toml::local_time(1, 15, 30)); + + BOOST_CHECK_EQUAL(v1.type(), toml::value_t::LocalDatetime); + BOOST_CHECK(v1.is(toml::value_t::LocalDatetime)); + BOOST_CHECK(v1.is()); + + BOOST_CHECK_EQUAL(v1.cast(), + toml::local_datetime( + toml::local_date(2018, toml::month_t::Apr, 1), + toml::local_time(1, 15, 30))); + + toml::value v2(v1); + BOOST_CHECK(v2 == v1); + + BOOST_CHECK_EQUAL(v2.type(), toml::value_t::LocalDatetime); + BOOST_CHECK(v2.is(toml::value_t::LocalDatetime)); + BOOST_CHECK(v2.is()); + + BOOST_CHECK_EQUAL(v2.cast(), + toml::local_datetime( + toml::local_date(2018, toml::month_t::Apr, 1), + toml::local_time(1, 15, 30))); + + v1 = true; + BOOST_CHECK_EQUAL(v1.type(), toml::value_t::Boolean); + BOOST_CHECK(v1.is(toml::value_t::Boolean)); + BOOST_CHECK(v1.is()); + BOOST_CHECK_EQUAL(v1.cast(), true); } -BOOST_AUTO_TEST_CASE(test_value_initializer_list) +BOOST_AUTO_TEST_CASE(test_value_offset_datetime) { - toml::value v1{3,1,4,1,5}; - toml::value v2{{"hoge", 1}, {"piyo", 3.14}, {"fuga", "string"}}; + toml::value v1(toml::offset_datetime( + toml::local_date(2018, toml::month_t::Jan, 31), + toml::local_time(12, 30, 45), + toml::time_offset(9, 0) + )); + + BOOST_CHECK_EQUAL(v1.type(), toml::value_t::OffsetDatetime); + BOOST_CHECK(v1.is(toml::value_t::OffsetDatetime)); + BOOST_CHECK(v1.is()); + + BOOST_CHECK_EQUAL(v1.cast(), + toml::offset_datetime( + toml::local_date(2018, toml::month_t::Jan, 31), + toml::local_time(12, 30, 45), + toml::time_offset(9, 0) + )); + + v1 = toml::offset_datetime( + toml::local_date(2018, toml::month_t::Apr, 1), + toml::local_time(1, 15, 30), + toml::time_offset(9, 0)); + + BOOST_CHECK_EQUAL(v1.type(), toml::value_t::OffsetDatetime); + BOOST_CHECK(v1.is(toml::value_t::OffsetDatetime)); + BOOST_CHECK(v1.is()); + + BOOST_CHECK_EQUAL(v1.cast(), + toml::offset_datetime( + toml::local_date(2018, toml::month_t::Apr, 1), + toml::local_time(1, 15, 30), + toml::time_offset(9, 0))); + + toml::value v2(v1); + BOOST_CHECK(v2 == v1); + + BOOST_CHECK_EQUAL(v2.type(), toml::value_t::OffsetDatetime); + BOOST_CHECK(v2.is(toml::value_t::OffsetDatetime)); + BOOST_CHECK(v2.is()); + + BOOST_CHECK_EQUAL(v2.cast(), + toml::offset_datetime( + toml::local_date(2018, toml::month_t::Apr, 1), + toml::local_time(1, 15, 30), + toml::time_offset(9, 0))); + v1 = true; + BOOST_CHECK_EQUAL(v1.type(), toml::value_t::Boolean); + BOOST_CHECK(v1.is(toml::value_t::Boolean)); + BOOST_CHECK(v1.is()); + BOOST_CHECK_EQUAL(v1.cast(), true); +} + +BOOST_AUTO_TEST_CASE(test_value_array) +{ + std::vector v{1,2,3,4,5}; + toml::value v1(v); + toml::value v2{6,7,8,9,0}; + + BOOST_CHECK_EQUAL(v1.type(), toml::value_t::Array); + BOOST_CHECK(v1.is(toml::value_t::Array)); + BOOST_CHECK(v1.is()); + + BOOST_CHECK_EQUAL(v2.type(), toml::value_t::Array); + BOOST_CHECK(v2.is(toml::value_t::Array)); + BOOST_CHECK(v2.is()); + + BOOST_CHECK_EQUAL(v1.cast().at(0).cast(), 1); + BOOST_CHECK_EQUAL(v1.cast().at(1).cast(), 2); + BOOST_CHECK_EQUAL(v1.cast().at(2).cast(), 3); + BOOST_CHECK_EQUAL(v1.cast().at(3).cast(), 4); + BOOST_CHECK_EQUAL(v1.cast().at(4).cast(), 5); + + BOOST_CHECK_EQUAL(v2.cast().at(0).cast(), 6); + BOOST_CHECK_EQUAL(v2.cast().at(1).cast(), 7); + BOOST_CHECK_EQUAL(v2.cast().at(2).cast(), 8); + BOOST_CHECK_EQUAL(v2.cast().at(3).cast(), 9); + BOOST_CHECK_EQUAL(v2.cast().at(4).cast(), 0); + + v1 = {6,7,8,9,0}; + v2 = v; BOOST_CHECK_EQUAL(v1.type(), toml::value_t::Array); - BOOST_CHECK_EQUAL(v2.type(), toml::value_t::Table); + BOOST_CHECK(v1.is(toml::value_t::Array)); + BOOST_CHECK(v1.is()); + + BOOST_CHECK_EQUAL(v2.type(), toml::value_t::Array); + BOOST_CHECK(v2.is(toml::value_t::Array)); + BOOST_CHECK(v2.is()); + + BOOST_CHECK_EQUAL(v1.cast().at(0).cast(), 6); + BOOST_CHECK_EQUAL(v1.cast().at(1).cast(), 7); + BOOST_CHECK_EQUAL(v1.cast().at(2).cast(), 8); + BOOST_CHECK_EQUAL(v1.cast().at(3).cast(), 9); + BOOST_CHECK_EQUAL(v1.cast().at(4).cast(), 0); + + BOOST_CHECK_EQUAL(v2.cast().at(0).cast(), 1); + BOOST_CHECK_EQUAL(v2.cast().at(1).cast(), 2); + BOOST_CHECK_EQUAL(v2.cast().at(2).cast(), 3); + BOOST_CHECK_EQUAL(v2.cast().at(3).cast(), 4); + BOOST_CHECK_EQUAL(v2.cast().at(4).cast(), 5); - const auto& ar = v1.cast(); - BOOST_CHECK_EQUAL(ar.at(0).cast(), 3); - BOOST_CHECK_EQUAL(ar.at(1).cast(), 1); - BOOST_CHECK_EQUAL(ar.at(2).cast(), 4); - BOOST_CHECK_EQUAL(ar.at(3).cast(), 1); - BOOST_CHECK_EQUAL(ar.at(4).cast(), 5); + toml::value v3(v1); + BOOST_CHECK(v3 == v1); - const auto& tb = v2.cast(); + BOOST_CHECK_EQUAL(v3.type(), toml::value_t::Array); + BOOST_CHECK(v3.is(toml::value_t::Array)); + BOOST_CHECK(v3.is()); - BOOST_CHECK_EQUAL(tb.at("hoge").type(), toml::value_t::Integer); - BOOST_CHECK_EQUAL(tb.at("piyo").type(), toml::value_t::Float); - BOOST_CHECK_EQUAL(tb.at("fuga").type(), toml::value_t::String); + BOOST_CHECK_EQUAL(v3.cast().at(0).cast(), 6); + BOOST_CHECK_EQUAL(v3.cast().at(1).cast(), 7); + BOOST_CHECK_EQUAL(v3.cast().at(2).cast(), 8); + BOOST_CHECK_EQUAL(v3.cast().at(3).cast(), 9); + BOOST_CHECK_EQUAL(v3.cast().at(4).cast(), 0); + + v1 = true; + BOOST_CHECK_EQUAL(v1.type(), toml::value_t::Boolean); + BOOST_CHECK(v1.is(toml::value_t::Boolean)); + BOOST_CHECK(v1.is()); + BOOST_CHECK_EQUAL(v1.cast(), true); +} + +BOOST_AUTO_TEST_CASE(test_value_table) +{ + toml::value v1{{"foo", 42}, {"bar", 3.14}, {"baz", "qux"}}; - BOOST_CHECK_EQUAL(tb.at("hoge").cast(), 1); - BOOST_CHECK_CLOSE_FRACTION(tb.at("piyo").cast(), 3.14, 1e-3); - BOOST_CHECK_EQUAL(tb.at("fuga").cast(), "string"); + BOOST_CHECK_EQUAL(v1.type(), toml::value_t::Table); + BOOST_CHECK(v1.is(toml::value_t::Table)); + BOOST_CHECK(v1.is()); + + BOOST_CHECK_EQUAL(v1.cast().at("foo").cast(), 42); + BOOST_CHECK_EQUAL(v1.cast().at("bar").cast(), 3.14); + BOOST_CHECK_EQUAL(v1.cast().at("baz").cast().str, "qux"); + + v1 = toml::table{{"foo", 2.71}, {"bar", 54}, {"baz", "quux"}}; + + BOOST_CHECK_EQUAL(v1.type(), toml::value_t::Table); + BOOST_CHECK(v1.is(toml::value_t::Table)); + BOOST_CHECK(v1.is()); + + BOOST_CHECK_EQUAL(v1.cast().at("foo").cast(), 2.71); + BOOST_CHECK_EQUAL(v1.cast().at("bar").cast(), 54); + BOOST_CHECK_EQUAL(v1.cast().at("baz").cast().str, "quux"); + + toml::value v3(v1); + BOOST_CHECK(v3 == v1); + + BOOST_CHECK_EQUAL(v3.type(), toml::value_t::Table); + BOOST_CHECK(v3.is(toml::value_t::Table)); + BOOST_CHECK(v3.is()); + + BOOST_CHECK_EQUAL(v3.cast().at("foo").cast(), 2.71); + BOOST_CHECK_EQUAL(v3.cast().at("bar").cast(), 54); + BOOST_CHECK_EQUAL(v3.cast().at("baz").cast().str, "quux"); + + v1 = true; + BOOST_CHECK_EQUAL(v1.type(), toml::value_t::Boolean); + BOOST_CHECK(v1.is(toml::value_t::Boolean)); + BOOST_CHECK(v1.is()); + BOOST_CHECK_EQUAL(v1.cast(), true); } diff --git a/tests/test_value_operator.cpp b/tests/test_value_operator.cpp deleted file mode 100644 index a630c982..00000000 --- a/tests/test_value_operator.cpp +++ /dev/null @@ -1,98 +0,0 @@ -#define BOOST_TEST_MODULE "test_value_operator" -#ifdef UNITTEST_FRAMEWORK_LIBRARY_EXIST -#include -#else -#define BOOST_TEST_NO_LIB -#include -#endif -#include -#include -#include - -BOOST_AUTO_TEST_CASE(test_value_equal) -{ - toml::Boolean b(true); - toml::Integer i(42); - toml::Float f(3.14); - toml::String s("hoge"); - toml::Datetime d(std::chrono::system_clock::now()); - toml::Array a; - a.emplace_back(2); - a.emplace_back(7); - a.emplace_back(1); - a.emplace_back(8); - a.emplace_back(2); - toml::Table t; - t.emplace("val1", true); - t.emplace("val2", 42); - t.emplace("val3", 3.14); - t.emplace("val4", "piyo"); - - toml::value v1(b); - toml::value v2(i); - toml::value v3(f); - toml::value v4(s); - toml::value v5(d); - toml::value v6(a); - toml::value v7(t); - - toml::value u1(b); - toml::value u2(i); - toml::value u3(f); - toml::value u4(s); - toml::value u5(d); - toml::value u6(a); - toml::value u7(t); - - const bool b1 = v1 == u1; - const bool b2 = v2 == u2; - const bool b3 = v3 == u3; - const bool b4 = v4 == u4; - const bool b5 = v5 == u5; - const bool b6 = v6 == u6; - const bool b7 = v7 == u7; - - BOOST_CHECK(b1); - BOOST_CHECK(b2); - BOOST_CHECK(b3); - BOOST_CHECK(b4); - BOOST_CHECK(b5); - BOOST_CHECK(b6); - BOOST_CHECK(b7); - - { - const bool n1 = v1 != u1; - const bool n2 = v2 != u2; - const bool n3 = v3 != u3; - const bool n4 = v4 != u4; - const bool n5 = v5 != u5; - const bool n6 = v6 != u6; - const bool n7 = v7 != u7; - - BOOST_CHECK(!n1); - BOOST_CHECK(!n2); - BOOST_CHECK(!n3); - BOOST_CHECK(!n4); - BOOST_CHECK(!n5); - BOOST_CHECK(!n6); - BOOST_CHECK(!n7); - } - - { - const bool n1 = v1 == u2; - const bool n2 = v2 == u3; - const bool n3 = v3 == u4; - const bool n4 = v4 == u5; - const bool n5 = v5 == u6; - const bool n6 = v6 == u7; - const bool n7 = v7 == u1; - - BOOST_CHECK(!n1); - BOOST_CHECK(!n2); - BOOST_CHECK(!n3); - BOOST_CHECK(!n4); - BOOST_CHECK(!n5); - BOOST_CHECK(!n6); - BOOST_CHECK(!n7); - } -} diff --git a/toml/acceptor.hpp b/toml/acceptor.hpp deleted file mode 100644 index 11844ea4..00000000 --- a/toml/acceptor.hpp +++ /dev/null @@ -1,777 +0,0 @@ -#ifndef TOML11_ACCEPTOR -#define TOML11_ACCEPTOR -#include -#include -#include -#include "exception.hpp" - -namespace toml -{ - -template -struct is_character -{ - typedef charT value_type; - constexpr static value_type target = c; - - template::value_type, - value_type>::value>::type> - constexpr static Iterator invoke(Iterator iter, Iterator end) - { - return iter == end ? iter : *iter == c ? std::next(iter) : iter; - } -}; - -template -struct is_in_range -{ - typedef charT value_type; - constexpr static value_type upper = up; - constexpr static value_type lower = lw; - static_assert(lower <= upper, "lower <= upper"); - - template::value_type, - value_type>::value>::type> - constexpr static Iterator invoke(Iterator iter, Iterator end) - { - return iter == end ? iter : - (lower <= *iter && *iter <= upper) ? std::next(iter) : iter; - } -}; - -template -struct is_one_of -{ - typedef typename headT::value_type value_type; - static_assert( - std::is_same::value_type>::value, - "different value_type"); - - template::value_type, - value_type>::value>::type> - static Iterator invoke(Iterator iter, Iterator end) - { - const Iterator tmp = headT::invoke(iter, end); - return (tmp != iter) ? tmp : is_one_of::invoke(iter, end); - } -}; -template -struct is_one_of -{ - typedef typename tailT::value_type value_type; - - template::value_type, - value_type>::value>::type> - static Iterator invoke(Iterator iter, Iterator end) - { - const Iterator tmp = tailT::invoke(iter, end); - return (tmp != iter) ? tmp : iter; - } -}; - -// just a wrapper for maybe_ignored -template -struct is_ignorable -{ - typedef typename condT::value_type value_type; - - template::value_type, - value_type>::value>::type> - static Iterator invoke(Iterator iter, Iterator end) - { - const Iterator tmp = condT::invoke(iter, end); - return (tmp != iter) ? tmp : iter; - } -}; - -template -struct maybe_ignored : std::false_type{}; -template -struct maybe_ignored> : std::true_type{}; - -template -struct is_chain_of_impl -{ - typedef typename headT::value_type value_type; - static_assert(std::is_same::value_type>::value, - "different value_type"); - - constexpr static bool ignorable = maybe_ignored::value; - - template::value_type, - value_type>::value>::type> - static Iterator invoke(Iterator iter, Iterator end, Iterator rollback) - { - const Iterator tmp = headT::invoke(iter, end); - return (tmp == iter && !ignorable) ? rollback : - is_chain_of_impl::invoke(tmp, end, rollback); - } -}; - -template -struct is_chain_of_impl -{ - typedef typename tailT::value_type value_type; - constexpr static bool ignorable = maybe_ignored::value; - - template::value_type, - value_type>::value>::type> - static Iterator invoke(Iterator iter, Iterator end, Iterator rollback) - { - const Iterator tmp = tailT::invoke(iter, end); - return (tmp == iter) ? (ignorable ? iter : rollback) : tmp; - } -}; - -template -struct is_chain_of -{ - typedef typename is_chain_of_impl::value_type value_type; - - template::value_type, - value_type>::value>::type> - static Iterator invoke(Iterator iter, Iterator end) - { - return is_chain_of_impl::invoke(iter, end, iter); - } -}; - -constexpr inline std::size_t repeat_infinite(){return 0ul;} - -template -struct is_repeat_of -{ - typedef typename condT::value_type value_type; - - template::value_type, - value_type>::value>::type> - static Iterator invoke(Iterator iter, Iterator end) - { - const Iterator rollback = iter; - Iterator tmp; - for(auto i=0ul; i -struct is_repeat_of -{ - typedef typename condT::value_type value_type; - - template::value_type, - value_type>::value>::type> - static Iterator invoke(Iterator iter, Iterator end) - { - Iterator tmp = condT::invoke(iter, end); - while(tmp != iter) - { - iter = tmp; - tmp = condT::invoke(iter, end); - } - return iter; - } -}; - -template -struct is_none_of -{ - typedef typename headT::value_type value_type; - static_assert( - std::is_same::value_type>::value, - "different value_type"); - - template::value_type, - value_type>::value>::type> - static Iterator invoke(Iterator iter, Iterator end) - { - const Iterator tmp = headT::invoke(iter, end); - return (tmp != iter) ? iter : is_none_of::invoke(iter, end); - } -}; - -template -struct is_none_of -{ - typedef typename tailT::value_type value_type; - - template::value_type, - value_type>::value>::type> - static Iterator invoke(Iterator iter, Iterator end) - { - const Iterator tmp = tailT::invoke(iter, end); - return (tmp != iter) ? iter : std::next(iter); - } -}; - -template -struct is_not_but -{ - typedef typename notT::value_type value_type; - static_assert( - std::is_same::value, - "different value type"); - - template::value_type, - value_type>::value>::type> - static Iterator invoke(Iterator iter, Iterator end) - { - return (iter != notT::invoke(iter, end)) ? iter : butT::invoke(iter, end); - } -}; - -template -using is_space = is_character; -template -using is_tab = is_character; -template -using is_number = is_in_range; -template -using is_lowercase = is_in_range; -template -using is_uppercase = is_in_range; -template -using is_alphabet = is_one_of, is_uppercase>; -template -using is_hex = is_one_of, is_in_range, - is_in_range>; -template -using is_whitespace = is_one_of, is_tab>; -template -using is_any_num_of_ws = - is_ignorable, repeat_infinite()>>; - -template -using is_newline = is_one_of, - is_chain_of, is_character>>; -template -using is_barekey_component = is_one_of, is_number, - is_character, is_character>; -template -using is_barekey = is_repeat_of, repeat_infinite()>; -template -using is_comment = - is_chain_of< - is_character, - is_repeat_of>, repeat_infinite()>, - is_newline - >; - -template -using is_basic_inline_string_component = - is_one_of< - is_none_of< is_in_range, is_character, - is_character, is_newline>, - is_chain_of, is_character>, - is_chain_of, is_character>, - is_chain_of, is_character>, - is_chain_of, is_character>, - is_chain_of, is_character>, - is_chain_of, is_character>, - is_chain_of, is_character>, - is_chain_of, is_character, - is_repeat_of, 4>>, - is_chain_of, is_character, - is_repeat_of, 8>> - >; -template -using is_basic_inline_string = - is_not_but< - is_repeat_of, 3>, // not multiline - is_chain_of< - is_character, - is_ignorable, - repeat_infinite()>>, - is_character - > - >; -template -using is_basic_multiline_string_component = - is_one_of< - is_none_of< is_in_range, - is_repeat_of, 3>, - is_character>, - is_newline, - is_chain_of, is_newline>, - is_chain_of, is_character>, - is_chain_of, is_character>, - is_chain_of, is_character>, - is_chain_of, is_character>, - is_chain_of, is_character>, - is_chain_of, is_character>, - is_chain_of, is_character>, - is_chain_of, is_character, - is_repeat_of, 4>>, - is_chain_of, is_character, - is_repeat_of, 8>> - >; -template -using is_basic_multiline_string = - is_chain_of< - is_repeat_of, 3>, - is_ignorable, - repeat_infinite()>>, - is_repeat_of, 3> - >; - -template -using is_literal_inline_string_component = - is_none_of, is_character>; - -template -using is_literal_inline_string = - is_not_but< - is_repeat_of, 3>, - is_chain_of< - is_character, - is_ignorable, - repeat_infinite()>>, - is_character - > - >; - -template -using is_literal_multiline_string_component = - is_one_of< - is_none_of, - is_repeat_of, 3>>, - is_newline - >; - -template -using is_literal_multiline_string = - is_chain_of< - is_repeat_of, 3>, - is_ignorable, - repeat_infinite()>>, - is_repeat_of, 3> - >; - -template -using is_string = - is_one_of< - is_basic_inline_string, - is_basic_multiline_string, - is_literal_inline_string, - is_literal_multiline_string - >; - - -template -using is_sign = is_one_of, is_character>; -template -using is_nonzero_number = is_in_range; - -template -using is_integer_component = - is_not_but< - is_repeat_of, 2>, - is_one_of< - is_character, is_number - > - >; -template -using is_integer = - is_chain_of< - is_ignorable>, - is_one_of< - is_character, - is_chain_of< - is_nonzero_number, - is_ignorable, - repeat_infinite()> - > - > - > - >; - -template -using is_fractional_part = - is_chain_of< - is_character, - is_repeat_of, repeat_infinite()> - >; -template -using is_exponent_part = - is_chain_of< - is_one_of, is_character>, - is_integer - >; -template -using is_float = - is_one_of< - is_chain_of< - is_integer, - is_fractional_part, - is_exponent_part - >, - is_chain_of< - is_integer, - is_fractional_part - >, - is_chain_of< - is_integer, - is_exponent_part - > - >; - -template -using is_boolean = - is_one_of< - is_chain_of< - is_character, - is_character, - is_character, - is_character - >, - is_chain_of< - is_character, - is_character, - is_character, - is_character, - is_character - > - >; - -template -using is_local_time = - is_chain_of< - is_repeat_of, 2>, - is_character, - is_repeat_of, 2>, - is_character, - is_repeat_of, 2>, - is_ignorable< - is_chain_of< - is_character, - is_repeat_of, repeat_infinite()> - > - > - >; - -template -using is_local_date = - is_chain_of< - is_repeat_of, 4>, - is_character, - is_repeat_of, 2>, - is_character, - is_repeat_of, 2> - >; - -template -using is_local_date_time = - is_chain_of< - is_local_date, - is_character, - is_local_time - >; - -template -using is_offset = - is_one_of< - is_character, - is_chain_of< - is_sign, - is_repeat_of, 2>, - is_character, - is_repeat_of, 2> - > - >; - -template -using is_offset_date_time = - is_chain_of< - is_local_date_time, - is_offset - >; - -template -using is_datetime = - is_one_of< - is_offset_date_time, - is_local_date_time, - is_local_date, - is_local_time - >; - -template -using is_fundamental_type = - is_one_of< - is_basic_inline_string, - is_basic_multiline_string, - is_literal_inline_string, - is_literal_multiline_string, - is_offset_date_time, - is_local_date_time, - is_local_date, - is_local_time, - is_boolean, - is_float, - is_integer - >; - -template -using is_skippable_in_array = - is_repeat_of< - is_one_of, is_newline, is_comment>, - repeat_infinite() - >; - -template -struct is_inline_table; - -template -using is_key = - is_one_of< - is_barekey, - is_string - >; - - -template -using is_fixed_type_array = - is_chain_of< - is_character, - is_ignorable< - is_repeat_of< - is_chain_of< - is_ignorable>, - is_array_component, - is_ignorable>, - is_character - >, - repeat_infinite() - > - >, - is_ignorable< - is_chain_of< - is_ignorable>, - is_array_component, - is_ignorable>, - is_ignorable> - > - >, - is_ignorable>, - is_character - >; - -template -struct is_array -{ - typedef charT value_type; - - template::value_type, - value_type>::value>::type> - static Iterator invoke(Iterator iter, Iterator end) - { - return is_one_of< - is_fixed_type_array>, - is_fixed_type_array>, - is_fixed_type_array>, - is_fixed_type_array>, - is_fixed_type_array>, - is_fixed_type_array>, - is_fixed_type_array> - >::invoke(iter, end); - } -}; - -template -struct is_inline_table -{ - typedef charT value_type; - - template::value_type, - value_type>::value>::type> - static Iterator invoke(Iterator iter, Iterator end) - { - typedef is_one_of, - is_array, is_inline_table> is_component; - - typedef is_chain_of< - is_any_num_of_ws, - is_key, - is_any_num_of_ws, - is_character, - is_ignorable< - is_repeat_of< - is_chain_of< - is_any_num_of_ws, - is_inline_key_value_pair, - is_any_num_of_ws, - is_character - >, - repeat_infinite() - > - >, - is_ignorable< - is_chain_of< - is_any_num_of_ws, - is_inline_key_value_pair, - is_any_num_of_ws, - is_ignorable> - > - >, - is_any_num_of_ws, - is_character - > entity; - return entity::invoke(iter, end); - } -}; - -template -using is_value = - is_one_of, is_array, is_inline_table>; - -// [] -template -using is_table_definition = - is_chain_of< - is_any_num_of_ws, - is_character, - is_any_num_of_ws, - is_key, - is_ignorable< - is_repeat_of< - is_chain_of< - is_any_num_of_ws, - is_character, - is_any_num_of_ws, - is_key, - is_any_num_of_ws - >, - repeat_infinite()> - >, - is_character - >; - -template -using is_array_of_table_definition = - is_chain_of< - is_any_num_of_ws, - is_repeat_of, 2>, - is_any_num_of_ws, - is_key, - is_ignorable< - is_repeat_of< - is_chain_of< - is_any_num_of_ws, - is_character, - is_any_num_of_ws, - is_key, - is_any_num_of_ws - >, - repeat_infinite()> - >, - is_repeat_of, 2> - >; - -template -using is_key_value_pair = - is_chain_of< - is_any_num_of_ws, - is_key, - is_any_num_of_ws, - is_character(std::addressof(c)))) + { + return std::string(1, c); + } + else + { + std::ostringstream oss; + oss << std::hex << std::setfill('0') << std::setw(2) << "0x" + << static_cast(c); + return oss.str(); + } +} + +template +struct character +{ + static constexpr char target = C; + + template + static result, std::string> invoke(location& loc) + { + static_assert(std::is_same::value, + "internal error: container::value_type should be `char`."); + + if(loc.iter() == loc.end()) {return err("not sufficient characters");} + const auto first = loc.iter(); + + const char c = *(loc.iter()); + if(c != target) + { + return err(concat_to_string("expected '", show_char(target), + "' but got '", show_char(c), "'.")); + } + ++(loc.iter()); // update location + + return ok(region(loc, first, loc.iter())); + } + + static std::string pattern() {return show_char(target);} +}; +template +constexpr char character::target; + +// closed interval [Low, Up]. both Low and Up are included. +template +struct in_range +{ + // assuming ascii part of UTF-8... + static_assert(Low <= Up, "lower bound should be less than upper bound."); + + static constexpr char upper = Up; + static constexpr char lower = Low; + + template + static result, std::string> invoke(location& loc) + { + static_assert(std::is_same::value, + "internal error: container::value_type should be `char`."); + + if(loc.iter() == loc.end()) {return err("not sufficient characters");} + const auto first = loc.iter(); + + const char c = *(loc.iter()); + if(c < lower || upper < c) + { + return err(concat_to_string("expected character in range " + "[", show_char(lower), ", ", show_char(upper), "] but got ", + "'", show_char(c), "'.")); + } + + ++(loc.iter()); + return ok(region(loc, first, loc.iter())); + } + + static std::string pattern() + { + return concat_to_string("[",show_char(lower),"-",show_char(upper),"]"); + } +}; +template constexpr char in_range::upper; +template constexpr char in_range::lower; + +// keep iterator if `Combinator` matches. otherwise, increment `iter` by 1 char. +// for detecting invalid characters, like control sequences in toml string. +template +struct exclude +{ + template + static result, std::string> invoke(location& loc) + { + static_assert(std::is_same::value, + "internal error: container::value_type should be `char`."); + + if(loc.iter() == loc.end()) {return err("not sufficient characters");} + auto first = loc.iter(); + + auto rslt = Combinator::invoke(loc); + if(rslt.is_ok()) + { + loc.iter() = first; // rollback + return err(concat_to_string( + "invalid pattern (", Combinator::pattern(), ") appeared ", + rslt.unwrap().str())); + } + loc.iter() = std::next(first); + return ok(region(loc, first, loc.iter())); + } + + static std::string pattern() + { + return concat_to_string("^(", Combinator::pattern(), ')'); + } +}; + +// increment `iter`, if matches. otherwise, just return empty string. +template +struct maybe +{ + template + static result, std::string> invoke(location& loc) + { + static_assert(std::is_same::value, + "internal error: container::value_type should be `char`."); + + const auto rslt = Combinator::invoke(loc); + if(rslt.is_ok()) + { + return rslt; + } + return ok(region(loc)); + } + + static std::string pattern() + { + return concat_to_string('(', Combinator::pattern(), ")?"); + } +}; + +template +struct sequence; + +template +struct sequence +{ + template + static result, std::string> invoke(location& loc) + { + static_assert(std::is_same::value, + "internal error: container::value_type should be `char`."); + + const auto first = loc.iter(); + const auto rslt = Head::invoke(loc); + if(rslt.is_err()) + { + loc.iter() = first; + return err(rslt.unwrap_err()); + } + return sequence::invoke(loc, std::move(rslt.unwrap()), first); + } + + // called from the above function only, recursively. + template + static result, std::string> + invoke(location& loc, region reg, Iterator first) + { + const auto rslt = Head::invoke(loc); + if(rslt.is_err()) + { + loc.iter() = first; + return err(rslt.unwrap_err()); + } + reg += rslt.unwrap(); // concat regions + return sequence::invoke(loc, std::move(reg), first); + } + + static std::string pattern() + { + return concat_to_string(Head::pattern(), sequence::pattern()); + } +}; + +template +struct sequence +{ + // would be called from sequence::invoke only. + template + static result, std::string> + invoke(location& loc, region reg, Iterator first) + { + const auto rslt = Head::invoke(loc); + if(rslt.is_err()) + { + loc.iter() = first; + return err(rslt.unwrap_err()); + } + reg += rslt.unwrap(); // concat regions + return ok(reg); + } + static std::string pattern() {return Head::pattern();} +}; + +template +struct either; + +template +struct either +{ + template + static result, std::string> invoke(location& loc) + { + static_assert(std::is_same::value, + "internal error: container::value_type should be `char`."); + + const auto rslt = Head::invoke(loc); + if(rslt.is_ok()) {return rslt;} + return either::invoke(loc); + } + + static std::string pattern() + { + return concat_to_string('(', Head::pattern(), ")|", either::pattern()); + } +}; +template +struct either +{ + template + static result, std::string> invoke(location& loc) + { + static_assert(std::is_same::value, + "internal error: container::value_type should be `char`."); + return Head::invoke(loc); + } + static std::string pattern() + { + return concat_to_string('(', Head::pattern(), ')'); + } +}; + +template +struct repeat; + +template struct exactly{}; +template struct at_least{}; +struct unlimited{}; + +template +struct repeat> +{ + template + static result, std::string> invoke(location& loc) + { + region retval(loc); + const auto first = loc.iter(); + for(std::size_t i=0; i +struct repeat> +{ + template + static result, std::string> invoke(location& loc) + { + region retval(loc); + + const auto first = loc.iter(); + for(std::size_t i=0; i +struct repeat +{ + template + static result, std::string> invoke(location& loc) + { + region retval(loc); + while(true) + { + auto rslt = T::invoke(loc); + if(rslt.is_err()) + { + return ok(std::move(retval)); + } + retval += rslt.unwrap(); + } + } + static std::string pattern() {return concat_to_string('(', T::pattern(), ")*");} +}; + +} // detail +} // toml +#endif// TOML11_COMBINATOR_HPP diff --git a/toml/datetime.hpp b/toml/datetime.hpp index 1978286a..a32968a5 100644 --- a/toml/datetime.hpp +++ b/toml/datetime.hpp @@ -1,231 +1,540 @@ +// Copyright Toru Niina 2017. +// Distributed under the MIT License. #ifndef TOML11_DATETIME #define TOML11_DATETIME #include +#include +#include +#include #include +#include +#include #include namespace toml { -template -struct basic_datetime -{ - typedef unsignedT number_type; - typedef intT offset_type; - constexpr static unsignedT undef = std::numeric_limits::max(); - constexpr static intT nooffset = std::numeric_limits::max(); - - unsignedT year; - unsignedT month; - unsignedT day; - unsignedT hour; - unsignedT minute; - unsignedT second; - unsignedT millisecond; - unsignedT microsecond; - intT offset_hour; - intT offset_minute; - - basic_datetime() = default; - ~basic_datetime() = default; - basic_datetime(const basic_datetime&) = default; - basic_datetime(basic_datetime&&) = default; - basic_datetime& operator=(const basic_datetime&) = default; - basic_datetime& operator=(basic_datetime&&) = default; - - basic_datetime(unsignedT y, unsignedT m, unsignedT d) - : year(y), month(m), day(d), hour(undef), minute(undef), second(undef), - millisecond(undef), microsecond(undef), - offset_hour(nooffset), offset_minute(nooffset) - {} - basic_datetime(unsignedT h, unsignedT m, unsignedT s, - unsignedT ms, unsignedT us) - : year(undef), month(undef), day(undef), hour(h), minute(m), second(s), - millisecond(ms), microsecond(us), - offset_hour(nooffset), offset_minute(nooffset) - {} - basic_datetime(unsignedT y, unsignedT mth, unsignedT d, - unsignedT h, unsignedT min, unsignedT s, - unsignedT ms, unsignedT us) - : year(y), month(mth), day(d), hour(h), minute(min), second(s), - millisecond(ms), microsecond(us), - offset_hour(nooffset), offset_minute(nooffset) - {} - basic_datetime(unsignedT y, unsignedT mth, unsignedT d, - unsignedT h, unsignedT min, unsignedT s, - unsignedT ss, unsignedT us, intT oh, intT om) - : year(y), month(mth), day(d), hour(h), minute(min), second(s), - millisecond(ss), microsecond(us), offset_hour(oh), offset_minute(om) +enum class month_t : std::int8_t +{ + Jan = 0, + Feb = 1, + Mar = 2, + Apr = 3, + May = 4, + Jun = 5, + Jul = 6, + Aug = 7, + Sep = 8, + Oct = 9, + Nov = 10, + Dec = 11 +}; + +struct local_date +{ + std::int16_t year; // A.D. (like, 2018) + std::uint8_t month; // [0, 11] + std::uint8_t day; // [1, 31] + + local_date(int y, month_t m, int d) + : year (static_cast(y)), + month(static_cast(m)), + day (static_cast(d)) {} - basic_datetime(std::chrono::system_clock::time_point tp); - basic_datetime(std::time_t t); + explicit local_date(const std::tm& t) + : year (static_cast(t.tm_year + 1900)), + month(static_cast(t.tm_mon)), + day (static_cast(t.tm_mday)) + {} - operator std::chrono::system_clock::time_point() const + explicit local_date(const std::chrono::system_clock::time_point& tp) { - std::tm time; - if(this->year == undef || this->month == undef || this->day == undef) - { - const auto now = std::chrono::system_clock::now(); - const auto t = std::chrono::system_clock::to_time_t(now); - std::tm* t_ = std::localtime(&t); - time.tm_year = t_->tm_year; - time.tm_mon = t_->tm_mon; - time.tm_mday = t_->tm_mday; - } - else - { - time.tm_year = this->year - 1900; - time.tm_mon = this->month - 1; - time.tm_mday = this->day; - } - time.tm_hour = (this->hour == undef) ? 0 : this->hour; - time.tm_min = (this->minute == undef) ? 0 : this->minute; - time.tm_sec = (this->second == undef) ? 0 : this->second; + const auto t = std::chrono::system_clock::to_time_t(tp); + const auto tmp = std::localtime(&t); //XXX: not threadsafe! + assert(tmp); // if std::localtime fails, tmp is nullptr + const std::tm time = *tmp; + *this = local_date(time); + } - auto tp = std::chrono::system_clock::from_time_t(std::mktime(&time)); - tp += std::chrono::milliseconds(this->millisecond); - tp += std::chrono::microseconds(this->microsecond); - // mktime regards the tm struct as localtime. so adding offset is not needed. + explicit local_date(const std::time_t t) + : local_date(std::chrono::system_clock::from_time_t(t)) + {} - return tp; + operator std::chrono::system_clock::time_point() const + { + // std::mktime returns date as local time zone. no conversion needed + std::tm t; + t.tm_sec = 0; + t.tm_min = 0; + t.tm_hour = 0; + t.tm_mday = this->day; + t.tm_mon = this->month; + t.tm_year = this->year - 1900; + t.tm_wday = 0; // the value will be ignored + t.tm_yday = 0; // the value will be ignored + t.tm_isdst = -1; + return std::chrono::system_clock::from_time_t(std::mktime(&t)); } + operator std::time_t() const { return std::chrono::system_clock::to_time_t( - std::chrono::system_clock::time_point(*this)); + std::chrono::system_clock::time_point(*this)); } + + local_date() = default; + ~local_date() = default; + local_date(local_date const&) = default; + local_date(local_date&&) = default; + local_date& operator=(local_date const&) = default; + local_date& operator=(local_date&&) = default; }; -template -basic_datetime::basic_datetime(std::chrono::system_clock::time_point tp) +inline bool operator==(const local_date& lhs, const local_date& rhs) { - const auto t = std::chrono::system_clock::to_time_t(tp); - std::tm *time = std::localtime(&t); - this->year = time->tm_year + 1900; - this->month = time->tm_mon + 1; - this->day = time->tm_mday; - this->hour = time->tm_hour; - this->minute = time->tm_min; - this->second = time->tm_sec; - auto t_ = std::chrono::system_clock::from_time_t(std::mktime(time)); - auto diff = tp - t_; - this->millisecond = std::chrono::duration_cast(diff).count() % 1000; - this->microsecond = std::chrono::duration_cast(diff).count() % 1000; - - std::tm *utc = std::gmtime(&t); - int total_offset = (this->hour - utc->tm_hour) * 60 + - (this->minute - utc->tm_min); - if(total_offset > 720) total_offset -= 1440; - else if(total_offset < -720) total_offset += 1440; - offset_hour = total_offset / 60; - offset_minute = total_offset - (offset_hour * 60); + return std::make_tuple(lhs.year, lhs.month, lhs.day) == + std::make_tuple(rhs.year, rhs.month, rhs.day); } - -template -basic_datetime::basic_datetime(std::time_t t) +inline bool operator!=(const local_date& lhs, const local_date& rhs) +{ + return !(lhs == rhs); +} +inline bool operator< (const local_date& lhs, const local_date& rhs) { - *this = basic_datetime(std::chrono::system_clock::from_time_t(t)); + return std::make_tuple(lhs.year, lhs.month, lhs.day) < + std::make_tuple(rhs.year, rhs.month, rhs.day); +} +inline bool operator<=(const local_date& lhs, const local_date& rhs) +{ + return (lhs < rhs) || (lhs == rhs); +} +inline bool operator> (const local_date& lhs, const local_date& rhs) +{ + return !(lhs <= rhs); +} +inline bool operator>=(const local_date& lhs, const local_date& rhs) +{ + return !(lhs < rhs); } - -template +template std::basic_ostream& -operator<<(std::basic_ostream& os, basic_datetime const& dt) +operator<<(std::basic_ostream& os, const local_date& date) +{ + os << std::setfill('0') << std::setw(4) << static_cast(date.year ) << '-'; + os << std::setfill('0') << std::setw(2) << static_cast(date.month + 1) << '-'; + os << std::setfill('0') << std::setw(2) << static_cast(date.day ); + return os; +} + +struct local_time { - bool date = false; - if(dt.year != basic_datetime::undef && - dt.month != basic_datetime::undef && - dt.day != basic_datetime::undef) + std::uint8_t hour; // [0, 23] + std::uint8_t minute; // [0, 59] + std::uint8_t second; // [0, 60] + std::uint16_t millisecond; // [0, 999] + std::uint16_t microsecond; // [0, 999] + std::uint16_t nanosecond; // [0, 999] + + local_time(int h, int m, int s, + int ms = 0, int us = 0, int ns = 0) + : hour (static_cast(h)), + minute(static_cast(m)), + second(static_cast(s)), + millisecond(static_cast(ms)), + microsecond(static_cast(us)), + nanosecond (static_cast(ns)) + {} + + explicit local_time(const std::tm& t) + : hour (static_cast(t.tm_hour)), + minute(static_cast(t.tm_min)), + second(static_cast(t.tm_sec)), + millisecond(0), microsecond(0), nanosecond(0) + {} + + template + explicit local_time(const std::chrono::duration& t) { - os << std::setfill('0') << std::setw(4) << dt.year << '-' - << std::setfill('0') << std::setw(2) << dt.month << '-' - << std::setfill('0') << std::setw(2) << dt.day; - date = true; + const auto h = std::chrono::duration_cast(t); + this->hour = h.count(); + const auto t2 = t - h; + const auto m = std::chrono::duration_cast(t2); + this->minute = m.count(); + const auto t3 = t2 - m; + const auto s = std::chrono::duration_cast(t3); + this->second = s.count(); + const auto t4 = t3 - s; + const auto ms = std::chrono::duration_cast(t4); + this->millisecond = ms.count(); + const auto t5 = t4 - ms; + const auto us = std::chrono::duration_cast(t5); + this->microsecond = us.count(); + const auto t6 = t5 - us; + const auto ns = std::chrono::duration_cast(t6); + this->nanosecond = ns.count(); } - if(dt.hour != basic_datetime::undef && - dt.minute != basic_datetime::undef && - dt.second != basic_datetime::undef) + + operator std::chrono::nanoseconds() const { - if(date) os << 'T'; - os << std::setfill('0') << std::setw(2) << dt.hour << ':' - << std::setfill('0') << std::setw(2) << dt.minute << ':' - << std::setfill('0') << std::setw(2) << dt.second << '.' - << std::setfill('0') << std::setw(3) << dt.millisecond - << std::setfill('0') << std::setw(3) << dt.microsecond; + return std::chrono::nanoseconds (this->nanosecond) + + std::chrono::microseconds(this->microsecond) + + std::chrono::milliseconds(this->millisecond) + + std::chrono::seconds(this->second) + + std::chrono::minutes(this->minute) + + std::chrono::hours(this->hour); } - if(dt.offset_hour != basic_datetime::nooffset && - dt.offset_minute != basic_datetime::nooffset) + + local_time() = default; + ~local_time() = default; + local_time(local_time const&) = default; + local_time(local_time&&) = default; + local_time& operator=(local_time const&) = default; + local_time& operator=(local_time&&) = default; +}; + +inline bool operator==(const local_time& lhs, const local_time& rhs) +{ + return std::make_tuple(lhs.hour, lhs.minute, lhs.second, lhs.millisecond, lhs.microsecond, lhs.nanosecond) == + std::make_tuple(rhs.hour, rhs.minute, rhs.second, rhs.millisecond, rhs.microsecond, rhs.nanosecond); +} +inline bool operator!=(const local_time& lhs, const local_time& rhs) +{ + return !(lhs == rhs); +} +inline bool operator< (const local_time& lhs, const local_time& rhs) +{ + return std::make_tuple(lhs.hour, lhs.minute, lhs.second, lhs.millisecond, lhs.microsecond, lhs.nanosecond) < + std::make_tuple(rhs.hour, rhs.minute, rhs.second, rhs.millisecond, rhs.microsecond, rhs.nanosecond); +} +inline bool operator<=(const local_time& lhs, const local_time& rhs) +{ + return (lhs < rhs) || (lhs == rhs); +} +inline bool operator> (const local_time& lhs, const local_time& rhs) +{ + return !(lhs <= rhs); +} +inline bool operator>=(const local_time& lhs, const local_time& rhs) +{ + return !(lhs < rhs); +} + +template +std::basic_ostream& +operator<<(std::basic_ostream& os, const local_time& time) +{ + os << std::setfill('0') << std::setw(2) << static_cast(time.hour ) << ':'; + os << std::setfill('0') << std::setw(2) << static_cast(time.minute) << ':'; + os << std::setfill('0') << std::setw(2) << static_cast(time.second); + if(time.millisecond != 0 || time.microsecond != 0 || time.nanosecond != 0) { - if(dt.offset_hour == 0 && dt.offset_minute == 0) - { - os << 'Z'; - } - else + os << '.'; + os << std::setfill('0') << std::setw(3) << static_cast(time.millisecond); + if(time.microsecond != 0 || time.nanosecond != 0) { - char sign = ' '; - iT oh = dt.offset_hour; - iT om = dt.offset_minute; - om += oh * 60; - if(om > 0) sign = '+'; else sign='-'; - oh = om / 60; - om -= oh * 60; - os << sign << std::setfill('0') << std::setw(2) << std::abs(oh) << ':' - << std::setfill('0') << std::setw(2) << std::abs(om); + os << std::setfill('0') << std::setw(3) << static_cast(time.microsecond); + if(time.nanosecond != 0) + { + os << std::setfill('0') << std::setw(3) << static_cast(time.nanosecond); + } } } return os; } -template -inline bool -operator==(basic_datetime const& lhs, basic_datetime const& rhs) +struct time_offset { - return lhs.year == rhs.year && lhs.month == rhs.month && - lhs.day == rhs.day && lhs.minute == rhs.minute && - lhs.second == rhs.second && lhs.millisecond == rhs.millisecond && - lhs.microsecond == rhs.microsecond && - lhs.offset_hour == rhs.offset_hour && - lhs.offset_minute == rhs.offset_minute; -} + std::int8_t hour; // [-12, 12] + std::int8_t minute; // [-59, 59] + + time_offset(int h, int m) + : hour (static_cast(h)), + minute(static_cast(m)) + {} -template -inline bool -operator!=(basic_datetime const& lhs, basic_datetime const& rhs) + operator std::chrono::minutes() const + { + return std::chrono::minutes(this->minute) + + std::chrono::hours(this->hour); + } + + time_offset() = default; + ~time_offset() = default; + time_offset(time_offset const&) = default; + time_offset(time_offset&&) = default; + time_offset& operator=(time_offset const&) = default; + time_offset& operator=(time_offset&&) = default; +}; + +inline bool operator==(const time_offset& lhs, const time_offset& rhs) +{ + return std::make_tuple(lhs.hour, lhs.minute) == + std::make_tuple(rhs.hour, rhs.minute); +} +inline bool operator!=(const time_offset& lhs, const time_offset& rhs) { return !(lhs == rhs); } +inline bool operator< (const time_offset& lhs, const time_offset& rhs) +{ + return std::make_tuple(lhs.hour, lhs.minute) < + std::make_tuple(rhs.hour, rhs.minute); +} +inline bool operator<=(const time_offset& lhs, const time_offset& rhs) +{ + return (lhs < rhs) || (lhs == rhs); +} +inline bool operator> (const time_offset& lhs, const time_offset& rhs) +{ + return !(lhs <= rhs); +} +inline bool operator>=(const time_offset& lhs, const time_offset& rhs) +{ + return !(lhs < rhs); +} -template -inline bool -operator<(basic_datetime const& lhs, basic_datetime const& rhs) +template +std::basic_ostream& +operator<<(std::basic_ostream& os, const time_offset& offset) { - return std::time_t(lhs) < std::time_t(rhs); + if(offset.hour == 0 && offset.minute == 0) + { + os << 'Z'; + return os; + } + if(static_cast(offset.hour) * static_cast(offset.minute) < 0) + { + const int min = static_cast(offset.hour) * 60 + offset.minute; + if(min < 0){os << '-';} else {os << '+';} + os << std::setfill('0') << std::setw(2) << min / 60 << ':'; + os << std::setfill('0') << std::setw(2) << min % 60; + return os; + } + if(offset.hour < 0){os << '-';} else {os << '+';} + os << std::setfill('0') << std::setw(2) << static_cast(offset.hour) << ':'; + os << std::setfill('0') << std::setw(2) << static_cast(offset.minute); + return os; } -template -inline bool -operator<=(basic_datetime const& lhs, basic_datetime const& rhs) +struct local_datetime +{ + local_date date; + local_time time; + + local_datetime(local_date d, local_time t): date(d), time(t) {} + + explicit local_datetime(const std::tm& t): date(t), time(t){} + + explicit local_datetime(const std::chrono::system_clock::time_point& tp) + { + const auto t = std::chrono::system_clock::to_time_t(tp); + const auto tmp = std::localtime(&t); //XXX: not threadsafe! + assert(tmp); // if std::localtime fails, tmp is nullptr + + std::tm time = *tmp; + this->date = local_date(time); + this->time = local_time(time); + + // std::tm lacks subsecond information, so diff between tp and tm + // can be used to get millisecond & microsecond information. + const auto t_diff = tp - + std::chrono::system_clock::from_time_t(std::mktime(&time)); + this->time.millisecond = std::chrono::duration_cast< + std::chrono::milliseconds>(t_diff).count(); + this->time.microsecond = std::chrono::duration_cast< + std::chrono::microseconds>(t_diff).count(); + this->time.nanosecond = std::chrono::duration_cast< + std::chrono::nanoseconds >(t_diff).count(); + } + + explicit local_datetime(const std::time_t t) + : local_datetime(std::chrono::system_clock::from_time_t(t)) + {} + + operator std::chrono::system_clock::time_point() const + { + using internal_duration = + typename std::chrono::system_clock::time_point::duration; + // std::mktime returns date as local time zone. no conversion needed + auto dt = std::chrono::system_clock::time_point(this->date); + dt += std::chrono::duration_cast( + std::chrono::nanoseconds(this->time)); + return dt; + } + + operator std::time_t() const + { + return std::chrono::system_clock::to_time_t( + std::chrono::system_clock::time_point(*this)); + } + + local_datetime() = default; + ~local_datetime() = default; + local_datetime(local_datetime const&) = default; + local_datetime(local_datetime&&) = default; + local_datetime& operator=(local_datetime const&) = default; + local_datetime& operator=(local_datetime&&) = default; +}; + +inline bool operator==(const local_datetime& lhs, const local_datetime& rhs) { - return std::time_t(lhs) <= std::time_t(rhs); + return std::make_tuple(lhs.date, lhs.time) == + std::make_tuple(rhs.date, rhs.time); +} +inline bool operator!=(const local_datetime& lhs, const local_datetime& rhs) +{ + return !(lhs == rhs); +} +inline bool operator< (const local_datetime& lhs, const local_datetime& rhs) +{ + return std::make_tuple(lhs.date, lhs.time) < + std::make_tuple(rhs.date, rhs.time); +} +inline bool operator<=(const local_datetime& lhs, const local_datetime& rhs) +{ + return (lhs < rhs) || (lhs == rhs); +} +inline bool operator> (const local_datetime& lhs, const local_datetime& rhs) +{ + return !(lhs <= rhs); +} +inline bool operator>=(const local_datetime& lhs, const local_datetime& rhs) +{ + return !(lhs < rhs); } -template -inline bool -operator>(basic_datetime const& lhs, basic_datetime const& rhs) +template +std::basic_ostream& +operator<<(std::basic_ostream& os, const local_datetime& dt) { - return std::time_t(lhs) > std::time_t(rhs); + os << dt.date << 'T' << dt.time; + return os; } -template -inline bool -operator>=(basic_datetime const& lhs, basic_datetime const& rhs) +struct offset_datetime +{ + local_date date; + local_time time; + time_offset offset; + + offset_datetime(local_date d, local_time t, time_offset o) + : date(d), time(t), offset(o) + {} + offset_datetime(const local_datetime& dt, time_offset o) + : date(dt.date), time(dt.time), offset(o) + {} + explicit offset_datetime(const local_datetime& ld) + : date(ld.date), time(ld.time), offset(get_local_offset()) + {} + explicit offset_datetime(const std::chrono::system_clock::time_point& tp) + : offset_datetime(local_datetime(tp)) + {} + explicit offset_datetime(const std::time_t& t) + : offset_datetime(local_datetime(t)) + {} + explicit offset_datetime(const std::tm& t) + : offset_datetime(local_datetime(t)) + {} + + operator std::chrono::system_clock::time_point() const + { + // get date-time + using internal_duration = + typename std::chrono::system_clock::time_point::duration; + std::chrono::system_clock::time_point tp = + std::chrono::system_clock::time_point(this->date) + + std::chrono::duration_cast( + std::chrono::nanoseconds(this->time)); + + // get date-time in UTC. let's say we are in +09:00 (JPN). + // writing 12:00:00 in +09:00 means 03:00:00Z. to represent + // 12:00:00Z, first we need to add +09:00. + const auto ofs = get_local_offset(); + tp += std::chrono::hours (ofs.hour); + tp += std::chrono::minutes(ofs.minute); + + // here, tp represents 12:00:00 in UTC but we have offset information. + // we need to subtract it. For example, let's say the input is + // 12:00:00-08:00. now we have tp = 12:00:00Z as a result of the above + // conversion. But the actual time we need to return is 20:00:00Z + // because of -08:00. + tp -= std::chrono::minutes(this->offset); + return tp; + } + + operator std::time_t() const + { + return std::chrono::system_clock::to_time_t( + std::chrono::system_clock::time_point(*this)); + } + + offset_datetime() = default; + ~offset_datetime() = default; + offset_datetime(offset_datetime const&) = default; + offset_datetime(offset_datetime&&) = default; + offset_datetime& operator=(offset_datetime const&) = default; + offset_datetime& operator=(offset_datetime&&) = default; + + private: + + static time_offset get_local_offset() + { + // get current timezone + const auto tmp1 = std::time(nullptr); + const auto tmp2 = std::localtime(&tmp1); // XXX not threadsafe! + assert(tmp2); + std::tm t = *tmp2; + + std::array buf; + const auto result = std::strftime(buf.data(), 6, "%z", &t); // +hhmm\0 + if(result != 5) + { + throw std::runtime_error("toml::offset_datetime: cannot obtain " + "timezone information of current env"); + } + const int ofs = std::atoi(buf.data()); + const int ofs_h = ofs / 100; + const int ofs_m = ofs - (ofs_h * 100); + return time_offset(ofs_h, ofs_m); + } +}; + +inline bool operator==(const offset_datetime& lhs, const offset_datetime& rhs) +{ + return std::make_tuple(lhs.date, lhs.time, lhs.offset) == + std::make_tuple(rhs.date, rhs.time, rhs.offset); +} +inline bool operator!=(const offset_datetime& lhs, const offset_datetime& rhs) +{ + return !(lhs == rhs); +} +inline bool operator< (const offset_datetime& lhs, const offset_datetime& rhs) { - return std::time_t(lhs) >= std::time_t(rhs); + return std::make_tuple(lhs.date, lhs.time, lhs.offset) < + std::make_tuple(rhs.date, rhs.time, rhs.offset); +} +inline bool operator<=(const offset_datetime& lhs, const offset_datetime& rhs) +{ + return (lhs < rhs) || (lhs == rhs); +} +inline bool operator> (const offset_datetime& lhs, const offset_datetime& rhs) +{ + return !(lhs <= rhs); +} +inline bool operator>=(const offset_datetime& lhs, const offset_datetime& rhs) +{ + return !(lhs < rhs); } +template +std::basic_ostream& +operator<<(std::basic_ostream& os, const offset_datetime& dt) +{ + os << dt.date << 'T' << dt.time << dt.offset; + return os; +} }//toml #endif// TOML11_DATETIME diff --git a/toml/exception.hpp b/toml/exception.hpp index 87316b67..f92a7c1c 100644 --- a/toml/exception.hpp +++ b/toml/exception.hpp @@ -1,3 +1,5 @@ +// Copyright Toru Niina 2017. +// Distributed under the MIT License. #ifndef TOML11_EXCEPTION #define TOML11_EXCEPTION #include diff --git a/toml/format.hpp b/toml/format.hpp deleted file mode 100644 index c4726dcc..00000000 --- a/toml/format.hpp +++ /dev/null @@ -1,386 +0,0 @@ -#ifndef TOML11_FORMAT -#define TOML11_FORMAT -#include "value.hpp" -#include -#include -#include -#include -#include - -namespace toml -{ - -// synopsis -// toml::format("key", value, toml::make_inline(80)) -// toml::format("key", value, toml::forceinline) -// std::cout << toml::make_inline(80) << value; -// std::cout << toml::forceinline << value; - -template, - typename alloc = std::allocator> -std::basic_string -format(const value& v); - -template, - typename alloc = std::allocator> -std::basic_string -format(const value& v, std::size_t mk); - -template, - typename alloc = std::allocator> -std::basic_string -format(const toml::key& k, const value& v); - -template, - typename alloc = std::allocator> -std::basic_string -format(const toml::key& k, const value& v, std::size_t mk); - -template -struct format_impl; - -template<> struct format_impl -{ - typedef detail::toml_default_type::type type; - - std::basic_string - operator()(const type& val) - { - return val ? "true" : "false"; - } -}; - -template<> struct format_impl -{ - typedef detail::toml_default_type::type type; - - std::basic_string - operator()(const type& val) - { - return std::to_string(val); - } -}; - -template<> struct format_impl -{ - typedef detail::toml_default_type::type type; - - std::basic_string - operator()(const type& val) - { - std::basic_ostringstream oss; - oss << std::showpoint << val; - if(oss.str().back() == '.') oss << '0'; - return oss.str(); - } -}; - -template<> struct format_impl -{ - typedef detail::toml_default_type::type type; - - std::size_t max_length; - - format_impl() : max_length(80){} - format_impl(std::size_t mx) : max_length(mx){} - - std::basic_string - operator()(const type& val) - { - auto tmp = make_inline(val); - if(max_length == std::numeric_limits::max() || - tmp.size() <= max_length) return tmp; - return convert_multiline(std::move(tmp)); - } - - private: - - std::basic_string - make_inline(const std::basic_string& val) - { - std::basic_string str; - str += '"'; - for(const auto& c : val) - { - if('\0' < c && c < '\31') - { - switch(c) - { - case '\b': str += "\\b"; break; - case '\t': str += "\\t"; break; - case '\n': str += "\\n"; break; - case '\f': str += "\\f"; break; - case '\r': str += "\\r"; break; - default: - { - str += 'u'; - std::basic_ostringstream oss; - oss << std::setw(4) << std::setfill('0') << std::hex - << static_cast(c); - auto hexdig = oss.str(); - std::transform(hexdig.begin(), hexdig.end(), hexdig.begin(), ::toupper); - str += oss.str(); - break; - } - } - } - else if(c == '"') - { - str += "\\\""; - } - else if(c == '\\') - { - str += "\\\\"; - } - else - { - str += c; - } - } - str += '"'; - return str; - } - - std::basic_string - convert_multiline(std::basic_string&& val) - { - std::basic_string str; str.reserve(val.size() + 6); - str += "\"\"\"\n"; - std::size_t current = 0; - for(auto iter = val.begin()+1; iter != val.end()-1; ++iter) - { - if(*iter != '\\') - { - if(current + 1 == max_length) str += "\\\n"; - str += *iter; continue; - } - assert(std::next(iter) < val.end()-1); - if(*std::next(iter) == 'u') - { - if(current + 5 == max_length) str += "\\\n"; - assert(iter + 5 < val.end()-1); - str += *iter; ++iter; // u - str += *iter; ++iter; // 0 - str += *iter; ++iter; // 1 - str += *iter; ++iter; // 2 - str += *iter; continue;// 3 - } - if(current + 2 == max_length) str += "\\\n"; - str += *iter; ++iter; str += *iter; - } - str += "\"\"\""; - return str; - } - -}; - -template<> struct format_impl -{ - typedef detail::toml_default_type::type type; - - std::basic_string - operator()(const type& val) - { - std::basic_ostringstream oss; - oss << val; - return oss.str(); - } -}; - -// TODO max length! -template<> struct format_impl -{ - typedef detail::toml_default_type::type type; - - std::size_t max_length; - - format_impl() : max_length(80){} - format_impl(std::size_t mx) : max_length(mx){} - - std::basic_string - operator()(const type& val) - { - std::basic_string retval; - retval += '['; - for(const auto& item : val) - { - auto tmp = format(val, max_length - 1); - retval += tmp; - retval += ", "; - if(tmp.size() * 2 > max_length) retval += '\n'; - } - retval += ']'; - return retval; - } -}; - -// TODO max length && inline! -template<> struct format_impl -{ - typedef detail::toml_default_type::type type; - - std::size_t max_length; - - format_impl() : max_length(80){} - format_impl(std::size_t mx) : max_length(mx){} - - std::basic_string - operator()(const type& val) - { - std::basic_string retval; - for(const auto& item : val) - { - retval += item.first; - retval += " = "; - retval += format(item.second); - retval += '\n'; - } - return retval; - } -}; - -template -std::basic_string -format(const value& v) -{ - switch(v.type()) - { - case value_t::Boolean : return format_impl{}(v.template cast()); - case value_t::Integer : return format_impl{}(v.template cast()); - case value_t::Float : return format_impl{}(v.template cast()); - case value_t::String : return format_impl{}(v.template cast()); - case value_t::Datetime: return format_impl{}(v.template cast()); - case value_t::Array : return format_impl{}(v.template cast()); - case value_t::Table : return format_impl{}(v.template cast()); - case value_t::Empty : throw std::runtime_error("toml::format: empty value"); - case value_t::Unknown : throw std::runtime_error("toml::format: unknown value"); - default: throw std::logic_error("toml::format: unknown enum value"); - } -} - -template -std::basic_string -format(const value& v, std::size_t inl) -{ - switch(v.type()) - { - case value_t::Boolean : return format_impl{}(v.template cast()); - case value_t::Integer : return format_impl{}(v.template cast()); - case value_t::Float : return format_impl{}(v.template cast()); - case value_t::String : return format_impl{inl}(v.template cast()); - case value_t::Datetime: return format_impl{}(v.template cast()); - case value_t::Array : return format_impl{inl}(v.template cast()); - case value_t::Table : return format_impl{inl}(v.template cast()); - case value_t::Empty : throw std::runtime_error("toml::format: empty value"); - case value_t::Unknown : throw std::runtime_error("toml::format: unknown value"); - default: throw std::logic_error("toml::format: unknown enum value"); - } -} - -template -std::basic_string -format(std::basic_string key, const value& val) -{ - std::basic_string retval(std::move(key)); - retval += " = "; - retval += format(val); - return retval; -} - -template -std::basic_string -format(std::basic_string key, const value& val, std::size_t mk) -{ - std::basic_string retval(std::move(key)); - retval += " = "; - retval += format(val, mk); - return retval; -} - - -// ----------------------------- stream operators ----------------------------- - -namespace detail -{ - -template -struct inline_limit -{ - static_assert(std::is_same::value, "do not instantiate this"); - static const int index; - T limit; - inline_limit() = default; - ~inline_limit() = default; - constexpr inline_limit(T i): limit(i){} - constexpr operator T() const {return limit;} - - static void callback(std::ios_base::event ev, std::ios_base& ios, int idx) - { - void*& info = ios.pword(idx); - switch (ev) - { - case std::ios_base::erase_event: - { - delete static_cast(info); - break; - } - case std::ios_base::copyfmt_event: - { - info = new std::size_t(*static_cast(info)); - break; - } - case std::ios_base::imbue_event: - { - break; - } - } - } -}; - -template -const int inline_limit::index = std::ios_base::xalloc(); - -} //detail - -template> -std::basic_ostream& -operator<<(std::basic_ostream& os, - const detail::inline_limit& inl) -{ - void*& info = os.pword(detail::inline_limit::index); - if(!os.bad()) - { - if(info == nullptr) - { - os.register_callback(detail::inline_limit::callback, - detail::inline_limit::index); - info = new std::size_t(inl.limit); - } - else - { - *static_cast(info) = inl.limit; - } - } - return os; -} - -constexpr static detail::inline_limit forceinline( - std::numeric_limits::max()); - -inline detail::inline_limit make_inline(std::size_t sz) -{ - return detail::inline_limit(sz); -} - -template> -std::basic_ostream& -operator<<(std::basic_ostream& os, - const toml::value& v) -{ - std::size_t* info = - static_cast(os.pword(detail::inline_limit::index)); - return os << (info == nullptr ? toml::format(v) : toml::format(v, *info)); -} - -} -#endif // TOML11_FORMAT diff --git a/toml/from_toml.hpp b/toml/from_toml.hpp index d9d8561d..a9348eb4 100644 --- a/toml/from_toml.hpp +++ b/toml/from_toml.hpp @@ -1,3 +1,5 @@ +// Copyright Toru Niina 2017. +// Distributed under the MIT License. #ifndef TOML11_FROM_TOML #define TOML11_FROM_TOML #include "get.hpp" @@ -8,7 +10,7 @@ namespace toml template void from_toml(T& x, const toml::value& v) { - x = toml::get(v); + x = toml::get::type>(v); return; } diff --git a/toml/get.hpp b/toml/get.hpp index cbd48a84..cddbe731 100644 --- a/toml/get.hpp +++ b/toml/get.hpp @@ -1,96 +1,223 @@ +// Copyright Toru Niina 2017. +// Distributed under the MIT License. #ifndef TOML11_GET #define TOML11_GET +#include "result.hpp" #include "value.hpp" #include namespace toml { +// ============================================================================ +// exact toml::* type + template::value, std::nullptr_t>::type = nullptr> inline T& get(value& v) { - constexpr value_t kind = detail::check_type(); - return v.cast(); + return v.cast::value>(); } template::value, std::nullptr_t>::type = nullptr> inline T const& get(const value& v) { - constexpr value_t kind = detail::check_type(); - return v.cast(); + return v.cast::value>(); +} + +template::value, std::nullptr_t>::type = nullptr> +inline T&& get(value&& v) +{ + return std::move(v.cast::value>()); } +// ============================================================================ +// integer convertible from toml::Integer + template>, - detail::negation>, std::is_integral + std::is_integral, // T is integral + detail::negation>, // but not bool + detail::negation> // but not toml::integer >::value, std::nullptr_t>::type = nullptr> inline T get(const value& v) { return static_cast(v.cast()); } + +// ============================================================================ +// floating point convertible from toml::Float + template>, std::is_floating_point + std::is_floating_point, // T is floating_point + detail::negation> // but not toml::Float >::value, std::nullptr_t>::type = nullptr> inline T get(const value& v) { return static_cast(v.cast()); } -// array-like type +// ============================================================================ +// std::string; toml uses its own toml::string, but it should be convertible to +// std::string seamlessly + +template::value, std::nullptr_t>::type = nullptr> +inline std::string& get(value& v) +{ + return v.cast().str; +} + +template::value, std::nullptr_t>::type = nullptr> +inline std::string const& get(const value& v) +{ + return v.cast().str; +} + +template::value, std::nullptr_t>::type = nullptr> +inline std::string get(value&& v) +{ + return std::move(v.cast().str); +} + +// ============================================================================ +// std::chrono::duration from toml::local_time. + +template::value, std::nullptr_t>::type = nullptr> +inline T get(value& v) +{ + return std::chrono::duration_cast( + std::chrono::nanoseconds(v.cast())); +} + +// ============================================================================ +// std::chrono::system_clock::time_point from toml::datetime variants + +template::value, + std::nullptr_t>::type = nullptr> +inline T get(value& v) +{ + switch(v.type()) + { + case value_t::LocalDate: + { + return std::chrono::system_clock::time_point( + v.cast()); + } + case value_t::LocalDatetime: + { + return std::chrono::system_clock::time_point( + v.cast()); + } + default: + { + return std::chrono::system_clock::time_point( + v.cast()); + } + } +} + +// ============================================================================ +// forward declaration to use this recursively. ignore this and go ahead. + template>, detail::is_container + detail::is_container, // T is container + detail::has_resize_method, // T::resize(N) works + detail::negation> // but not toml::array >::value, std::nullptr_t>::type = nullptr> +T get(const value& v); +template, // T is container + detail::negation>, // no T::resize() exists + detail::negation> // not toml::array + >::value, std::nullptr_t>::type = nullptr> +T get(const value& v); +template::value, std::nullptr_t>::type = nullptr> +T get(const value& v); +template::value, std::nullptr_t>::type = nullptr> +T get(const value& v); +template, // T is map + detail::negation> // but not toml::table + >::value, std::nullptr_t>::type = nullptr> +T get(const toml::value& v); + +// ============================================================================ +// array-like types; most likely STL container, like std::vector, etc. + +template, // T is container + detail::has_resize_method, // T::resize(N) works + detail::negation> // but not toml::array + >::value, std::nullptr_t>::type> T get(const value& v) { + using value_type = typename T::value_type; const auto& ar = v.cast(); - T tmp; - try - { - ::toml::resize(tmp, ar.size()); - } - catch(std::invalid_argument& iv) - { - throw type_error("toml::get: static array: size is not enough"); - } - std::transform(ar.cbegin(), ar.cend(), tmp.begin(), - [](value const& elem){return get(elem);}); - return tmp; + + T container; container.resize(ar.size()); + std::transform(ar.cbegin(), ar.cend(), container.begin(), + [](const value& x){return ::toml::get(x);}); + return container; } -// table-like case +// ============================================================================ +// array-like types; but does not have resize(); most likely std::array. + template>, detail::is_map - >::value, std::nullptr_t>::type = nullptr> -T get(const toml::value& v) + detail::is_container, // T is container + detail::negation>, // no T::resize() exists + detail::negation> // not toml::array + >::value, std::nullptr_t>::type> +T get(const value& v) { - const auto& tb = v.cast(); - T tmp; - for(const auto& kv : tb){tmp.insert(kv);} - return tmp; + using value_type = typename T::value_type; + const auto& ar = v.cast(); + + T container; + if(ar.size() != container.size()) + { + throw std::out_of_range(detail::format_underline(concat_to_string( + "[erorr] toml::get specified container size is ", container.size(), + " but there are ", ar.size(), " elements in toml array."), + detail::get_region(v), "here")); + } + std::transform(ar.cbegin(), ar.cend(), container.begin(), + [](const value& x){return ::toml::get(x);}); + return container; } -// array -> pair -template::value, - std::nullptr_t>::type = nullptr> +// ============================================================================ +// std::pair. + +template::value, std::nullptr_t>::type> T get(const value& v) { using first_type = typename T::first_type; using second_type = typename T::second_type; + const auto& ar = v.cast(); if(ar.size() != 2) { - throw std::out_of_range( - "toml::get: value does not have 2 elements."); + throw std::out_of_range(detail::format_underline(concat_to_string( + "[erorr] toml::get specified std::pair but there are ", ar.size(), + " elements in toml array."), detail::get_region(v), "here")); } - - T tmp; - tmp.first = get(ar.at(0)); - tmp.second = get(ar.at(1)); - return tmp; + return std::make_pair(::toml::get(ar.at(0)), + ::toml::get(ar.at(1))); } +// ============================================================================ +// std::tuple. + namespace detail { @@ -103,33 +230,349 @@ T get_tuple_impl(const toml::Array& a, index_sequence) } // detail -// array -> tuple -template::value, - std::nullptr_t>::type = nullptr> +template::value, std::nullptr_t>::type> T get(const value& v) { const auto& ar = v.cast(); if(ar.size() != std::tuple_size::value) { - throw std::out_of_range( - "toml::get: array value does not have " + - std::to_string(std::tuple_size::value) + - std::string(" elements (array has ") + std::to_string(ar.size()) + - std::string(" elements).")); + throw std::out_of_range(detail::format_underline(concat_to_string( + "[erorr] toml::get specified std::tuple with ", + std::tuple_size::value, "elements, but there are ", ar.size(), + " elements in toml array."), detail::get_region(v), "here")); } return detail::get_tuple_impl(ar, detail::make_index_sequence::value>{}); } -// get_or ----------------------------------------------------------------- +// ============================================================================ +// map-like types; most likely STL map, like std::map or std::unordered_map. + +template, // T is map + detail::negation> // but not toml::table + >::value, std::nullptr_t>::type> +T get(const toml::value& v) +{ + using key_type = typename T::key_type; + using mapped_type = typename T::mapped_type; + static_assert(std::is_convertible::value, + "toml::get only supports map type of which key_type is " + "convertible from std::string."); + T map; + for(const auto& kv : v.cast()) + { + map[key_type(kv.first)] = ::toml::get(kv.second); + } + return map; +} + +// ============================================================================ +// find and get + +template +decltype(::toml::get(std::declval())) +find(const toml::table& tab, const toml::key& ky, + std::string tablename = "unknown table") +{ + if(tab.count(ky) == 0) + { + throw std::out_of_range(concat_to_string("[error] key \"", ky, + "\" not found in ", tablename)); + } + return ::toml::get(tab.at(ky)); +} +template +decltype(::toml::get(std::declval<::toml::value&>())) +find(toml::table& tab, const toml::key& ky, + std::string tablename = "unknown table") +{ + if(tab.count(ky) == 0) + { + throw std::out_of_range(concat_to_string("[error] key \"", ky, + "\" not found in ", tablename)); + } + return ::toml::get(tab[ky]); +} +template +decltype(::toml::get(std::declval<::toml::value&&>())) +find(toml::table&& tab, const toml::key& ky, + std::string tablename = "unknown table") +{ + if(tab.count(ky) == 0) + { + throw std::out_of_range(concat_to_string("[error] key \"", ky, + "\" not found in ", tablename)); + } + return ::toml::get(std::move(tab[ky])); +} + +template +decltype(::toml::get(std::declval())) +find(const toml::value& v, const toml::key& ky) +{ + const auto& tab = ::toml::get(v); + if(tab.count(ky) == 0) + { + throw std::out_of_range(detail::format_underline(concat_to_string( + "[error] key \"", ky, "\" not found"), detail::get_region(v), + "in this table")); + } + return ::toml::get(tab.at(ky)); +} +template +decltype(::toml::get(std::declval<::toml::value&>())) +find(toml::value& v, const toml::key& ky) +{ + auto& tab = ::toml::get(v); + if(tab.count(ky) == 0) + { + throw std::out_of_range(detail::format_underline(concat_to_string( + "[error] key \"", ky, "\" not found"), detail::get_region(v), + "in this table")); + } + return ::toml::get(tab.at(ky)); +} +template +decltype(::toml::get(std::declval<::toml::value&&>())) +find(toml::value&& v, const toml::key& ky) +{ + auto tab = ::toml::get(std::move(v)); + if(tab.count(ky) == 0) + { + throw std::out_of_range(detail::format_underline(concat_to_string( + "[error] key \"", ky, "\" not found"), detail::get_region(v), + "in this table")); + } + return ::toml::get(std::move(tab[ky])); +} + + +// ============================================================================ +// get_or + +template +decltype(::toml::get::type>::type>( + std::declval())) +get_or(const toml::value& v, T&& opt) +{ + try + { + return get::type>::type>(v); + } + catch(...) + { + return std::forward(opt); + } +} +template +decltype(::toml::get::type>::type>( + std::declval())) +get_or(toml::value& v, T&& opt) +{ + try + { + return get::type>::type>(v); + } + catch(...) + { + return std::forward(opt); + } +} +template +decltype(::toml::get::type>::type>( + std::declval())) +get_or(toml::value&& v, T&& opt) +{ + try + { + return get::type>::type>(std::move(v)); + } + catch(...) + { + return std::forward(opt); + } +} + + +template +auto get_or(const toml::table& tab, const toml::key& ky, T&& opt) + -> decltype(get_or(std::declval(), std::forward(opt))) +{ + if(tab.count(ky) == 0) {return std::forward(opt);} + return ::toml::get_or(tab.at(ky), std::forward(opt)); +} +template +auto get_or(toml::table& tab, const toml::key& ky, T&& opt) + -> decltype(get_or(std::declval(), std::forward(opt))) +{ + if(tab.count(ky) == 0) {return std::forward(opt);} + return ::toml::get_or(tab[ky], std::forward(opt)); +} +template +auto get_or(toml::table&& tab, const toml::key& ky, T&& opt) + -> decltype(get_or(std::declval(), std::forward(opt))) +{ + if(tab.count(ky) == 0) {return std::forward(opt);} + return ::toml::get_or(std::move(tab[ky]), std::forward(opt)); +} template -inline typename std::remove_cv::type>::type -get_or(const toml::Table& tab, const toml::key& ky, T&& opt) +auto get_or(const toml::value& v, const toml::key& ky, T&& opt) + -> decltype(get_or(std::declval(), std::forward(opt))) +{ + if(v.type() != toml::value_t::Table){return std::forward(opt);} + const auto& tab = toml::get(v); + if(tab.count(ky) == 0) {return std::forward(opt);} + return ::toml::get_or(tab.at(ky), std::forward(opt)); +} +template +auto get_or(toml::value& v, const toml::key& ky, T&& opt) + -> decltype(get_or(std::declval(), std::forward(opt))) { + if(v.type() != toml::value_t::Table){return std::forward(opt);} + auto& tab = toml::get(v); if(tab.count(ky) == 0) {return std::forward(opt);} - return get::type>::type>(tab.find(ky)->second); + return ::toml::get_or(tab[ky], std::forward(opt)); +} +template +auto get_or(toml::value&& v, const toml::key& ky, T&& opt) + -> decltype(get_or(std::declval(), std::forward(opt))) +{ + if(v.type() != toml::value_t::Table){return std::forward(opt);} + auto tab = toml::get(std::move(v)); + if(tab.count(ky) == 0) {return std::forward(opt);} + return ::toml::get_or(std::move(tab[ky]), std::forward(opt)); +} + +// ============================================================================ +// expect + +template +auto expect(const toml::value& v) + -> result(v)), std::string> +{ + try + { + return ok(get(v)); + } + catch(const type_error& te) + { + return err(te.what()); + } +} +template +auto expect(toml::value& v) + -> result(v)), std::string> +{ + try + { + return ok(get(v)); + } + catch(const type_error& te) + { + return err(te.what()); + } +} +template +auto expect(toml::value&& v) + -> result(std::move(v))), std::string> +{ + try + { + return ok(get(std::move(v))); + } + catch(const type_error& te) + { + return err(te.what()); + } +} + +template +auto expect(const toml::value& v, const toml::key& k) + -> result(v, k)), std::string> +{ + try + { + return ok(get(v, k)); + } + catch(const std::exception& e) + { + return err(e.what()); + } +} +template +auto expect(toml::value& v, const toml::key& k) + -> result(v, k)), std::string> +{ + try + { + return ok(get(v, k)); + } + catch(const std::exception& e) + { + return err(e.what()); + } +} +template +auto expect(toml::value&& v, const toml::key& k) + -> result(std::move(v), k)), std::string> +{ + try + { + return ok(get(std::move(v), k)); + } + catch(const std::exception& e) + { + return err(e.what()); + } +} + +template +auto expect(const toml::table& t, const toml::key& k, std::string tn) + -> result(t, k, std::move(tn))), std::string> +{ + try + { + return ok(get(t, k, std::move(tn))); + } + catch(const std::exception& e) + { + return err(e.what()); + } +} +template +auto expect(toml::table& t, const toml::key& k, std::string tn) + -> result(t, k, std::move(tn))), std::string> +{ + try + { + return ok(get(t, k, std::move(tn))); + } + catch(const std::exception& e) + { + return err(e.what()); + } +} +template +auto expect(toml::table&& t, const toml::key& k, std::string tn) + -> result(std::move(t), k, std::move(tn))), std::string> +{ + try + { + return ok(get(std::move(t), k, std::move(tn))); + } + catch(const std::exception& e) + { + return err(e.what()); + } } } // toml diff --git a/toml/lexer.hpp b/toml/lexer.hpp new file mode 100644 index 00000000..4f170c59 --- /dev/null +++ b/toml/lexer.hpp @@ -0,0 +1,222 @@ +// Copyright Toru Niina 2017. +// Distributed under the MIT License. +#ifndef TOML11_LEXER_HPP +#define TOML11_LEXER_HPP +#include "combinator.hpp" +#include +#include +#include +#include + +namespace toml +{ +namespace detail +{ + +// these scans contents from current location in a container of char +// and extract a region that matches their own pattern. +// to see the implementation of each component, see combinator.hpp. + +using lex_wschar = either, character<'\t'>>; +using lex_ws = repeat>; +using lex_newline = either, + sequence, character<'\n'>>>; +using lex_lower = in_range<'a', 'z'>; +using lex_upper = in_range<'A', 'Z'>; +using lex_alpha = either; +using lex_digit = in_range<'0', '9'>; +using lex_nonzero = in_range<'1', '9'>; +using lex_oct_dig = in_range<'0', '7'>; +using lex_bin_dig = in_range<'0', '1'>; +using lex_hex_dig = either, in_range<'a', 'f'>>; + +using lex_hex_prefix = sequence, character<'x'>>; +using lex_oct_prefix = sequence, character<'o'>>; +using lex_bin_prefix = sequence, character<'b'>>; +using lex_underscore = character<'_'>; +using lex_plus = character<'+'>; +using lex_minus = character<'-'>; +using lex_sign = either; + +// digit | nonzero 1*(digit | _ digit) +using lex_unsigned_dec_int = either>, at_least<1>>>, + lex_digit>; +// (+|-)? unsigned_dec_int +using lex_dec_int = sequence, lex_unsigned_dec_int>; + +// hex_prefix hex_dig *(hex_dig | _ hex_dig) +using lex_hex_int = sequence>, unlimited>>>; +// oct_prefix oct_dig *(oct_dig | _ oct_dig) +using lex_oct_int = sequence>, unlimited>>>; +// bin_prefix bin_dig *(bin_dig | _ bin_dig) +using lex_bin_int = sequence>, unlimited>>>; + +// (dec_int | hex_int | oct_int | bin_int) +using lex_integer = either; + +// =========================================================================== + +using lex_inf = sequence, character<'n'>, character<'f'>>; +using lex_nan = sequence, character<'a'>, character<'n'>>; +using lex_special_float = sequence, either>; +using lex_exponent_part = sequence, character<'E'>>, lex_dec_int>; + +using lex_zero_prefixable_int = sequence>, unlimited>>; +using lex_fractional_part = sequence, lex_zero_prefixable_int>; + +using lex_float = either>>>>; + +// =========================================================================== + +using lex_true = sequence, character<'r'>, + character<'u'>, character<'e'>>; +using lex_false = sequence, character<'a'>, character<'l'>, + character<'s'>, character<'e'>>; +using lex_boolean = either; + +// =========================================================================== + +using lex_date_fullyear = repeat>; +using lex_date_month = repeat>; +using lex_date_mday = repeat>; +using lex_time_delim = either, character<'t'>, character<' '>>; +using lex_time_hour = repeat>; +using lex_time_minute = repeat>; +using lex_time_second = repeat>; +using lex_time_secfrac = sequence, + repeat>>; + +using lex_time_numoffset = sequence, character<'-'>>, + sequence, + lex_time_minute>>; +using lex_time_offset = either, character<'z'>, + lex_time_numoffset>; + +using lex_partial_time = sequence, + lex_time_minute, character<':'>, + lex_time_second, maybe>; +using lex_full_date = sequence, + lex_date_month, character<'-'>, + lex_date_mday>; +using lex_full_time = sequence; + +using lex_offset_date_time = sequence; +using lex_local_date_time = sequence; +using lex_local_date = lex_full_date; +using lex_local_time = lex_partial_time; + +// =========================================================================== + +using lex_quotation_mark = character<'"'>; +using lex_basic_unescaped = exclude, + character<0x22>, character<0x5C>, + character<0x7F>>>; +using lex_escape = character<'\\'>; +using lex_escape_unicode_short = sequence, + repeat>>; +using lex_escape_unicode_long = sequence, + repeat>>; +using lex_escape_seq_char = either, character<'\\'>, + character<'/'>, character<'b'>, + character<'f'>, character<'n'>, + character<'r'>, character<'t'>, + lex_escape_unicode_short, + lex_escape_unicode_long + >; +using lex_escaped = sequence; +using lex_basic_char = either; +using lex_basic_string = sequence, + lex_quotation_mark>; + +using lex_ml_basic_string_delim = repeat>; +using lex_ml_basic_unescaped = exclude, + character<0x5C>, + character<0x7F>, + lex_ml_basic_string_delim>>; + +using lex_ml_basic_escaped_newline = sequence< + lex_escape, maybe, lex_newline, + repeat, unlimited>>; + +using lex_ml_basic_char = either; +using lex_ml_basic_body = repeat, + unlimited>; +using lex_ml_basic_string = sequence; + +using lex_literal_char = exclude, + in_range<0x10, 0x19>, character<0x27>>>; +using lex_apostrophe = character<'\''>; +using lex_literal_string = sequence, + lex_apostrophe>; + +using lex_ml_literal_string_delim = repeat>; + +using lex_ml_literal_char = exclude, + in_range<0x10, 0x1F>, + character<0x7F>, + lex_ml_literal_string_delim>>; +using lex_ml_literal_body = repeat, + unlimited>; +using lex_ml_literal_string = sequence; + +using lex_string = either; + +// =========================================================================== + +using lex_comment_start_symbol = character<'#'>; +using lex_non_eol = either, exclude>>; +using lex_comment = sequence>; + +using lex_dot_sep = sequence, character<'.'>, maybe>; + +using lex_unquoted_key = repeat, character<'_'>>, + at_least<1>>; +using lex_quoted_key = either; +using lex_simple_key = either; +using lex_dotted_key = sequence, + at_least<1> + > + >; +using lex_key = either; + +using lex_keyval_sep = sequence, + character<'='>, + maybe>; + +using lex_std_table_open = character<'['>; +using lex_std_table_close = character<']'>; +using lex_std_table = sequence, + lex_key, + maybe, + lex_std_table_close>; + +using lex_array_table_open = sequence; +using lex_array_table_close = sequence; +using lex_array_table = sequence, + lex_key, + maybe, + lex_array_table_close>; + +} // detail +} // toml +#endif // TOML_LEXER_HPP diff --git a/toml/parser.hpp b/toml/parser.hpp index f4703305..e83de1df 100644 --- a/toml/parser.hpp +++ b/toml/parser.hpp @@ -1,1130 +1,1504 @@ -#ifndef TOML11_PARSER -#define TOML11_PARSER +// Copyright Toru Niina 2017. +// Distributed under the MIT License. +#ifndef TOML11_PARSER_HPP +#define TOML11_PARSER_HPP +#include "result.hpp" +#include "region.hpp" +#include "combinator.hpp" +#include "lexer.hpp" +#include "types.hpp" #include "value.hpp" -#include "acceptor.hpp" -#include -#include #include -#include -#include +#include namespace toml { - namespace detail { -// it is just an inferior vertion of boost/std::optional -template -struct result +template +result>, std::string> +parse_boolean(location& loc) { - result() : ok_(false){} - ~result() = default; - result(const result& rhs) = default; - result(result&& rhs) = default; - result& operator=(const result& rhs) = default; - result& operator=(result&& rhs) = default; - - result(const T& v) : ok_(true), value_(v){} - result(T&& v) : ok_(true), value_(std::move(v)){} - result& operator=(const T& rhs){ok_ = true; value_ = rhs; return *this;} - result& operator=(T&& rhs) {ok_ = true; value_ = rhs; return *this;} - - template - result& operator=(const result& u) {ok_ = u.ok(); if(ok_)value_ = u.move(); return *this;} - template - result& operator=(result&& u) {ok_ = u.ok(); if(ok_)value_ = u.move(); return *this;} - template - result(const result& u): ok_(u.ok()){if(ok_)value_ = u.get();} - template - result(result&& u): ok_(u.ok()){if(ok_)value_ = u.move();} - - bool ok() const {return ok_;} - operator bool() const {return ok_;} - - T& get() {if(!ok_) throw std::logic_error("result::get"); return value_;} - T const& get() const {if(!ok_) throw std::logic_error("result::get"); return value_;} - T&& move() - {if(!ok_) throw std::logic_error("result::move"); ok_ = false; return std::move(value_);} - - private: - bool ok_; - T value_; -}; - -}//detail - -struct parse_escape_sequence + const auto first = loc.iter(); + if(const auto token = lex_boolean::invoke(loc)) + { + const auto reg = token.unwrap(); + if (reg.str() == "true") {return ok(std::make_pair(true, reg));} + else if(reg.str() == "false") {return ok(std::make_pair(false, reg));} + else // internal error. + { + throw toml::internal_error(format_underline( + "[error] toml::parse_boolean: internal error", reg, + "invalid token")); + } + } + loc.iter() = first; //rollback + return err(format_underline("[error] toml::parse_boolean", loc, + "token is not boolean", {"boolean is `true` or `false`"})); +} + +template +result>, std::string> +parse_binary_integer(location& loc) { - typedef toml::character value_type; - typedef toml::String string_type; - typedef detail::result result_type; - - template::value_type, - value_type>::value>::type> - static std::pair invoke(Iterator iter, Iterator end) + const auto first = loc.iter(); + if(const auto token = lex_bin_int::invoke(loc)) { - const auto beg = iter; - if(iter == end || *iter != '\\') - return std::make_pair(result_type{}, iter); - ++iter; - switch(*iter) + auto str = token.unwrap().str(); + assert(str.size() > 2); // minimum -> 0b1 + integer retval(0), base(1); + for(auto i(str.rbegin()), e(str.rend() - 2); i!=e; ++i) { - case '\\': return std::make_pair(string_type("\\"), std::next(iter)); - case '"' : return std::make_pair(string_type("\""), std::next(iter)); - case 'b' : return std::make_pair(string_type("\b"), std::next(iter)); - case 't' : return std::make_pair(string_type("\t"), std::next(iter)); - case 'n' : return std::make_pair(string_type("\n"), std::next(iter)); - case 'f' : return std::make_pair(string_type("\f"), std::next(iter)); - case 'r' : return std::make_pair(string_type("\r"), std::next(iter)); - case 'u' : - { - if(std::distance(iter, end) < 5) - throw std::make_pair(iter, syntax_error( - "invalid escape sequence: " + std::string(beg, end))); - return std::make_pair(utf8_to_char(make_codepoint( - string_type(iter+1, iter+5))), iter+5); - } - case 'U': + if (*i == '1'){retval += base; base *= 2;} + else if(*i == '0'){base *= 2;} + else if(*i == '_'){/* do nothing. */} + else // internal error. { - if(std::distance(iter, end) < 8) - throw std::make_pair(iter, syntax_error( - "invalid escape sequence: " + std::string(beg, end))); - return std::make_pair(utf8_to_char(make_codepoint( - string_type(iter+1, iter+9))), iter+9); + throw toml::internal_error(format_underline( + "[error] toml::parse_integer: internal error", + token.unwrap(), "invalid token")); } - default: throw std::make_pair(iter, syntax_error( - "unkwnon escape sequence: " + std::string(iter, end))); } + return ok(std::make_pair(retval, token.unwrap())); + } + loc.iter() = first; + return err(format_underline("[error] toml::parse_binary_integer", loc, + "token is not binary integer", {"binary integer is like: 0b0011"})); +} + +template +result>, std::string> +parse_octal_integer(location& loc) +{ + const auto first = loc.iter(); + if(const auto token = lex_oct_int::invoke(loc)) + { + auto str = token.unwrap().str(); + str.erase(std::remove(str.begin(), str.end(), '_'), str.end()); + str.erase(str.begin()); str.erase(str.begin()); // remove `0o` prefix + + std::istringstream iss(str); + integer retval(0); + iss >> std::oct >> retval; + return ok(std::make_pair(retval, token.unwrap())); + } + loc.iter() = first; + + return err(format_underline("[error] toml::parse_octal_integer", loc, + "token is not octal integer", {"octal integer is like: 0o775"})); +} + +template +result>, std::string> +parse_hexadecimal_integer(location& loc) +{ + const auto first = loc.iter(); + if(const auto token = lex_hex_int::invoke(loc)) + { + auto str = token.unwrap().str(); + str.erase(std::remove(str.begin(), str.end(), '_'), str.end()); + str.erase(str.begin()); str.erase(str.begin()); // remove `0x` prefix + + std::istringstream iss(str); + integer retval(0); + iss >> std::hex >> retval; + return ok(std::make_pair(retval, token.unwrap())); + } + loc.iter() = first; + return err(format_underline("[error] toml::parse_hexadecimal_integer", loc, + "token is not hex integer", {"hex integer is like: 0xC0FFEE"})); +} + +template +result>, std::string> +parse_integer(location& loc) +{ + const auto first = loc.iter(); + if(first != loc.end() && *first == '0') + { + if(const auto bin = parse_binary_integer (loc)) {return bin;} + if(const auto oct = parse_octal_integer (loc)) {return oct;} + if(const auto hex = parse_hexadecimal_integer(loc)) {return hex;} + // else, maybe just zero. } - static unsigned int make_codepoint(string_type str) + if(const auto token = lex_dec_int::invoke(loc)) { - unsigned int codepoint; - std::basic_istringstream iss(str); - iss >> std::hex >> codepoint; - return codepoint; + auto str = token.unwrap().str(); + str.erase(std::remove(str.begin(), str.end(), '_'), str.end()); + + std::istringstream iss(str); + integer retval(0); + iss >> retval; + return ok(std::make_pair(retval, token.unwrap())); } + loc.iter() = first; + return err(format_underline("[error] toml::parse_integer", loc, + "token is not integer", {"integer is like: +42", + "hex integer is like: 0xC0FFEE", "octal integer is like: 0o775", + "binary integer is like: 0b0011"})); +} - static string_type utf8_to_char(const unsigned int codepoint) +template +result>, std::string> +parse_floating(location& loc) +{ + const auto first = loc.iter(); + if(const auto token = lex_float::invoke(loc)) { - string_type character; - if(codepoint < 0x80) + auto str = token.unwrap().str(); + if(str == "inf" || str == "+inf") + { + if(std::numeric_limits::has_infinity) + { + return ok(std::make_pair( + std::numeric_limits::infinity(), token.unwrap())); + } + else + { + throw std::domain_error("toml::parse_floating: inf value found" + " but the current environment does not support inf. Please" + " make sure that the floating-point implementation conforms" + " IEEE 754/ISO 60559 international standard."); + } + } + else if(str == "-inf") { - character += static_cast(codepoint); + if(std::numeric_limits::has_infinity) + { + return ok(std::make_pair( + -std::numeric_limits::infinity(), token.unwrap())); + } + else + { + throw std::domain_error("toml::parse_floating: inf value found" + " but the current environment does not support inf. Please" + " make sure that the floating-point implementation conforms" + " IEEE 754/ISO 60559 international standard."); + } } - else if(codepoint < 0x800) + else if(str == "nan" || str == "+nan") { - character += static_cast(0xC0| codepoint >> 6); - character += static_cast(0x80|(codepoint & 0x3F)); + if(std::numeric_limits::has_quiet_NaN) + { + return ok(std::make_pair( + std::numeric_limits::quiet_NaN(), token.unwrap())); + } + else if(std::numeric_limits::has_signaling_NaN) + { + return ok(std::make_pair( + std::numeric_limits::signaling_NaN(), token.unwrap())); + } + else + { + throw std::domain_error("toml::parse_floating: NaN value found" + " but the current environment does not support NaN. Please" + " make sure that the floating-point implementation conforms" + " IEEE 754/ISO 60559 international standard."); + } } - else if(codepoint < 0x10000) + else if(str == "-nan") { - character += static_cast(0xE0| codepoint >>12); - character += static_cast(0x80|(codepoint >>6&0x3F)); - character += static_cast(0x80|(codepoint & 0x3F)); + if(std::numeric_limits::has_quiet_NaN) + { + return ok(std::make_pair( + -std::numeric_limits::quiet_NaN(), token.unwrap())); + } + else if(std::numeric_limits::has_signaling_NaN) + { + return ok(std::make_pair( + -std::numeric_limits::signaling_NaN(), token.unwrap())); + } + else + { + throw std::domain_error("toml::parse_floating: NaN value found" + " but the current environment does not support NaN. Please" + " make sure that the floating-point implementation conforms" + " IEEE 754/ISO 60559 international standard."); + } } - else + str.erase(std::remove(str.begin(), str.end(), '_'), str.end()); + std::istringstream iss(str); + floating v(0.0); + iss >> v; + return ok(std::make_pair(v, token.unwrap())); + } + loc.iter() = first; + return err(format_underline("[error] toml::parse_floating: ", loc, + "token is not a float", {"floating point is like: -3.14e+1"})); +} + +template +std::string read_utf8_codepoint(const region& reg) +{ + const auto str = reg.str().substr(1); + std::uint_least32_t codepoint; + std::istringstream iss(str); + iss >> std::hex >> codepoint; + + std::string character; + if(codepoint < 0x80) // U+0000 ... U+0079 ; just an ASCII. + { + character += static_cast(codepoint); + } + else if(codepoint < 0x800) //U+0080 ... U+07FF + { + // 110yyyyx 10xxxxxx; 0x3f == 0b0011'1111 + character += static_cast(0xC0| codepoint >> 6); + character += static_cast(0x80|(codepoint & 0x3F)); + } + else if(codepoint < 0x10000) // U+0800...U+FFFF + { + // 1110yyyy 10yxxxxx 10xxxxxx + character += static_cast(0xE0| codepoint >> 12); + character += static_cast(0x80|(codepoint >> 6 & 0x3F)); + character += static_cast(0x80|(codepoint & 0x3F)); + } + else if(codepoint < 0x200000) // U+10000 ... U+1FFFFF + { + if(0x10FFFF < codepoint) // out of Unicode region { - character += static_cast(0xF0| codepoint >>18); - character += static_cast(0x80|(codepoint >>12&0x3F)); - character += static_cast(0x80|(codepoint >>6 &0x3F)); - character += static_cast(0x80|(codepoint & 0x3F)); + std::cerr << format_underline(concat_to_string("[warning] " + "input codepoint (", str, ") is too large to decode as " + "a unicode character. The result may not be able to render " + "to your screen."), reg, "should be in [0x00..0x10FFFF]") + << std::endl; } - return character; + // 11110yyy 10yyxxxx 10xxxxxx 10xxxxxx + character += static_cast(0xF0| codepoint >> 18); + character += static_cast(0x80|(codepoint >> 12 & 0x3F)); + character += static_cast(0x80|(codepoint >> 6 & 0x3F)); + character += static_cast(0x80|(codepoint & 0x3F)); + } + else // out of UTF-8 region + { + throw std::range_error(format_underline(concat_to_string("[error] " + "input codepoint (", str, ") is too large to encode as utf-8."), + reg, "should be in [0x00..0x10FFFF]")); } -}; + return character; +} -struct parse_basic_inline_string +template +result parse_escape_sequence(location& loc) { - typedef toml::character value_type; - typedef detail::result result_type; - - template::value_type, - value_type>::value>::type> - static std::pair - invoke(Iterator iter, Iterator range_end) + const auto first = loc.iter(); + if(first == loc.end() || *first != '\\') + { + return err(format_underline("[error]: " + "toml::parse_escape_sequence: location does not points \"\\\"", + loc, "should be \"\\\"")); + } + ++loc.iter(); + switch(*loc.iter()) { - const Iterator end = - is_basic_inline_string::invoke(iter, range_end); - if(iter == end) return std::make_pair(result_type{}, iter); - if(std::distance(iter, end) < 2) - throw internal_error("is_basic_inline_string"); - - toml::String result; result.reserve(std::distance(iter, end)-2); - ++iter; - const Iterator last = std::prev(end); // ignore '"' - while(iter != last) + case '\\':{++loc.iter(); return ok(std::string("\\"));} + case '"' :{++loc.iter(); return ok(std::string("\""));} + case 'b' :{++loc.iter(); return ok(std::string("\b"));} + case 't' :{++loc.iter(); return ok(std::string("\t"));} + case 'n' :{++loc.iter(); return ok(std::string("\n"));} + case 'f' :{++loc.iter(); return ok(std::string("\f"));} + case 'r' :{++loc.iter(); return ok(std::string("\r"));} + case 'u' : { - if(*iter == '\\') + if(const auto token = lex_escape_unicode_short::invoke(loc)) { - auto r = parse_escape_sequence::invoke(iter, last); - if(!r.first.ok()) - throw internal_error("parse_basic_inline_string"); - result += r.first.move(); - iter = r.second; + return ok(read_utf8_codepoint(token.unwrap())); } else { - result += *iter; - ++iter; + return err(format_underline("[error] parse_escape_sequence: " + "invalid token found in UTF-8 codepoint uXXXX.", + loc, token.unwrap_err())); } } - return std::make_pair(result, end); - } -}; - -struct parse_basic_multiline_string -{ - typedef toml::character value_type; - typedef toml::String string_type; - typedef detail::result result_type; - - typedef is_chain_of, is_newline> - is_line_ending_backslash; - typedef is_repeat_of, is_newline>, - repeat_infinite()> ws_nl_after_backslash_remover; - - template::value_type, - value_type>::value>::type> - static std::pair - invoke(Iterator iter, Iterator range_end) - { - const Iterator end = - is_basic_multiline_string::invoke(iter, range_end); - if(iter == end) return std::make_pair(result_type{}, iter); - if(std::distance(iter, end) < 6) - throw internal_error("is_basic_inline_string"); - - toml::String result; result.reserve(std::distance(iter, end)-6); - std::advance(iter, 3); - const Iterator last = end - 3; - iter = is_newline::invoke(iter, last); - while(iter != last) + case 'U': { - if(*iter == '\\') + if(const auto token = lex_escape_unicode_long::invoke(loc)) { - if(is_line_ending_backslash::invoke(iter, last) != iter) - { - iter = ws_nl_after_backslash_remover::invoke(std::next(iter), last); - } - else - { - auto r = parse_escape_sequence::invoke(iter, last); - if(!r.first.ok()) - throw internal_error("parse_basic_inline_string"); - result += r.first.move(); - iter = r.second; - } + return ok(read_utf8_codepoint(token.unwrap())); } else { - result.push_back(*iter); - ++iter; + return err(format_underline("[error] parse_escape_sequence: " + "invalid token found in UTF-8 codepoint Uxxxxxxxx", + loc, token.unwrap_err())); } } - return std::make_pair(result, end); } -}; -struct parse_literal_inline_string + const auto msg = format_underline("[error] parse_escape_sequence: " + "unknown escape sequence appeared.", loc, "escape sequence is one of" + " \\, \", b, t, n, f, r, uxxxx, Uxxxxxxxx", {"if you want to write " + "backslash as just one backslash, use literal string like:", + "regex = '<\\i\\c*\\s*>'"}); + loc.iter() = first; + return err(msg); +} + +template +result>, std::string> +parse_ml_basic_string(location& loc) { - typedef toml::character value_type; - typedef detail::result result_type; - - template::value_type, - value_type>::value>::type> - static std::pair - invoke(Iterator iter, Iterator range_end) + const auto first = loc.iter(); + if(const auto token = lex_ml_basic_string::invoke(loc)) { - const Iterator end = - is_literal_inline_string::invoke(iter, range_end); - if(iter == end) return std::make_pair(result_type{}, iter); - if(std::distance(iter, end) < 2) - throw internal_error("is_literal_inline_string"); - - toml::String result; result.reserve(std::distance(iter, end)-2); - ++iter; - const Iterator last = end - 1; - while(iter != last) + auto inner_loc = loc; + inner_loc.iter() = first; + + std::string retval; + retval.reserve(token.unwrap().size()); + + auto delim = lex_ml_basic_string_delim::invoke(inner_loc); + if(!delim) { - result.push_back(*iter); - ++iter; + throw internal_error(format_underline("[error] " + "parse_ml_basic_string: invalid token", + inner_loc, "should be \"\"\"")); } - return std::make_pair(result, end); + // immediate newline is ignored (if exists) + /* discard return value */ lex_newline::invoke(inner_loc); + + delim = err("tmp"); + while(!delim) + { + using lex_unescaped_seq = repeat< + either, unlimited>; + if(auto unescaped = lex_unescaped_seq::invoke(inner_loc)) + { + retval += unescaped.unwrap().str(); + } + if(auto escaped = parse_escape_sequence(inner_loc)) + { + retval += escaped.unwrap(); + } + if(auto esc_nl = lex_ml_basic_escaped_newline::invoke(inner_loc)) + { + // ignore newline after escape until next non-ws char + } + if(inner_loc.iter() == inner_loc.end()) + { + throw internal_error(format_underline("[error] " + "parse_ml_basic_string: unexpected end of region", + inner_loc, "not sufficient token")); + } + delim = lex_ml_basic_string_delim::invoke(inner_loc); + } + return ok(std::make_pair(toml::string(retval), token.unwrap())); } -}; + else + { + loc.iter() = first; + return err(token.unwrap_err()); + } +} -struct parse_literal_multiline_string +template +result>, std::string> +parse_basic_string(location& loc) { - typedef toml::character value_type; - typedef detail::result result_type; - - typedef is_chain_of, is_newline> - is_line_ending_backslash; - typedef is_repeat_of, is_newline>, - repeat_infinite()> ws_nl_after_backslash_remover; - - template::value_type, - value_type>::value>::type> - static std::pair - invoke(Iterator iter, Iterator range_end) + const auto first = loc.iter(); + if(const auto token = lex_basic_string::invoke(loc)) { - const Iterator end = - is_literal_multiline_string::invoke(iter, range_end); - if(iter == end) return std::make_pair(result_type{}, iter); - if(std::distance(iter, end) < 6) - throw internal_error("is_literal_multiline_string"); - - toml::String result; result.reserve(std::distance(iter, end)-6); - std::advance(iter, 3); - const Iterator last = end - 3; - iter = is_newline::invoke(iter, last); // trim first newline if exist - while(iter != last) + auto inner_loc = loc; + inner_loc.iter() = first; + + auto quot = lex_quotation_mark::invoke(inner_loc); + if(!quot) { - result.push_back(*iter); - ++iter; + throw internal_error(format_underline("[error] parse_basic_string: " + "invalid token", inner_loc, "should be \"")); } - return std::make_pair(result, end); - } -}; -struct parse_string -{ - typedef toml::character value_type; - typedef detail::result result_type; - - template::value_type, - value_type>::value>::type> - static std::pair - invoke(Iterator iter, Iterator range_end) + std::string retval; + retval.reserve(token.unwrap().size()); + + quot = err("tmp"); + while(!quot) + { + using lex_unescaped_seq = repeat; + if(auto unescaped = lex_unescaped_seq::invoke(inner_loc)) + { + retval += unescaped.unwrap().str(); + } + if(auto escaped = parse_escape_sequence(inner_loc)) + { + retval += escaped.unwrap(); + } + if(inner_loc.iter() == inner_loc.end()) + { + throw internal_error(format_underline("[error] " + "parse_ml_basic_string: unexpected end of region", + inner_loc, "not sufficient token")); + } + quot = lex_quotation_mark::invoke(inner_loc); + } + return ok(std::make_pair(toml::string(retval), token.unwrap())); + } + else { - std::pair result; - if((result = parse_basic_inline_string::invoke(iter, range_end)).first.ok()) - return result; - else if((result = parse_basic_multiline_string::invoke(iter, range_end)).first.ok()) - return result; - else if((result = parse_literal_inline_string::invoke(iter, range_end)).first.ok()) - return result; - else if((result = parse_literal_multiline_string::invoke(iter, range_end)).first.ok()) - return result; - else - return std::make_pair(result_type{}, iter); + loc.iter() = first; // rollback + return err(token.unwrap_err()); } -}; +} -struct parse_integer +template +result>, std::string> +parse_ml_literal_string(location& loc) { - typedef toml::character value_type; - typedef std::basic_string string_type; - typedef detail::result result_type; - - template::value_type, - value_type>::value>::type> - static std::pair invoke(Iterator iter, Iterator range_end) + const auto first = loc.iter(); + if(const auto token = lex_ml_literal_string::invoke(loc)) { - const Iterator end = is_integer::invoke(iter, range_end); - if(iter == end) return std::make_pair(result_type{}, iter); + location inner_loc(loc.name(), token.unwrap().str()); - string_type result; result.resize(std::distance(iter, end)); - std::copy_if(iter, end, result.begin(), [](value_type c){return c != '_';}); - return std::make_pair(std::stoll(result), end); + const auto open = lex_ml_literal_string_delim::invoke(inner_loc); + if(!open) + { + throw internal_error(format_underline("[error] " + "parse_ml_literal_string: invalid token", + inner_loc, "should be '''")); + } + // immediate newline is ignored (if exists) + /* discard return value */ lex_newline::invoke(inner_loc); + + const auto body = lex_ml_literal_body::invoke(inner_loc); + + const auto close = lex_ml_literal_string_delim::invoke(inner_loc); + if(!close) + { + throw internal_error(format_underline("[error] " + "parse_ml_literal_string: invalid token", + inner_loc, "should be '''")); + } + return ok(std::make_pair( + toml::string(body.unwrap().str(), toml::string_t::literal), + token.unwrap())); + } + else + { + loc.iter() = first; // rollback + return err(token.unwrap_err()); } -}; +} -struct parse_float +template +result>, std::string> +parse_literal_string(location& loc) { - typedef toml::character value_type; - typedef std::basic_string string_type; - typedef detail::result result_type; - - template::value_type, - value_type>::value>::type> - static std::pair - invoke(Iterator iter, Iterator range_end) + const auto first = loc.iter(); + if(const auto token = lex_literal_string::invoke(loc)) { - const Iterator end = is_float::invoke(iter, range_end); - if(iter == end) return std::make_pair(result_type{}, iter); + location inner_loc(loc.name(), token.unwrap().str()); - string_type result; result.resize(std::distance(iter, end)); - std::copy_if(iter, end, result.begin(), [](value_type c){return c != '_';}); - try{ - return std::make_pair(std::stod(result), end); + const auto open = lex_apostrophe::invoke(inner_loc); + if(!open) + { + throw internal_error(format_underline("[error] " + "parse_literal_string: invalid token", + inner_loc, "should be '")); } - catch(std::out_of_range& oor) + + const auto body = repeat::invoke(inner_loc); + + const auto close = lex_apostrophe::invoke(inner_loc); + if(!close) { - std::cout << "extremely large Float value appeared: " - << result << "; it is negrected" << std::endl; - return std::make_pair(0, end); + throw internal_error(format_underline("[error] " + "parse_literal_string: invalid token", + inner_loc, "should be '")); } + return ok(std::make_pair( + toml::string(body.unwrap().str(), toml::string_t::literal), + token.unwrap())); } -}; - -struct parse_boolean -{ - typedef toml::character value_type; - typedef detail::result result_type; - - template::value_type, - value_type>::value>::type> - static std::pair - invoke(Iterator iter, Iterator range_end) + else { - const Iterator end = is_boolean::invoke(iter, range_end); - if(iter == end) return std::make_pair(result_type{}, iter); - return std::make_pair((std::distance(iter, end) == 4), end); + loc.iter() = first; // rollback + return err(token.unwrap_err()); } -}; +} + +template +result>, std::string> +parse_string(location& loc) +{ + if(const auto rslt = parse_ml_basic_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_literal_string(loc)) {return rslt;} + return err(format_underline("[error] toml::parse_string: not a string", + loc, "not a string")); +} -struct parse_local_time +template +result>, std::string> +parse_local_date(location& loc) { - typedef toml::character value_type; - typedef std::basic_string string_type; - typedef detail::result result_type; - typedef typename toml::Datetime::number_type number_type; - template - using nums = is_repeat_of, N>; - typedef is_character delim; - typedef is_character fract; - - template::value_type, - value_type>::value>::type> - static std::pair - invoke(Iterator iter, Iterator range_end) + const auto first = loc.iter(); + if(const auto token = lex_local_date::invoke(loc)) { - const Iterator end = is_local_time::invoke(iter, range_end); - if(iter == end) return std::make_pair(result_type{}, iter); - - toml::Datetime result; - result.hour = std::stoi(string_type(iter, nums<2>::invoke(iter, end))); - iter = delim::invoke(nums<2>::invoke(iter, end), end); - result.minute = std::stoi(string_type(iter, nums<2>::invoke(iter, end))); - iter = delim::invoke(nums<2>::invoke(iter, end), end); - result.second = std::stoi(string_type(iter, nums<2>::invoke(iter, end))); - iter = fract::invoke(nums<2>::invoke(iter, end), end); - if(iter == end) + location inner_loc(loc.name(), token.unwrap().str()); + + const auto y = lex_date_fullyear::invoke(inner_loc); + if(!y || inner_loc.iter() == inner_loc.end() || *inner_loc.iter() != '-') { - result.millisecond = 0.0; - result.microsecond = 0.0; + throw internal_error(format_underline("[error]: " + "toml::parse_inner_local_date: invalid year format", + inner_loc, y.map_err_or_else([](const std::string& msg) { + return msg; + }, "should be `-`"))); } - else if(std::distance(iter, end) <= 3) + ++inner_loc.iter(); + const auto m = lex_date_month::invoke(inner_loc); + if(!m || inner_loc.iter() == inner_loc.end() || *inner_loc.iter() != '-') { - result.millisecond = parse_number(iter, end); - result.microsecond = 0.0; + throw internal_error(format_underline("[error]: " + "toml::parse_local_date: invalid month format", + inner_loc, m.map_err_or_else([](const std::string& msg) { + return msg; + }, "should be `-`"))); } - else + ++inner_loc.iter(); + const auto d = lex_date_mday::invoke(inner_loc); + if(!d) { - result.millisecond = parse_number(iter, iter + 3); - result.microsecond = parse_number(iter + 3, end); + throw internal_error(format_underline("[error]: " + "toml::parse_local_date: invalid day format", + inner_loc, d.unwrap_err())); } - result.offset_hour = toml::Datetime::nooffset; - result.offset_minute = toml::Datetime::nooffset; - result.year = toml::Datetime::undef; - result.month = toml::Datetime::undef; - result.day = toml::Datetime::undef; - return std::make_pair(result, end); + return ok(std::make_pair(local_date( + static_cast(from_string(y.unwrap().str(), 0)), + static_cast( + static_cast(from_string(m.unwrap().str(), 0)-1)), + static_cast(from_string(d.unwrap().str(), 0))), + token.unwrap())); } - - template::value_type, - value_type>::value>::type> - static number_type parse_number(Iterator iter, Iterator end) + else { - if(std::distance(iter, end) > 3) end = iter + 3; - string_type str(iter, end); - while(str.size() < 3){str += '0';} - return std::stoi(str); + 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; + return err(std::move(msg)); } -}; +} -struct parse_local_date +template +result>, std::string> +parse_local_time(location& loc) { - typedef toml::character value_type; - typedef std::basic_string string_type; - typedef detail::result result_type; - template - using nums = is_repeat_of, N>; - typedef is_character delim; - - template::value_type, - value_type>::value>::type> - static std::pair - invoke(Iterator iter, Iterator range_end) + const auto first = loc.iter(); + if(const auto token = lex_local_time::invoke(loc)) { - const Iterator end = is_local_date::invoke(iter, range_end); - if(iter == end) return std::make_pair(result_type{}, iter); - - toml::Datetime result; - result.year = std::stoi(string_type(iter, nums<4>::invoke(iter, end))); - iter = delim::invoke(nums<4>::invoke(iter, end), end); - result.month = std::stoi(string_type(iter, nums<2>::invoke(iter, end))); - iter = delim::invoke(nums<2>::invoke(iter, end), end); - result.day = std::stoi(string_type(iter, nums<2>::invoke(iter, end))); - - result.offset_hour = toml::Datetime::nooffset; - result.offset_minute = toml::Datetime::nooffset; - result.hour = toml::Datetime::undef; - result.minute = toml::Datetime::undef; - result.second = toml::Datetime::undef; - result.millisecond = toml::Datetime::undef; - result.microsecond = toml::Datetime::undef; - return std::make_pair(result, end); - } -}; + location inner_loc(loc.name(), token.unwrap().str()); -struct parse_local_date_time -{ - typedef toml::character value_type; - typedef std::basic_string string_type; - typedef detail::result result_type; - template - using nums = is_repeat_of, N>; - typedef is_character delim; - - template::value_type, - value_type>::value>::type> - static std::pair - invoke(Iterator iter, Iterator range_end) - { - const Iterator end = - is_local_date_time::invoke(iter, range_end); - if(iter == end) return std::make_pair(result_type{}, iter); - - auto ld = parse_local_date::invoke(iter, end); - if(!ld.first.ok()) - throw std::make_pair(iter, syntax_error("invalid local datetime")); - toml::Datetime result(ld.first.move()); - iter = delim::invoke(ld.second, end);// 'T' - - const auto time = parse_local_time::invoke(iter, end); - result.hour = time.first.get().hour; - result.minute = time.first.get().minute; - result.second = time.first.get().second; - result.millisecond = time.first.get().millisecond; - result.microsecond = time.first.get().microsecond; - result.offset_hour = toml::Datetime::nooffset; - result.offset_minute = toml::Datetime::nooffset; - return std::make_pair(result, end); - } -}; + const auto h = lex_time_hour::invoke(inner_loc); + if(!h || inner_loc.iter() == inner_loc.end() || *inner_loc.iter() != ':') + { + throw internal_error(format_underline("[error]: " + "toml::parse_local_time: invalid year format", + inner_loc, h.map_err_or_else([](const std::string& msg) { + return msg; + }, "should be `:`"))); + } + ++inner_loc.iter(); + const auto m = lex_time_minute::invoke(inner_loc); + if(!m || inner_loc.iter() == inner_loc.end() || *inner_loc.iter() != ':') + { + throw internal_error(format_underline("[error]: " + "toml::parse_local_time: invalid month format", + inner_loc, m.map_err_or_else([](const std::string& msg) { + return msg; + }, "should be `:`"))); + } + ++inner_loc.iter(); + const auto s = lex_time_second::invoke(inner_loc); + if(!s) + { + throw internal_error(format_underline("[error]: " + "toml::parse_local_time: invalid second format", + inner_loc, s.unwrap_err())); + } + local_time time( + static_cast(from_string(h.unwrap().str(), 0)), + static_cast(from_string(m.unwrap().str(), 0)), + static_cast(from_string(s.unwrap().str(), 0)), 0, 0); -struct parse_offset_date_time -{ - typedef toml::character value_type; - typedef std::basic_string string_type; - typedef detail::result result_type; - template - using nums = is_repeat_of, N>; - typedef is_character delim; - - template::value_type, - value_type>::value>::type> - static std::pair - invoke(Iterator iter, Iterator range_end) - { - const Iterator end = - is_offset_date_time::invoke(iter, range_end); - if(iter == end) return std::make_pair(result_type{}, iter); - - auto ldt = parse_local_date_time::invoke(iter, end); - if(!ldt.first.ok()) - throw std::make_pair(iter, syntax_error("invalid offset datetime")); - toml::Datetime result(ldt.first.move()); - iter = ldt.second; - if(*iter == 'Z') + const auto before_secfrac = inner_loc.iter(); + if(const auto secfrac = lex_time_secfrac::invoke(inner_loc)) { - result.offset_hour = 0; - result.offset_minute = 0; + auto sf = secfrac.unwrap().str(); + sf.erase(sf.begin()); // sf.front() == '.' + switch(sf.size() % 3) + { + case 2: sf += '0'; break; + case 1: sf += "00"; break; + case 0: break; + default: break; + } + if(sf.size() >= 6) + { + time.millisecond = from_string(sf.substr(0, 3), 0); + time.microsecond = from_string(sf.substr(3, 3), 0); + } + else if(sf.size() >= 3) + { + time.millisecond = from_string(sf, 0); + time.microsecond = 0; + } } else { - if(*iter != '+' && *iter != '-') - throw std::make_pair(iter, syntax_error("invalid offset-datetime")); - const int sign = (*iter == '-') ? -1 : 1; - ++iter; - result.offset_hour = sign * - std::stoi(string_type(iter, nums<2>::invoke(iter, end))); - iter = delim::invoke(nums<2>::invoke(iter, end), end); - result.offset_minute = sign * - std::stoi(string_type(iter, nums<2>::invoke(iter, end))); + if(before_secfrac != inner_loc.iter()) + { + throw internal_error(format_underline("[error]: " + "toml::parse_local_time: invalid subsecond format", + inner_loc, secfrac.unwrap_err())); + } } - return std::make_pair(result, end); + return ok(std::make_pair(time, token.unwrap())); } -}; - -struct parse_datetime -{ - typedef toml::character value_type; - typedef detail::result result_type; - - template::value_type, - value_type>::value>::type> - static std::pair - invoke(Iterator iter, Iterator range_end) + else { - std::pair result; - if((result = parse_offset_date_time::invoke(iter, range_end)).first.ok()) - return result; - else if((result = parse_local_date_time::invoke(iter, range_end)).first.ok()) - return result; - else if((result = parse_local_date::invoke(iter, range_end)).first.ok()) - return result; - else if((result = parse_local_time::invoke(iter, range_end)).first.ok()) - return result; - else - return std::make_pair(result_type{}, iter); + 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; + return err(std::move(msg)); } -}; +} -template -struct parse_fixed_type_array +template +result>, std::string> +parse_local_datetime(location& loc) { - typedef toml::character value_type; - typedef detail::result result_type; - typedef acceptorT acceptor_type; - typedef parserT parser_type; - typedef is_skippable_in_array skippable; - - template::value_type, - value_type>::value>::type> - static std::pair invoke(Iterator iter, Iterator range_end) + const auto first = loc.iter(); + if(const auto token = lex_local_date_time::invoke(loc)) { - const Iterator end = - is_fixed_type_array::invoke(iter, range_end); - if(iter == end) return std::make_pair(result_type{}, iter); - - toml::Array result; - const Iterator last = std::prev(end); - iter = skippable::invoke(std::next(iter), last); - while(iter != last) + location inner_loc(loc.name(), token.unwrap().str()); + const auto date = parse_local_date(inner_loc); + if(!date || inner_loc.iter() == inner_loc.end()) { - const Iterator tmp = acceptor_type::invoke(iter, last); - if(tmp == iter) - throw std::make_pair(iter, syntax_error("parse_array")); - auto next = parser_type::invoke(iter, last); - if(!next.first.ok()) - throw std::make_pair(iter, syntax_error("parse_array")); - result.emplace_back(next.first.move()); - iter = tmp; - iter = skippable::invoke(iter, last); - iter = is_character::invoke(iter, last); - iter = skippable::invoke(iter, last); + throw internal_error(format_underline("[error]: " + "toml::parse_local_datetime: invalid datetime format", + inner_loc, date.map_err_or_else([](const std::string& msg){ + return msg; + }, "date, not datetime"))); } - return std::make_pair(result, end); + const char delim = *(inner_loc.iter()++); + if(delim != 'T' && delim != 't' && delim != ' ') + { + throw internal_error(format_underline("[error]: " + "toml::parse_local_datetime: invalid datetime format", + inner_loc, "should be `T` or ` ` (space)")); + } + const auto time = parse_local_time(inner_loc); + if(!time) + { + throw internal_error(format_underline("[error]: " + "toml::parse_local_datetime: invalid datetime format", + inner_loc, "invalid time fomrat")); + } + return ok(std::make_pair( + local_datetime(date.unwrap().first, time.unwrap().first), + token.unwrap())); } -}; - -template -struct parse_inline_table; - -template -struct parse_array -{ - typedef charT value_type; - static_assert(std::is_same::value, ""); - typedef detail::result result_type; - typedef is_skippable_in_array skippable; - - template::value_type, - value_type>::value>::type> - static std::pair - invoke(Iterator iter, Iterator range_end) + else { - if(iter == is_array::invoke(iter, range_end)) - return std::make_pair(result_type{}, iter); - - std::pair result; - if((result = parse_fixed_type_array, - parse_boolean>::invoke(iter, range_end)).first.ok()) return result; - else if((result = parse_fixed_type_array, - parse_string>::invoke(iter, range_end)).first.ok()) return result; - else if((result = parse_fixed_type_array, - parse_datetime>::invoke(iter, range_end)).first.ok()) return result; - else if((result = parse_fixed_type_array, - parse_float>::invoke(iter, range_end)).first.ok()) return result; - else if((result = parse_fixed_type_array, - parse_integer>::invoke(iter, range_end)).first.ok()) return result; - else if((result = parse_fixed_type_array, - parse_array>::invoke(iter, range_end)).first.ok()) return result; - else if((result = parse_fixed_type_array, - parse_inline_table>::invoke(iter, range_end)).first.ok()) - return result; - else if(skippable::invoke(std::next(iter), range_end) == // empty - std::prev(is_array::invoke(iter, range_end)) - ) return std::make_pair( - toml::Array{}, is_array::invoke(iter, range_end)); - else throw std::make_pair(iter, syntax_error("no valid array here")); + 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; + return err(std::move(msg)); } -}; +} -template -struct parse_value +template +result>, std::string> +parse_offset_datetime(location& loc) { - typedef charT value_type; - static_assert(std::is_same::value, ""); - typedef detail::result result_type; - - template::value_type, - value_type>::value>::type> - static std::pair - invoke(Iterator iter, Iterator range_end) + const auto first = loc.iter(); + if(const auto token = lex_offset_date_time::invoke(loc)) { - std::pair result; - if((result = parse_boolean::invoke(iter, range_end)).first.ok()) - return result; - else if((result = parse_string::invoke(iter, range_end)).first.ok()) - return result; - else if((result = parse_datetime::invoke(iter, range_end)).first.ok()) - return result; - else if((result = parse_float::invoke(iter, range_end)).first.ok()) - return result; - else if((result = parse_integer::invoke(iter, range_end)).first.ok()) - return result; - else if((result = parse_array::invoke(iter, range_end)).first.ok()) - return result; - else if((result = parse_inline_table::invoke(iter, range_end)).first.ok()) - return result; - else - return std::make_pair(result_type{}, iter); + location inner_loc(loc.name(), token.unwrap().str()); + const auto datetime = parse_local_datetime(inner_loc); + if(!datetime || inner_loc.iter() == inner_loc.end()) + { + throw internal_error(format_underline("[error]: " + "toml::parse_offset_datetime: invalid datetime format", + inner_loc, datetime.map_err_or_else([](const std::string& msg){ + return msg; + }, "date, not datetime"))); + } + time_offset offset(0, 0); + if(const auto ofs = lex_time_numoffset::invoke(inner_loc)) + { + const auto str = ofs.unwrap().str(); + if(str.front() == '+') + { + offset.hour = static_cast(from_string(str.substr(1,2), 0)); + offset.minute = static_cast(from_string(str.substr(4,2), 0)); + } + else + { + offset.hour = -static_cast(from_string(str.substr(1,2), 0)); + offset.minute = -static_cast(from_string(str.substr(4,2), 0)); + } + } + else if(*inner_loc.iter() != 'Z' && *inner_loc.iter() != 'z') + { + throw internal_error(format_underline("[error]: " + "toml::parse_offset_datetime: invalid datetime format", + inner_loc, "should be `Z` or `+HH:MM`")); + } + return ok(std::make_pair(offset_datetime(datetime.unwrap().first, offset), + token.unwrap())); } -}; - -struct parse_barekey -{ - typedef toml::character value_type; - typedef detail::result result_type; - - template::value_type, - value_type>::value>::type> - static std::pair - invoke(Iterator iter, Iterator range_end) + else { - const Iterator end = is_barekey::invoke(iter, range_end); - if(iter == end) return std::make_pair(result_type{}, iter); - return std::make_pair(toml::key(iter, end), end); + 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; + return err(std::move(msg)); } -}; +} -struct parse_key +template +result parse_simple_key(location& loc) { - typedef toml::character value_type; - typedef detail::result result_type; - - template::value_type, - value_type>::value>::type> - static std::pair - invoke(Iterator iter, Iterator range_end) + if(const auto bstr = parse_basic_string(loc)) { - std::pair result; - if((result = parse_barekey::invoke(iter, range_end)).first.ok()) - return result; - else if((result = parse_string::invoke(iter, range_end)).first.ok()) - return result; - else return std::make_pair(result_type{}, iter); + return ok(bstr.unwrap().first.str); } -}; - -template -struct parse_key_value_pair -{ - typedef charT value_type; - static_assert(std::is_same::value, ""); - typedef detail::result> result_type; - - template::value_type, - value_type>::value>::type> - static std::pair - invoke(Iterator iter, Iterator range_end) + if(const auto lstr = parse_literal_string(loc)) + { + return ok(lstr.unwrap().first.str); + } + if(const auto bare = lex_unquoted_key::invoke(loc)) { - auto tmp_key = parse_key::invoke(iter, range_end); - if(!tmp_key.first.ok()) - return std::make_pair(result_type{}, iter); - iter = is_any_num_of_ws::invoke(tmp_key.second, range_end); - if(*iter != '=') - throw std::make_pair(iter, syntax_error("invalid key value pair")); - iter = is_any_num_of_ws::invoke(std::next(iter), range_end); - - auto tmp_value = parse_value::invoke(iter, range_end); - if(!tmp_value.first.ok()) - throw std::make_pair(iter, syntax_error("invalid key value pair")); - - iter = tmp_value.second; - - return std::make_pair(std::make_pair( - tmp_key.first.move(), tmp_value.first.move()), - is_any_num_of_ws::invoke(tmp_value.second, range_end)); + return ok(bare.unwrap().str()); } -}; + return err(format_underline("[error] toml::parse_simple_key: " + "the next token is not a simple key", loc, "not a key")); +} -template -struct parse_inline_table +// dotted key become vector of keys +template +result, std::string> parse_key(location& loc) { - typedef charT value_type; - static_assert(std::is_same::value, ""); - typedef detail::result result_type; - - template::value_type, - value_type>::value>::type> - static std::pair - invoke(Iterator iter, Iterator range_end) + const auto first = loc.iter(); + // dotted key -> foo.bar.baz whitespaces are allowed + if(const auto token = lex_dotted_key::invoke(loc)) { - const Iterator end = is_inline_table::invoke(iter, range_end); - if(iter == end) return std::make_pair(result_type{}, iter); - - iter = is_any_num_of_ws::invoke(std::next(iter), range_end); + location inner_loc(loc.name(), token.unwrap().str()); + std::vector keys; - const Iterator last = std::prev(end); - toml::Table result; - while(iter != last) + while(inner_loc.iter() != inner_loc.end()) { - auto tmp = parse_key_value_pair::invoke(iter, last); - if(!tmp.first.ok()) - throw std::make_pair(iter, syntax_error("parse_inline_table")); - - result.emplace(tmp.first.move()); - iter = tmp.second; + lex_ws::invoke(inner_loc); + if(const auto k = parse_simple_key(inner_loc)) + { + keys.push_back(k.unwrap()); + } + else + { + throw internal_error(format_underline("[error] " + "toml::detail::parse_key: dotted key contains invalid key", + inner_loc, k.unwrap_err())); + } - iter = is_any_num_of_ws::invoke(iter, last); - iter = is_character::invoke(iter, last); - iter = is_any_num_of_ws::invoke(iter, last); + lex_ws::invoke(inner_loc); + if(inner_loc.iter() == inner_loc.end()) + { + break; + } + else if(*inner_loc.iter() == '.') + { + ++inner_loc.iter(); // to skip `.` + } + else + { + throw internal_error(format_underline("[error] toml::parse_key: " + "dotted key contains invalid key ", inner_loc, + "should be `.`")); + } } - return std::make_pair(result, end); + return ok(keys); } -}; + loc.iter() = first; -struct parse_table_definition + // simple key -> foo + if(const auto smpl = parse_simple_key(loc)) + { + return ok(std::vector(1, smpl.unwrap())); + } + return err(format_underline("toml::parse_key: the next token is not a key", + loc, "not a key")); +} + +// forward-decl to implement parse_array and parse_table +template +result parse_value(location&); + +template +result>, std::string> +parse_array(location& loc) { - typedef toml::character value_type; - typedef detail::result> result_type; - - template::value_type, - value_type>::value>::type> - static std::pair - invoke(Iterator iter, Iterator range_end) + const auto first = loc.iter(); + if(loc.iter() == loc.end()) + { + return err("[error] toml::parse_array: input is empty"); + } + if(*loc.iter() != '[') { - const Iterator end = - is_table_definition::invoke(iter, range_end); - if(iter == end) return std::make_pair(result_type{}, iter); + return err(format_underline("[error] toml::parse_array: " + "token is not an array", loc, "should be [")); + } + ++loc.iter(); + + using lex_ws_comment_newline = repeat< + either, unlimited>; - std::vector result; - result.reserve(std::count(iter, end, '.')+1); + array retval; + while(loc.iter() != loc.end()) + { + lex_ws_comment_newline::invoke(loc); // skip - const Iterator last = std::prev(end); - iter = is_any_num_of_ws::invoke(iter, last); - iter = is_any_num_of_ws::invoke(std::next(iter), last); + if(loc.iter() != loc.end() && *loc.iter() == ']') + { + ++loc.iter(); // skip ']' + return ok(std::make_pair(retval, + region(loc, first, loc.iter()))); + } - auto tmp = parse_key::invoke(iter, last); - if(!tmp.first.ok()) - throw std::make_pair(iter, syntax_error("table definition")); - result.emplace_back(tmp.first.move()); - iter = is_any_num_of_ws::invoke(tmp.second, last); + if(auto val = parse_value(loc)) + { + if(!retval.empty() && retval.front().type() != val.as_ok().type()) + { + throw syntax_error(format_underline( + "[error] toml::parse_array: type of elements should be the " + "same each other.", region(loc, first, loc.iter()), + "inhomogenous types")); + } + retval.push_back(std::move(val.unwrap())); + } + else + { + return err(val.unwrap_err()); + } - while(iter != last) + using lex_array_separator = sequence, character<','>>; + const auto sp = lex_array_separator::invoke(loc); + if(!sp) { - iter = is_character::invoke(iter, last); - iter = is_any_num_of_ws::invoke(iter, last); - - tmp = parse_key::invoke(iter, last); - if(!tmp.first.ok()) - throw std::make_pair(iter, syntax_error("table definition")); - result.emplace_back(tmp.first.move()); - iter = is_any_num_of_ws::invoke(tmp.second, last); + lex_ws_comment_newline::invoke(loc); + if(loc.iter() != loc.end() && *loc.iter() == ']') + { + ++loc.iter(); // skip ']' + return ok(std::make_pair(retval, + region(loc, first, loc.iter()))); + } + else + { + return err(format_underline("[error] toml::parse_array: " + "missing array separator `,`", loc, "should be `,`")); + } } - return std::make_pair(result, end); } -}; + loc.iter() = first; + return err(format_underline("[error] toml::parse_array: " + "array did not closed by `]`", loc, "should be closed")); +} -struct parse_array_of_table_definition +template +result, value>, std::string> +parse_key_value_pair(location& loc) { - typedef toml::character value_type; - typedef detail::result> result_type; - - template::value_type, - value_type>::value>::type> - static std::pair - invoke(Iterator iter, Iterator range_end) + const auto first = loc.iter(); + auto key = parse_key(loc); + if(!key) { - const Iterator end = - is_array_of_table_definition::invoke(iter, range_end); - if(iter == end) return std::make_pair(result_type{}, iter); - - if(std::distance(iter, end) < 5) - throw std::make_pair(iter, syntax_error("invalid array_of_table definition")); - - std::vector result; - result.reserve(std::count(iter, end, '.')+1); - - const Iterator last = end - 2; - iter = is_any_num_of_ws::invoke(iter, last) + 2; - iter = is_any_num_of_ws::invoke(iter, last); + std::string msg = std::move(key.unwrap_err()); + // 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". + if(const auto keyval_sep = lex_keyval_sep::invoke(loc)) + { + loc.iter() = first; + msg = format_underline("[error] toml::parse_key_value_pair: " + "empty key is not allowed.", loc, "key expected before '='"); + } + return err(std::move(msg)); + } - auto tmp = parse_key::invoke(iter, last); - if(!tmp.first.ok()) - throw std::make_pair(iter, syntax_error("array of table definition")); - result.emplace_back(tmp.first.move()); - iter = is_any_num_of_ws::invoke(tmp.second, last); + const auto kvsp = lex_keyval_sep::invoke(loc); + if(!kvsp) + { + std::string msg; + // if the line contains '=' after the invalid sequence, possibly the + // error is in the key (like, invalid character in bare key). + const auto line_end = std::find(loc.iter(), loc.end(), '\n'); + if(std::find(loc.iter(), line_end, '=') != line_end) + { + msg = format_underline("[error] toml::parse_key_value_pair: " + "invalid format for key", loc, "invalid character in key", { + "Did you forget '.' to separate dotted-key?", + "Allowed characters for bare key are [0-9a-zA-Z_-]."}); + } + else // if not, the error is lack of key-value separator. + { + msg = format_underline("[error] toml::parse_key_value_pair: " + "missing key-value separator `=`", loc, "should be `=`"); + } + loc.iter() = first; + return err(std::move(msg)); + } - while(iter != last) + const auto after_kvsp = loc.iter(); // err msg + auto val = parse_value(loc); + if(!val) + { + std::string msg; + loc.iter() = after_kvsp; + if(sequence, maybe, lex_newline>::invoke(loc)) + { + loc.iter() = after_kvsp; + msg = format_underline("[error] toml::parse_key_value_pair: " + "missing value after key-value separator '='", loc, + "expected value, but got nothing"); + } + else { - iter = is_character::invoke(iter, last); - iter = is_any_num_of_ws::invoke(iter, last); - - tmp = parse_key::invoke(iter, last); - if(!tmp.first.ok()) - throw std::make_pair(iter, syntax_error("array of table definition")); - result.emplace_back(tmp.first.move()); - iter = is_any_num_of_ws::invoke(tmp.second, last); + msg = val.unwrap_err(); } - return std::make_pair(result, end); + loc.iter() = first; + return err(msg); } -}; + return ok(std::make_pair(std::move(key.unwrap()), std::move(val.unwrap()))); +} -struct parse_data +// for error messages. +template +std::string format_dotted_keys(InputIterator first, const InputIterator last) { - typedef toml::character value_type; - typedef toml::Table result_type; + static_assert(std::is_same::value_type>::value,""); - template::value_type, - value_type>::value>::type> - static result_type invoke(Iterator iter, const Iterator end) + std::string retval(*first++); + for(; first != last; ++first) { - toml::Table result; - auto noname = parse_table_contents(iter, end); - result = std::move(noname.first); - iter = skip_empty(noname.second, end); + retval += '.'; + retval += *first; + } + return retval; +} - while(iter != end) +template +result +insert_nested_key(table& root, const toml::value& v, + InputIterator iter, const InputIterator last, + const bool is_array_of_table = false) +{ + static_assert(std::is_same::value_type>::value,""); + + const auto first = iter; + assert(iter != last); + + table* tab = std::addressof(root); + for(; iter != last; ++iter) // search recursively + { + const key& k = *iter; + if(std::next(iter) == last) // k is the last key { - iter = skip_empty(iter, end); - std::pair>, Iterator> tabname; - if((tabname = parse_table_definition::invoke(iter, end)).first.ok()) + // XXX if the value is array-of-tables, there can be several + // tables that are in the same array. in that case, we need to + // find the last element and insert it to there. + if(is_array_of_table) { - auto contents = parse_table_contents(tabname.second, end); - push_table(result, std::move(contents.first), - tabname.first.get().begin(), tabname.first.get().end()); - iter = contents.second; + if(tab->count(k) == 1) // there is already an array of table + { + if(tab->at(k).is(value_t::Table)) + { + // show special err msg for conflicting table + throw syntax_error(format_underline(concat_to_string( + "[error] toml::insert_value: array of table (\"", + format_dotted_keys(first, last), "\") cannot insert" + "ed"), get_region(tab->at(k)), "table already defined", + get_region(v), "this conflicts with the previous table")); + } + else if(!(tab->at(k).is(value_t::Array))) + { + throw syntax_error(format_underline(concat_to_string( + "[error] toml::insert_value: array of table (\"", + format_dotted_keys(first, last), "\") collides with" + " existing value"), get_region(tab->at(k)), + concat_to_string("this ", tab->at(k).type(), "value" + "already exists"), get_region(v), "while inserting" + "this array-of-tables")); + } + array& a = tab->at(k).template cast(); + if(!(a.front().is(value_t::Table))) + { + throw syntax_error(format_underline(concat_to_string( + "[error] toml::insert_value: array of table (\"", + format_dotted_keys(first, last), "\") collides with" + " existing value"), get_region(tab->at(k)), + concat_to_string("this ", tab->at(k).type(), "value" + "already exists"), get_region(v), "while inserting" + "this array-of-tables")); + } + a.push_back(v); + return ok(true); + } + else // if not, we need to create the array of table + { + toml::value aot(v); // copy region info from table to array + // 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)); + return ok(true); + } } - else if((tabname = parse_array_of_table_definition::invoke(iter, end)).first.ok()) + if(tab->count(k) == 1) { - auto contents = parse_table_contents(tabname.second, end); - push_array_of_table(result, std::move(contents.first), - tabname.first.get().begin(), tabname.first.get().end()); - iter = contents.second; + if(tab->at(k).is(value_t::Table) && v.is(value_t::Table)) + { + throw syntax_error(format_underline(concat_to_string( + "[error] toml::insert_value: table (\"", + format_dotted_keys(first, last), "\") already exists."), + get_region(tab->at(k)), "table already exists here", + get_region(v), "table defined twice")); + } + else if(v.is(value_t::Table) && + tab->at(k).is(value_t::Array) && + tab->at(k).cast().size() > 0 && + tab->at(k).cast().front().is(value_t::Table)) + { + throw syntax_error(format_underline(concat_to_string( + "[error] toml::insert_value: array of tables (\"", + format_dotted_keys(first, last), "\") already exists."), + get_region(tab->at(k)), "array of tables defined here", + get_region(v), "table conflicts with the previous array" + " of table")); + } + else + { + throw syntax_error(format_underline(concat_to_string( + "[error] toml::insert_value: value (\"", + format_dotted_keys(first, last), "\") already exists."), + get_region(tab->at(k)), "value already exists here", + get_region(v), "value inserted twice")); + } } - else - throw std::make_pair(iter, syntax_error("parse_data: unknown line")); + tab->insert(std::make_pair(k, v)); + return ok(true); } - return result; - } - - template::value_type, - value_type>::value>::type> - static Iterator - skip_empty(Iterator iter, Iterator end) - { - while(iter != end) + else { - if(*iter == '#') + // if there is no corresponding value, insert it first. + // related: you don't need to write + // # [x] + // # [x.y] + // to write + // [x.y.z] + if(tab->count(k) == 0) { - while(iter != end && - iter == is_newline::invoke(iter, end)){++iter;} + // the region of [x.y] is the same as [x.y.z]. + (*tab)[k] = v; // copy region_info_ + detail::assign_keeping_region((*tab)[k], + ::toml::value(::toml::table{})); } - else if(iter == is_newline::invoke(iter, end) && - iter == is_whitespace::invoke(iter, end)) + + // type checking... + if(tab->at(k).is(value_t::Table)) { - return iter; + tab = std::addressof((*tab)[k].template cast()); + } + else if(tab->at(k).is(value_t::Array)) // inserting to array-of-tables? + { + array& a = (*tab)[k].template cast(); + if(!a.back().is(value_t::Table)) + { + throw syntax_error(format_underline(concat_to_string( + "[error] toml::insert_value: target (", + format_dotted_keys(first, std::next(iter)), + ") is neither table nor an array of tables"), + get_region(a.back()), concat_to_string("actual type is ", + a.back().type()), get_region(v), "inserting this")); + } + tab = std::addressof(a.back().template cast()); } else { - ++iter; + throw syntax_error(format_underline(concat_to_string( + "[error] toml::insert_value: target (", + format_dotted_keys(first, std::next(iter)), + ") is neither table nor an array of tables"), + get_region(tab->at(k)), concat_to_string("actual type is ", + tab->at(k).type()), get_region(v), "inserting this")); } } - return iter; } + return err(std::string("toml::detail::insert_nested_key: never reach here")); +} - template::value_type, - value_type>::value>::type> - static std::pair - parse_table_contents(Iterator iter, Iterator end) +template +result>, std::string> +parse_inline_table(location& loc) +{ + const auto first = loc.iter(); + table retval; + if(!(loc.iter() != loc.end() && *loc.iter() == '{')) { - toml::Table table; - iter = skip_empty(iter, end); - while(iter != end) + return err(format_underline("[error] toml::parse_inline_table: " + "the next token is not an inline table", loc, "not `{`.")); + } + ++loc.iter(); + while(loc.iter() != loc.end()) + { + maybe::invoke(loc); + if(loc.iter() != loc.end() && *loc.iter() == '}') + { + ++loc.iter(); // skip `}` + return ok(std::make_pair( + retval, region(loc, first, loc.iter()))); + } + + const auto kv_r = parse_key_value_pair(loc); + if(!kv_r) { - auto kv = parse_key_value_pair::invoke(iter, end); - if(!kv.first.ok()) return std::make_pair(table, iter); + return err(kv_r.unwrap_err()); + } + const std::vector& keys = kv_r.unwrap().first; + const value& val = kv_r.unwrap().second; - table.emplace(kv.first.move()); - iter = kv.second; - iter = skip_empty(iter, end); + const auto inserted = + insert_nested_key(retval, val, keys.begin(), keys.end()); + if(!inserted) + { + throw internal_error("[error] toml::parse_inline_table: " + "failed to insert value into table: " + inserted.unwrap_err()); + } + + using lex_table_separator = sequence, character<','>>; + const auto sp = lex_table_separator::invoke(loc); + if(!sp) + { + maybe::invoke(loc); + if(loc.iter() != loc.end() && *loc.iter() == '}') + { + ++loc.iter(); // skip `}` + return ok(std::make_pair( + retval, region(loc, first, loc.iter()))); + } + else + { + return err(format_underline("[error] toml:::parse_inline_table:" + " missing table separator `,` ", loc, "should be `,`")); + } } - return std::make_pair(table, iter); } + loc.iter() = first; + return err(format_underline("[error] toml::parse_inline_table: " + "inline table did not closed by `}`", loc, "should be closed")); +} - template::value_type, - toml::key>::value>::type> - static void - push_table(toml::Table& data, toml::Table&& v, Iterator iter, Iterator end) +template +result parse_value(location& loc) +{ + const auto first = loc.iter(); + if(first == loc.end()) + { + return err(std::string("[error] toml::parse_value: input is empty")); + } + if(auto r = parse_string (loc)) + {return ok(value(std::move(r.unwrap().first), std::move(r.unwrap().second)));} + if(auto r = parse_array (loc)) + {return ok(value(std::move(r.unwrap().first), std::move(r.unwrap().second)));} + if(auto r = parse_inline_table (loc)) + {return ok(value(std::move(r.unwrap().first), std::move(r.unwrap().second)));} + if(auto r = parse_boolean (loc)) + {return ok(value(std::move(r.unwrap().first), std::move(r.unwrap().second)));} + if(auto r = parse_offset_datetime(loc)) + {return ok(value(std::move(r.unwrap().first), std::move(r.unwrap().second)));} + if(auto r = parse_local_datetime (loc)) + {return ok(value(std::move(r.unwrap().first), std::move(r.unwrap().second)));} + if(auto r = parse_local_date (loc)) + {return ok(value(std::move(r.unwrap().first), std::move(r.unwrap().second)));} + if(auto r = parse_local_time (loc)) + {return ok(value(std::move(r.unwrap().first), std::move(r.unwrap().second)));} + if(auto r = parse_floating (loc)) + {return ok(value(std::move(r.unwrap().first), std::move(r.unwrap().second)));} + if(auto r = parse_integer (loc)) + {return ok(value(std::move(r.unwrap().first), std::move(r.unwrap().second)));} + + const auto msg = format_underline("[error] toml::parse_value: " + "unknown token appeared", loc, "unknown"); + loc.iter() = first; + return err(msg); +} + +template +result, region>, std::string> +parse_table_key(location& loc) +{ + if(auto token = lex_std_table::invoke(loc)) { - if(iter == std::prev(end)) + location inner_loc(loc.name(), token.unwrap().str()); + + const auto open = lex_std_table_open::invoke(inner_loc); + if(!open || inner_loc.iter() == inner_loc.end()) + { + throw internal_error(format_underline("[error] " + "toml::parse_table_key: no `[`", inner_loc, "should be `[`")); + } + // to skip [ a . b . c ] + // ^----------- this whitespace + lex_ws::invoke(inner_loc); + const auto keys = parse_key(inner_loc); + if(!keys) { - if(data.count(*iter) == 1) - throw syntax_error("duplicate key: " + *iter); - data.emplace(*iter, std::move(v)); - return; + throw internal_error(format_underline("[error] " + "toml::parse_table_key: invalid key", inner_loc, "not key")); } + // to skip [ a . b . c ] + // ^-- this whitespace + lex_ws::invoke(inner_loc); + const auto close = lex_std_table_close::invoke(inner_loc); + if(!close) + { + throw internal_error(format_underline("[error] " + "toml::parse_table_key: no `]`", inner_loc, "should be `]`")); + } + return ok(std::make_pair(keys.unwrap(), token.unwrap())); + } + else + { + return err(token.unwrap_err()); + } +} - if(data.count(*iter) == 0) +template +result, region>, std::string> +parse_array_table_key(location& loc) +{ + if(auto token = lex_array_table::invoke(loc)) + { + location inner_loc(loc.name(), token.unwrap().str()); + + const auto open = lex_array_table_open::invoke(inner_loc); + if(!open || inner_loc.iter() == inner_loc.end()) { - data.emplace(*iter, toml::Table()); - return push_table(data[*iter].template cast(), - std::move(v), std::next(iter), end); + throw internal_error(format_underline("[error] " + "toml::parse_array_table_key: no `[[`", inner_loc, + "should be `[[`")); } - else if(data[*iter].type() == value_t::Table) + lex_ws::invoke(inner_loc); + const auto keys = parse_key(inner_loc); + if(!keys) { - return push_table(data[*iter].template cast(), - std::move(v), std::next(iter), end); + throw internal_error(format_underline("[error] " + "toml::parse_array_table_key: invalid key", inner_loc, + "not key")); } - else if(data[*iter].type() == value_t::Array) + lex_ws::invoke(inner_loc); + const auto close = lex_array_table_close::invoke(inner_loc); + if(!close) { - auto& ar = data[*iter].template cast(); - if(ar.empty()) ar.emplace_back(toml::Table{}); - if(ar.back().type() != value_t::Table) - throw syntax_error("assign table into array having non-table type: " + *iter); - return push_table(ar.back().template cast(), - std::move(v), std::next(iter), end); + throw internal_error(format_underline("[error] " + "toml::parse_table_key: no `]]`", inner_loc, "should be `]]`")); } - else - throw syntax_error("assign table into not table: " + *iter); + return ok(std::make_pair(keys.unwrap(), token.unwrap())); + } + else + { + return err(token.unwrap_err()); } +} - template::value_type, - toml::key>::value>::type> - static void - push_array_of_table(toml::Table& data, toml::Table&& v, - Iterator iter, Iterator end) +// parse table body (key-value pairs until the iter hits the next [tablekey]) +template +result parse_ml_table(location& loc) +{ + const auto first = loc.iter(); + if(first == loc.end()) { - //XXX Iterator::value_type == toml::key - if(iter == std::prev(end)) - { - if(data.count(*iter) == 0) - data.emplace(*iter, toml::Array()); - else if(data.at(*iter).type() != value_t::Array) - throw syntax_error("duplicate key: " + *iter); + return err(std::string("toml::parse_ml_table: input is empty")); + } - data[*iter].template cast().emplace_back(std::move(v)); - return; - } + // XXX at lest one newline is needed + using skip_line = repeat< + sequence, maybe, lex_newline>, at_least<1>>; + skip_line::invoke(loc); - if(data.count(*iter) == 0) + table tab; + while(loc.iter() != loc.end()) + { + lex_ws::invoke(loc); + const auto before = loc.iter(); + if(const auto tmp = parse_array_table_key(loc)) // next table found { - data.emplace(*iter, toml::Table()); - return push_array_of_table(data[*iter].template cast(), - std::move(v), std::next(iter), end); + loc.iter() = before; + return ok(tab); } - else if(data[*iter].type() == value_t::Table) + if(const auto tmp = parse_table_key(loc)) // next table found { - return push_array_of_table(data[*iter].template cast(), - std::move(v), std::next(iter), end); + loc.iter() = before; + return ok(tab); } - else if(data[*iter].type() == value_t::Array) + if(const auto kv = parse_key_value_pair(loc)) { - auto& ar = data[*iter].template cast(); - if(ar.empty()) ar.emplace_back(toml::Table{}); - if(ar.back().type() != value_t::Table) - throw syntax_error("assign table into array having non-table type: " + *iter); - return push_array_of_table(ar.back().template cast(), - std::move(v), std::next(iter), end); + const std::vector& keys = kv.unwrap().first; + const value& val = kv.unwrap().second; + const auto inserted = + insert_nested_key(tab, val, keys.begin(), keys.end()); + if(!inserted) + { + return err(inserted.unwrap_err()); + } } else - throw syntax_error("assign array of table into not table: " + *iter); - } + { + return err(kv.unwrap_err()); + } -}; + const auto newline = skip_line::invoke(loc); + if(!newline && loc.iter() != loc.end()) + { + const auto before = loc.iter(); + lex_ws::invoke(loc); // skip whitespace + const auto msg = format_underline("[error] toml::parse_table: " + "invalid line format", loc, concat_to_string( + "expected newline, but got '", show_char(*loc.iter()), "'.")); + loc.iter() = before; + return err(msg); + } -template> -toml::Table parse(std::basic_istream& is) + // comment lines are skipped by the above function call. + // However, if the file ends with comment without newline, + // it might cause parsing error because skip_line matches + // `comment + newline`, not `comment` itself. to skip the + // last comment, call lex_comment one more time. + lex_comment::invoke(loc); + } + return ok(tab); +} + +template +result parse_toml_file(location& loc) { - const auto initial = is.tellg(); - is.seekg(0, std::ios::end); - const auto eofpos = is.tellg(); - const std::size_t size = eofpos - initial; - is.seekg(initial); - std::vector contents(size); - typedef std::vector::const_iterator iterator_type; - is.read(contents.data(), size); - try + const auto first = loc.iter(); + if(first == loc.end()) { - return parse_data::invoke(contents.cbegin(), contents.cend()); + return err(std::string("toml::detail::parse_toml_file: input is empty")); } - catch(std::pair iter_except) + + table data; + /* root object is also table, but without [tablename] */ + if(auto tab = parse_ml_table(loc)) { - std::cerr << "toml syntax error." << std::endl; - auto iter = iter_except.first; - const std::size_t nline = 1 + std::count(contents.cbegin(), iter, '\n'); - std::cerr << "processing at line " << nline << std::endl; - while(*iter != '\n' && iter != contents.cbegin()){--iter;} - ++iter; - while(*iter != '\n' && iter != contents.cend()) + data = std::move(tab.unwrap()); + } + else // failed (empty table is regarded as success in parse_ml_table) + { + return err(tab.unwrap_err()); + } + while(loc.iter() != loc.end()) + { + // here, the region of [table] is regarded as the table-key because + // the table body is normally too big and it is not so informative + // if the first key-value pair of the table is shown in the error + // message. + if(const auto tabkey = parse_array_table_key(loc)) { - std::cerr << *iter; ++iter; + const auto tab = parse_ml_table(loc); + if(!tab){return err(tab.unwrap_err());} + + const auto& keys = tabkey.unwrap().first; + const auto& reg = tabkey.unwrap().second; + + const auto inserted = insert_nested_key(data, + toml::value(tab.unwrap(), reg), + keys.begin(), keys.end(), /*is_array_of_table=*/ true); + if(!inserted) {return err(inserted.unwrap_err());} + + continue; } - std::cerr << std::endl; + if(const auto tabkey = parse_table_key(loc)) + { + const auto tab = parse_ml_table(loc); + if(!tab){return err(tab.unwrap_err());} + + const auto& keys = tabkey.unwrap().first; + const auto& reg = tabkey.unwrap().second; - throw iter_except.second; + const auto inserted = insert_nested_key(data, + toml::value(tab.unwrap(), reg), keys.begin(), keys.end()); + if(!inserted) {return err(inserted.unwrap_err());} + + continue; + } + return err(format_underline("[error]: toml::parse_toml_file: " + "unknown line appeared", loc, "unknown format")); } + return ok(data); } -inline toml::Table parse(const char* filename) +} // detail + +inline table parse(std::istream& is, std::string fname = "unknown file") { - std::ifstream ifs(filename, std::ios_base::in | std::ios_base::binary); - if(!ifs.good()) + const auto beg = is.tellg(); + is.seekg(0, std::ios::end); + const auto end = is.tellg(); + const auto fsize = end - beg; + is.seekg(beg); + + // read whole file as a sequence of char + std::vector letters(fsize); + is.read(letters.data(), fsize); + + detail::location> + loc(std::move(fname), std::move(letters)); + + // skip BOM if exists. + // XXX component of BOM (like 0xEF) exceeds the representable range of + // signed char, so on some (actually, most) of the environment, these cannot + // 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 + // convert char to unsigned char to guarantee the comparability. + if(letters.size() >= 3) + { + std::array BOM; + std::memcpy(BOM.data(), letters.data(), 3); + if(BOM[0] == 0xEF && BOM[1] == 0xBB && BOM[2] == 0xBF) + { + loc.iter() += 3; // BOM found. skip. + } + } + + const auto data = detail::parse_toml_file(loc); + if(!data) { - throw std::runtime_error("file open error: " + std::string(filename)); + throw syntax_error(data.unwrap_err()); } - return parse(ifs); + return data.unwrap(); } -template> -inline toml::Table parse(const std::basic_string& filename) +inline table parse(const std::string& fname) { - std::ifstream ifs(filename, std::ios_base::in | std::ios_base::binary); + std::ifstream ifs(fname.c_str()); if(!ifs.good()) { - throw std::runtime_error("file open error: " + filename); + throw std::runtime_error("toml::parse: file open error -> " + fname); } - return parse(ifs); + return parse(ifs, fname); } - - -}// toml -#endif// TOML11_PARSER +} // toml +#endif// TOML11_PARSER_HPP diff --git a/toml/region.hpp b/toml/region.hpp new file mode 100644 index 00000000..8a673f6d --- /dev/null +++ b/toml/region.hpp @@ -0,0 +1,361 @@ +// Copyright Toru Niina 2017. +// Distributed under the MIT License. +#ifndef TOML11_REGION_H +#define TOML11_REGION_H +#include "exception.hpp" +#include +#include +#include +#include +#include +#include + +namespace toml +{ +namespace detail +{ + +// helper function to avoid std::string(0, 'c') +template +std::string make_string(Iterator first, Iterator last) +{ + if(first == last) {return "";} + return std::string(first, last); +} +inline std::string make_string(std::size_t len, char c) +{ + if(len == 0) {return "";} + 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 +struct location +{ + static_assert(std::is_same::value,""); + using const_iterator = typename Container::const_iterator; + using source_ptr = std::shared_ptr; + + location(std::string name, Container cont) + : source_(std::make_shared(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. +// shared_ptr points the resource that the iter points. +// combinators returns this. +// it will be used to generate better error messages. +struct region_base +{ + region_base() = default; + virtual ~region_base() = default; + region_base(const region_base&) = default; + region_base(region_base&& ) = default; + region_base& operator=(const region_base&) = default; + region_base& operator=(region_base&& ) = default; + + virtual bool is_ok() const noexcept {return false;} + + virtual std::string str() const {return std::string("unknown region");} + virtual std::string name() const {return std::string("unknown file");} + virtual std::string line() const {return std::string("unknown line");} + virtual std::string line_num() const {return std::string("?");} + + + virtual std::size_t before() const noexcept {return 0;} + virtual std::size_t size() const noexcept {return 0;} + virtual std::size_t after() const noexcept {return 0;} +}; + +template +struct region final : public region_base +{ + static_assert(std::is_same::value,""); + using const_iterator = typename Container::const_iterator; + using source_ptr = std::shared_ptr; + + // delete default constructor. source_ never be null. + region() = delete; + + region(const location& loc) + : source_(loc.source()), source_name_(loc.name()), + first_(loc.iter()), last_(loc.iter()) + {} + region(location&& loc) + : source_(loc.source()), source_name_(loc.name()), + first_(loc.iter()), last_(loc.iter()) + {} + + region(const location& loc, const_iterator f, const_iterator l) + : source_(loc.source()), source_name_(loc.name()), first_(f), last_(l) + {} + region(location&& loc, const_iterator f, const_iterator l) + : source_(loc.source()), source_name_(loc.name()), first_(f), last_(l) + {} + + region(const region&) = default; + region(region&&) = default; + region& operator=(const region&) = default; + region& operator=(region&&) = default; + ~region() = default; + + region& operator+=(const region& other) + { + if(this->begin() != other.begin() || this->end() != other.end() || + this->last_ != other.first_) + { + throw internal_error("invalid region concatenation"); + } + this->last_ = other.last_; + return *this; + } + + bool is_ok() const noexcept override {return static_cast(source_);} + + std::string str() const override {return make_string(first_, last_);} + std::string line() const override + { + if(this->contain_newline()) + { + return make_string(this->line_begin(), + std::find(this->line_begin(), this->last(), '\n')); + } + return make_string(this->line_begin(), this->line_end()); + } + std::string line_num() const override + { + return std::to_string(1 + std::count(this->begin(), this->first(), '\n')); + } + + std::size_t size() const noexcept override + { + return std::distance(first_, last_); + } + std::size_t before() const noexcept override + { + return std::distance(this->line_begin(), this->first()); + } + std::size_t after() const noexcept override + { + return std::distance(this->last(), this->line_end()); + } + + bool contain_newline() const noexcept + { + return std::find(this->first(), this->last(), '\n') != this->last(); + } + + const_iterator line_begin() const noexcept + { + using reverse_iterator = std::reverse_iterator; + return std::find(reverse_iterator(this->first()), + reverse_iterator(this->begin()), '\n').base(); + } + const_iterator line_end() const noexcept + { + return std::find(this->last(), this->end(), '\n'); + } + + const_iterator begin() const noexcept {return source_->cbegin();} + const_iterator end() const noexcept {return source_->cend();} + const_iterator first() const noexcept {return first_;} + const_iterator last() const noexcept {return last_;} + + source_ptr const& source() const& noexcept {return source_;} + source_ptr&& source() && noexcept {return std::move(source_);} + + std::string name() const override {return source_name_;} + + private: + + source_ptr source_; + std::string source_name_; + const_iterator first_, last_; +}; + +// to show a better error message. +inline std::string format_underline(const std::string& message, + const region_base& reg, const std::string& comment_for_underline, + std::vector helps = {}) +{ +#ifdef _WIN32 + const auto newline = "\r\n"; +#else + const char newline = '\n'; +#endif + const auto line = reg.line(); + const auto line_number = reg.line_num(); + + std::string retval; + retval += message; + retval += newline; + retval += " --> "; + retval += reg.name(); + retval += newline; + retval += ' '; + retval += line_number; + retval += " | "; + retval += line; + retval += newline; + retval += make_string(line_number.size() + 1, ' '); + retval += " | "; + retval += make_string(reg.before(), ' '); + retval += make_string(reg.size(), '~'); + retval += ' '; + retval += comment_for_underline; + if(helps.size() != 0) + { + retval += newline; + retval += make_string(line_number.size() + 1, ' '); + retval += " | "; + for(const auto help : helps) + { + retval += newline; + retval += "Hint: "; + retval += help; + } + } + return retval; +} + +// to show a better error message. +inline std::string format_underline(const std::string& message, + const region_base& reg1, const std::string& comment_for_underline1, + const region_base& reg2, const std::string& comment_for_underline2, + std::vector helps = {}) +{ +#ifdef _WIN32 + const auto newline = "\r\n"; +#else + const char newline = '\n'; +#endif + const auto line1 = reg1.line(); + const auto line_number1 = reg1.line_num(); + const auto line2 = reg2.line(); + const auto line_number2 = reg2.line_num(); + const auto line_num_width = + std::max(line_number1.size(), line_number2.size()); + + std::ostringstream retval; + retval << message; + retval << newline; + retval << " --> "; + retval << reg1.name() << newline;; +// --------------------------------------- + retval << ' ' << std::setw(line_num_width) << line_number1; + retval << " | " << line1 << newline; + retval << make_string(line_num_width + 1, ' '); + retval << " | "; + retval << make_string(reg1.before(), ' '); + retval << make_string(reg1.size(), '~'); + retval << ' '; + retval << comment_for_underline1 << newline; +// --------------------------------------- + retval << " ..." << newline; + retval << ' ' << std::setw(line_num_width) << line_number2; + retval << " | " << line2 << newline; + retval << make_string(line_num_width + 1, ' '); + retval << " | "; + retval << make_string(reg2.before(), ' '); + retval << make_string(reg2.size(), '~'); + retval << ' '; + retval << comment_for_underline2; + if(helps.size() != 0) + { + retval << newline; + retval << make_string(line_num_width + 1, ' '); + retval << " | "; + for(const auto help : helps) + { + retval << newline; + retval << "Hint: "; + retval << help; + } + } + return retval.str(); +} + + +// to show a better error message. +template +std::string +format_underline(const std::string& message, const location& loc, + const std::string& comment_for_underline, + std::vector helps = {}) +{ +#ifdef _WIN32 + const auto newline = "\r\n"; +#else + const char newline = '\n'; +#endif + using const_iterator = typename location::const_iterator; + using reverse_iterator = std::reverse_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( + 1 + std::count(loc.begin(), loc.iter(), '\n')); + + 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) + { + retval += newline; + retval += make_string(line_number.size() + 1, ' '); + retval += " | "; + for(const auto help : helps) + { + retval += newline; + retval += "Hint: "; + retval += help; + } + } + return retval; +} + +} // detail +} // toml +#endif// TOML11_REGION_H diff --git a/toml/result.hpp b/toml/result.hpp new file mode 100644 index 00000000..3b13acde --- /dev/null +++ b/toml/result.hpp @@ -0,0 +1,642 @@ +// Copyright Toru Niina 2017. +// Distributed under the MIT License. +#ifndef TOML11_RESULT_H +#define TOML11_RESULT_H +#include +#include +#include +#include +#include +#include +#include + +namespace toml +{ + +#if __cplusplus >= 201703L + +template +using return_type_of_t = std::invoke_result_t; + +#else +// result_of is deprecated after C++17 +template +using return_type_of_t = typename std::result_of::type; + +#endif + + +template +struct success +{ + using value_type = T; + value_type value; + + explicit success(const value_type& v) + noexcept(std::is_nothrow_copy_constructible::value) + : value(v) + {} + explicit success(value_type&& v) + noexcept(std::is_nothrow_move_constructible::value) + : value(std::move(v)) + {} + + template + explicit success(U&& v): value(std::forward(v)) {} + + template + explicit success(const success& v): value(v.value) {} + template + explicit success(success&& v): value(std::move(v.value)) {} + + ~success() = default; + success(const success&) = default; + success(success&&) = default; + success& operator=(const success&) = default; + success& operator=(success&&) = default; +}; + +template +struct failure +{ + using value_type = T; + value_type value; + + explicit failure(const value_type& v) + noexcept(std::is_nothrow_copy_constructible::value) + : value(v) + {} + explicit failure(value_type&& v) + noexcept(std::is_nothrow_move_constructible::value) + : value(std::move(v)) + {} + + template + explicit failure(U&& v): value(std::forward(v)) {} + + template + explicit failure(const failure& v): value(v.value) {} + template + explicit failure(failure&& v): value(std::move(v.value)) {} + + ~failure() = default; + failure(const failure&) = default; + failure(failure&&) = default; + failure& operator=(const failure&) = default; + failure& operator=(failure&&) = default; +}; + +template +success::type>::type> +ok(T&& v) +{ + return success< + typename std::remove_cv::type>::type + >(std::forward(v)); +} +template +failure::type>::type> +err(T&& v) +{ + return failure< + typename std::remove_cv::type>::type + >(std::forward(v)); +} + +inline success ok(const char* literal) +{ + return success(std::string(literal)); +} +inline failure err(const char* literal) +{ + return failure(std::string(literal)); +} + + +template +struct result +{ + using value_type = T; + using error_type = E; + using success_type = success; + using failure_type = failure; + + result(const success_type& s): is_ok_(true) + { + auto tmp = ::new(std::addressof(this->succ)) success_type(s); + assert(tmp == std::addressof(this->succ)); + } + result(const failure_type& f): is_ok_(false) + { + auto tmp = ::new(std::addressof(this->fail)) failure_type(f); + assert(tmp == std::addressof(this->fail)); + } + result(success_type&& s): is_ok_(true) + { + auto tmp = ::new(std::addressof(this->succ)) success_type(std::move(s)); + assert(tmp == std::addressof(this->succ)); + } + result(failure_type&& f): is_ok_(false) + { + auto tmp = ::new(std::addressof(this->fail)) failure_type(std::move(f)); + assert(tmp == std::addressof(this->fail)); + } + + template + result(const success& s): is_ok_(true) + { + auto tmp = ::new(std::addressof(this->succ)) success_type(s.value); + assert(tmp == std::addressof(this->succ)); + } + template + result(const failure& f): is_ok_(false) + { + auto tmp = ::new(std::addressof(this->fail)) failure_type(f.value); + assert(tmp == std::addressof(this->fail)); + } + template + result(success&& s): is_ok_(true) + { + auto tmp = ::new(std::addressof(this->succ)) success_type(std::move(s.value)); + assert(tmp == std::addressof(this->succ)); + } + template + result(failure&& f): is_ok_(false) + { + auto tmp = ::new(std::addressof(this->fail)) failure_type(std::move(f.value)); + assert(tmp == std::addressof(this->fail)); + } + + result& operator=(const success_type& s) + { + this->cleanup(); + this->is_ok_ = true; + auto tmp = ::new(std::addressof(this->succ)) success_type(s); + assert(tmp == std::addressof(this->succ)); + return *this; + } + result& operator=(const failure_type& f) + { + this->cleanup(); + this->is_ok_ = false; + auto tmp = ::new(std::addressof(this->fail)) failure_type(f); + assert(tmp == std::addressof(this->fail)); + return *this; + } + result& operator=(success_type&& s) + { + this->cleanup(); + this->is_ok_ = true; + auto tmp = ::new(std::addressof(this->succ)) success_type(std::move(s)); + assert(tmp == std::addressof(this->succ)); + return *this; + } + result& operator=(failure_type&& f) + { + this->cleanup(); + this->is_ok_ = false; + auto tmp = ::new(std::addressof(this->fail)) failure_type(std::move(f)); + assert(tmp == std::addressof(this->fail)); + return *this; + } + + template + result& operator=(const success& s) + { + this->cleanup(); + this->is_ok_ = true; + auto tmp = ::new(std::addressof(this->succ)) success_type(s.value); + assert(tmp == std::addressof(this->succ)); + return *this; + } + template + result& operator=(const failure& f) + { + this->cleanup(); + this->is_ok_ = false; + auto tmp = ::new(std::addressof(this->fail)) failure_type(f.value); + assert(tmp == std::addressof(this->fail)); + return *this; + } + template + result& operator=(success&& s) + { + this->cleanup(); + this->is_ok_ = true; + auto tmp = ::new(std::addressof(this->succ)) success_type(std::move(s.value)); + assert(tmp == std::addressof(this->succ)); + return *this; + } + template + result& operator=(failure&& f) + { + this->cleanup(); + this->is_ok_ = false; + auto tmp = ::new(std::addressof(this->fail)) failure_type(std::move(f.value)); + assert(tmp == std::addressof(this->fail)); + return *this; + } + + ~result() noexcept {this->cleanup();} + + result(const result& other): is_ok_(other.is_ok()) + { + if(other.is_ok()) + { + auto tmp = ::new(std::addressof(this->succ)) success_type(other.as_ok()); + assert(tmp == std::addressof(this->succ)); + } + else + { + auto tmp = ::new(std::addressof(this->fail)) failure_type(other.as_err()); + assert(tmp == std::addressof(this->fail)); + } + } + result(result&& other): is_ok_(other.is_ok()) + { + if(other.is_ok()) + { + auto tmp = ::new(std::addressof(this->succ)) success_type(std::move(other.as_ok())); + assert(tmp == std::addressof(this->succ)); + } + else + { + auto tmp = ::new(std::addressof(this->fail)) failure_type(std::move(other.as_err())); + assert(tmp == std::addressof(this->fail)); + } + } + + template + result(const result& other): is_ok_(other.is_ok()) + { + if(other.is_ok()) + { + auto tmp = ::new(std::addressof(this->succ)) success_type(other.as_ok()); + assert(tmp == std::addressof(this->succ)); + } + else + { + auto tmp = ::new(std::addressof(this->fail)) failure_type(other.as_err()); + assert(tmp == std::addressof(this->fail)); + } + } + template + result(result&& other): is_ok_(other.is_ok()) + { + if(other.is_ok()) + { + auto tmp = ::new(std::addressof(this->succ)) success_type(std::move(other.as_ok())); + assert(tmp == std::addressof(this->succ)); + } + else + { + auto tmp = ::new(std::addressof(this->fail)) failure_type(std::move(other.as_err())); + assert(tmp == std::addressof(this->fail)); + } + } + + result& operator=(const result& other) + { + this->cleanup(); + if(other.is_ok()) + { + auto tmp = ::new(std::addressof(this->succ)) success_type(other.as_ok()); + assert(tmp == std::addressof(this->succ)); + } + else + { + auto tmp = ::new(std::addressof(this->fail)) failure_type(other.as_err()); + assert(tmp == std::addressof(this->fail)); + } + is_ok_ = other.is_ok(); + return *this; + } + result& operator=(result&& other) + { + this->cleanup(); + if(other.is_ok()) + { + auto tmp = ::new(std::addressof(this->succ)) success_type(std::move(other.as_ok())); + assert(tmp == std::addressof(this->succ)); + } + else + { + auto tmp = ::new(std::addressof(this->fail)) failure_type(std::move(other.as_err())); + assert(tmp == std::addressof(this->fail)); + } + is_ok_ = other.is_ok(); + return *this; + } + + template + result& operator=(const result& other) + { + this->cleanup(); + if(other.is_ok()) + { + auto tmp = ::new(std::addressof(this->succ)) success_type(other.as_ok()); + assert(tmp == std::addressof(this->succ)); + } + else + { + auto tmp = ::new(std::addressof(this->fail)) failure_type(other.as_err()); + assert(tmp == std::addressof(this->fail)); + } + is_ok_ = other.is_ok(); + return *this; + } + template + result& operator=(result&& other) + { + this->cleanup(); + if(other.is_ok()) + { + auto tmp = ::new(std::addressof(this->succ)) success_type(std::move(other.as_ok())); + assert(tmp == std::addressof(this->succ)); + } + else + { + auto tmp = ::new(std::addressof(this->fail)) failure_type(std::move(other.as_err())); + assert(tmp == std::addressof(this->fail)); + } + is_ok_ = other.is_ok(); + return *this; + } + + bool is_ok() const noexcept {return is_ok_;} + bool is_err() const noexcept {return !is_ok_;} + + operator bool() const noexcept {return is_ok_;} + + value_type& unwrap() & + { + if(is_err()) + { + throw std::runtime_error("toml::result: bad unwrap: " + + format_error(this->as_err())); + } + return this->succ.value; + } + value_type const& unwrap() const& + { + if(is_err()) + { + throw std::runtime_error("toml::result: bad unwrap: " + + format_error(this->as_err())); + } + return this->succ.value; + } + value_type&& unwrap() && + { + if(is_err()) + { + throw std::runtime_error("toml::result: bad unwrap: " + + format_error(this->as_err())); + } + return std::move(this->succ.value); + } + + template + value_type& unwrap_or(U& opt) & + { + if(is_err()) {return opt;} + return this->succ.value; + } + template + value_type const& unwrap_or(U const& opt) const& + { + if(is_err()) {return opt;} + return this->succ.value; + } + template + value_type&& unwrap_or(U&& opt) && + { + if(is_err()) {return std::move(opt);} + return std::move(this->succ.value); + } + + error_type& unwrap_err() & + { + if(is_ok()) {throw std::runtime_error("toml::result: bad unwrap_err");} + return this->fail.value; + } + error_type const& unwrap_err() const& + { + if(is_ok()) {throw std::runtime_error("toml::result: bad unwrap_err");} + return this->fail.value; + } + error_type&& unwrap_err() && + { + if(is_ok()) {throw std::runtime_error("toml::result: bad unwrap_err");} + return std::move(this->fail.value); + } + + value_type& as_ok() & noexcept {return this->succ.value;} + value_type const& as_ok() const& noexcept {return this->succ.value;} + value_type&& as_ok() && noexcept {return std::move(this->succ.value);} + + error_type& as_err() & noexcept {return this->fail.value;} + error_type const& as_err() const& noexcept {return this->fail.value;} + error_type&& as_err() && noexcept {return std::move(this->fail.value);} + + + // prerequisities + // F: T -> U + // retval: result + template + result, error_type> + map(F&& f) & + { + if(this->is_ok()){return ok(f(this->as_ok()));} + return err(this->as_err()); + } + template + result, error_type> + map(F&& f) const& + { + if(this->is_ok()){return ok(f(this->as_ok()));} + return err(this->as_err()); + } + template + result, error_type> + map(F&& f) && + { + if(this->is_ok()){return ok(f(std::move(this->as_ok())));} + return err(std::move(this->as_err())); + } + + // prerequisities + // F: E -> F + // retval: result + template + result> + map_err(F&& f) & + { + if(this->is_err()){return err(f(this->as_err()));} + return ok(this->as_ok()); + } + template + result> + map_err(F&& f) const& + { + if(this->is_err()){return err(f(this->as_err()));} + return ok(this->as_ok()); + } + template + result> + map_err(F&& f) && + { + if(this->is_err()){return err(f(std::move(this->as_err())));} + return ok(std::move(this->as_ok())); + } + + // prerequisities + // F: T -> U + // retval: U + template + return_type_of_t + map_or_else(F&& f, U&& opt) & + { + if(this->is_err()){return std::forward(opt);} + return f(this->as_ok()); + } + template + return_type_of_t + map_or_else(F&& f, U&& opt) const& + { + if(this->is_err()){return std::forward(opt);} + return f(this->as_ok()); + } + template + return_type_of_t + map_or_else(F&& f, U&& opt) && + { + if(this->is_err()){return std::forward(opt);} + return f(std::move(this->as_ok())); + } + + // prerequisities + // F: E -> U + // retval: U + template + return_type_of_t + map_err_or_else(F&& f, U&& opt) & + { + if(this->is_ok()){return std::forward(opt);} + return f(this->as_err()); + } + template + return_type_of_t + map_err_or_else(F&& f, U&& opt) const& + { + if(this->is_ok()){return std::forward(opt);} + return f(this->as_err()); + } + template + return_type_of_t + map_err_or_else(F&& f, U&& opt) && + { + if(this->is_ok()){return std::forward(opt);} + return f(std::move(this->as_err())); + } + + // prerequisities: + // F: func T -> U + // toml::err(error_type) should be convertible to U. + // normally, type U is another result and E is convertible to F + template + return_type_of_t + and_then(F&& f) & + { + if(this->is_ok()){return f(this->as_ok());} + return err(this->as_err()); + } + template + return_type_of_t + and_then(F&& f) const& + { + if(this->is_ok()){return f(this->as_ok());} + return err(this->as_err()); + } + template + return_type_of_t + and_then(F&& f) && + { + if(this->is_ok()){return f(std::move(this->as_ok()));} + return err(std::move(this->as_err())); + } + + // prerequisities: + // F: func E -> U + // toml::ok(value_type) should be convertible to U. + // normally, type U is another result and T is convertible to S + template + return_type_of_t + or_else(F&& f) & + { + if(this->is_err()){return f(this->as_err());} + return ok(this->as_ok()); + } + template + return_type_of_t + or_else(F&& f) const& + { + if(this->is_err()){return f(this->as_err());} + return ok(this->as_ok()); + } + template + return_type_of_t + or_else(F&& f) && + { + if(this->is_err()){return f(std::move(this->as_err()));} + return ok(std::move(this->as_ok())); + } + + void swap(result& other) + { + result tmp(std::move(*this)); + *this = std::move(other); + other = std::move(tmp); + return ; + } + + private: + + static std::string format_error(std::exception const& excpt) + { + return std::string(excpt.what()); + } + template::value, std::nullptr_t>::type = nullptr> + static std::string format_error(U const& others) + { + std::ostringstream oss; oss << others; + return oss.str(); + } + + void cleanup() noexcept + { + if(this->is_ok_) {this->succ.~success_type();} + else {this->fail.~failure_type();} + return; + } + + private: + + bool is_ok_; + union + { + success_type succ; + failure_type fail; + }; +}; + +template +void swap(result& lhs, result& rhs) +{ + lhs.swap(rhs); + return; +} + +} // toml11 +#endif// TOML11_RESULT_H diff --git a/toml/storage.hpp b/toml/storage.hpp new file mode 100644 index 00000000..30408f97 --- /dev/null +++ b/toml/storage.hpp @@ -0,0 +1,44 @@ +// Copyright Toru Niina 2017. +// Distributed under the MIT License. +#ifndef TOML11_STORAGE_HPP +#define TOML11_STORAGE_HPP +#include "utility.hpp" + +namespace toml +{ +namespace detail +{ + +// this contains pointer and deep-copy the content if copied. +// to avoid recursive pointer. +template +struct storage +{ + using value_type = T; + + storage(value_type const& v): ptr(toml::make_unique(v)) {} + storage(value_type&& v): ptr(toml::make_unique(std::move(v))) {} + ~storage() = default; + + storage(const storage& rhs): ptr(toml::make_unique(*rhs.ptr)) {} + storage& operator=(const storage& rhs) + { + this->ptr = toml::make_unique(*rhs.ptr); + return *this; + } + storage(storage&&) = default; + storage& operator=(storage&&) = default; + + bool is_ok() const noexcept {return static_cast(ptr);} + + value_type& value() & noexcept {return *ptr;} + value_type const& value() const& noexcept {return *ptr;} + value_type&& value() && noexcept {return std::move(*ptr);} + + private: + std::unique_ptr ptr; +}; + +} // detail +} // toml +#endif// TOML11_STORAGE_HPP diff --git a/toml/string.hpp b/toml/string.hpp new file mode 100644 index 00000000..eef932d5 --- /dev/null +++ b/toml/string.hpp @@ -0,0 +1,133 @@ +// Copyright Toru Niina 2017. +// Distributed under the MIT License. +#ifndef TOML11_STRING_H +#define TOML11_STRING_H +#include +#include + +namespace toml +{ + +enum class string_t : std::uint8_t +{ + basic = 0, + literal = 1, +}; + +struct string +{ + string() = default; + ~string() = default; + string(const string& s) = default; + string(string&& s) = default; + string& operator=(const string& s) = default; + string& operator=(string&& s) = default; + + string(const std::string& s): kind(string_t::basic), str(s){} + string(const std::string& s, string_t k): kind(k), str(s){} + string(const char* s): kind(string_t::basic), str(s){} + string(const char* s, string_t k): kind(k), str(s){} + + string(std::string&& s): kind(string_t::basic), str(std::move(s)){} + string(std::string&& s, string_t k): kind(k), str(std::move(s)){} + + string& operator=(const std::string& s) + {kind = string_t::basic; str = s; return *this;} + string& operator=(std::string&& s) + {kind = string_t::basic; str = std::move(s); return *this;} + + operator std::string& () & noexcept {return str;} + operator std::string const& () const& noexcept {return str;} + operator std::string&& () && noexcept {return std::move(str);} + + string_t kind; + std::string str; +}; + +inline bool operator==(const string& lhs, const string& rhs) +{ + return lhs.kind == rhs.kind && lhs.str == rhs.str; +} +inline bool operator!=(const string& lhs, const string& rhs) +{ + return !(lhs == rhs); +} +inline bool operator<(const string& lhs, const string& rhs) +{ + return (lhs.kind == rhs.kind) ? (lhs.str < rhs.str) : (lhs.kind < rhs.kind); +} +inline bool operator>(const string& lhs, const string& rhs) +{ + return rhs < lhs; +} +inline bool operator<=(const string& lhs, const string& rhs) +{ + return !(rhs < lhs); +} +inline bool operator>=(const string& lhs, const string& rhs) +{ + return !(lhs < rhs); +} + +inline bool +operator==(const string& lhs, const std::string& rhs) {return lhs.str == rhs;} +inline bool +operator!=(const string& lhs, const std::string& rhs) {return lhs.str != rhs;} +inline bool +operator< (const string& lhs, const std::string& rhs) {return lhs.str < rhs;} +inline bool +operator> (const string& lhs, const std::string& rhs) {return lhs.str > rhs;} +inline bool +operator<=(const string& lhs, const std::string& rhs) {return lhs.str <= rhs;} +inline bool +operator>=(const string& lhs, const std::string& rhs) {return lhs.str >= rhs;} + +inline bool +operator==(const std::string& lhs, const string& rhs) {return lhs == rhs.str;} +inline bool +operator!=(const std::string& lhs, const string& rhs) {return lhs != rhs.str;} +inline bool +operator< (const std::string& lhs, const string& rhs) {return lhs < rhs.str;} +inline bool +operator> (const std::string& lhs, const string& rhs) {return lhs > rhs.str;} +inline bool +operator<=(const std::string& lhs, const string& rhs) {return lhs <= rhs.str;} +inline bool +operator>=(const std::string& lhs, const string& rhs) {return lhs >= rhs.str;} + +inline bool +operator==(const string& lhs, const char* rhs) {return lhs.str == std::string(rhs);} +inline bool +operator!=(const string& lhs, const char* rhs) {return lhs.str != std::string(rhs);} +inline bool +operator< (const string& lhs, const char* rhs) {return lhs.str < std::string(rhs);} +inline bool +operator> (const string& lhs, const char* rhs) {return lhs.str > std::string(rhs);} +inline bool +operator<=(const string& lhs, const char* rhs) {return lhs.str <= std::string(rhs);} +inline bool +operator>=(const string& lhs, const char* rhs) {return lhs.str >= std::string(rhs);} + +inline bool +operator==(const char* lhs, const string& rhs) {return std::string(lhs) == rhs.str;} +inline bool +operator!=(const char* lhs, const string& rhs) {return std::string(lhs) != rhs.str;} +inline bool +operator< (const char* lhs, const string& rhs) {return std::string(lhs) < rhs.str;} +inline bool +operator> (const char* lhs, const string& rhs) {return std::string(lhs) > rhs.str;} +inline bool +operator<=(const char* lhs, const string& rhs) {return std::string(lhs) <= rhs.str;} +inline bool +operator>=(const char* lhs, const string& rhs) {return std::string(lhs) >= rhs.str;} + +template +std::basic_ostream& +operator<<(std::basic_ostream& os, const string& str) +{ + os << str.str; + return os; +} + +} // toml +#endif// TOML11_STRING_H diff --git a/toml/to_toml.hpp b/toml/to_toml.hpp index 9b8cde7a..348ce478 100644 --- a/toml/to_toml.hpp +++ b/toml/to_toml.hpp @@ -1,3 +1,5 @@ +// Copyright Toru Niina 2017. +// Distributed under the MIT License. #ifndef TOML11_TO_TOML #define TOML11_TO_TOML #include "value.hpp" @@ -5,76 +7,36 @@ namespace toml { -template::value, std::nullptr_t>::type = nullptr> -inline value to_toml(const T& x) -{ - return value(x); -} - -template>, std::is_integral - >::value, std::nullptr_t>::type = nullptr> -inline value to_toml(const T& x) -{ - return value(::toml::Integer(x)); -} - -template>, std::is_floating_point - >::value, std::nullptr_t>::type = nullptr> -inline value to_toml(const T& x) -{ - return value(::toml::Float(x)); -} - -inline value to_toml(const char* str) +template +inline value to_toml(T&& x) { - return value(::toml::String(str)); + return value(std::forward(x)); } -template>, detail::is_container - >::value, std::nullptr_t>::type = nullptr> -value to_toml(const T& x) +template +inline value to_toml(T&& x, string_t kind) { - Array tmp; - tmp.reserve(std::distance(std::begin(x), std::end(x))); - for(auto iter = std::begin(x); iter != std::end(x); ++iter) - { - tmp.emplace_back(*iter); - } - return value(std::move(tmp)); + return value(std::forward(x), kind); } -template>, detail::is_map - >::value, std::nullptr_t>::type = nullptr> -value to_toml(const T& x) +inline value to_toml(local_date d, local_time t) { - Table tmp; - for(auto iter = std::begin(x); iter != std::end(x); ++iter) - { - tmp.emplace(iter->first, to_toml(iter->second)); - } - return value(std::move(tmp)); + return value(local_datetime(d, t)); } - -template -inline value to_toml(std::initializer_list init) +inline value to_toml(local_date d, local_time t, time_offset ofs) { - return value(std::move(init)); + return value(offset_datetime(d, t, ofs)); } -inline value to_toml(std::initializer_list> init) +template +inline value to_toml(Ts&& ... xs) { - return value(std::move(init)); + return value(toml::array{toml::value(std::forward(xs)) ... }); } -template -inline value to_toml(const value& x) +inline value to_toml(std::initializer_list> xs) { - return x; + return value(toml::table(xs.begin(), xs.end())); } } // toml diff --git a/toml/traits.hpp b/toml/traits.hpp index 6a347e87..fc4ef853 100644 --- a/toml/traits.hpp +++ b/toml/traits.hpp @@ -1,7 +1,10 @@ +// Copyright Toru Niina 2017. +// Distributed under the MIT License. #ifndef TOML11_TRAITS #define TOML11_TRAITS #include #include +#include #include namespace toml @@ -85,6 +88,10 @@ template struct is_std_tuple : std::false_type{}; template struct is_std_tuple> : std::true_type{}; +template struct is_chrono_duration: std::false_type{}; +template +struct is_chrono_duration>: std::true_type{}; + // to use toml::get> in C++11 template struct index_sequence{}; diff --git a/toml/types.hpp b/toml/types.hpp index d822823b..cd13edfb 100644 --- a/toml/types.hpp +++ b/toml/types.hpp @@ -1,10 +1,12 @@ +// Copyright Toru Niina 2017. +// Distributed under the MIT License. #ifndef TOML11_TYPES_H #define TOML11_TYPES_H #include "datetime.hpp" -#include +#include "string.hpp" +#include "traits.hpp" #include #include -#include namespace toml { @@ -12,66 +14,72 @@ namespace toml using character = char; class value; -using key = std::basic_string; +using key = std::string; -using Boolean = bool; -using Integer = std::int64_t; -using Float = double; -using String = std::basic_string; -using Datetime = basic_datetime; -using Array = std::vector; -using Table = std::unordered_map; +using Boolean = bool; +using Integer = std::int64_t; +using Float = double; +using String = ::toml::string; +using Datetime = offset_datetime; +using OffsetDatetime = offset_datetime; +using LocalDatetime = local_datetime; +using LocalDate = local_date; +using LocalTime = local_time; +using Array = std::vector; +using Table = std::unordered_map; + +// alias for snake_case, consistency with STL/Boost, toml::key, toml::value +using boolean = Boolean; +using integer = Integer; +using floating = Float; // XXX `float` is keyword. we can't use it here +using array = Array; +using table = Table; enum class value_t : std::uint8_t { - Boolean = 1, - Integer = 2, - Float = 3, - String = 4, - Datetime = 5, - Array = 6, - Table = 7, - Empty = 0, - Unknown = 255, + Empty = 0, + Boolean = 1, + Integer = 2, + Float = 3, + String = 4, + OffsetDatetime = 5, + LocalDatetime = 6, + LocalDate = 7, + LocalTime = 8, + Array = 9, + Table = 10, + Unknown = 255, }; -template> +template inline std::basic_ostream& operator<<(std::basic_ostream& os, value_t t) { switch(t) { - case toml::value_t::Boolean : os << "Boolean"; return os; - case toml::value_t::Integer : os << "Integer"; return os; - case toml::value_t::Float : os << "Float"; return os; - case toml::value_t::String : os << "String"; return os; - case toml::value_t::Datetime: os << "Datetime"; return os; - case toml::value_t::Array : os << "Array"; return os; - case toml::value_t::Table : os << "Table"; return os; - case toml::value_t::Empty : os << "Empty"; return os; - case toml::value_t::Unknown : os << "Unknown"; return os; - default : os << "Nothing"; return os; + case toml::value_t::Boolean : os << "boolean"; return os; + case toml::value_t::Integer : os << "integer"; return os; + case toml::value_t::Float : os << "float"; return os; + case toml::value_t::String : os << "string"; return os; + case toml::value_t::OffsetDatetime: os << "offset_datetime"; return os; + case toml::value_t::LocalDatetime : os << "local_datetime"; return os; + case toml::value_t::LocalDate : os << "local_date"; return os; + case toml::value_t::LocalTime : os << "local_time"; return os; + case toml::value_t::Array : os << "array"; return os; + case toml::value_t::Table : os << "table"; return os; + case toml::value_t::Empty : os << "empty"; return os; + case toml::value_t::Unknown : os << "unknown"; return os; + default : os << "nothing"; return os; } } template, typename alloc = std::allocator> -inline std::basic_string -stringize(value_t t) +inline std::basic_string stringize(value_t t) { - switch(t) - { - case toml::value_t::Boolean : return "Boolean"; - case toml::value_t::Integer : return "Integer"; - case toml::value_t::Float : return "Float"; - case toml::value_t::String : return "String"; - case toml::value_t::Datetime: return "Datetime"; - case toml::value_t::Array : return "Array"; - case toml::value_t::Table : return "Table"; - case toml::value_t::Empty : return "Empty"; - case toml::value_t::Unknown : return "Unknown"; - default : return "Nothing"; - } + std::ostringstream oss; + oss << t; + return oss.str(); } namespace detail @@ -80,13 +88,22 @@ namespace detail template constexpr inline value_t check_type() { - return std::is_same, toml::Boolean >::value ? value_t::Boolean : - std::is_integral>::value ? value_t::Integer : - std::is_floating_point>::value ? value_t::Float : - std::is_convertible, toml::String >::value ? value_t::String : - std::is_convertible, toml::Datetime>::value ? value_t::Datetime: - std::is_convertible, toml::Array >::value ? value_t::Array : - std::is_convertible, toml::Table >::value ? value_t::Table : + using type = typename std::remove_cv< + typename std::remove_reference::type + >::type; + return std::is_same::value ? value_t::Boolean : + std::is_integral::value ? value_t::Integer : + std::is_floating_point::value ? value_t::Float : + std::is_same::value ? value_t::String : + std::is_same::value ? value_t::String : + std::is_same::value ? value_t::LocalDate : + std::is_same::value ? value_t::LocalTime : + is_chrono_duration::value ? value_t::LocalTime : + std::is_same::value ? value_t::LocalDatetime : + std::is_same::value ? value_t::OffsetDatetime : + std::is_same::value ? value_t::OffsetDatetime : + std::is_convertible::value ? value_t::Array : + std::is_convertible::value ? value_t::Table : value_t::Unknown; } @@ -95,16 +112,42 @@ constexpr inline bool is_valid(value_t vt) return vt != value_t::Unknown; } -template struct toml_default_type{}; -template<> struct toml_default_type{typedef Boolean type;}; -template<> struct toml_default_type{typedef Integer type;}; -template<> struct toml_default_type{typedef Float type;}; -template<> struct toml_default_type{typedef String type;}; -template<> struct toml_default_type{typedef Datetime type;}; -template<> struct toml_default_type{typedef Array type;}; -template<> struct toml_default_type{typedef Table type;}; -template<> struct toml_default_type{typedef void type;}; -template<> struct toml_default_type{typedef void type;}; +template struct toml_default_type; +template<> struct toml_default_type {typedef boolean type;}; +template<> struct toml_default_type {typedef integer type;}; +template<> struct toml_default_type {typedef floating type;}; +template<> struct toml_default_type {typedef string type;}; +template<> struct toml_default_type{typedef offset_datetime type;}; +template<> struct toml_default_type {typedef local_datetime type;}; +template<> struct toml_default_type {typedef local_date type;}; +template<> struct toml_default_type {typedef local_time type;}; +template<> struct toml_default_type {typedef array type;}; +template<> struct toml_default_type {typedef table type;}; +template<> struct toml_default_type {typedef void type;}; +template<> struct toml_default_type {typedef void type;}; + +template struct toml_value_t {static constexpr value_t value = value_t::Unknown ;}; +template<> struct toml_value_t{static constexpr value_t value = value_t::Boolean ;}; +template<> struct toml_value_t{static constexpr value_t value = value_t::Integer ;}; +template<> struct toml_value_t{static constexpr value_t value = value_t::Float ;}; +template<> struct toml_value_t{static constexpr value_t value = value_t::String ;}; +template<> struct toml_value_t{static constexpr value_t value = value_t::OffsetDatetime;}; +template<> struct toml_value_t{static constexpr value_t value = value_t::LocalDatetime ;}; +template<> struct toml_value_t{static constexpr value_t value = value_t::LocalDate ;}; +template<> struct toml_value_t{static constexpr value_t value = value_t::LocalTime ;}; +template<> struct toml_value_t{static constexpr value_t value = value_t::Array ;}; +template<> struct toml_value_t{static constexpr value_t value = value_t::Table ;}; +template constexpr value_t toml_value_t::value; +constexpr value_t toml_value_t::value; +constexpr value_t toml_value_t::value; +constexpr value_t toml_value_t::value; +constexpr value_t toml_value_t::value; +constexpr value_t toml_value_t::value; +constexpr value_t toml_value_t::value; +constexpr value_t toml_value_t::value; +constexpr value_t toml_value_t::value; +constexpr value_t toml_value_t::value; +constexpr value_t toml_value_t
::value; template struct is_exact_toml_type : disjunction< @@ -112,10 +155,17 @@ struct is_exact_toml_type : disjunction< std::is_same, std::is_same, std::is_same, - std::is_same, + std::is_same, + std::is_same, + std::is_same, + std::is_same, std::is_same, std::is_same >{}; +template struct is_exact_toml_type : is_exact_toml_type{}; +template struct is_exact_toml_type : is_exact_toml_type{}; +template struct is_exact_toml_type : is_exact_toml_type{}; +template struct is_exact_toml_type: is_exact_toml_type{}; template struct is_map : conjunction< @@ -124,14 +174,23 @@ struct is_map : conjunction< has_key_type, has_mapped_type >{}; +template struct is_map : is_map{}; +template struct is_map : is_map{}; +template struct is_map : is_map{}; +template struct is_map : is_map{}; template struct is_container : conjunction< negation>, - negation>, + negation>, has_iterator, has_value_type >{}; +template struct is_container : is_container{}; +template struct is_container : is_container{}; +template struct is_container : is_container{}; +template struct is_container : is_container{}; + } // detail } // toml diff --git a/toml/utility.hpp b/toml/utility.hpp index 0df0d07c..66b19855 100644 --- a/toml/utility.hpp +++ b/toml/utility.hpp @@ -1,8 +1,11 @@ +// Copyright Toru Niina 2017. +// Distributed under the MIT License. #ifndef TOML11_UTILITY #define TOML11_UTILITY #include "traits.hpp" #include #include +#include namespace toml { @@ -30,7 +33,7 @@ inline void resize_impl(T& container, std::size_t N, std::false_type) else throw std::invalid_argument("not resizable type"); } -} +} // detail template inline void resize(T& container, std::size_t N) @@ -39,5 +42,38 @@ inline void resize(T& container, std::size_t N) else return detail::resize_impl(container, N, detail::has_resize_method()); } +namespace detail +{ +inline std::string concat_to_string_impl(std::ostringstream& oss) +{ + return oss.str(); +} +template +std::string concat_to_string_impl(std::ostringstream& oss, T&& head, Ts&& ... tail) +{ + oss << std::forward(head); + return concat_to_string_impl(oss, std::forward(tail) ... ); +} +} // detail + +template +std::string concat_to_string(Ts&& ... args) +{ + std::ostringstream oss; + oss << std::boolalpha << std::fixed; + return detail::concat_to_string_impl(oss, std::forward(args) ...); +} + +template +T from_string(const std::string& str, U&& opt) +{ + T v(std::forward(opt)); + std::istringstream iss(str); + iss >> v; + return v; +} + + + }// toml #endif // TOML11_UTILITY diff --git a/toml/value.hpp b/toml/value.hpp index 67d0c6bf..68c063ab 100644 --- a/toml/value.hpp +++ b/toml/value.hpp @@ -1,8 +1,12 @@ +// Copyright Toru Niina 2017. +// Distributed under the MIT License. #ifndef TOML11_VALUE #define TOML11_VALUE #include "traits.hpp" #include "utility.hpp" #include "exception.hpp" +#include "storage.hpp" +#include "region.hpp" #include "types.hpp" #include #include @@ -15,35 +19,11 @@ namespace toml namespace detail { -struct storage_base -{ - storage_base(): type(toml::value_t::Empty){} - storage_base(toml::value_t t): type(t){} - virtual ~storage_base() = default; - toml::value_t type; -}; - -template -struct storage : public storage_base -{ - static_assert(std::is_same::value || - std::is_same::value, - "toml::detail::storage is for toml::Array or toml::Table!"); - typedef T value_type; - - storage() = default; - ~storage() noexcept override = default; - storage(storage const&) = default; - storage(storage&&) = default; - storage& operator=(storage const&) = default; - storage& operator=(storage&&) = default; - - storage(value_type const& v) : value(v){} - storage(value_type&& v) : value(std::move(v)){} - - value_type value; -}; -} // detail +// to show error messages. not recommended for users. +region_base const& get_region(const value&); +// ditto. +void assign_keeping_region(value&, value); +}// detail template struct value_traits @@ -59,482 +39,694 @@ constexpr bool value_traits::is_toml_type; class value { - typedef std::unique_ptr storage_ptr; + template + static void assigner(T& dst, U&& v) + { + const auto tmp = ::new(std::addressof(dst)) T(std::forward(v)); + assert(tmp == std::addressof(dst)); + } + + using region_base = detail::region_base; public: - value() : type_(value_t::Empty){} - ~value(); + value() noexcept + : type_(value_t::Empty), + region_info_(std::make_shared(region_base{})) + {} - value(const value& v); - value(value&& v); - value& operator=(const value& v); - value& operator=(value&& v); + ~value() noexcept {this->cleanup();} - template::is_toml_type, std::nullptr_t>::type = nullptr> - value(T&& v); + value(const value& v): type_(v.type()), region_info_(v.region_info_) + { + switch(v.type()) + { + case value_t::Boolean : assigner(boolean_ , v.boolean_ ); break; + case value_t::Integer : assigner(integer_ , v.integer_ ); break; + case value_t::Float : assigner(floating_ , v.floating_ ); break; + case value_t::String : assigner(string_ , v.string_ ); break; + case value_t::OffsetDatetime: assigner(offset_datetime_, v.offset_datetime_); break; + case value_t::LocalDatetime : assigner(local_datetime_ , v.local_datetime_ ); break; + case value_t::LocalDate : assigner(local_date_ , v.local_date_ ); break; + case value_t::LocalTime : assigner(local_time_ , v.local_time_ ); break; + case value_t::Array : assigner(array_ , v.array_ ); break; + case value_t::Table : assigner(table_ , v.table_ ); break; + default: break; + } + } + value(value&& v): type_(v.type()), region_info_(std::move(v.region_info_)) + { + switch(this->type_) + { + case value_t::Boolean : assigner(boolean_ , std::move(v.boolean_ )); break; + case value_t::Integer : assigner(integer_ , std::move(v.integer_ )); break; + case value_t::Float : assigner(floating_ , std::move(v.floating_ )); break; + case value_t::String : assigner(string_ , std::move(v.string_ )); break; + case value_t::OffsetDatetime: assigner(offset_datetime_, std::move(v.offset_datetime_)); break; + case value_t::LocalDatetime : assigner(local_datetime_ , std::move(v.local_datetime_ )); break; + case value_t::LocalDate : assigner(local_date_ , std::move(v.local_date_ )); break; + case value_t::LocalTime : assigner(local_time_ , std::move(v.local_time_ )); break; + case value_t::Array : assigner(array_ , std::move(v.array_ )); break; + case value_t::Table : assigner(table_ , std::move(v.table_ )); break; + default: break; + } + } + value& operator=(const value& v) + { + this->cleanup(); + this->region_info_ = v.region_info_; + this->type_ = v.type(); + switch(this->type_) + { + case value_t::Boolean : assigner(boolean_ , v.boolean_ ); break; + case value_t::Integer : assigner(integer_ , v.integer_ ); break; + case value_t::Float : assigner(floating_ , v.floating_ ); break; + case value_t::String : assigner(string_ , v.string_ ); break; + case value_t::OffsetDatetime: assigner(offset_datetime_, v.offset_datetime_); break; + case value_t::LocalDatetime : assigner(local_datetime_ , v.local_datetime_ ); break; + case value_t::LocalDate : assigner(local_date_ , v.local_date_ ); break; + case value_t::LocalTime : assigner(local_time_ , v.local_time_ ); break; + case value_t::Array : assigner(array_ , v.array_ ); break; + case value_t::Table : assigner(table_ , v.table_ ); break; + default: break; + } + return *this; + } + value& operator=(value&& v) + { + this->cleanup(); + this->region_info_ = std::move(v.region_info_); + this->type_ = v.type(); + switch(this->type_) + { + case value_t::Boolean : assigner(boolean_ , std::move(v.boolean_ )); break; + case value_t::Integer : assigner(integer_ , std::move(v.integer_ )); break; + case value_t::Float : assigner(floating_ , std::move(v.floating_ )); break; + case value_t::String : assigner(string_ , std::move(v.string_ )); break; + case value_t::OffsetDatetime: assigner(offset_datetime_, std::move(v.offset_datetime_)); break; + case value_t::LocalDatetime : assigner(local_datetime_ , std::move(v.local_datetime_ )); break; + case value_t::LocalDate : assigner(local_date_ , std::move(v.local_date_ )); break; + case value_t::LocalTime : assigner(local_time_ , std::move(v.local_time_ )); break; + case value_t::Array : assigner(array_ , std::move(v.array_ )); break; + case value_t::Table : assigner(table_ , std::move(v.table_ )); break; + default: break; + } + return *this; + } - template::is_toml_type, std::nullptr_t>::type = nullptr> - value& operator=(T&& v); + // boolean ============================================================== - template::is_toml_type, std::nullptr_t>::type = nullptr> - value(std::initializer_list init); + value(boolean b) + : type_(value_t::Boolean), + region_info_(std::make_shared(region_base{})) + { + assigner(this->boolean_, b); + } - value(std::initializer_list> init); + value& operator=(boolean b) + { + this->cleanup(); + this->type_ = value_t::Boolean; + this->region_info_ = std::make_shared(region_base{}); + assigner(this->boolean_, b); + return *this; + } - value_t type() const {return type_;} + template + value(boolean b, detail::region reg) + : type_(value_t::Boolean), + region_info_(std::make_shared>(std::move(reg))) + { + assigner(this->boolean_, b); + } - template - typename detail::toml_default_type::type const& cast() const; - template - typename detail::toml_default_type::type& cast(); + // integer ============================================================== - private: + template, detail::negation>>::value, + std::nullptr_t>::type = nullptr> + value(T i) + : type_(value_t::Integer), + region_info_(std::make_shared(region_base{})) + { + assigner(this->integer_, static_cast(i)); + } - void switch_clean(value_t t); - template struct switch_assign; - template struct switch_cast; + template, + detail::negation> + >::value, std::nullptr_t>::type = nullptr> + value(T i, detail::region reg) + : type_(value_t::Integer), + region_info_(std::make_shared>(std::move(reg))) + { + assigner(this->integer_, static_cast(i)); + } - static bool should_be_cleaned(value_t vt) + template, detail::negation>>::value, + std::nullptr_t>::type = nullptr> + value& operator=(T i) { - return (vt == value_t::String) || (vt == value_t::Array) || - (vt == value_t::Table) || (vt == value_t::Datetime); + this->cleanup(); + this->type_ = value_t::Integer; + this->region_info_ = std::make_shared(region_base{}); + assigner(this->integer_, static_cast(i)); + return *this; } - private: + // floating ============================================================= - value_t type_; - union + template::value, std::nullptr_t>::type = nullptr> + value(T f) + : type_(value_t::Float), + region_info_(std::make_shared(region_base{})) { - Boolean boolean_; - Integer integer_; - Float float_; - String string_; - Datetime datetime_; - storage_ptr storage_; //ptr to table or array - }; -}; + assigner(this->floating_, f); + } -template<> struct value::switch_assign -{ - template - static void invoke(value& v, valT&& val) + template::value, std::nullptr_t>::type = nullptr> + value(T f, detail::region reg) + : type_(value_t::Float), + region_info_(std::make_shared>(std::move(reg))) { - v.boolean_ = static_cast(val); + assigner(this->floating_, f); } -}; -template<> struct value::switch_assign -{ - template - static void invoke(value& v, valT&& val) + + template::value, std::nullptr_t>::type = nullptr> + value& operator=(T f) { - v.integer_ = static_cast(val); + this->cleanup(); + this->type_ = value_t::Float; + this->region_info_ = std::make_shared(region_base{}); + assigner(this->floating_, f); + return *this; } -}; -template<> struct value::switch_assign -{ - template - static void invoke(value& v, valT&& val) + + // string =============================================================== + + value(toml::string s) + : type_(value_t::String), + region_info_(std::make_shared(region_base{})) { - v.float_ = static_cast(val); + assigner(this->string_, std::move(s)); } -}; -template<> struct value::switch_assign -{ - template - static void invoke(value& v, valT&& val) + template + value(toml::string s, detail::region reg) + : type_(value_t::String), + region_info_(std::make_shared>(std::move(reg))) { - new(&v.string_) String(val); + assigner(this->string_, std::move(s)); } -}; -template<> struct value::switch_assign -{ - template - static void invoke(value& v, valT&& val) + value& operator=(toml::string s) { - new(&v.datetime_) Datetime(val); + this->cleanup(); + this->type_ = value_t::String; + this->region_info_ = std::make_shared(region_base{}); + assigner(this->string_, s); + return *this; } -}; -template<> struct value::switch_assign -{ - template - static void invoke(value& v, valT&& val) + + value(std::string s) + : type_(value_t::String), + region_info_(std::make_shared(region_base{})) { - new(&v.storage_) storage_ptr( - toml::make_unique>(val)); + assigner(this->string_, toml::string(std::move(s))); } -}; -template<> struct value::switch_assign -{ - template - static void invoke(value& v, valT&& val) + value& operator=(std::string s) { - new(&v.storage_) storage_ptr( - toml::make_unique>(val)); + this->cleanup(); + this->type_ = value_t::String; + this->region_info_ = std::make_shared(region_base{}); + assigner(this->string_, toml::string(std::move(s))); + return *this; + } + value(std::string s, string_t kind) + : type_(value_t::String), + region_info_(std::make_shared(region_base{})) + { + assigner(this->string_, toml::string(std::move(s), kind)); } -}; -template<> struct value::switch_cast -{ - static Boolean& invoke(value& v) {return v.boolean_;} - static Boolean const& invoke(value const& v) {return v.boolean_;} -}; -template<> struct value::switch_cast -{ - static Integer& invoke(value& v) {return v.integer_;} - static Integer const& invoke(value const& v) {return v.integer_;} -}; -template<> struct value::switch_cast -{ - static Float& invoke(value& v) {return v.float_;} - static Float const& invoke(value const& v) {return v.float_;} -}; -template<> struct value::switch_cast -{ - static String& invoke(value& v) {return v.string_;} - static String const& invoke(value const& v) {return v.string_;} -}; -template<> struct value::switch_cast -{ - static Datetime& invoke(value& v) {return v.datetime_;} - static Datetime const& invoke(value const& v) {return v.datetime_;} -}; -template<> struct value::switch_cast -{ - // switch_cast assumes tmeplate argument is correct. - // if not, the behaviour is undefined. - static Array& invoke(value& v) + value(const char* s) + : type_(value_t::String), + region_info_(std::make_shared(region_base{})) { - return static_cast*>(v.storage_.get())->value; + assigner(this->string_, toml::string(std::string(s))); } - static Array const& invoke(value const& v) + value& operator=(const char* s) { - return static_cast*>(v.storage_.get())->value; + this->cleanup(); + this->type_ = value_t::String; + this->region_info_ = std::make_shared(region_base{}); + assigner(this->string_, toml::string(std::string(s))); + return *this; } -}; -template<> struct value::switch_cast -{ - static Table& invoke(value& v) + value(const char* s, string_t kind) + : type_(value_t::String), + region_info_(std::make_shared(region_base{})) { - return static_cast*>(v.storage_.get())->value; + assigner(this->string_, toml::string(std::string(s), kind)); } - static Table const& invoke(value const& v) + + // local date =========================================================== + + value(const local_date& ld) + : type_(value_t::LocalDate), + region_info_(std::make_shared(region_base{})) { - return static_cast*>(v.storage_.get())->value; + assigner(this->local_date_, ld); + } + template + value(const local_date& ld, detail::region reg) + : type_(value_t::LocalDate), + region_info_(std::make_shared>(std::move(reg))) + { + assigner(this->local_date_, ld); + } + value& operator=(const local_date& ld) + { + this->cleanup(); + this->type_ = value_t::LocalDate; + this->region_info_ = std::make_shared(region_base{}); + assigner(this->local_date_, ld); + return *this; } -}; -inline void value::switch_clean(value_t t) -{ - switch(t) - { - case value_t::Boolean : {boolean_.~Boolean(); return;} - case value_t::Integer : {integer_.~Integer(); return;} - case value_t::Float : {float_.~Float(); return;} - case value_t::String : {string_.~String(); return;} - case value_t::Datetime : {datetime_.~Datetime(); return;} - case value_t::Array : {storage_.~storage_ptr(); return;} - case value_t::Table : {storage_.~storage_ptr(); return;} - case value_t::Empty : return; - case value_t::Unknown : assert(false); - default : assert(false); + // local time =========================================================== + + value(const local_time& lt) + : type_(value_t::LocalTime), + region_info_(std::make_shared(region_base{})) + { + assigner(this->local_time_, lt); + } + template + value(const local_time& lt, detail::region reg) + : type_(value_t::LocalTime), + region_info_(std::make_shared>(std::move(reg))) + { + assigner(this->local_time_, lt); + } + value& operator=(const local_time& lt) + { + this->cleanup(); + this->type_ = value_t::LocalTime; + this->region_info_ = std::make_shared(region_base{}); + assigner(this->local_time_, lt); + return *this; + } + template + value(const std::chrono::duration& dur) + : type_(value_t::LocalTime), + region_info_(std::make_shared(region_base{})) + { + assigner(this->local_time_, local_time(dur)); + } + template + value& operator=(const std::chrono::duration& dur) + { + this->cleanup(); + this->type_ = value_t::LocalTime; + this->region_info_ = std::make_shared(region_base{}); + assigner(this->local_time_, local_time(dur)); + return *this; } -} -inline value::~value() -{ - switch_clean(this->type_); -} + // local datetime ======================================================= -inline value::value(const value& v) : type_(v.type()) -{ - switch(v.type()) + value(const local_datetime& ldt) + : type_(value_t::LocalDatetime), + region_info_(std::make_shared(region_base{})) { - case value_t::Boolean : - { - switch_assign::invoke( - *this, v.cast()); - break; - } - case value_t::Integer : - { - switch_assign::invoke( - *this, v.cast()); - break; - } - case value_t::Float : - { - switch_assign::invoke( - *this, v.cast()); - break; - } - case value_t::String : - { - switch_assign::invoke( - *this, v.cast()); - break; - } - case value_t::Datetime: - { - switch_assign::invoke( - *this, v.cast()); - break; - } - case value_t::Array : - { - switch_assign::invoke( - *this, v.cast()); - break; - } - case value_t::Table : - { - switch_assign::invoke( - *this, v.cast()); - break; - } - case value_t::Empty : break; - case value_t::Unknown : assert(false); - default: assert(false); + assigner(this->local_datetime_, ldt); + } + template + value(const local_datetime& ldt, detail::region reg) + : type_(value_t::LocalDatetime), + region_info_(std::make_shared>(std::move(reg))) + { + assigner(this->local_datetime_, ldt); + } + value& operator=(const local_datetime& ldt) + { + this->cleanup(); + this->type_ = value_t::LocalDatetime; + this->region_info_ = std::make_shared(region_base{}); + assigner(this->local_datetime_, ldt); + return *this; } -} -inline value::value(value&& v) -{ - this->type_ = v.type_; - switch(this->type_) + // offset datetime ====================================================== + + value(const offset_datetime& odt) + : type_(value_t::OffsetDatetime), + region_info_(std::make_shared(region_base{})) { - case value_t::Boolean : - { - switch_assign::invoke(*this, - std::move(v.cast())); - break; - } - case value_t::Integer : - { - switch_assign::invoke(*this, - std::move(v.cast())); - break; - } - case value_t::Float : - { - switch_assign::invoke(*this, - std::move(v.cast())); - break; - } - case value_t::String : - { - switch_assign::invoke(*this, - std::move(v.cast())); - break; - } - case value_t::Datetime: - { - switch_assign::invoke(*this, - std::move(v.cast())); - break; - } - case value_t::Array : - { - new(&this->storage_) storage_ptr(std::move(v.storage_)); - break; - } - case value_t::Table : - { - new(&this->storage_) storage_ptr(std::move(v.storage_)); - break; - } - case value_t::Empty : break; - case value_t::Unknown : assert(false); - default: assert(false); + assigner(this->offset_datetime_, odt); + } + template + value(const offset_datetime& odt, detail::region reg) + : type_(value_t::OffsetDatetime), + region_info_(std::make_shared>(std::move(reg))) + { + assigner(this->offset_datetime_, odt); + } + value& operator=(const offset_datetime& odt) + { + this->cleanup(); + this->type_ = value_t::OffsetDatetime; + this->region_info_ = std::make_shared(region_base{}); + assigner(this->offset_datetime_, odt); + return *this; + } + value(const std::chrono::system_clock::time_point& tp) + : type_(value_t::OffsetDatetime), + region_info_(std::make_shared(region_base{})) + { + assigner(this->offset_datetime_, offset_datetime(tp)); + } + value& operator=(const std::chrono::system_clock::time_point& tp) + { + this->cleanup(); + this->type_ = value_t::OffsetDatetime; + this->region_info_ = std::make_shared(region_base{}); + assigner(this->offset_datetime_, offset_datetime(tp)); + return *this; } -} -inline value& value::operator=(const value& v) -{ - if(should_be_cleaned(this->type_)) + // array ================================================================ + + value(const array& ary) + : type_(value_t::Array), + region_info_(std::make_shared(region_base{})) + { + assigner(this->array_, ary); + } + template + value(const array& ary, detail::region reg) + : type_(value_t::Array), + region_info_(std::make_shared>(std::move(reg))) + { + assigner(this->array_, ary); + } + value& operator=(const array& ary) { - this->switch_clean(this->type_); + this->cleanup(); + this->type_ = value_t::Array; + this->region_info_ = std::make_shared(region_base{}); + assigner(this->array_, ary); + return *this; } - this->type_ = v.type(); - switch(this->type_) + template::is_toml_type, + std::nullptr_t>::type = nullptr> + value(std::initializer_list list) + : type_(value_t::Array), + region_info_(std::make_shared(region_base{})) { - case value_t::Boolean : - { - switch_assign::invoke(*this, - v.cast()); - break; - } - case value_t::Integer : - { - switch_assign::invoke(*this, - v.cast()); - break; - } - case value_t::Float : - { - switch_assign::invoke(*this, - v.cast()); - break; - } - case value_t::String : - { - switch_assign::invoke(*this, - v.cast()); - break; - } - case value_t::Datetime: - { - switch_assign::invoke(*this, - v.cast()); - break; - } - case value_t::Array : - { - switch_assign::invoke(*this, - v.cast()); - break; - } - case value_t::Table : - { - switch_assign::invoke(*this, - v.cast()); - break; - } - case value_t::Empty : break; - case value_t::Unknown : assert(false); - default: assert(false); + array ary; ary.reserve(list.size()); + for(auto& elem : list) {ary.emplace_back(std::move(elem));} + assigner(this->array_, std::move(ary)); + } + template::is_toml_type, + std::nullptr_t>::type = nullptr> + value& operator=(std::initializer_list list) + { + this->cleanup(); + this->type_ = value_t::Array; + this->region_info_ = std::make_shared(region_base{}); + + array ary; ary.reserve(list.size()); + for(auto& elem : list) {ary.emplace_back(std::move(elem));} + assigner(this->array_, std::move(ary)); + return *this; } - return *this; -} -inline value& value::operator=(value&& v) -{ - if(should_be_cleaned(this->type_)) + template::value, + std::nullptr_t>::type = nullptr> + value(T&& list) + : type_(value_t::Array), + region_info_(std::make_shared(region_base{})) + { + array ary; ary.reserve(list.size()); + for(auto& elem : list) {ary.emplace_back(std::move(elem));} + assigner(this->array_, std::move(ary)); + } + template::value, + std::nullptr_t>::type = nullptr> + value& operator=(T&& list) { - this->switch_clean(this->type_); + this->cleanup(); + this->type_ = value_t::Array; + this->region_info_ = std::make_shared(region_base{}); + + array ary; ary.reserve(list.size()); + for(auto& elem : list) {ary.emplace_back(std::move(elem));} + assigner(this->array_, std::move(ary)); + return *this; } - this->type_ = v.type_; - switch(this->type_) + // table ================================================================ + + value(const table& tab) + : type_(value_t::Table), + region_info_(std::make_shared(region_base{})) { - case value_t::Boolean : - { - switch_assign::invoke(*this, - std::move(v.cast())); - break; - } - case value_t::Integer : - { - switch_assign::invoke(*this, - std::move(v.cast())); - break; - } - case value_t::Float : - { - switch_assign::invoke(*this, - std::move(v.cast())); - break; - } - case value_t::String : - { - switch_assign::invoke(*this, - std::move(v.cast())); - break; - } - case value_t::Datetime: - { - switch_assign::invoke(*this, - std::move(v.cast())); - break; - } - case value_t::Array : - { - switch_assign::invoke(*this, - std::move(v.cast())); - break; - } - case value_t::Table : + assigner(this->table_, tab); + } + template + value(const table& tab, detail::region reg) + : type_(value_t::Table), + region_info_(std::make_shared>(std::move(reg))) + { + assigner(this->table_, tab); + } + value& operator=(const table& tab) + { + this->cleanup(); + this->type_ = value_t::Table; + this->region_info_ = std::make_shared(region_base{}); + assigner(this->table_, tab); + return *this; + } + value(std::initializer_list> list) + : type_(value_t::Table), + region_info_(std::make_shared(region_base{})) + { + table tab; + for(const auto& elem : list) {tab[elem.first] = elem.second;} + assigner(this->table_, std::move(tab)); + } + value& operator=(std::initializer_list> list) + { + this->cleanup(); + this->type_ = value_t::Array; + this->region_info_ = std::make_shared(region_base{}); + + table tab; + for(const auto& elem : list) {tab[elem.first] = elem.second;} + assigner(this->table_, std::move(tab)); + return *this; + } + + template + bool is() const noexcept {return value_traits::type_index == this->type_;} + bool is(value_t t) const noexcept {return t == this->type_;} + + value_t type() const {return type_;} + + template + typename detail::toml_default_type::type& cast() &; + template + typename detail::toml_default_type::type const& cast() const&; + template + typename detail::toml_default_type::type&& cast() &&; + + private: + + void cleanup() noexcept + { + switch(this->type_) { - switch_assign::invoke(*this, - std::move(v.cast())); - break; + case value_t::String : {string_.~string(); return;} + case value_t::Array : {array_.~array_storage(); return;} + case value_t::Table : {table_.~table_storage(); return;} + default : return; } - case value_t::Empty : break; - case value_t::Unknown : assert(false); - default: assert(false); } - return *this; -} -template::is_toml_type, std::nullptr_t>::type> -value::value(T&& v) : type_(toml::detail::check_type()) -{ - constexpr value_t kind = toml::detail::check_type(); - switch_assign::invoke(*this, std::forward(v)); -} + // for error messages + friend region_base const& detail::get_region(const value&); + + // to see why it's here, see detail::insert_nested_key. + friend void detail::assign_keeping_region(value&, value); + + template + struct switch_cast; + + private: + + using array_storage = detail::storage; + using table_storage = detail::storage
; + + value_t type_; + + // for error message information. + std::shared_ptr region_info_; -template::is_toml_type, std::nullptr_t>::type> -value& value::operator=(T&& v) + union + { + boolean boolean_; + integer integer_; + floating floating_; + string string_; + offset_datetime offset_datetime_; + local_datetime local_datetime_; + local_date local_date_; + local_time local_time_; + array_storage array_; + table_storage table_; + }; +}; + +namespace detail { - constexpr value_t kind = toml::detail::check_type(); - if(should_be_cleaned(this->type_)) +inline region_base const& get_region(const value& v) +{ + return *(v.region_info_); +} +// If we keep region information after assigning another toml::* types, the +// error message become different from the actual value contained. +// To avoid this kind of confusing phenomena, the default assigners clear the +// 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 + // keep region_info_ intact + v.type_ = other.type(); + switch(v.type()) { - switch_clean(this->type_); + case value_t::Boolean : ::toml::value::assigner(v.boolean_ , other.boolean_ ); break; + case value_t::Integer : ::toml::value::assigner(v.integer_ , other.integer_ ); break; + 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; } - this->type_ = kind; - switch_assign::invoke(*this, std::forward(v)); - return *this; + return; } +}// detail -template::is_toml_type, std::nullptr_t>::type> -value::value(std::initializer_list init) - : type_(toml::value_t::Array) -{ - toml::Array arr; arr.reserve(init.size()); - for(auto&& item : init) - arr.emplace_back(std::move(item)); - switch_assign::invoke(*this, std::move(arr)); -} -inline value::value( - std::initializer_list> init) - : type_(toml::value_t::Table) +template<> struct value::switch_cast { - toml::Table tmp; - for(auto&& item : init) - tmp.emplace(std::move(item.first), std::move(item.second)); - switch_assign::invoke(*this, std::move(tmp)); -} + static Boolean& invoke(value& v) {return v.boolean_;} + static Boolean const& invoke(value const& v) {return v.boolean_;} + static Boolean&& invoke(value&& v) {return std::move(v.boolean_);} +}; +template<> struct value::switch_cast +{ + static Integer& invoke(value& v) {return v.integer_;} + static Integer const& invoke(value const& v) {return v.integer_;} + static Integer&& invoke(value&& v) {return std::move(v.integer_);} +}; +template<> struct value::switch_cast +{ + static Float& invoke(value& v) {return v.floating_;} + static Float const& invoke(value const& v) {return v.floating_;} + static Float&& invoke(value&& v) {return std::move(v.floating_);} +}; +template<> struct value::switch_cast +{ + static String& invoke(value& v) {return v.string_;} + static String const& invoke(value const& v) {return v.string_;} + static String&& invoke(value&& v) {return std::move(v.string_);} +}; +template<> struct value::switch_cast +{ + static OffsetDatetime& invoke(value& v) {return v.offset_datetime_;} + static OffsetDatetime const& invoke(value const& v) {return v.offset_datetime_;} + static OffsetDatetime&& invoke(value&& v) {return std::move(v.offset_datetime_);} +}; +template<> struct value::switch_cast +{ + static LocalDatetime& invoke(value& v) {return v.local_datetime_;} + static LocalDatetime const& invoke(value const& v) {return v.local_datetime_;} + static LocalDatetime&& invoke(value&& v) {return std::move(v.local_datetime_);} +}; +template<> struct value::switch_cast +{ + static LocalDate& invoke(value& v) {return v.local_date_;} + static LocalDate const& invoke(value const& v) {return v.local_date_;} + static LocalDate&& invoke(value&& v) {return std::move(v.local_date_);} +}; +template<> struct value::switch_cast +{ + static LocalTime& invoke(value& v) {return v.local_time_;} + static LocalTime const& invoke(value const& v) {return v.local_time_;} + static LocalTime&& invoke(value&& v) {return std::move(v.local_time_);} +}; +template<> struct value::switch_cast +{ + static Array& invoke(value& v) {return v.array_.value();} + static Array const& invoke(value const& v) {return v.array_.value();} + static Array&& invoke(value&& v) {return std::move(v.array_.value());} +}; +template<> struct value::switch_cast +{ + static Table& invoke(value& v) {return v.table_.value();} + static Table const& invoke(value const& v) {return v.table_.value();} + static Table&& invoke(value&& v) {return std::move(v.table_.value());} +}; template -inline typename detail::toml_default_type::type const& -value::cast() const +typename detail::toml_default_type::type& value::cast() & { if(T != this->type_) - throw type_error("current type: " + stringize(this->type_) + - std::string(" is not query type: ") + stringize(T)); + { + throw type_error(format_underline(concat_to_string( + "[error] toml::value bad_cast to ", T), *region_info_, + concat_to_string("the actual type is ", this->type_))); + } return switch_cast::invoke(*this); } template -inline typename detail::toml_default_type::type& -value::cast() +typename detail::toml_default_type::type const& value::cast() const& { if(T != this->type_) - throw type_error("current type: " + stringize(this->type_) + - std::string(" is not query type: ") + stringize(T)); + { + throw type_error(format_underline(concat_to_string( + "[error] toml::value bad_cast to ", T), *region_info_, + concat_to_string("the actual type is ", this->type_))); + } return switch_cast::invoke(*this); } +template +typename detail::toml_default_type::type&& value::cast() && +{ + if(T != this->type_) + { + throw type_error(format_underline(concat_to_string( + "[error] toml::value bad_cast to ", T), *region_info_, + concat_to_string("the actual type is ", this->type_))); + } + return switch_cast::invoke(std::move(*this)); +} inline bool operator==(const toml::value& lhs, const toml::value& rhs) { - if(lhs.type() != rhs.type()) return false; + if(lhs.type() != rhs.type()){return false;} switch(lhs.type()) { case value_t::Boolean : @@ -545,8 +737,14 @@ inline bool operator==(const toml::value& lhs, const toml::value& rhs) return lhs.cast() == rhs.cast(); case value_t::String : return lhs.cast() == rhs.cast(); - case value_t::Datetime: - return lhs.cast() == rhs.cast(); + case value_t::OffsetDatetime: + return lhs.cast() == rhs.cast(); + case value_t::LocalDatetime: + return lhs.cast() == rhs.cast(); + case value_t::LocalDate: + return lhs.cast() == rhs.cast(); + case value_t::LocalTime: + return lhs.cast() == rhs.cast(); case value_t::Array : return lhs.cast() == rhs.cast(); case value_t::Table : @@ -558,7 +756,7 @@ inline bool operator==(const toml::value& lhs, const toml::value& rhs) } inline bool operator<(const toml::value& lhs, const toml::value& rhs) { - if(lhs.type() != rhs.type()) return (lhs.type() < rhs.type()); + if(lhs.type() != rhs.type()){return (lhs.type() < rhs.type());} switch(lhs.type()) { case value_t::Boolean : @@ -569,8 +767,12 @@ inline bool operator<(const toml::value& lhs, const toml::value& rhs) return lhs.cast() < rhs.cast(); case value_t::String : return lhs.cast() < rhs.cast(); - case value_t::Datetime: - return lhs.cast() < rhs.cast(); + case value_t::LocalDatetime: + return lhs.cast() < rhs.cast(); + case value_t::LocalDate: + return lhs.cast() < rhs.cast(); + case value_t::LocalTime: + return lhs.cast() < rhs.cast(); case value_t::Array : return lhs.cast() < rhs.cast(); case value_t::Table :