From 125f608fa5214b1d3d68bf0cd71950893df11e2b Mon Sep 17 00:00:00 2001 From: ToruNiina Date: Fri, 3 Apr 2020 23:42:58 +0900 Subject: [PATCH 01/12] feat: remove TOML11_UNRELEASED_FEATURES. v1.0.0-rc.1 has been released --- tests/test_error_detection.cpp | 6 +++--- toml/lexer.hpp | 26 ------------------------- toml/parser.hpp | 10 ++++++---- toml/serializer.hpp | 35 ---------------------------------- 4 files changed, 9 insertions(+), 68 deletions(-) diff --git a/tests/test_error_detection.cpp b/tests/test_error_detection.cpp index 49dff6e..07c607f 100644 --- a/tests/test_error_detection.cpp +++ b/tests/test_error_detection.cpp @@ -80,13 +80,13 @@ BOOST_AUTO_TEST_CASE(test_detect_conflicting_value) BOOST_AUTO_TEST_CASE(test_detect_inhomogeneous_array) { -#ifdef TOML11_USE_UNRELEASED_TOML_FEATURES - BOOST_TEST_MESSAGE("heterogeneous array will be allowed in the next release"); -#else +#ifdef TOML11_DISALLOW_HETEROGENEOUS_ARRAYS std::istringstream stream(std::string( "a = [1, 1.0]\n" )); BOOST_CHECK_THROW(toml::parse(stream), toml::syntax_error); +#else + BOOST_TEST_MESSAGE("After v1.0.0-rc.1, heterogeneous arrays are allowed"); #endif } diff --git a/toml/lexer.hpp b/toml/lexer.hpp index 509217a..ee672c1 100644 --- a/toml/lexer.hpp +++ b/toml/lexer.hpp @@ -69,15 +69,8 @@ using lex_zero_prefixable_int = sequence, lex_zero_prefixable_int>; -#ifdef TOML11_USE_UNRELEASED_TOML_FEATURES -// use toml-lang/toml HEAD using lex_exponent_part = sequence, character<'E'>>, maybe, lex_zero_prefixable_int>; -#else -// strictly TOML v0.5.0 -using lex_exponent_part = sequence, character<'E'>>, - lex_dec_int>; -#endif using lex_float = either; -#ifdef TOML11_USE_UNRELEASED_TOML_FEATURES using lex_basic_unescaped = exclude, // 0x09 (tab) in_range<0x0a, 0x1F>, // is allowed character<0x22>, character<0x5C>, character<0x7F>>>; -#else -using lex_basic_unescaped = exclude, - character<0x22>, character<0x5C>, - character<0x7F>>>; -#endif using lex_escape = character<'\\'>; using lex_escape_unicode_short = sequence, repeat>>; @@ -183,12 +170,6 @@ using lex_basic_string = sequence>; using lex_ml_basic_string_open = lex_ml_basic_string_delim; using lex_ml_basic_string_close = sequence< @@ -196,18 +177,11 @@ using lex_ml_basic_string_close = sequence< maybe, maybe >; -#ifdef TOML11_USE_UNRELEASED_TOML_FEATURES using lex_ml_basic_unescaped = exclude, // 0x09 in_range<0x0a, 0x1F>, // is tab character<0x5C>, // backslash character<0x7F>, // DEL lex_ml_basic_string_delim>>; -#else // TOML v0.5.0 -using lex_ml_basic_unescaped = exclude, - character<0x5C>, - character<0x7F>, - lex_ml_basic_string_delim>>; -#endif using lex_ml_basic_escaped_newline = sequence< lex_escape, maybe, lex_newline, diff --git a/toml/parser.hpp b/toml/parser.hpp index 80fe704..bba6b47 100644 --- a/toml/parser.hpp +++ b/toml/parser.hpp @@ -977,7 +977,12 @@ parse_array(location& loc) if(auto val = parse_value(loc)) { -#ifndef TOML11_USE_UNRELEASED_TOML_FEATURES + // After TOML v1.0.0-rc.1, array becomes to be able to have values + // with different types. So here we will omit this by default. + // + // But some of the test-suite checks if the parser accepts a hetero- + // geneous arrays, so we keep this for a while. +#ifdef TOML11_DISALLOW_HETEROGENEOUS_ARRAYS if(!retval.empty() && retval.front().type() != val.as_ok().type()) { auto array_start_loc = loc; @@ -1389,9 +1394,6 @@ insert_nested_key(typename Value::table_type& root, const Value& v, // According to toml-lang/toml:36d3091b3 "Clarify that inline // tables are immutable", check if it adds key-value pair to an // inline table. - // This is one of the unreleased (after-0.5.0) toml feature. - // But this is marked as "Clarify", so TOML-lang intended that - // inline tables are immutable in all version. { // here, if the value is a (multi-line) table, the region // should be something like `[table-name]`. diff --git a/toml/serializer.hpp b/toml/serializer.hpp index ddd082e..46a2b70 100644 --- a/toml/serializer.hpp +++ b/toml/serializer.hpp @@ -135,43 +135,8 @@ struct serializer { // the resulting value does not have any float specific part! token += ".0"; - return token; - } - if(!has_exponent) - { - return token; // there is no exponent part. just return it. - } -#ifdef TOML11_USE_UNRELEASED_TOML_FEATURES - // Although currently it is not released yet as a tagged version, - // TOML will allow zero-prefix in an exponent part, such as `1.234e+01`. - // ```toml - // num1 = 1.234e+1 # OK in TOML v0.5.0 - // num2 = 1.234e+01 # error in TOML v0.5.0 but will be allowed soon - // ``` - // To avoid `e+01`, the following `else` section removes the zero - // prefixes in the exponent part. - // If the feature is activated, it can be skipped. - return token; -#else - // zero-prefix in an exponent is NOT allowed in TOML v0.5.0. - // remove it if it exists. - bool sign_exists = false; - std::size_t zero_prefix = 0; - for(auto iter = std::next(e), iend = token.cend(); iter != iend; ++iter) - { - if(*iter == '+' || *iter == '-'){sign_exists = true; continue;} - if(*iter == '0'){zero_prefix += 1;} - else {break;} - } - if(zero_prefix != 0) - { - const auto offset = std::distance(token.cbegin(), e) + - (sign_exists ? 2 : 1); - token.erase(static_cast(offset), - zero_prefix); } return token; -#endif } std::string operator()(const string_type& s) const { From 7c07f4382c279114c919886c6661820f417d786c Mon Sep 17 00:00:00 2001 From: ToruNiina Date: Fri, 3 Apr 2020 23:43:59 +0900 Subject: [PATCH 02/12] ci: add DISALLOW_HETEROGENEOUS_ARRAYS to toml-test --- .circleci/config.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index 0867ff4..2660506 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -12,7 +12,7 @@ jobs: command: | g++ --version cd tests/ - g++ -std=c++11 -O2 -Wall -Wextra -Werror -DTOML11_COLORIZE_ERROR_MESSAGE -I../ check_toml_test.cpp -o check_toml_test + g++ -std=c++11 -O2 -Wall -Wextra -Werror -DTOML11_DISALLOW_HETEROGENEOUS_ARRAYS -I../ check_toml_test.cpp -o check_toml_test go get github.com/BurntSushi/toml-test $GOPATH/bin/toml-test ./check_toml_test test_serialization: @@ -24,7 +24,7 @@ jobs: command: | g++ --version cd tests/ - g++ -std=c++11 -O2 -Wall -Wextra -Wpedantic -Werror -DTOML11_COLORIZE_ERROR_MESSAGE -I../ check_serialization.cpp -o check_serialization + g++ -std=c++11 -O2 -Wall -Wextra -Wpedantic -Werror -I../ check_serialization.cpp -o check_serialization git clone https://github.com/BurntSushi/toml-test.git cp check_serialization toml-test/tests/valid cd toml-test/tests/valid @@ -47,7 +47,7 @@ jobs: command: | g++ --version cd tests/ - g++ -std=c++11 -O2 -Wall -Wextra -Wpedantic -Werror -DTOML11_COLORIZE_ERROR_MESSAGE -I../ check.cpp -o check + g++ -std=c++11 -O2 -Wall -Wextra -Wpedantic -Werror -I../ check.cpp -o check git clone https://github.com/BurntSushi/toml-test.git cp check toml-test/tests/invalid cp check toml-test/tests/valid From bf992e8f94c38a46fd3bef497e0d6a0c77605e26 Mon Sep 17 00:00:00 2001 From: ToruNiina Date: Fri, 3 Apr 2020 23:45:45 +0900 Subject: [PATCH 03/12] doc: update README for v1-rc1 --- README.md | 38 ++++++++++++++++++++++---------------- 1 file changed, 22 insertions(+), 16 deletions(-) diff --git a/README.md b/README.md index 3823a0d..4528d21 100644 --- a/README.md +++ b/README.md @@ -10,8 +10,7 @@ toml11 toml11 is a C++11 (or later) header-only toml parser/encoder depending only on C++ standard library. -- It is compatible to the latest version of [TOML v0.5.0](https://github.com/toml-lang/toml/blob/master/versions/en/toml-v0.5.0.md). -- It optionally supports the [unreleased features](#unreleased-toml-features) in the master branch of toml-lang/toml. +- It is compatible to the latest version of [TOML v1.0.0-rc.1](https://github.com/toml-lang/toml/blob/master/versions/en/toml-v1.0.0-rc.1.md). - It is one of the most TOML standard compliant libraries, tested with [the language agnostic test suite for TOML parsers by BurntSushi](https://github.com/BurntSushi/toml-test). - It shows highly informative error messages. You can see the error messages about invalid files at [CircleCI](https://circleci.com/gh/ToruNiina/toml11). - It has configurable container. You can use any random-access containers and key-value maps as backend containers. @@ -1652,13 +1651,8 @@ not capable of representing a Local Time independent from a specific day. ## Unreleased TOML features -There are some unreleased features in toml-lang/toml:master. -Currently, the following features are available after defining -`TOML11_USE_UNRELEASED_TOML_FEATURES` macro flag. - -To use those features, `#define` `TOML11_USE_UNRELEASED_TOML_FEATURES` before -including `toml.hpp` or pass `-DTOML11_USE_UNRELEASED_TOML_FEATURES` to your -compiler. +Since TOML v1.0.0-rc.1 has been released, those features are now activated by +default. We no longer need to define `TOML11_USE_UNRELEASED_FEATURES`. - Leading zeroes in exponent parts of floats are permitted. - e.g. `1.0e+01`, `5e+05` @@ -1668,10 +1662,10 @@ compiler. - Allow heterogeneous arrays - [toml-lang/toml/PR/676](https://github.com/toml-lang/toml/pull/676) -### Note about heterogeneous arrays +## Note about heterogeneous arrays Although `toml::parse` allows heterogeneous arrays, constructor of `toml::value` -does not. +does not. Here the reason is explained. ```cpp // this won't be compiled @@ -1680,8 +1674,10 @@ toml::value v{ } ``` -There is a workaround for this issue. By explicitly converting values into +There is a workaround for this. By explicitly converting values into `toml::value`, you can initialize `toml::value` with a heterogeneous array. +Also, you can first initialize a `toml::value` with an array and then +`push_back` into it. ```cpp // OK! @@ -1689,6 +1685,17 @@ toml::value v{ toml::value("foo"), toml::value(3.14), toml::value(42), toml::value{1,2,3,4,5}, toml::value{{"key", "value"}} } + +// OK! +toml::value v(toml::array{}); +v.push_back("foo"); +v.push_back(3.14); + +// OK! +toml::array a; +a.push_back("foo"); +a.push_back(3.14); +toml::value v(std::move(a)); ``` The reason why the first example is not allowed is the following. @@ -1717,15 +1724,14 @@ This means that the above C++ code makes constructor's overload resolution ambiguous. So a constructor that allows both "table as an initializer-list" and "heterogeneous array as an initializer-list" cannot be implemented. -Thus, although it is painful, you need to explicitly cast values into -`toml::value` when you initialize heterogeneous array in C++ code. +Thus, although it is painful, we need to explicitly cast values into +`toml::value` when you initialize heterogeneous array in a C++ code. ```cpp -// You need to do this when you want to initialize hetero array. toml::value v{ toml::value("foo"), toml::value(3.14), toml::value(42), toml::value{1,2,3,4,5}, toml::value{{"key", "value"}} -} +}; ``` ## Breaking Changes from v2 From deb3ab66176d04c43c1155983615dc285aac43cf Mon Sep 17 00:00:00 2001 From: ToruNiina Date: Fri, 3 Apr 2020 23:57:52 +0900 Subject: [PATCH 04/12] ci: add DISALLOW_HETEROGENEOUS_ARRAYS --- .circleci/config.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index 2660506..6a68cfa 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -24,7 +24,7 @@ jobs: command: | g++ --version cd tests/ - g++ -std=c++11 -O2 -Wall -Wextra -Wpedantic -Werror -I../ check_serialization.cpp -o check_serialization + g++ -std=c++11 -O2 -Wall -Wextra -Wpedantic -Werror -DTOML11_DISALLOW_HETEROGENEOUS_ARRAYS -I../ check_serialization.cpp -o check_serialization git clone https://github.com/BurntSushi/toml-test.git cp check_serialization toml-test/tests/valid cd toml-test/tests/valid @@ -47,7 +47,7 @@ jobs: command: | g++ --version cd tests/ - g++ -std=c++11 -O2 -Wall -Wextra -Wpedantic -Werror -I../ check.cpp -o check + g++ -std=c++11 -O2 -Wall -Wextra -Wpedantic -Werror -DTOML11_DISALLOW_HETEROGENEOUS_ARRAYS -I../ check.cpp -o check git clone https://github.com/BurntSushi/toml-test.git cp check toml-test/tests/invalid cp check toml-test/tests/valid From 57d4e196a396b5e6bf4d87f34e8cb1625c01d2ff Mon Sep 17 00:00:00 2001 From: Scott McCaskill Date: Sun, 10 May 2020 16:06:52 -0500 Subject: [PATCH 05/12] when parsing a local_time, parse up to 9 digits worth (nanoseconds) of fractional seconds --- toml/parser.hpp | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/toml/parser.hpp b/toml/parser.hpp index bba6b47..ff2a57f 100644 --- a/toml/parser.hpp +++ b/toml/parser.hpp @@ -733,7 +733,13 @@ parse_local_time(location& loc) case 0: break; default: break; } - if(sf.size() >= 6) + if(sf.size() >= 9) + { + time.millisecond = from_string(sf.substr(0, 3), 0u); + time.microsecond = from_string(sf.substr(3, 3), 0u); + time.nanosecond = from_string(sf.substr(6, 3), 0u); + } + else if(sf.size() >= 6) { time.millisecond = from_string(sf.substr(0, 3), 0u); time.microsecond = from_string(sf.substr(3, 3), 0u); From f29f42277e012c06b12a03688c0910807d71ed0c Mon Sep 17 00:00:00 2001 From: Shu Wang Date: Mon, 18 May 2020 13:53:48 +0800 Subject: [PATCH 06/12] fix: "Finding a value in an array" example in README --- README.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 4528d21..fc58f7e 100644 --- a/README.md +++ b/README.md @@ -176,7 +176,7 @@ what(): [error] bad time: should be HH:MM:SS.subsec --> ./datetime-malformed-no-secs.toml 1 | no-secs = 1987-07-05T17:45Z | ^------- HH:MM:SS.subsec - | + | Hint: pass: 1979-05-27T07:32:00, 1979-05-27 07:32:00.999999 Hint: fail: 1979-05-27T7:32:00, 1979-05-27 17:32 ``` @@ -266,11 +266,11 @@ shape = "round" ``` cpp const auto data = toml::parse("fruit.toml"); const auto& fruit = toml::find(data, "fruit"); -const auto name = toml::find(fruit, "apple"); +const auto name = toml::find(fruit, "name"); const auto& physical = toml::find(fruit, "physical"); -const auto color = toml::find(fruit, "color"); -const auto shape = toml::find(fruit, "shape"); +const auto color = toml::find(physical, "color"); +const auto shape = toml::find(physical, "shape"); ``` Here, variable `fruit` is a `toml::value` and can be used as the first argument From 2963d9a25b47c8496e757f48694bcac223cf68b0 Mon Sep 17 00:00:00 2001 From: ToruNiina Date: Fri, 5 Jun 2020 19:43:23 +0900 Subject: [PATCH 07/12] feat: add std::filesystem::path support --- toml/parser.hpp | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/toml/parser.hpp b/toml/parser.hpp index 80fe704..62900ee 100644 --- a/toml/parser.hpp +++ b/toml/parser.hpp @@ -12,6 +12,13 @@ #include #include +#if __cplusplus >= 201703L +#if __has_include() +#define TOML11_HAS_STD_FILESYSTEM +#include +#endif // has_include() +#endif // cplusplus >= C++17 + namespace toml { namespace detail @@ -2094,5 +2101,21 @@ basic_value parse(const std::string& fname) return parse(ifs, fname); } +#ifdef TOML11_HAS_STD_FILESYSTEM +template class Table = std::unordered_map, + template class Array = std::vector> +basic_value parse(const std::filesystem::path& fpath) +{ + std::ifstream ifs(fpath, std::ios_base::binary); + if(!ifs.good()) + { + throw std::runtime_error("toml::parse: file open error -> " + + fpath.string()); + } + return parse(ifs, fname); +} +#endif // TOML11_HAS_STD_FILESYSTEM + } // toml #endif// TOML11_PARSER_HPP From 46ed051740803bb2a81c1d5cdc79a19d8512dd25 Mon Sep 17 00:00:00 2001 From: ToruNiina Date: Fri, 5 Jun 2020 23:15:19 +0900 Subject: [PATCH 08/12] fix: pass path.string as a filename --- toml/parser.hpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/toml/parser.hpp b/toml/parser.hpp index 62900ee..b2e4755 100644 --- a/toml/parser.hpp +++ b/toml/parser.hpp @@ -2113,7 +2113,7 @@ basic_value parse(const std::filesystem::path& fpath) throw std::runtime_error("toml::parse: file open error -> " + fpath.string()); } - return parse(ifs, fname); + return parse(ifs, fpath.string()); } #endif // TOML11_HAS_STD_FILESYSTEM From defde33544ed949e312a5116375e6b32f29b0840 Mon Sep 17 00:00:00 2001 From: ToruNiina Date: Sat, 6 Jun 2020 17:18:02 +0900 Subject: [PATCH 09/12] fix: avoid ambiguity in overload resolution Since both `std::string` and `std::filesystem::path` can be convertible from `const char &[N]` (like, `parse("file.toml")`), after adding `parse(std::filesystem::path)`, the overload resolution of `parse("file.toml")` becomes ambiguous. By adding `parse(...)` that exactly matches to `parse("file.toml")`, we can remove this ambiguity. --- toml/parser.hpp | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/toml/parser.hpp b/toml/parser.hpp index b2e4755..d919c16 100644 --- a/toml/parser.hpp +++ b/toml/parser.hpp @@ -2102,6 +2102,24 @@ basic_value parse(const std::string& fname) } #ifdef TOML11_HAS_STD_FILESYSTEM +// This function just forwards `parse("filename.toml")` to std::string version +// to avoid the ambiguity in overload resolution. +// +// Both std::string and std::filesystem::path are convertible from const char[]. +// Without this, both parse(std::string) and parse(std::filesystem::path) +// matches to parse("filename.toml"). This breaks the existing code. +// +// This function exactly matches to the invokation with string literal. +// So this function is preferred than others and the ambiguity disappears. +template class Table = std::unordered_map, + template class Array = std::vector, + std::size_t N> +basic_value parse(const char (&fname)[N]) +{ + return parse(std::string(fname)); +} + template class Table = std::unordered_map, template class Array = std::vector> From 2164fd39f7ca0c4ea8fad0078a8127bd47990e0d Mon Sep 17 00:00:00 2001 From: ToruNiina Date: Sun, 21 Jun 2020 14:00:03 +0900 Subject: [PATCH 10/12] doc: explain about the type of the top-level value fix #120. --- README.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/README.md b/README.md index fc58f7e..2c7f1a4 100644 --- a/README.md +++ b/README.md @@ -119,6 +119,9 @@ const std::string fname("sample.toml"); const toml::value data = toml::parse(fname); ``` +As required by the TOML specification, the top-level value is always a table. +You can find a value inside it, cast it into a table explicitly, and insert it as a value into other `toml::value`. + If it encounters an error while opening a file, it will throw `std::runtime_error`. You can also pass a `std::istream` to the `toml::parse` function. From 9633e5fe5a7a7aa64a542debe02b1a37496850bc Mon Sep 17 00:00:00 2001 From: ToruNiina Date: Sun, 21 Jun 2020 14:11:26 +0900 Subject: [PATCH 11/12] doc: add iteration examples into as_xxx section related to #120 --- README.md | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/README.md b/README.md index 2c7f1a4..9629ebd 100644 --- a/README.md +++ b/README.md @@ -465,6 +465,24 @@ if(answer.is_integer() && answer.as_integer(std::nothrow) == 42) If `std::nothrow` is passed, the functions are marked as noexcept. +By casting a `toml::value` into an array or a table, you can iterate over the +elements. + +```cpp +const auto data = toml::parse("example.toml"); +std::cout << "keys in the top-level table are the following: \n"; +for(const auto& [k, v] : data.as_table()) +{ + std::cout << k << '\n'; +} + +const auto& fruits = toml::find(data, "fruits"); +for(const auto& v : fruits.as_array()) +{ + std::cout << toml::find(v, "name") << '\n'; +} +``` + The full list of the functions is below. ```cpp From 92aa42a58e3c418f7804b44eac0c1dd3c17d5454 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marius=20Maa=C3=9F?= Date: Thu, 2 Jul 2020 13:36:20 +0200 Subject: [PATCH 12/12] Fix MSVC warning C4866 This fixes the warning "compiler may not enforce left-to-right evaluation order for call to" that is caused by Visual Studio if this is compiled with a target of C++17. --- toml/serializer.hpp | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/toml/serializer.hpp b/toml/serializer.hpp index 46a2b70..c733582 100644 --- a/toml/serializer.hpp +++ b/toml/serializer.hpp @@ -697,7 +697,8 @@ format(const basic_value& v, std::size_t w = 80u, oss << v.comments(); oss << '\n'; // to split the file comment from the first element } - oss << visit(serializer(w, fprec, no_comment, false), v); + const auto serialized = visit(serializer(w, fprec, no_comment, false), v); + oss << serialized; return oss.str(); } return visit(serializer(w, fprec, force_inline), v); @@ -753,7 +754,8 @@ operator<<(std::basic_ostream& os, const basic_value& v) os << '\n'; // to split the file comment from the first element } // the root object can't be an inline table. so pass `false`. - os << visit(serializer(w, fprec, false, no_comment), v); + const auto serialized = visit(serializer(w, fprec, no_comment, false), v); + os << serialized; // if v is a non-table value, and has only one comment, then // put a comment just after a value. in the following way.