23 #include <unordered_map>
26 #if __cplusplus > 201103L
27 #define CPPTOML_DEPRECATED(reason) [[deprecated(reason)]]
28 #elif defined(__clang__)
29 #define CPPTOML_DEPRECATED(reason) __attribute__((deprecated(reason)))
30 #elif defined(__GNUG__)
31 #define CPPTOML_DEPRECATED(reason) __attribute__((deprecated))
32 #elif defined(_MSC_VER)
34 #define CPPTOML_DEPRECATED(reason) __declspec(deprecated)
36 #define CPPTOML_DEPRECATED(reason) [[deprecated(reason)]]
44 #if defined(CPPTOML_USE_MAP)
47 using string_to_base_map = std::map<std::string, std::shared_ptr<base>>;
51 using string_to_base_map
52 = std::unordered_map<std::string, std::shared_ptr<base>>;
73 explicit operator bool()
const
78 const T& operator*()
const
83 const T* operator->()
const
89 T value_or(U&& alternative)
const
93 return static_cast<T
>(std::forward<U>(alternative));
119 int minute_offset = 0;
131 dt.year = t.tm_year + 1900;
132 dt.month = t.tm_mon + 1;
135 dt.minute = t.tm_min;
136 dt.second = t.tm_sec;
139 strftime(buf, 16,
"%z", &t);
141 int offset = std::stoi(buf);
142 dt.hour_offset = offset / 100;
143 dt.minute_offset = offset % 100;
147 CPPTOML_DEPRECATED(
"from_local has been renamed to from_zoned")
150 return from_zoned(t);
156 dt.year = t.tm_year + 1900;
157 dt.month = t.tm_mon + 1;
160 dt.minute = t.tm_min;
161 dt.second = t.tm_sec;
166 CPPTOML_DEPRECATED(
"datetime has been renamed to offset_datetime")
172 fill_guard(std::ostream& os) : os_(os), fill_{os.fill()}
184 std::ostream::char_type fill_;
187 inline std::ostream& operator<<(std::ostream& os,
const local_date& dt)
193 os << setw(4) << dt.year <<
"-" << setw(2) << dt.month <<
"-" << setw(2)
199 inline std::ostream& operator<<(std::ostream& os,
const local_time& ltime)
205 os << setw(2) << ltime.hour <<
":" << setw(2) << ltime.minute <<
":"
206 << setw(2) << ltime.second;
208 if (ltime.microsecond > 0)
212 for (
int curr_us = ltime.microsecond; curr_us; power /= 10)
214 auto num = curr_us / power;
216 curr_us -= num * power;
223 inline std::ostream& operator<<(std::ostream& os,
const zone_offset& zo)
230 if (zo.hour_offset != 0 || zo.minute_offset != 0)
232 if (zo.hour_offset > 0)
240 os << setw(2) << std::abs(zo.hour_offset) <<
":" << setw(2)
241 << std::abs(zo.minute_offset);
251 inline std::ostream& operator<<(std::ostream& os,
const local_datetime& dt)
253 return os << static_cast<const local_date&>(dt) <<
"T"
254 <<
static_cast<const local_time&
>(dt);
257 inline std::ostream& operator<<(std::ostream& os,
const offset_datetime& dt)
259 return os << static_cast<const local_datetime&>(dt)
260 <<
static_cast<const zone_offset&
>(dt);
263 template <
class T,
class... Ts>
266 template <
class T,
class V>
271 template <
class T,
class V,
class... Ts>
274 const static bool value
283 :
is_one_of<T, std::string, int64_t, double, bool, local_date, local_time,
284 local_datetime, offset_datetime>
288 template <
class T,
class Enable =
void>
296 || std::is_convertible<T, std::string>::value;
301 valid_value_or_string_convertible<T>::value>
::type>
303 using value_type =
typename std::conditional<
305 typename std::decay<T>::type, std::string>
::type;
309 static value_type construct(T&& val)
311 return value_type(val);
318 typename std::enable_if<
319 !valid_value_or_string_convertible<T>::value
320 && std::is_floating_point<typename std::decay<T>::type>::value>
::type>
322 using value_type =
typename std::decay<T>::type;
326 static value_type construct(T&& val)
328 return value_type(val);
334 T, typename std::enable_if<
335 !valid_value_or_string_convertible<T>::value
336 && !std::is_floating_point<typename std::decay<T>::type>::value
337 && std::is_signed<typename std::decay<T>::type>::value>
::type>
339 using value_type = int64_t;
343 static value_type construct(T&& val)
345 if (val < (std::numeric_limits<int64_t>::min)())
346 throw std::underflow_error{
"constructed value cannot be "
347 "represented by a 64-bit signed "
350 if (val > (std::numeric_limits<int64_t>::max)())
351 throw std::overflow_error{
"constructed value cannot be represented "
352 "by a 64-bit signed integer"};
354 return static_cast<int64_t
>(val);
360 T, typename std::enable_if<
361 !valid_value_or_string_convertible<T>::value
362 && std::is_unsigned<typename std::decay<T>::type>::value>
::type>
364 using value_type = int64_t;
368 static value_type construct(T&& val)
370 if (val >
static_cast<uint64_t
>((std::numeric_limits<int64_t>::max)()))
371 throw std::overflow_error{
"constructed value cannot be represented "
372 "by a 64-bit signed integer"};
374 return static_cast<int64_t
>(val);
395 inline std::shared_ptr<typename value_traits<T>::type> make_value(T&& val);
396 inline std::shared_ptr<array> make_array();
401 inline std::shared_ptr<T> make_element();
404 inline std::shared_ptr<table> make_table();
405 inline std::shared_ptr<table_array> make_table_array(
bool is_inline =
false);
407 #if defined(CPPTOML_NO_RTTI)
427 struct base_type_traits;
430 struct base_type_traits<std::string>
432 static const base_type type = base_type::STRING;
436 struct base_type_traits<local_time>
438 static const base_type type = base_type::LOCAL_TIME;
442 struct base_type_traits<local_date>
444 static const base_type type = base_type::LOCAL_DATE;
448 struct base_type_traits<local_datetime>
450 static const base_type type = base_type::LOCAL_DATETIME;
454 struct base_type_traits<offset_datetime>
456 static const base_type type = base_type::OFFSET_DATETIME;
460 struct base_type_traits<int64_t>
462 static const base_type type = base_type::INT;
466 struct base_type_traits<double>
468 static const base_type type = base_type::FLOAT;
472 struct base_type_traits<bool>
474 static const base_type type = base_type::BOOL;
478 struct base_type_traits<table>
480 static const base_type type = base_type::TABLE;
484 struct base_type_traits<array>
486 static const base_type type = base_type::ARRAY;
490 struct base_type_traits<table_array>
492 static const base_type type = base_type::TABLE_ARRAY;
499 class base :
public std::enable_shared_from_this<base>
502 virtual ~
base() =
default;
504 virtual std::shared_ptr<base> clone()
const = 0;
528 return std::static_pointer_cast<table>(shared_from_this());
545 return std::static_pointer_cast<array>(shared_from_this());
563 return std::static_pointer_cast<table_array>(shared_from_this());
572 std::shared_ptr<value<T>>
as();
575 std::shared_ptr<const value<T>>
as()
const;
577 template <
class Visitor,
class... Args>
578 void accept(Visitor&& visitor, Args&&... args)
const;
580 #if defined(CPPTOML_NO_RTTI)
581 base_type type()
const
587 base(
const base_type t) : type_(t)
593 const base_type type_ = base_type::NONE;
616 friend std::shared_ptr<typename value_traits<U>::type>
617 cpptoml::make_value(U&& val);
622 std::shared_ptr<base> clone()
const override;
657 #if defined(CPPTOML_NO_RTTI)
658 value(
const T& val) :
base(base_type_traits<T>::type), data_(val)
672 std::shared_ptr<typename value_traits<T>::type> make_value(T&& val)
674 using value_type =
typename value_traits<T>::type;
675 using enabler =
typename value_type::make_shared_enabler;
676 return std::make_shared<value_type>(
677 enabler{}, value_traits<T>::construct(std::forward<T>(val)));
683 #if defined(CPPTOML_NO_RTTI)
684 if (type() == base_type_traits<T>::type)
685 return std::static_pointer_cast<value<T>>(shared_from_this());
689 return std::dynamic_pointer_cast<value<T>>(shared_from_this());
696 inline std::shared_ptr<value<double>>
base::as()
698 #if defined(CPPTOML_NO_RTTI)
699 if (type() == base_type::FLOAT)
700 return std::static_pointer_cast<value<double>>(shared_from_this());
702 if (type() == base_type::INT)
704 auto v = std::static_pointer_cast<value<int64_t>>(shared_from_this());
705 return make_value<double>(
static_cast<double>(v->get()));
708 if (
auto v = std::dynamic_pointer_cast<value<double>>(shared_from_this()))
711 if (
auto v = std::dynamic_pointer_cast<value<int64_t>>(shared_from_this()))
712 return make_value<double>(
static_cast<double>(v->get()));
719 inline std::shared_ptr<const value<T>>
base::as()
const
721 #if defined(CPPTOML_NO_RTTI)
722 if (type() == base_type_traits<T>::type)
723 return std::static_pointer_cast<const value<T>>(shared_from_this());
727 return std::dynamic_pointer_cast<const value<T>>(shared_from_this());
734 inline std::shared_ptr<const value<double>>
base::as()
const
736 #if defined(CPPTOML_NO_RTTI)
737 if (type() == base_type::FLOAT)
738 return std::static_pointer_cast<const value<double>>(
741 if (type() == base_type::INT)
743 auto v = as<int64_t>();
746 return make_value<double>(
static_cast<double>(v->get()));
750 = std::dynamic_pointer_cast<
const value<double>>(shared_from_this()))
753 if (
auto v = as<int64_t>())
757 return make_value<double>(
static_cast<double>(v->get()));
778 friend std::shared_ptr<array> make_array();
780 std::shared_ptr<base> clone()
const override;
787 using size_type = std::size_t;
801 return values_.begin();
806 return values_.begin();
811 return values_.end();
816 return values_.end();
822 std::vector<std::shared_ptr<base>>&
get()
830 const std::vector<std::shared_ptr<base>>&
get()
const
835 std::shared_ptr<base> at(
size_t idx)
const
837 return values_.at(idx);
845 std::vector<std::shared_ptr<value<T>>>
array_of()
const
847 std::vector<std::shared_ptr<value<T>>> result(values_.size());
849 std::transform(values_.begin(), values_.end(), result.begin(),
850 [&](std::shared_ptr<base> v) { return v->as<T>(); });
862 std::vector<T> result;
863 result.reserve(values_.size());
865 for (
const auto& val : values_)
867 if (
auto v = val->as<T>())
868 result.push_back(v->get());
873 return {std::move(result)};
882 std::vector<std::shared_ptr<array>> result(values_.size());
884 std::transform(values_.begin(), values_.end(), result.begin(),
885 [&](std::shared_ptr<base> v) -> std::shared_ptr<array> {
887 return std::static_pointer_cast<array>(v);
888 return std::shared_ptr<array>{};
900 if (values_.empty() || values_[0]->as<T>())
902 values_.push_back(val);
915 if (values_.empty() || values_[0]->is_array())
917 values_.push_back(val);
932 push_back(make_value(std::forward<T>(val)));
941 if (values_.empty() || values_[0]->as<T>())
943 return values_.insert(position,
value);
956 if (values_.empty() || values_[0]->is_array())
958 return values_.insert(position,
value);
973 return insert(position, make_value(std::forward<T>(val)));
981 return values_.erase(position);
1001 #if defined(CPPTOML_NO_RTTI)
1010 template <
class InputIterator>
1011 array(InputIterator begin, InputIterator end) : values_{begin, end}
1016 array(
const array& obj) =
delete;
1017 array& operator=(
const array& obj) =
delete;
1019 std::vector<std::shared_ptr<base>> values_;
1022 inline std::shared_ptr<array> make_array()
1024 struct make_shared_enabler :
public array
1026 make_shared_enabler()
1032 return std::make_shared<make_shared_enabler>();
1038 inline std::shared_ptr<array> make_element<array>()
1040 return make_array();
1050 array::get_array_of<array>()
const
1052 std::vector<std::shared_ptr<array>> result;
1053 result.reserve(values_.size());
1055 for (
const auto& val : values_)
1057 if (
auto v = val->as_array())
1058 result.push_back(v);
1063 return {std::move(result)};
1071 friend std::shared_ptr<table_array> make_table_array(
bool);
1074 std::shared_ptr<base> clone()
const override;
1076 using size_type = std::size_t;
1090 return array_.begin();
1093 const_iterator begin()
const
1095 return array_.begin();
1100 return array_.end();
1103 const_iterator end()
const
1105 return array_.end();
1113 std::vector<std::shared_ptr<table>>& get()
1118 const std::vector<std::shared_ptr<table>>& get()
const
1128 array_.push_back(val);
1136 return array_.insert(position,
value);
1144 return array_.erase(position);
1174 #if defined(CPPTOML_NO_RTTI)
1176 :
base(base_type::TABLE_ARRAY), is_inline_(is_inline)
1181 table_array(
bool is_inline =
false) : is_inline_(is_inline)
1187 table_array(
const table_array& obj) =
delete;
1188 table_array& operator=(
const table_array& rhs) =
delete;
1190 std::vector<std::shared_ptr<table>> array_;
1191 const bool is_inline_ =
false;
1194 inline std::shared_ptr<table_array> make_table_array(
bool is_inline)
1196 struct make_shared_enabler :
public table_array
1198 make_shared_enabler(
bool mse_is_inline) : table_array(mse_is_inline)
1204 return std::make_shared<make_shared_enabler>(is_inline);
1210 inline std::shared_ptr<table_array> make_element<table_array>()
1212 return make_table_array(
true);
1220 typename std::enable_if<!std::is_floating_point<T>::value
1221 && std::is_signed<T>::value,
1223 get_impl(
const std::shared_ptr<base>& elem)
1225 if (
auto v = elem->as<int64_t>())
1227 if (v->get() < (std::numeric_limits<T>::min)())
1228 throw std::underflow_error{
1229 "T cannot represent the value requested in get"};
1231 if (v->get() > (std::numeric_limits<T>::max)())
1232 throw std::overflow_error{
1233 "T cannot represent the value requested in get"};
1235 return {
static_cast<T
>(v->get())};
1244 typename std::enable_if<!std::is_same<T, bool>::value
1245 && std::is_unsigned<T>::value,
1247 get_impl(
const std::shared_ptr<base>& elem)
1249 if (
auto v = elem->as<int64_t>())
1252 throw std::underflow_error{
"T cannot store negative value in get"};
1254 if (
static_cast<uint64_t
>(v->get()) > (std::numeric_limits<T>::max)())
1255 throw std::overflow_error{
1256 "T cannot represent the value requested in get"};
1258 return {
static_cast<T
>(v->get())};
1267 typename std::enable_if<!std::is_integral<T>::value
1268 || std::is_same<T, bool>::value,
1270 get_impl(
const std::shared_ptr<base>& elem)
1272 if (
auto v = elem->as<T>())
1289 friend std::shared_ptr<table> make_table();
1291 std::shared_ptr<base> clone()
const override;
1305 return map_.begin();
1308 const_iterator begin()
const
1310 return map_.begin();
1318 const_iterator end()
const
1330 return map_.empty();
1338 return map_.find(key) != map_.end();
1348 return resolve_qualified(key);
1355 std::shared_ptr<base>
get(
const std::string& key)
const
1357 return map_.at(key);
1369 std::shared_ptr<base> p;
1370 resolve_qualified(key, &p);
1377 std::shared_ptr<table>
get_table(
const std::string& key)
const
1379 if (contains(key) && get(key)->is_table())
1380 return std::static_pointer_cast<table>(get(key));
1390 if (contains_qualified(key) && get_qualified(key)->is_table())
1391 return std::static_pointer_cast<table>(get_qualified(key));
1398 std::shared_ptr<array>
get_array(
const std::string& key)
const
1402 return get(key)->as_array();
1410 if (!contains_qualified(key))
1412 return get_qualified(key)->as_array();
1422 return get(key)->as_table_array();
1429 std::shared_ptr<table_array>
1432 if (!contains_qualified(key))
1434 return get_qualified(key)->as_table_array();
1446 return get_impl<T>(get(key));
1448 catch (
const std::out_of_range&)
1464 return get_impl<T>(get_qualified(key));
1466 catch (
const std::out_of_range&)
1485 if (
auto v = get_array(key))
1487 std::vector<T> result;
1488 result.reserve(v->get().size());
1490 for (
const auto& b : v->get())
1492 if (
auto val = b->as<T>())
1493 result.push_back(val->get());
1497 return {std::move(result)};
1517 if (
auto v = get_array_qualified(key))
1519 std::vector<T> result;
1520 result.reserve(v->get().size());
1522 for (
const auto& b : v->get())
1524 if (
auto val = b->as<T>())
1525 result.push_back(val->get());
1529 return {std::move(result)};
1538 void insert(
const std::string& key,
const std::shared_ptr<base>&
value)
1551 insert(key, make_value(std::forward<T>(val)));
1563 #if defined(CPPTOML_NO_RTTI)
1575 table(
const table& obj) =
delete;
1576 table& operator=(
const table& rhs) =
delete;
1578 std::vector<std::string> split(
const std::string& value,
1579 char separator)
const
1581 std::vector<std::string> result;
1582 std::string::size_type p = 0;
1583 std::string::size_type q;
1584 while ((q = value.find(separator, p)) != std::string::npos)
1586 result.emplace_back(value, p, q - p);
1589 result.emplace_back(value, p);
1598 bool resolve_qualified(
const std::string& key,
1599 std::shared_ptr<base>* p =
nullptr)
const
1601 auto parts = split(key,
'.');
1602 auto last_key = parts.back();
1605 auto cur_table =
this;
1606 for (
const auto& part : parts)
1608 cur_table = cur_table->get_table(part).get();
1614 throw std::out_of_range{key +
" is not a valid key"};
1619 return cur_table->map_.count(last_key) != 0;
1621 *p = cur_table->map_.at(last_key);
1625 string_to_base_map map_;
1639 table::get_array_of<array>(
const std::string& key)
const
1641 if (
auto v = get_array(key))
1643 std::vector<std::shared_ptr<array>> result;
1644 result.reserve(v->get().size());
1646 for (
const auto& b : v->get())
1648 if (
auto val = b->as_array())
1649 result.push_back(val);
1654 return {std::move(result)};
1671 table::get_qualified_array_of<array>(
const std::string& key)
const
1673 if (
auto v = get_array_qualified(key))
1675 std::vector<std::shared_ptr<array>> result;
1676 result.reserve(v->get().size());
1678 for (
const auto& b : v->get())
1680 if (
auto val = b->as_array())
1681 result.push_back(val);
1686 return {std::move(result)};
1692 std::shared_ptr<table> make_table()
1694 struct make_shared_enabler :
public table
1696 make_shared_enabler()
1702 return std::make_shared<make_shared_enabler>();
1708 inline std::shared_ptr<table> make_element<table>()
1710 return make_table();
1715 std::shared_ptr<base> value<T>::clone()
const
1717 return make_value(data_);
1720 inline std::shared_ptr<base> array::clone()
const
1722 auto result = make_array();
1723 result->reserve(values_.size());
1724 for (
const auto& ptr : values_)
1725 result->values_.push_back(ptr->clone());
1729 inline std::shared_ptr<base> table_array::clone()
const
1731 auto result = make_table_array(is_inline());
1732 result->reserve(array_.size());
1733 for (
const auto& ptr : array_)
1734 result->array_.push_back(ptr->clone()->as_table());
1738 inline std::shared_ptr<base> table::clone()
const
1740 auto result = make_table();
1741 for (
const auto& pr : map_)
1742 result->insert(pr.first, pr.second->clone());
1757 : std::runtime_error{err +
" at line " + std::to_string(line_number)}
1762 inline bool is_number(
char c)
1764 return c >=
'0' && c <=
'9';
1767 inline bool is_hex(
char c)
1769 return is_number(c) || (c >=
'a' && c <=
'f') || (c >=
'A' && c <=
'F');
1775 template <
class OnError>
1779 consumer(std::string::iterator& it,
const std::string::iterator& end,
1781 : it_(it), end_(end), on_error_(std::forward<OnError>(on_error))
1786 void operator()(
char c)
1788 if (it_ == end_ || *it_ != c)
1793 template <std::
size_t N>
1794 void operator()(
const char (&str)[N])
1796 std::for_each(std::begin(str), std::end(str) - 1,
1797 [&](
char c) { (*this)(c); });
1800 void eat_or(
char a,
char b)
1802 if (it_ == end_ || (*it_ != a && *it_ != b))
1807 int eat_digits(
int len)
1810 for (
int i = 0; i < len; ++i)
1812 if (!is_number(*it_) || it_ == end_)
1814 val = 10 * val + (*it_++ -
'0');
1825 std::string::iterator& it_;
1826 const std::string::iterator& end_;
1830 template <
class OnError>
1832 const std::string::iterator& end,
1842 inline std::istream& getline(std::istream& input, std::string& line)
1846 std::istream::sentry sentry{input,
true};
1847 auto sb = input.rdbuf();
1851 auto c = sb->sbumpc();
1854 if (sb->sgetc() ==
'\n')
1861 if (c == std::istream::traits_type::eof())
1864 input.setstate(std::ios::eofbit);
1868 line.push_back(
static_cast<char>(c));
1882 parser(std::istream& stream) : input_(stream)
1895 std::shared_ptr<table> root = make_table();
1899 while (detail::getline(input_, line_))
1902 auto it = line_.begin();
1903 auto end = line_.end();
1904 consume_whitespace(it, end);
1905 if (it == end || *it ==
'#')
1909 curr_table = root.
get();
1910 parse_table(it, end, curr_table);
1914 parse_key_value(it, end, curr_table);
1915 consume_whitespace(it, end);
1916 eol_or_comment(it, end);
1923 #if defined _MSC_VER
1924 __declspec(noreturn)
1925 #elif defined __GNUC__
1926 __attribute__((noreturn))
1928 void throw_parse_exception(
const std::string& err)
1933 void parse_table(std::string::iterator& it,
1934 const std::string::iterator& end, table*& curr_table)
1939 throw_parse_exception(
"Unexpected end of table");
1941 parse_table_array(it, end, curr_table);
1943 parse_single_table(it, end, curr_table);
1946 void parse_single_table(std::string::iterator& it,
1947 const std::string::iterator& end,
1950 if (it == end || *it ==
']')
1951 throw_parse_exception(
"Table name cannot be empty");
1953 std::string full_table_name;
1954 bool inserted =
false;
1956 auto key_end = [](
char c) {
return c ==
']'; };
1958 auto key_part_handler = [&](
const std::string& part) {
1960 throw_parse_exception(
"Empty component of table name");
1962 if (!full_table_name.empty())
1963 full_table_name +=
'.';
1964 full_table_name += part;
1966 if (curr_table->contains(part))
1969 auto b = curr_table->get(part);
1972 std::shared_ptr<base> b = curr_table->get(part);
1975 curr_table =
static_cast<table*
>(b.get());
1976 else if (b->is_table_array())
1977 curr_table = std::static_pointer_cast<table_array>(b)
1982 throw_parse_exception(
"Key " + full_table_name
1983 +
"already exists as a value");
1988 curr_table->insert(part, make_table());
1989 curr_table =
static_cast<table*
>(curr_table->get(part).get());
1993 key_part_handler(parse_key(it, end, key_end, key_part_handler));
1996 throw_parse_exception(
1997 "Unterminated table declaration; did you forget a ']'?");
2001 std::string errmsg{
"Unexpected character in table definition: "};
2005 throw_parse_exception(errmsg);
2012 = [](
const std::pair<
const std::string&,
2013 const std::shared_ptr<base>&>& p) {
2014 return p.second->is_value();
2021 if (curr_table->empty()
2022 || std::any_of(curr_table->begin(), curr_table->end(),
2025 throw_parse_exception(
"Redefinition of table "
2031 consume_whitespace(it, end);
2032 eol_or_comment(it, end);
2035 void parse_table_array(std::string::iterator& it,
2036 const std::string::iterator& end, table*& curr_table)
2039 if (it == end || *it ==
']')
2040 throw_parse_exception(
"Table array name cannot be empty");
2042 auto key_end = [](
char c) {
return c ==
']'; };
2044 std::string full_ta_name;
2045 auto key_part_handler = [&](
const std::string& part) {
2047 throw_parse_exception(
"Empty component of table array name");
2049 if (!full_ta_name.empty())
2050 full_ta_name +=
'.';
2051 full_ta_name += part;
2053 if (curr_table->contains(part))
2056 auto b = curr_table->get(part);
2059 std::shared_ptr<base> b = curr_table->get(part);
2065 if (it != end && *it ==
']')
2067 if (!b->is_table_array())
2069 throw_parse_exception(
"Key " + full_ta_name
2070 +
" is not a table array");
2073 auto v = b->as_table_array();
2077 throw_parse_exception(
"Static array " + full_ta_name
2078 +
" cannot be appended to");
2081 v->get().push_back(make_table());
2082 curr_table = v->get().back().get();
2088 curr_table =
static_cast<table*
>(b.get());
2089 else if (b->is_table_array())
2090 curr_table = std::static_pointer_cast<table_array>(b)
2095 throw_parse_exception(
"Key " + full_ta_name
2096 +
" already exists as a value");
2104 if (it != end && *it ==
']')
2106 curr_table->insert(part, make_table_array());
2107 auto arr = std::static_pointer_cast<table_array>(
2108 curr_table->get(part));
2109 arr->get().push_back(make_table());
2110 curr_table = arr->get().back().get();
2116 curr_table->insert(part, make_table());
2118 =
static_cast<table*
>(curr_table->get(part).get());
2123 key_part_handler(parse_key(it, end, key_end, key_part_handler));
2126 auto eat = make_consumer(it, end, [
this]() {
2127 throw_parse_exception(
"Unterminated table array name");
2132 consume_whitespace(it, end);
2133 eol_or_comment(it, end);
2136 void parse_key_value(std::string::iterator& it, std::string::iterator& end,
2139 auto key_end = [](
char c) {
return c ==
'='; };
2141 auto key_part_handler = [&](
const std::string& part) {
2145 if (curr_table->contains(part))
2147 auto val = curr_table->get(part);
2148 if (val->is_table())
2150 curr_table =
static_cast<table*
>(val.get());
2154 throw_parse_exception(
"Key " + part
2155 +
" already exists as a value");
2160 auto newtable = make_table();
2161 curr_table->insert(part, newtable);
2162 curr_table = newtable.get();
2166 auto key = parse_key(it, end, key_end, key_part_handler);
2168 if (curr_table->contains(key))
2169 throw_parse_exception(
"Key " + key +
" already present");
2170 if (it == end || *it !=
'=')
2171 throw_parse_exception(
"Value must follow after a '='");
2173 consume_whitespace(it, end);
2174 curr_table->insert(key, parse_value(it, end));
2175 consume_whitespace(it, end);
2178 template <
class KeyEndFinder,
class KeyPartHandler>
2180 parse_key(std::string::iterator& it,
const std::string::iterator& end,
2181 KeyEndFinder&& key_end, KeyPartHandler&& key_part_handler)
2184 while (it != end && !key_end(*it))
2186 auto part = parse_simple_key(it, end);
2187 consume_whitespace(it, end);
2189 if (it == end || key_end(*it))
2196 std::string errmsg{
"Unexpected character in key: "};
2200 throw_parse_exception(errmsg);
2203 key_part_handler(part);
2209 throw_parse_exception(
"Unexpected end of key");
2212 std::string parse_simple_key(std::string::iterator& it,
2213 const std::string::iterator& end)
2215 consume_whitespace(it, end);
2218 throw_parse_exception(
"Unexpected end of key (blank key?)");
2220 if (*it ==
'"' || *it ==
'\'')
2222 return string_literal(it, end, *it);
2226 auto bke = std::find_if(it, end, [](
char c) {
2227 return c ==
'.' || c ==
'=' || c ==
']';
2229 return parse_bare_key(it, bke);
2233 std::string parse_bare_key(std::string::iterator& it,
2234 const std::string::iterator& end)
2238 throw_parse_exception(
"Bare key missing name");
2243 consume_backwards_whitespace(key_end, it);
2245 std::string key{it, key_end};
2247 if (std::find(it, key_end,
'#') != key_end)
2249 throw_parse_exception(
"Bare key " + key +
" cannot contain #");
2252 if (std::find_if(it, key_end,
2253 [](
char c) {
return c ==
' ' || c ==
'\t'; })
2256 throw_parse_exception(
"Bare key " + key
2257 +
" cannot contain whitespace");
2260 if (std::find_if(it, key_end,
2261 [](
char c) {
return c ==
'[' || c ==
']'; })
2264 throw_parse_exception(
"Bare key " + key
2265 +
" cannot contain '[' or ']'");
2272 enum class parse_type
2286 std::shared_ptr<base> parse_value(std::string::iterator& it,
2287 std::string::iterator& end)
2289 parse_type type = determine_value_type(it, end);
2292 case parse_type::STRING:
2293 return parse_string(it, end);
2294 case parse_type::LOCAL_TIME:
2295 return parse_time(it, end);
2296 case parse_type::LOCAL_DATE:
2297 case parse_type::LOCAL_DATETIME:
2298 case parse_type::OFFSET_DATETIME:
2299 return parse_date(it, end);
2300 case parse_type::INT:
2301 case parse_type::FLOAT:
2302 return parse_number(it, end);
2303 case parse_type::BOOL:
2304 return parse_bool(it, end);
2305 case parse_type::ARRAY:
2306 return parse_array(it, end);
2307 case parse_type::INLINE_TABLE:
2308 return parse_inline_table(it, end);
2310 throw_parse_exception(
"Failed to parse value");
2314 parse_type determine_value_type(
const std::string::iterator& it,
2315 const std::string::iterator& end)
2319 throw_parse_exception(
"Failed to parse value type");
2321 if (*it ==
'"' || *it ==
'\'')
2323 return parse_type::STRING;
2325 else if (is_time(it, end))
2327 return parse_type::LOCAL_TIME;
2329 else if (
auto dtype = date_type(it, end))
2333 else if (is_number(*it) || *it ==
'-' || *it ==
'+'
2334 || (*it ==
'i' && it + 1 != end && it[1] ==
'n'
2335 && it + 2 != end && it[2] ==
'f')
2336 || (*it ==
'n' && it + 1 != end && it[1] ==
'a'
2337 && it + 2 != end && it[2] ==
'n'))
2339 return determine_number_type(it, end);
2341 else if (*it ==
't' || *it ==
'f')
2343 return parse_type::BOOL;
2345 else if (*it ==
'[')
2347 return parse_type::ARRAY;
2349 else if (*it ==
'{')
2351 return parse_type::INLINE_TABLE;
2353 throw_parse_exception(
"Failed to parse value type");
2356 parse_type determine_number_type(
const std::string::iterator& it,
2357 const std::string::iterator& end)
2361 if (*check_it ==
'-' || *check_it ==
'+')
2364 if (check_it == end)
2365 throw_parse_exception(
"Malformed number");
2367 if (*check_it ==
'i' || *check_it ==
'n')
2368 return parse_type::FLOAT;
2370 while (check_it != end && is_number(*check_it))
2372 if (check_it != end && *check_it ==
'.')
2375 while (check_it != end && is_number(*check_it))
2377 return parse_type::FLOAT;
2381 return parse_type::INT;
2385 std::shared_ptr<value<std::string>> parse_string(std::string::iterator& it,
2386 std::string::iterator& end)
2389 assert(delim ==
'"' || delim ==
'\'');
2395 if (check_it != end && *check_it == delim)
2398 if (check_it != end && *check_it == delim)
2401 return parse_multiline_string(it, end, delim);
2404 return make_value<std::string>(string_literal(it, end, delim));
2407 std::shared_ptr<value<std::string>>
2408 parse_multiline_string(std::string::iterator& it,
2409 std::string::iterator& end,
char delim)
2411 std::stringstream ss;
2413 auto is_ws = [](
char c) {
return c ==
' ' || c ==
'\t'; };
2415 bool consuming =
false;
2416 std::shared_ptr<value<std::string>> ret;
2418 auto handle_line = [&](std::string::iterator& local_it,
2419 std::string::iterator& local_end) {
2422 local_it = std::find_if_not(local_it, local_end, is_ws);
2425 if (local_it == local_end)
2431 while (local_it != local_end)
2434 if (delim ==
'"' && *local_it ==
'\\')
2436 auto check = local_it;
2440 consume_whitespace(check, local_end);
2441 if (check == local_end)
2447 ss << parse_escape_code(local_it, local_end);
2452 if (std::distance(local_it, local_end) >= 3)
2454 auto check = local_it;
2456 if (*check++ == delim && *check++ == delim
2457 && *check++ == delim)
2460 ret = make_value<std::string>(ss.str());
2470 handle_line(it, end);
2475 while (detail::getline(input_, line_))
2482 handle_line(it, end);
2491 throw_parse_exception(
"Unterminated multi-line basic string");
2494 std::string string_literal(std::string::iterator& it,
2495 const std::string::iterator& end,
char delim)
2502 if (delim ==
'"' && *it ==
'\\')
2504 val += parse_escape_code(it, end);
2506 else if (*it == delim)
2509 consume_whitespace(it, end);
2517 throw_parse_exception(
"Unterminated string literal");
2520 std::string parse_escape_code(std::string::iterator& it,
2521 const std::string::iterator& end)
2525 throw_parse_exception(
"Invalid escape sequence");
2531 else if (*it ==
't')
2535 else if (*it ==
'n')
2539 else if (*it ==
'f')
2543 else if (*it ==
'r')
2547 else if (*it ==
'"')
2551 else if (*it ==
'\\')
2555 else if (*it ==
'u' || *it ==
'U')
2557 return parse_unicode(it, end);
2561 throw_parse_exception(
"Invalid escape sequence");
2564 return std::string(1, value);
2567 std::string parse_unicode(std::string::iterator& it,
2568 const std::string::iterator& end)
2570 bool large = *it++ ==
'U';
2571 auto codepoint = parse_hex(it, end, large ? 0x10000000 : 0x1000);
2573 if ((codepoint > 0xd7ff && codepoint < 0xe000) || codepoint > 0x10ffff)
2575 throw_parse_exception(
2576 "Unicode escape sequence is not a Unicode scalar value");
2581 if (codepoint <= 0x7f)
2585 result +=
static_cast<char>(codepoint & 0x7f);
2587 else if (codepoint <= 0x7ff)
2595 result +=
static_cast<char>(0xc0 | ((codepoint >> 6) & 0x1f));
2600 result +=
static_cast<char>(0x80 | (codepoint & 0x3f));
2602 else if (codepoint <= 0xffff)
2610 result +=
static_cast<char>(0xe0 | ((codepoint >> 12) & 0x0f));
2611 result +=
static_cast<char>(0x80 | ((codepoint >> 6) & 0x1f));
2612 result +=
static_cast<char>(0x80 | (codepoint & 0x3f));
2622 result +=
static_cast<char>(0xf0 | ((codepoint >> 18) & 0x07));
2623 result +=
static_cast<char>(0x80 | ((codepoint >> 12) & 0x3f));
2624 result +=
static_cast<char>(0x80 | ((codepoint >> 6) & 0x3f));
2625 result +=
static_cast<char>(0x80 | (codepoint & 0x3f));
2630 uint32_t parse_hex(std::string::iterator& it,
2631 const std::string::iterator& end, uint32_t place)
2637 throw_parse_exception(
"Unexpected end of unicode sequence");
2640 throw_parse_exception(
"Invalid unicode escape sequence");
2642 value += place * hex_to_digit(*it++);
2648 uint32_t hex_to_digit(
char c)
2651 return static_cast<uint32_t
>(c -
'0');
2653 +
static_cast<uint32_t
>(c
2654 - ((c >=
'a' && c <=
'f') ?
'a' :
'A'));
2657 std::shared_ptr<base> parse_number(std::string::iterator& it,
2658 const std::string::iterator& end)
2661 auto check_end = find_end_of_number(it, end);
2663 auto eat_sign = [&]() {
2664 if (check_it != end && (*check_it ==
'-' || *check_it ==
'+'))
2668 auto check_no_leading_zero = [&]() {
2669 if (check_it != end && *check_it ==
'0' && check_it + 1 != check_end
2670 && check_it[1] !=
'.')
2672 throw_parse_exception(
"Numbers may not have leading zeros");
2676 auto eat_digits = [&](bool (*check_char)(char)) {
2677 auto beg = check_it;
2678 while (check_it != end && check_char(*check_it))
2681 if (check_it != end && *check_it ==
'_')
2684 if (check_it == end || !check_char(*check_it))
2685 throw_parse_exception(
"Malformed number");
2689 if (check_it == beg)
2690 throw_parse_exception(
"Malformed number");
2693 auto eat_hex = [&]() { eat_digits(&is_hex); };
2695 auto eat_numbers = [&]() { eat_digits(&is_number); };
2697 if (check_it != end && *check_it ==
'0' && check_it + 1 != check_end
2698 && (check_it[1] ==
'x' || check_it[1] ==
'o' || check_it[1] ==
'b'))
2701 char base = *check_it;
2706 return parse_int(it, check_it, 16);
2708 else if (base ==
'o')
2710 auto start = check_it;
2712 auto val = parse_int(start, check_it, 8,
"0");
2718 auto start = check_it;
2720 auto val = parse_int(start, check_it, 2);
2727 check_no_leading_zero();
2729 if (check_it != end && check_it + 1 != end && check_it + 2 != end)
2731 if (check_it[0] ==
'i' && check_it[1] ==
'n' && check_it[2] ==
'f')
2733 auto val = std::numeric_limits<double>::infinity();
2737 return make_value(val);
2739 else if (check_it[0] ==
'n' && check_it[1] ==
'a'
2740 && check_it[2] ==
'n')
2742 auto val = std::numeric_limits<double>::quiet_NaN();
2746 return make_value(val);
2753 && (*check_it ==
'.' || *check_it ==
'e' || *check_it ==
'E'))
2755 bool is_exp = *check_it ==
'e' || *check_it ==
'E';
2758 if (check_it == end)
2759 throw_parse_exception(
"Floats must have trailing digits");
2761 auto eat_exp = [&]() {
2763 check_no_leading_zero();
2772 if (!is_exp && check_it != end
2773 && (*check_it ==
'e' || *check_it ==
'E'))
2779 return parse_float(it, check_it);
2783 return parse_int(it, check_it);
2787 std::shared_ptr<value<int64_t>> parse_int(std::string::iterator& it,
2788 const std::string::iterator& end,
2790 const char* prefix =
"")
2792 std::string v{it, end};
2794 v.erase(std::remove(v.begin(), v.end(),
'_'), v.end());
2798 return make_value<int64_t>(std::stoll(v,
nullptr, base));
2800 catch (
const std::invalid_argument& ex)
2802 throw_parse_exception(
"Malformed number (invalid argument: "
2803 + std::string{ex.what()} +
")");
2805 catch (
const std::out_of_range& ex)
2807 throw_parse_exception(
"Malformed number (out of range: "
2808 + std::string{ex.what()} +
")");
2812 std::shared_ptr<value<double>> parse_float(std::string::iterator& it,
2813 const std::string::iterator& end)
2815 std::string v{it, end};
2816 v.erase(std::remove(v.begin(), v.end(),
'_'), v.end());
2818 char decimal_point = std::localeconv()->decimal_point[0];
2819 std::replace(v.begin(), v.end(),
'.', decimal_point);
2822 return make_value<double>(std::stod(v));
2824 catch (
const std::invalid_argument& ex)
2826 throw_parse_exception(
"Malformed number (invalid argument: "
2827 + std::string{ex.what()} +
")");
2829 catch (
const std::out_of_range& ex)
2831 throw_parse_exception(
"Malformed number (out of range: "
2832 + std::string{ex.what()} +
")");
2836 std::shared_ptr<value<bool>> parse_bool(std::string::iterator& it,
2837 const std::string::iterator& end)
2839 auto eat = make_consumer(it, end, [
this]() {
2840 throw_parse_exception(
"Attempted to parse invalid boolean value");
2846 return make_value<bool>(
true);
2848 else if (*it ==
'f')
2851 return make_value<bool>(
false);
2858 std::string::iterator find_end_of_number(std::string::iterator it,
2859 std::string::iterator end)
2861 auto ret = std::find_if(it, end, [](
char c) {
2862 return !is_number(c) && c !=
'_' && c !=
'.' && c !=
'e' && c !=
'E'
2863 && c !=
'-' && c !=
'+' && c !=
'x' && c !=
'o' && c !=
'b';
2865 if (ret != end && ret + 1 != end && ret + 2 != end)
2867 if ((ret[0] ==
'i' && ret[1] ==
'n' && ret[2] ==
'f')
2868 || (ret[0] ==
'n' && ret[1] ==
'a' && ret[2] ==
'n'))
2876 std::string::iterator find_end_of_date(std::string::iterator it,
2877 std::string::iterator end)
2879 auto end_of_date = std::find_if(it, end, [](
char c) {
2880 return !is_number(c) && c !=
'-';
2882 if (end_of_date != end && *end_of_date ==
' ' && end_of_date + 1 != end
2883 && is_number(end_of_date[1]))
2885 return std::find_if(end_of_date, end, [](
char c) {
2886 return !is_number(c) && c !=
'T' && c !=
'Z' && c !=
':'
2887 && c !=
'-' && c !=
'+' && c !=
'.';
2891 std::string::iterator find_end_of_time(std::string::iterator it,
2892 std::string::iterator end)
2894 return std::find_if(it, end, [](
char c) {
2895 return !is_number(c) && c !=
':' && c !=
'.';
2899 local_time read_time(std::string::iterator& it,
2900 const std::string::iterator& end)
2902 auto time_end = find_end_of_time(it, end);
2904 auto eat = make_consumer(
2905 it, time_end, [&]() { throw_parse_exception(
"Malformed time"); });
2909 ltime.hour = eat.eat_digits(2);
2911 ltime.minute = eat.eat_digits(2);
2913 ltime.second = eat.eat_digits(2);
2916 if (it != time_end && *it ==
'.')
2919 while (it != time_end && is_number(*it))
2921 ltime.microsecond += power * (*it++ -
'0');
2927 throw_parse_exception(
"Malformed time");
2932 std::shared_ptr<value<local_time>>
2933 parse_time(std::string::iterator& it,
const std::string::iterator& end)
2935 return make_value(read_time(it, end));
2938 std::shared_ptr<base> parse_date(std::string::iterator& it,
2939 const std::string::iterator& end)
2941 auto date_end = find_end_of_date(it, end);
2943 auto eat = make_consumer(
2944 it, date_end, [&]() { throw_parse_exception(
"Malformed date"); });
2947 ldate.year = eat.eat_digits(4);
2949 ldate.month = eat.eat_digits(2);
2951 ldate.day = eat.eat_digits(2);
2954 return make_value(ldate);
2956 eat.eat_or(
'T',
' ');
2959 static_cast<local_date&
>(ldt) = ldate;
2960 static_cast<local_time&
>(ldt) = read_time(it, date_end);
2963 return make_value(ldt);
2966 static_cast<local_datetime&
>(dt) = ldt;
2970 if (*it ==
'+' || *it ==
'-')
2972 auto plus = *it ==
'+';
2975 hoff = eat.eat_digits(2);
2976 dt.hour_offset = (plus) ? hoff : -hoff;
2978 moff = eat.eat_digits(2);
2979 dt.minute_offset = (plus) ? moff : -moff;
2981 else if (*it ==
'Z')
2987 throw_parse_exception(
"Malformed date");
2989 return make_value(dt);
2992 std::shared_ptr<base> parse_array(std::string::iterator& it,
2993 std::string::iterator& end)
3004 skip_whitespace_and_comments(it, end);
3010 return make_array();
3013 auto val_end = std::find_if(
3014 it, end, [](
char c) {
return c ==
',' || c ==
']' || c ==
'#'; });
3015 parse_type type = determine_value_type(it, val_end);
3018 case parse_type::STRING:
3019 return parse_value_array<std::string>(it, end);
3020 case parse_type::LOCAL_TIME:
3021 return parse_value_array<local_time>(it, end);
3022 case parse_type::LOCAL_DATE:
3023 return parse_value_array<local_date>(it, end);
3024 case parse_type::LOCAL_DATETIME:
3025 return parse_value_array<local_datetime>(it, end);
3026 case parse_type::OFFSET_DATETIME:
3027 return parse_value_array<offset_datetime>(it, end);
3028 case parse_type::INT:
3029 return parse_value_array<int64_t>(it, end);
3030 case parse_type::FLOAT:
3031 return parse_value_array<double>(it, end);
3032 case parse_type::BOOL:
3033 return parse_value_array<bool>(it, end);
3034 case parse_type::ARRAY:
3035 return parse_object_array<array>(&parser::parse_array,
'[', it,
3037 case parse_type::INLINE_TABLE:
3038 return parse_object_array<table_array>(
3039 &parser::parse_inline_table,
'{', it, end);
3041 throw_parse_exception(
"Unable to parse array");
3045 template <
class Value>
3046 std::shared_ptr<array> parse_value_array(std::string::iterator& it,
3047 std::string::iterator& end)
3049 auto arr = make_array();
3050 while (it != end && *it !=
']')
3052 auto val = parse_value(it, end);
3053 if (
auto v = val->as<Value>())
3054 arr->get().push_back(val);
3056 throw_parse_exception(
"Arrays must be homogeneous");
3057 skip_whitespace_and_comments(it, end);
3061 skip_whitespace_and_comments(it, end);
3068 template <
class Object,
class Function>
3069 std::shared_ptr<Object> parse_object_array(Function&& fun,
char delim,
3070 std::string::iterator& it,
3071 std::string::iterator& end)
3073 auto arr = detail::make_element<Object>();
3075 while (it != end && *it !=
']')
3078 throw_parse_exception(
"Unexpected character in array");
3080 arr->get().push_back(((*this).*fun)(it, end));
3081 skip_whitespace_and_comments(it, end);
3083 if (it == end || *it !=
',')
3087 skip_whitespace_and_comments(it, end);
3090 if (it == end || *it !=
']')
3091 throw_parse_exception(
"Unterminated array");
3097 std::shared_ptr<table> parse_inline_table(std::string::iterator& it,
3098 std::string::iterator& end)
3100 auto tbl = make_table();
3105 throw_parse_exception(
"Unterminated inline table");
3107 consume_whitespace(it, end);
3108 if (it != end && *it !=
'}')
3110 parse_key_value(it, end, tbl.get());
3111 consume_whitespace(it, end);
3113 }
while (*it ==
',');
3115 if (it == end || *it !=
'}')
3116 throw_parse_exception(
"Unterminated inline table");
3119 consume_whitespace(it, end);
3124 void skip_whitespace_and_comments(std::string::iterator& start,
3125 std::string::iterator& end)
3127 consume_whitespace(start, end);
3128 while (start == end || *start ==
'#')
3130 if (!detail::getline(input_, line_))
3131 throw_parse_exception(
"Unclosed array");
3133 start = line_.begin();
3135 consume_whitespace(start, end);
3139 void consume_whitespace(std::string::iterator& it,
3140 const std::string::iterator& end)
3142 while (it != end && (*it ==
' ' || *it ==
'\t'))
3146 void consume_backwards_whitespace(std::string::iterator& back,
3147 const std::string::iterator& front)
3149 while (back != front && (*back ==
' ' || *back ==
'\t'))
3153 void eol_or_comment(
const std::string::iterator& it,
3154 const std::string::iterator& end)
3156 if (it != end && *it !=
'#')
3157 throw_parse_exception(
"Unidentified trailing character '"
3159 +
"'---did you forget a '#'?");
3162 bool is_time(
const std::string::iterator& it,
3163 const std::string::iterator& end)
3165 auto time_end = find_end_of_time(it, end);
3166 auto len = std::distance(it, time_end);
3171 if (it[2] !=
':' || it[5] !=
':')
3175 return it[8] ==
'.' && len > 9;
3180 option<parse_type> date_type(
const std::string::iterator& it,
3181 const std::string::iterator& end)
3183 auto date_end = find_end_of_date(it, end);
3184 auto len = std::distance(it, date_end);
3189 if (it[4] !=
'-' || it[7] !=
'-')
3192 if (len >= 19 && (it[10] ==
'T' || it[10] ==
' ')
3193 && is_time(it + 11, date_end))
3196 auto time_end = find_end_of_time(it + 11, date_end);
3197 if (time_end == date_end)
3198 return {parse_type::LOCAL_DATETIME};
3200 return {parse_type::OFFSET_DATETIME};
3205 return {parse_type::LOCAL_DATE};
3211 std::istream& input_;
3213 std::size_t line_number_ = 0;
3220 inline std::shared_ptr<table>
parse_file(
const std::string& filename)
3222 #if defined(BOOST_NOWIDE_FSTREAM_INCLUDED_HPP)
3223 boost::nowide::ifstream file{filename.c_str()};
3224 #elif defined(NOWIDE_FSTREAM_INCLUDED_HPP)
3225 nowide::ifstream file{filename.c_str()};
3227 std::ifstream file{filename};
3229 if (!file.is_open())
3235 template <
class... Ts>
3241 template <
class Visitor,
class... Args>
3242 static void accept(
const base&, Visitor&&, Args&&...)
3248 template <
class T,
class... Ts>
3251 template <
class Visitor,
class... Args>
3252 static void accept(
const base& b, Visitor&& visitor, Args&&... args)
3254 if (
auto v = b.
as<T>())
3256 visitor.visit(*v, std::forward<Args>(args)...);
3261 std::forward<Args>(args)...);
3270 template <
class Visitor,
class... Args>
3271 void base::accept(Visitor&& visitor, Args&&... args)
const
3275 using value_acceptor
3278 value_acceptor::accept(*
this, std::forward<Visitor>(visitor),
3279 std::forward<Args>(args)...);
3281 else if (is_table())
3283 visitor.visit(
static_cast<const table&
>(*
this),
3284 std::forward<Args>(args)...);
3286 else if (is_array())
3288 visitor.visit(
static_cast<const array&
>(*
this),
3289 std::forward<Args>(args)...);
3291 else if (is_table_array())
3293 visitor.visit(
static_cast<const table_array&
>(*
this),
3294 std::forward<Args>(args)...);
3309 : stream_(s), indent_(indent_space), has_naked_endline_(false)
3329 write_table_header(in_array);
3330 std::vector<std::string> values;
3331 std::vector<std::string> tables;
3333 for (
const auto& i : t)
3335 if (i.second->is_table() || i.second->is_table_array())
3337 tables.push_back(i.first);
3341 values.push_back(i.first);
3345 for (
unsigned int i = 0; i < values.size(); ++i)
3347 path_.push_back(values[i]);
3352 write_table_item_header(*t.
get(values[i]));
3353 t.
get(values[i])->accept(*
this,
false);
3357 for (
unsigned int i = 0; i < tables.size(); ++i)
3359 path_.push_back(tables[i]);
3361 if (values.size() > 0 || i > 0)
3364 write_table_item_header(*t.
get(tables[i]));
3365 t.
get(tables[i])->accept(*
this,
false);
3379 for (
unsigned int i = 0; i < a.
get().size(); ++i)
3384 if (a.
get()[i]->is_array())
3386 a.
get()[i]->as_array()->accept(*
this,
true);
3390 a.
get()[i]->accept(*
this,
true);
3402 for (
unsigned int j = 0; j < t.get().size(); ++j)
3407 t.get()[j]->accept(*
this,
true);
3419 for (
auto it = str.begin(); it != str.end(); ++it)
3425 else if (*it ==
'\t')
3429 else if (*it ==
'\n')
3433 else if (*it ==
'\f')
3437 else if (*it ==
'\r')
3441 else if (*it ==
'"')
3445 else if (*it ==
'\\')
3449 else if (
static_cast<uint32_t
>(*it) <= UINT32_C(0x001f))
3452 std::stringstream ss;
3453 ss << std::hex << static_cast<uint32_t>(*it);
3471 write(escape_string(v.
get()));
3480 std::stringstream ss;
3481 ss << std::showpoint
3482 << std::setprecision(std::numeric_limits<double>::max_digits10)
3485 auto double_str = ss.str();
3486 auto pos = double_str.find(
"e0");
3487 if (pos != std::string::npos)
3488 double_str.replace(pos, 2,
"e");
3489 pos = double_str.find(
"e-0");
3490 if (pos != std::string::npos)
3491 double_str.replace(pos, 3,
"e-");
3493 stream_ << double_str;
3494 has_naked_endline_ =
false;
3502 typename std::enable_if<
3515 write((v.
get() ?
"true" :
"false"));
3534 for (
unsigned int i = 0; i < path_.size(); ++i)
3541 if (path_[i].find_first_not_of(
"ABCDEFGHIJKLMNOPQRSTUVWXYZabcde"
3542 "fghijklmnopqrstuvwxyz0123456789"
3544 == std::string::npos)
3551 write(escape_string(path_[i]));
3575 if (path_.back().find_first_not_of(
"ABCDEFGHIJKLMNOPQRSTUVWXYZabcde"
3576 "fghijklmnopqrstuvwxyz0123456789"
3578 == std::string::npos)
3580 write(path_.back());
3585 write(escape_string(path_.back()));
3600 for (std::size_t i = 1; i < path_.size(); ++i)
3611 has_naked_endline_ =
false;
3619 if (!has_naked_endline_)
3622 has_naked_endline_ =
true;
3627 std::ostream& stream_;
3628 const std::string indent_;
3629 std::vector<std::string> path_;
3630 bool has_naked_endline_;
3633 inline std::ostream& operator<<(std::ostream& stream,
const base& b)
3635 toml_writer writer{stream};
3641 std::ostream& operator<<(std::ostream& stream,
const value<T>& v)
3643 toml_writer writer{stream};
3648 inline std::ostream& operator<<(std::ostream& stream,
const table& t)
3650 toml_writer writer{stream};
3655 inline std::ostream& operator<<(std::ostream& stream,
const table_array& t)
3657 toml_writer writer{stream};
3662 inline std::ostream& operator<<(std::ostream& stream,
const array& a)
3664 toml_writer writer{stream};
Exception class for array insertion errors.
Definition: cpptoml.h:768
Definition: cpptoml.h:776
iterator insert(iterator position, const std::shared_ptr< array > &value)
Insert an array into the array.
Definition: cpptoml.h:954
void reserve(size_type n)
Reserve space for n values.
Definition: cpptoml.h:995
std::vector< std::shared_ptr< value< T > > > array_of() const
Obtains an array of value<T>s.
Definition: cpptoml.h:845
virtual bool is_array() const override
Determines if the TOML element is an array of "leaf" elements.
Definition: cpptoml.h:782
iterator erase(iterator position)
Erase an element from the array.
Definition: cpptoml.h:979
std::vector< std::shared_ptr< base > > & get()
Obtains the array (vector) of base values.
Definition: cpptoml.h:822
array_of_trait< T >::return_type get_array_of() const
Obtains a option<vector<T>>.
Definition: cpptoml.h:860
void push_back(const std::shared_ptr< value< T >> &val)
Add a value to the end of the array.
Definition: cpptoml.h:898
void clear()
Clear the array.
Definition: cpptoml.h:987
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:970
const std::vector< std::shared_ptr< base > > & get() const
Obtains the array (vector) of base values.
Definition: cpptoml.h:830
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:930
std::vector< std::shared_ptr< base > >::const_iterator const_iterator
arrays can be iterated over.
Definition: cpptoml.h:797
void push_back(const std::shared_ptr< array > &val)
Add an array to the end of the array.
Definition: cpptoml.h:913
std::vector< std::shared_ptr< array > > nested_array() const
Obtains an array of arrays.
Definition: cpptoml.h:880
std::vector< std::shared_ptr< base > >::iterator iterator
arrays can be iterated over
Definition: cpptoml.h:792
iterator insert(iterator position, const std::shared_ptr< value< T >> &value)
Insert a value into the array.
Definition: cpptoml.h:939
A generic base TOML value used for type erasure.
Definition: cpptoml.h:500
virtual bool is_value() const
Determines if the given TOML element is a value.
Definition: cpptoml.h:509
virtual bool is_table_array() const
Determines if the given TOML element is an array of tables.
Definition: cpptoml.h:552
void accept(Visitor &&visitor, Args &&... args) const
base implementation of accept() that calls visitor.visit() on the concrete class.
Definition: cpptoml.h:3271
std::shared_ptr< table > as_table()
Converts the TOML element into a table.
Definition: cpptoml.h:525
std::shared_ptr< array > as_array()
Converts the TOML element to an array.
Definition: cpptoml.h:542
std::shared_ptr< table_array > as_table_array()
Converts the TOML element into a table array.
Definition: cpptoml.h:560
virtual bool is_array() const
Determines if the TOML element is an array of "leaf" elements.
Definition: cpptoml.h:534
std::shared_ptr< value< T > > as()
Attempts to coerce the TOML element into a concrete TOML value of type T.
Definition: cpptoml.h:681
virtual bool is_table() const
Determines if the given TOML element is a table.
Definition: cpptoml.h:517
Helper object for consuming expected characters.
Definition: cpptoml.h:1777
Definition: cpptoml.h:170
Exception class for all TOML parsing errors.
Definition: cpptoml.h:1750
The parser class.
Definition: cpptoml.h:1877
parser(std::istream &stream)
Parsers are constructed from streams.
Definition: cpptoml.h:1882
std::shared_ptr< table > parse()
Parses the stream this parser was created on until EOF.
Definition: cpptoml.h:1893
Definition: cpptoml.h:1069
bool is_inline() const
Whether or not the table array is declared inline.
Definition: cpptoml.h:1168
std::vector< std::shared_ptr< table > >::iterator iterator
arrays can be iterated over
Definition: cpptoml.h:1081
void push_back(const std::shared_ptr< table > &val)
Add a table to the end of the array.
Definition: cpptoml.h:1126
iterator erase(iterator position)
Erase an element from the array.
Definition: cpptoml.h:1142
void reserve(size_type n)
Reserve space for n tables.
Definition: cpptoml.h:1158
iterator insert(iterator position, const std::shared_ptr< table > &value)
Insert a table into the array.
Definition: cpptoml.h:1134
std::vector< std::shared_ptr< table > >::const_iterator const_iterator
arrays can be iterated over.
Definition: cpptoml.h:1086
void clear()
Clear the array.
Definition: cpptoml.h:1150
virtual bool is_table_array() const override
Determines if the given TOML element is an array of tables.
Definition: cpptoml.h:1108
Represents a TOML keytable.
Definition: cpptoml.h:1286
std::shared_ptr< array > get_array_qualified(const std::string &key) const
Obtains an array for a given key.
Definition: cpptoml.h:1408
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:1515
std::shared_ptr< table > get_table_qualified(const std::string &key) const
Obtains a table for a given key, if possible.
Definition: cpptoml.h:1388
bool contains(const std::string &key) const
Determines if this key table contains the given key.
Definition: cpptoml.h:1336
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:1548
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:1483
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:1430
string_to_base_map::iterator iterator
tables can be iterated over.
Definition: cpptoml.h:1296
void insert(const std::string &key, const std::shared_ptr< base > &value)
Adds an element to the keytable.
Definition: cpptoml.h:1538
std::shared_ptr< table > get_table(const std::string &key) const
Obtains a table for a given key, if possible.
Definition: cpptoml.h:1377
void erase(const std::string &key)
Removes an element from the table.
Definition: cpptoml.h:1557
bool is_table() const override
Determines if the given TOML element is a table.
Definition: cpptoml.h:1323
std::shared_ptr< base > get_qualified(const std::string &key) const
Obtains the base for a given key.
Definition: cpptoml.h:1367
string_to_base_map::const_iterator const_iterator
tables can be iterated over.
Definition: cpptoml.h:1301
std::shared_ptr< array > get_array(const std::string &key) const
Obtains an array for a given key.
Definition: cpptoml.h:1398
std::shared_ptr< base > get(const std::string &key) const
Obtains the base for a given key.
Definition: cpptoml.h:1355
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:1460
bool contains_qualified(const std::string &key) const
Determines if this key table contains the given key.
Definition: cpptoml.h:1346
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:1442
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:1418
Writer that can be passed to accept() functions of cpptoml objects and will output valid TOML to a st...
Definition: cpptoml.h:3303
void visit(const table &t, bool in_array=false)
Output a table element of the TOML tree.
Definition: cpptoml.h:3327
void write_table_item_header(const base &b)
Write out the identifier for an item in a table.
Definition: cpptoml.h:3569
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:3505
void write(const T &v)
Write a value out to the stream.
Definition: cpptoml.h:3608
void indent()
Indent the proper number of tabs given the size of the path.
Definition: cpptoml.h:3598
void write_table_header(bool in_array=false)
Write out the header of a table.
Definition: cpptoml.h:3521
toml_writer(std::ostream &s, const std::string &indent_space="\t")
Construct a toml_writer that will write to the given stream.
Definition: cpptoml.h:3308
void write(const value< double > &v)
Write out a double.
Definition: cpptoml.h:3478
void visit(const array &a, bool=false)
Output an array element of the TOML tree.
Definition: cpptoml.h:3375
static std::string escape_string(const std::string &str)
Escape a string for output.
Definition: cpptoml.h:3416
void write(const value< std::string > &v)
Write out a string.
Definition: cpptoml.h:3468
void write(const value< bool > &v)
Write out a boolean.
Definition: cpptoml.h:3513
void visit(const table_array &t, bool=false)
Output a table_array element of the TOML tree.
Definition: cpptoml.h:3400
void endline()
Write an endline out to the stream.
Definition: cpptoml.h:3617
void visit(const value< T > &v, bool=false)
Output a base value of the TOML tree.
Definition: cpptoml.h:3319
A concrete TOML value representing the "leaves" of the "tree".
Definition: cpptoml.h:609
T & get()
Gets the data associated with this value.
Definition: cpptoml.h:638
value(const T &val)
Constructs a value from the given data.
Definition: cpptoml.h:662
bool is_value() const override
Determines if the given TOML element is a value.
Definition: cpptoml.h:630
const T & get() const
Gets the data associated with this value.
Definition: cpptoml.h:646
std::shared_ptr< table > parse_file(const std::string &filename)
Utility function to parse a file as a TOML file.
Definition: cpptoml.h:3220
Definition: cpptoml.h:384
Definition: cpptoml.h:264
Definition: cpptoml.h:102
Definition: cpptoml.h:123
Definition: cpptoml.h:109
Definition: cpptoml.h:127
Definition: cpptoml.h:293
Definition: cpptoml.h:285
Definition: cpptoml.h:611
Definition: cpptoml.h:3236
Definition: cpptoml.h:289
Definition: cpptoml.h:117