22 #include <unordered_map> 25 #if __cplusplus > 201103L 26 #define CPPTOML_DEPRECATED(reason) [[deprecated(reason)]] 27 #elif defined(__clang__) 28 #define CPPTOML_DEPRECATED(reason) __attribute__((deprecated(reason))) 29 #elif defined(__GNUG__) 30 #define CPPTOML_DEPRECATED(reason) __attribute__((deprecated)) 31 #elif defined(_MSC_VER) 33 #define CPPTOML_DEPRECATED(reason) __declspec(deprecated) 35 #define CPPTOML_DEPRECATED(reason) [[deprecated(reason)]] 43 #if defined(CPPTOML_USE_MAP) 46 using string_to_base_map = std::map<std::string, std::shared_ptr<base>>;
50 using string_to_base_map
51 = std::unordered_map<std::string, std::shared_ptr<base>>;
62 option() : empty_{
true}, value_()
67 option(T
value) : empty_{
false}, value_(std::move(value))
72 explicit operator bool()
const 77 const T& operator*()
const 82 const T* operator->()
const 88 T value_or(U&& alternative)
const 92 return static_cast<T
>(std::forward<U>(alternative));
118 int minute_offset = 0;
130 dt.year = t.tm_year + 1900;
131 dt.month = t.tm_mon + 1;
134 dt.minute = t.tm_min;
135 dt.second = t.tm_sec;
138 strftime(buf, 16,
"%z", &t);
140 int offset = std::stoi(buf);
141 dt.hour_offset = offset / 100;
142 dt.minute_offset = offset % 100;
146 CPPTOML_DEPRECATED(
"from_local has been renamed to from_zoned")
149 return from_zoned(t);
155 dt.year = t.tm_year + 1900;
156 dt.month = t.tm_mon + 1;
159 dt.minute = t.tm_min;
160 dt.second = t.tm_sec;
165 CPPTOML_DEPRECATED(
"datetime has been renamed to offset_datetime")
171 fill_guard(std::ostream& os) : os_(os), fill_{os.fill()}
183 std::ostream::char_type fill_;
186 inline std::ostream& operator<<(std::ostream& os,
const local_date& dt)
192 os << setw(4) << dt.year <<
"-" << setw(2) << dt.month <<
"-" << setw(2)
198 inline std::ostream& operator<<(std::ostream& os,
const local_time& ltime)
204 os << setw(2) << ltime.hour <<
":" << setw(2) << ltime.minute <<
":" 205 << setw(2) << ltime.second;
207 if (ltime.microsecond > 0)
211 for (
int curr_us = ltime.microsecond; curr_us; power /= 10)
213 auto num = curr_us / power;
215 curr_us -= num * power;
222 inline std::ostream& operator<<(std::ostream& os,
const zone_offset& zo)
229 if (zo.hour_offset != 0 || zo.minute_offset != 0)
231 if (zo.hour_offset > 0)
239 os << setw(2) << std::abs(zo.hour_offset) <<
":" << setw(2)
240 << std::abs(zo.minute_offset);
250 inline std::ostream& operator<<(std::ostream& os,
const local_datetime& dt)
252 return os << static_cast<const local_date&>(dt) <<
"T" 253 << static_cast<const local_time&>(dt);
256 inline std::ostream& operator<<(std::ostream& os,
const offset_datetime& dt)
258 return os << static_cast<const local_datetime&>(dt)
259 << static_cast<const zone_offset&>(dt);
262 template <
class T,
class... Ts>
265 template <
class T,
class V>
270 template <
class T,
class V,
class... Ts>
273 const static bool value 282 :
is_one_of<T, std::string, int64_t, double, bool, local_date, local_time,
283 local_datetime, offset_datetime>
287 template <
class T,
class Enable =
void>
295 || std::is_convertible<T, std::string>::value;
300 valid_value_or_string_convertible<T>::value>
::type>
302 using value_type =
typename std::conditional<
304 typename std::decay<T>::type, std::string>
::type;
308 static value_type construct(T&& val)
310 return value_type(val);
317 typename
std::enable_if<
318 !valid_value_or_string_convertible<T>::value
319 && std::is_floating_point<typename std::decay<T>::type>::value>
::type>
321 using value_type =
typename std::decay<T>::type;
325 static value_type construct(T&& val)
327 return value_type(val);
333 T, typename
std::enable_if<
334 !valid_value_or_string_convertible<T>::value
335 && !std::is_floating_point<typename std::decay<T>::type>::value
336 && std::is_signed<typename std::decay<T>::type>::value>
::type>
338 using value_type = int64_t;
342 static value_type construct(T&& val)
344 if (val < (std::numeric_limits<int64_t>::min)())
345 throw std::underflow_error{
"constructed value cannot be " 346 "represented by a 64-bit signed " 349 if (val > (std::numeric_limits<int64_t>::max)())
350 throw std::overflow_error{
"constructed value cannot be represented " 351 "by a 64-bit signed integer"};
353 return static_cast<int64_t
>(val);
359 T, typename
std::enable_if<
360 !valid_value_or_string_convertible<T>::value
361 && std::is_unsigned<typename std::decay<T>::type>::value>
::type>
363 using value_type = int64_t;
367 static value_type construct(T&& val)
369 if (val > static_cast<uint64_t>((std::numeric_limits<int64_t>::max)()))
370 throw std::overflow_error{
"constructed value cannot be represented " 371 "by a 64-bit signed integer"};
373 return static_cast<int64_t
>(val);
394 inline std::shared_ptr<typename value_traits<T>::type> make_value(T&& val);
395 inline std::shared_ptr<array> make_array();
400 inline std::shared_ptr<T> make_element();
403 inline std::shared_ptr<table> make_table();
404 inline std::shared_ptr<table_array> make_table_array(
bool is_inline =
false);
406 #if defined(CPPTOML_NO_RTTI) 426 struct base_type_traits;
429 struct base_type_traits<std::string>
431 static const base_type type = base_type::STRING;
435 struct base_type_traits<local_time>
437 static const base_type type = base_type::LOCAL_TIME;
441 struct base_type_traits<local_date>
443 static const base_type type = base_type::LOCAL_DATE;
447 struct base_type_traits<local_datetime>
449 static const base_type type = base_type::LOCAL_DATETIME;
453 struct base_type_traits<offset_datetime>
455 static const base_type type = base_type::OFFSET_DATETIME;
459 struct base_type_traits<int64_t>
461 static const base_type type = base_type::INT;
465 struct base_type_traits<double>
467 static const base_type type = base_type::FLOAT;
471 struct base_type_traits<bool>
473 static const base_type type = base_type::BOOL;
477 struct base_type_traits<table>
479 static const base_type type = base_type::TABLE;
483 struct base_type_traits<array>
485 static const base_type type = base_type::ARRAY;
489 struct base_type_traits<table_array>
491 static const base_type type = base_type::TABLE_ARRAY;
498 class base :
public std::enable_shared_from_this<base>
501 virtual ~
base() =
default;
503 virtual std::shared_ptr<base> clone()
const = 0;
527 return std::static_pointer_cast<table>(shared_from_this());
544 return std::static_pointer_cast<array>(shared_from_this());
561 if (is_table_array())
562 return std::static_pointer_cast<table_array>(shared_from_this());
571 std::shared_ptr<value<T>> as();
574 std::shared_ptr<const value<T>> as()
const;
576 template <
class Visitor,
class... Args>
577 void accept(Visitor&& visitor, Args&&... args)
const;
579 #if defined(CPPTOML_NO_RTTI) 580 base_type type()
const 586 base(
const base_type t) : type_(t)
592 const base_type type_ = base_type::NONE;
609 struct make_shared_enabler
615 friend std::shared_ptr<typename value_traits<U>::type>
616 cpptoml::make_value(U&& val);
621 std::shared_ptr<base> clone()
const override;
623 value(
const make_shared_enabler&,
const T& val) :
value(val)
656 #if defined(CPPTOML_NO_RTTI) 657 value(
const T& val) :
base(base_type_traits<T>::type), data_(val)
661 value(
const T& val) : data_(val)
671 std::shared_ptr<typename value_traits<T>::type> make_value(T&& val)
674 using enabler =
typename value_type::make_shared_enabler;
675 return std::make_shared<value_type>(
682 #if defined(CPPTOML_NO_RTTI) 683 if (type() == base_type_traits<T>::type)
684 return std::static_pointer_cast<
value<T>>(shared_from_this());
688 return std::dynamic_pointer_cast<value<T>>(shared_from_this());
695 inline std::shared_ptr<value<double>>
base::as()
697 #if defined(CPPTOML_NO_RTTI) 698 if (type() == base_type::FLOAT)
699 return std::static_pointer_cast<
value<double>>(shared_from_this());
701 if (type() == base_type::INT)
703 auto v = std::static_pointer_cast<
value<int64_t>>(shared_from_this());
704 return make_value<double>(
static_cast<double>(v->get()));
707 if (
auto v = std::dynamic_pointer_cast<
value<double>>(shared_from_this()))
710 if (
auto v = std::dynamic_pointer_cast<
value<int64_t>>(shared_from_this()))
711 return make_value<double>(
static_cast<double>(v->get()));
718 inline std::shared_ptr<const value<T>>
base::as()
const 720 #if defined(CPPTOML_NO_RTTI) 721 if (type() == base_type_traits<T>::type)
722 return std::static_pointer_cast<
const value<T>>(shared_from_this());
726 return std::dynamic_pointer_cast<
const value<T>>(shared_from_this());
733 inline std::shared_ptr<const value<double>>
base::as()
const 735 #if defined(CPPTOML_NO_RTTI) 736 if (type() == base_type::FLOAT)
740 if (type() == base_type::INT)
742 auto v = as<int64_t>();
745 return make_value<double>(
static_cast<double>(v->get()));
749 = std::dynamic_pointer_cast<
const value<double>>(shared_from_this()))
752 if (
auto v = as<int64_t>())
756 return make_value<double>(
static_cast<double>(v->get()));
777 friend std::shared_ptr<array> make_array();
779 std::shared_ptr<base> clone()
const override;
786 using size_type = std::size_t;
800 return values_.begin();
805 return values_.begin();
810 return values_.end();
815 return values_.end();
821 std::vector<std::shared_ptr<base>>&
get()
829 const std::vector<std::shared_ptr<base>>&
get()
const 834 std::shared_ptr<base> at(
size_t idx_source)
const 836 return values_.at(idx_source);
844 std::vector<std::shared_ptr<value<T>>>
array_of()
const 846 std::vector<std::shared_ptr<value<T>>> result(values_.size());
848 std::transform(values_.begin(), values_.end(), result.begin(),
849 [&](std::shared_ptr<base> v) {
return v->as<T>(); });
861 std::vector<T> result;
862 result.reserve(values_.size());
864 for (
const auto& val : values_)
866 if (
auto v = val->as<T>())
867 result.push_back(v->get());
872 return {std::move(result)};
881 std::vector<std::shared_ptr<array>> result(values_.size());
883 std::transform(values_.begin(), values_.end(), result.begin(),
884 [&](std::shared_ptr<base> v) -> std::shared_ptr<array> {
886 return std::static_pointer_cast<array>(v);
887 return std::shared_ptr<array>{};
899 if (values_.empty() || values_[0]->as<T>())
901 values_.push_back(val);
914 if (values_.empty() || values_[0]->is_array())
916 values_.push_back(val);
931 push_back(make_value(std::forward<T>(val)));
940 if (values_.empty() || values_[0]->as<T>())
942 return values_.insert(position,
value);
955 if (values_.empty() || values_[0]->is_array())
957 return values_.insert(position, value);
972 return insert(position, make_value(std::forward<T>(val)));
980 return values_.erase(position);
1000 #if defined(CPPTOML_NO_RTTI) 1001 array() :
base(base_type::ARRAY)
1009 template <
class InputIterator>
1010 array(InputIterator begin, InputIterator end) : values_{begin, end}
1015 array(
const array& obj) =
delete;
1016 array& operator=(
const array& obj) =
delete;
1018 std::vector<std::shared_ptr<base>> values_;
1021 inline std::shared_ptr<array> make_array()
1023 struct make_shared_enabler :
public array
1025 make_shared_enabler()
1031 return std::make_shared<make_shared_enabler>();
1037 inline std::shared_ptr<array> make_element<array>()
1039 return make_array();
1049 array::get_array_of<array>()
const 1051 std::vector<std::shared_ptr<array>> result;
1052 result.reserve(values_.size());
1054 for (
const auto& val : values_)
1056 if (
auto v = val->as_array())
1057 result.push_back(v);
1062 return {std::move(result)};
1070 friend std::shared_ptr<table_array> make_table_array(
bool);
1073 std::shared_ptr<base> clone()
const override;
1075 using size_type = std::size_t;
1089 return array_.begin();
1094 return array_.begin();
1099 return array_.end();
1104 return array_.end();
1112 std::vector<std::shared_ptr<table>>&
get()
1117 const std::vector<std::shared_ptr<table>>&
get()
const 1127 array_.push_back(val);
1135 return array_.insert(position, value);
1143 return array_.erase(position);
1173 #if defined(CPPTOML_NO_RTTI) 1174 table_array(
bool is_inline =
false)
1175 :
base(base_type::TABLE_ARRAY), is_inline_(is_inline)
1180 table_array(
bool is_inline =
false) : is_inline_(is_inline)
1186 table_array(
const table_array& obj) =
delete;
1187 table_array& operator=(
const table_array& rhs) =
delete;
1189 std::vector<std::shared_ptr<table>> array_;
1190 const bool is_inline_ =
false;
1193 inline std::shared_ptr<table_array> make_table_array(
bool is_inline)
1195 struct make_shared_enabler :
public table_array
1197 make_shared_enabler(
bool mse_is_inline) : table_array(mse_is_inline)
1203 return std::make_shared<make_shared_enabler>(is_inline);
1209 inline std::shared_ptr<table_array> make_element<table_array>()
1211 return make_table_array(
true);
1219 typename std::enable_if<!std::is_floating_point<T>::value
1220 && std::is_signed<T>::value,
1222 get_impl(
const std::shared_ptr<base>& elem)
1224 if (
auto v = elem->as<int64_t>())
1226 if (v->get() < (std::numeric_limits<T>::min)())
1227 throw std::underflow_error{
1228 "T cannot represent the value requested in get"};
1230 if (v->get() > (std::numeric_limits<T>::max)())
1231 throw std::overflow_error{
1232 "T cannot represent the value requested in get"};
1234 return {
static_cast<T
>(v->get())};
1243 typename std::enable_if<!std::is_same<T, bool>::value
1244 && std::is_unsigned<T>::value,
1246 get_impl(
const std::shared_ptr<base>& elem)
1248 if (
auto v = elem->as<int64_t>())
1251 throw std::underflow_error{
"T cannot store negative value in get"};
1253 if (static_cast<uint64_t>(v->get()) > (std::numeric_limits<T>::max)())
1254 throw std::overflow_error{
1255 "T cannot represent the value requested in get"};
1257 return {
static_cast<T
>(v->get())};
1266 typename std::enable_if<!std::is_integral<T>::value
1267 || std::is_same<T, bool>::value,
1269 get_impl(
const std::shared_ptr<base>& elem)
1271 if (
auto v = elem->as<T>())
1287 friend class table_array;
1288 friend std::shared_ptr<table> make_table();
1290 std::shared_ptr<base> clone()
const override;
1304 return map_.begin();
1309 return map_.begin();
1329 return map_.empty();
1337 return map_.find(key) != map_.end();
1347 return resolve_qualified(key);
1354 std::shared_ptr<base>
get(
const std::string& key)
const 1356 return map_.at(key);
1368 std::shared_ptr<base> p;
1369 resolve_qualified(key, &p);
1376 std::shared_ptr<table>
get_table(
const std::string& key)
const 1378 if (contains(key) &&
get(key)->is_table())
1379 return std::static_pointer_cast<table>(
get(key));
1389 if (contains_qualified(key) && get_qualified(key)->is_table())
1390 return std::static_pointer_cast<table>(get_qualified(key));
1397 std::shared_ptr<array>
get_array(
const std::string& key)
const 1401 return get(key)->as_array();
1409 if (!contains_qualified(key))
1411 return get_qualified(key)->as_array();
1421 return get(key)->as_table_array();
1428 std::shared_ptr<table_array>
1431 if (!contains_qualified(key))
1433 return get_qualified(key)->as_table_array();
1445 return get_impl<T>(
get(key));
1447 catch (
const std::out_of_range&)
1463 return get_impl<T>(get_qualified(key));
1465 catch (
const std::out_of_range&)
1484 if (
auto v = get_array(key))
1486 std::vector<T> result;
1487 result.reserve(v->get().size());
1489 for (
const auto& b : v->get())
1491 if (
auto val = b->as<T>())
1492 result.push_back(val->get());
1496 return {std::move(result)};
1516 if (
auto v = get_array_qualified(key))
1518 std::vector<T> result;
1519 result.reserve(v->get().size());
1521 for (
const auto& b : v->get())
1523 if (
auto val = b->as<T>())
1524 result.push_back(val->get());
1528 return {std::move(result)};
1537 void insert(
const std::string& key,
const std::shared_ptr<base>&
value)
1550 insert(key, make_value(std::forward<T>(val)));
1562 #if defined(CPPTOML_NO_RTTI) 1563 table() :
base(base_type::TABLE)
1574 table(
const table& obj) =
delete;
1575 table& operator=(
const table& rhs) =
delete;
1577 std::vector<std::string> split(
const std::string&
value,
1578 char separator)
const 1580 std::vector<std::string> result;
1581 std::string::size_type p = 0;
1582 std::string::size_type q;
1583 while ((q = value.find(separator, p)) != std::string::npos)
1585 result.emplace_back(value, p, q - p);
1588 result.emplace_back(value, p);
1597 bool resolve_qualified(
const std::string& key,
1598 std::shared_ptr<base>* p =
nullptr)
const 1600 auto parts = split(key,
'.');
1601 auto last_key = parts.back();
1604 auto cur_table =
this;
1605 for (
const auto& part : parts)
1607 cur_table = cur_table->get_table(part).get();
1613 throw std::out_of_range{key +
" is not a valid key"};
1618 return cur_table->map_.count(last_key) != 0;
1620 *p = cur_table->map_.at(last_key);
1624 string_to_base_map map_;
1638 table::get_array_of<array>(
const std::string& key)
const 1640 if (
auto v = get_array(key))
1642 std::vector<std::shared_ptr<array>> result;
1643 result.reserve(v->get().size());
1645 for (
const auto& b : v->get())
1647 if (
auto val = b->as_array())
1648 result.push_back(val);
1653 return {std::move(result)};
1670 table::get_qualified_array_of<array>(
const std::string& key)
const 1672 if (
auto v = get_array_qualified(key))
1674 std::vector<std::shared_ptr<array>> result;
1675 result.reserve(v->get().size());
1677 for (
const auto& b : v->get())
1679 if (
auto val = b->as_array())
1680 result.push_back(val);
1685 return {std::move(result)};
1691 std::shared_ptr<table> make_table()
1693 struct make_shared_enabler :
public table
1695 make_shared_enabler()
1701 return std::make_shared<make_shared_enabler>();
1707 inline std::shared_ptr<table> make_element<table>()
1709 return make_table();
1716 return make_value(data_);
1719 inline std::shared_ptr<base> array::clone()
const 1721 auto result = make_array();
1722 result->reserve(values_.size());
1723 for (
const auto& ptr : values_)
1724 result->values_.push_back(ptr->clone());
1728 inline std::shared_ptr<base> table_array::clone()
const 1730 auto result = make_table_array(is_inline());
1731 result->reserve(array_.size());
1732 for (
const auto& ptr : array_)
1733 result->array_.push_back(ptr->clone()->as_table());
1737 inline std::shared_ptr<base> table::clone()
const 1739 auto result = make_table();
1740 for (
const auto& pr : map_)
1741 result->insert(pr.first, pr.second->clone());
1756 : std::runtime_error{err +
" at line " + std::to_string(line_number)}
1761 inline bool is_number(
char c)
1763 return c >=
'0' && c <=
'9';
1766 inline bool is_hex(
char c)
1768 return is_number(c) || (c >=
'a' && c <= 'f') || (c >=
'A' && c <=
'F');
1774 template <
class OnError>
1778 consumer(std::string::iterator& it,
const std::string::iterator& end,
1780 : it_(it), end_(end), on_error_(std::forward<OnError>(on_error))
1785 void operator()(
char c)
1787 if (it_ == end_ || *it_ != c)
1792 template <std::
size_t N>
1793 void operator()(
const char (&str)[N])
1795 std::for_each(std::begin(str), std::end(str) - 1,
1796 [&](
char c) { (*this)(c); });
1799 void eat_or(
char a,
char b)
1801 if (it_ == end_ || (*it_ != a && *it_ != b))
1806 int eat_digits(
int len)
1809 for (
int i = 0; i < len; ++i)
1811 if (!is_number(*it_) || it_ == end_)
1813 val = 10 * val + (*it_++ -
'0');
1824 std::string::iterator& it_;
1825 const std::string::iterator& end_;
1829 template <
class OnError>
1831 const std::string::iterator& end,
1841 inline std::istream& getline(std::istream& input, std::string& line)
1845 std::istream::sentry sentry{input,
true};
1846 auto sb = input.rdbuf();
1850 auto c = sb->sbumpc();
1853 if (sb->sgetc() ==
'\n')
1860 if (c == std::istream::traits_type::eof())
1863 input.setstate(std::ios::eofbit);
1867 line.push_back(static_cast<char>(c));
1881 parser(std::istream& stream) : input_(stream)
1894 std::shared_ptr<table> root = make_table();
1896 table* curr_table = root.get();
1898 while (detail::getline(input_, line_))
1901 auto it = line_.begin();
1902 auto end = line_.end();
1903 consume_whitespace(it, end);
1911 if (it == end || *it ==
'#')
1915 curr_table = root.
get();
1916 parse_table(it, end, curr_table);
1920 parse_key_value(it, end, curr_table);
1921 consume_whitespace(it, end);
1922 eol_or_comment(it, end);
1929 #if defined _MSC_VER 1930 __declspec(noreturn)
1931 #elif defined __GNUC__ 1932 __attribute__((noreturn))
1934 void throw_parse_exception(
const std::string& err)
1939 void parse_table(std::string::iterator& it,
1940 const std::string::iterator& end, table*& curr_table)
1945 throw_parse_exception(
"Unexpected end of table");
1947 parse_table_array(it, end, curr_table);
1949 parse_single_table(it, end, curr_table);
1952 void parse_single_table(std::string::iterator& it,
1953 const std::string::iterator& end,
1956 if (it == end || *it ==
']')
1957 throw_parse_exception(
"Table name cannot be empty");
1959 std::string full_table_name;
1960 bool inserted =
false;
1962 auto key_end = [](
char c) {
return c ==
']'; };
1964 auto key_part_handler = [&](
const std::string& part) {
1966 throw_parse_exception(
"Empty component of table name");
1968 if (!full_table_name.empty())
1969 full_table_name +=
'.';
1970 full_table_name += part;
1975 auto b = curr_table->
get(part);
1978 std::shared_ptr<base> b = curr_table->
get(part);
1981 curr_table = static_cast<table*>(b.get());
1982 else if (b->is_table_array())
1983 curr_table = std::static_pointer_cast<table_array>(b)
1988 throw_parse_exception(
"Key " + full_table_name
1989 +
"already exists as a value");
1994 curr_table->
insert(part, make_table());
1995 curr_table =
static_cast<table*
>(curr_table->
get(part).get());
1999 key_part_handler(parse_key(it, end, key_end, key_part_handler));
2002 throw_parse_exception(
2003 "Unterminated table declaration; did you forget a ']'?");
2007 std::string errmsg{
"Unexpected character in table definition: "};
2011 throw_parse_exception(errmsg);
2018 = [](
const std::pair<
const std::string&,
2019 const std::shared_ptr<base>&>& p) {
2020 return p.second->is_value();
2027 if (curr_table->empty()
2028 || std::any_of(curr_table->begin(), curr_table->end(),
2031 throw_parse_exception(
"Redefinition of table " 2037 consume_whitespace(it, end);
2038 eol_or_comment(it, end);
2041 void parse_table_array(std::string::iterator& it,
2042 const std::string::iterator& end, table*& curr_table)
2045 if (it == end || *it ==
']')
2046 throw_parse_exception(
"Table array name cannot be empty");
2048 auto key_end = [](
char c) {
return c ==
']'; };
2050 std::string full_ta_name;
2051 auto key_part_handler = [&](
const std::string& part) {
2053 throw_parse_exception(
"Empty component of table array name");
2055 if (!full_ta_name.empty())
2056 full_ta_name +=
'.';
2057 full_ta_name += part;
2062 auto b = curr_table->
get(part);
2065 std::shared_ptr<base> b = curr_table->
get(part);
2071 if (it != end && *it ==
']')
2073 if (!b->is_table_array())
2075 throw_parse_exception(
"Key " + full_ta_name
2076 +
" is not a table array");
2079 auto v = b->as_table_array();
2083 throw_parse_exception(
"Static array " + full_ta_name
2084 +
" cannot be appended to");
2087 v->get().push_back(make_table());
2088 curr_table = v->
get().back().get();
2094 curr_table = static_cast<table*>(b.get());
2095 else if (b->is_table_array())
2096 curr_table = std::static_pointer_cast<table_array>(b)
2101 throw_parse_exception(
"Key " + full_ta_name
2102 +
" already exists as a value");
2110 if (it != end && *it ==
']')
2112 curr_table->
insert(part, make_table_array());
2113 auto arr = std::static_pointer_cast<table_array>(
2114 curr_table->
get(part));
2115 arr->get().push_back(make_table());
2116 curr_table = arr->
get().back().get();
2122 curr_table->
insert(part, make_table());
2124 =
static_cast<table*
>(curr_table->
get(part).get());
2129 key_part_handler(parse_key(it, end, key_end, key_part_handler));
2132 auto eat = make_consumer(it, end, [
this]() {
2133 throw_parse_exception(
"Unterminated table array name");
2138 consume_whitespace(it, end);
2139 eol_or_comment(it, end);
2142 void parse_key_value(std::string::iterator& it, std::string::iterator& end,
2145 auto key_end = [](
char c) {
return c ==
'='; };
2147 auto key_part_handler = [&](
const std::string& part) {
2153 auto val = curr_table->
get(part);
2154 if (val->is_table())
2156 curr_table =
static_cast<table*
>(val.get());
2160 throw_parse_exception(
"Key " + part
2161 +
" already exists as a value");
2166 auto newtable = make_table();
2167 curr_table->
insert(part, newtable);
2168 curr_table = newtable.
get();
2172 auto key = parse_key(it, end, key_end, key_part_handler);
2175 throw_parse_exception(
"Key " + key +
" already present");
2176 if (it == end || *it !=
'=')
2177 throw_parse_exception(
"Value must follow after a '='");
2179 consume_whitespace(it, end);
2180 curr_table->
insert(key, parse_value(it, end));
2181 consume_whitespace(it, end);
2184 template <
class KeyEndFinder,
class KeyPartHandler>
2186 parse_key(std::string::iterator& it,
const std::string::iterator& end,
2187 KeyEndFinder&& key_end, KeyPartHandler&& key_part_handler)
2190 while (it != end && !key_end(*it))
2192 auto part = parse_simple_key(it, end);
2193 consume_whitespace(it, end);
2195 if (it == end || key_end(*it))
2202 std::string errmsg{
"Unexpected character in key: "};
2206 throw_parse_exception(errmsg);
2209 key_part_handler(part);
2215 throw_parse_exception(
"Unexpected end of key");
2218 std::string parse_simple_key(std::string::iterator& it,
2219 const std::string::iterator& end)
2221 consume_whitespace(it, end);
2224 throw_parse_exception(
"Unexpected end of key (blank key?)");
2226 if (*it ==
'"' || *it ==
'\'')
2228 return string_literal(it, end, *it);
2232 auto bke = std::find_if(it, end, [](
char c) {
2233 return c ==
'.' || c ==
'=' || c ==
']';
2235 return parse_bare_key(it, bke);
2239 std::string parse_bare_key(std::string::iterator& it,
2240 const std::string::iterator& end)
2244 throw_parse_exception(
"Bare key missing name");
2249 consume_backwards_whitespace(key_end, it);
2251 std::string key{it, key_end};
2253 if (std::find(it, key_end,
'#') != key_end)
2255 throw_parse_exception(
"Bare key " + key +
" cannot contain #");
2258 if (std::find_if(it, key_end,
2259 [](
char c) {
return c ==
' ' || c ==
'\t'; })
2262 throw_parse_exception(
"Bare key " + key
2263 +
" cannot contain whitespace");
2266 if (std::find_if(it, key_end,
2267 [](
char c) {
return c ==
'[' || c ==
']'; })
2270 throw_parse_exception(
"Bare key " + key
2271 +
" cannot contain '[' or ']'");
2278 enum class parse_type
2292 std::shared_ptr<base> parse_value(std::string::iterator& it,
2293 std::string::iterator& end)
2295 parse_type type = determine_value_type(it, end);
2298 case parse_type::STRING:
2299 return parse_string(it, end);
2300 case parse_type::LOCAL_TIME:
2301 return parse_time(it, end);
2302 case parse_type::LOCAL_DATE:
2303 case parse_type::LOCAL_DATETIME:
2304 case parse_type::OFFSET_DATETIME:
2305 return parse_date(it, end);
2306 case parse_type::INT:
2307 case parse_type::FLOAT:
2308 return parse_number(it, end);
2309 case parse_type::BOOL:
2310 return parse_bool(it, end);
2311 case parse_type::ARRAY:
2312 return parse_array(it, end);
2313 case parse_type::INLINE_TABLE:
2314 return parse_inline_table(it, end);
2316 throw_parse_exception(
"Failed to parse value");
2320 parse_type determine_value_type(
const std::string::iterator& it,
2321 const std::string::iterator& end)
2325 throw_parse_exception(
"Failed to parse value type");
2327 if (*it ==
'"' || *it ==
'\'')
2329 return parse_type::STRING;
2331 else if (is_time(it, end))
2333 return parse_type::LOCAL_TIME;
2335 else if (
auto dtype = date_type(it, end))
2339 else if (is_number(*it) || *it ==
'-' || *it ==
'+' 2340 || (*it ==
'i' && it + 1 != end && it[1] ==
'n' 2341 && it + 2 != end && it[2] ==
'f')
2342 || (*it ==
'n' && it + 1 != end && it[1] ==
'a' 2343 && it + 2 != end && it[2] ==
'n'))
2345 return determine_number_type(it, end);
2347 else if (*it ==
't' || *it ==
'f')
2349 return parse_type::BOOL;
2351 else if (*it ==
'[')
2353 return parse_type::ARRAY;
2355 else if (*it ==
'{')
2357 return parse_type::INLINE_TABLE;
2359 throw_parse_exception(
"Failed to parse value type");
2362 parse_type determine_number_type(
const std::string::iterator& it,
2363 const std::string::iterator& end)
2367 if (*check_it ==
'-' || *check_it ==
'+')
2370 if (check_it == end)
2371 throw_parse_exception(
"Malformed number");
2373 if (*check_it ==
'i' || *check_it ==
'n')
2374 return parse_type::FLOAT;
2376 while (check_it != end && is_number(*check_it))
2378 if (check_it != end && *check_it ==
'.')
2381 while (check_it != end && is_number(*check_it))
2383 return parse_type::FLOAT;
2387 return parse_type::INT;
2391 std::shared_ptr<value<std::string>> parse_string(std::string::iterator& it,
2392 std::string::iterator& end)
2395 assert(delim ==
'"' || delim ==
'\'');
2401 if (check_it != end && *check_it == delim)
2404 if (check_it != end && *check_it == delim)
2407 return parse_multiline_string(it, end, delim);
2410 return make_value<std::string>(string_literal(it, end, delim));
2413 std::shared_ptr<value<std::string>>
2414 parse_multiline_string(std::string::iterator& it,
2415 std::string::iterator& end,
char delim)
2417 std::stringstream ss;
2419 auto is_ws = [](
char c) {
return c ==
' ' || c ==
'\t'; };
2421 bool consuming =
false;
2422 std::shared_ptr<value<std::string>> ret;
2424 auto handle_line = [&](std::string::iterator& local_it,
2425 std::string::iterator& local_end) {
2428 local_it = std::find_if_not(local_it, local_end, is_ws);
2431 if (local_it == local_end)
2437 while (local_it != local_end)
2440 if (delim ==
'"' && *local_it ==
'\\')
2442 auto check = local_it;
2446 consume_whitespace(check, local_end);
2447 if (check == local_end)
2453 ss << parse_escape_code(local_it, local_end);
2458 if (std::distance(local_it, local_end) >= 3)
2460 auto check = local_it;
2462 if (*check++ == delim && *check++ == delim
2463 && *check++ == delim)
2466 ret = make_value<std::string>(ss.str());
2476 handle_line(it, end);
2481 while (detail::getline(input_, line_))
2488 handle_line(it, end);
2497 throw_parse_exception(
"Unterminated multi-line basic string");
2500 std::string string_literal(std::string::iterator& it,
2501 const std::string::iterator& end,
char delim)
2508 if (delim ==
'"' && *it ==
'\\')
2510 val += parse_escape_code(it, end);
2512 else if (*it == delim)
2515 consume_whitespace(it, end);
2523 throw_parse_exception(
"Unterminated string literal");
2526 std::string parse_escape_code(std::string::iterator& it,
2527 const std::string::iterator& end)
2531 throw_parse_exception(
"Invalid escape sequence");
2537 else if (*it ==
't')
2541 else if (*it ==
'n')
2545 else if (*it ==
'f')
2549 else if (*it ==
'r')
2553 else if (*it ==
'"')
2557 else if (*it ==
'\\')
2561 else if (*it ==
'u' || *it ==
'U')
2563 return parse_unicode(it, end);
2567 throw_parse_exception(
"Invalid escape sequence");
2570 return std::string(1, value);
2573 std::string parse_unicode(std::string::iterator& it,
2574 const std::string::iterator& end)
2576 bool large = *it++ ==
'U';
2577 auto codepoint = parse_hex(it, end, large ? 0x10000000 : 0x1000);
2579 if ((codepoint > 0xd7ff && codepoint < 0xe000) || codepoint > 0x10ffff)
2581 throw_parse_exception(
2582 "Unicode escape sequence is not a Unicode scalar value");
2587 if (codepoint <= 0x7f)
2591 result +=
static_cast<char>(codepoint & 0x7f);
2593 else if (codepoint <= 0x7ff)
2601 result +=
static_cast<char>(0xc0 | ((codepoint >> 6) & 0x1f));
2606 result +=
static_cast<char>(0x80 | (codepoint & 0x3f));
2608 else if (codepoint <= 0xffff)
2616 result +=
static_cast<char>(0xe0 | ((codepoint >> 12) & 0x0f));
2617 result +=
static_cast<char>(0x80 | ((codepoint >> 6) & 0x1f));
2618 result +=
static_cast<char>(0x80 | (codepoint & 0x3f));
2628 result +=
static_cast<char>(0xf0 | ((codepoint >> 18) & 0x07));
2629 result +=
static_cast<char>(0x80 | ((codepoint >> 12) & 0x3f));
2630 result +=
static_cast<char>(0x80 | ((codepoint >> 6) & 0x3f));
2631 result +=
static_cast<char>(0x80 | (codepoint & 0x3f));
2636 uint32_t parse_hex(std::string::iterator& it,
2637 const std::string::iterator& end, uint32_t place)
2643 throw_parse_exception(
"Unexpected end of unicode sequence");
2646 throw_parse_exception(
"Invalid unicode escape sequence");
2648 value += place * hex_to_digit(*it++);
2654 uint32_t hex_to_digit(
char c)
2657 return static_cast<uint32_t
>(c -
'0');
2659 +
static_cast<uint32_t
>(c
2660 - ((c >=
'a' && c <=
'f') ?
'a' :
'A'));
2663 std::shared_ptr<base> parse_number(std::string::iterator& it,
2664 const std::string::iterator& end)
2667 auto check_end = find_end_of_number(it, end);
2669 auto eat_sign = [&]() {
2670 if (check_it != end && (*check_it ==
'-' || *check_it ==
'+'))
2674 auto check_no_leading_zero = [&]() {
2675 if (check_it != end && *check_it ==
'0' && check_it + 1 != check_end
2676 && check_it[1] !=
'.')
2678 throw_parse_exception(
"Numbers may not have leading zeros");
2682 auto eat_digits = [&](bool (*check_char)(char)) {
2683 auto beg = check_it;
2684 while (check_it != end && check_char(*check_it))
2687 if (check_it != end && *check_it ==
'_')
2690 if (check_it == end || !check_char(*check_it))
2691 throw_parse_exception(
"Malformed number");
2695 if (check_it == beg)
2696 throw_parse_exception(
"Malformed number");
2699 auto eat_hex = [&]() { eat_digits(&is_hex); };
2701 auto eat_numbers = [&]() { eat_digits(&is_number); };
2703 if (check_it != end && *check_it ==
'0' && check_it + 1 != check_end
2704 && (check_it[1] ==
'x' || check_it[1] ==
'o' || check_it[1] ==
'b'))
2707 char base = *check_it;
2712 return parse_int(it, check_it, 16);
2714 else if (base ==
'o')
2716 auto start = check_it;
2718 auto val = parse_int(start, check_it, 8,
"0");
2724 auto start = check_it;
2726 auto val = parse_int(start, check_it, 2);
2733 check_no_leading_zero();
2735 if (check_it != end && check_it + 1 != end && check_it + 2 != end)
2737 if (check_it[0] ==
'i' && check_it[1] ==
'n' && check_it[2] ==
'f')
2739 auto val = std::numeric_limits<double>::infinity();
2743 return make_value(val);
2745 else if (check_it[0] ==
'n' && check_it[1] ==
'a' 2746 && check_it[2] ==
'n')
2748 auto val = std::numeric_limits<double>::quiet_NaN();
2752 return make_value(val);
2759 && (*check_it ==
'.' || *check_it ==
'e' || *check_it ==
'E'))
2761 bool is_exp = *check_it ==
'e' || *check_it ==
'E';
2764 if (check_it == end)
2765 throw_parse_exception(
"Floats must have trailing digits");
2767 auto eat_exp = [&]() {
2769 check_no_leading_zero();
2778 if (!is_exp && check_it != end
2779 && (*check_it ==
'e' || *check_it ==
'E'))
2785 return parse_float(it, check_it);
2789 return parse_int(it, check_it);
2793 std::shared_ptr<value<int64_t>> parse_int(std::string::iterator& it,
2794 const std::string::iterator& end,
2796 const char* prefix =
"")
2798 std::string v{it, end};
2800 v.erase(std::remove(v.begin(), v.end(),
'_'), v.end());
2804 return make_value<int64_t>(std::stoll(v,
nullptr,
base));
2806 catch (
const std::invalid_argument& ex)
2808 throw_parse_exception(
"Malformed number (invalid argument: " 2809 + std::string{ex.what()} +
")");
2811 catch (
const std::out_of_range& ex)
2813 throw_parse_exception(
"Malformed number (out of range: " 2814 + std::string{ex.what()} +
")");
2818 std::shared_ptr<value<double>> parse_float(std::string::iterator& it,
2819 const std::string::iterator& end)
2821 std::string v{it, end};
2822 v.erase(std::remove(v.begin(), v.end(),
'_'), v.end());
2824 char decimal_point = std::localeconv()->decimal_point[0];
2825 std::replace(v.begin(), v.end(),
'.', decimal_point);
2828 return make_value<double>(std::stod(v));
2830 catch (
const std::invalid_argument& ex)
2832 throw_parse_exception(
"Malformed number (invalid argument: " 2833 + std::string{ex.what()} +
")");
2835 catch (
const std::out_of_range& ex)
2837 throw_parse_exception(
"Malformed number (out of range: " 2838 + std::string{ex.what()} +
")");
2842 std::shared_ptr<value<bool>> parse_bool(std::string::iterator& it,
2843 const std::string::iterator& end)
2845 auto eat = make_consumer(it, end, [
this]() {
2846 throw_parse_exception(
"Attempted to parse invalid boolean value");
2852 return make_value<bool>(
true);
2854 else if (*it ==
'f')
2857 return make_value<bool>(
false);
2864 std::string::iterator find_end_of_number(std::string::iterator it,
2865 std::string::iterator end)
2867 auto ret = std::find_if(it, end, [](
char c) {
2868 return !is_number(c) && c !=
'_' && c !=
'.' && c !=
'e' && c !=
'E' 2869 && c !=
'-' && c !=
'+' && c !=
'x' && c !=
'o' && c !=
'b';
2871 if (ret != end && ret + 1 != end && ret + 2 != end)
2873 if ((ret[0] ==
'i' && ret[1] ==
'n' && ret[2] ==
'f')
2874 || (ret[0] ==
'n' && ret[1] ==
'a' && ret[2] ==
'n'))
2882 std::string::iterator find_end_of_date(std::string::iterator it,
2883 std::string::iterator end)
2885 auto end_of_date = std::find_if(it, end, [](
char c) {
2886 return !is_number(c) && c !=
'-';
2888 if (end_of_date != end && *end_of_date ==
' ' && end_of_date + 1 != end
2889 && is_number(end_of_date[1]))
2891 return std::find_if(end_of_date, end, [](
char c) {
2892 return !is_number(c) && c !=
'T' && c !=
'Z' && c !=
':' 2893 && c !=
'-' && c !=
'+' && c !=
'.';
2897 std::string::iterator find_end_of_time(std::string::iterator it,
2898 std::string::iterator end)
2900 return std::find_if(it, end, [](
char c) {
2901 return !is_number(c) && c !=
':' && c !=
'.';
2905 local_time read_time(std::string::iterator& it,
2906 const std::string::iterator& end)
2908 auto time_end = find_end_of_time(it, end);
2910 auto eat = make_consumer(
2911 it, time_end, [&]() { throw_parse_exception(
"Malformed time"); });
2915 ltime.hour = eat.eat_digits(2);
2917 ltime.minute = eat.eat_digits(2);
2919 ltime.second = eat.eat_digits(2);
2922 if (it != time_end && *it ==
'.')
2925 while (it != time_end && is_number(*it))
2927 ltime.microsecond += power * (*it++ -
'0');
2933 throw_parse_exception(
"Malformed time");
2938 std::shared_ptr<value<local_time>>
2939 parse_time(std::string::iterator& it,
const std::string::iterator& end)
2941 return make_value(read_time(it, end));
2944 std::shared_ptr<base> parse_date(std::string::iterator& it,
2945 const std::string::iterator& end)
2947 auto date_end = find_end_of_date(it, end);
2949 auto eat = make_consumer(
2950 it, date_end, [&]() { throw_parse_exception(
"Malformed date"); });
2953 ldate.year = eat.eat_digits(4);
2955 ldate.month = eat.eat_digits(2);
2957 ldate.day = eat.eat_digits(2);
2960 return make_value(ldate);
2962 eat.eat_or(
'T',
' ');
2965 static_cast<local_date&
>(ldt) = ldate;
2966 static_cast<local_time&
>(ldt) = read_time(it, date_end);
2969 return make_value(ldt);
2972 static_cast<local_datetime&
>(dt) = ldt;
2976 if (*it ==
'+' || *it ==
'-')
2978 auto plus = *it ==
'+';
2981 hoff = eat.eat_digits(2);
2982 dt.hour_offset = (plus) ? hoff : -hoff;
2984 moff = eat.eat_digits(2);
2985 dt.minute_offset = (plus) ? moff : -moff;
2987 else if (*it ==
'Z')
2993 throw_parse_exception(
"Malformed date");
2995 return make_value(dt);
2998 std::shared_ptr<base> parse_array(std::string::iterator& it,
2999 std::string::iterator& end)
3010 skip_whitespace_and_comments(it, end);
3016 return make_array();
3019 auto val_end = std::find_if(
3020 it, end, [](
char c) {
return c ==
',' || c ==
']' || c ==
'#'; });
3021 parse_type type = determine_value_type(it, val_end);
3024 case parse_type::STRING:
3025 return parse_value_array<std::string>(it, end);
3026 case parse_type::LOCAL_TIME:
3027 return parse_value_array<local_time>(it, end);
3028 case parse_type::LOCAL_DATE:
3029 return parse_value_array<local_date>(it, end);
3030 case parse_type::LOCAL_DATETIME:
3031 return parse_value_array<local_datetime>(it, end);
3032 case parse_type::OFFSET_DATETIME:
3033 return parse_value_array<offset_datetime>(it, end);
3034 case parse_type::INT:
3035 return parse_value_array<int64_t>(it, end);
3036 case parse_type::FLOAT:
3037 return parse_value_array<double>(it, end);
3038 case parse_type::BOOL:
3039 return parse_value_array<bool>(it, end);
3040 case parse_type::ARRAY:
3041 return parse_object_array<array>(&parser::parse_array,
'[', it,
3043 case parse_type::INLINE_TABLE:
3044 return parse_object_array<table_array>(
3045 &parser::parse_inline_table,
'{', it, end);
3047 throw_parse_exception(
"Unable to parse array");
3051 template <
class Value>
3052 std::shared_ptr<array> parse_value_array(std::string::iterator& it,
3053 std::string::iterator& end)
3055 auto arr = make_array();
3056 while (it != end && *it !=
']')
3058 auto val = parse_value(it, end);
3059 if (
auto v = val->as<Value>())
3060 arr->get().push_back(val);
3062 throw_parse_exception(
"Arrays must be homogeneous");
3063 skip_whitespace_and_comments(it, end);
3067 skip_whitespace_and_comments(it, end);
3074 template <
class Object,
class Function>
3075 std::shared_ptr<Object> parse_object_array(Function&& fun,
char delim,
3076 std::string::iterator& it,
3077 std::string::iterator& end)
3079 auto arr = detail::make_element<Object>();
3081 while (it != end && *it !=
']')
3084 throw_parse_exception(
"Unexpected character in array");
3086 arr->get().push_back(((*this).*fun)(it, end));
3087 skip_whitespace_and_comments(it, end);
3089 if (it == end || *it !=
',')
3093 skip_whitespace_and_comments(it, end);
3096 if (it == end || *it !=
']')
3097 throw_parse_exception(
"Unterminated array");
3103 std::shared_ptr<table> parse_inline_table(std::string::iterator& it,
3104 std::string::iterator& end)
3106 auto tbl = make_table();
3111 throw_parse_exception(
"Unterminated inline table");
3113 consume_whitespace(it, end);
3114 if (it != end && *it !=
'}')
3116 parse_key_value(it, end, tbl.get());
3117 consume_whitespace(it, end);
3119 }
while (*it ==
',');
3121 if (it == end || *it !=
'}')
3122 throw_parse_exception(
"Unterminated inline table");
3125 consume_whitespace(it, end);
3130 void skip_whitespace_and_comments(std::string::iterator& start,
3131 std::string::iterator& end)
3133 consume_whitespace(start, end);
3134 while (start == end || *start ==
'#')
3136 if (!detail::getline(input_, line_))
3137 throw_parse_exception(
"Unclosed array");
3139 start = line_.begin();
3141 consume_whitespace(start, end);
3145 void consume_whitespace(std::string::iterator& it,
3146 const std::string::iterator& end)
3148 while (it != end && (*it ==
' ' || *it ==
'\t'))
3152 void consume_backwards_whitespace(std::string::iterator& back,
3153 const std::string::iterator& front)
3155 while (back != front && (*back ==
' ' || *back ==
'\t'))
3159 void eol_or_comment(
const std::string::iterator& it,
3160 const std::string::iterator& end)
3162 if (it != end && *it !=
'#')
3163 throw_parse_exception(
"Unidentified trailing character '" 3165 +
"'---did you forget a '#'?");
3168 bool is_time(
const std::string::iterator& it,
3169 const std::string::iterator& end)
3171 auto time_end = find_end_of_time(it, end);
3172 auto len = std::distance(it, time_end);
3177 if (it[2] !=
':' || it[5] !=
':')
3181 return it[8] ==
'.' && len > 9;
3187 const std::string::iterator& end)
3189 auto date_end = find_end_of_date(it, end);
3190 auto len = std::distance(it, date_end);
3195 if (it[4] !=
'-' || it[7] !=
'-')
3198 if (len >= 19 && (it[10] ==
'T' || it[10] ==
' ')
3199 && is_time(it + 11, date_end))
3202 auto time_end = find_end_of_time(it + 11, date_end);
3203 if (time_end == date_end)
3204 return {parse_type::LOCAL_DATETIME};
3206 return {parse_type::OFFSET_DATETIME};
3211 return {parse_type::LOCAL_DATE};
3217 std::istream& input_;
3219 std::size_t line_number_ = 0;
3226 inline std::shared_ptr<table>
parse_file(
const std::string& filename)
3228 #if defined(BOOST_NOWIDE_FSTREAM_INCLUDED_HPP) 3229 boost::nowide::ifstream file{filename.c_str()};
3230 #elif defined(NOWIDE_FSTREAM_INCLUDED_HPP) 3231 nowide::ifstream file{filename.c_str()};
3233 std::ifstream file{filename};
3235 if (!file.is_open())
3241 template <
class... Ts>
3247 template <
class Visitor,
class... Args>
3248 static void accept(
const base&, Visitor&&, Args&&...)
3254 template <
class T,
class... Ts>
3257 template <
class Visitor,
class... Args>
3258 static void accept(
const base& b, Visitor&& visitor, Args&&... args)
3260 if (
auto v = b.
as<T>())
3262 visitor.visit(*v, std::forward<Args>(args)...);
3267 std::forward<Args>(args)...);
3276 template <
class Visitor,
class... Args>
3281 using value_acceptor
3282 =
value_accept<std::string, int64_t, double, bool, local_date,
3283 local_time, local_datetime, offset_datetime>;
3284 value_acceptor::accept(*
this, std::forward<Visitor>(visitor),
3285 std::forward<Args>(args)...);
3287 else if (is_table())
3289 visitor.visit(static_cast<const table&>(*
this),
3290 std::forward<Args>(args)...);
3292 else if (is_array())
3294 visitor.visit(static_cast<const array&>(*
this),
3295 std::forward<Args>(args)...);
3297 else if (is_table_array())
3299 visitor.visit(static_cast<const table_array&>(*
this),
3300 std::forward<Args>(args)...);
3315 : stream_(s), indent_(indent_space), has_naked_endline_(false)
3333 void visit(
const table& t,
bool in_array =
false)
3335 write_table_header(in_array);
3336 std::vector<std::string> values;
3337 std::vector<std::string> tables;
3339 for (
const auto& i : t)
3341 if (i.second->is_table() || i.second->is_table_array())
3343 tables.push_back(i.first);
3347 values.push_back(i.first);
3351 for (
unsigned int i = 0; i < values.size(); ++i)
3353 path_.push_back(values[i]);
3358 write_table_item_header(*t.get(values[i]));
3359 t.get(values[i])->accept(*
this,
false);
3363 for (
unsigned int i = 0; i < tables.size(); ++i)
3365 path_.push_back(tables[i]);
3367 if (values.size() > 0 || i > 0)
3370 write_table_item_header(*t.get(tables[i]));
3371 t.get(tables[i])->accept(*
this,
false);
3381 void visit(
const array& a,
bool =
false)
3385 for (
unsigned int i = 0; i < a.
get().size(); ++i)
3390 if (a.
get()[i]->is_array())
3392 a.
get()[i]->as_array()->accept(*
this,
true);
3396 a.
get()[i]->accept(*
this,
true);
3406 void visit(
const table_array& t,
bool =
false)
3408 for (
unsigned int j = 0; j < t.get().size(); ++j)
3413 t.get()[j]->accept(*
this,
true);
3425 for (
auto it = str.begin(); it != str.end(); ++it)
3431 else if (*it ==
'\t')
3435 else if (*it ==
'\n')
3439 else if (*it ==
'\f')
3443 else if (*it ==
'\r')
3447 else if (*it ==
'"')
3451 else if (*it ==
'\\')
3455 else if (static_cast<uint32_t>(*it) <= UINT32_C(0x001f))
3458 std::stringstream ss;
3459 ss << std::hex << static_cast<uint32_t>(*it);
3477 write(escape_string(v.
get()));
3486 std::stringstream ss;
3487 ss << std::showpoint
3488 << std::setprecision(std::numeric_limits<double>::max_digits10)
3491 auto double_str = ss.str();
3492 auto pos = double_str.find(
"e0");
3493 if (pos != std::string::npos)
3494 double_str.replace(pos, 2,
"e");
3495 pos = double_str.find(
"e-0");
3496 if (pos != std::string::npos)
3497 double_str.replace(pos, 3,
"e-");
3499 stream_ << double_str;
3500 has_naked_endline_ =
false;
3508 typename std::enable_if<
3509 is_one_of<T, int64_t, local_date, local_time, local_datetime,
3510 offset_datetime>
::value>::type
3521 write((v.
get() ?
"true" :
"false"));
3540 for (
unsigned int i = 0; i < path_.size(); ++i)
3547 if (path_[i].find_first_not_of(
"ABCDEFGHIJKLMNOPQRSTUVWXYZabcde" 3548 "fghijklmnopqrstuvwxyz0123456789" 3550 == std::string::npos)
3557 write(escape_string(path_[i]));
3581 if (path_.back().find_first_not_of(
"ABCDEFGHIJKLMNOPQRSTUVWXYZabcde" 3582 "fghijklmnopqrstuvwxyz0123456789" 3584 == std::string::npos)
3586 write(path_.back());
3591 write(escape_string(path_.back()));
3606 for (std::size_t i = 1; i < path_.size(); ++i)
3614 void write(
const T& v)
3617 has_naked_endline_ =
false;
3625 if (!has_naked_endline_)
3628 has_naked_endline_ =
true;
3633 std::ostream& stream_;
3634 const std::string indent_;
3635 std::vector<std::string> path_;
3636 bool has_naked_endline_;
3639 inline std::ostream& operator<<(std::ostream& stream,
const base& b)
3647 std::ostream& operator<<(std::ostream& stream, const value<T>& v)
3654 inline std::ostream& operator<<(std::ostream& stream,
const table& t)
3661 inline std::ostream& operator<<(std::ostream& stream,
const table_array& t)
3668 inline std::ostream& operator<<(std::ostream& stream,
const array& a)
std::shared_ptr< base > get_qualified(const std::string &key) const
Obtains the base for a given key.
Definition: cpptoml.h:1366
Definition: cpptoml.h:774
iterator insert(iterator position, const std::shared_ptr< value< T >> &value)
Insert a value into the array.
Definition: cpptoml.h:938
void insert(const std::string &key, const std::shared_ptr< base > &value)
Adds an element to the keytable.
Definition: cpptoml.h:1537
std::shared_ptr< value< T > > as()
Attempts to coerce the TOML element into a concrete TOML value of type T.
Definition: cpptoml.h:680
iterator insert(iterator position, const std::shared_ptr< array > &value)
Insert an array into the array.
Definition: cpptoml.h:953
void reserve(size_type n)
Reserve space for n tables.
Definition: cpptoml.h:1157
void push_back(const std::shared_ptr< value< T >> &val)
Add a value to the end of the array.
Definition: cpptoml.h:897
array_of_trait< T >::return_type get_qualified_array_of(const std::string &key) const
Helper function that attempts to get an array of values of a given type corresponding to the template...
Definition: cpptoml.h:1514
std::shared_ptr< table_array > as_table_array()
Converts the TOML element into a table array.
Definition: cpptoml.h:559
std::shared_ptr< array > get_array_qualified(const std::string &key) const
Obtains an array for a given key.
Definition: cpptoml.h:1407
Definition: cpptoml.h:288
virtual bool is_table_array() const override
Determines if the given TOML element is an array of tables.
Definition: cpptoml.h:1107
std::enable_if< is_one_of< T, int64_t, local_date, local_time, local_datetime, offset_datetime >::value >::type write(const value< T > &v)
Write out an integer, local_date, local_time, local_datetime, or offset_datetime. ...
Definition: cpptoml.h:3511
void visit(const value< T > &v, bool=false)
Output a base value of the TOML tree.
Definition: cpptoml.h:3325
Definition: cpptoml.h:382
void write(const value< std::string > &v)
Write out a string.
Definition: cpptoml.h:3474
void visit(const table &t, bool in_array=false)
Output a table element of the TOML tree.
Definition: cpptoml.h:3333
std::vector< std::shared_ptr< table >>::const_iterator const_iterator
arrays can be iterated over.
Definition: cpptoml.h:1085
static std::string escape_string(const std::string &str)
Escape a string for output.
Definition: cpptoml.h:3422
iterator erase(iterator position)
Erase an element from the array.
Definition: cpptoml.h:1141
std::shared_ptr< base > get(const std::string &key) const
Obtains the base for a given key.
Definition: cpptoml.h:1354
A generic base TOML value used for type erasure.
Definition: cpptoml.h:498
Definition: stringutils.h:172
std::vector< std::shared_ptr< base >>::const_iterator const_iterator
arrays can be iterated over.
Definition: cpptoml.h:796
bool contains(const std::string &key) const
Determines if this key table contains the given key.
Definition: cpptoml.h:1335
std::shared_ptr< array > as_array()
Converts the TOML element to an array.
Definition: cpptoml.h:541
virtual bool is_table() const
Determines if the given TOML element is a table.
Definition: cpptoml.h:516
void clear()
Clear the array.
Definition: cpptoml.h:986
std::shared_ptr< table_array > get_table_array(const std::string &key) const
Obtains a table_array for a given key, if possible.
Definition: cpptoml.h:1417
Definition: cpptoml.h:291
Writer that can be passed to accept() functions of cpptoml objects and will output valid TOML to a st...
Definition: cpptoml.h:3308
virtual bool is_array() const override
Determines if the TOML element is an array of "leaf" elements.
Definition: cpptoml.h:781
T & get()
Gets the data associated with this value.
Definition: cpptoml.h:637
std::vector< std::shared_ptr< table >>::iterator iterator
arrays can be iterated over
Definition: cpptoml.h:1080
virtual bool is_array() const
Determines if the TOML element is an array of "leaf" elements.
Definition: cpptoml.h:533
string_to_base_map::const_iterator const_iterator
tables can be iterated over.
Definition: cpptoml.h:1300
Definition: cpptoml.h:100
void write(const value< double > &v)
Write out a double.
Definition: cpptoml.h:3484
Helper object for consuming expected characters.
Definition: cpptoml.h:1775
Exception class for all TOML parsing errors.
Definition: cpptoml.h:1748
The parser class.
Definition: cpptoml.h:1875
Definition: cpptoml.h:107
std::shared_ptr< table > get_table_qualified(const std::string &key) const
Obtains a table for a given key, if possible.
Definition: cpptoml.h:1387
Represents a TOML keytable.
Definition: cpptoml.h:1284
std::shared_ptr< table > parse()
Parses the stream this parser was created on until EOF.
Definition: cpptoml.h:1892
virtual bool is_value() const
Determines if the given TOML element is a value.
Definition: cpptoml.h:508
Definition: cpptoml.h:1067
void push_back(T &&val, typename value_traits< T >::type *=0)
Convenience function for adding a simple element to the end of the array.
Definition: cpptoml.h:929
parser(std::istream &stream)
Parsers are constructed from streams.
Definition: cpptoml.h:1881
std::vector< std::shared_ptr< base >>::iterator iterator
arrays can be iterated over
Definition: cpptoml.h:791
bool is_inline() const
Whether or not the table array is declared inline.
Definition: cpptoml.h:1167
void write(const value< bool > &v)
Write out a boolean.
Definition: cpptoml.h:3519
std::shared_ptr< array > get_array(const std::string &key) const
Obtains an array for a given key.
Definition: cpptoml.h:1397
std::shared_ptr< table > get_table(const std::string &key) const
Obtains a table for a given key, if possible.
Definition: cpptoml.h:1376
std::shared_ptr< table_array > get_table_array_qualified(const std::string &key) const
Obtains a table_array for a given key, if possible.
Definition: cpptoml.h:1429
void visit(const table_array &t, bool=false)
Output a table_array element of the TOML tree.
Definition: cpptoml.h:3406
void reserve(size_type n)
Reserve space for n values.
Definition: cpptoml.h:994
Definition: cpptoml.h:121
void accept(Visitor &&visitor, Args &&... args) const
base implementation of accept() that calls visitor.visit() on the concrete class. ...
Definition: cpptoml.h:3277
void erase(const std::string &key)
Removes an element from the table.
Definition: cpptoml.h:1556
Definition: cpptoml.h:281
array_of_trait< T >::return_type get_array_of() const
Obtains a option<vector<T>>.
Definition: cpptoml.h:859
bool contains_qualified(const std::string &key) const
Determines if this key table contains the given key.
Definition: cpptoml.h:1345
iterator erase(iterator position)
Erase an element from the array.
Definition: cpptoml.h:978
toml_writer(std::ostream &s, const std::string &indent_space="\)
Construct a toml_writer that will write to the given stream.
Definition: cpptoml.h:3314
Definition: cpptoml.h:263
void insert(const std::string &key, T &&val, typename value_traits< T >::type *=0)
Convenience shorthand for adding a simple element to the keytable.
Definition: cpptoml.h:1547
iterator insert(iterator position, const std::shared_ptr< table > &value)
Insert a table into the array.
Definition: cpptoml.h:1133
array_of_trait< T >::return_type get_array_of(const std::string &key) const
Helper function that attempts to get an array of values of a given type corresponding to the template...
Definition: cpptoml.h:1482
Definition: cpptoml.h:168
Exception class for array insertion errors.
Definition: cpptoml.h:766
bool is_table() const override
Determines if the given TOML element is a table.
Definition: cpptoml.h:1322
void visit(const array &a, bool=false)
Output an array element of the TOML tree.
Definition: cpptoml.h:3381
string_to_base_map::iterator iterator
tables can be iterated over.
Definition: cpptoml.h:1295
void write_table_header(bool in_array=false)
Write out the header of a table.
Definition: cpptoml.h:3527
std::vector< std::shared_ptr< base > > & get()
Obtains the array (vector) of base values.
Definition: cpptoml.h:821
void push_back(const std::shared_ptr< array > &val)
Add an array to the end of the array.
Definition: cpptoml.h:912
Definition: cpptoml.h:115
Definition: cpptoml.h:125
std::shared_ptr< table > parse_file(const std::string &filename)
Utility function to parse a file as a TOML file.
Definition: cpptoml.h:3226
Definition: cpptoml.h:3242
void write_table_item_header(const base &b)
Write out the identifier for an item in a table.
Definition: cpptoml.h:3575
iterator insert(iterator position, T &&val, typename value_traits< T >::type *=0)
Convenience function for inserting a simple element in the array.
Definition: cpptoml.h:969
std::vector< std::shared_ptr< array > > nested_array() const
Obtains an array of arrays.
Definition: cpptoml.h:879
option< T > get_qualified_as(const std::string &key) const
Helper function that attempts to get a value corresponding to the template parameter from a given key...
Definition: cpptoml.h:1459
A concrete TOML value representing the "leaves" of the "tree".
Definition: cpptoml.h:278
virtual bool is_table_array() const
Determines if the given TOML element is an array of tables.
Definition: cpptoml.h:551
void clear()
Clear the array.
Definition: cpptoml.h:1149
std::vector< std::shared_ptr< value< T > > > array_of() const
Obtains an array of value<T>s.
Definition: cpptoml.h:844
std::shared_ptr< table > as_table()
Converts the TOML element into a table.
Definition: cpptoml.h:524
option< T > get_as(const std::string &key) const
Helper function that attempts to get a value corresponding to the template parameter from a given key...
Definition: cpptoml.h:1441
void push_back(const std::shared_ptr< table > &val)
Add a table to the end of the array.
Definition: cpptoml.h:1125
bool is_value() const override
Determines if the given TOML element is a value.
Definition: cpptoml.h:629