From d88521d63c2f29b0e6e91ddd60ec4e99728a307a Mon Sep 17 00:00:00 2001 From: ToruNiina Date: Mon, 4 Mar 2019 15:01:28 +0900 Subject: [PATCH 1/2] feat: enable to change region of value To allow the following toml file, we need to replace the region after the more precise region is found. ```toml [a.b.c] d = 42 [a] e = 2.71 ``` If the precise region (here, [a]) is found, the region of `a` should be `[a]`, not `[a.b.c]`. After `[a]` is defined, toml does not allow to write `[a]` twice. To check it, we need to replace the region of values to the precise one. --- toml/value.hpp | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/toml/value.hpp b/toml/value.hpp index d6f5f0e1..03812c71 100644 --- a/toml/value.hpp +++ b/toml/value.hpp @@ -21,6 +21,8 @@ namespace detail { // to show error messages. not recommended for users. region_base const& get_region(const value&); +template +void change_region(value&, Region&&); }// detail template @@ -560,6 +562,9 @@ class value // for error messages friend region_base const& detail::get_region(const value&); + template + friend void detail::change_region(value&, Region&&); + template struct switch_cast; @@ -594,6 +599,20 @@ inline region_base const& get_region(const value& v) { return *(v.region_info_); } + +template +void change_region(value& v, Region&& reg) +{ + using region_type = typename std::remove_reference< + typename std::remove_cv::type + >::type; + + std::shared_ptr new_reg = + std::make_shared(std::forward(reg)); + v.region_info_ = new_reg; + return; +} + }// detail template<> struct value::switch_cast From b0ed122214db80a80f7ef740e063200b3aea7ca7 Mon Sep 17 00:00:00 2001 From: ToruNiina Date: Tue, 5 Mar 2019 23:25:25 +0900 Subject: [PATCH 2/2] fix: allow deeper table appeared before allow the following toml file. ```toml [a.b.c] d = 10 [a] e = 2.718 ``` --- toml/parser.hpp | 99 ++++++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 93 insertions(+), 6 deletions(-) diff --git a/toml/parser.hpp b/toml/parser.hpp index faa7213b..85126431 100644 --- a/toml/parser.hpp +++ b/toml/parser.hpp @@ -987,6 +987,75 @@ std::string format_dotted_keys(InputIterator first, const InputIterator last) return retval; } +// forward decl for is_valid_forward_table_definition +template +result, region>, std::string> +parse_table_key(location& loc); +// The following toml file is allowed. +// ```toml +// [a.b.c] # here, table `a` has element `b`. +// foo = "bar" +// [a] # merge a = {baz = "qux"} to a = {b = {...}} +// baz = "qux" +// ``` +// But the following is not allowed. +// ```toml +// [a] +// b.c.foo = "bar" +// [a] # error! the same table [a] defined! +// baz = "qux" +// ``` +// The following is neither allowed. +// ```toml +// a = { b.c.foo = "bar"} +// [a] # error! the same table [a] defined! +// baz = "qux" +// ``` +// Here, it parses region of `tab->at(k)` as a table key and check the depth +// of the key. If the key region points deeper node, it would be allowed. +// Otherwise, the key points the same node. It would be rejected. +template +bool is_valid_forward_table_definition(const value& fwd, + Iterator key_first, Iterator key_curr, Iterator key_last) +{ + location def("internal", detail::get_region(fwd).str()); + if(const auto tabkeys = parse_table_key(def)) + { + // table keys always contains all the nodes from the root. + const auto& tks = tabkeys.unwrap().first; + if(std::distance(key_first, key_last) == tks.size() && + std::equal(tks.begin(), tks.end(), key_first)) + { + // the keys are equivalent. it is not allowed. + return false; + } + // the keys are not equivalent. it is allowed. + return true; + } + if(const auto dotkeys = parse_key(def)) + { + // consider the following case. + // [a] + // b.c = {d = 42} + // [a.b.c] + // e = 2.71 + // this defines the table [a.b.c] twice. no? + + // a dotted key starts from the node representing a table in which the + // dotted key belongs to. + const auto& dks = dotkeys.unwrap().first; + if(std::distance(key_curr, key_last) == dks.size() && + std::equal(dks.begin(), dks.end(), key_curr)) + { + // the keys are equivalent. it is not allowed. + return false; + } + // the keys are not equivalent. it is allowed. + return true; + } + return false; +} + template result insert_nested_key(table& root, const toml::value& v, @@ -1077,16 +1146,34 @@ insert_nested_key(table& root, const toml::value& v, tab->insert(std::make_pair(k, aot)); return ok(true); } - } + } // end if(array of table) + if(tab->count(k) == 1) { 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")); + if(!is_valid_forward_table_definition( + tab->at(k), first, iter, last)) + { + 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")); + } + // to allow the following toml file. + // [a.b.c] + // d = 42 + // [a] + // e = 2.71 + auto& t = tab->at(k).cast(); + for(const auto& kv : v.cast()) + { + t[kv.first] = kv.second; + } + detail::change_region(tab->at(k), key_reg); + return ok(true); } else if(v.is(value_t::Table) && tab->at(k).is(value_t::Array) &&