21 #include <unordered_map>
24 #if __cplusplus > 201103L
25 #define CPPTOML_DEPRECATED(reason) [[deprecated(reason)]]
26 #elif defined(__clang__)
27 #define CPPTOML_DEPRECATED(reason) __attribute__((deprecated(reason)))
28 #elif defined(__GNUG__)
29 #define CPPTOML_DEPRECATED(reason) __attribute__((deprecated))
30 #elif defined(_MSC_VER)
32 #define CPPTOML_DEPRECATED(reason) __declspec(deprecated)
34 #define CPPTOML_DEPRECATED(reason) [[deprecated(reason)]]
40 class writer; // forward declaration
41 class base; // forward declaration
42 #if defined(CPPTOML_USE_MAP)
43 // a std::map will ensure that entries a sorted, albeit at a slight
44 // performance penalty relative to the (default) unordered_map
45 using string_to_base_map = std::map<std::string, std::shared_ptr<base>>;
47 // by default an unordered_map is used for best performance as the
48 // toml specification does not require entries to be sorted
49 using string_to_base_map
50 = std::unordered_map<std::string, std::shared_ptr<base>>;
57 option() : empty_{true}
62 option(T value) : empty_{false}, value_{std::move(value)}
67 explicit operator bool() const
72 const T& operator*() const
77 const T* operator->() const
82 const T& value_or(const T& alternative) const
112 int minute_offset = 0;
115 struct local_datetime : local_date, local_time
119 struct offset_datetime : local_datetime, zone_offset
121 static inline struct offset_datetime from_zoned(const struct tm& t)
124 dt.year = t.tm_year + 1900;
125 dt.month = t.tm_mon + 1;
128 dt.minute = t.tm_min;
129 dt.second = t.tm_sec;
132 strftime(buf, 16, "%z", &t);
134 int offset = std::stoi(buf);
135 dt.hour_offset = offset / 100;
136 dt.minute_offset = offset % 100;
140 CPPTOML_DEPRECATED("from_local has been renamed to from_zoned")
141 static inline struct offset_datetime from_local(const struct tm& t)
143 return from_zoned(t);
146 static inline struct offset_datetime from_utc(const struct tm& t)
149 dt.year = t.tm_year + 1900;
150 dt.month = t.tm_mon + 1;
153 dt.minute = t.tm_min;
154 dt.second = t.tm_sec;
159 CPPTOML_DEPRECATED("datetime has been renamed to offset_datetime")
160 typedef offset_datetime datetime;
165 fill_guard(std::ostream& os) : os_(os), fill_{os.fill()}
177 std::ostream::char_type fill_;
180 inline std::ostream& operator<<(std::ostream& os, const local_date& dt)
186 os << setw(4) << dt.year << "-" << setw(2) << dt.month << "-" << setw(2)
192 inline std::ostream& operator<<(std::ostream& os, const local_time& ltime)
198 os << setw(2) << ltime.hour << ":" << setw(2) << ltime.minute << ":"
199 << setw(2) << ltime.second;
201 if (ltime.microsecond > 0)
205 for (int curr_us = ltime.microsecond; curr_us; power /= 10)
207 auto num = curr_us / power;
209 curr_us -= num * power;
216 inline std::ostream& operator<<(std::ostream& os, const zone_offset& zo)
223 if (zo.hour_offset != 0 || zo.minute_offset != 0)
225 if (zo.hour_offset > 0)
233 os << setw(2) << std::abs(zo.hour_offset) << ":" << setw(2)
234 << std::abs(zo.minute_offset);
244 inline std::ostream& operator<<(std::ostream& os, const local_datetime& dt)
246 return os << static_cast<const local_date&>(dt) << "T"
247 << static_cast<const local_time&>(dt);
250 inline std::ostream& operator<<(std::ostream& os, const offset_datetime& dt)
252 return os << static_cast<const local_datetime&>(dt)
253 << static_cast<const zone_offset&>(dt);
256 template <class T, class... Ts>
259 template <class T, class V>
260 struct is_one_of<T, V> : std::is_same<T, V>
264 template <class T, class V, class... Ts>
265 struct is_one_of<T, V, Ts...>
267 const static bool value
268 = std::is_same<T, V>::value || is_one_of<T, Ts...>::value;
276 : is_one_of<T, std::string, int64_t, double, bool, local_date, local_time,
277 local_datetime, offset_datetime>
281 template <class T, class Enable = void>
285 struct valid_value_or_string_convertible
288 const static bool value = valid_value<typename std::decay<T>::type>::value
289 || std::is_convertible<T, std::string>::value;
293 struct value_traits<T, typename std::
294 enable_if<valid_value_or_string_convertible<T>::
297 using value_type = typename std::
298 conditional<valid_value<typename std::decay<T>::type>::value,
299 typename std::decay<T>::type, std::string>::type;
301 using type = value<value_type>;
303 static value_type construct(T&& val)
305 return value_type(val);
310 struct value_traits<T,
312 enable_if<!valid_value_or_string_convertible<T>::value
313 && std::is_floating_point<
314 typename std::decay<T>::type>::value>::
317 using value_type = typename std::decay<T>::type;
319 using type = value<double>;
321 static value_type construct(T&& val)
323 return value_type(val);
328 struct value_traits<T,
330 enable_if<!valid_value_or_string_convertible<T>::value
331 && std::is_signed<typename std::decay<T>::
334 using value_type = int64_t;
336 using type = value<int64_t>;
338 static value_type construct(T&& val)
340 if (val < std::numeric_limits<int64_t>::min())
341 throw std::underflow_error{"constructed value cannot be "
342 "represented by a 64-bit signed "
345 if (val > std::numeric_limits<int64_t>::max())
346 throw std::overflow_error{"constructed value cannot be represented "
347 "by a 64-bit signed integer"};
349 return static_cast<int64_t>(val);
354 struct value_traits<T,
356 enable_if<!valid_value_or_string_convertible<T>::value
357 && std::is_unsigned<typename std::decay<T>::
360 using value_type = int64_t;
362 using type = value<int64_t>;
364 static value_type construct(T&& val)
366 if (val > static_cast<uint64_t>(std::numeric_limits<int64_t>::max()))
367 throw std::overflow_error{"constructed value cannot be represented "
368 "by a 64-bit signed integer"};
370 return static_cast<int64_t>(val);
379 struct array_of_trait
381 using return_type = option<std::vector<T>>;
385 struct array_of_trait<array>
387 using return_type = option<std::vector<std::shared_ptr<array>>>;
391 inline std::shared_ptr<typename value_traits<T>::type> make_value(T&& val);
392 inline std::shared_ptr<array> make_array();
394 inline std::shared_ptr<T> make_element();
395 inline std::shared_ptr<table> make_table();
396 inline std::shared_ptr<table_array> make_table_array();
399 * A generic base TOML value used for type erasure.
401 class base : public std::enable_shared_from_this<base>
404 virtual ~base() = default;
406 virtual std::shared_ptr<base> clone() const = 0;
409 * Determines if the given TOML element is a value.
411 virtual bool is_value() const
417 * Determines if the given TOML element is a table.
419 virtual bool is_table() const
425 * Converts the TOML element into a table.
427 std::shared_ptr<table> as_table()
430 return std::static_pointer_cast<table>(shared_from_this());
434 * Determines if the TOML element is an array of "leaf" elements.
436 virtual bool is_array() const
442 * Converts the TOML element to an array.
444 std::shared_ptr<array> as_array()
447 return std::static_pointer_cast<array>(shared_from_this());
452 * Determines if the given TOML element is an array of tables.
454 virtual bool is_table_array() const
460 * Converts the TOML element into a table array.
462 std::shared_ptr<table_array> as_table_array()
464 if (is_table_array())
465 return std::static_pointer_cast<table_array>(shared_from_this());
470 * Attempts to coerce the TOML element into a concrete TOML value
474 std::shared_ptr<value<T>> as();
477 std::shared_ptr<const value<T>> as() const;
479 template <class Visitor, class... Args>
480 void accept(Visitor&& visitor, Args&&... args) const;
490 * A concrete TOML value representing the "leaves" of the "tree".
493 class value : public base
495 struct make_shared_enabler
497 // nothing; this is a private key accessible only to friends
501 friend std::shared_ptr<typename value_traits<U>::type>
502 cpptoml::make_value(U&& val);
505 static_assert(valid_value<T>::value, "invalid value type");
507 std::shared_ptr<base> clone() const override;
509 value(const make_shared_enabler&, const T& val) : value(val)
511 // nothing; note that users cannot actually invoke this function
512 // because they lack access to the make_shared_enabler.
515 bool is_value() const override
521 * Gets the data associated with this value.
529 * Gets the data associated with this value. Const version.
540 * Constructs a value from the given data.
542 value(const T& val) : data_(val)
546 value(const value& val) = delete;
547 value& operator=(const value& val) = delete;
551 std::shared_ptr<typename value_traits<T>::type> make_value(T&& val)
553 using value_type = typename value_traits<T>::type;
554 using enabler = typename value_type::make_shared_enabler;
555 return std::make_shared<value_type>(
556 enabler{}, value_traits<T>::construct(std::forward<T>(val)));
560 inline std::shared_ptr<value<T>> base::as()
562 return std::dynamic_pointer_cast<value<T>>(shared_from_this());
565 // special case value<double> to allow getting an integer parameter as a
568 inline std::shared_ptr<value<double>> base::as()
570 if (auto v = std::dynamic_pointer_cast<value<double>>(shared_from_this()))
573 if (auto v = std::dynamic_pointer_cast<value<int64_t>>(shared_from_this()))
574 return make_value<double>(static_cast<double>(v->get()));
580 inline std::shared_ptr<const value<T>> base::as() const
582 return std::dynamic_pointer_cast<const value<T>>(shared_from_this());
585 // special case value<double> to allow getting an integer parameter as a
588 inline std::shared_ptr<const value<double>> base::as() const
591 = std::dynamic_pointer_cast<const value<double>>(shared_from_this()))
594 if (auto v = as<int64_t>())
596 // the below has to be a non-const value<double> due to a bug in
597 // libc++: https://llvm.org/bugs/show_bug.cgi?id=18843
598 return make_value<double>(static_cast<double>(v->get()));
605 * Exception class for array insertion errors.
607 class array_exception : public std::runtime_error
610 array_exception(const std::string& err) : std::runtime_error{err}
615 class array : public base
618 friend std::shared_ptr<array> make_array();
620 std::shared_ptr<base> clone() const override;
622 virtual bool is_array() const override
627 using size_type = std::size_t;
630 * arrays can be iterated over
632 using iterator = std::vector<std::shared_ptr<base>>::iterator;
635 * arrays can be iterated over. Const version.
637 using const_iterator = std::vector<std::shared_ptr<base>>::const_iterator;
641 return values_.begin();
644 const_iterator begin() const
646 return values_.begin();
651 return values_.end();
654 const_iterator end() const
656 return values_.end();
660 * Obtains the array (vector) of base values.
662 std::vector<std::shared_ptr<base>>& get()
668 * Obtains the array (vector) of base values. Const version.
670 const std::vector<std::shared_ptr<base>>& get() const
675 std::shared_ptr<base> at(size_t idx) const
677 return values_.at(idx);
681 * Obtains an array of value<T>s. Note that elements may be
682 * nullptr if they cannot be converted to a value<T>.
685 std::vector<std::shared_ptr<value<T>>> array_of() const
687 std::vector<std::shared_ptr<value<T>>> result(values_.size());
689 std::transform(values_.begin(), values_.end(), result.begin(),
690 [&](std::shared_ptr<base> v) { return v->as<T>(); });
696 * Obtains a option<vector<T>>. The option will be empty if the array
697 * contains values that are not of type T.
700 inline typename array_of_trait<T>::return_type get_array_of() const
702 std::vector<T> result;
703 result.reserve(values_.size());
705 for (const auto& val : values_)
707 if (auto v = val->as<T>())
708 result.push_back(v->get());
713 return {std::move(result)};
717 * Obtains an array of arrays. Note that elements may be nullptr
718 * if they cannot be converted to a array.
720 std::vector<std::shared_ptr<array>> nested_array() const
722 std::vector<std::shared_ptr<array>> result(values_.size());
724 std::transform(values_.begin(), values_.end(), result.begin(),
725 [&](std::shared_ptr<base> v) -> std::shared_ptr<array> {
727 return std::static_pointer_cast<array>(v);
728 return std::shared_ptr<array>{};
735 * Add a value to the end of the array
738 void push_back(const std::shared_ptr<value<T>>& val)
740 if (values_.empty() || values_[0]->as<T>())
742 values_.push_back(val);
746 throw array_exception{"Arrays must be homogenous."};
751 * Add an array to the end of the array
753 void push_back(const std::shared_ptr<array>& val)
755 if (values_.empty() || values_[0]->is_array())
757 values_.push_back(val);
761 throw array_exception{"Arrays must be homogenous."};
766 * Convenience function for adding a simple element to the end
770 void push_back(T&& val, typename value_traits<T>::type* = 0)
772 push_back(make_value(std::forward<T>(val)));
776 * Insert a value into the array
779 iterator insert(iterator position, const std::shared_ptr<value<T>>& value)
781 if (values_.empty() || values_[0]->as<T>())
783 return values_.insert(position, value);
787 throw array_exception{"Arrays must be homogenous."};
792 * Insert an array into the array
794 iterator insert(iterator position, const std::shared_ptr<array>& value)
796 if (values_.empty() || values_[0]->is_array())
798 return values_.insert(position, value);
802 throw array_exception{"Arrays must be homogenous."};
807 * Convenience function for inserting a simple element in the array
810 iterator insert(iterator position, T&& val,
811 typename value_traits<T>::type* = 0)
813 return insert(position, make_value(std::forward<T>(val)));
817 * Erase an element from the array
819 iterator erase(iterator position)
821 return values_.erase(position);
833 * Reserve space for n values.
835 void reserve(size_type n)
843 template <class InputIterator>
844 array(InputIterator begin, InputIterator end) : values_{begin, end}
849 array(const array& obj) = delete;
850 array& operator=(const array& obj) = delete;
852 std::vector<std::shared_ptr<base>> values_;
855 inline std::shared_ptr<array> make_array()
857 struct make_shared_enabler : public array
859 make_shared_enabler()
865 return std::make_shared<make_shared_enabler>();
869 inline std::shared_ptr<array> make_element<array>()
875 * Obtains a option<vector<T>>. The option will be empty if the array
876 * contains values that are not of type T.
879 inline typename array_of_trait<array>::return_type
880 array::get_array_of<array>() const
882 std::vector<std::shared_ptr<array>> result;
883 result.reserve(values_.size());
885 for (const auto& val : values_)
887 if (auto v = val->as_array())
893 return {std::move(result)};
898 class table_array : public base
901 friend std::shared_ptr<table_array> make_table_array();
904 std::shared_ptr<base> clone() const override;
906 using size_type = std::size_t;
909 * arrays can be iterated over
911 using iterator = std::vector<std::shared_ptr<table>>::iterator;
914 * arrays can be iterated over. Const version.
916 using const_iterator = std::vector<std::shared_ptr<table>>::const_iterator;
920 return array_.begin();
923 const_iterator begin() const
925 return array_.begin();
933 const_iterator end() const
938 virtual bool is_table_array() const override
943 std::vector<std::shared_ptr<table>>& get()
948 const std::vector<std::shared_ptr<table>>& get() const
954 * Add a table to the end of the array
956 void push_back(const std::shared_ptr<table>& val)
958 array_.push_back(val);
962 * Insert a table into the array
964 iterator insert(iterator position, const std::shared_ptr<table>& value)
966 return array_.insert(position, value);
970 * Erase an element from the array
972 iterator erase(iterator position)
974 return array_.erase(position);
986 * Reserve space for n tables.
988 void reserve(size_type n)
999 table_array(const table_array& obj) = delete;
1000 table_array& operator=(const table_array& rhs) = delete;
1002 std::vector<std::shared_ptr<table>> array_;
1005 inline std::shared_ptr<table_array> make_table_array()
1007 struct make_shared_enabler : public table_array
1009 make_shared_enabler()
1015 return std::make_shared<make_shared_enabler>();
1019 inline std::shared_ptr<table_array> make_element<table_array>()
1021 return make_table_array();
1024 // The below are overloads for fetching specific value types out of a value
1025 // where special casting behavior (like bounds checking) is desired
1028 typename std::enable_if<!std::is_floating_point<T>::value
1029 && std::is_signed<T>::value,
1031 get_impl(const std::shared_ptr<base>& elem)
1033 if (auto v = elem->as<int64_t>())
1035 if (v->get() < std::numeric_limits<T>::min())
1036 throw std::underflow_error{
1037 "T cannot represent the value requested in get"};
1039 if (v->get() > std::numeric_limits<T>::max())
1040 throw std::overflow_error{
1041 "T cannot represent the value requested in get"};
1043 return {static_cast<T>(v->get())};
1052 typename std::enable_if<!std::is_same<T, bool>::value
1053 && std::is_unsigned<T>::value,
1055 get_impl(const std::shared_ptr<base>& elem)
1057 if (auto v = elem->as<int64_t>())
1060 throw std::underflow_error{"T cannot store negative value in get"};
1062 if (static_cast<uint64_t>(v->get()) > std::numeric_limits<T>::max())
1063 throw std::overflow_error{
1064 "T cannot represent the value requested in get"};
1066 return {static_cast<T>(v->get())};
1075 typename std::enable_if<!std::is_integral<T>::value
1076 || std::is_same<T, bool>::value,
1078 get_impl(const std::shared_ptr<base>& elem)
1080 if (auto v = elem->as<T>())
1091 * Represents a TOML keytable.
1093 class table : public base
1096 friend class table_array;
1097 friend std::shared_ptr<table> make_table();
1099 std::shared_ptr<base> clone() const override;
1102 * tables can be iterated over.
1104 using iterator = string_to_base_map::iterator;
1107 * tables can be iterated over. Const version.
1109 using const_iterator = string_to_base_map::const_iterator;
1113 return map_.begin();
1116 const_iterator begin() const
1118 return map_.begin();
1126 const_iterator end() const
1131 bool is_table() const override
1138 return map_.empty();
1142 * Determines if this key table contains the given key.
1144 bool contains(const std::string& key) const
1146 return map_.find(key) != map_.end();
1150 * Determines if this key table contains the given key. Will
1151 * resolve "qualified keys". Qualified keys are the full access
1152 * path separated with dots like "grandparent.parent.child".
1154 bool contains_qualified(const std::string& key) const
1156 return resolve_qualified(key);
1160 * Obtains the base for a given key.
1161 * @throw std::out_of_range if the key does not exist
1163 std::shared_ptr<base> get(const std::string& key) const
1165 return map_.at(key);
1169 * Obtains the base for a given key. Will resolve "qualified
1170 * keys". Qualified keys are the full access path separated with
1171 * dots like "grandparent.parent.child".
1173 * @throw std::out_of_range if the key does not exist
1175 std::shared_ptr<base> get_qualified(const std::string& key) const
1177 std::shared_ptr<base> p;
1178 resolve_qualified(key, &p);
1183 * Obtains a table for a given key, if possible.
1185 std::shared_ptr<table> get_table(const std::string& key) const
1187 if (contains(key) && get(key)->is_table())
1188 return std::static_pointer_cast<table>(get(key));
1193 * Obtains a table for a given key, if possible. Will resolve
1196 std::shared_ptr<table> get_table_qualified(const std::string& key) const
1198 if (contains_qualified(key) && get_qualified(key)->is_table())
1199 return std::static_pointer_cast<table>(get_qualified(key));
1204 * Obtains an array for a given key.
1206 std::shared_ptr<array> get_array(const std::string& key) const
1210 return get(key)->as_array();
1214 * Obtains an array for a given key. Will resolve "qualified keys".
1216 std::shared_ptr<array> get_array_qualified(const std::string& key) const
1218 if (!contains_qualified(key))
1220 return get_qualified(key)->as_array();
1224 * Obtains a table_array for a given key, if possible.
1226 std::shared_ptr<table_array> get_table_array(const std::string& key) const
1230 return get(key)->as_table_array();
1234 * Obtains a table_array for a given key, if possible. Will resolve
1237 std::shared_ptr<table_array>
1238 get_table_array_qualified(const std::string& key) const
1240 if (!contains_qualified(key))
1242 return get_qualified(key)->as_table_array();
1246 * Helper function that attempts to get a value corresponding
1247 * to the template parameter from a given key.
1250 option<T> get_as(const std::string& key) const
1254 return get_impl<T>(get(key));
1256 catch (const std::out_of_range&)
1263 * Helper function that attempts to get a value corresponding
1264 * to the template parameter from a given key. Will resolve "qualified
1268 option<T> get_qualified_as(const std::string& key) const
1272 return get_impl<T>(get_qualified(key));
1274 catch (const std::out_of_range&)
1281 * Helper function that attempts to get an array of values of a given
1282 * type corresponding to the template parameter for a given key.
1284 * If the key doesn't exist, doesn't exist as an array type, or one or
1285 * more keys inside the array type are not of type T, an empty option
1286 * is returned. Otherwise, an option containing a vector of the values
1290 inline typename array_of_trait<T>::return_type
1291 get_array_of(const std::string& key) const
1293 if (auto v = get_array(key))
1295 std::vector<T> result;
1296 result.reserve(v->get().size());
1298 for (const auto& b : v->get())
1300 if (auto val = b->as<T>())
1301 result.push_back(val->get());
1305 return {std::move(result)};
1312 * Helper function that attempts to get an array of values of a given
1313 * type corresponding to the template parameter for a given key. Will
1314 * resolve "qualified keys".
1316 * If the key doesn't exist, doesn't exist as an array type, or one or
1317 * more keys inside the array type are not of type T, an empty option
1318 * is returned. Otherwise, an option containing a vector of the values
1322 inline typename array_of_trait<T>::return_type
1323 get_qualified_array_of(const std::string& key) const
1325 if (auto v = get_array_qualified(key))
1327 std::vector<T> result;
1328 result.reserve(v->get().size());
1330 for (const auto& b : v->get())
1332 if (auto val = b->as<T>())
1333 result.push_back(val->get());
1337 return {std::move(result)};
1344 * Adds an element to the keytable.
1346 void insert(const std::string& key, const std::shared_ptr<base>& value)
1352 * Convenience shorthand for adding a simple element to the
1356 void insert(const std::string& key, T&& val,
1357 typename value_traits<T>::type* = 0)
1359 insert(key, make_value(std::forward<T>(val)));
1363 * Removes an element from the table.
1365 void erase(const std::string& key)
1376 table(const table& obj) = delete;
1377 table& operator=(const table& rhs) = delete;
1379 std::vector<std::string> split(const std::string& value,
1380 char separator) const
1382 std::vector<std::string> result;
1383 std::string::size_type p = 0;
1384 std::string::size_type q;
1385 while ((q = value.find(separator, p)) != std::string::npos)
1387 result.emplace_back(value, p, q - p);
1390 result.emplace_back(value, p);
1394 // If output parameter p is specified, fill it with the pointer to the
1395 // specified entry and throw std::out_of_range if it couldn't be found.
1397 // Otherwise, just return true if the entry could be found or false
1398 // otherwise and do not throw.
1399 bool resolve_qualified(const std::string& key,
1400 std::shared_ptr<base>* p = nullptr) const
1402 auto parts = split(key, '.');
1403 auto last_key = parts.back();
1407 for (const auto& part : parts)
1409 table = table->get_table(part).get();
1415 throw std::out_of_range{key + " is not a valid key"};
1420 return table->map_.count(last_key) != 0;
1422 *p = table->map_.at(last_key);
1426 string_to_base_map map_;
1430 * Helper function that attempts to get an array of arrays for a given
1433 * If the key doesn't exist, doesn't exist as an array type, or one or
1434 * more keys inside the array type are not of type T, an empty option
1435 * is returned. Otherwise, an option containing a vector of the values
1439 inline typename array_of_trait<array>::return_type
1440 table::get_array_of<array>(const std::string& key) const
1442 if (auto v = get_array(key))
1444 std::vector<std::shared_ptr<array>> result;
1445 result.reserve(v->get().size());
1447 for (const auto& b : v->get())
1449 if (auto val = b->as_array())
1450 result.push_back(val);
1455 return {std::move(result)};
1462 * Helper function that attempts to get an array of arrays for a given
1463 * key. Will resolve "qualified keys".
1465 * If the key doesn't exist, doesn't exist as an array type, or one or
1466 * more keys inside the array type are not of type T, an empty option
1467 * is returned. Otherwise, an option containing a vector of the values
1471 inline typename array_of_trait<array>::return_type
1472 table::get_qualified_array_of<array>(const std::string& key) const
1474 if (auto v = get_array_qualified(key))
1476 std::vector<std::shared_ptr<array>> result;
1477 result.reserve(v->get().size());
1479 for (const auto& b : v->get())
1481 if (auto val = b->as_array())
1482 result.push_back(val);
1487 return {std::move(result)};
1493 std::shared_ptr<table> make_table()
1495 struct make_shared_enabler : public table
1497 make_shared_enabler()
1503 return std::make_shared<make_shared_enabler>();
1507 inline std::shared_ptr<table> make_element<table>()
1509 return make_table();
1513 std::shared_ptr<base> value<T>::clone() const
1515 return make_value(data_);
1518 inline std::shared_ptr<base> array::clone() const
1520 auto result = make_array();
1521 result->reserve(values_.size());
1522 for (const auto& ptr : values_)
1523 result->values_.push_back(ptr->clone());
1527 inline std::shared_ptr<base> table_array::clone() const
1529 auto result = make_table_array();
1530 result->reserve(array_.size());
1531 for (const auto& ptr : array_)
1532 result->array_.push_back(ptr->clone()->as_table());
1536 inline std::shared_ptr<base> table::clone() const
1538 auto result = make_table();
1539 for (const auto& pr : map_)
1540 result->insert(pr.first, pr.second->clone());
1545 * Exception class for all TOML parsing errors.
1547 class parse_exception : public std::runtime_error
1550 parse_exception(const std::string& err) : std::runtime_error{err}
1554 parse_exception(const std::string& err, std::size_t line_number)
1555 : std::runtime_error{err + " at line " + std::to_string(line_number)}
1560 inline bool is_number(char c)
1562 return c >= '0' && c <= '9';
1566 * Helper object for consuming expected characters.
1568 template <class OnError>
1572 consumer(std::string::iterator& it, const std::string::iterator& end,
1574 : it_(it), end_(end), on_error_(std::forward<OnError>(on_error))
1579 void operator()(char c)
1581 if (it_ == end_ || *it_ != c)
1586 template <std::size_t N>
1587 void operator()(const char (&str)[N])
1589 std::for_each(std::begin(str), std::end(str) - 1,
1590 [&](char c) { (*this)(c); });
1593 int eat_digits(int len)
1596 for (int i = 0; i < len; ++i)
1598 if (!is_number(*it_) || it_ == end_)
1600 val = 10 * val + (*it_++ - '0');
1611 std::string::iterator& it_;
1612 const std::string::iterator& end_;
1616 template <class OnError>
1617 consumer<OnError> make_consumer(std::string::iterator& it,
1618 const std::string::iterator& end,
1621 return consumer<OnError>(it, end, std::forward<OnError>(on_error));
1624 // replacement for std::getline to handle incorrectly line-ended files
1625 // https://stackoverflow.com/questions/6089231/getting-std-ifstream-to-handle-lf-cr-and-crlf
1628 inline std::istream& getline(std::istream& input, std::string& line)
1632 std::istream::sentry sentry{input, true};
1633 auto sb = input.rdbuf();
1637 auto c = sb->sbumpc();
1640 if (sb->sgetc() == '\n')
1647 if (c == std::istream::traits_type::eof())
1650 input.setstate(std::ios::eofbit);
1654 line.push_back(static_cast<char>(c));
1666 * Parsers are constructed from streams.
1668 parser(std::istream& stream) : input_(stream)
1673 parser& operator=(const parser& parser) = delete;
1676 * Parses the stream this parser was created on until EOF.
1677 * @throw parse_exception if there are errors in parsing
1679 std::shared_ptr<table> parse()
1681 std::shared_ptr<table> root = make_table();
1683 table* curr_table = root.get();
1685 while (detail::getline(input_, line_))
1688 auto it = line_.begin();
1689 auto end = line_.end();
1690 consume_whitespace(it, end);
1691 if (it == end || *it == '#')
1695 curr_table = root.get();
1696 parse_table(it, end, curr_table);
1700 parse_key_value(it, end, curr_table);
1701 consume_whitespace(it, end);
1702 eol_or_comment(it, end);
1709 #if defined _MSC_VER
1710 __declspec(noreturn)
1711 #elif defined __GNUC__
1712 __attribute__((noreturn))
1714 void throw_parse_exception(const std::string& err)
1716 throw parse_exception{err, line_number_};
1719 void parse_table(std::string::iterator& it,
1720 const std::string::iterator& end, table*& curr_table)
1722 // remove the beginning keytable marker
1725 throw_parse_exception("Unexpected end of table");
1727 parse_table_array(it, end, curr_table);
1729 parse_single_table(it, end, curr_table);
1732 void parse_single_table(std::string::iterator& it,
1733 const std::string::iterator& end,
1736 if (it == end || *it == ']')
1737 throw_parse_exception("Table name cannot be empty");
1739 std::string full_table_name;
1740 bool inserted = false;
1741 while (it != end && *it != ']')
1743 auto part = parse_key(it, end,
1744 [](char c) { return c == '.' || c == ']'; });
1747 throw_parse_exception("Empty component of table name");
1749 if (!full_table_name.empty())
1750 full_table_name += ".";
1751 full_table_name += part;
1753 if (curr_table->contains(part))
1755 auto b = curr_table->get(part);
1757 curr_table = static_cast<table*>(b.get());
1758 else if (b->is_table_array())
1759 curr_table = std::static_pointer_cast<table_array>(b)
1764 throw_parse_exception("Key " + full_table_name
1765 + "already exists as a value");
1770 curr_table->insert(part, make_table());
1771 curr_table = static_cast<table*>(curr_table->get(part).get());
1773 consume_whitespace(it, end);
1774 if (it != end && *it == '.')
1776 consume_whitespace(it, end);
1780 throw_parse_exception(
1781 "Unterminated table declaration; did you forget a ']'?");
1783 // table already existed
1787 = [](const std::pair<const std::string&,
1788 const std::shared_ptr<base>&>& p) {
1789 return p.second->is_value();
1792 // if there are any values, we can't add values to this table
1793 // since it has already been defined. If there aren't any
1794 // values, then it was implicitly created by something like
1796 if (curr_table->empty() || std::any_of(curr_table->begin(),
1797 curr_table->end(), is_value))
1799 throw_parse_exception("Redefinition of table "
1805 consume_whitespace(it, end);
1806 eol_or_comment(it, end);
1809 void parse_table_array(std::string::iterator& it,
1810 const std::string::iterator& end, table*& curr_table)
1813 if (it == end || *it == ']')
1814 throw_parse_exception("Table array name cannot be empty");
1816 std::string full_ta_name;
1817 while (it != end && *it != ']')
1819 auto part = parse_key(it, end,
1820 [](char c) { return c == '.' || c == ']'; });
1823 throw_parse_exception("Empty component of table array name");
1825 if (!full_ta_name.empty())
1826 full_ta_name += ".";
1827 full_ta_name += part;
1829 consume_whitespace(it, end);
1830 if (it != end && *it == '.')
1832 consume_whitespace(it, end);
1834 if (curr_table->contains(part))
1836 auto b = curr_table->get(part);
1838 // if this is the end of the table array name, add an
1839 // element to the table array that we just looked up
1840 if (it != end && *it == ']')
1842 if (!b->is_table_array())
1843 throw_parse_exception("Key " + full_ta_name
1844 + " is not a table array");
1845 auto v = b->as_table_array();
1846 v->get().push_back(make_table());
1847 curr_table = v->get().back().get();
1849 // otherwise, just keep traversing down the key name
1853 curr_table = static_cast<table*>(b.get());
1854 else if (b->is_table_array())
1855 curr_table = std::static_pointer_cast<table_array>(b)
1860 throw_parse_exception("Key " + full_ta_name
1861 + " already exists as a value");
1866 // if this is the end of the table array name, add a new
1867 // table array and a new table inside that array for us to
1869 if (it != end && *it == ']')
1871 curr_table->insert(part, make_table_array());
1872 auto arr = std::static_pointer_cast<table_array>(
1873 curr_table->get(part));
1874 arr->get().push_back(make_table());
1875 curr_table = arr->get().back().get();
1877 // otherwise, create the implicitly defined table and move
1881 curr_table->insert(part, make_table());
1883 = static_cast<table*>(curr_table->get(part).get());
1888 // consume the last "]]"
1890 throw_parse_exception("Unterminated table array name");
1893 throw_parse_exception("Unterminated table array name");
1896 consume_whitespace(it, end);
1897 eol_or_comment(it, end);
1900 void parse_key_value(std::string::iterator& it, std::string::iterator& end,
1903 auto key = parse_key(it, end, [](char c) { return c == '='; });
1904 if (curr_table->contains(key))
1905 throw_parse_exception("Key " + key + " already present");
1906 if (it == end || *it != '=')
1907 throw_parse_exception("Value must follow after a '='");
1909 consume_whitespace(it, end);
1910 curr_table->insert(key, parse_value(it, end));
1911 consume_whitespace(it, end);
1914 template <class Function>
1915 std::string parse_key(std::string::iterator& it,
1916 const std::string::iterator& end, Function&& fun)
1918 consume_whitespace(it, end);
1921 return parse_quoted_key(it, end);
1925 auto bke = std::find_if(it, end, std::forward<Function>(fun));
1926 return parse_bare_key(it, bke);
1930 std::string parse_bare_key(std::string::iterator& it,
1931 const std::string::iterator& end)
1935 throw_parse_exception("Bare key missing name");
1940 consume_backwards_whitespace(key_end, it);
1942 std::string key{it, key_end};
1944 if (std::find(it, key_end, '#') != key_end)
1946 throw_parse_exception("Bare key " + key + " cannot contain #");
1949 if (std::find_if(it, key_end,
1950 [](char c) { return c == ' ' || c == '\t'; })
1953 throw_parse_exception("Bare key " + key
1954 + " cannot contain whitespace");
1957 if (std::find_if(it, key_end,
1958 [](char c) { return c == '[' || c == ']'; })
1961 throw_parse_exception("Bare key " + key
1962 + " cannot contain '[' or ']'");
1969 std::string parse_quoted_key(std::string::iterator& it,
1970 const std::string::iterator& end)
1972 return string_literal(it, end, '"');
1975 enum class parse_type
1989 std::shared_ptr<base> parse_value(std::string::iterator& it,
1990 std::string::iterator& end)
1992 parse_type type = determine_value_type(it, end);
1995 case parse_type::STRING:
1996 return parse_string(it, end);
1997 case parse_type::LOCAL_TIME:
1998 return parse_time(it, end);
1999 case parse_type::LOCAL_DATE:
2000 case parse_type::LOCAL_DATETIME:
2001 case parse_type::OFFSET_DATETIME:
2002 return parse_date(it, end);
2003 case parse_type::INT:
2004 case parse_type::FLOAT:
2005 return parse_number(it, end);
2006 case parse_type::BOOL:
2007 return parse_bool(it, end);
2008 case parse_type::ARRAY:
2009 return parse_array(it, end);
2010 case parse_type::INLINE_TABLE:
2011 return parse_inline_table(it, end);
2013 throw_parse_exception("Failed to parse value");
2017 parse_type determine_value_type(const std::string::iterator& it,
2018 const std::string::iterator& end)
2020 if (*it == '"' || *it == '\'')
2022 return parse_type::STRING;
2024 else if (is_time(it, end))
2026 return parse_type::LOCAL_TIME;
2028 else if (auto dtype = date_type(it, end))
2032 else if (is_number(*it) || *it == '-' || *it == '+')
2034 return determine_number_type(it, end);
2036 else if (*it == 't' || *it == 'f')
2038 return parse_type::BOOL;
2040 else if (*it == '[')
2042 return parse_type::ARRAY;
2044 else if (*it == '{')
2046 return parse_type::INLINE_TABLE;
2048 throw_parse_exception("Failed to parse value type");
2051 parse_type determine_number_type(const std::string::iterator& it,
2052 const std::string::iterator& end)
2054 // determine if we are an integer or a float
2056 if (*check_it == '-' || *check_it == '+')
2058 while (check_it != end && is_number(*check_it))
2060 if (check_it != end && *check_it == '.')
2063 while (check_it != end && is_number(*check_it))
2065 return parse_type::FLOAT;
2069 return parse_type::INT;
2073 std::shared_ptr<value<std::string>> parse_string(std::string::iterator& it,
2074 std::string::iterator& end)
2077 assert(delim == '"' || delim == '\'');
2079 // end is non-const here because we have to be able to potentially
2080 // parse multiple lines in a string, not just one
2083 if (check_it != end && *check_it == delim)
2086 if (check_it != end && *check_it == delim)
2089 return parse_multiline_string(it, end, delim);
2092 return make_value<std::string>(string_literal(it, end, delim));
2095 std::shared_ptr<value<std::string>>
2096 parse_multiline_string(std::string::iterator& it,
2097 std::string::iterator& end, char delim)
2099 std::stringstream ss;
2101 auto is_ws = [](char c) { return c == ' ' || c == '\t'; };
2103 bool consuming = false;
2104 std::shared_ptr<value<std::string>> ret;
2107 = [&](std::string::iterator& it, std::string::iterator& end) {
2110 it = std::find_if_not(it, end, is_ws);
2112 // whole line is whitespace
2121 // handle escaped characters
2122 if (delim == '"' && *it == '\\')
2125 // check if this is an actual escape sequence or a
2126 // whitespace escaping backslash
2128 consume_whitespace(check, end);
2135 ss << parse_escape_code(it, end);
2139 // if we can end the string
2140 if (std::distance(it, end) >= 3)
2144 if (*check++ == delim && *check++ == delim
2145 && *check++ == delim)
2148 ret = make_value<std::string>(ss.str());
2157 // handle the remainder of the current line
2158 handle_line(it, end);
2162 // start eating lines
2163 while (detail::getline(input_, line_))
2170 handle_line(it, end);
2179 throw_parse_exception("Unterminated multi-line basic string");
2182 std::string string_literal(std::string::iterator& it,
2183 const std::string::iterator& end, char delim)
2189 // handle escaped characters
2190 if (delim == '"' && *it == '\\')
2192 val += parse_escape_code(it, end);
2194 else if (*it == delim)
2197 consume_whitespace(it, end);
2205 throw_parse_exception("Unterminated string literal");
2208 std::string parse_escape_code(std::string::iterator& it,
2209 const std::string::iterator& end)
2213 throw_parse_exception("Invalid escape sequence");
2219 else if (*it == 't')
2223 else if (*it == 'n')
2227 else if (*it == 'f')
2231 else if (*it == 'r')
2235 else if (*it == '"')
2239 else if (*it == '\\')
2243 else if (*it == 'u' || *it == 'U')
2245 return parse_unicode(it, end);
2249 throw_parse_exception("Invalid escape sequence");
2252 return std::string(1, value);
2255 std::string parse_unicode(std::string::iterator& it,
2256 const std::string::iterator& end)
2258 bool large = *it++ == 'U';
2259 auto codepoint = parse_hex(it, end, large ? 0x10000000 : 0x1000);
2261 if ((codepoint > 0xd7ff && codepoint < 0xe000) || codepoint > 0x10ffff)
2263 throw_parse_exception(
2264 "Unicode escape sequence is not a Unicode scalar value");
2268 // See Table 3-6 of the Unicode standard
2269 if (codepoint <= 0x7f)
2271 // 1-byte codepoints: 00000000 0xxxxxxx
2273 result += static_cast<char>(codepoint & 0x7f);
2275 else if (codepoint <= 0x7ff)
2277 // 2-byte codepoints: 00000yyy yyxxxxxx
2278 // repr: 110yyyyy 10xxxxxx
2283 result += static_cast<char>(0xc0 | ((codepoint >> 6) & 0x1f));
2288 result += static_cast<char>(0x80 | (codepoint & 0x3f));
2290 else if (codepoint <= 0xffff)
2292 // 3-byte codepoints: zzzzyyyy yyxxxxxx
2293 // repr: 1110zzzz 10yyyyyy 10xxxxxx
2298 result += static_cast<char>(0xe0 | ((codepoint >> 12) & 0x0f));
2299 result += static_cast<char>(0x80 | ((codepoint >> 6) & 0x1f));
2300 result += static_cast<char>(0x80 | (codepoint & 0x3f));
2304 // 4-byte codepoints: 000uuuuu zzzzyyyy yyxxxxxx
2305 // repr: 11110uuu 10uuzzzz 10yyyyyy 10xxxxxx
2310 result += static_cast<char>(0xf0 | ((codepoint >> 18) & 0x07));
2311 result += static_cast<char>(0x80 | ((codepoint >> 12) & 0x3f));
2312 result += static_cast<char>(0x80 | ((codepoint >> 6) & 0x3f));
2313 result += static_cast<char>(0x80 | (codepoint & 0x3f));
2318 uint32_t parse_hex(std::string::iterator& it,
2319 const std::string::iterator& end, uint32_t place)
2325 throw_parse_exception("Unexpected end of unicode sequence");
2328 throw_parse_exception("Invalid unicode escape sequence");
2330 value += place * hex_to_digit(*it++);
2338 return is_number(c) || (c >= 'a' && c <= 'f') || (c >= 'A' && c <= 'F');
2341 uint32_t hex_to_digit(char c)
2344 return static_cast<uint32_t>(c - '0');
2345 return 10 + static_cast<uint32_t>(
2346 c - ((c >= 'a' && c <= 'f') ? 'a' : 'A'));
2349 std::shared_ptr<base> parse_number(std::string::iterator& it,
2350 const std::string::iterator& end)
2353 auto check_end = find_end_of_number(it, end);
2355 auto eat_sign = [&]() {
2356 if (check_it != end && (*check_it == '-' || *check_it == '+'))
2362 auto eat_numbers = [&]() {
2363 auto beg = check_it;
2364 while (check_it != end && is_number(*check_it))
2367 if (check_it != end && *check_it == '_')
2370 if (check_it == end || !is_number(*check_it))
2371 throw_parse_exception("Malformed number");
2375 if (check_it == beg)
2376 throw_parse_exception("Malformed number");
2379 auto check_no_leading_zero = [&]() {
2380 if (check_it != end && *check_it == '0' && check_it + 1 != check_end
2381 && check_it[1] != '.')
2383 throw_parse_exception("Numbers may not have leading zeros");
2387 check_no_leading_zero();
2391 && (*check_it == '.' || *check_it == 'e' || *check_it == 'E'))
2393 bool is_exp = *check_it == 'e' || *check_it == 'E';
2396 if (check_it == end)
2397 throw_parse_exception("Floats must have trailing digits");
2399 auto eat_exp = [&]() {
2401 check_no_leading_zero();
2410 if (!is_exp && check_it != end
2411 && (*check_it == 'e' || *check_it == 'E'))
2417 return parse_float(it, check_it);
2421 return parse_int(it, check_it);
2425 std::shared_ptr<value<int64_t>> parse_int(std::string::iterator& it,
2426 const std::string::iterator& end)
2428 std::string v{it, end};
2429 v.erase(std::remove(v.begin(), v.end(), '_'), v.end());
2433 return make_value<int64_t>(std::stoll(v));
2435 catch (const std::invalid_argument& ex)
2437 throw_parse_exception("Malformed number (invalid argument: "
2438 + std::string{ex.what()} + ")");
2440 catch (const std::out_of_range& ex)
2442 throw_parse_exception("Malformed number (out of range: "
2443 + std::string{ex.what()} + ")");
2447 std::shared_ptr<value<double>> parse_float(std::string::iterator& it,
2448 const std::string::iterator& end)
2450 std::string v{it, end};
2451 v.erase(std::remove(v.begin(), v.end(), '_'), v.end());
2455 return make_value<double>(std::stod(v));
2457 catch (const std::invalid_argument& ex)
2459 throw_parse_exception("Malformed number (invalid argument: "
2460 + std::string{ex.what()} + ")");
2462 catch (const std::out_of_range& ex)
2464 throw_parse_exception("Malformed number (out of range: "
2465 + std::string{ex.what()} + ")");
2469 std::shared_ptr<value<bool>> parse_bool(std::string::iterator& it,
2470 const std::string::iterator& end)
2472 auto eat = make_consumer(it, end, [this]() {
2473 throw_parse_exception("Attempted to parse invalid boolean value");
2479 return make_value<bool>(true);
2481 else if (*it == 'f')
2484 return make_value<bool>(false);
2491 std::string::iterator find_end_of_number(std::string::iterator it,
2492 std::string::iterator end)
2494 return std::find_if(it, end, [](char c) {
2495 return !is_number(c) && c != '_' && c != '.' && c != 'e' && c != 'E'
2496 && c != '-' && c != '+';
2500 std::string::iterator find_end_of_date(std::string::iterator it,
2501 std::string::iterator end)
2503 return std::find_if(it, end, [](char c) {
2504 return !is_number(c) && c != 'T' && c != 'Z' && c != ':' && c != '-'
2505 && c != '+' && c != '.';
2509 std::string::iterator find_end_of_time(std::string::iterator it,
2510 std::string::iterator end)
2512 return std::find_if(it, end, [](char c) {
2513 return !is_number(c) && c != ':' && c != '.';
2517 local_time read_time(std::string::iterator& it,
2518 const std::string::iterator& end)
2520 auto time_end = find_end_of_time(it, end);
2522 auto eat = make_consumer(
2523 it, time_end, [&]() { throw_parse_exception("Malformed time"); });
2527 ltime.hour = eat.eat_digits(2);
2529 ltime.minute = eat.eat_digits(2);
2531 ltime.second = eat.eat_digits(2);
2534 if (it != time_end && *it == '.')
2537 while (it != time_end && is_number(*it))
2539 ltime.microsecond += power * (*it++ - '0');
2545 throw_parse_exception("Malformed time");
2550 std::shared_ptr<value<local_time>>
2551 parse_time(std::string::iterator& it, const std::string::iterator& end)
2553 return make_value(read_time(it, end));
2556 std::shared_ptr<base> parse_date(std::string::iterator& it,
2557 const std::string::iterator& end)
2559 auto date_end = find_end_of_date(it, end);
2561 auto eat = make_consumer(
2562 it, date_end, [&]() { throw_parse_exception("Malformed date"); });
2565 ldate.year = eat.eat_digits(4);
2567 ldate.month = eat.eat_digits(2);
2569 ldate.day = eat.eat_digits(2);
2572 return make_value(ldate);
2577 static_cast<local_date&>(ldt) = ldate;
2578 static_cast<local_time&>(ldt) = read_time(it, date_end);
2581 return make_value(ldt);
2584 static_cast<local_datetime&>(dt) = ldt;
2588 if (*it == '+' || *it == '-')
2590 auto plus = *it == '+';
2593 hoff = eat.eat_digits(2);
2594 dt.hour_offset = (plus) ? hoff : -hoff;
2596 moff = eat.eat_digits(2);
2597 dt.minute_offset = (plus) ? moff : -moff;
2599 else if (*it == 'Z')
2605 throw_parse_exception("Malformed date");
2607 return make_value(dt);
2610 std::shared_ptr<base> parse_array(std::string::iterator& it,
2611 std::string::iterator& end)
2613 // this gets ugly because of the "homogeneity" restriction:
2614 // arrays can either be of only one type, or contain arrays
2615 // (each of those arrays could be of different types, though)
2617 // because of the latter portion, we don't really have a choice
2618 // but to represent them as arrays of base values...
2621 // ugh---have to read the first value to determine array type...
2622 skip_whitespace_and_comments(it, end);
2624 // edge case---empty array
2628 return make_array();
2631 auto val_end = std::find_if(
2632 it, end, [](char c) { return c == ',' || c == ']' || c == '#'; });
2633 parse_type type = determine_value_type(it, val_end);
2636 case parse_type::STRING:
2637 return parse_value_array<std::string>(it, end);
2638 case parse_type::LOCAL_TIME:
2639 return parse_value_array<local_time>(it, end);
2640 case parse_type::LOCAL_DATE:
2641 return parse_value_array<local_date>(it, end);
2642 case parse_type::LOCAL_DATETIME:
2643 return parse_value_array<local_datetime>(it, end);
2644 case parse_type::OFFSET_DATETIME:
2645 return parse_value_array<offset_datetime>(it, end);
2646 case parse_type::INT:
2647 return parse_value_array<int64_t>(it, end);
2648 case parse_type::FLOAT:
2649 return parse_value_array<double>(it, end);
2650 case parse_type::BOOL:
2651 return parse_value_array<bool>(it, end);
2652 case parse_type::ARRAY:
2653 return parse_object_array<array>(&parser::parse_array, '[', it,
2655 case parse_type::INLINE_TABLE:
2656 return parse_object_array<table_array>(
2657 &parser::parse_inline_table, '{', it, end);
2659 throw_parse_exception("Unable to parse array");
2663 template <class Value>
2664 std::shared_ptr<array> parse_value_array(std::string::iterator& it,
2665 std::string::iterator& end)
2667 auto arr = make_array();
2668 while (it != end && *it != ']')
2670 auto value = parse_value(it, end);
2671 if (auto v = value->as<Value>())
2672 arr->get().push_back(value);
2674 throw_parse_exception("Arrays must be heterogeneous");
2675 skip_whitespace_and_comments(it, end);
2679 skip_whitespace_and_comments(it, end);
2686 template <class Object, class Function>
2687 std::shared_ptr<Object> parse_object_array(Function&& fun, char delim,
2688 std::string::iterator& it,
2689 std::string::iterator& end)
2691 auto arr = make_element<Object>();
2693 while (it != end && *it != ']')
2696 throw_parse_exception("Unexpected character in array");
2698 arr->get().push_back(((*this).*fun)(it, end));
2699 skip_whitespace_and_comments(it, end);
2705 skip_whitespace_and_comments(it, end);
2708 if (it == end || *it != ']')
2709 throw_parse_exception("Unterminated array");
2715 std::shared_ptr<table> parse_inline_table(std::string::iterator& it,
2716 std::string::iterator& end)
2718 auto tbl = make_table();
2723 throw_parse_exception("Unterminated inline table");
2725 consume_whitespace(it, end);
2726 parse_key_value(it, end, tbl.get());
2727 consume_whitespace(it, end);
2728 } while (*it == ',');
2730 if (it == end || *it != '}')
2731 throw_parse_exception("Unterminated inline table");
2734 consume_whitespace(it, end);
2739 void skip_whitespace_and_comments(std::string::iterator& start,
2740 std::string::iterator& end)
2742 consume_whitespace(start, end);
2743 while (start == end || *start == '#')
2745 if (!detail::getline(input_, line_))
2746 throw_parse_exception("Unclosed array");
2748 start = line_.begin();
2750 consume_whitespace(start, end);
2754 void consume_whitespace(std::string::iterator& it,
2755 const std::string::iterator& end)
2757 while (it != end && (*it == ' ' || *it == '\t'))
2761 void consume_backwards_whitespace(std::string::iterator& back,
2762 const std::string::iterator& front)
2764 while (back != front && (*back == ' ' || *back == '\t'))
2768 void eol_or_comment(const std::string::iterator& it,
2769 const std::string::iterator& end)
2771 if (it != end && *it != '#')
2772 throw_parse_exception("Unidentified trailing character '"
2774 + "'---did you forget a '#'?");
2777 bool is_time(const std::string::iterator& it,
2778 const std::string::iterator& end)
2780 auto time_end = find_end_of_time(it, end);
2781 auto len = std::distance(it, time_end);
2786 if (it[2] != ':' || it[5] != ':')
2790 return it[8] == '.' && len > 9;
2795 option<parse_type> date_type(const std::string::iterator& it,
2796 const std::string::iterator& end)
2798 auto date_end = find_end_of_date(it, end);
2799 auto len = std::distance(it, date_end);
2804 if (it[4] != '-' || it[7] != '-')
2807 if (len >= 19 && it[10] == 'T' && is_time(it + 11, date_end))
2810 auto time_end = find_end_of_time(it + 11, date_end);
2811 if (time_end == date_end)
2812 return {parse_type::LOCAL_DATETIME};
2814 return {parse_type::OFFSET_DATETIME};
2818 // just a regular date
2819 return {parse_type::LOCAL_DATE};
2825 std::istream& input_;
2827 std::size_t line_number_ = 0;
2831 * Utility function to parse a file as a TOML file. Returns the root table.
2832 * Throws a parse_exception if the file cannot be opened.
2834 inline std::shared_ptr<table> parse_file(const std::string& filename)
2836 #if defined(BOOST_NOWIDE_FSTREAM_INCLUDED_HPP)
2837 boost::nowide::ifstream file{filename.c_str()};
2838 #elif defined(NOWIDE_FSTREAM_INCLUDED_HPP)
2839 nowide::ifstream file{filename.c_str()};
2841 std::ifstream file{filename};
2843 if (!file.is_open())
2844 throw parse_exception{filename + " could not be opened for parsing"};
2849 template <class... Ts>
2850 struct value_accept;
2853 struct value_accept<>
2855 template <class Visitor, class... Args>
2856 static void accept(const base&, Visitor&&, Args&&...)
2862 template <class T, class... Ts>
2863 struct value_accept<T, Ts...>
2865 template <class Visitor, class... Args>
2866 static void accept(const base& b, Visitor&& visitor, Args&&... args)
2868 if (auto v = b.as<T>())
2870 visitor.visit(*v, std::forward<Args>(args)...);
2874 value_accept<Ts...>::accept(b, std::forward<Visitor>(visitor),
2875 std::forward<Args>(args)...);
2881 * base implementation of accept() that calls visitor.visit() on the concrete
2884 template <class Visitor, class... Args>
2885 void base::accept(Visitor&& visitor, Args&&... args) const
2889 using value_acceptor
2890 = value_accept<std::string, int64_t, double, bool, local_date,
2891 local_time, local_datetime, offset_datetime>;
2892 value_acceptor::accept(*this, std::forward<Visitor>(visitor),
2893 std::forward<Args>(args)...);
2895 else if (is_table())
2897 visitor.visit(static_cast<const table&>(*this),
2898 std::forward<Args>(args)...);
2900 else if (is_array())
2902 visitor.visit(static_cast<const array&>(*this),
2903 std::forward<Args>(args)...);
2905 else if (is_table_array())
2907 visitor.visit(static_cast<const table_array&>(*this),
2908 std::forward<Args>(args)...);
2913 * Writer that can be passed to accept() functions of cpptoml objects and
2914 * will output valid TOML to a stream.
2920 * Construct a toml_writer that will write to the given stream
2922 toml_writer(std::ostream& s, const std::string& indent_space = "\t")
2923 : stream_(s), indent_(indent_space), has_naked_endline_(false)
2930 * Output a base value of the TOML tree.
2933 void visit(const value<T>& v, bool = false)
2939 * Output a table element of the TOML tree
2941 void visit(const table& t, bool in_array = false)
2943 write_table_header(in_array);
2944 std::vector<std::string> values;
2945 std::vector<std::string> tables;
2947 for (const auto& i : t)
2949 if (i.second->is_table() || i.second->is_table_array())
2951 tables.push_back(i.first);
2955 values.push_back(i.first);
2959 for (unsigned int i = 0; i < values.size(); ++i)
2961 path_.push_back(values[i]);
2966 write_table_item_header(*t.get(values[i]));
2967 t.get(values[i])->accept(*this, false);
2971 for (unsigned int i = 0; i < tables.size(); ++i)
2973 path_.push_back(tables[i]);
2975 if (values.size() > 0 || i > 0)
2978 write_table_item_header(*t.get(tables[i]));
2979 t.get(tables[i])->accept(*this, false);
2987 * Output an array element of the TOML tree
2989 void visit(const array& a, bool = false)
2993 for (unsigned int i = 0; i < a.get().size(); ++i)
2998 if (a.get()[i]->is_array())
3000 a.get()[i]->as_array()->accept(*this, true);
3004 a.get()[i]->accept(*this, true);
3012 * Output a table_array element of the TOML tree
3014 void visit(const table_array& t, bool = false)
3016 for (unsigned int j = 0; j < t.get().size(); ++j)
3021 t.get()[j]->accept(*this, true);
3028 * Escape a string for output.
3030 static std::string escape_string(const std::string& str)
3033 for (auto it = str.begin(); it != str.end(); ++it)
3039 else if (*it == '\t')
3043 else if (*it == '\n')
3047 else if (*it == '\f')
3051 else if (*it == '\r')
3055 else if (*it == '"')
3059 else if (*it == '\\')
3063 else if (*it >= 0x0000 && *it <= 0x001f)
3066 std::stringstream ss;
3067 ss << std::hex << static_cast<uint32_t>(*it);
3080 * Write out a string.
3082 void write(const value<std::string>& v)
3085 write(escape_string(v.get()));
3090 * Write out a double.
3092 void write(const value<double>& v)
3094 std::ios::fmtflags flags{stream_.flags()};
3096 stream_ << std::showpoint;
3099 stream_.flags(flags);
3103 * Write out an integer, local_date, local_time, local_datetime, or
3107 typename std::enable_if<is_one_of<T, int64_t, local_date, local_time,
3109 offset_datetime>::value>::type
3110 write(const value<T>& v)
3116 * Write out a boolean.
3118 void write(const value<bool>& v)
3120 write((v.get() ? "true" : "false"));
3124 * Write out the header of a table.
3126 void write_table_header(bool in_array = false)
3139 for (unsigned int i = 0; i < path_.size(); ++i)
3146 if (path_[i].find_first_not_of("ABCDEFGHIJKLMNOPQRSTUVWXYZabcde"
3147 "fghijklmnopqrstuvwxyz0123456789"
3149 == std::string::npos)
3156 write(escape_string(path_[i]));
3172 * Write out the identifier for an item in a table.
3174 void write_table_item_header(const base& b)
3176 if (!b.is_table() && !b.is_table_array())
3180 if (path_.back().find_first_not_of("ABCDEFGHIJKLMNOPQRSTUVWXYZabcde"
3181 "fghijklmnopqrstuvwxyz0123456789"
3183 == std::string::npos)
3185 write(path_.back());
3190 write(escape_string(path_.back()));
3200 * Indent the proper number of tabs given the size of
3205 for (std::size_t i = 1; i < path_.size(); ++i)
3210 * Write a value out to the stream.
3213 void write(const T& v)
3216 has_naked_endline_ = false;
3220 * Write an endline out to the stream
3224 if (!has_naked_endline_)
3227 has_naked_endline_ = true;
3232 std::ostream& stream_;
3233 const std::string indent_;
3234 std::vector<std::string> path_;
3235 bool has_naked_endline_;
3238 inline std::ostream& operator<<(std::ostream& stream, const base& b)
3240 toml_writer writer{stream};
3246 std::ostream& operator<<(std::ostream& stream, const value<T>& v)
3248 toml_writer writer{stream};
3253 inline std::ostream& operator<<(std::ostream& stream, const table& t)
3255 toml_writer writer{stream};
3260 inline std::ostream& operator<<(std::ostream& stream, const table_array& t)
3262 toml_writer writer{stream};
3267 inline std::ostream& operator<<(std::ostream& stream, const array& a)
3269 toml_writer writer{stream};