mirror of
https://github.com/ToruNiina/toml11.git
synced 2025-12-16 03:08:52 +08:00
refactor: Merge branch 'refactor-region'
This commit is contained in:
@@ -8,7 +8,7 @@
|
||||
do { \
|
||||
const std::string token (tkn); \
|
||||
const std::string expected(expct); \
|
||||
toml::detail::location<std::string> loc("test", token); \
|
||||
toml::detail::location loc("test", token); \
|
||||
const auto result = lxr::invoke(loc); \
|
||||
BOOST_TEST(result.is_ok()); \
|
||||
if(result.is_ok()){ \
|
||||
@@ -28,7 +28,7 @@ do { \
|
||||
#define TOML11_TEST_LEX_REJECT(lxr, tkn) \
|
||||
do { \
|
||||
const std::string token (tkn); \
|
||||
toml::detail::location<std::string> loc("test", token); \
|
||||
toml::detail::location loc("test", token); \
|
||||
const auto result = lxr::invoke(loc); \
|
||||
BOOST_TEST(result.is_err()); \
|
||||
const bool loc_same = (loc.begin() == loc.iter()); \
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
#define TOML11_TEST_PARSE_EQUAL(psr, tkn, expct) \
|
||||
do { \
|
||||
const std::string token(tkn); \
|
||||
toml::detail::location<std::string> loc("test", token); \
|
||||
toml::detail::location loc("test", token); \
|
||||
const auto result = psr(loc); \
|
||||
BOOST_TEST(result.is_ok()); \
|
||||
if(result.is_ok()){ \
|
||||
@@ -23,7 +23,7 @@ do { \
|
||||
#define TOML11_TEST_PARSE_EQUAL_VALUE(psr, tkn, expct) \
|
||||
do { \
|
||||
const std::string token(tkn); \
|
||||
toml::detail::location<std::string> loc("test", token); \
|
||||
toml::detail::location loc("test", token); \
|
||||
const auto result = psr(loc); \
|
||||
BOOST_TEST(result.is_ok()); \
|
||||
if(result.is_ok()){ \
|
||||
|
||||
@@ -128,7 +128,7 @@ BOOST_AUTO_TEST_CASE(test_inf)
|
||||
{
|
||||
{
|
||||
const std::string token("inf");
|
||||
toml::detail::location<std::string> loc("test", token);
|
||||
toml::detail::location loc("test", token);
|
||||
const auto r = parse_floating(loc);
|
||||
BOOST_CHECK(r.is_ok());
|
||||
BOOST_CHECK(std::isinf(r.unwrap().first));
|
||||
@@ -136,7 +136,7 @@ BOOST_AUTO_TEST_CASE(test_inf)
|
||||
}
|
||||
{
|
||||
const std::string token("+inf");
|
||||
toml::detail::location<std::string> loc("test", token);
|
||||
toml::detail::location loc("test", token);
|
||||
const auto r = parse_floating(loc);
|
||||
BOOST_CHECK(r.is_ok());
|
||||
BOOST_CHECK(std::isinf(r.unwrap().first));
|
||||
@@ -144,7 +144,7 @@ BOOST_AUTO_TEST_CASE(test_inf)
|
||||
}
|
||||
{
|
||||
const std::string token("-inf");
|
||||
toml::detail::location<std::string> loc("test", token);
|
||||
toml::detail::location loc("test", token);
|
||||
const auto r = parse_floating(loc);
|
||||
BOOST_CHECK(r.is_ok());
|
||||
BOOST_CHECK(std::isinf(r.unwrap().first));
|
||||
@@ -156,21 +156,21 @@ BOOST_AUTO_TEST_CASE(test_nan)
|
||||
{
|
||||
{
|
||||
const std::string token("nan");
|
||||
toml::detail::location<std::string> loc("test", token);
|
||||
toml::detail::location loc("test", token);
|
||||
const auto r = parse_floating(loc);
|
||||
BOOST_CHECK(r.is_ok());
|
||||
BOOST_CHECK(std::isnan(r.unwrap().first));
|
||||
}
|
||||
{
|
||||
const std::string token("+nan");
|
||||
toml::detail::location<std::string> loc("test", token);
|
||||
toml::detail::location loc("test", token);
|
||||
const auto r = parse_floating(loc);
|
||||
BOOST_CHECK(r.is_ok());
|
||||
BOOST_CHECK(std::isnan(r.unwrap().first));
|
||||
}
|
||||
{
|
||||
const std::string token("-nan");
|
||||
toml::detail::location<std::string> loc("test", token);
|
||||
toml::detail::location loc("test", token);
|
||||
const auto r = parse_floating(loc);
|
||||
BOOST_CHECK(r.is_ok());
|
||||
BOOST_CHECK(std::isnan(r.unwrap().first));
|
||||
|
||||
@@ -19,7 +19,7 @@ BOOST_AUTO_TEST_CASE(test_normal_table)
|
||||
"key2 = 42\n"
|
||||
"key3 = 3.14\n"
|
||||
);
|
||||
location<std::string> loc("test", table);
|
||||
location loc("test", table);
|
||||
|
||||
const auto result = toml::detail::parse_ml_table<toml::value>(loc);
|
||||
BOOST_TEST(result.is_ok());
|
||||
@@ -36,7 +36,7 @@ BOOST_AUTO_TEST_CASE(test_nested_table)
|
||||
"a.b = \"value\"\n"
|
||||
"a.c.d = 42\n"
|
||||
);
|
||||
location<std::string> loc("test", table);
|
||||
location loc("test", table);
|
||||
|
||||
const auto result = toml::detail::parse_ml_table<toml::value>(loc);
|
||||
BOOST_TEST(result.is_ok());
|
||||
|
||||
@@ -58,13 +58,9 @@ struct character
|
||||
{
|
||||
static constexpr char target = C;
|
||||
|
||||
template<typename Cont>
|
||||
static result<region<Cont>, none_t>
|
||||
invoke(location<Cont>& loc)
|
||||
static result<region, none_t>
|
||||
invoke(location& loc)
|
||||
{
|
||||
static_assert(std::is_same<char, typename Cont::value_type>::value,
|
||||
"internal error: container::value_type should be `char`.");
|
||||
|
||||
if(loc.iter() == loc.end()) {return none();}
|
||||
const auto first = loc.iter();
|
||||
|
||||
@@ -75,7 +71,7 @@ struct character
|
||||
}
|
||||
loc.advance(); // update location
|
||||
|
||||
return ok(region<Cont>(loc, first, loc.iter()));
|
||||
return ok(region(loc, first, loc.iter()));
|
||||
}
|
||||
};
|
||||
template<char C>
|
||||
@@ -91,13 +87,9 @@ struct in_range
|
||||
static constexpr char upper = Up;
|
||||
static constexpr char lower = Low;
|
||||
|
||||
template<typename Cont>
|
||||
static result<region<Cont>, none_t>
|
||||
invoke(location<Cont>& loc)
|
||||
static result<region, none_t>
|
||||
invoke(location& loc)
|
||||
{
|
||||
static_assert(std::is_same<char, typename Cont::value_type>::value,
|
||||
"internal error: container::value_type should be `char`.");
|
||||
|
||||
if(loc.iter() == loc.end()) {return none();}
|
||||
const auto first = loc.iter();
|
||||
|
||||
@@ -108,7 +100,7 @@ struct in_range
|
||||
}
|
||||
|
||||
loc.advance();
|
||||
return ok(region<Cont>(loc, first, loc.iter()));
|
||||
return ok(region(loc, first, loc.iter()));
|
||||
}
|
||||
};
|
||||
template<char L, char U> constexpr char in_range<L, U>::upper;
|
||||
@@ -119,13 +111,9 @@ template<char L, char U> constexpr char in_range<L, U>::lower;
|
||||
template<typename Combinator>
|
||||
struct exclude
|
||||
{
|
||||
template<typename Cont>
|
||||
static result<region<Cont>, none_t>
|
||||
invoke(location<Cont>& loc)
|
||||
static result<region, none_t>
|
||||
invoke(location& loc)
|
||||
{
|
||||
static_assert(std::is_same<char, typename Cont::value_type>::value,
|
||||
"internal error: container::value_type should be `char`.");
|
||||
|
||||
if(loc.iter() == loc.end()) {return none();}
|
||||
auto first = loc.iter();
|
||||
|
||||
@@ -136,7 +124,7 @@ struct exclude
|
||||
return none();
|
||||
}
|
||||
loc.reset(std::next(first)); // XXX maybe loc.advance() is okay but...
|
||||
return ok(region<Cont>(loc, first, loc.iter()));
|
||||
return ok(region(loc, first, loc.iter()));
|
||||
}
|
||||
};
|
||||
|
||||
@@ -144,19 +132,15 @@ struct exclude
|
||||
template<typename Combinator>
|
||||
struct maybe
|
||||
{
|
||||
template<typename Cont>
|
||||
static result<region<Cont>, none_t>
|
||||
invoke(location<Cont>& loc)
|
||||
static result<region, none_t>
|
||||
invoke(location& loc)
|
||||
{
|
||||
static_assert(std::is_same<char, typename Cont::value_type>::value,
|
||||
"internal error: container::value_type should be `char`.");
|
||||
|
||||
const auto rslt = Combinator::invoke(loc);
|
||||
if(rslt.is_ok())
|
||||
{
|
||||
return rslt;
|
||||
}
|
||||
return ok(region<Cont>(loc));
|
||||
return ok(region(loc));
|
||||
}
|
||||
};
|
||||
|
||||
@@ -166,13 +150,9 @@ struct sequence;
|
||||
template<typename Head, typename ... Tail>
|
||||
struct sequence<Head, Tail...>
|
||||
{
|
||||
template<typename Cont>
|
||||
static result<region<Cont>, none_t>
|
||||
invoke(location<Cont>& loc)
|
||||
static result<region, none_t>
|
||||
invoke(location& loc)
|
||||
{
|
||||
static_assert(std::is_same<char, typename Cont::value_type>::value,
|
||||
"internal error: container::value_type should be `char`.");
|
||||
|
||||
const auto first = loc.iter();
|
||||
const auto rslt = Head::invoke(loc);
|
||||
if(rslt.is_err())
|
||||
@@ -184,9 +164,9 @@ struct sequence<Head, Tail...>
|
||||
}
|
||||
|
||||
// called from the above function only, recursively.
|
||||
template<typename Cont, typename Iterator>
|
||||
static result<region<Cont>, none_t>
|
||||
invoke(location<Cont>& loc, region<Cont> reg, Iterator first)
|
||||
template<typename Iterator>
|
||||
static result<region, none_t>
|
||||
invoke(location& loc, region reg, Iterator first)
|
||||
{
|
||||
const auto rslt = Head::invoke(loc);
|
||||
if(rslt.is_err())
|
||||
@@ -203,9 +183,9 @@ template<typename Head>
|
||||
struct sequence<Head>
|
||||
{
|
||||
// would be called from sequence<T ...>::invoke only.
|
||||
template<typename Cont, typename Iterator>
|
||||
static result<region<Cont>, none_t>
|
||||
invoke(location<Cont>& loc, region<Cont> reg, Iterator first)
|
||||
template<typename Iterator>
|
||||
static result<region, none_t>
|
||||
invoke(location& loc, region reg, Iterator first)
|
||||
{
|
||||
const auto rslt = Head::invoke(loc);
|
||||
if(rslt.is_err())
|
||||
@@ -224,13 +204,9 @@ struct either;
|
||||
template<typename Head, typename ... Tail>
|
||||
struct either<Head, Tail...>
|
||||
{
|
||||
template<typename Cont>
|
||||
static result<region<Cont>, none_t>
|
||||
invoke(location<Cont>& loc)
|
||||
static result<region, none_t>
|
||||
invoke(location& loc)
|
||||
{
|
||||
static_assert(std::is_same<char, typename Cont::value_type>::value,
|
||||
"internal error: container::value_type should be `char`.");
|
||||
|
||||
const auto rslt = Head::invoke(loc);
|
||||
if(rslt.is_ok()) {return rslt;}
|
||||
return either<Tail...>::invoke(loc);
|
||||
@@ -239,12 +215,9 @@ struct either<Head, Tail...>
|
||||
template<typename Head>
|
||||
struct either<Head>
|
||||
{
|
||||
template<typename Cont>
|
||||
static result<region<Cont>, none_t>
|
||||
invoke(location<Cont>& loc)
|
||||
static result<region, none_t>
|
||||
invoke(location& loc)
|
||||
{
|
||||
static_assert(std::is_same<char, typename Cont::value_type>::value,
|
||||
"internal error: container::value_type should be `char`.");
|
||||
return Head::invoke(loc);
|
||||
}
|
||||
};
|
||||
@@ -259,11 +232,10 @@ struct unlimited{};
|
||||
template<typename T, std::size_t N>
|
||||
struct repeat<T, exactly<N>>
|
||||
{
|
||||
template<typename Cont>
|
||||
static result<region<Cont>, none_t>
|
||||
invoke(location<Cont>& loc)
|
||||
static result<region, none_t>
|
||||
invoke(location& loc)
|
||||
{
|
||||
region<Cont> retval(loc);
|
||||
region retval(loc);
|
||||
const auto first = loc.iter();
|
||||
for(std::size_t i=0; i<N; ++i)
|
||||
{
|
||||
@@ -282,11 +254,10 @@ struct repeat<T, exactly<N>>
|
||||
template<typename T, std::size_t N>
|
||||
struct repeat<T, at_least<N>>
|
||||
{
|
||||
template<typename Cont>
|
||||
static result<region<Cont>, none_t>
|
||||
invoke(location<Cont>& loc)
|
||||
static result<region, none_t>
|
||||
invoke(location& loc)
|
||||
{
|
||||
region<Cont> retval(loc);
|
||||
region retval(loc);
|
||||
|
||||
const auto first = loc.iter();
|
||||
for(std::size_t i=0; i<N; ++i)
|
||||
@@ -314,11 +285,10 @@ struct repeat<T, at_least<N>>
|
||||
template<typename T>
|
||||
struct repeat<T, unlimited>
|
||||
{
|
||||
template<typename Cont>
|
||||
static result<region<Cont>, none_t>
|
||||
invoke(location<Cont>& loc)
|
||||
static result<region, none_t>
|
||||
invoke(location& loc)
|
||||
{
|
||||
region<Cont> retval(loc);
|
||||
region retval(loc);
|
||||
while(true)
|
||||
{
|
||||
auto rslt = T::invoke(loc);
|
||||
|
||||
37
toml/get.hpp
37
toml/get.hpp
@@ -189,8 +189,7 @@ get(const basic_value<C, M, V>& v)
|
||||
{
|
||||
throw type_error(detail::format_underline("toml::value: "
|
||||
"bad_cast to std::chrono::system_clock::time_point", {
|
||||
{std::addressof(detail::get_region(v)),
|
||||
concat_to_string("the actual type is ", v.type())}
|
||||
{v.location(), concat_to_string("the actual type is ", v.type())}
|
||||
}), v.location());
|
||||
}
|
||||
}
|
||||
@@ -336,7 +335,7 @@ get(const basic_value<C, M, V>& v)
|
||||
throw std::out_of_range(detail::format_underline(concat_to_string(
|
||||
"toml::get: specified container size is ", container.size(),
|
||||
" but there are ", ar.size(), " elements in toml array."), {
|
||||
{std::addressof(detail::get_region(v)), "here"}
|
||||
{v.location(), "here"}
|
||||
}));
|
||||
}
|
||||
std::transform(ar.cbegin(), ar.cend(), container.begin(),
|
||||
@@ -360,9 +359,7 @@ get(const basic_value<C, M, V>& v)
|
||||
{
|
||||
throw std::out_of_range(detail::format_underline(concat_to_string(
|
||||
"toml::get: specified std::pair but there are ", ar.size(),
|
||||
" elements in toml array."), {
|
||||
{std::addressof(detail::get_region(v)), "here"}
|
||||
}));
|
||||
" elements in toml array."), {{v.location(), "here"}}));
|
||||
}
|
||||
return std::make_pair(::toml::get<first_type >(ar.at(0)),
|
||||
::toml::get<second_type>(ar.at(1)));
|
||||
@@ -392,9 +389,7 @@ get(const basic_value<C, M, V>& v)
|
||||
throw std::out_of_range(detail::format_underline(concat_to_string(
|
||||
"toml::get: specified std::tuple with ",
|
||||
std::tuple_size<T>::value, " elements, but there are ", ar.size(),
|
||||
" elements in toml array."), {
|
||||
{std::addressof(detail::get_region(v)), "here"}
|
||||
}));
|
||||
" elements in toml array."), {{v.location(), "here"}}));
|
||||
}
|
||||
return detail::get_tuple_impl<T>(ar,
|
||||
detail::make_index_sequence<std::tuple_size<T>::value>{});
|
||||
@@ -511,9 +506,7 @@ find(const basic_value<C, M, V>& v, const std::size_t idx)
|
||||
if(ary.size() <= idx)
|
||||
{
|
||||
throw std::out_of_range(detail::format_underline(concat_to_string(
|
||||
"index ", idx, " is out of range"), {
|
||||
{std::addressof(detail::get_region(v)), "in this array"}
|
||||
}));
|
||||
"index ", idx, " is out of range"), {{v.location(), "in this array"}}));
|
||||
}
|
||||
return ary.at(idx);
|
||||
}
|
||||
@@ -525,9 +518,7 @@ basic_value<C, M, V>& find(basic_value<C, M, V>& v, const std::size_t idx)
|
||||
if(ary.size() <= idx)
|
||||
{
|
||||
throw std::out_of_range(detail::format_underline(concat_to_string(
|
||||
"index ", idx, " is out of range"), {
|
||||
{std::addressof(detail::get_region(v)), "in this array"}
|
||||
}));
|
||||
"index ", idx, " is out of range"), {{v.location(), "in this array"}}));
|
||||
}
|
||||
return ary.at(idx);
|
||||
}
|
||||
@@ -539,9 +530,7 @@ basic_value<C, M, V> find(basic_value<C, M, V>&& v, const std::size_t idx)
|
||||
if(ary.size() <= idx)
|
||||
{
|
||||
throw std::out_of_range(detail::format_underline(concat_to_string(
|
||||
"index ", idx, " is out of range"), {
|
||||
{std::addressof(detail::get_region(v)), "in this array"}
|
||||
}));
|
||||
"index ", idx, " is out of range"), {{v.location(), "in this array"}}));
|
||||
}
|
||||
return basic_value<C, M, V>(std::move(ary.at(idx)));
|
||||
}
|
||||
@@ -599,9 +588,7 @@ find(const basic_value<C, M, V>& v, const std::size_t idx)
|
||||
if(ary.size() <= idx)
|
||||
{
|
||||
throw std::out_of_range(detail::format_underline(concat_to_string(
|
||||
"index ", idx, " is out of range"), {
|
||||
{std::addressof(detail::get_region(v)), "in this array"}
|
||||
}));
|
||||
"index ", idx, " is out of range"), {{v.location(), "in this array"}}));
|
||||
}
|
||||
return ::toml::get<T>(ary.at(idx));
|
||||
}
|
||||
@@ -614,9 +601,7 @@ find(basic_value<C, M, V>& v, const std::size_t idx)
|
||||
if(ary.size() <= idx)
|
||||
{
|
||||
throw std::out_of_range(detail::format_underline(concat_to_string(
|
||||
"index ", idx, " is out of range"), {
|
||||
{std::addressof(detail::get_region(v)), "in this array"}
|
||||
}));
|
||||
"index ", idx, " is out of range"), {{v.location(), "in this array"}}));
|
||||
}
|
||||
return ::toml::get<T>(ary.at(idx));
|
||||
}
|
||||
@@ -629,9 +614,7 @@ find(basic_value<C, M, V>&& v, const std::size_t idx)
|
||||
if(ary.size() <= idx)
|
||||
{
|
||||
throw std::out_of_range(detail::format_underline(concat_to_string(
|
||||
"index ", idx, " is out of range"), {
|
||||
{std::addressof(detail::get_region(v)), "in this array"}
|
||||
}));
|
||||
"index ", idx, " is out of range"), {{v.location(), "in this array"}}));
|
||||
}
|
||||
return ::toml::get<T>(std::move(ary.at(idx)));
|
||||
}
|
||||
|
||||
@@ -13,7 +13,7 @@ inline namespace toml_literals
|
||||
|
||||
inline ::toml::value operator"" _toml(const char* str, std::size_t len)
|
||||
{
|
||||
::toml::detail::location<std::vector<char>>
|
||||
::toml::detail::location
|
||||
loc(/* filename = */ std::string("TOML literal encoded in a C++ code"),
|
||||
/* contents = */ std::vector<char>(str, str + len));
|
||||
|
||||
@@ -76,8 +76,7 @@ inline ::toml::value operator"" _toml(const char* str, std::size_t len)
|
||||
}
|
||||
else // none of them.
|
||||
{
|
||||
throw ::toml::syntax_error(data.unwrap_err(),
|
||||
source_location(std::addressof(loc)));
|
||||
throw ::toml::syntax_error(data.unwrap_err(), source_location(loc));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
554
toml/parser.hpp
554
toml/parser.hpp
File diff suppressed because it is too large
Load Diff
140
toml/region.hpp
140
toml/region.hpp
@@ -67,22 +67,21 @@ struct region_base
|
||||
//
|
||||
// it contains pointer to the file content and iterator that points the current
|
||||
// location.
|
||||
template<typename Container>
|
||||
struct location final : public region_base
|
||||
{
|
||||
using const_iterator = typename Container::const_iterator;
|
||||
using const_iterator = typename std::vector<char>::const_iterator;
|
||||
using difference_type = typename const_iterator::difference_type;
|
||||
using source_ptr = std::shared_ptr<const Container>;
|
||||
using source_ptr = std::shared_ptr<const std::vector<char>>;
|
||||
|
||||
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)
|
||||
: source_(std::make_shared<Container>(std::move(cont))), line_number_(1),
|
||||
source_name_(std::move(name)), iter_(source_->cbegin())
|
||||
location(std::string name, std::vector<char> cont)
|
||||
: source_(std::make_shared<std::vector<char>>(std::move(cont))),
|
||||
line_number_(1), source_name_(std::move(name)), iter_(source_->cbegin())
|
||||
{}
|
||||
location(std::string name, const std::string& cont)
|
||||
: source_(std::make_shared<std::vector<char>>(cont.begin(), cont.end())),
|
||||
line_number_(1), source_name_(std::move(name)), iter_(source_->cbegin())
|
||||
{}
|
||||
|
||||
location(const location&) = default;
|
||||
location(location&&) = default;
|
||||
location& operator=(const location&) = default;
|
||||
@@ -195,33 +194,27 @@ struct location final : public region_base
|
||||
//
|
||||
// it contains pointer to the file content and iterator that points the first
|
||||
// and last location.
|
||||
template<typename Container>
|
||||
struct region final : public region_base
|
||||
{
|
||||
using const_iterator = typename Container::const_iterator;
|
||||
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");
|
||||
using const_iterator = typename std::vector<char>::const_iterator;
|
||||
using source_ptr = std::shared_ptr<const std::vector<char>>;
|
||||
|
||||
// delete default constructor. source_ never be null.
|
||||
region() = delete;
|
||||
|
||||
region(const location<Container>& loc)
|
||||
explicit region(const location& loc)
|
||||
: source_(loc.source()), source_name_(loc.name()),
|
||||
first_(loc.iter()), last_(loc.iter())
|
||||
{}
|
||||
region(location<Container>&& loc)
|
||||
explicit region(location&& loc)
|
||||
: source_(loc.source()), source_name_(loc.name()),
|
||||
first_(loc.iter()), last_(loc.iter())
|
||||
{}
|
||||
|
||||
region(const location<Container>& loc, const_iterator f, const_iterator l)
|
||||
region(const location& loc, const_iterator f, const_iterator l)
|
||||
: source_(loc.source()), source_name_(loc.name()), first_(f), last_(l)
|
||||
{}
|
||||
region(location<Container>&& loc, const_iterator f, const_iterator l)
|
||||
region(location&& loc, const_iterator f, const_iterator l)
|
||||
: source_(loc.source()), source_name_(loc.name()), first_(f), last_(l)
|
||||
{}
|
||||
|
||||
@@ -419,107 +412,6 @@ struct region final : public region_base
|
||||
const_iterator first_, last_;
|
||||
};
|
||||
|
||||
// to show a better error message.
|
||||
inline std::string format_underline(const std::string& message,
|
||||
const std::vector<std::pair<region_base const*, std::string>>& reg_com,
|
||||
const std::vector<std::string>& helps = {},
|
||||
const bool colorize = TOML11_ERROR_MESSAGE_COLORIZED)
|
||||
{
|
||||
assert(!reg_com.empty());
|
||||
|
||||
const auto line_num_width = static_cast<int>(std::max_element(
|
||||
reg_com.begin(), reg_com.end(),
|
||||
[](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;
|
||||
|
||||
if(colorize)
|
||||
{
|
||||
retval << color::colorize; // turn on ANSI color
|
||||
}
|
||||
|
||||
// XXX
|
||||
// Here, before `colorize` support, it does not output `[error]` prefix
|
||||
// automatically. So some user may output it manually and this change may
|
||||
// duplicate the prefix. To avoid it, check the first 7 characters and
|
||||
// if it is "[error]", it removes that part from the message shown.
|
||||
if(message.size() > 7 && message.substr(0, 7) == "[error]")
|
||||
{
|
||||
retval << color::bold << color::red << "[error]" << color::reset
|
||||
<< color::bold << message.substr(7) << color::reset << '\n';
|
||||
}
|
||||
else
|
||||
{
|
||||
retval << color::bold << color::red << "[error] " << color::reset
|
||||
<< color::bold << message << color::reset << '\n';
|
||||
}
|
||||
|
||||
for(auto iter = reg_com.begin(); iter != reg_com.end(); ++iter)
|
||||
{
|
||||
// if the filenames are the same, print "..."
|
||||
if(iter != reg_com.begin() &&
|
||||
std::prev(iter)->first->name() == iter->first->name())
|
||||
{
|
||||
retval << color::bold << color::blue << "\n ...\n" << color::reset;
|
||||
}
|
||||
else // if filename differs, print " --> filename.toml"
|
||||
{
|
||||
if(iter != reg_com.begin()) {retval << '\n';}
|
||||
retval << color::bold << color::blue << " --> " << color::reset
|
||||
<< iter->first->name() << '\n';
|
||||
// add one almost-empty line for readability
|
||||
retval << make_string(static_cast<std::size_t>(line_num_width + 1), ' ')
|
||||
<< color::bold << color::blue << " | " << color::reset << '\n';
|
||||
}
|
||||
const region_base* const reg = iter->first;
|
||||
const std::string& comment = iter->second;
|
||||
|
||||
retval << ' ' << color::bold << color::blue << std::setw(line_num_width)
|
||||
<< std::right << reg->line_num() << " | " << color::reset
|
||||
<< reg->line() << '\n';
|
||||
|
||||
retval << make_string(static_cast<std::size_t>(line_num_width + 1), ' ')
|
||||
<< color::bold << color::blue << " | " << color::reset
|
||||
<< make_string(reg->before(), ' ');
|
||||
|
||||
if(reg->size() == 1)
|
||||
{
|
||||
// invalid
|
||||
// ^------
|
||||
retval << color::bold << color::red
|
||||
<< '^' << make_string(reg->after(), '-') << color::reset;
|
||||
}
|
||||
else
|
||||
{
|
||||
// invalid
|
||||
// ~~~~~~~
|
||||
const auto underline_len = (std::min)(reg->size(), reg->line().size());
|
||||
retval << color::bold << color::red
|
||||
<< make_string(underline_len, '~') << color::reset;
|
||||
}
|
||||
retval << ' ';
|
||||
retval << comment;
|
||||
}
|
||||
|
||||
if(!helps.empty())
|
||||
{
|
||||
retval << '\n';
|
||||
retval << make_string(static_cast<std::size_t>(line_num_width + 1), ' ');
|
||||
retval << color::bold << color::blue << " | " << color::reset;
|
||||
for(const auto& help : helps)
|
||||
{
|
||||
retval << color::bold << "\nHint: " << color::reset;
|
||||
retval << help;
|
||||
}
|
||||
}
|
||||
return retval.str();
|
||||
}
|
||||
|
||||
} // detail
|
||||
} // toml
|
||||
#endif// TOML11_REGION_H
|
||||
|
||||
@@ -29,7 +29,7 @@ std::basic_string<charT, traits, Alloc>
|
||||
format_key(const std::basic_string<charT, traits, Alloc>& key)
|
||||
{
|
||||
// check the key can be a bare (unquoted) key
|
||||
detail::location<toml::key> loc(key, key);
|
||||
detail::location loc(key, std::vector<char>(key.begin(), key.end()));
|
||||
detail::lex_unquoted_key::invoke(loc);
|
||||
if(loc.iter() == loc.end())
|
||||
{
|
||||
|
||||
@@ -61,6 +61,21 @@ struct source_location
|
||||
}
|
||||
}
|
||||
|
||||
explicit source_location(const detail::region& reg)
|
||||
: line_num_(static_cast<std::uint_least32_t>(std::stoul(reg.line_num()))),
|
||||
column_num_(static_cast<std::uint_least32_t>(reg.before() + 1)),
|
||||
region_size_(static_cast<std::uint_least32_t>(reg.size())),
|
||||
file_name_(reg.name()),
|
||||
line_str_ (reg.line())
|
||||
{}
|
||||
explicit source_location(const detail::location& loc)
|
||||
: line_num_(static_cast<std::uint_least32_t>(std::stoul(loc.line_num()))),
|
||||
column_num_(static_cast<std::uint_least32_t>(loc.before() + 1)),
|
||||
region_size_(static_cast<std::uint_least32_t>(loc.size())),
|
||||
file_name_(loc.name()),
|
||||
line_str_ (loc.line())
|
||||
{}
|
||||
|
||||
~source_location() = default;
|
||||
source_location(source_location const&) = default;
|
||||
source_location(source_location &&) = default;
|
||||
@@ -83,5 +98,135 @@ struct source_location
|
||||
std::string line_str_;
|
||||
};
|
||||
|
||||
namespace detail
|
||||
{
|
||||
|
||||
// internal error message generation.
|
||||
inline std::string format_underline(const std::string& message,
|
||||
const std::vector<std::pair<source_location, std::string>>& loc_com,
|
||||
const std::vector<std::string>& helps = {},
|
||||
const bool colorize = TOML11_ERROR_MESSAGE_COLORIZED)
|
||||
{
|
||||
std::size_t line_num_width = 0;
|
||||
for(const auto& lc : loc_com)
|
||||
{
|
||||
std::uint_least32_t line = lc.first.line();
|
||||
std::size_t digit = 0;
|
||||
while(line != 0)
|
||||
{
|
||||
line /= 10;
|
||||
digit += 1;
|
||||
}
|
||||
line_num_width = (std::max)(line_num_width, digit);
|
||||
}
|
||||
// 1 is the minimum width
|
||||
line_num_width = std::max<std::size_t>(line_num_width, 1);
|
||||
|
||||
std::ostringstream retval;
|
||||
|
||||
if(colorize)
|
||||
{
|
||||
retval << color::colorize; // turn on ANSI color
|
||||
}
|
||||
|
||||
// XXX
|
||||
// Here, before `colorize` support, it does not output `[error]` prefix
|
||||
// automatically. So some user may output it manually and this change may
|
||||
// duplicate the prefix. To avoid it, check the first 7 characters and
|
||||
// if it is "[error]", it removes that part from the message shown.
|
||||
if(message.size() > 7 && message.substr(0, 7) == "[error]")
|
||||
{
|
||||
retval << color::bold << color::red << "[error]" << color::reset
|
||||
<< color::bold << message.substr(7) << color::reset << '\n';
|
||||
}
|
||||
else
|
||||
{
|
||||
retval << color::bold << color::red << "[error] " << color::reset
|
||||
<< color::bold << message << color::reset << '\n';
|
||||
}
|
||||
|
||||
const auto format_one_location = [line_num_width]
|
||||
(std::ostringstream& oss,
|
||||
const source_location& loc, const std::string& comment) -> void
|
||||
{
|
||||
oss << ' ' << color::bold << color::blue
|
||||
<< std::setw(static_cast<int>(line_num_width))
|
||||
<< std::right << loc.line() << " | " << color::reset
|
||||
<< loc.line_str() << '\n';
|
||||
|
||||
oss << make_string(line_num_width + 1, ' ')
|
||||
<< color::bold << color::blue << " | " << color::reset
|
||||
<< make_string(loc.column()-1 /*1-origin*/, ' ');
|
||||
|
||||
if(loc.region() == 1)
|
||||
{
|
||||
// invalid
|
||||
// ^------
|
||||
oss << color::bold << color::red << "^---" << color::reset;
|
||||
}
|
||||
else
|
||||
{
|
||||
// invalid
|
||||
// ~~~~~~~
|
||||
const auto underline_len = (std::min)(
|
||||
static_cast<std::size_t>(loc.region()), loc.line_str().size());
|
||||
oss << color::bold << color::red
|
||||
<< make_string(underline_len, '~') << color::reset;
|
||||
}
|
||||
oss << ' ';
|
||||
oss << comment;
|
||||
return;
|
||||
};
|
||||
|
||||
assert(!loc_com.empty());
|
||||
|
||||
// --> example.toml
|
||||
// |
|
||||
retval << color::bold << color::blue << " --> " << color::reset
|
||||
<< loc_com.front().first.file_name() << '\n';
|
||||
retval << make_string(line_num_width + 1, ' ')
|
||||
<< color::bold << color::blue << " | " << color::reset << '\n';
|
||||
// 1 | key value
|
||||
// | ^--- missing =
|
||||
format_one_location(retval, loc_com.front().first, loc_com.front().second);
|
||||
|
||||
// process the rest of the locations
|
||||
for(std::size_t i=1; i<loc_com.size(); ++i)
|
||||
{
|
||||
const auto& prev = loc_com.at(i-1);
|
||||
const auto& curr = loc_com.at(i);
|
||||
|
||||
retval << '\n';
|
||||
// if the filenames are the same, print "..."
|
||||
if(prev.first.file_name() == curr.first.file_name())
|
||||
{
|
||||
retval << color::bold << color::blue << " ...\n" << color::reset;
|
||||
}
|
||||
else // if filename differs, print " --> filename.toml" again
|
||||
{
|
||||
retval << color::bold << color::blue << " --> " << color::reset
|
||||
<< curr.first.file_name() << '\n';
|
||||
retval << make_string(line_num_width + 1, ' ')
|
||||
<< color::bold << color::blue << " |\n" << color::reset;
|
||||
}
|
||||
|
||||
format_one_location(retval, curr.first, curr.second);
|
||||
}
|
||||
|
||||
if(!helps.empty())
|
||||
{
|
||||
retval << '\n';
|
||||
retval << make_string(line_num_width + 1, ' ');
|
||||
retval << color::bold << color::blue << " | " << color::reset;
|
||||
for(const auto& help : helps)
|
||||
{
|
||||
retval << color::bold << "\nHint: " << color::reset;
|
||||
retval << help;
|
||||
}
|
||||
}
|
||||
return retval.str();
|
||||
}
|
||||
|
||||
} // detail
|
||||
} // toml
|
||||
#endif// TOML11_SOURCE_LOCATION_HPP
|
||||
|
||||
124
toml/value.hpp
124
toml/value.hpp
@@ -22,21 +22,15 @@ namespace detail
|
||||
|
||||
// to show error messages. not recommended for users.
|
||||
template<typename Value>
|
||||
inline region_base const& get_region(const Value& v)
|
||||
inline region_base const* get_region(const Value& v)
|
||||
{
|
||||
return *(v.region_info_);
|
||||
return v.region_info_.get();
|
||||
}
|
||||
|
||||
template<typename Value, typename Region>
|
||||
void change_region(Value& v, Region&& reg)
|
||||
template<typename Value>
|
||||
void change_region(Value& v, region reg)
|
||||
{
|
||||
using region_type = typename std::remove_reference<
|
||||
typename std::remove_cv<Region>::type
|
||||
>::type;
|
||||
|
||||
std::shared_ptr<region_base> new_reg =
|
||||
std::make_shared<region_type>(std::forward<region_type>(reg));
|
||||
v.region_info_ = new_reg;
|
||||
v.region_info_ = std::make_shared<region>(std::move(reg));
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -46,8 +40,7 @@ throw_bad_cast(const std::string& funcname, value_t actual, const Value& v)
|
||||
{
|
||||
throw type_error(detail::format_underline(
|
||||
concat_to_string(funcname, "bad_cast to ", Expected), {
|
||||
{std::addressof(get_region(v)),
|
||||
concat_to_string("the actual type is ", actual)}
|
||||
{v.location(), concat_to_string("the actual type is ", actual)}
|
||||
}), v.location());
|
||||
}
|
||||
|
||||
@@ -74,8 +67,8 @@ throw_key_not_found_error(const Value& v, const key& ky)
|
||||
// It actually points to the top-level table at the first character,
|
||||
// not `[table]`. But it is too confusing. To avoid the confusion, the error
|
||||
// message should explicitly say "key not found in the top-level table".
|
||||
const auto& reg = get_region(v);
|
||||
if(reg.line_num() == "1" && reg.size() == 1)
|
||||
const auto loc = v.location();
|
||||
if(loc.line() == 1 && loc.region() == 1)
|
||||
{
|
||||
// Here it assumes that top-level table starts at the first character.
|
||||
// The region corresponds to the top-level table will be generated at
|
||||
@@ -111,16 +104,14 @@ throw_key_not_found_error(const Value& v, const key& ky)
|
||||
//
|
||||
throw std::out_of_range(format_underline(concat_to_string(
|
||||
"key \"", ky, "\" not found in the top-level table"), {
|
||||
{std::addressof(reg), "the top-level table starts here"}
|
||||
{loc, "the top-level table starts here"}
|
||||
}));
|
||||
}
|
||||
else
|
||||
{
|
||||
// normal table.
|
||||
throw std::out_of_range(format_underline(concat_to_string(
|
||||
"key \"", ky, "\" not found"), {
|
||||
{std::addressof(reg), "in this table"}
|
||||
}));
|
||||
"key \"", ky, "\" not found"), { {loc, "in this table"} }));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1056,95 +1047,87 @@ class basic_value
|
||||
//
|
||||
// Those constructors take detail::region that contains parse result.
|
||||
|
||||
template<typename Container>
|
||||
basic_value(boolean b, detail::region<Container> reg)
|
||||
basic_value(boolean b, detail::region reg)
|
||||
: type_(value_t::boolean),
|
||||
region_info_(std::make_shared<detail::region<Container>>(std::move(reg))),
|
||||
region_info_(std::make_shared<detail::region>(std::move(reg))),
|
||||
comments_(region_info_->comments())
|
||||
{
|
||||
assigner(this->boolean_, b);
|
||||
}
|
||||
template<typename T, typename Container, typename std::enable_if<
|
||||
template<typename T, typename std::enable_if<
|
||||
detail::conjunction<
|
||||
std::is_integral<T>, detail::negation<std::is_same<T, boolean>>
|
||||
>::value, std::nullptr_t>::type = nullptr>
|
||||
basic_value(T i, detail::region<Container> reg)
|
||||
basic_value(T i, detail::region reg)
|
||||
: type_(value_t::integer),
|
||||
region_info_(std::make_shared<detail::region<Container>>(std::move(reg))),
|
||||
region_info_(std::make_shared<detail::region>(std::move(reg))),
|
||||
comments_(region_info_->comments())
|
||||
{
|
||||
assigner(this->integer_, static_cast<integer>(i));
|
||||
}
|
||||
template<typename T, typename Container, typename std::enable_if<
|
||||
template<typename T, typename std::enable_if<
|
||||
std::is_floating_point<T>::value, std::nullptr_t>::type = nullptr>
|
||||
basic_value(T f, detail::region<Container> reg)
|
||||
basic_value(T f, detail::region reg)
|
||||
: type_(value_t::floating),
|
||||
region_info_(std::make_shared<detail::region<Container>>(std::move(reg))),
|
||||
region_info_(std::make_shared<detail::region>(std::move(reg))),
|
||||
comments_(region_info_->comments())
|
||||
{
|
||||
assigner(this->floating_, static_cast<floating>(f));
|
||||
}
|
||||
template<typename Container>
|
||||
basic_value(toml::string s, detail::region<Container> reg)
|
||||
basic_value(toml::string s, detail::region reg)
|
||||
: type_(value_t::string),
|
||||
region_info_(std::make_shared<detail::region<Container>>(std::move(reg))),
|
||||
region_info_(std::make_shared<detail::region>(std::move(reg))),
|
||||
comments_(region_info_->comments())
|
||||
{
|
||||
assigner(this->string_, std::move(s));
|
||||
}
|
||||
template<typename Container>
|
||||
basic_value(const local_date& ld, detail::region<Container> reg)
|
||||
basic_value(const local_date& ld, detail::region reg)
|
||||
: type_(value_t::local_date),
|
||||
region_info_(std::make_shared<detail::region<Container>>(std::move(reg))),
|
||||
region_info_(std::make_shared<detail::region>(std::move(reg))),
|
||||
comments_(region_info_->comments())
|
||||
{
|
||||
assigner(this->local_date_, ld);
|
||||
}
|
||||
template<typename Container>
|
||||
basic_value(const local_time& lt, detail::region<Container> reg)
|
||||
basic_value(const local_time& lt, detail::region reg)
|
||||
: type_(value_t::local_time),
|
||||
region_info_(std::make_shared<detail::region<Container>>(std::move(reg))),
|
||||
region_info_(std::make_shared<detail::region>(std::move(reg))),
|
||||
comments_(region_info_->comments())
|
||||
{
|
||||
assigner(this->local_time_, lt);
|
||||
}
|
||||
template<typename Container>
|
||||
basic_value(const local_datetime& ldt, detail::region<Container> reg)
|
||||
basic_value(const local_datetime& ldt, detail::region reg)
|
||||
: type_(value_t::local_datetime),
|
||||
region_info_(std::make_shared<detail::region<Container>>(std::move(reg))),
|
||||
region_info_(std::make_shared<detail::region>(std::move(reg))),
|
||||
comments_(region_info_->comments())
|
||||
{
|
||||
assigner(this->local_datetime_, ldt);
|
||||
}
|
||||
template<typename Container>
|
||||
basic_value(const offset_datetime& odt, detail::region<Container> reg)
|
||||
basic_value(const offset_datetime& odt, detail::region reg)
|
||||
: type_(value_t::offset_datetime),
|
||||
region_info_(std::make_shared<detail::region<Container>>(std::move(reg))),
|
||||
region_info_(std::make_shared<detail::region>(std::move(reg))),
|
||||
comments_(region_info_->comments())
|
||||
{
|
||||
assigner(this->offset_datetime_, odt);
|
||||
}
|
||||
template<typename Container>
|
||||
basic_value(const array_type& ary, detail::region<Container> reg)
|
||||
basic_value(const array_type& ary, detail::region reg)
|
||||
: type_(value_t::array),
|
||||
region_info_(std::make_shared<detail::region<Container>>(std::move(reg))),
|
||||
region_info_(std::make_shared<detail::region>(std::move(reg))),
|
||||
comments_(region_info_->comments())
|
||||
{
|
||||
assigner(this->array_, ary);
|
||||
}
|
||||
template<typename Container>
|
||||
basic_value(const table_type& tab, detail::region<Container> reg)
|
||||
basic_value(const table_type& tab, detail::region reg)
|
||||
: type_(value_t::table),
|
||||
region_info_(std::make_shared<detail::region<Container>>(std::move(reg))),
|
||||
region_info_(std::make_shared<detail::region>(std::move(reg))),
|
||||
comments_(region_info_->comments())
|
||||
{
|
||||
assigner(this->table_, tab);
|
||||
}
|
||||
|
||||
template<typename T, typename Container, typename std::enable_if<
|
||||
template<typename T, typename std::enable_if<
|
||||
detail::is_exact_toml_type<T, value_type>::value,
|
||||
std::nullptr_t>::type = nullptr>
|
||||
basic_value(std::pair<T, detail::region<Container>> parse_result)
|
||||
basic_value(std::pair<T, detail::region> parse_result)
|
||||
: basic_value(std::move(parse_result.first), std::move(parse_result.second))
|
||||
{}
|
||||
|
||||
@@ -1578,9 +1561,9 @@ class basic_value
|
||||
{
|
||||
throw std::out_of_range(detail::format_underline(
|
||||
"toml::value::at(idx): no element corresponding to the index", {
|
||||
{this->region_info_.get(),
|
||||
concat_to_string("the length is ", this->as_array(std::nothrow).size(),
|
||||
", and the specified index is ", idx)}
|
||||
{this->location(), concat_to_string("the length is ",
|
||||
this->as_array(std::nothrow).size(),
|
||||
", and the specified index is ", idx)}
|
||||
}));
|
||||
}
|
||||
return this->as_array().at(idx);
|
||||
@@ -1596,9 +1579,9 @@ class basic_value
|
||||
{
|
||||
throw std::out_of_range(detail::format_underline(
|
||||
"toml::value::at(idx): no element corresponding to the index", {
|
||||
{this->region_info_.get(),
|
||||
concat_to_string("the length is ", this->as_array(std::nothrow).size(),
|
||||
", and the specified index is ", idx)}
|
||||
{this->location(), concat_to_string("the length is ",
|
||||
this->as_array(std::nothrow).size(),
|
||||
", and the specified index is ", idx)}
|
||||
}));
|
||||
}
|
||||
return this->as_array(std::nothrow).at(idx);
|
||||
@@ -1668,7 +1651,7 @@ class basic_value
|
||||
{
|
||||
throw type_error(detail::format_underline(
|
||||
"toml::value::size(): bad_cast to container types", {
|
||||
{this->region_info_.get(),
|
||||
{this->location(),
|
||||
concat_to_string("the actual type is ", this->type_)}
|
||||
}), this->location());
|
||||
}
|
||||
@@ -1718,10 +1701,10 @@ class basic_value
|
||||
|
||||
// for error messages
|
||||
template<typename Value>
|
||||
friend region_base const& detail::get_region(const Value& v);
|
||||
friend region_base const* detail::get_region(const Value& v);
|
||||
|
||||
template<typename Value, typename Region>
|
||||
friend void detail::change_region(Value& v, Region&& reg);
|
||||
template<typename Value>
|
||||
friend void detail::change_region(Value& v, detail::region reg);
|
||||
|
||||
private:
|
||||
|
||||
@@ -1926,10 +1909,8 @@ inline std::string format_error(const std::string& err_msg,
|
||||
std::vector<std::string> hints = {},
|
||||
const bool colorize = TOML11_ERROR_MESSAGE_COLORIZED)
|
||||
{
|
||||
return detail::format_underline(err_msg,
|
||||
std::vector<std::pair<detail::region_base const*, std::string>>{
|
||||
{std::addressof(detail::get_region(v)), comment}
|
||||
}, std::move(hints), colorize);
|
||||
return detail::format_underline(err_msg, {{v.location(), comment}},
|
||||
std::move(hints), colorize);
|
||||
}
|
||||
|
||||
template<typename C, template<typename ...> class T, template<typename ...> class A>
|
||||
@@ -1939,10 +1920,8 @@ inline std::string format_error(const std::string& err_msg,
|
||||
std::vector<std::string> hints = {},
|
||||
const bool colorize = TOML11_ERROR_MESSAGE_COLORIZED)
|
||||
{
|
||||
return detail::format_underline(err_msg,
|
||||
std::vector<std::pair<detail::region_base const*, std::string>>{
|
||||
{std::addressof(detail::get_region(v1)), comment1},
|
||||
{std::addressof(detail::get_region(v2)), comment2}
|
||||
return detail::format_underline(err_msg, {
|
||||
{v1.location(), comment1}, {v2.location(), comment2}
|
||||
}, std::move(hints), colorize);
|
||||
}
|
||||
|
||||
@@ -1954,11 +1933,8 @@ inline std::string format_error(const std::string& err_msg,
|
||||
std::vector<std::string> hints = {},
|
||||
const bool colorize = TOML11_ERROR_MESSAGE_COLORIZED)
|
||||
{
|
||||
return detail::format_underline(err_msg,
|
||||
std::vector<std::pair<detail::region_base const*, std::string>>{
|
||||
{std::addressof(detail::get_region(v1)), comment1},
|
||||
{std::addressof(detail::get_region(v2)), comment2},
|
||||
{std::addressof(detail::get_region(v3)), comment3}
|
||||
return detail::format_underline(err_msg, {{v1.location(), comment1},
|
||||
{v2.location(), comment2}, {v3.location(), comment3}
|
||||
}, std::move(hints), colorize);
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user