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)
{
TOML11_TEST_PARSE_EQUAL_VALUE(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_VALUE(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, "barekey", std::vector<key>(1, "barekey"));
TOML11_TEST_PARSE_EQUAL(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(parse_key, "1234", std::vector<key>(1, "1234"));
}
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_VALUE(parse_key, "\"character encoding\"", std::vector<key>(1, "character encoding"));
TOML11_TEST_PARSE_EQUAL(parse_key, "\"127.0.0.1\"", std::vector<key>(1, "127.0.0.1" ));
TOML11_TEST_PARSE_EQUAL(parse_key, "\"character encoding\"", std::vector<key>(1, "character encoding"));
#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
TOML11_TEST_PARSE_EQUAL_VALUE(parse_key, "\"ʎǝʞ\"", std::vector<key>(1, "ʎǝʞ" ));
TOML11_TEST_PARSE_EQUAL(parse_key, "\"ʎǝʞ\"", std::vector<key>(1, "ʎǝʞ" ));
#endif
TOML11_TEST_PARSE_EQUAL_VALUE(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, "'key2'", std::vector<key>(1, "key2" ));
TOML11_TEST_PARSE_EQUAL(parse_key, "'quoted \"value\"'", std::vector<key>(1, "quoted \"value\"" ));
}
BOOST_AUTO_TEST_CASE(test_dotted_key)
@@ -38,13 +38,13 @@ BOOST_AUTO_TEST_CASE(test_dotted_key)
std::vector<key> keys(2);
keys[0] = "physical";
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);
keys[0] = "physical";
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);
@@ -52,12 +52,12 @@ BOOST_AUTO_TEST_CASE(test_dotted_key)
keys[1] = "y";
keys[2] = "z";
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);
keys[0] = "site";
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>
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))
{
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))
{
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))
{
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,
"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
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();
// dotted key -> foo.bar.baz whitespaces are allowed
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;
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);
if(const auto k = parse_simple_key(inner_loc))
{
keys.push_back(k.unwrap());
keys.push_back(k.unwrap().first);
}
else
{
@@ -813,14 +817,15 @@ result<std::vector<key>, std::string> parse_key(location<Container>& loc)
"should be `.`"));
}
}
return ok(keys);
return ok(std::make_pair(keys, reg));
}
loc.iter() = first;
// simple key -> foo
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"));
}
@@ -899,14 +904,14 @@ parse_array(location<Container>& loc)
}
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)
{
const auto first = loc.iter();
auto key = parse_key(loc);
if(!key)
auto key_reg = parse_key(loc);
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
// key. then we need to show error as "empty key is not allowed".
if(const auto keyval_sep = lex_keyval_sep::invoke(loc))
@@ -962,7 +967,8 @@ parse_key_value_pair(location<Container>& loc)
loc.iter() = first;
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.
@@ -981,10 +987,11 @@ std::string format_dotted_keys(InputIterator first, const InputIterator last)
return retval;
}
template<typename InputIterator>
template<typename InputIterator, typename Container>
result<bool, std::string>
insert_nested_key(table& root, const toml::value& v,
InputIterator iter, const InputIterator last,
region<Container> key_reg,
const bool is_array_of_table = false)
{
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
{
toml::value aot(v); // copy region info from table to array
// update content by an array with one element
detail::assign_keeping_region(aot,
::toml::value(toml::array(1, v)));
toml::value aot(toml::array(1, v), key_reg);
tab->insert(std::make_pair(k, aot));
return ok(true);
}
@@ -1119,10 +1122,7 @@ insert_nested_key(table& root, const toml::value& v,
// [x.y.z]
if(tab->count(k) == 0)
{
// the region of [x.y] is the same as [x.y.z].
(*tab)[k] = v; // copy region_info_
detail::assign_keeping_region((*tab)[k],
::toml::value(::toml::table{}));
(*tab)[k] = toml::value(toml::table{}, key_reg);
}
// type checking...
@@ -1186,11 +1186,12 @@ parse_inline_table(location<Container>& loc)
{
return err(kv_r.unwrap_err());
}
const std::vector<key>& keys = kv_r.unwrap().first;
const value& val = kv_r.unwrap().second;
const std::vector<key>& keys = kv_r.unwrap().first.first;
const region<Container>& key_reg = kv_r.unwrap().first.second;
const value& val = kv_r.unwrap().second;
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)
{
throw internal_error("[error] toml::parse_inline_table: "
@@ -1288,7 +1289,7 @@ parse_table_key(location<Container>& loc)
throw internal_error(format_underline("[error] "
"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
{
@@ -1326,7 +1327,7 @@ parse_array_table_key(location<Container>& loc)
throw internal_error(format_underline("[error] "
"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
{
@@ -1367,10 +1368,11 @@ result<table, std::string> parse_ml_table(location<Container>& loc)
if(const auto kv = parse_key_value_pair(loc))
{
const std::vector<key>& keys = kv.unwrap().first;
const value& val = kv.unwrap().second;
const std::vector<key>& keys = kv.unwrap().first.first;
const region<Container>& key_reg = kv.unwrap().first.second;
const value& val = kv.unwrap().second;
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)
{
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,
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());}
continue;
@@ -1462,7 +1465,7 @@ result<table, std::string> parse_toml_file(location<Container>& loc)
const auto& reg = tabkey.unwrap().second;
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());}
continue;

View File

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