mirror of
https://github.com/ToruNiina/toml11.git
synced 2025-12-16 03:08:52 +08:00
Compare commits
30 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
6185dfee14 | ||
|
|
a74ad23514 | ||
|
|
2d9b4992ec | ||
|
|
82e8c1e68b | ||
|
|
46be054ce9 | ||
|
|
789d784769 | ||
|
|
81deb8efde | ||
|
|
072dccd05d | ||
|
|
637c99d637 | ||
|
|
0f48852730 | ||
|
|
0499b2907d | ||
|
|
61e69c9251 | ||
|
|
4a560ea1e5 | ||
|
|
c5b6ee6f81 | ||
|
|
1a7bf63622 | ||
|
|
8847cdc0a9 | ||
|
|
c82e76a111 | ||
|
|
4db486d76d | ||
|
|
91966a6917 | ||
|
|
b3917aaadf | ||
|
|
ba307003c4 | ||
|
|
21fd1271d9 | ||
|
|
f9ab7d6f56 | ||
|
|
0a3a41a708 | ||
|
|
6c2a536fa5 | ||
|
|
26eced3640 | ||
|
|
6f950c9ec8 | ||
|
|
ea13e40889 | ||
|
|
595fb1aef3 | ||
|
|
18986978fb |
49
README.md
49
README.md
@@ -19,6 +19,28 @@ Not only the test suite itself, a TOML reader/encoder also runs on [CircleCI](ht
|
|||||||
You can see the error messages about invalid files and serialization results of valid files at
|
You can see the error messages about invalid files and serialization results of valid files at
|
||||||
[CircleCI](https://circleci.com/gh/ToruNiina/toml11).
|
[CircleCI](https://circleci.com/gh/ToruNiina/toml11).
|
||||||
|
|
||||||
|
## Example
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
#include <toml11/toml.hpp>
|
||||||
|
#include <iostream>
|
||||||
|
|
||||||
|
int main()
|
||||||
|
{
|
||||||
|
const auto data = toml::parse("example.toml");
|
||||||
|
|
||||||
|
// title = "an example toml file"
|
||||||
|
std::string title = toml::get<std::string>(data.at("title"));
|
||||||
|
std::cout << "the title is " << title << std::endl;
|
||||||
|
|
||||||
|
// nums = [1, 2, 3, 4, 5]
|
||||||
|
std::vector<int> nums = toml::get<std::vector<int>>(data.at("nums"));
|
||||||
|
std::cout << "the length of `nums` is" << nums.size() << std::endl;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
## Table of Contents
|
## Table of Contents
|
||||||
|
|
||||||
- [Integration](#integration)
|
- [Integration](#integration)
|
||||||
@@ -53,12 +75,14 @@ Just include the file after adding it to the include path.
|
|||||||
|
|
||||||
```cpp
|
```cpp
|
||||||
#include <toml11/toml.hpp> // that's all! now you can use it.
|
#include <toml11/toml.hpp> // that's all! now you can use it.
|
||||||
|
#include <iostream>
|
||||||
|
|
||||||
int main()
|
int main()
|
||||||
{
|
{
|
||||||
const auto data = toml::parse("example.toml");
|
const auto data = toml::parse("example.toml");
|
||||||
const auto title = toml::get<std::string>(data.at("title"));
|
const auto title = toml::get<std::string>(data.at("title"));
|
||||||
std::cout << "the title is " << title << std::endl;
|
std::cout << "the title is " << title << std::endl;
|
||||||
|
return 0;
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
@@ -579,6 +603,27 @@ toml::value operator""_toml(const char* str, std::size_t len);
|
|||||||
Access to the operator can be gained with `using namespace toml::literals;`,
|
Access to the operator can be gained with `using namespace toml::literals;`,
|
||||||
`using namespace toml::toml_literals`, and `using namespace toml::literals::toml_literals`.
|
`using namespace toml::toml_literals`, and `using namespace toml::literals::toml_literals`.
|
||||||
|
|
||||||
|
Note that a key that is composed only of digits is allowed in TOML.
|
||||||
|
And, unlike the file parser, toml-literal allows a bare value without a key.
|
||||||
|
Thus it is difficult to distinguish arrays having integers and definitions of
|
||||||
|
tables that are named as digits.
|
||||||
|
Currently, literal `[1]` becomes a table named "1".
|
||||||
|
To ensure a literal to be considered as an array with one element, you need to
|
||||||
|
add a comma after the first element (like `[1,]`).
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
"[1,2,3]"_toml; // This is an array
|
||||||
|
"[table]"_toml; // This is a table that has an empty table named "table" inside.
|
||||||
|
"[[1,2,3]]"_toml; // This is an array of arrays
|
||||||
|
"[[table]]"_toml; // This is a table that has an array of tables inside.
|
||||||
|
|
||||||
|
"[[1]]"_toml; // This literal is ambiguous.
|
||||||
|
// Currently, it becomes a table that has array of table "1".
|
||||||
|
"1 = [{}]"_toml; // This is a table that has an array of table named 1.
|
||||||
|
"[[1,]]"_toml; // This is an array of arrays.
|
||||||
|
"[[1],]"_toml; // ditto.
|
||||||
|
```
|
||||||
|
|
||||||
## Conversion between toml value and arbitrary types
|
## Conversion between toml value and arbitrary types
|
||||||
|
|
||||||
You can also use `toml::get` and other related functions with the types you defined
|
You can also use `toml::get` and other related functions with the types you defined
|
||||||
@@ -937,11 +982,13 @@ I appreciate the help of the contributors who introduced the great feature to th
|
|||||||
- Quentin Khan (@xaxousis)
|
- Quentin Khan (@xaxousis)
|
||||||
- Found & Fixed a bug around ODR
|
- Found & Fixed a bug around ODR
|
||||||
- Improved error messages for invaild keys to show the location where the parser fails
|
- Improved error messages for invaild keys to show the location where the parser fails
|
||||||
|
- Petr Beneš (@wbenny)
|
||||||
|
- Fixed warnings on MSVC
|
||||||
|
|
||||||
## Licensing terms
|
## Licensing terms
|
||||||
|
|
||||||
This product is licensed under the terms of the [MIT License](LICENSE).
|
This product is licensed under the terms of the [MIT License](LICENSE).
|
||||||
|
|
||||||
- Copyright (c) 2017 Toru Niina
|
- Copyright (c) 2017-2019 Toru Niina
|
||||||
|
|
||||||
All rights reserved.
|
All rights reserved.
|
||||||
|
|||||||
@@ -33,6 +33,18 @@ BOOST_AUTO_TEST_CASE(test_file_as_literal)
|
|||||||
b = "baz"
|
b = "baz"
|
||||||
)"_toml;
|
)"_toml;
|
||||||
|
|
||||||
|
BOOST_CHECK_EQUAL(r, v);
|
||||||
|
}
|
||||||
|
{
|
||||||
|
const toml::value r{
|
||||||
|
{"table", toml::table{{"a", 42}, {"b", "baz"}}}
|
||||||
|
};
|
||||||
|
const toml::value v = u8R"(
|
||||||
|
[table]
|
||||||
|
a = 42
|
||||||
|
b = "baz"
|
||||||
|
)"_toml;
|
||||||
|
|
||||||
BOOST_CHECK_EQUAL(r, v);
|
BOOST_CHECK_EQUAL(r, v);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -91,6 +103,19 @@ BOOST_AUTO_TEST_CASE(test_value_as_literal)
|
|||||||
|
|
||||||
BOOST_CHECK(v1.is_array());
|
BOOST_CHECK(v1.is_array());
|
||||||
BOOST_CHECK((toml::get<std::vector<int>>(v1) == std::vector<int>{1,2,3}));
|
BOOST_CHECK((toml::get<std::vector<int>>(v1) == std::vector<int>{1,2,3}));
|
||||||
|
|
||||||
|
const toml::value v2 = u8R"([1,])"_toml;
|
||||||
|
|
||||||
|
BOOST_CHECK(v2.is_array());
|
||||||
|
BOOST_CHECK((toml::get<std::vector<int>>(v2) == std::vector<int>{1}));
|
||||||
|
|
||||||
|
const toml::value v3 = u8R"([[1,]])"_toml;
|
||||||
|
BOOST_CHECK(v3.is_array());
|
||||||
|
BOOST_CHECK((toml::get<std::vector<int>>(toml::get<toml::array>(v3).front()) == std::vector<int>{1}));
|
||||||
|
|
||||||
|
const toml::value v4 = u8R"([[1],])"_toml;
|
||||||
|
BOOST_CHECK(v4.is_array());
|
||||||
|
BOOST_CHECK((toml::get<std::vector<int>>(toml::get<toml::array>(v4).front()) == std::vector<int>{1}));
|
||||||
}
|
}
|
||||||
{
|
{
|
||||||
const toml::value v1 = u8R"({a = 42})"_toml;
|
const toml::value v1 = u8R"({a = 42})"_toml;
|
||||||
|
|||||||
@@ -9,7 +9,10 @@
|
|||||||
#include <type_traits>
|
#include <type_traits>
|
||||||
#include <iterator>
|
#include <iterator>
|
||||||
#include <limits>
|
#include <limits>
|
||||||
|
#include <array>
|
||||||
#include <iomanip>
|
#include <iomanip>
|
||||||
|
#include <cstdio>
|
||||||
|
#include <cassert>
|
||||||
#include <cctype>
|
#include <cctype>
|
||||||
|
|
||||||
// they scans characters and returns region if it matches to the condition.
|
// they scans characters and returns region if it matches to the condition.
|
||||||
@@ -38,10 +41,12 @@ inline std::string show_char(const char c)
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
std::ostringstream oss;
|
std::array<char, 5> buf;
|
||||||
oss << "0x" << std::hex << std::setfill('0') << std::setw(2)
|
buf.fill('\0');
|
||||||
<< static_cast<int>(c);
|
const auto r = std::snprintf(
|
||||||
return oss.str();
|
buf.data(), buf.size(), "0x%02x", static_cast<int>(c) & 0xFF);
|
||||||
|
assert(r == buf.size() - 1);
|
||||||
|
return std::string(buf.data());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -51,7 +56,8 @@ struct character
|
|||||||
static constexpr char target = C;
|
static constexpr char target = C;
|
||||||
|
|
||||||
template<typename Cont>
|
template<typename Cont>
|
||||||
static result<region<Cont>, std::string> invoke(location<Cont>& loc)
|
static result<region<Cont>, std::string>
|
||||||
|
invoke(location<Cont>& loc, const bool msg = false)
|
||||||
{
|
{
|
||||||
static_assert(std::is_same<char, typename Cont::value_type>::value,
|
static_assert(std::is_same<char, typename Cont::value_type>::value,
|
||||||
"internal error: container::value_type should be `char`.");
|
"internal error: container::value_type should be `char`.");
|
||||||
@@ -61,11 +67,15 @@ struct character
|
|||||||
|
|
||||||
const char c = *(loc.iter());
|
const char c = *(loc.iter());
|
||||||
if(c != target)
|
if(c != target)
|
||||||
|
{
|
||||||
|
if(msg)
|
||||||
{
|
{
|
||||||
return err(concat_to_string("expected '", show_char(target),
|
return err(concat_to_string("expected '", show_char(target),
|
||||||
"' but got '", show_char(c), "'."));
|
"' but got '", show_char(c), "'."));
|
||||||
}
|
}
|
||||||
++(loc.iter()); // update location
|
return err("");
|
||||||
|
}
|
||||||
|
loc.advance(); // update location
|
||||||
|
|
||||||
return ok(region<Cont>(loc, first, loc.iter()));
|
return ok(region<Cont>(loc, first, loc.iter()));
|
||||||
}
|
}
|
||||||
@@ -86,7 +96,8 @@ struct in_range
|
|||||||
static constexpr char lower = Low;
|
static constexpr char lower = Low;
|
||||||
|
|
||||||
template<typename Cont>
|
template<typename Cont>
|
||||||
static result<region<Cont>, std::string> invoke(location<Cont>& loc)
|
static result<region<Cont>, std::string>
|
||||||
|
invoke(location<Cont>& loc, const bool msg = false)
|
||||||
{
|
{
|
||||||
static_assert(std::is_same<char, typename Cont::value_type>::value,
|
static_assert(std::is_same<char, typename Cont::value_type>::value,
|
||||||
"internal error: container::value_type should be `char`.");
|
"internal error: container::value_type should be `char`.");
|
||||||
@@ -96,13 +107,17 @@ struct in_range
|
|||||||
|
|
||||||
const char c = *(loc.iter());
|
const char c = *(loc.iter());
|
||||||
if(c < lower || upper < c)
|
if(c < lower || upper < c)
|
||||||
|
{
|
||||||
|
if(msg)
|
||||||
{
|
{
|
||||||
return err(concat_to_string("expected character in range "
|
return err(concat_to_string("expected character in range "
|
||||||
"[", show_char(lower), ", ", show_char(upper), "] but got ",
|
"[", show_char(lower), ", ", show_char(upper), "] but got ",
|
||||||
"'", show_char(c), "'."));
|
"'", show_char(c), "'."));
|
||||||
}
|
}
|
||||||
|
return err("");
|
||||||
|
}
|
||||||
|
|
||||||
++(loc.iter());
|
loc.advance();
|
||||||
return ok(region<Cont>(loc, first, loc.iter()));
|
return ok(region<Cont>(loc, first, loc.iter()));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -120,7 +135,8 @@ template<typename Combinator>
|
|||||||
struct exclude
|
struct exclude
|
||||||
{
|
{
|
||||||
template<typename Cont>
|
template<typename Cont>
|
||||||
static result<region<Cont>, std::string> invoke(location<Cont>& loc)
|
static result<region<Cont>, std::string>
|
||||||
|
invoke(location<Cont>& loc, const bool msg = false)
|
||||||
{
|
{
|
||||||
static_assert(std::is_same<char, typename Cont::value_type>::value,
|
static_assert(std::is_same<char, typename Cont::value_type>::value,
|
||||||
"internal error: container::value_type should be `char`.");
|
"internal error: container::value_type should be `char`.");
|
||||||
@@ -128,15 +144,18 @@ struct exclude
|
|||||||
if(loc.iter() == loc.end()) {return err("not sufficient characters");}
|
if(loc.iter() == loc.end()) {return err("not sufficient characters");}
|
||||||
auto first = loc.iter();
|
auto first = loc.iter();
|
||||||
|
|
||||||
auto rslt = Combinator::invoke(loc);
|
auto rslt = Combinator::invoke(loc, msg);
|
||||||
if(rslt.is_ok())
|
if(rslt.is_ok())
|
||||||
{
|
{
|
||||||
loc.iter() = first; // rollback
|
loc.reset(first);
|
||||||
return err(concat_to_string(
|
if(msg)
|
||||||
"invalid pattern (", Combinator::pattern(), ") appeared ",
|
{
|
||||||
rslt.unwrap().str()));
|
return err(concat_to_string("invalid pattern (",
|
||||||
|
Combinator::pattern(), ") appeared ", rslt.unwrap().str()));
|
||||||
}
|
}
|
||||||
loc.iter() = std::next(first);
|
return err("");
|
||||||
|
}
|
||||||
|
loc.reset(std::next(first)); // XXX maybe loc.advance() is okay but...
|
||||||
return ok(region<Cont>(loc, first, loc.iter()));
|
return ok(region<Cont>(loc, first, loc.iter()));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -151,12 +170,13 @@ template<typename Combinator>
|
|||||||
struct maybe
|
struct maybe
|
||||||
{
|
{
|
||||||
template<typename Cont>
|
template<typename Cont>
|
||||||
static result<region<Cont>, std::string> invoke(location<Cont>& loc)
|
static result<region<Cont>, std::string>
|
||||||
|
invoke(location<Cont>& loc, const bool msg = false)
|
||||||
{
|
{
|
||||||
static_assert(std::is_same<char, typename Cont::value_type>::value,
|
static_assert(std::is_same<char, typename Cont::value_type>::value,
|
||||||
"internal error: container::value_type should be `char`.");
|
"internal error: container::value_type should be `char`.");
|
||||||
|
|
||||||
const auto rslt = Combinator::invoke(loc);
|
const auto rslt = Combinator::invoke(loc, msg);
|
||||||
if(rslt.is_ok())
|
if(rslt.is_ok())
|
||||||
{
|
{
|
||||||
return rslt;
|
return rslt;
|
||||||
@@ -177,34 +197,36 @@ template<typename Head, typename ... Tail>
|
|||||||
struct sequence<Head, Tail...>
|
struct sequence<Head, Tail...>
|
||||||
{
|
{
|
||||||
template<typename Cont>
|
template<typename Cont>
|
||||||
static result<region<Cont>, std::string> invoke(location<Cont>& loc)
|
static result<region<Cont>, std::string>
|
||||||
|
invoke(location<Cont>& loc, const bool msg = false)
|
||||||
{
|
{
|
||||||
static_assert(std::is_same<char, typename Cont::value_type>::value,
|
static_assert(std::is_same<char, typename Cont::value_type>::value,
|
||||||
"internal error: container::value_type should be `char`.");
|
"internal error: container::value_type should be `char`.");
|
||||||
|
|
||||||
const auto first = loc.iter();
|
const auto first = loc.iter();
|
||||||
const auto rslt = Head::invoke(loc);
|
const auto rslt = Head::invoke(loc, msg);
|
||||||
if(rslt.is_err())
|
if(rslt.is_err())
|
||||||
{
|
{
|
||||||
loc.iter() = first;
|
loc.reset(first);
|
||||||
return err(rslt.unwrap_err());
|
return err(rslt.unwrap_err());
|
||||||
}
|
}
|
||||||
return sequence<Tail...>::invoke(loc, std::move(rslt.unwrap()), first);
|
return sequence<Tail...>::invoke(loc, std::move(rslt.unwrap()), first, msg);
|
||||||
}
|
}
|
||||||
|
|
||||||
// called from the above function only, recursively.
|
// called from the above function only, recursively.
|
||||||
template<typename Cont, typename Iterator>
|
template<typename Cont, typename Iterator>
|
||||||
static result<region<Cont>, std::string>
|
static result<region<Cont>, std::string>
|
||||||
invoke(location<Cont>& loc, region<Cont> reg, Iterator first)
|
invoke(location<Cont>& loc, region<Cont> reg, Iterator first,
|
||||||
|
const bool msg = false)
|
||||||
{
|
{
|
||||||
const auto rslt = Head::invoke(loc);
|
const auto rslt = Head::invoke(loc, msg);
|
||||||
if(rslt.is_err())
|
if(rslt.is_err())
|
||||||
{
|
{
|
||||||
loc.iter() = first;
|
loc.reset(first);
|
||||||
return err(rslt.unwrap_err());
|
return err(rslt.unwrap_err());
|
||||||
}
|
}
|
||||||
reg += rslt.unwrap(); // concat regions
|
reg += rslt.unwrap(); // concat regions
|
||||||
return sequence<Tail...>::invoke(loc, std::move(reg), first);
|
return sequence<Tail...>::invoke(loc, std::move(reg), first, msg);
|
||||||
}
|
}
|
||||||
|
|
||||||
static std::string pattern()
|
static std::string pattern()
|
||||||
@@ -219,12 +241,13 @@ struct sequence<Head>
|
|||||||
// would be called from sequence<T ...>::invoke only.
|
// would be called from sequence<T ...>::invoke only.
|
||||||
template<typename Cont, typename Iterator>
|
template<typename Cont, typename Iterator>
|
||||||
static result<region<Cont>, std::string>
|
static result<region<Cont>, std::string>
|
||||||
invoke(location<Cont>& loc, region<Cont> reg, Iterator first)
|
invoke(location<Cont>& loc, region<Cont> reg, Iterator first,
|
||||||
|
const bool msg = false)
|
||||||
{
|
{
|
||||||
const auto rslt = Head::invoke(loc);
|
const auto rslt = Head::invoke(loc, msg);
|
||||||
if(rslt.is_err())
|
if(rslt.is_err())
|
||||||
{
|
{
|
||||||
loc.iter() = first;
|
loc.reset(first);
|
||||||
return err(rslt.unwrap_err());
|
return err(rslt.unwrap_err());
|
||||||
}
|
}
|
||||||
reg += rslt.unwrap(); // concat regions
|
reg += rslt.unwrap(); // concat regions
|
||||||
@@ -240,14 +263,15 @@ template<typename Head, typename ... Tail>
|
|||||||
struct either<Head, Tail...>
|
struct either<Head, Tail...>
|
||||||
{
|
{
|
||||||
template<typename Cont>
|
template<typename Cont>
|
||||||
static result<region<Cont>, std::string> invoke(location<Cont>& loc)
|
static result<region<Cont>, std::string>
|
||||||
|
invoke(location<Cont>& loc, const bool msg = false)
|
||||||
{
|
{
|
||||||
static_assert(std::is_same<char, typename Cont::value_type>::value,
|
static_assert(std::is_same<char, typename Cont::value_type>::value,
|
||||||
"internal error: container::value_type should be `char`.");
|
"internal error: container::value_type should be `char`.");
|
||||||
|
|
||||||
const auto rslt = Head::invoke(loc);
|
const auto rslt = Head::invoke(loc, msg);
|
||||||
if(rslt.is_ok()) {return rslt;}
|
if(rslt.is_ok()) {return rslt;}
|
||||||
return either<Tail...>::invoke(loc);
|
return either<Tail...>::invoke(loc, msg);
|
||||||
}
|
}
|
||||||
|
|
||||||
static std::string pattern()
|
static std::string pattern()
|
||||||
@@ -259,11 +283,12 @@ template<typename Head>
|
|||||||
struct either<Head>
|
struct either<Head>
|
||||||
{
|
{
|
||||||
template<typename Cont>
|
template<typename Cont>
|
||||||
static result<region<Cont>, std::string> invoke(location<Cont>& loc)
|
static result<region<Cont>, std::string>
|
||||||
|
invoke(location<Cont>& loc, const bool msg = false)
|
||||||
{
|
{
|
||||||
static_assert(std::is_same<char, typename Cont::value_type>::value,
|
static_assert(std::is_same<char, typename Cont::value_type>::value,
|
||||||
"internal error: container::value_type should be `char`.");
|
"internal error: container::value_type should be `char`.");
|
||||||
return Head::invoke(loc);
|
return Head::invoke(loc, msg);
|
||||||
}
|
}
|
||||||
static std::string pattern()
|
static std::string pattern()
|
||||||
{
|
{
|
||||||
@@ -282,16 +307,17 @@ template<typename T, std::size_t N>
|
|||||||
struct repeat<T, exactly<N>>
|
struct repeat<T, exactly<N>>
|
||||||
{
|
{
|
||||||
template<typename Cont>
|
template<typename Cont>
|
||||||
static result<region<Cont>, std::string> invoke(location<Cont>& loc)
|
static result<region<Cont>, std::string>
|
||||||
|
invoke(location<Cont>& loc, const bool msg = false)
|
||||||
{
|
{
|
||||||
region<Cont> retval(loc);
|
region<Cont> retval(loc);
|
||||||
const auto first = loc.iter();
|
const auto first = loc.iter();
|
||||||
for(std::size_t i=0; i<N; ++i)
|
for(std::size_t i=0; i<N; ++i)
|
||||||
{
|
{
|
||||||
auto rslt = T::invoke(loc);
|
auto rslt = T::invoke(loc, msg);
|
||||||
if(rslt.is_err())
|
if(rslt.is_err())
|
||||||
{
|
{
|
||||||
loc.iter() = first;
|
loc.reset(first);
|
||||||
return err(rslt.unwrap_err());
|
return err(rslt.unwrap_err());
|
||||||
}
|
}
|
||||||
retval += rslt.unwrap();
|
retval += rslt.unwrap();
|
||||||
@@ -308,24 +334,25 @@ template<typename T, std::size_t N>
|
|||||||
struct repeat<T, at_least<N>>
|
struct repeat<T, at_least<N>>
|
||||||
{
|
{
|
||||||
template<typename Cont>
|
template<typename Cont>
|
||||||
static result<region<Cont>, std::string> invoke(location<Cont>& loc)
|
static result<region<Cont>, std::string>
|
||||||
|
invoke(location<Cont>& loc, const bool msg = false)
|
||||||
{
|
{
|
||||||
region<Cont> retval(loc);
|
region<Cont> retval(loc);
|
||||||
|
|
||||||
const auto first = loc.iter();
|
const auto first = loc.iter();
|
||||||
for(std::size_t i=0; i<N; ++i)
|
for(std::size_t i=0; i<N; ++i)
|
||||||
{
|
{
|
||||||
auto rslt = T::invoke(loc);
|
auto rslt = T::invoke(loc, msg);
|
||||||
if(rslt.is_err())
|
if(rslt.is_err())
|
||||||
{
|
{
|
||||||
loc.iter() = first;
|
loc.reset(first);
|
||||||
return err(rslt.unwrap_err());
|
return err(rslt.unwrap_err());
|
||||||
}
|
}
|
||||||
retval += rslt.unwrap();
|
retval += rslt.unwrap();
|
||||||
}
|
}
|
||||||
while(true)
|
while(true)
|
||||||
{
|
{
|
||||||
auto rslt = T::invoke(loc);
|
auto rslt = T::invoke(loc, msg);
|
||||||
if(rslt.is_err())
|
if(rslt.is_err())
|
||||||
{
|
{
|
||||||
return ok(std::move(retval));
|
return ok(std::move(retval));
|
||||||
@@ -343,12 +370,13 @@ template<typename T>
|
|||||||
struct repeat<T, unlimited>
|
struct repeat<T, unlimited>
|
||||||
{
|
{
|
||||||
template<typename Cont>
|
template<typename Cont>
|
||||||
static result<region<Cont>, std::string> invoke(location<Cont>& loc)
|
static result<region<Cont>, std::string>
|
||||||
|
invoke(location<Cont>& loc, const bool msg = false)
|
||||||
{
|
{
|
||||||
region<Cont> retval(loc);
|
region<Cont> retval(loc);
|
||||||
while(true)
|
while(true)
|
||||||
{
|
{
|
||||||
auto rslt = T::invoke(loc);
|
auto rslt = T::invoke(loc, msg);
|
||||||
if(rslt.is_err())
|
if(rslt.is_err())
|
||||||
{
|
{
|
||||||
return ok(std::move(retval));
|
return ok(std::move(retval));
|
||||||
|
|||||||
@@ -25,16 +25,18 @@ inline std::tm localtime_s(const std::time_t* src)
|
|||||||
{
|
{
|
||||||
std::tm dst;
|
std::tm dst;
|
||||||
const auto result = ::localtime_r(src, &dst);
|
const auto result = ::localtime_r(src, &dst);
|
||||||
if(!result)
|
if (!result) { throw std::runtime_error("localtime_r failed."); }
|
||||||
{
|
return dst;
|
||||||
throw std::runtime_error("localtime_r failed.");
|
|
||||||
}
|
}
|
||||||
|
#elif _MSC_VER
|
||||||
|
inline std::tm localtime_s(const std::time_t* src)
|
||||||
|
{
|
||||||
|
std::tm dst;
|
||||||
|
const auto result = ::localtime_s(&dst, src);
|
||||||
|
if (result) { throw std::runtime_error("localtime_s failed."); }
|
||||||
return dst;
|
return dst;
|
||||||
}
|
}
|
||||||
#else
|
#else
|
||||||
// XXX: On Windows, std::localtime is thread-safe because they uses thread-local
|
|
||||||
// storage to store the instance of std::tm. On the other platforms, it may not
|
|
||||||
// be thread-safe.
|
|
||||||
inline std::tm localtime_s(const std::time_t* src)
|
inline std::tm localtime_s(const std::time_t* src)
|
||||||
{
|
{
|
||||||
const auto result = std::localtime(src);
|
const auto result = std::localtime(src);
|
||||||
@@ -360,12 +362,12 @@ struct local_datetime
|
|||||||
// can be used to get millisecond & microsecond information.
|
// can be used to get millisecond & microsecond information.
|
||||||
const auto t_diff = tp -
|
const auto t_diff = tp -
|
||||||
std::chrono::system_clock::from_time_t(std::mktime(&time));
|
std::chrono::system_clock::from_time_t(std::mktime(&time));
|
||||||
this->time.millisecond = std::chrono::duration_cast<
|
this->time.millisecond = static_cast<std::uint16_t>(
|
||||||
std::chrono::milliseconds>(t_diff).count();
|
std::chrono::duration_cast<std::chrono::milliseconds>(t_diff).count());
|
||||||
this->time.microsecond = std::chrono::duration_cast<
|
this->time.microsecond = static_cast<std::uint16_t>(
|
||||||
std::chrono::microseconds>(t_diff).count();
|
std::chrono::duration_cast<std::chrono::microseconds>(t_diff).count());
|
||||||
this->time.nanosecond = std::chrono::duration_cast<
|
this->time.nanosecond = static_cast<std::uint16_t>(
|
||||||
std::chrono::nanoseconds >(t_diff).count();
|
std::chrono::duration_cast<std::chrono::nanoseconds >(t_diff).count());
|
||||||
}
|
}
|
||||||
|
|
||||||
explicit local_datetime(const std::time_t t)
|
explicit local_datetime(const std::time_t t)
|
||||||
|
|||||||
@@ -51,7 +51,7 @@ struct from_toml_tie_impl
|
|||||||
template<typename ... Ts>
|
template<typename ... Ts>
|
||||||
struct from_toml_tie_impl<0, Ts...>
|
struct from_toml_tie_impl<0, Ts...>
|
||||||
{
|
{
|
||||||
static void invoke(std::tuple<Ts& ...> tie, const toml::value& v)
|
static void invoke(std::tuple<Ts& ...>, const toml::value&)
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -30,16 +30,52 @@ inline ::toml::value operator""_toml(const char* str, std::size_t len)
|
|||||||
::toml::detail::lex_ws, ::toml::detail::at_least<1>>;
|
::toml::detail::lex_ws, ::toml::detail::at_least<1>>;
|
||||||
skip_ws::invoke(loc);
|
skip_ws::invoke(loc);
|
||||||
|
|
||||||
// literal may be a bare value. try them first.
|
// to distinguish arrays and tables, first check it is a table or not.
|
||||||
|
//
|
||||||
|
// "[1,2,3]"_toml; // this is an array
|
||||||
|
// "[table]"_toml; // a table that has an empty table named "table" inside.
|
||||||
|
// "[[1,2,3]]"_toml; // this is an array of arrays
|
||||||
|
// "[[table]]"_toml; // this is a table that has an array of tables inside.
|
||||||
|
//
|
||||||
|
// "[[1]]"_toml; // this can be both... (currently it becomes a table)
|
||||||
|
// "1 = [{}]"_toml; // this is a table that has an array of table named 1.
|
||||||
|
// "[[1,]]"_toml; // this is an array of arrays.
|
||||||
|
// "[[1],]"_toml; // this also.
|
||||||
|
|
||||||
|
const auto the_front = loc.iter();
|
||||||
|
|
||||||
|
const bool is_table_key = ::toml::detail::lex_std_table::invoke(loc);
|
||||||
|
loc.reset(the_front);
|
||||||
|
|
||||||
|
const bool is_aots_key = ::toml::detail::lex_array_table::invoke(loc);
|
||||||
|
loc.reset(the_front);
|
||||||
|
|
||||||
|
// If it is neither a table-key or a array-of-table-key, it may be a value.
|
||||||
|
if(!is_table_key && !is_aots_key)
|
||||||
|
{
|
||||||
if(auto data = ::toml::detail::parse_value(loc))
|
if(auto data = ::toml::detail::parse_value(loc))
|
||||||
{
|
{
|
||||||
return data.unwrap();
|
return data.unwrap();
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Note that still it can be a table, because the literal might be something
|
||||||
|
// like the following.
|
||||||
|
// ```cpp
|
||||||
|
// R"( // c++11 raw string literals
|
||||||
|
// key = "value"
|
||||||
|
// int = 42
|
||||||
|
// )"_toml;
|
||||||
|
// ```
|
||||||
|
// It is a valid toml file.
|
||||||
|
// It should be parsed as if we parse a file with this content.
|
||||||
|
|
||||||
// literal is a TOML file (i.e. multiline table).
|
|
||||||
if(auto data = ::toml::detail::parse_toml_file(loc))
|
if(auto data = ::toml::detail::parse_toml_file(loc))
|
||||||
{
|
{
|
||||||
loc.iter() = loc.begin(); // rollback to the top of the literal
|
loc.reset(loc.begin()); // rollback to the top of the literal
|
||||||
|
// skip needless characters for error message
|
||||||
|
skip_line::invoke(loc); // skip the first several needless lines
|
||||||
|
skip_ws::invoke(loc); // skip the first several needless whitespaces
|
||||||
return ::toml::value(std::move(data.unwrap()),
|
return ::toml::value(std::move(data.unwrap()),
|
||||||
::toml::detail::region<std::vector<char>>(std::move(loc)));
|
::toml::detail::region<std::vector<char>>(std::move(loc)));
|
||||||
}
|
}
|
||||||
|
|||||||
276
toml/parser.hpp
276
toml/parser.hpp
@@ -33,7 +33,7 @@ parse_boolean(location<Container>& loc)
|
|||||||
{{std::addressof(reg), "invalid token"}}));
|
{{std::addressof(reg), "invalid token"}}));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
loc.iter() = first; //rollback
|
loc.reset(first); //rollback
|
||||||
return err(format_underline("[error] toml::parse_boolean: ",
|
return err(format_underline("[error] toml::parse_boolean: ",
|
||||||
{{std::addressof(loc), "the next token is not a boolean"}}));
|
{{std::addressof(loc), "the next token is not a boolean"}}));
|
||||||
}
|
}
|
||||||
@@ -62,7 +62,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.reset(first);
|
||||||
return err(format_underline("[error] toml::parse_binary_integer:",
|
return err(format_underline("[error] toml::parse_binary_integer:",
|
||||||
{{std::addressof(loc), "the next token is not an integer"}}));
|
{{std::addressof(loc), "the next token is not an integer"}}));
|
||||||
}
|
}
|
||||||
@@ -83,7 +83,7 @@ parse_octal_integer(location<Container>& loc)
|
|||||||
iss >> std::oct >> retval;
|
iss >> std::oct >> retval;
|
||||||
return ok(std::make_pair(retval, token.unwrap()));
|
return ok(std::make_pair(retval, token.unwrap()));
|
||||||
}
|
}
|
||||||
loc.iter() = first;
|
loc.reset(first);
|
||||||
return err(format_underline("[error] toml::parse_octal_integer:",
|
return err(format_underline("[error] toml::parse_octal_integer:",
|
||||||
{{std::addressof(loc), "the next token is not an integer"}}));
|
{{std::addressof(loc), "the next token is not an integer"}}));
|
||||||
}
|
}
|
||||||
@@ -104,7 +104,7 @@ parse_hexadecimal_integer(location<Container>& loc)
|
|||||||
iss >> std::hex >> retval;
|
iss >> std::hex >> retval;
|
||||||
return ok(std::make_pair(retval, token.unwrap()));
|
return ok(std::make_pair(retval, token.unwrap()));
|
||||||
}
|
}
|
||||||
loc.iter() = first;
|
loc.reset(first);
|
||||||
return err(format_underline("[error] toml::parse_hexadecimal_integer",
|
return err(format_underline("[error] toml::parse_hexadecimal_integer",
|
||||||
{{std::addressof(loc), "the next token is not an integer"}}));
|
{{std::addressof(loc), "the next token is not an integer"}}));
|
||||||
}
|
}
|
||||||
@@ -116,10 +116,28 @@ parse_integer(location<Container>& loc)
|
|||||||
const auto first = loc.iter();
|
const auto first = loc.iter();
|
||||||
if(first != loc.end() && *first == '0')
|
if(first != loc.end() && *first == '0')
|
||||||
{
|
{
|
||||||
if(const auto bin = parse_binary_integer (loc)) {return bin;}
|
const auto second = std::next(first);
|
||||||
if(const auto oct = parse_octal_integer (loc)) {return oct;}
|
if(second == loc.end()) // the token is just zero.
|
||||||
if(const auto hex = parse_hexadecimal_integer(loc)) {return hex;}
|
{
|
||||||
// else, maybe just zero.
|
return ok(std::make_pair(0, region<Container>(loc, first, second)));
|
||||||
|
}
|
||||||
|
|
||||||
|
if(*second == 'b') {return parse_binary_integer (loc);} // 0b1100
|
||||||
|
if(*second == 'o') {return parse_octal_integer (loc);} // 0o775
|
||||||
|
if(*second == 'x') {return parse_hexadecimal_integer(loc);} // 0xC0FFEE
|
||||||
|
|
||||||
|
if(std::isdigit(*second))
|
||||||
|
{
|
||||||
|
return err(format_underline("[error] toml::parse_integer: "
|
||||||
|
"leading zero in an Integer is not allowed.",
|
||||||
|
{{std::addressof(loc), "leading zero"}}));
|
||||||
|
}
|
||||||
|
else if(std::isalpha(*second))
|
||||||
|
{
|
||||||
|
return err(format_underline("[error] toml::parse_integer: "
|
||||||
|
"unknown integer prefix appeared.",
|
||||||
|
{{std::addressof(loc), "none of 0x, 0o, 0b"}}));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if(const auto token = lex_dec_int::invoke(loc))
|
if(const auto token = lex_dec_int::invoke(loc))
|
||||||
@@ -132,7 +150,7 @@ parse_integer(location<Container>& loc)
|
|||||||
iss >> retval;
|
iss >> retval;
|
||||||
return ok(std::make_pair(retval, token.unwrap()));
|
return ok(std::make_pair(retval, token.unwrap()));
|
||||||
}
|
}
|
||||||
loc.iter() = first;
|
loc.reset(first);
|
||||||
return err(format_underline("[error] toml::parse_integer: ",
|
return err(format_underline("[error] toml::parse_integer: ",
|
||||||
{{std::addressof(loc), "the next token is not an integer"}}));
|
{{std::addressof(loc), "the next token is not an integer"}}));
|
||||||
}
|
}
|
||||||
@@ -221,7 +239,7 @@ parse_floating(location<Container>& loc)
|
|||||||
iss >> v;
|
iss >> v;
|
||||||
return ok(std::make_pair(v, token.unwrap()));
|
return ok(std::make_pair(v, token.unwrap()));
|
||||||
}
|
}
|
||||||
loc.iter() = first;
|
loc.reset(first);
|
||||||
return err(format_underline("[error] toml::parse_floating: ",
|
return err(format_underline("[error] toml::parse_floating: ",
|
||||||
{{std::addressof(loc), "the next token is not a float"}}));
|
{{std::addressof(loc), "the next token is not a float"}}));
|
||||||
}
|
}
|
||||||
@@ -288,16 +306,16 @@ result<std::string, std::string> parse_escape_sequence(location<Container>& loc)
|
|||||||
return err(format_underline("[error]: toml::parse_escape_sequence: ", {{
|
return err(format_underline("[error]: toml::parse_escape_sequence: ", {{
|
||||||
std::addressof(loc), "the next token is not a backslash \"\\\""}}));
|
std::addressof(loc), "the next token is not a backslash \"\\\""}}));
|
||||||
}
|
}
|
||||||
++loc.iter();
|
loc.advance();
|
||||||
switch(*loc.iter())
|
switch(*loc.iter())
|
||||||
{
|
{
|
||||||
case '\\':{++loc.iter(); return ok(std::string("\\"));}
|
case '\\':{loc.advance(); return ok(std::string("\\"));}
|
||||||
case '"' :{++loc.iter(); return ok(std::string("\""));}
|
case '"' :{loc.advance(); return ok(std::string("\""));}
|
||||||
case 'b' :{++loc.iter(); return ok(std::string("\b"));}
|
case 'b' :{loc.advance(); return ok(std::string("\b"));}
|
||||||
case 't' :{++loc.iter(); return ok(std::string("\t"));}
|
case 't' :{loc.advance(); return ok(std::string("\t"));}
|
||||||
case 'n' :{++loc.iter(); return ok(std::string("\n"));}
|
case 'n' :{loc.advance(); return ok(std::string("\n"));}
|
||||||
case 'f' :{++loc.iter(); return ok(std::string("\f"));}
|
case 'f' :{loc.advance(); return ok(std::string("\f"));}
|
||||||
case 'r' :{++loc.iter(); return ok(std::string("\r"));}
|
case 'r' :{loc.advance(); return ok(std::string("\r"));}
|
||||||
case 'u' :
|
case 'u' :
|
||||||
{
|
{
|
||||||
if(const auto token = lex_escape_unicode_short::invoke(loc))
|
if(const auto token = lex_escape_unicode_short::invoke(loc))
|
||||||
@@ -308,7 +326,7 @@ result<std::string, std::string> parse_escape_sequence(location<Container>& loc)
|
|||||||
{
|
{
|
||||||
return err(format_underline("[error] parse_escape_sequence: "
|
return err(format_underline("[error] parse_escape_sequence: "
|
||||||
"invalid token found in UTF-8 codepoint uXXXX.",
|
"invalid token found in UTF-8 codepoint uXXXX.",
|
||||||
{{std::addressof(loc), token.unwrap_err()}}));
|
{{std::addressof(loc), "here"}}));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
case 'U':
|
case 'U':
|
||||||
@@ -321,7 +339,7 @@ result<std::string, std::string> parse_escape_sequence(location<Container>& loc)
|
|||||||
{
|
{
|
||||||
return err(format_underline("[error] parse_escape_sequence: "
|
return err(format_underline("[error] parse_escape_sequence: "
|
||||||
"invalid token found in UTF-8 codepoint Uxxxxxxxx",
|
"invalid token found in UTF-8 codepoint Uxxxxxxxx",
|
||||||
{{std::addressof(loc), token.unwrap_err()}}));
|
{{std::addressof(loc), "here"}}));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -331,7 +349,7 @@ result<std::string, std::string> parse_escape_sequence(location<Container>& loc)
|
|||||||
"escape sequence is one of \\, \", b, t, n, f, r, uxxxx, Uxxxxxxxx"}},
|
"escape sequence is one of \\, \", b, t, n, f, r, uxxxx, Uxxxxxxxx"}},
|
||||||
/* Hints = */{"if you want to write backslash as just one backslash, "
|
/* Hints = */{"if you want to write backslash as just one backslash, "
|
||||||
"use literal string like: regex = '<\\i\\c*\\s*>'"});
|
"use literal string like: regex = '<\\i\\c*\\s*>'"});
|
||||||
loc.iter() = first;
|
loc.reset(first);
|
||||||
return err(msg);
|
return err(msg);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -343,7 +361,7 @@ parse_ml_basic_string(location<Container>& loc)
|
|||||||
if(const auto token = lex_ml_basic_string::invoke(loc))
|
if(const auto token = lex_ml_basic_string::invoke(loc))
|
||||||
{
|
{
|
||||||
auto inner_loc = loc;
|
auto inner_loc = loc;
|
||||||
inner_loc.iter() = first;
|
inner_loc.reset(first);
|
||||||
|
|
||||||
std::string retval;
|
std::string retval;
|
||||||
retval.reserve(token.unwrap().size());
|
retval.reserve(token.unwrap().size());
|
||||||
@@ -387,8 +405,10 @@ parse_ml_basic_string(location<Container>& loc)
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
loc.iter() = first;
|
loc.reset(first);
|
||||||
return err(token.unwrap_err());
|
return err(format_underline("[error] toml::parse_ml_basic_string: "
|
||||||
|
"the next token is not a multiline string",
|
||||||
|
{{std::addressof(loc), "here"}}));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -400,7 +420,7 @@ parse_basic_string(location<Container>& loc)
|
|||||||
if(const auto token = lex_basic_string::invoke(loc))
|
if(const auto token = lex_basic_string::invoke(loc))
|
||||||
{
|
{
|
||||||
auto inner_loc = loc;
|
auto inner_loc = loc;
|
||||||
inner_loc.iter() = first;
|
inner_loc.reset(first);
|
||||||
|
|
||||||
auto quot = lex_quotation_mark::invoke(inner_loc);
|
auto quot = lex_quotation_mark::invoke(inner_loc);
|
||||||
if(!quot)
|
if(!quot)
|
||||||
@@ -436,8 +456,10 @@ parse_basic_string(location<Container>& loc)
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
loc.iter() = first; // rollback
|
loc.reset(first); // rollback
|
||||||
return err(token.unwrap_err());
|
return err(format_underline("[error] toml::parse_basic_string: "
|
||||||
|
"the next token is not a string",
|
||||||
|
{{std::addressof(loc), "here"}}));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -475,8 +497,10 @@ parse_ml_literal_string(location<Container>& loc)
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
loc.iter() = first; // rollback
|
loc.reset(first); // rollback
|
||||||
return err(token.unwrap_err());
|
return err(format_underline("[error] toml::parse_ml_literal_string: "
|
||||||
|
"the next token is not a multiline literal string",
|
||||||
|
{{std::addressof(loc), "here"}}));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -512,8 +536,10 @@ parse_literal_string(location<Container>& loc)
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
loc.iter() = first; // rollback
|
loc.reset(first); // rollback
|
||||||
return err(token.unwrap_err());
|
return err(format_underline("[error] toml::parse_literal_string: "
|
||||||
|
"the next token is not a literal string",
|
||||||
|
{{std::addressof(loc), "here"}}));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -521,10 +547,30 @@ template<typename Container>
|
|||||||
result<std::pair<toml::string, region<Container>>, std::string>
|
result<std::pair<toml::string, region<Container>>, std::string>
|
||||||
parse_string(location<Container>& loc)
|
parse_string(location<Container>& loc)
|
||||||
{
|
{
|
||||||
if(const auto rslt = parse_ml_basic_string(loc)) {return rslt;}
|
if(loc.iter() != loc.end() && *(loc.iter()) == '"')
|
||||||
if(const auto rslt = parse_ml_literal_string(loc)) {return rslt;}
|
{
|
||||||
if(const auto rslt = parse_basic_string(loc)) {return rslt;}
|
if(loc.iter() + 1 != loc.end() && *(loc.iter() + 1) == '"' &&
|
||||||
if(const auto rslt = parse_literal_string(loc)) {return rslt;}
|
loc.iter() + 2 != loc.end() && *(loc.iter() + 2) == '"')
|
||||||
|
{
|
||||||
|
return parse_ml_basic_string(loc);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return parse_basic_string(loc);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if(loc.iter() != loc.end() && *(loc.iter()) == '\'')
|
||||||
|
{
|
||||||
|
if(loc.iter() + 1 != loc.end() && *(loc.iter() + 1) == '\'' &&
|
||||||
|
loc.iter() + 2 != loc.end() && *(loc.iter() + 2) == '\'')
|
||||||
|
{
|
||||||
|
return parse_ml_literal_string(loc);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return parse_literal_string(loc);
|
||||||
|
}
|
||||||
|
}
|
||||||
return err(format_underline("[error] toml::parse_string: ",
|
return err(format_underline("[error] toml::parse_string: ",
|
||||||
{{std::addressof(loc), "the next token is not a string"}}));
|
{{std::addressof(loc), "the next token is not a string"}}));
|
||||||
}
|
}
|
||||||
@@ -548,7 +594,7 @@ parse_local_date(location<Container>& loc)
|
|||||||
"toml::parse_inner_local_date: invalid year format",
|
"toml::parse_inner_local_date: invalid year format",
|
||||||
{{std::addressof(inner_loc), msg}}));
|
{{std::addressof(inner_loc), msg}}));
|
||||||
}
|
}
|
||||||
++inner_loc.iter();
|
inner_loc.advance();
|
||||||
const auto m = lex_date_month::invoke(inner_loc);
|
const auto m = lex_date_month::invoke(inner_loc);
|
||||||
if(!m || inner_loc.iter() == inner_loc.end() || *inner_loc.iter() != '-')
|
if(!m || inner_loc.iter() == inner_loc.end() || *inner_loc.iter() != '-')
|
||||||
{
|
{
|
||||||
@@ -559,7 +605,7 @@ parse_local_date(location<Container>& loc)
|
|||||||
"toml::parse_local_date: invalid month format",
|
"toml::parse_local_date: invalid month format",
|
||||||
{{std::addressof(inner_loc), msg}}));
|
{{std::addressof(inner_loc), msg}}));
|
||||||
}
|
}
|
||||||
++inner_loc.iter();
|
inner_loc.advance();
|
||||||
const auto d = lex_date_mday::invoke(inner_loc);
|
const auto d = lex_date_mday::invoke(inner_loc);
|
||||||
if(!d)
|
if(!d)
|
||||||
{
|
{
|
||||||
@@ -576,7 +622,7 @@ parse_local_date(location<Container>& loc)
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
loc.iter() = first;
|
loc.reset(first);
|
||||||
return err(format_underline("[error]: toml::parse_local_date: ",
|
return err(format_underline("[error]: toml::parse_local_date: ",
|
||||||
{{std::addressof(loc), "the next token is not a local_date"}}));
|
{{std::addressof(loc), "the next token is not a local_date"}}));
|
||||||
}
|
}
|
||||||
@@ -601,7 +647,7 @@ parse_local_time(location<Container>& loc)
|
|||||||
"toml::parse_local_time: invalid year format",
|
"toml::parse_local_time: invalid year format",
|
||||||
{{std::addressof(inner_loc), msg}}));
|
{{std::addressof(inner_loc), msg}}));
|
||||||
}
|
}
|
||||||
++inner_loc.iter();
|
inner_loc.advance();
|
||||||
const auto m = lex_time_minute::invoke(inner_loc);
|
const auto m = lex_time_minute::invoke(inner_loc);
|
||||||
if(!m || inner_loc.iter() == inner_loc.end() || *inner_loc.iter() != ':')
|
if(!m || inner_loc.iter() == inner_loc.end() || *inner_loc.iter() != ':')
|
||||||
{
|
{
|
||||||
@@ -612,7 +658,7 @@ parse_local_time(location<Container>& loc)
|
|||||||
"toml::parse_local_time: invalid month format",
|
"toml::parse_local_time: invalid month format",
|
||||||
{{std::addressof(inner_loc), msg}}));
|
{{std::addressof(inner_loc), msg}}));
|
||||||
}
|
}
|
||||||
++inner_loc.iter();
|
inner_loc.advance();
|
||||||
const auto s = lex_time_second::invoke(inner_loc);
|
const auto s = lex_time_second::invoke(inner_loc);
|
||||||
if(!s)
|
if(!s)
|
||||||
{
|
{
|
||||||
@@ -661,7 +707,7 @@ parse_local_time(location<Container>& loc)
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
loc.iter() = first;
|
loc.reset(first);
|
||||||
return err(format_underline("[error]: toml::parse_local_time: ",
|
return err(format_underline("[error]: toml::parse_local_time: ",
|
||||||
{{std::addressof(loc), "the next token is not a local_time"}}));
|
{{std::addressof(loc), "the next token is not a local_time"}}));
|
||||||
}
|
}
|
||||||
@@ -685,13 +731,14 @@ parse_local_datetime(location<Container>& loc)
|
|||||||
"toml::parse_local_datetime: invalid datetime format",
|
"toml::parse_local_datetime: invalid datetime format",
|
||||||
{{std::addressof(inner_loc), msg}}));
|
{{std::addressof(inner_loc), msg}}));
|
||||||
}
|
}
|
||||||
const char delim = *(inner_loc.iter()++);
|
const char delim = *(inner_loc.iter());
|
||||||
if(delim != 'T' && delim != 't' && delim != ' ')
|
if(delim != 'T' && delim != 't' && delim != ' ')
|
||||||
{
|
{
|
||||||
throw internal_error(format_underline("[error]: "
|
throw internal_error(format_underline("[error]: "
|
||||||
"toml::parse_local_datetime: invalid datetime format",
|
"toml::parse_local_datetime: invalid datetime format",
|
||||||
{{std::addressof(inner_loc), "should be `T` or ` ` (space)"}}));
|
{{std::addressof(inner_loc), "should be `T` or ` ` (space)"}}));
|
||||||
}
|
}
|
||||||
|
inner_loc.advance();
|
||||||
const auto time = parse_local_time(inner_loc);
|
const auto time = parse_local_time(inner_loc);
|
||||||
if(!time)
|
if(!time)
|
||||||
{
|
{
|
||||||
@@ -705,7 +752,7 @@ parse_local_datetime(location<Container>& loc)
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
loc.iter() = first;
|
loc.reset(first);
|
||||||
return err(format_underline("[error]: toml::parse_local_datetime: ",
|
return err(format_underline("[error]: toml::parse_local_datetime: ",
|
||||||
{{std::addressof(loc), "the next token is not a local_datetime"}}));
|
{{std::addressof(loc), "the next token is not a local_datetime"}}));
|
||||||
}
|
}
|
||||||
@@ -755,9 +802,9 @@ parse_offset_datetime(location<Container>& loc)
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
loc.iter() = first;
|
loc.reset(first);
|
||||||
return err(format_underline("[error]: toml::parse_offset_datetime: ",
|
return err(format_underline("[error]: toml::parse_offset_datetime: ",
|
||||||
{{std::addressof(loc), "the next token is not a local_datetime"}}));
|
{{std::addressof(loc), "the next token is not a offset_datetime"}}));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -816,7 +863,7 @@ parse_key(location<Container>& loc)
|
|||||||
}
|
}
|
||||||
else if(*inner_loc.iter() == '.')
|
else if(*inner_loc.iter() == '.')
|
||||||
{
|
{
|
||||||
++inner_loc.iter(); // to skip `.`
|
inner_loc.advance(); // to skip `.`
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@@ -827,7 +874,7 @@ parse_key(location<Container>& loc)
|
|||||||
}
|
}
|
||||||
return ok(std::make_pair(keys, reg));
|
return ok(std::make_pair(keys, reg));
|
||||||
}
|
}
|
||||||
loc.iter() = first;
|
loc.reset(first);
|
||||||
|
|
||||||
// simple key -> foo
|
// simple key -> foo
|
||||||
if(const auto smpl = parse_simple_key(loc))
|
if(const auto smpl = parse_simple_key(loc))
|
||||||
@@ -856,7 +903,7 @@ parse_array(location<Container>& loc)
|
|||||||
{
|
{
|
||||||
return err("[error] toml::parse_array: token is not an array");
|
return err("[error] toml::parse_array: token is not an array");
|
||||||
}
|
}
|
||||||
++loc.iter();
|
loc.advance();
|
||||||
|
|
||||||
using lex_ws_comment_newline = repeat<
|
using lex_ws_comment_newline = repeat<
|
||||||
either<lex_wschar, lex_newline, lex_comment>, unlimited>;
|
either<lex_wschar, lex_newline, lex_comment>, unlimited>;
|
||||||
@@ -868,7 +915,7 @@ parse_array(location<Container>& loc)
|
|||||||
|
|
||||||
if(loc.iter() != loc.end() && *loc.iter() == ']')
|
if(loc.iter() != loc.end() && *loc.iter() == ']')
|
||||||
{
|
{
|
||||||
++loc.iter(); // skip ']'
|
loc.advance(); // skip ']'
|
||||||
return ok(std::make_pair(retval,
|
return ok(std::make_pair(retval,
|
||||||
region<Container>(loc, first, loc.iter())));
|
region<Container>(loc, first, loc.iter())));
|
||||||
}
|
}
|
||||||
@@ -878,7 +925,7 @@ 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())
|
||||||
{
|
{
|
||||||
auto array_start_loc = loc;
|
auto array_start_loc = loc;
|
||||||
array_start_loc.iter() = first;
|
array_start_loc.reset(first);
|
||||||
|
|
||||||
throw syntax_error(format_underline("[error] toml::parse_array: "
|
throw syntax_error(format_underline("[error] toml::parse_array: "
|
||||||
"type of elements should be the same each other.", {
|
"type of elements should be the same each other.", {
|
||||||
@@ -898,7 +945,7 @@ parse_array(location<Container>& loc)
|
|||||||
else
|
else
|
||||||
{
|
{
|
||||||
auto array_start_loc = loc;
|
auto array_start_loc = loc;
|
||||||
array_start_loc.iter() = first;
|
array_start_loc.reset(first);
|
||||||
|
|
||||||
throw syntax_error(format_underline("[error] toml::parse_array: "
|
throw syntax_error(format_underline("[error] toml::parse_array: "
|
||||||
"value having invalid format appeared in an array", {
|
"value having invalid format appeared in an array", {
|
||||||
@@ -914,14 +961,14 @@ parse_array(location<Container>& loc)
|
|||||||
lex_ws_comment_newline::invoke(loc);
|
lex_ws_comment_newline::invoke(loc);
|
||||||
if(loc.iter() != loc.end() && *loc.iter() == ']')
|
if(loc.iter() != loc.end() && *loc.iter() == ']')
|
||||||
{
|
{
|
||||||
++loc.iter(); // skip ']'
|
loc.advance(); // skip ']'
|
||||||
return ok(std::make_pair(retval,
|
return ok(std::make_pair(retval,
|
||||||
region<Container>(loc, first, loc.iter())));
|
region<Container>(loc, first, loc.iter())));
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
auto array_start_loc = loc;
|
auto array_start_loc = loc;
|
||||||
array_start_loc.iter() = first;
|
array_start_loc.reset(first);
|
||||||
|
|
||||||
throw syntax_error(format_underline("[error] toml::parse_array:"
|
throw syntax_error(format_underline("[error] toml::parse_array:"
|
||||||
" missing array separator `,` after a value", {
|
" missing array separator `,` after a value", {
|
||||||
@@ -931,7 +978,7 @@ parse_array(location<Container>& loc)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
loc.iter() = first;
|
loc.reset(first);
|
||||||
throw syntax_error(format_underline("[error] toml::parse_array: "
|
throw syntax_error(format_underline("[error] toml::parse_array: "
|
||||||
"array did not closed by `]`",
|
"array did not closed by `]`",
|
||||||
{{std::addressof(loc), "should be closed"}}));
|
{{std::addressof(loc), "should be closed"}}));
|
||||||
@@ -950,7 +997,7 @@ parse_key_value_pair(location<Container>& loc)
|
|||||||
// 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))
|
||||||
{
|
{
|
||||||
loc.iter() = first;
|
loc.reset(first);
|
||||||
msg = format_underline("[error] toml::parse_key_value_pair: "
|
msg = format_underline("[error] toml::parse_key_value_pair: "
|
||||||
"empty key is not allowed.",
|
"empty key is not allowed.",
|
||||||
{{std::addressof(loc), "key expected before '='"}});
|
{{std::addressof(loc), "key expected before '='"}});
|
||||||
@@ -979,7 +1026,7 @@ parse_key_value_pair(location<Container>& loc)
|
|||||||
"missing key-value separator `=`",
|
"missing key-value separator `=`",
|
||||||
{{std::addressof(loc), "should be `=`"}});
|
{{std::addressof(loc), "should be `=`"}});
|
||||||
}
|
}
|
||||||
loc.iter() = first;
|
loc.reset(first);
|
||||||
return err(std::move(msg));
|
return err(std::move(msg));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -988,11 +1035,11 @@ parse_key_value_pair(location<Container>& loc)
|
|||||||
if(!val)
|
if(!val)
|
||||||
{
|
{
|
||||||
std::string msg;
|
std::string msg;
|
||||||
loc.iter() = after_kvsp;
|
loc.reset(after_kvsp);
|
||||||
// check there is something not a comment/whitespace after `=`
|
// 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.reset(after_kvsp);
|
||||||
msg = format_underline("[error] toml::parse_key_value_pair: "
|
msg = format_underline("[error] toml::parse_key_value_pair: "
|
||||||
"missing value after key-value separator '='",
|
"missing value after key-value separator '='",
|
||||||
{{std::addressof(loc), "expected value, but got nothing"}});
|
{{std::addressof(loc), "expected value, but got nothing"}});
|
||||||
@@ -1001,7 +1048,7 @@ parse_key_value_pair(location<Container>& loc)
|
|||||||
{
|
{
|
||||||
msg = std::move(val.unwrap_err());
|
msg = std::move(val.unwrap_err());
|
||||||
}
|
}
|
||||||
loc.iter() = first;
|
loc.reset(first);
|
||||||
return err(msg);
|
return err(msg);
|
||||||
}
|
}
|
||||||
return ok(std::make_pair(std::move(key_reg.unwrap()),
|
return ok(std::make_pair(std::move(key_reg.unwrap()),
|
||||||
@@ -1028,6 +1075,7 @@ std::string format_dotted_keys(InputIterator first, const InputIterator last)
|
|||||||
template<typename Container>
|
template<typename Container>
|
||||||
result<std::pair<std::vector<key>, region<Container>>, std::string>
|
result<std::pair<std::vector<key>, region<Container>>, std::string>
|
||||||
parse_table_key(location<Container>& loc);
|
parse_table_key(location<Container>& loc);
|
||||||
|
|
||||||
// The following toml file is allowed.
|
// The following toml file is allowed.
|
||||||
// ```toml
|
// ```toml
|
||||||
// [a.b.c] # here, table `a` has element `b`.
|
// [a.b.c] # here, table `a` has element `b`.
|
||||||
@@ -1318,14 +1366,14 @@ parse_inline_table(location<Container>& loc)
|
|||||||
return err(format_underline("[error] toml::parse_inline_table: ",
|
return err(format_underline("[error] toml::parse_inline_table: ",
|
||||||
{{std::addressof(loc), "the next token is not an inline table"}}));
|
{{std::addressof(loc), "the next token is not an inline table"}}));
|
||||||
}
|
}
|
||||||
++loc.iter();
|
loc.advance();
|
||||||
// it starts from "{". it should be formatted as inline-table
|
// it starts from "{". it should be formatted as inline-table
|
||||||
while(loc.iter() != loc.end())
|
while(loc.iter() != loc.end())
|
||||||
{
|
{
|
||||||
maybe<lex_ws>::invoke(loc);
|
maybe<lex_ws>::invoke(loc);
|
||||||
if(loc.iter() != loc.end() && *loc.iter() == '}')
|
if(loc.iter() != loc.end() && *loc.iter() == '}')
|
||||||
{
|
{
|
||||||
++loc.iter(); // skip `}`
|
loc.advance(); // skip `}`
|
||||||
return ok(std::make_pair(
|
return ok(std::make_pair(
|
||||||
retval, region<Container>(loc, first, loc.iter())));
|
retval, region<Container>(loc, first, loc.iter())));
|
||||||
}
|
}
|
||||||
@@ -1354,24 +1402,70 @@ parse_inline_table(location<Container>& loc)
|
|||||||
maybe<lex_ws>::invoke(loc);
|
maybe<lex_ws>::invoke(loc);
|
||||||
if(loc.iter() != loc.end() && *loc.iter() == '}')
|
if(loc.iter() != loc.end() && *loc.iter() == '}')
|
||||||
{
|
{
|
||||||
++loc.iter(); // skip `}`
|
loc.advance(); // skip `}`
|
||||||
return ok(std::make_pair(
|
return ok(std::make_pair(
|
||||||
retval, region<Container>(loc, first, loc.iter())));
|
retval, region<Container>(loc, first, loc.iter())));
|
||||||
}
|
}
|
||||||
|
else if(*loc.iter() == '#' || *loc.iter() == '\r' || *loc.iter() == '\n')
|
||||||
|
{
|
||||||
|
throw syntax_error(format_underline("[error] "
|
||||||
|
"toml::parse_inline_table: missing curly brace `}`",
|
||||||
|
{{std::addressof(loc), "should be `}`"}}));
|
||||||
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
throw syntax_error(format_underline("[error] "
|
throw syntax_error(format_underline("[error] "
|
||||||
"toml:::parse_inline_table: missing table separator `,` ",
|
"toml::parse_inline_table: missing table separator `,` ",
|
||||||
{{std::addressof(loc), "should be `,`"}}));
|
{{std::addressof(loc), "should be `,`"}}));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
loc.iter() = first;
|
loc.reset(first);
|
||||||
throw syntax_error(format_underline("[error] toml::parse_inline_table: "
|
throw syntax_error(format_underline("[error] toml::parse_inline_table: "
|
||||||
"inline table did not closed by `}`",
|
"inline table did not closed by `}`",
|
||||||
{{std::addressof(loc), "should be closed"}}));
|
{{std::addressof(loc), "should be closed"}}));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
template<typename Container>
|
||||||
|
value_t guess_number_type(const location<Container>& l)
|
||||||
|
{
|
||||||
|
location<Container> loc = l;
|
||||||
|
|
||||||
|
if(lex_offset_date_time::invoke(loc)) {return value_t::OffsetDatetime;}
|
||||||
|
loc.reset(l.iter());
|
||||||
|
|
||||||
|
if(lex_local_date_time::invoke(loc)) {return value_t::LocalDatetime;}
|
||||||
|
loc.reset(l.iter());
|
||||||
|
|
||||||
|
if(lex_local_date::invoke(loc)) {return value_t::LocalDate;}
|
||||||
|
loc.reset(l.iter());
|
||||||
|
|
||||||
|
if(lex_local_time::invoke(loc)) {return value_t::LocalTime;}
|
||||||
|
loc.reset(l.iter());
|
||||||
|
|
||||||
|
if(lex_float::invoke(loc)) {return value_t::Float;}
|
||||||
|
loc.reset(l.iter());
|
||||||
|
|
||||||
|
return value_t::Integer;
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename Container>
|
||||||
|
value_t guess_value_type(const location<Container>& loc)
|
||||||
|
{
|
||||||
|
switch(*loc.iter())
|
||||||
|
{
|
||||||
|
case '"' : {return value_t::String; }
|
||||||
|
case '\'': {return value_t::String; }
|
||||||
|
case 't' : {return value_t::Boolean;}
|
||||||
|
case 'f' : {return value_t::Boolean;}
|
||||||
|
case '[' : {return value_t::Array; }
|
||||||
|
case '{' : {return value_t::Table; }
|
||||||
|
case 'i' : {return value_t::Float; } // inf.
|
||||||
|
case 'n' : {return value_t::Float; } // nan.
|
||||||
|
default : {return guess_number_type(loc);}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
template<typename Container>
|
template<typename Container>
|
||||||
result<value, std::string> parse_value(location<Container>& loc)
|
result<value, std::string> parse_value(location<Container>& loc)
|
||||||
{
|
{
|
||||||
@@ -1381,32 +1475,28 @@ result<value, std::string> parse_value(location<Container>& loc)
|
|||||||
return err(format_underline("[error] toml::parse_value: input is empty",
|
return err(format_underline("[error] toml::parse_value: input is empty",
|
||||||
{{std::addressof(loc), ""}}));
|
{{std::addressof(loc), ""}}));
|
||||||
}
|
}
|
||||||
if(auto r = parse_string (loc))
|
|
||||||
{return ok(value(std::move(r.unwrap().first), std::move(r.unwrap().second)));}
|
|
||||||
if(auto r = parse_array (loc))
|
|
||||||
{return ok(value(std::move(r.unwrap().first), std::move(r.unwrap().second)));}
|
|
||||||
if(auto r = parse_inline_table (loc))
|
|
||||||
{return ok(value(std::move(r.unwrap().first), std::move(r.unwrap().second)));}
|
|
||||||
if(auto r = parse_boolean (loc))
|
|
||||||
{return ok(value(std::move(r.unwrap().first), std::move(r.unwrap().second)));}
|
|
||||||
if(auto r = parse_offset_datetime(loc))
|
|
||||||
{return ok(value(std::move(r.unwrap().first), std::move(r.unwrap().second)));}
|
|
||||||
if(auto r = parse_local_datetime (loc))
|
|
||||||
{return ok(value(std::move(r.unwrap().first), std::move(r.unwrap().second)));}
|
|
||||||
if(auto r = parse_local_date (loc))
|
|
||||||
{return ok(value(std::move(r.unwrap().first), std::move(r.unwrap().second)));}
|
|
||||||
if(auto r = parse_local_time (loc))
|
|
||||||
{return ok(value(std::move(r.unwrap().first), std::move(r.unwrap().second)));}
|
|
||||||
if(auto r = parse_floating (loc))
|
|
||||||
{return ok(value(std::move(r.unwrap().first), std::move(r.unwrap().second)));}
|
|
||||||
if(auto r = parse_integer (loc))
|
|
||||||
{return ok(value(std::move(r.unwrap().first), std::move(r.unwrap().second)));}
|
|
||||||
|
|
||||||
|
switch(guess_value_type(loc))
|
||||||
|
{
|
||||||
|
case value_t::Boolean : {return parse_boolean(loc); }
|
||||||
|
case value_t::Integer : {return parse_integer(loc); }
|
||||||
|
case value_t::Float : {return parse_floating(loc); }
|
||||||
|
case value_t::String : {return parse_string(loc); }
|
||||||
|
case value_t::OffsetDatetime : {return parse_offset_datetime(loc);}
|
||||||
|
case value_t::LocalDatetime : {return parse_local_datetime(loc); }
|
||||||
|
case value_t::LocalDate : {return parse_local_date(loc); }
|
||||||
|
case value_t::LocalTime : {return parse_local_time(loc); }
|
||||||
|
case value_t::Array : {return parse_array(loc); }
|
||||||
|
case value_t::Table : {return parse_inline_table(loc); }
|
||||||
|
default:
|
||||||
|
{
|
||||||
const auto msg = format_underline("[error] toml::parse_value: "
|
const auto msg = format_underline("[error] toml::parse_value: "
|
||||||
"unknown token appeared", {{std::addressof(loc), "unknown"}});
|
"unknown token appeared", {{std::addressof(loc), "unknown"}});
|
||||||
loc.iter() = first;
|
loc.reset(first);
|
||||||
return err(msg);
|
return err(msg);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
template<typename Container>
|
template<typename Container>
|
||||||
result<std::pair<std::vector<key>, region<Container>>, std::string>
|
result<std::pair<std::vector<key>, region<Container>>, std::string>
|
||||||
@@ -1461,7 +1551,8 @@ parse_table_key(location<Container>& loc)
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
return err(token.unwrap_err());
|
return err(format_underline("[error] toml::parse_table_key: "
|
||||||
|
"not a valid table key", {{std::addressof(loc), "here"}}));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1469,7 +1560,7 @@ template<typename Container>
|
|||||||
result<std::pair<std::vector<key>, region<Container>>, std::string>
|
result<std::pair<std::vector<key>, region<Container>>, std::string>
|
||||||
parse_array_table_key(location<Container>& loc)
|
parse_array_table_key(location<Container>& loc)
|
||||||
{
|
{
|
||||||
if(auto token = lex_array_table::invoke(loc))
|
if(auto token = lex_array_table::invoke(loc, true))
|
||||||
{
|
{
|
||||||
location<std::string> inner_loc(loc.name(), token.unwrap().str());
|
location<std::string> inner_loc(loc.name(), token.unwrap().str());
|
||||||
|
|
||||||
@@ -1514,7 +1605,8 @@ parse_array_table_key(location<Container>& loc)
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
return err(token.unwrap_err());
|
return err(format_underline("[error] toml::parse_array_table_key: "
|
||||||
|
"not a valid table key", {{std::addressof(loc), "here"}}));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1540,12 +1632,12 @@ result<table, std::string> parse_ml_table(location<Container>& loc)
|
|||||||
const auto before = loc.iter();
|
const auto before = loc.iter();
|
||||||
if(const auto tmp = parse_array_table_key(loc)) // next table found
|
if(const auto tmp = parse_array_table_key(loc)) // next table found
|
||||||
{
|
{
|
||||||
loc.iter() = before;
|
loc.reset(before);
|
||||||
return ok(tab);
|
return ok(tab);
|
||||||
}
|
}
|
||||||
if(const auto tmp = parse_table_key(loc)) // next table found
|
if(const auto tmp = parse_table_key(loc)) // next table found
|
||||||
{
|
{
|
||||||
loc.iter() = before;
|
loc.reset(before);
|
||||||
return ok(tab);
|
return ok(tab);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1585,7 +1677,7 @@ result<table, std::string> parse_ml_table(location<Container>& loc)
|
|||||||
const auto msg = format_underline("[error] toml::parse_table: "
|
const auto msg = format_underline("[error] toml::parse_table: "
|
||||||
"invalid line format", {{std::addressof(loc), concat_to_string(
|
"invalid line format", {{std::addressof(loc), concat_to_string(
|
||||||
"expected newline, but got '", show_char(*loc.iter()), "'.")}});
|
"expected newline, but got '", show_char(*loc.iter()), "'.")}});
|
||||||
loc.iter() = before;
|
loc.reset(before);
|
||||||
return err(msg);
|
return err(msg);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1688,7 +1780,7 @@ inline table parse(std::istream& is, std::string fname = "unknown file")
|
|||||||
std::memcpy(BOM.data(), loc.source()->data(), 3);
|
std::memcpy(BOM.data(), loc.source()->data(), 3);
|
||||||
if(BOM[0] == 0xEF && BOM[1] == 0xBB && BOM[2] == 0xBF)
|
if(BOM[0] == 0xEF && BOM[1] == 0xBB && BOM[2] == 0xBF)
|
||||||
{
|
{
|
||||||
loc.iter() += 3; // BOM found. skip.
|
loc.advance(3); // BOM found. skip.
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -62,12 +62,16 @@ struct region_base
|
|||||||
template<typename Container>
|
template<typename Container>
|
||||||
struct location final : public region_base
|
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 const_iterator = typename Container::const_iterator;
|
||||||
using source_ptr = std::shared_ptr<const Container>;
|
using source_ptr = std::shared_ptr<const Container>;
|
||||||
|
|
||||||
|
static_assert(std::is_same<char, typename Container::value_type>::value,"");
|
||||||
|
static_assert(std::is_same<std::random_access_iterator_tag,
|
||||||
|
typename std::iterator_traits<const_iterator>::iterator_category>::value,
|
||||||
|
"container should be randomly accessible");
|
||||||
|
|
||||||
location(std::string name, Container cont)
|
location(std::string name, Container cont)
|
||||||
: source_(std::make_shared<Container>(std::move(cont))),
|
: source_(std::make_shared<Container>(std::move(cont))), line_number_(1),
|
||||||
source_name_(std::move(name)), iter_(source_->cbegin())
|
source_name_(std::move(name)), iter_(source_->cbegin())
|
||||||
{}
|
{}
|
||||||
location(const location&) = default;
|
location(const location&) = default;
|
||||||
@@ -78,18 +82,54 @@ struct location final : public region_base
|
|||||||
|
|
||||||
bool is_ok() const noexcept override {return static_cast<bool>(source_);}
|
bool is_ok() const noexcept override {return static_cast<bool>(source_);}
|
||||||
|
|
||||||
const_iterator& iter() noexcept {return iter_;}
|
// this const prohibits codes like `++(loc.iter())`.
|
||||||
const_iterator iter() const noexcept {return iter_;}
|
const const_iterator iter() const noexcept {return iter_;}
|
||||||
|
|
||||||
const_iterator begin() const noexcept {return source_->cbegin();}
|
const_iterator begin() const noexcept {return source_->cbegin();}
|
||||||
const_iterator end() const noexcept {return source_->cend();}
|
const_iterator end() const noexcept {return source_->cend();}
|
||||||
|
|
||||||
|
// XXX `location::line_num()` used to be implemented using `std::count` to
|
||||||
|
// count a number of '\n'. But with a long toml file (typically, 10k lines),
|
||||||
|
// it becomes intolerably slow because each time it generates error messages,
|
||||||
|
// it counts '\n' from thousands of characters. To workaround it, I decided
|
||||||
|
// to introduce `location::line_number_` member variable and synchronize it
|
||||||
|
// to the location changes the point to look. So an overload of `iter()`
|
||||||
|
// which returns mutable reference is removed and `advance()`, `retrace()`
|
||||||
|
// and `reset()` is added.
|
||||||
|
void advance(std::size_t n = 1) noexcept
|
||||||
|
{
|
||||||
|
this->line_number_ += std::count(this->iter_, this->iter_ + n, '\n');
|
||||||
|
this->iter_ += n;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
void retrace(std::size_t n = 1) noexcept
|
||||||
|
{
|
||||||
|
this->line_number_ -= std::count(this->iter_ - n, this->iter_, '\n');
|
||||||
|
this->iter_ -= n;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
void reset(const_iterator rollback) noexcept
|
||||||
|
{
|
||||||
|
// since c++11, std::distance works in both ways for random-access
|
||||||
|
// iterators and returns a negative value if `first > last`.
|
||||||
|
if(0 <= std::distance(rollback, this->iter_)) // rollback < iter
|
||||||
|
{
|
||||||
|
this->line_number_ -= std::count(rollback, this->iter_, '\n');
|
||||||
|
}
|
||||||
|
else // iter < rollback [[unlikely]]
|
||||||
|
{
|
||||||
|
this->line_number_ += std::count(this->iter_, rollback, '\n');
|
||||||
|
}
|
||||||
|
this->iter_ = rollback;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
std::string str() const override {return make_string(1, *this->iter());}
|
std::string str() const override {return make_string(1, *this->iter());}
|
||||||
std::string name() const override {return source_name_;}
|
std::string name() const override {return source_name_;}
|
||||||
|
|
||||||
std::string line_num() const override
|
std::string line_num() const override
|
||||||
{
|
{
|
||||||
return std::to_string(1+std::count(this->begin(), this->iter(), '\n'));
|
return std::to_string(this->line_number_);
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string line() const override
|
std::string line() const override
|
||||||
@@ -128,6 +168,7 @@ struct location final : public region_base
|
|||||||
private:
|
private:
|
||||||
|
|
||||||
source_ptr source_;
|
source_ptr source_;
|
||||||
|
std::size_t line_number_;
|
||||||
std::string source_name_;
|
std::string source_name_;
|
||||||
const_iterator iter_;
|
const_iterator iter_;
|
||||||
};
|
};
|
||||||
@@ -139,10 +180,14 @@ struct location final : public region_base
|
|||||||
template<typename Container>
|
template<typename Container>
|
||||||
struct region final : public region_base
|
struct region final : public region_base
|
||||||
{
|
{
|
||||||
static_assert(std::is_same<char, typename Container::value_type>::value,"");
|
|
||||||
using const_iterator = typename Container::const_iterator;
|
using const_iterator = typename Container::const_iterator;
|
||||||
using source_ptr = std::shared_ptr<const Container>;
|
using source_ptr = std::shared_ptr<const Container>;
|
||||||
|
|
||||||
|
static_assert(std::is_same<char, typename Container::value_type>::value,"");
|
||||||
|
static_assert(std::is_same<std::random_access_iterator_tag,
|
||||||
|
typename std::iterator_traits<const_iterator>::iterator_category>::value,
|
||||||
|
"container should be randomly accessible");
|
||||||
|
|
||||||
// delete default constructor. source_ never be null.
|
// delete default constructor. source_ never be null.
|
||||||
region() = delete;
|
region() = delete;
|
||||||
|
|
||||||
@@ -298,7 +343,8 @@ inline std::string format_underline(const std::string& message,
|
|||||||
{
|
{
|
||||||
// invalid
|
// invalid
|
||||||
// ~~~~~~~
|
// ~~~~~~~
|
||||||
retval << make_string(reg->size(), '~');
|
const auto underline_len = std::min(reg->size(), reg->line().size());
|
||||||
|
retval << make_string(underline_len, '~');
|
||||||
}
|
}
|
||||||
|
|
||||||
retval << ' ';
|
retval << ' ';
|
||||||
|
|||||||
@@ -572,6 +572,14 @@ class value
|
|||||||
return *this;
|
return *this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// for internal use ------------------------------------------------------
|
||||||
|
|
||||||
|
template<typename T, typename Container, typename std::enable_if<
|
||||||
|
detail::is_exact_toml_type<T>::value, std::nullptr_t>::type = nullptr>
|
||||||
|
value(std::pair<T, detail::region<Container>> parse_result)
|
||||||
|
: value(std::move(parse_result.first), std::move(parse_result.second))
|
||||||
|
{}
|
||||||
|
|
||||||
// type checking and casting ============================================
|
// type checking and casting ============================================
|
||||||
|
|
||||||
template<typename T>
|
template<typename T>
|
||||||
|
|||||||
Reference in New Issue
Block a user