mirror of
https://github.com/ToruNiina/toml11.git
synced 2025-12-16 03:08:52 +08:00
Compare commits
25 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
f5079a7892 | ||
|
|
d90ffb63c6 | ||
|
|
b0ed122214 | ||
|
|
d88521d63c | ||
|
|
2accc9d22c | ||
|
|
363927f489 | ||
|
|
ae793fb631 | ||
|
|
944b83642a | ||
|
|
5a8e5dee73 | ||
|
|
7f870d5861 | ||
|
|
536b23dc84 | ||
|
|
0c9806e99f | ||
|
|
5a92932019 | ||
|
|
e929d2f00f | ||
|
|
1e1e4c06e8 | ||
|
|
d0726db473 | ||
|
|
4cdc15a824 | ||
|
|
b36fdf2f54 | ||
|
|
73ba6b385f | ||
|
|
30d1639aa4 | ||
|
|
d82814fc86 | ||
|
|
2220efd682 | ||
|
|
679b365cf7 | ||
|
|
83bf83b6dd | ||
|
|
321364c7c2 |
@@ -2,7 +2,7 @@ toml11
|
|||||||
======
|
======
|
||||||
|
|
||||||
[](https://travis-ci.org/ToruNiina/toml11)
|
[](https://travis-ci.org/ToruNiina/toml11)
|
||||||
[](https://ci.appveyor.com/project/ToruNiina/toml11)
|
[](https://ci.appveyor.com/project/ToruNiina/toml11/branch/master)
|
||||||
[](https://github.com/ToruNiina/toml11/releases)
|
[](https://github.com/ToruNiina/toml11/releases)
|
||||||
[](LICENSE)
|
[](LICENSE)
|
||||||
[](https://doi.org/10.5281/zenodo.1209136)
|
[](https://doi.org/10.5281/zenodo.1209136)
|
||||||
|
|||||||
@@ -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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -39,7 +39,7 @@ inline std::string show_char(const char c)
|
|||||||
else
|
else
|
||||||
{
|
{
|
||||||
std::ostringstream oss;
|
std::ostringstream oss;
|
||||||
oss << std::hex << std::setfill('0') << std::setw(2) << "0x"
|
oss << "0x" << std::hex << std::setfill('0') << std::setw(2)
|
||||||
<< static_cast<int>(c);
|
<< static_cast<int>(c);
|
||||||
return oss.str();
|
return oss.str();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -124,9 +124,9 @@ using lex_escape_unicode_short = sequence<character<'u'>,
|
|||||||
using lex_escape_unicode_long = sequence<character<'U'>,
|
using lex_escape_unicode_long = sequence<character<'U'>,
|
||||||
repeat<lex_hex_dig, exactly<8>>>;
|
repeat<lex_hex_dig, exactly<8>>>;
|
||||||
using lex_escape_seq_char = either<character<'"'>, character<'\\'>,
|
using lex_escape_seq_char = either<character<'"'>, character<'\\'>,
|
||||||
character<'/'>, character<'b'>,
|
character<'b'>, character<'f'>,
|
||||||
character<'f'>, character<'n'>,
|
character<'n'>, character<'r'>,
|
||||||
character<'r'>, character<'t'>,
|
character<'t'>,
|
||||||
lex_escape_unicode_short,
|
lex_escape_unicode_short,
|
||||||
lex_escape_unicode_long
|
lex_escape_unicode_long
|
||||||
>;
|
>;
|
||||||
|
|||||||
300
toml/parser.hpp
300
toml/parser.hpp
@@ -34,7 +34,7 @@ parse_boolean(location<Container>& loc)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
loc.iter() = first; //rollback
|
loc.iter() = first; //rollback
|
||||||
return err(std::string("[error] toml::parse_boolean: "
|
return err(format_underline("[error] toml::parse_boolean: ", loc,
|
||||||
"the next token is not a boolean"));
|
"the next token is not a boolean"));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -63,7 +63,7 @@ parse_binary_integer(location<Container>& loc)
|
|||||||
return ok(std::make_pair(retval, token.unwrap()));
|
return ok(std::make_pair(retval, token.unwrap()));
|
||||||
}
|
}
|
||||||
loc.iter() = first;
|
loc.iter() = first;
|
||||||
return err(std::string("[error] toml::parse_binary_integer:"
|
return err(format_underline("[error] toml::parse_binary_integer:", loc,
|
||||||
"the next token is not an integer"));
|
"the next token is not an integer"));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -84,7 +84,7 @@ parse_octal_integer(location<Container>& loc)
|
|||||||
return ok(std::make_pair(retval, token.unwrap()));
|
return ok(std::make_pair(retval, token.unwrap()));
|
||||||
}
|
}
|
||||||
loc.iter() = first;
|
loc.iter() = first;
|
||||||
return err(std::string("[error] toml::parse_octal_integer:"
|
return err(format_underline("[error] toml::parse_octal_integer:", loc,
|
||||||
"the next token is not an integer"));
|
"the next token is not an integer"));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -105,7 +105,7 @@ parse_hexadecimal_integer(location<Container>& loc)
|
|||||||
return ok(std::make_pair(retval, token.unwrap()));
|
return ok(std::make_pair(retval, token.unwrap()));
|
||||||
}
|
}
|
||||||
loc.iter() = first;
|
loc.iter() = first;
|
||||||
return err(std::string("[error] toml::parse_hexadecimal_integer"
|
return err(format_underline("[error] toml::parse_hexadecimal_integer", loc,
|
||||||
"the next token is not an integer"));
|
"the next token is not an integer"));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -133,7 +133,7 @@ parse_integer(location<Container>& loc)
|
|||||||
return ok(std::make_pair(retval, token.unwrap()));
|
return ok(std::make_pair(retval, token.unwrap()));
|
||||||
}
|
}
|
||||||
loc.iter() = first;
|
loc.iter() = first;
|
||||||
return err(std::string("[error] toml::parse_integer: "
|
return err(format_underline("[error] toml::parse_integer: ", loc,
|
||||||
"the next token is not an integer"));
|
"the next token is not an integer"));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -222,12 +222,13 @@ parse_floating(location<Container>& loc)
|
|||||||
return ok(std::make_pair(v, token.unwrap()));
|
return ok(std::make_pair(v, token.unwrap()));
|
||||||
}
|
}
|
||||||
loc.iter() = first;
|
loc.iter() = first;
|
||||||
return err(std::string("[error] toml::parse_floating: "
|
return err(format_underline("[error] toml::parse_floating: ", loc,
|
||||||
"the next token is not a float"));
|
"the next token is not a float"));
|
||||||
}
|
}
|
||||||
|
|
||||||
template<typename Container>
|
template<typename Container, typename Container2>
|
||||||
std::string read_utf8_codepoint(const region<Container>& reg)
|
std::string read_utf8_codepoint(const region<Container>& reg,
|
||||||
|
/* for err msg */ const location<Container2>& loc)
|
||||||
{
|
{
|
||||||
const auto str = reg.str().substr(1);
|
const auto str = reg.str().substr(1);
|
||||||
std::uint_least32_t codepoint;
|
std::uint_least32_t codepoint;
|
||||||
@@ -247,20 +248,27 @@ std::string read_utf8_codepoint(const region<Container>& reg)
|
|||||||
}
|
}
|
||||||
else if(codepoint < 0x10000) // U+0800...U+FFFF
|
else if(codepoint < 0x10000) // U+0800...U+FFFF
|
||||||
{
|
{
|
||||||
|
if(0xD800 <= codepoint && codepoint <= 0xDFFF)
|
||||||
|
{
|
||||||
|
std::cerr << format_underline("[warning] "
|
||||||
|
"toml::read_utf8_codepoint: codepoints in the range "
|
||||||
|
"[0xD800, 0xDFFF] are not valid UTF-8.",
|
||||||
|
loc, "not a valid UTF-8 codepoint") << std::endl;
|
||||||
|
}
|
||||||
|
assert(codepoint < 0xD800 || 0xDFFF < codepoint);
|
||||||
// 1110yyyy 10yxxxxx 10xxxxxx
|
// 1110yyyy 10yxxxxx 10xxxxxx
|
||||||
character += static_cast<unsigned char>(0xE0| codepoint >> 12);
|
character += static_cast<unsigned char>(0xE0| codepoint >> 12);
|
||||||
character += static_cast<unsigned char>(0x80|(codepoint >> 6 & 0x3F));
|
character += static_cast<unsigned char>(0x80|(codepoint >> 6 & 0x3F));
|
||||||
character += static_cast<unsigned char>(0x80|(codepoint & 0x3F));
|
character += static_cast<unsigned char>(0x80|(codepoint & 0x3F));
|
||||||
}
|
}
|
||||||
else if(codepoint < 0x200000) // U+10000 ... U+1FFFFF
|
else if(codepoint < 0x200000) // U+010000 ... U+1FFFFF
|
||||||
{
|
{
|
||||||
if(0x10FFFF < codepoint) // out of Unicode region
|
if(0x10FFFF < codepoint) // out of Unicode region
|
||||||
{
|
{
|
||||||
std::cerr << format_underline(concat_to_string("[warning] "
|
std::cerr << format_underline("[error] "
|
||||||
"input codepoint (", str, ") is too large to decode as "
|
"toml::read_utf8_codepoint: input codepoint is too large to "
|
||||||
"a unicode character. The result may not be able to render "
|
"decode as a unicode character.", loc,
|
||||||
"to your screen."), reg, "should be in [0x00..0x10FFFF]")
|
"should be in [0x00..0x10FFFF]") << std::endl;
|
||||||
<< std::endl;
|
|
||||||
}
|
}
|
||||||
// 11110yyy 10yyxxxx 10xxxxxx 10xxxxxx
|
// 11110yyy 10yyxxxx 10xxxxxx 10xxxxxx
|
||||||
character += static_cast<unsigned char>(0xF0| codepoint >> 18);
|
character += static_cast<unsigned char>(0xF0| codepoint >> 18);
|
||||||
@@ -283,7 +291,7 @@ result<std::string, std::string> parse_escape_sequence(location<Container>& loc)
|
|||||||
const auto first = loc.iter();
|
const auto first = loc.iter();
|
||||||
if(first == loc.end() || *first != '\\')
|
if(first == loc.end() || *first != '\\')
|
||||||
{
|
{
|
||||||
return err(std::string("[error]: toml::parse_escape_sequence: "
|
return err(format_underline("[error]: toml::parse_escape_sequence: ", loc,
|
||||||
"the next token is not an escape sequence \"\\\""));
|
"the next token is not an escape sequence \"\\\""));
|
||||||
}
|
}
|
||||||
++loc.iter();
|
++loc.iter();
|
||||||
@@ -300,7 +308,7 @@ result<std::string, std::string> parse_escape_sequence(location<Container>& loc)
|
|||||||
{
|
{
|
||||||
if(const auto token = lex_escape_unicode_short::invoke(loc))
|
if(const auto token = lex_escape_unicode_short::invoke(loc))
|
||||||
{
|
{
|
||||||
return ok(read_utf8_codepoint(token.unwrap()));
|
return ok(read_utf8_codepoint(token.unwrap(), loc));
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@@ -313,7 +321,7 @@ result<std::string, std::string> parse_escape_sequence(location<Container>& loc)
|
|||||||
{
|
{
|
||||||
if(const auto token = lex_escape_unicode_long::invoke(loc))
|
if(const auto token = lex_escape_unicode_long::invoke(loc))
|
||||||
{
|
{
|
||||||
return ok(read_utf8_codepoint(token.unwrap()));
|
return ok(read_utf8_codepoint(token.unwrap(), loc));
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@@ -523,7 +531,7 @@ parse_string(location<Container>& loc)
|
|||||||
if(const auto rslt = parse_ml_literal_string(loc)) {return rslt;}
|
if(const auto rslt = parse_ml_literal_string(loc)) {return rslt;}
|
||||||
if(const auto rslt = parse_basic_string(loc)) {return rslt;}
|
if(const auto rslt = parse_basic_string(loc)) {return rslt;}
|
||||||
if(const auto rslt = parse_literal_string(loc)) {return rslt;}
|
if(const auto rslt = parse_literal_string(loc)) {return rslt;}
|
||||||
return err(std::string("[error] toml::parse_string: "
|
return err(format_underline("[error] toml::parse_string: ", loc,
|
||||||
"the next token is not a string"));
|
"the next token is not a string"));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -573,7 +581,7 @@ parse_local_date(location<Container>& loc)
|
|||||||
else
|
else
|
||||||
{
|
{
|
||||||
loc.iter() = first;
|
loc.iter() = first;
|
||||||
return err(std::string("[error]: toml::parse_local_date: "
|
return err(format_underline("[error]: toml::parse_local_date: ", loc,
|
||||||
"the next token is not a local_date"));
|
"the next token is not a local_date"));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -656,7 +664,7 @@ parse_local_time(location<Container>& loc)
|
|||||||
else
|
else
|
||||||
{
|
{
|
||||||
loc.iter() = first;
|
loc.iter() = first;
|
||||||
return err(std::string("[error]: toml::parse_local_time: "
|
return err(format_underline("[error]: toml::parse_local_time: ", loc,
|
||||||
"the next token is not a local_time"));
|
"the next token is not a local_time"));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -699,7 +707,7 @@ parse_local_datetime(location<Container>& loc)
|
|||||||
else
|
else
|
||||||
{
|
{
|
||||||
loc.iter() = first;
|
loc.iter() = first;
|
||||||
return err(std::string("[error]: toml::parse_local_datetime: "
|
return err(format_underline("[error]: toml::parse_local_datetime: ", loc,
|
||||||
"the next token is not a local_datetime"));
|
"the next token is not a local_datetime"));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -748,39 +756,43 @@ parse_offset_datetime(location<Container>& loc)
|
|||||||
else
|
else
|
||||||
{
|
{
|
||||||
loc.iter() = first;
|
loc.iter() = first;
|
||||||
return err(std::string("[error]: toml::parse_offset_datetime: "
|
return err(format_underline("[error]: toml::parse_offset_datetime: ", loc,
|
||||||
"the next token is not a local_datetime"));
|
"the next token is not a local_datetime"));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
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(std::string("[error] toml::parse_simple_key: "
|
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"));
|
||||||
}
|
}
|
||||||
|
|
||||||
// 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 +800,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,17 +825,17 @@ 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(std::string("[error] toml::parse_key: "
|
return err(format_underline("[error] toml::parse_key: ", loc, "is not a valid key"));
|
||||||
"the next token is not a key"));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// forward-decl to implement parse_array and parse_table
|
// forward-decl to implement parse_array and parse_table
|
||||||
@@ -864,16 +876,39 @@ parse_array(location<Container>& loc)
|
|||||||
{
|
{
|
||||||
if(!retval.empty() && retval.front().type() != val.as_ok().type())
|
if(!retval.empty() && retval.front().type() != val.as_ok().type())
|
||||||
{
|
{
|
||||||
throw syntax_error(format_underline(
|
auto array_start_loc = loc;
|
||||||
"[error] toml::parse_array: type of elements should be the "
|
array_start_loc.iter() = first;
|
||||||
"same each other.", region<Container>(loc, first, loc.iter()),
|
|
||||||
"inhomogeneous types"));
|
throw syntax_error(format_underline("[error] toml::parse_array: "
|
||||||
|
"type of elements should be the same each other.",
|
||||||
|
std::vector<std::pair<region_base const*, std::string>>{
|
||||||
|
std::make_pair(
|
||||||
|
std::addressof(array_start_loc),
|
||||||
|
std::string("array starts here")
|
||||||
|
),
|
||||||
|
std::make_pair(
|
||||||
|
std::addressof(get_region(retval.front())),
|
||||||
|
std::string("value has type ") +
|
||||||
|
stringize(retval.front().type())
|
||||||
|
),
|
||||||
|
std::make_pair(
|
||||||
|
std::addressof(get_region(val.unwrap())),
|
||||||
|
std::string("value has different type, ") +
|
||||||
|
stringize(val.unwrap().type())
|
||||||
|
)
|
||||||
|
}));
|
||||||
}
|
}
|
||||||
retval.push_back(std::move(val.unwrap()));
|
retval.push_back(std::move(val.unwrap()));
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
return err(val.unwrap_err());
|
auto array_start_loc = loc;
|
||||||
|
array_start_loc.iter() = first;
|
||||||
|
|
||||||
|
throw syntax_error(format_underline("[error] toml::parse_array: "
|
||||||
|
"value having invalid format appeared in an array",
|
||||||
|
array_start_loc, "array starts here",
|
||||||
|
loc, "it is not a valid value."));
|
||||||
}
|
}
|
||||||
|
|
||||||
using lex_array_separator = sequence<maybe<lex_ws>, character<','>>;
|
using lex_array_separator = sequence<maybe<lex_ws>, character<','>>;
|
||||||
@@ -889,8 +924,12 @@ parse_array(location<Container>& loc)
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
auto array_start_loc = loc;
|
||||||
|
array_start_loc.iter() = first;
|
||||||
|
|
||||||
throw syntax_error(format_underline("[error] toml::parse_array:"
|
throw syntax_error(format_underline("[error] toml::parse_array:"
|
||||||
" missing array separator `,`", loc, "should be `,`"));
|
" missing array separator `,` after a value",
|
||||||
|
array_start_loc, "array starts here", loc, "should be `,`"));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -900,14 +939,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))
|
||||||
@@ -948,6 +987,7 @@ parse_key_value_pair(location<Container>& loc)
|
|||||||
{
|
{
|
||||||
std::string msg;
|
std::string msg;
|
||||||
loc.iter() = after_kvsp;
|
loc.iter() = after_kvsp;
|
||||||
|
// check there is something not a comment/whitespace after `=`
|
||||||
if(sequence<maybe<lex_ws>, maybe<lex_comment>, lex_newline>::invoke(loc))
|
if(sequence<maybe<lex_ws>, maybe<lex_comment>, lex_newline>::invoke(loc))
|
||||||
{
|
{
|
||||||
loc.iter() = after_kvsp;
|
loc.iter() = after_kvsp;
|
||||||
@@ -955,15 +995,15 @@ parse_key_value_pair(location<Container>& loc)
|
|||||||
"missing value after key-value separator '='", loc,
|
"missing value after key-value separator '='", loc,
|
||||||
"expected value, but got nothing");
|
"expected value, but got nothing");
|
||||||
}
|
}
|
||||||
else
|
else // there is something not a comment/whitespace, so invalid format.
|
||||||
{
|
{
|
||||||
msg = format_underline("[error] toml::parse_key_value_pair: "
|
msg = std::move(val.unwrap_err());
|
||||||
"invalid value format", loc, val.unwrap_err());
|
|
||||||
}
|
}
|
||||||
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.
|
||||||
@@ -982,10 +1022,80 @@ std::string format_dotted_keys(InputIterator first, const InputIterator last)
|
|||||||
return retval;
|
return retval;
|
||||||
}
|
}
|
||||||
|
|
||||||
template<typename InputIterator>
|
// 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>
|
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,
|
||||||
@@ -1067,25 +1177,39 @@ 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);
|
||||||
}
|
}
|
||||||
}
|
} // end if(array of table)
|
||||||
|
|
||||||
if(tab->count(k) == 1)
|
if(tab->count(k) == 1)
|
||||||
{
|
{
|
||||||
if(tab->at(k).is(value_t::Table) && v.is(value_t::Table))
|
if(tab->at(k).is(value_t::Table) && v.is(value_t::Table))
|
||||||
|
{
|
||||||
|
if(!is_valid_forward_table_definition(
|
||||||
|
tab->at(k), first, iter, last))
|
||||||
{
|
{
|
||||||
throw syntax_error(format_underline(concat_to_string(
|
throw syntax_error(format_underline(concat_to_string(
|
||||||
"[error] toml::insert_value: table (\"",
|
"[error] toml::insert_value: table (\"",
|
||||||
format_dotted_keys(first, last), "\") already exists."),
|
format_dotted_keys(first, last),
|
||||||
|
"\") already exists."),
|
||||||
get_region(tab->at(k)), "table already exists here",
|
get_region(tab->at(k)), "table already exists here",
|
||||||
get_region(v), "table defined twice"));
|
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) &&
|
else if(v.is(value_t::Table) &&
|
||||||
tab->at(k).is(value_t::Array) &&
|
tab->at(k).is(value_t::Array) &&
|
||||||
tab->at(k).cast<value_t::Array>().size() > 0 &&
|
tab->at(k).cast<value_t::Array>().size() > 0 &&
|
||||||
@@ -1104,7 +1228,7 @@ insert_nested_key(table& root, const toml::value& v,
|
|||||||
"[error] toml::insert_value: value (\"",
|
"[error] toml::insert_value: value (\"",
|
||||||
format_dotted_keys(first, last), "\") already exists."),
|
format_dotted_keys(first, last), "\") already exists."),
|
||||||
get_region(tab->at(k)), "value already exists here",
|
get_region(tab->at(k)), "value already exists here",
|
||||||
get_region(v), "value inserted twice"));
|
get_region(v), "value defined twice"));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
tab->insert(std::make_pair(k, v));
|
tab->insert(std::make_pair(k, v));
|
||||||
@@ -1120,10 +1244,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...
|
||||||
@@ -1167,7 +1288,7 @@ parse_inline_table(location<Container>& loc)
|
|||||||
table retval;
|
table retval;
|
||||||
if(!(loc.iter() != loc.end() && *loc.iter() == '{'))
|
if(!(loc.iter() != loc.end() && *loc.iter() == '{'))
|
||||||
{
|
{
|
||||||
return err(std::string("[error] toml::parse_inline_table: "
|
return err(format_underline("[error] toml::parse_inline_table: ", loc,
|
||||||
"the next token is not an inline table"));
|
"the next token is not an inline table"));
|
||||||
}
|
}
|
||||||
++loc.iter();
|
++loc.iter();
|
||||||
@@ -1187,11 +1308,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 region<Container>& key_reg = kv_r.unwrap().first.second;
|
||||||
const value& val = kv_r.unwrap().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: "
|
||||||
@@ -1228,7 +1350,7 @@ result<value, std::string> parse_value(location<Container>& loc)
|
|||||||
const auto first = loc.iter();
|
const auto first = loc.iter();
|
||||||
if(first == loc.end())
|
if(first == loc.end())
|
||||||
{
|
{
|
||||||
return err(std::string("[error] toml::parse_value: input is empty"));
|
return err(format_underline("[error] toml::parse_value: input is empty", loc, ""));
|
||||||
}
|
}
|
||||||
if(auto r = parse_string (loc))
|
if(auto r = parse_string (loc))
|
||||||
{return ok(value(std::move(r.unwrap().first), std::move(r.unwrap().second)));}
|
{return ok(value(std::move(r.unwrap().first), std::move(r.unwrap().second)));}
|
||||||
@@ -1289,7 +1411,21 @@ 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()));
|
|
||||||
|
// after [table.key], newline or EOF(empty table) requried.
|
||||||
|
if(loc.iter() != loc.end())
|
||||||
|
{
|
||||||
|
using lex_newline_after_table_key =
|
||||||
|
sequence<maybe<lex_ws>, maybe<lex_comment>, lex_newline>;
|
||||||
|
const auto nl = lex_newline_after_table_key::invoke(loc);
|
||||||
|
if(!nl)
|
||||||
|
{
|
||||||
|
throw syntax_error(format_underline("[error] "
|
||||||
|
"toml::parse_table_key: newline required after [table.key]",
|
||||||
|
loc, "expected newline"));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return ok(std::make_pair(keys.unwrap().first, token.unwrap()));
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@@ -1327,7 +1463,21 @@ 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()));
|
|
||||||
|
// after [[table.key]], newline or EOF(empty table) requried.
|
||||||
|
if(loc.iter() != loc.end())
|
||||||
|
{
|
||||||
|
using lex_newline_after_table_key =
|
||||||
|
sequence<maybe<lex_ws>, maybe<lex_comment>, lex_newline>;
|
||||||
|
const auto nl = lex_newline_after_table_key::invoke(loc);
|
||||||
|
if(!nl)
|
||||||
|
{
|
||||||
|
throw syntax_error(format_underline("[error] "
|
||||||
|
"toml::parse_array_table_key: newline required after "
|
||||||
|
"[[table.key]]", loc, "expected newline"));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return ok(std::make_pair(keys.unwrap().first, token.unwrap()));
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@@ -1342,7 +1492,7 @@ result<table, std::string> parse_ml_table(location<Container>& loc)
|
|||||||
const auto first = loc.iter();
|
const auto first = loc.iter();
|
||||||
if(first == loc.end())
|
if(first == loc.end())
|
||||||
{
|
{
|
||||||
return err(std::string("toml::parse_ml_table: input is empty"));
|
return ok(toml::table{});
|
||||||
}
|
}
|
||||||
|
|
||||||
// XXX at lest one newline is needed.
|
// XXX at lest one newline is needed.
|
||||||
@@ -1368,10 +1518,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 region<Container>& key_reg = kv.unwrap().first.second;
|
||||||
const value& val = kv.unwrap().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());
|
||||||
@@ -1420,11 +1571,11 @@ result<table, std::string> parse_toml_file(location<Container>& loc)
|
|||||||
const auto first = loc.iter();
|
const auto first = loc.iter();
|
||||||
if(first == loc.end())
|
if(first == loc.end())
|
||||||
{
|
{
|
||||||
return err(std::string("toml::detail::parse_toml_file: input is empty"));
|
return ok(toml::table{});
|
||||||
}
|
}
|
||||||
|
|
||||||
table data;
|
table data;
|
||||||
/* root object is also table, but without [tablename] */
|
// root object is also a table, but without [tablename]
|
||||||
if(auto tab = parse_ml_table(loc))
|
if(auto tab = parse_ml_table(loc))
|
||||||
{
|
{
|
||||||
data = std::move(tab.unwrap());
|
data = std::move(tab.unwrap());
|
||||||
@@ -1449,7 +1600,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;
|
||||||
@@ -1463,7 +1615,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;
|
||||||
|
|||||||
251
toml/region.hpp
251
toml/region.hpp
@@ -28,44 +28,6 @@ inline std::string make_string(std::size_t len, char c)
|
|||||||
return std::string(len, c);
|
return std::string(len, c);
|
||||||
}
|
}
|
||||||
|
|
||||||
// location in a container, normally in a file content.
|
|
||||||
// shared_ptr points the resource that the iter points.
|
|
||||||
// it can be used not only for resource handling, but also error message.
|
|
||||||
template<typename Container>
|
|
||||||
struct location
|
|
||||||
{
|
|
||||||
static_assert(std::is_same<char, typename Container::value_type>::value,"");
|
|
||||||
using const_iterator = typename Container::const_iterator;
|
|
||||||
using source_ptr = std::shared_ptr<const Container>;
|
|
||||||
|
|
||||||
location(std::string name, Container cont)
|
|
||||||
: source_(std::make_shared<Container>(std::move(cont))),
|
|
||||||
source_name_(std::move(name)), iter_(source_->cbegin())
|
|
||||||
{}
|
|
||||||
location(const location&) = default;
|
|
||||||
location(location&&) = default;
|
|
||||||
location& operator=(const location&) = default;
|
|
||||||
location& operator=(location&&) = default;
|
|
||||||
~location() = default;
|
|
||||||
|
|
||||||
const_iterator& iter() noexcept {return iter_;}
|
|
||||||
const_iterator iter() const noexcept {return iter_;}
|
|
||||||
|
|
||||||
const_iterator begin() const noexcept {return source_->cbegin();}
|
|
||||||
const_iterator end() const noexcept {return source_->cend();}
|
|
||||||
|
|
||||||
source_ptr const& source() const& noexcept {return source_;}
|
|
||||||
source_ptr&& source() && noexcept {return std::move(source_);}
|
|
||||||
|
|
||||||
std::string const& name() const noexcept {return source_name_;}
|
|
||||||
|
|
||||||
private:
|
|
||||||
|
|
||||||
source_ptr source_;
|
|
||||||
std::string source_name_;
|
|
||||||
const_iterator iter_;
|
|
||||||
};
|
|
||||||
|
|
||||||
// region in a container, normally in a file content.
|
// region in a container, normally in a file content.
|
||||||
// shared_ptr points the resource that the iter points.
|
// shared_ptr points the resource that the iter points.
|
||||||
// combinators returns this.
|
// combinators returns this.
|
||||||
@@ -86,12 +48,89 @@ struct region_base
|
|||||||
virtual std::string line() const {return std::string("unknown line");}
|
virtual std::string line() const {return std::string("unknown line");}
|
||||||
virtual std::string line_num() const {return std::string("?");}
|
virtual std::string line_num() const {return std::string("?");}
|
||||||
|
|
||||||
|
|
||||||
virtual std::size_t before() const noexcept {return 0;}
|
virtual std::size_t before() const noexcept {return 0;}
|
||||||
virtual std::size_t size() const noexcept {return 0;}
|
virtual std::size_t size() const noexcept {return 0;}
|
||||||
virtual std::size_t after() const noexcept {return 0;}
|
virtual std::size_t after() const noexcept {return 0;}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// location in a container, normally in a file content.
|
||||||
|
// shared_ptr points the resource that the iter points.
|
||||||
|
// it can be used not only for resource handling, but also error message.
|
||||||
|
//
|
||||||
|
// it can be considered as a region that contains only one character.
|
||||||
|
template<typename Container>
|
||||||
|
struct location final : public region_base
|
||||||
|
{
|
||||||
|
static_assert(std::is_same<char, typename Container::value_type>::value,"");
|
||||||
|
using const_iterator = typename Container::const_iterator;
|
||||||
|
using source_ptr = std::shared_ptr<const Container>;
|
||||||
|
|
||||||
|
location(std::string name, Container cont)
|
||||||
|
: source_(std::make_shared<Container>(std::move(cont))),
|
||||||
|
source_name_(std::move(name)), iter_(source_->cbegin())
|
||||||
|
{}
|
||||||
|
location(const location&) = default;
|
||||||
|
location(location&&) = default;
|
||||||
|
location& operator=(const location&) = default;
|
||||||
|
location& operator=(location&&) = default;
|
||||||
|
~location() = default;
|
||||||
|
|
||||||
|
bool is_ok() const noexcept override {return static_cast<bool>(source_);}
|
||||||
|
|
||||||
|
const_iterator& iter() noexcept {return iter_;}
|
||||||
|
const_iterator iter() const noexcept {return iter_;}
|
||||||
|
|
||||||
|
const_iterator begin() const noexcept {return source_->cbegin();}
|
||||||
|
const_iterator end() const noexcept {return source_->cend();}
|
||||||
|
|
||||||
|
std::string str() const override {return make_string(1, *this->iter());}
|
||||||
|
std::string name() const override {return source_name_;}
|
||||||
|
|
||||||
|
std::string line_num() const override
|
||||||
|
{
|
||||||
|
return std::to_string(1+std::count(this->begin(), this->iter(), '\n'));
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string line() const override
|
||||||
|
{
|
||||||
|
return make_string(this->line_begin(), this->line_end());
|
||||||
|
}
|
||||||
|
|
||||||
|
const_iterator line_begin() const noexcept
|
||||||
|
{
|
||||||
|
using reverse_iterator = std::reverse_iterator<const_iterator>;
|
||||||
|
return std::find(reverse_iterator(this->iter()),
|
||||||
|
reverse_iterator(this->begin()), '\n').base();
|
||||||
|
}
|
||||||
|
const_iterator line_end() const noexcept
|
||||||
|
{
|
||||||
|
return std::find(this->iter(), this->end(), '\n');
|
||||||
|
}
|
||||||
|
|
||||||
|
// location is always points a character. so the size is 1.
|
||||||
|
std::size_t size() const noexcept override
|
||||||
|
{
|
||||||
|
return 1u;
|
||||||
|
}
|
||||||
|
std::size_t before() const noexcept override
|
||||||
|
{
|
||||||
|
return std::distance(this->line_begin(), this->iter());
|
||||||
|
}
|
||||||
|
std::size_t after() const noexcept override
|
||||||
|
{
|
||||||
|
return std::distance(this->iter(), this->line_end());
|
||||||
|
}
|
||||||
|
|
||||||
|
source_ptr const& source() const& noexcept {return source_;}
|
||||||
|
source_ptr&& source() && noexcept {return std::move(source_);}
|
||||||
|
|
||||||
|
private:
|
||||||
|
|
||||||
|
source_ptr source_;
|
||||||
|
std::string source_name_;
|
||||||
|
const_iterator iter_;
|
||||||
|
};
|
||||||
|
|
||||||
template<typename Container>
|
template<typename Container>
|
||||||
struct region final : public region_base
|
struct region final : public region_base
|
||||||
{
|
{
|
||||||
@@ -225,7 +264,19 @@ inline std::string format_underline(const std::string& message,
|
|||||||
retval += make_string(line_number.size() + 1, ' ');
|
retval += make_string(line_number.size() + 1, ' ');
|
||||||
retval += " | ";
|
retval += " | ";
|
||||||
retval += make_string(reg.before(), ' ');
|
retval += make_string(reg.before(), ' ');
|
||||||
|
if(reg.size() == 1)
|
||||||
|
{
|
||||||
|
// invalid
|
||||||
|
// ^------
|
||||||
|
retval += '^';
|
||||||
|
retval += make_string(reg.after(), '-');
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// invalid
|
||||||
|
// ~~~~~~~
|
||||||
retval += make_string(reg.size(), '~');
|
retval += make_string(reg.size(), '~');
|
||||||
|
}
|
||||||
retval += ' ';
|
retval += ' ';
|
||||||
retval += comment_for_underline;
|
retval += comment_for_underline;
|
||||||
if(helps.size() != 0)
|
if(helps.size() != 0)
|
||||||
@@ -270,7 +321,19 @@ inline std::string format_underline(const std::string& message,
|
|||||||
retval << make_string(line_num_width + 1, ' ');
|
retval << make_string(line_num_width + 1, ' ');
|
||||||
retval << " | ";
|
retval << " | ";
|
||||||
retval << make_string(reg1.before(), ' ');
|
retval << make_string(reg1.before(), ' ');
|
||||||
|
if(reg1.size() == 1)
|
||||||
|
{
|
||||||
|
// invalid
|
||||||
|
// ^------
|
||||||
|
retval << '^';
|
||||||
|
retval << make_string(reg1.after(), '-');
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// invalid
|
||||||
|
// ~~~~~~~
|
||||||
retval << make_string(reg1.size(), '~');
|
retval << make_string(reg1.size(), '~');
|
||||||
|
}
|
||||||
retval << ' ';
|
retval << ' ';
|
||||||
retval << comment_for_underline1 << newline;
|
retval << comment_for_underline1 << newline;
|
||||||
// ---------------------------------------
|
// ---------------------------------------
|
||||||
@@ -287,7 +350,19 @@ inline std::string format_underline(const std::string& message,
|
|||||||
retval << make_string(line_num_width + 1, ' ');
|
retval << make_string(line_num_width + 1, ' ');
|
||||||
retval << " | ";
|
retval << " | ";
|
||||||
retval << make_string(reg2.before(), ' ');
|
retval << make_string(reg2.before(), ' ');
|
||||||
|
if(reg2.size() == 1)
|
||||||
|
{
|
||||||
|
// invalid
|
||||||
|
// ^------
|
||||||
|
retval << '^';
|
||||||
|
retval << make_string(reg2.after(), '-');
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// invalid
|
||||||
|
// ~~~~~~~
|
||||||
retval << make_string(reg2.size(), '~');
|
retval << make_string(reg2.size(), '~');
|
||||||
|
}
|
||||||
retval << ' ';
|
retval << ' ';
|
||||||
retval << comment_for_underline2;
|
retval << comment_for_underline2;
|
||||||
if(helps.size() != 0)
|
if(helps.size() != 0)
|
||||||
@@ -305,62 +380,84 @@ inline std::string format_underline(const std::string& message,
|
|||||||
return retval.str();
|
return retval.str();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// to show a better error message.
|
// to show a better error message.
|
||||||
template<typename Container>
|
inline std::string format_underline(const std::string& message,
|
||||||
std::string
|
std::vector<std::pair<region_base const*, std::string>> reg_com,
|
||||||
format_underline(const std::string& message, const location<Container>& loc,
|
|
||||||
const std::string& comment_for_underline,
|
|
||||||
std::vector<std::string> helps = {})
|
std::vector<std::string> helps = {})
|
||||||
{
|
{
|
||||||
|
assert(!reg_com.empty());
|
||||||
|
|
||||||
#ifdef _WIN32
|
#ifdef _WIN32
|
||||||
const auto newline = "\r\n";
|
const auto newline = "\r\n";
|
||||||
#else
|
#else
|
||||||
const char newline = '\n';
|
const char newline = '\n';
|
||||||
#endif
|
#endif
|
||||||
using const_iterator = typename location<Container>::const_iterator;
|
|
||||||
using reverse_iterator = std::reverse_iterator<const_iterator>;
|
|
||||||
const auto line_begin = std::find(reverse_iterator(loc.iter()),
|
|
||||||
reverse_iterator(loc.begin()),
|
|
||||||
'\n').base();
|
|
||||||
const auto line_end = std::find(loc.iter(), loc.end(), '\n');
|
|
||||||
|
|
||||||
const auto line_number = std::to_string(
|
const auto line_num_width = std::max_element(reg_com.begin(), reg_com.end(),
|
||||||
1 + std::count(loc.begin(), loc.iter(), '\n'));
|
[](std::pair<region_base const*, std::string> const& lhs,
|
||||||
|
std::pair<region_base const*, std::string> const& rhs)
|
||||||
|
{
|
||||||
|
return lhs.first->line_num().size() < rhs.first->line_num().size();
|
||||||
|
}
|
||||||
|
)->first->line_num().size();
|
||||||
|
|
||||||
|
std::ostringstream retval;
|
||||||
|
retval << message << newline;
|
||||||
|
|
||||||
|
for(std::size_t i=0; i<reg_com.size(); ++i)
|
||||||
|
{
|
||||||
|
if(i!=0 && reg_com.at(i-1).first->name() == reg_com.at(i).first->name())
|
||||||
|
{
|
||||||
|
retval << " ..." << newline;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
retval << " --> " << reg_com.at(i).first->name() << newline;
|
||||||
|
}
|
||||||
|
|
||||||
|
const region_base* const reg = reg_com.at(i).first;
|
||||||
|
const std::string& comment = reg_com.at(i).second;
|
||||||
|
|
||||||
|
|
||||||
|
retval << ' ' << std::setw(line_num_width) << reg->line_num();
|
||||||
|
retval << " | " << reg->line() << newline;
|
||||||
|
retval << make_string(line_num_width + 1, ' ');
|
||||||
|
retval << " | " << make_string(reg->before(), ' ');
|
||||||
|
|
||||||
|
if(reg->size() == 1)
|
||||||
|
{
|
||||||
|
// invalid
|
||||||
|
// ^------
|
||||||
|
retval << '^';
|
||||||
|
retval << make_string(reg->after(), '-');
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// invalid
|
||||||
|
// ~~~~~~~
|
||||||
|
retval << make_string(reg->size(), '~');
|
||||||
|
}
|
||||||
|
|
||||||
|
retval << ' ';
|
||||||
|
retval << comment << newline;
|
||||||
|
}
|
||||||
|
|
||||||
std::string retval;
|
|
||||||
retval += message;
|
|
||||||
retval += newline;
|
|
||||||
retval += " --> ";
|
|
||||||
retval += loc.name();
|
|
||||||
retval += newline;
|
|
||||||
retval += ' ';
|
|
||||||
retval += line_number;
|
|
||||||
retval += " | ";
|
|
||||||
retval += make_string(line_begin, line_end);
|
|
||||||
retval += newline;
|
|
||||||
retval += make_string(line_number.size() + 1, ' ');
|
|
||||||
retval += " | ";
|
|
||||||
retval += make_string(std::distance(line_begin, loc.iter()),' ');
|
|
||||||
retval += '^';
|
|
||||||
retval += make_string(std::distance(loc.iter(), line_end), '-');
|
|
||||||
retval += ' ';
|
|
||||||
retval += comment_for_underline;
|
|
||||||
if(helps.size() != 0)
|
if(helps.size() != 0)
|
||||||
{
|
{
|
||||||
retval += newline;
|
retval << newline;
|
||||||
retval += make_string(line_number.size() + 1, ' ');
|
retval << make_string(line_num_width + 1, ' ');
|
||||||
retval += " | ";
|
retval << " | ";
|
||||||
for(const auto help : helps)
|
for(const auto help : helps)
|
||||||
{
|
{
|
||||||
retval += newline;
|
retval << newline;
|
||||||
retval += "Hint: ";
|
retval << "Hint: ";
|
||||||
retval += help;
|
retval << help;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return retval;
|
return retval.str();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
} // detail
|
} // detail
|
||||||
} // toml
|
} // toml
|
||||||
#endif// TOML11_REGION_H
|
#endif// TOML11_REGION_H
|
||||||
|
|||||||
@@ -29,8 +29,9 @@ inline void resize_impl(T& container, std::size_t N, std::true_type)
|
|||||||
template<typename T>
|
template<typename T>
|
||||||
inline void resize_impl(T& container, std::size_t N, std::false_type)
|
inline void resize_impl(T& container, std::size_t N, std::false_type)
|
||||||
{
|
{
|
||||||
if(container.size() >= N) return;
|
if(container.size() >= N) {return;}
|
||||||
else throw std::invalid_argument("not resizable type");
|
|
||||||
|
throw std::invalid_argument("not resizable type");
|
||||||
}
|
}
|
||||||
|
|
||||||
} // detail
|
} // detail
|
||||||
@@ -38,8 +39,9 @@ inline void resize_impl(T& container, std::size_t N, std::false_type)
|
|||||||
template<typename T>
|
template<typename T>
|
||||||
inline void resize(T& container, std::size_t N)
|
inline void resize(T& container, std::size_t N)
|
||||||
{
|
{
|
||||||
if(container.size() == N) return;
|
if(container.size() == N) {return;}
|
||||||
else return detail::resize_impl(container, N, detail::has_resize_method<T>());
|
|
||||||
|
return detail::resize_impl(container, N, detail::has_resize_method<T>());
|
||||||
}
|
}
|
||||||
|
|
||||||
namespace detail
|
namespace detail
|
||||||
@@ -73,7 +75,5 @@ T from_string(const std::string& str, U&& opt)
|
|||||||
return v;
|
return v;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
}// toml
|
}// toml
|
||||||
#endif // TOML11_UTILITY
|
#endif // TOML11_UTILITY
|
||||||
|
|||||||
@@ -21,8 +21,8 @@ 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.
|
template<typename Region>
|
||||||
void assign_keeping_region(value&, value);
|
void change_region(value&, Region&&);
|
||||||
}// detail
|
}// detail
|
||||||
|
|
||||||
template<typename T>
|
template<typename T>
|
||||||
@@ -562,8 +562,8 @@ 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.
|
template<typename Region>
|
||||||
friend void detail::assign_keeping_region(value&, value);
|
friend void detail::change_region(value&, Region&&);
|
||||||
|
|
||||||
template<value_t T>
|
template<value_t T>
|
||||||
struct switch_cast;
|
struct switch_cast;
|
||||||
@@ -599,35 +599,21 @@ 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.
|
template<typename Region>
|
||||||
// To avoid this kind of confusing phenomena, the default assigners clear the
|
void change_region(value& v, Region&& reg)
|
||||||
// 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
|
using region_type = typename std::remove_reference<
|
||||||
// keep region_info_ intact
|
typename std::remove_cv<Region>::type
|
||||||
v.type_ = other.type();
|
>::type;
|
||||||
switch(v.type())
|
|
||||||
{
|
std::shared_ptr<region_base> new_reg =
|
||||||
case value_t::Boolean : ::toml::value::assigner(v.boolean_ , other.boolean_ ); break;
|
std::make_shared<region_type>(std::forward<region_type>(reg));
|
||||||
case value_t::Integer : ::toml::value::assigner(v.integer_ , other.integer_ ); break;
|
v.region_info_ = new_reg;
|
||||||
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;
|
return;
|
||||||
}
|
}
|
||||||
}// detail
|
|
||||||
|
|
||||||
|
}// detail
|
||||||
|
|
||||||
template<> struct value::switch_cast<value_t::Boolean>
|
template<> struct value::switch_cast<value_t::Boolean>
|
||||||
{
|
{
|
||||||
|
|||||||
Reference in New Issue
Block a user