From 6c2a536fa5418aefc7dd58ef5a773c6a49715df2 Mon Sep 17 00:00:00 2001 From: ToruNiina Date: Sun, 14 Apr 2019 19:48:43 +0900 Subject: [PATCH 1/3] fix: check literal has a table or an array first The literal like this `"[[table]]"_toml` caused a syntax error. It is because the literal parser first check that it might be a bare value without a key, and parse_array directory throws syntax_error. This change makes the parser first check a literal is a name of table, and then parse the content. --- toml/literal.hpp | 41 +++++++++++++++++++++++++++++++++++++---- 1 file changed, 37 insertions(+), 4 deletions(-) diff --git a/toml/literal.hpp b/toml/literal.hpp index 08d7b5e..d7194ed 100644 --- a/toml/literal.hpp +++ b/toml/literal.hpp @@ -30,13 +30,46 @@ inline ::toml::value operator""_toml(const char* str, std::size_t len) ::toml::detail::lex_ws, ::toml::detail::at_least<1>>; skip_ws::invoke(loc); - // literal may be a bare value. try them first. - if(auto data = ::toml::detail::parse_value(loc)) + // to distinguish arrays and tables, first check it is a table or not. + // + // "[1,2,3]"_toml; // this is an array + // "[table]"_toml; // a table that has an empty table named "table" inside. + // "[[1,2,3]]"_toml; // this is an array of arrays + // "[[table]]"_toml; // this is a table that has an array of tables inside. + // + // "[[1]]"_toml; // this can be both... (currently it becomes a table) + // "1 = [{}]"_toml; // this is a table that has an array of table named 1. + // "[[1,]]"_toml; // this is an array of arrays. + // "[[1],]"_toml; // this also. + + const auto the_front = loc.iter(); + + const bool is_table_key = ::toml::detail::lex_std_table::invoke(loc); + loc.reset(the_front); + + const bool is_aots_key = ::toml::detail::lex_array_table::invoke(loc); + loc.reset(the_front); + + // If it is neither a table-key or a array-of-table-key, it may be a value. + if(!is_table_key && !is_aots_key) { - return data.unwrap(); + if(auto data = ::toml::detail::parse_value(loc)) + { + return data.unwrap(); + } } - // literal is a TOML file (i.e. multiline table). + // Note that still it can be a table, because the literal might be something + // like the following. + // ```cpp + // R"( // c++11 raw string literals + // key = "value" + // int = 42 + // )"_toml; + // ``` + // It is a valid toml file. + // It should be parsed as if we parse a file with this content. + if(auto data = ::toml::detail::parse_toml_file(loc)) { loc.reset(loc.begin()); // rollback to the top of the literal From 0a3a41a708e8e2777963d76ce26083922365044a Mon Sep 17 00:00:00 2001 From: ToruNiina Date: Sun, 14 Apr 2019 20:06:11 +0900 Subject: [PATCH 2/3] test: add test for literals for difficult case --- tests/test_literals.cpp | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/tests/test_literals.cpp b/tests/test_literals.cpp index 7431f80..2342a44 100644 --- a/tests/test_literals.cpp +++ b/tests/test_literals.cpp @@ -33,6 +33,18 @@ BOOST_AUTO_TEST_CASE(test_file_as_literal) b = "baz" )"_toml; + BOOST_CHECK_EQUAL(r, v); + } + { + const toml::value r{ + {"table", toml::table{{"a", 42}, {"b", "baz"}}} + }; + const toml::value v = u8R"( + [table] + a = 42 + b = "baz" + )"_toml; + BOOST_CHECK_EQUAL(r, v); } } @@ -91,6 +103,19 @@ BOOST_AUTO_TEST_CASE(test_value_as_literal) BOOST_CHECK(v1.is_array()); BOOST_CHECK((toml::get>(v1) == std::vector{1,2,3})); + + const toml::value v2 = u8R"([1,])"_toml; + + BOOST_CHECK(v2.is_array()); + BOOST_CHECK((toml::get>(v2) == std::vector{1})); + + const toml::value v3 = u8R"([[1,]])"_toml; + BOOST_CHECK(v3.is_array()); + BOOST_CHECK((toml::get>(toml::get(v3).front()) == std::vector{1})); + + const toml::value v4 = u8R"([[1],])"_toml; + BOOST_CHECK(v4.is_array()); + BOOST_CHECK((toml::get>(toml::get(v4).front()) == std::vector{1})); } { const toml::value v1 = u8R"({a = 42})"_toml; From f9ab7d6f56802842f67a317fde1e0e7b4469abef Mon Sep 17 00:00:00 2001 From: ToruNiina Date: Sun, 14 Apr 2019 20:08:23 +0900 Subject: [PATCH 3/3] chore: add note about literals to README.md --- README.md | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/README.md b/README.md index ed28ec2..495a07a 100644 --- a/README.md +++ b/README.md @@ -603,6 +603,25 @@ toml::value operator""_toml(const char* str, std::size_t len); Access to the operator can be gained with `using namespace toml::literals;`, `using namespace toml::toml_literals`, and `using namespace toml::literals::toml_literals`. +Note that since it allows a bare value without a key, it is difficult to distinguish +arrays and table definitions. +Currently, it parses `[1]` as a table definition if there are no commas. +To ensure a literal to be considered as an array with one element, you need to +add a comma after the first element (like `[1,]`). + +```cpp +"[1,2,3]"_toml; // This is an array +"[table]"_toml; // This is a table that has an empty table named "table" inside. +"[[1,2,3]]"_toml; // This is an array of arrays +"[[table]]"_toml; // This is a table that has an array of tables inside. + +"[[1]]"_toml; // This is ambiguous. + // Currently, it becomes a table taht has array of table "1". +"1 = [{}]"_toml; // This is a table that has an array of table named 1. +"[[1,]]"_toml; // This is an array of arrays. +"[[1],]"_toml; // ditto. +``` + ## Conversion between toml value and arbitrary types You can also use `toml::get` and other related functions with the types you defined