Merge pull request #29 from ToruNiina/err-msg-dotted-key

Error message for getting values corresponds to dotted keys
This commit is contained in:
Toru Niina
2019-02-27 12:21:03 +09:00
committed by GitHub
3 changed files with 51 additions and 81 deletions

View File

@@ -13,23 +13,23 @@ using namespace detail;
BOOST_AUTO_TEST_CASE(test_bare_key) BOOST_AUTO_TEST_CASE(test_bare_key)
{ {
TOML11_TEST_PARSE_EQUAL_VALUE(parse_key, "barekey", std::vector<key>(1, "barekey")); TOML11_TEST_PARSE_EQUAL(parse_key, "barekey", std::vector<key>(1, "barekey"));
TOML11_TEST_PARSE_EQUAL_VALUE(parse_key, "bare-key", std::vector<key>(1, "bare-key")); TOML11_TEST_PARSE_EQUAL(parse_key, "bare-key", std::vector<key>(1, "bare-key"));
TOML11_TEST_PARSE_EQUAL_VALUE(parse_key, "bare_key", std::vector<key>(1, "bare_key")); TOML11_TEST_PARSE_EQUAL(parse_key, "bare_key", std::vector<key>(1, "bare_key"));
TOML11_TEST_PARSE_EQUAL_VALUE(parse_key, "1234", std::vector<key>(1, "1234")); TOML11_TEST_PARSE_EQUAL(parse_key, "1234", std::vector<key>(1, "1234"));
} }
BOOST_AUTO_TEST_CASE(test_quoted_key) BOOST_AUTO_TEST_CASE(test_quoted_key)
{ {
TOML11_TEST_PARSE_EQUAL_VALUE(parse_key, "\"127.0.0.1\"", std::vector<key>(1, "127.0.0.1" )); TOML11_TEST_PARSE_EQUAL(parse_key, "\"127.0.0.1\"", std::vector<key>(1, "127.0.0.1" ));
TOML11_TEST_PARSE_EQUAL_VALUE(parse_key, "\"character encoding\"", std::vector<key>(1, "character encoding")); TOML11_TEST_PARSE_EQUAL(parse_key, "\"character encoding\"", std::vector<key>(1, "character encoding"));
#if defined(_MSC_VER) || defined(__INTEL_COMPILER) #if defined(_MSC_VER) || defined(__INTEL_COMPILER)
TOML11_TEST_PARSE_EQUAL_VALUE(parse_key, "\"\xCA\x8E\xC7\x9D\xCA\x9E\"", std::vector<key>(1, "\xCA\x8E\xC7\x9D\xCA\x9E")); TOML11_TEST_PARSE_EQUAL(parse_key, "\"\xCA\x8E\xC7\x9D\xCA\x9E\"", std::vector<key>(1, "\xCA\x8E\xC7\x9D\xCA\x9E"));
#else #else
TOML11_TEST_PARSE_EQUAL_VALUE(parse_key, "\"ʎǝʞ\"", std::vector<key>(1, "ʎǝʞ" )); TOML11_TEST_PARSE_EQUAL(parse_key, "\"ʎǝʞ\"", std::vector<key>(1, "ʎǝʞ" ));
#endif #endif
TOML11_TEST_PARSE_EQUAL_VALUE(parse_key, "'key2'", std::vector<key>(1, "key2" )); TOML11_TEST_PARSE_EQUAL(parse_key, "'key2'", std::vector<key>(1, "key2" ));
TOML11_TEST_PARSE_EQUAL_VALUE(parse_key, "'quoted \"value\"'", std::vector<key>(1, "quoted \"value\"" )); TOML11_TEST_PARSE_EQUAL(parse_key, "'quoted \"value\"'", std::vector<key>(1, "quoted \"value\"" ));
} }
BOOST_AUTO_TEST_CASE(test_dotted_key) BOOST_AUTO_TEST_CASE(test_dotted_key)
@@ -38,13 +38,13 @@ BOOST_AUTO_TEST_CASE(test_dotted_key)
std::vector<key> keys(2); std::vector<key> keys(2);
keys[0] = "physical"; keys[0] = "physical";
keys[1] = "color"; keys[1] = "color";
TOML11_TEST_PARSE_EQUAL_VALUE(parse_key, "physical.color", keys); TOML11_TEST_PARSE_EQUAL(parse_key, "physical.color", keys);
} }
{ {
std::vector<key> keys(2); std::vector<key> keys(2);
keys[0] = "physical"; keys[0] = "physical";
keys[1] = "shape"; keys[1] = "shape";
TOML11_TEST_PARSE_EQUAL_VALUE(parse_key, "physical.shape", keys); TOML11_TEST_PARSE_EQUAL(parse_key, "physical.shape", keys);
} }
{ {
std::vector<key> keys(4); std::vector<key> keys(4);
@@ -52,12 +52,12 @@ BOOST_AUTO_TEST_CASE(test_dotted_key)
keys[1] = "y"; keys[1] = "y";
keys[2] = "z"; keys[2] = "z";
keys[3] = "w"; keys[3] = "w";
TOML11_TEST_PARSE_EQUAL_VALUE(parse_key, "x.y.z.w", keys); TOML11_TEST_PARSE_EQUAL(parse_key, "x.y.z.w", keys);
} }
{ {
std::vector<key> keys(2); std::vector<key> keys(2);
keys[0] = "site"; keys[0] = "site";
keys[1] = "google.com"; keys[1] = "google.com";
TOML11_TEST_PARSE_EQUAL_VALUE(parse_key, "site.\"google.com\"", keys); TOML11_TEST_PARSE_EQUAL(parse_key, "site.\"google.com\"", keys);
} }
} }

