Skip to content

Commit

Permalink
Merge pull request #32 from ToruNiina/allow-deeper-table-before
Browse files Browse the repository at this point in the history
Allow deeper table before
  • Loading branch information
ToruNiina authored Mar 6, 2019
2 parents 2accc9d + d90ffb6 commit f5079a7
Show file tree
Hide file tree
Showing 2 changed files with 112 additions and 6 deletions.
99 changes: 93 additions & 6 deletions toml/parser.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -1022,6 +1022,75 @@ std::string format_dotted_keys(InputIterator first, const InputIterator last)
return retval;
}

// forward decl for is_valid_forward_table_definition
template<typename Container>
result<std::pair<std::vector<key>, region<Container>>, std::string>
parse_table_key(location<Container>& loc);
// The following toml file is allowed.
// ```toml
// [a.b.c] # here, table `a` has element `b`.
// foo = "bar"
// [a] # merge a = {baz = "qux"} to a = {b = {...}}
// baz = "qux"
// ```
// But the following is not allowed.
// ```toml
// [a]
// b.c.foo = "bar"
// [a] # error! the same table [a] defined!
// baz = "qux"
// ```
// The following is neither allowed.
// ```toml
// a = { b.c.foo = "bar"}
// [a] # error! the same table [a] defined!
// baz = "qux"
// ```
// Here, it parses region of `tab->at(k)` as a table key and check the depth
// of the key. If the key region points deeper node, it would be allowed.
// Otherwise, the key points the same node. It would be rejected.
template<typename Iterator>
bool is_valid_forward_table_definition(const value& fwd,
Iterator key_first, Iterator key_curr, Iterator key_last)
{
location<std::string> def("internal", detail::get_region(fwd).str());
if(const auto tabkeys = parse_table_key(def))
{
// table keys always contains all the nodes from the root.
const auto& tks = tabkeys.unwrap().first;
if(std::distance(key_first, key_last) == tks.size() &&
std::equal(tks.begin(), tks.end(), key_first))
{
// the keys are equivalent. it is not allowed.
return false;
}
// the keys are not equivalent. it is allowed.
return true;
}
if(const auto dotkeys = parse_key(def))
{
// consider the following case.
// [a]
// b.c = {d = 42}
// [a.b.c]
// e = 2.71
// this defines the table [a.b.c] twice. no?

// a dotted key starts from the node representing a table in which the
// dotted key belongs to.
const auto& dks = dotkeys.unwrap().first;
if(std::distance(key_curr, key_last) == dks.size() &&
std::equal(dks.begin(), dks.end(), key_curr))
{
// the keys are equivalent. it is not allowed.
return false;
}
// the keys are not equivalent. it is allowed.
return true;
}
return false;
}

template<typename InputIterator, typename Container>
result<bool, std::string>
insert_nested_key(table& root, const toml::value& v,
Expand Down Expand Up @@ -1112,16 +1181,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<value_t::Table>();
for(const auto& kv : v.cast<value_t::Table>())
{
t[kv.first] = kv.second;
}
detail::change_region(tab->at(k), key_reg);
return ok(true);
}
else if(v.is(value_t::Table) &&
tab->at(k).is(value_t::Array) &&
Expand Down
19 changes: 19 additions & 0 deletions toml/value.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,8 @@ namespace detail
{
// to show error messages. not recommended for users.
region_base const& get_region(const value&);
template<typename Region>
void change_region(value&, Region&&);
}// detail

template<typename T>
Expand Down Expand Up @@ -560,6 +562,9 @@ class value
// for error messages
friend region_base const& detail::get_region(const value&);

template<typename Region>
friend void detail::change_region(value&, Region&&);

template<value_t T>
struct switch_cast;

Expand Down Expand Up @@ -594,6 +599,20 @@ inline region_base const& get_region(const value& v)
{
return *(v.region_info_);
}

template<typename Region>
void change_region(value& v, Region&& reg)
{
using region_type = typename std::remove_reference<
typename std::remove_cv<Region>::type
>::type;

std::shared_ptr<region_base> new_reg =
std::make_shared<region_type>(std::forward<region_type>(reg));
v.region_info_ = new_reg;
return;
}

}// detail

template<> struct value::switch_cast<value_t::Boolean>
Expand Down

0 comments on commit f5079a7

Please sign in to comment.