View File

@@ -754,19 +754,21 @@ parse_offset_datetime(location<Container>& loc)
} }
template<typename Container> template<typename Container>
result<key, std::string> parse_simple_key(location<Container>& loc) result<std::pair<key, region<Container>>, std::string>
parse_simple_key(location<Container>& loc)
{ {
if(const auto bstr = parse_basic_string(loc)) if(const auto bstr = parse_basic_string(loc))
{ {
return ok(bstr.unwrap().first.str); return ok(std::make_pair(bstr.unwrap().first.str, bstr.unwrap().second));
} }
if(const auto lstr = parse_literal_string(loc)) if(const auto lstr = parse_literal_string(loc))
{ {
return ok(lstr.unwrap().first.str); return ok(std::make_pair(lstr.unwrap().first.str, lstr.unwrap().second));
} }
if(const auto bare = lex_unquoted_key::invoke(loc)) if(const auto bare = lex_unquoted_key::invoke(loc))
{ {
return ok(bare.unwrap().str()); const auto reg = bare.unwrap();
return ok(std::make_pair(reg.str(), reg));
} }
return err(format_underline("[error] toml::parse_simple_key: ", loc, return err(format_underline("[error] toml::parse_simple_key: ", loc,
"the next token is not a simple key")); "the next token is not a simple key"));
@@ -774,13 +776,15 @@ result<key, std::string> parse_simple_key(location<Container>& loc)
// dotted key become vector of keys // dotted key become vector of keys
template<typename Container> template<typename Container>
result<std::vector<key>, std::string> parse_key(location<Container>& loc) result<std::pair<std::vector<key>, region<Container>>, std::string>
parse_key(location<Container>& loc)
{ {
const auto first = loc.iter(); const auto first = loc.iter();
// dotted key -> foo.bar.baz whitespaces are allowed // dotted key -> foo.bar.baz whitespaces are allowed
if(const auto token = lex_dotted_key::invoke(loc)) if(const auto token = lex_dotted_key::invoke(loc))
{ {
location<std::string> inner_loc(loc.name(), token.unwrap().str()); const auto reg = token.unwrap();
location<std::string> inner_loc(loc.name(), reg.str());
std::vector<key> keys; std::vector<key> keys;
while(inner_loc.iter() != inner_loc.end()) while(inner_loc.iter() != inner_loc.end())
@@ -788,7 +792,7 @@ result<std::vector<key>, std::string> parse_key(location<Container>& loc)
lex_ws::invoke(inner_loc); lex_ws::invoke(inner_loc);
if(const auto k = parse_simple_key(inner_loc)) if(const auto k = parse_simple_key(inner_loc))
{ {
keys.push_back(k.unwrap()); keys.push_back(k.unwrap().first);
} }
else else
{ {
@@ -813,14 +817,15 @@ result<std::vector<key>, std::string> parse_key(location<Container>& loc)
"should be `.`")); "should be `.`"));
} }
} }
return ok(keys); return ok(std::make_pair(keys, reg));
} }
loc.iter() = first; loc.iter() = first;
// simple key -> foo // simple key -> foo
if(const auto smpl = parse_simple_key(loc)) if(const auto smpl = parse_simple_key(loc))
{ {
return ok(std::vector<key>(1, smpl.unwrap())); return ok(std::make_pair(std::vector<key>(1, smpl.unwrap().first),
smpl.unwrap().second));
} }
return err(format_underline("[error] toml::parse_key: ", loc, "is not a valid key")); return err(format_underline("[error] toml::parse_key: ", loc, "is not a valid key"));
} }
@@ -899,14 +904,14 @@ parse_array(location<Container>& loc)
} }
template<typename Container> template<typename Container>
result<std::pair<std::vector<key>, value>, std::string> result<std::pair<std::pair<std::vector<key>, region<Container>>, value>, std::string>
parse_key_value_pair(location<Container>& loc) parse_key_value_pair(location<Container>& loc)
{ {
const auto first = loc.iter(); const auto first = loc.iter();
auto key = parse_key(loc); auto key_reg = parse_key(loc);
if(!key) if(!key_reg)
{ {
std::string msg = std::move(key.unwrap_err()); std::string msg = std::move(key_reg.unwrap_err());
// if the next token is keyvalue-separator, it means that there are no // 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". // key. then we need to show error as "empty key is not allowed".
if(const auto keyval_sep = lex_keyval_sep::invoke(loc)) if(const auto keyval_sep = lex_keyval_sep::invoke(loc))
@@ -962,7 +967,8 @@ parse_key_value_pair(location<Container>& loc)
loc.iter() = first; loc.iter() = first;
return err(msg); return err(msg);
} }
return ok(std::make_pair(std::move(key.unwrap()), std::move(val.unwrap()))); return ok(std::make_pair(std::move(key_reg.unwrap()),
std::move(val.unwrap())));
} }
// for error messages. // for error messages.
@@ -981,10 +987,11 @@ std::string format_dotted_keys(InputIterator first, const InputIterator last)
return retval; return retval;
} }
template<typename InputIterator> template<typename InputIterator, typename Container>
result<bool, std::string> result<bool, std::string>
insert_nested_key(table& root, const toml::value& v, insert_nested_key(table& root, const toml::value& v,
InputIterator iter, const InputIterator last, InputIterator iter, const InputIterator last,
region<Container> key_reg,
const bool is_array_of_table = false) const bool is_array_of_table = false)
{ {
static_assert(std::is_same<key, static_assert(std::is_same<key,
@@ -1066,11 +1073,7 @@ insert_nested_key(table& root, const toml::value& v,
} }
else // if not, we need to create the array of table else // if not, we need to create the array of table
{ {
toml::value aot(v); // copy region info from table to array toml::value aot(toml::array(1, v), key_reg);
// 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)); tab->insert(std::make_pair(k, aot));
return ok(true); return ok(true);
} }
@@ -1119,10 +1122,7 @@ insert_nested_key(table& root, const toml::value& v,
// [x.y.z] // [x.y.z]
if(tab->count(k) == 0) if(tab->count(k) == 0)
{ {
// the region of [x.y] is the same as [x.y.z]. (*tab)[k] = toml::value(toml::table{}, key_reg);
(*tab)[k] = v; // copy region_info_
detail::assign_keeping_region((*tab)[k],
::toml::value(::toml::table{}));
} }
// type checking... // type checking...
@@ -1186,11 +1186,12 @@ parse_inline_table(location<Container>& loc)
{ {
return err(kv_r.unwrap_err()); return err(kv_r.unwrap_err());
} }
const std::vector<key>& keys = kv_r.unwrap().first; const std::vector<key>& keys = kv_r.unwrap().first.first;
const value& val = kv_r.unwrap().second; const region<Container>& key_reg = kv_r.unwrap().first.second;
const value& val = kv_r.unwrap().second;
const auto inserted = const auto inserted =
insert_nested_key(retval, val, keys.begin(), keys.end()); insert_nested_key(retval, val, keys.begin(), keys.end(), key_reg);
if(!inserted) if(!inserted)
{ {
throw internal_error("[error] toml::parse_inline_table: " throw internal_error("[error] toml::parse_inline_table: "
@@ -1288,7 +1289,7 @@ parse_table_key(location<Container>& loc)
throw internal_error(format_underline("[error] " throw internal_error(format_underline("[error] "
"toml::parse_table_key: no `]`", inner_loc, "should be `]`")); "toml::parse_table_key: no `]`", inner_loc, "should be `]`"));
} }
return ok(std::make_pair(keys.unwrap(), token.unwrap())); return ok(std::make_pair(keys.unwrap().first, token.unwrap()));
} }
else else
{ {
@@ -1326,7 +1327,7 @@ parse_array_table_key(location<Container>& loc)
throw internal_error(format_underline("[error] " throw internal_error(format_underline("[error] "
"toml::parse_table_key: no `]]`", inner_loc, "should be `]]`")); "toml::parse_table_key: no `]]`", inner_loc, "should be `]]`"));
} }
return ok(std::make_pair(keys.unwrap(), token.unwrap())); return ok(std::make_pair(keys.unwrap().first, token.unwrap()));
} }
else else
{ {
@@ -1367,10 +1368,11 @@ result<table, std::string> parse_ml_table(location<Container>& loc)
if(const auto kv = parse_key_value_pair(loc)) if(const auto kv = parse_key_value_pair(loc))
{ {
const std::vector<key>& keys = kv.unwrap().first; const std::vector<key>& keys = kv.unwrap().first.first;
const value& val = kv.unwrap().second; const region<Container>& key_reg = kv.unwrap().first.second;
const value& val = kv.unwrap().second;
const auto inserted = const auto inserted =
insert_nested_key(tab, val, keys.begin(), keys.end()); insert_nested_key(tab, val, keys.begin(), keys.end(), key_reg);
if(!inserted) if(!inserted)
{ {
return err(inserted.unwrap_err()); return err(inserted.unwrap_err());
@@ -1448,7 +1450,8 @@ result<table, std::string> parse_toml_file(location<Container>& loc)
const auto inserted = insert_nested_key(data, const auto inserted = insert_nested_key(data,
toml::value(tab.unwrap(), reg), toml::value(tab.unwrap(), reg),
keys.begin(), keys.end(), /*is_array_of_table=*/ true); keys.begin(), keys.end(), reg,
/*is_array_of_table=*/ true);
if(!inserted) {return err(inserted.unwrap_err());} if(!inserted) {return err(inserted.unwrap_err());}
continue; continue;
@@ -1462,7 +1465,7 @@ result<table, std::string> parse_toml_file(location<Container>& loc)
const auto& reg = tabkey.unwrap().second; const auto& reg = tabkey.unwrap().second;
const auto inserted = insert_nested_key(data, const auto inserted = insert_nested_key(data,
toml::value(tab.unwrap(), reg), keys.begin(), keys.end()); toml::value(tab.unwrap(), reg), keys.begin(), keys.end(), reg);
if(!inserted) {return err(inserted.unwrap_err());} if(!inserted) {return err(inserted.unwrap_err());}
continue; continue;

View File

@@ -21,8 +21,6 @@ namespace detail
{ {
// to show error messages. not recommended for users. // to show error messages. not recommended for users.
region_base const& get_region(const value&); region_base const& get_region(const value&);
// ditto.
void assign_keeping_region(value&, value);
}// detail }// detail
template<typename T> template<typename T>
@@ -562,9 +560,6 @@ class value
// for error messages // for error messages
friend region_base const& detail::get_region(const value&); 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<value_t T> template<value_t T>
struct switch_cast; struct switch_cast;
@@ -599,36 +594,8 @@ inline region_base const& get_region(const value& v)
{ {
return *(v.region_info_); 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())
{
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;
}
return;
}
}// detail }// detail
template<> struct value::switch_cast<value_t::Boolean> template<> struct value::switch_cast<value_t::Boolean>
{ {
static Boolean& invoke(value& v) {return v.boolean_;} static Boolean& invoke(value& v) {return v.boolean_;}