GRASP rci-qed
cpptoml.h
Go to the documentation of this file.
1 // Copyright (c) 2014 Chase Geigle
2 // License: MIT
3 // https://github.com/skystrife/cpptoml
10 #ifndef CPPTOML_H
11 #define CPPTOML_H
12 
13 #include <algorithm>
14 #include <cassert>
15 #include <clocale>
16 #include <cstdint>
17 #include <cstring>
18 #include <fstream>
19 #include <iomanip>
20 #include <map>
21 #include <memory>
22 #include <sstream>
23 #include <stdexcept>
24 #include <string>
25 #include <unordered_map>
26 #include <vector>
27 
28 #if __cplusplus > 201103L
29 #define CPPTOML_DEPRECATED(reason) [[deprecated(reason)]]
30 #elif defined(__clang__)
31 #define CPPTOML_DEPRECATED(reason) __attribute__((deprecated(reason)))
32 #elif defined(__GNUG__)
33 #define CPPTOML_DEPRECATED(reason) __attribute__((deprecated))
34 #elif defined(_MSC_VER)
35 #if _MSC_VER < 1910
36 #define CPPTOML_DEPRECATED(reason) __declspec(deprecated)
37 #else
38 #define CPPTOML_DEPRECATED(reason) [[deprecated(reason)]]
39 #endif
40 #endif
41 
42 namespace cpptoml
43 {
44 class writer; // forward declaration
45 class base; // forward declaration
46 #if defined(CPPTOML_USE_MAP)
47 // a std::map will ensure that entries a sorted, albeit at a slight
48 // performance penalty relative to the (default) unordered_map
49 using string_to_base_map = std::map<std::string, std::shared_ptr<base>>;
50 #else
51 // by default an unordered_map is used for best performance as the
52 // toml specification does not require entries to be sorted
54  = std::unordered_map<std::string, std::shared_ptr<base>>;
55 #endif
56 
57 // if defined, `base` will retain type information in form of an enum class
58 // such that static_cast can be used instead of dynamic_cast
59 // #define CPPTOML_NO_RTTI
60 
61 template <class T>
62 class option
63 {
64  public:
65  option() : empty_{true}
66  {
67  // nothing
68  }
69 
70  option(T value) : empty_{false}, value_(std::move(value))
71  {
72  // nothing
73  }
74 
75  explicit operator bool() const
76  {
77  return !empty_;
78  }
79 
80  const T& operator*() const
81  {
82  return value_;
83  }
84 
85  const T* operator->() const
86  {
87  return &value_;
88  }
89 
90  template <class U>
91  T value_or(U&& alternative) const
92  {
93  if (!empty_)
94  return value_;
95  return static_cast<T>(std::forward<U>(alternative));
96  }
97 
98  private:
99  bool empty_;
101 };
102 
104 {
105  int year = 0;
106  int month = 0;
107  int day = 0;
108 };
109 
111 {
112  int hour = 0;
113  int minute = 0;
114  int second = 0;
115  int microsecond = 0;
116 };
117 
119 {
120  int hour_offset = 0;
121  int minute_offset = 0;
122 };
123 
125 {
126 };
127 
129 {
130  static inline struct offset_datetime from_zoned(const struct tm& t)
131  {
132  offset_datetime dt;
133  dt.year = t.tm_year + 1900;
134  dt.month = t.tm_mon + 1;
135  dt.day = t.tm_mday;
136  dt.hour = t.tm_hour;
137  dt.minute = t.tm_min;
138  dt.second = t.tm_sec;
139 
140  char buf[16];
141  strftime(buf, 16, "%z", &t);
142 
143  int offset = std::stoi(buf);
144  dt.hour_offset = offset / 100;
145  dt.minute_offset = offset % 100;
146  return dt;
147  }
148 
149  CPPTOML_DEPRECATED("from_local has been renamed to from_zoned")
150  static inline struct offset_datetime from_local(const struct tm& t)
151  {
152  return from_zoned(t);
153  }
154 
155  static inline struct offset_datetime from_utc(const struct tm& t)
156  {
157  offset_datetime dt;
158  dt.year = t.tm_year + 1900;
159  dt.month = t.tm_mon + 1;
160  dt.day = t.tm_mday;
161  dt.hour = t.tm_hour;
162  dt.minute = t.tm_min;
163  dt.second = t.tm_sec;
164  return dt;
165  }
166 };
167 
168 CPPTOML_DEPRECATED("datetime has been renamed to offset_datetime")
170 
172 {
173  public:
174  fill_guard(std::ostream& os) : os_(os), fill_{os.fill()}
175  {
176  // nothing
177  }
178 
180  {
181  os_.fill(fill_);
182  }
183 
184  private:
185  std::ostream& os_;
186  std::ostream::char_type fill_;
187 };
188 
189 inline std::ostream& operator<<(std::ostream& os, const local_date& dt)
190 {
191  fill_guard g{os};
192  os.fill('0');
193 
194  using std::setw;
195  os << setw(4) << dt.year << "-" << setw(2) << dt.month << "-" << setw(2)
196  << dt.day;
197 
198  return os;
199 }
200 
201 inline std::ostream& operator<<(std::ostream& os, const local_time& ltime)
202 {
203  fill_guard g{os};
204  os.fill('0');
205 
206  using std::setw;
207  os << setw(2) << ltime.hour << ":" << setw(2) << ltime.minute << ":"
208  << setw(2) << ltime.second;
209 
210  if (ltime.microsecond > 0)
211  {
212  os << ".";
213  int power = 100000;
214  for (int curr_us = ltime.microsecond; curr_us; power /= 10)
215  {
216  auto num = curr_us / power;
217  os << num;
218  curr_us -= num * power;
219  }
220  }
221 
222  return os;
223 }
224 
225 inline std::ostream& operator<<(std::ostream& os, const zone_offset& zo)
226 {
227  fill_guard g{os};
228  os.fill('0');
229 
230  using std::setw;
231 
232  if (zo.hour_offset != 0 || zo.minute_offset != 0)
233  {
234  if (zo.hour_offset > 0)
235  {
236  os << "+";
237  }
238  else
239  {
240  os << "-";
241  }
242  os << setw(2) << std::abs(zo.hour_offset) << ":" << setw(2)
243  << std::abs(zo.minute_offset);
244  }
245  else
246  {
247  os << "Z";
248  }
249 
250  return os;
251 }
252 
253 inline std::ostream& operator<<(std::ostream& os, const local_datetime& dt)
254 {
255  return os << static_cast<const local_date&>(dt) << "T"
256  << static_cast<const local_time&>(dt);
257 }
258 
259 inline std::ostream& operator<<(std::ostream& os, const offset_datetime& dt)
260 {
261  return os << static_cast<const local_datetime&>(dt)
262  << static_cast<const zone_offset&>(dt);
263 }
264 
265 template <class T, class... Ts>
266 struct is_one_of;
267 
268 template <class T, class V>
269 struct is_one_of<T, V> : std::is_same<T, V>
270 {
271 };
272 
273 template <class T, class V, class... Ts>
274 struct is_one_of<T, V, Ts...>
275 {
276  const static bool value
277  = std::is_same<T, V>::value || is_one_of<T, Ts...>::value;
278 };
279 
280 template <class T>
281 class value;
282 
283 template <class T>
285  : is_one_of<T, std::string, int64_t, double, bool, local_date, local_time,
286  local_datetime, offset_datetime>
287 {
288 };
289 
290 template <class T, class Enable = void>
292 
293 template <class T>
295 {
296 
298  || std::is_convertible<T, std::string>::value;
299 };
300 
301 template <class T>
302 struct value_traits<T, typename std::enable_if<
303  valid_value_or_string_convertible<T>::value>::type>
304 {
305  using value_type = typename std::conditional<
307  typename std::decay<T>::type, std::string>::type;
308 
310 
311  static value_type construct(T&& val)
312  {
313  return value_type(val);
314  }
315 };
316 
317 template <class T>
319  T,
320  typename std::enable_if<
321  !valid_value_or_string_convertible<T>::value
322  && std::is_floating_point<typename std::decay<T>::type>::value>::type>
323 {
324  using value_type = typename std::decay<T>::type;
325 
327 
328  static value_type construct(T&& val)
329  {
330  return value_type(val);
331  }
332 };
333 
334 template <class T>
336  T, typename std::enable_if<
337  !valid_value_or_string_convertible<T>::value
338  && !std::is_floating_point<typename std::decay<T>::type>::value
339  && std::is_signed<typename std::decay<T>::type>::value>::type>
340 {
341  using value_type = int64_t;
342 
344 
345  static value_type construct(T&& val)
346  {
347  if (val < (std::numeric_limits<int64_t>::min)())
348  throw std::underflow_error{"constructed value cannot be "
349  "represented by a 64-bit signed "
350  "integer"};
351 
352  if (val > (std::numeric_limits<int64_t>::max)())
353  throw std::overflow_error{"constructed value cannot be represented "
354  "by a 64-bit signed integer"};
355 
356  return static_cast<int64_t>(val);
357  }
358 };
359 
360 template <class T>
362  T, typename std::enable_if<
363  !valid_value_or_string_convertible<T>::value
364  && std::is_unsigned<typename std::decay<T>::type>::value>::type>
365 {
366  using value_type = int64_t;
367 
369 
370  static value_type construct(T&& val)
371  {
372  if (val > static_cast<uint64_t>((std::numeric_limits<int64_t>::max)()))
373  throw std::overflow_error{"constructed value cannot be represented "
374  "by a 64-bit signed integer"};
375 
376  return static_cast<int64_t>(val);
377  }
378 };
379 
380 class array;
381 class table;
382 class table_array;
383 
384 template <class T>
386 {
388 };
389 
390 template <>
392 {
394 };
395 
396 template <class T>
397 inline std::shared_ptr<typename value_traits<T>::type> make_value(T&& val);
398 inline std::shared_ptr<array> make_array();
399 
400 namespace detail
401 {
402 template <class T>
403 inline std::shared_ptr<T> make_element();
404 }
405 
406 inline std::shared_ptr<table> make_table();
407 inline std::shared_ptr<table_array> make_table_array(bool is_inline = false);
408 
409 #if defined(CPPTOML_NO_RTTI)
410 enum class base_type
412 {
413  NONE,
414  STRING,
415  LOCAL_TIME,
416  LOCAL_DATE,
417  LOCAL_DATETIME,
418  OFFSET_DATETIME,
419  INT,
420  FLOAT,
421  BOOL,
422  TABLE,
423  ARRAY,
424  TABLE_ARRAY
425 };
426 
428 template <class T>
429 struct base_type_traits;
430 
431 template <>
432 struct base_type_traits<std::string>
433 {
434  static const base_type type = base_type::STRING;
435 };
436 
437 template <>
438 struct base_type_traits<local_time>
439 {
440  static const base_type type = base_type::LOCAL_TIME;
441 };
442 
443 template <>
444 struct base_type_traits<local_date>
445 {
446  static const base_type type = base_type::LOCAL_DATE;
447 };
448 
449 template <>
450 struct base_type_traits<local_datetime>
451 {
452  static const base_type type = base_type::LOCAL_DATETIME;
453 };
454 
455 template <>
456 struct base_type_traits<offset_datetime>
457 {
458  static const base_type type = base_type::OFFSET_DATETIME;
459 };
460 
461 template <>
462 struct base_type_traits<int64_t>
463 {
464  static const base_type type = base_type::INT;
465 };
466 
467 template <>
468 struct base_type_traits<double>
469 {
470  static const base_type type = base_type::FLOAT;
471 };
472 
473 template <>
474 struct base_type_traits<bool>
475 {
476  static const base_type type = base_type::BOOL;
477 };
478 
479 template <>
480 struct base_type_traits<table>
481 {
482  static const base_type type = base_type::TABLE;
483 };
484 
485 template <>
486 struct base_type_traits<array>
487 {
488  static const base_type type = base_type::ARRAY;
489 };
490 
491 template <>
492 struct base_type_traits<table_array>
493 {
494  static const base_type type = base_type::TABLE_ARRAY;
495 };
496 #endif
497 
501 class base : public std::enable_shared_from_this<base>
502 {
503  public:
504  virtual ~base() = default;
505 
506  virtual std::shared_ptr<base> clone() const = 0;
507 
511  virtual bool is_value() const
512  {
513  return false;
514  }
515 
519  virtual bool is_table() const
520  {
521  return false;
522  }
523 
527  std::shared_ptr<table> as_table()
528  {
529  if (is_table())
530  return std::static_pointer_cast<table>(shared_from_this());
531  return nullptr;
532  }
536  virtual bool is_array() const
537  {
538  return false;
539  }
540 
544  std::shared_ptr<array> as_array()
545  {
546  if (is_array())
547  return std::static_pointer_cast<array>(shared_from_this());
548  return nullptr;
549  }
550 
554  virtual bool is_table_array() const
555  {
556  return false;
557  }
558 
562  std::shared_ptr<table_array> as_table_array()
563  {
564  if (is_table_array())
565  return std::static_pointer_cast<table_array>(shared_from_this());
566  return nullptr;
567  }
568 
573  template <class T>
574  std::shared_ptr<value<T>> as();
575 
576  template <class T>
577  std::shared_ptr<const value<T>> as() const;
578 
579  template <class Visitor, class... Args>
580  void accept(Visitor&& visitor, Args&&... args) const;
581 
582 #if defined(CPPTOML_NO_RTTI)
583  base_type type() const
584  {
585  return type_;
586  }
587 
588  protected:
589  base(const base_type t) : type_(t)
590  {
591  // nothing
592  }
593 
594  private:
595  const base_type type_ = base_type::NONE;
596 
597 #else
598  protected:
600  {
601  // nothing
602  }
603 #endif
604 };
605 
609 template <class T>
610 class value : public base
611 {
613  {
614  // nothing; this is a private key accessible only to friends
615  };
616 
617  template <class U>
618  friend std::shared_ptr<typename value_traits<U>::type>
619  cpptoml::make_value(U&& val);
620 
621  public:
622  static_assert(valid_value<T>::value, "invalid value type");
623 
624  std::shared_ptr<base> clone() const override;
625 
626  value(const make_shared_enabler&, const T& val) : value(val)
627  {
628  // nothing; note that users cannot actually invoke this function
629  // because they lack access to the make_shared_enabler.
630  }
631 
632  bool is_value() const override
633  {
634  return true;
635  }
636 
640  T& get()
641  {
642  return data_;
643  }
644 
648  const T& get() const
649  {
650  return data_;
651  }
652 
653  private:
654  T data_;
655 
659 #if defined(CPPTOML_NO_RTTI)
660  value(const T& val) : base(base_type_traits<T>::type), data_(val)
661  {
662  }
663 #else
664  value(const T& val) : data_(val)
665  {
666  }
667 #endif
668 
669  value(const value& val) = delete;
670  value& operator=(const value& val) = delete;
671 };
672 
673 template <class T>
674 std::shared_ptr<typename value_traits<T>::type> make_value(T&& val)
675 {
676  using value_type = typename value_traits<T>::type;
677  using enabler = typename value_type::make_shared_enabler;
678  return std::make_shared<value_type>(
679  enabler{}, value_traits<T>::construct(std::forward<T>(val)));
680 }
681 
682 template <class T>
683 inline std::shared_ptr<value<T>> base::as()
684 {
685 #if defined(CPPTOML_NO_RTTI)
686  if (type() == base_type_traits<T>::type)
687  return std::static_pointer_cast<value<T>>(shared_from_this());
688  else
689  return nullptr;
690 #else
691  return std::dynamic_pointer_cast<value<T>>(shared_from_this());
692 #endif
693 }
694 
695 // special case value<double> to allow getting an integer parameter as a
696 // double value
697 template <>
698 inline std::shared_ptr<value<double>> base::as()
699 {
700 #if defined(CPPTOML_NO_RTTI)
701  if (type() == base_type::FLOAT)
702  return std::static_pointer_cast<value<double>>(shared_from_this());
703 
704  if (type() == base_type::INT)
705  {
706  auto v = std::static_pointer_cast<value<int64_t>>(shared_from_this());
707  return make_value<double>(static_cast<double>(v->get()));
708  }
709 #else
710  if (auto v = std::dynamic_pointer_cast<value<double>>(shared_from_this()))
711  return v;
712 
713  if (auto v = std::dynamic_pointer_cast<value<int64_t>>(shared_from_this()))
714  return make_value<double>(static_cast<double>(v->get()));
715 #endif
716 
717  return nullptr;
718 }
719 
720 template <class T>
721 inline std::shared_ptr<const value<T>> base::as() const
722 {
723 #if defined(CPPTOML_NO_RTTI)
724  if (type() == base_type_traits<T>::type)
725  return std::static_pointer_cast<const value<T>>(shared_from_this());
726  else
727  return nullptr;
728 #else
729  return std::dynamic_pointer_cast<const value<T>>(shared_from_this());
730 #endif
731 }
732 
733 // special case value<double> to allow getting an integer parameter as a
734 // double value
735 template <>
736 inline std::shared_ptr<const value<double>> base::as() const
737 {
738 #if defined(CPPTOML_NO_RTTI)
739  if (type() == base_type::FLOAT)
740  return std::static_pointer_cast<const value<double>>(
741  shared_from_this());
742 
743  if (type() == base_type::INT)
744  {
745  auto v = as<int64_t>();
746  // the below has to be a non-const value<double> due to a bug in
747  // libc++: https://llvm.org/bugs/show_bug.cgi?id=18843
748  return make_value<double>(static_cast<double>(v->get()));
749  }
750 #else
751  if (auto v
752  = std::dynamic_pointer_cast<const value<double>>(shared_from_this()))
753  return v;
754 
755  if (auto v = as<int64_t>())
756  {
757  // the below has to be a non-const value<double> due to a bug in
758  // libc++: https://llvm.org/bugs/show_bug.cgi?id=18843
759  return make_value<double>(static_cast<double>(v->get()));
760  }
761 #endif
762 
763  return nullptr;
764 }
765 
769 class array_exception : public std::runtime_error
770 {
771  public:
772  array_exception(const std::string& err) : std::runtime_error{err}
773  {
774  }
775 };
776 
777 class array : public base
778 {
779  public:
780  friend std::shared_ptr<array> make_array();
781 
782  std::shared_ptr<base> clone() const override;
783 
784  virtual bool is_array() const override
785  {
786  return true;
787  }
788 
789  using size_type = std::size_t;
790 
794  using iterator = std::vector<std::shared_ptr<base>>::iterator;
795 
799  using const_iterator = std::vector<std::shared_ptr<base>>::const_iterator;
800 
802  {
803  return values_.begin();
804  }
805 
807  {
808  return values_.begin();
809  }
810 
812  {
813  return values_.end();
814  }
815 
817  {
818  return values_.end();
819  }
820 
824  std::vector<std::shared_ptr<base>>& get()
825  {
826  return values_;
827  }
828 
832  const std::vector<std::shared_ptr<base>>& get() const
833  {
834  return values_;
835  }
836 
837  std::shared_ptr<base> at(size_t idx) const
838  {
839  return values_.at(idx);
840  }
841 
846  template <class T>
847  std::vector<std::shared_ptr<value<T>>> array_of() const
848  {
849  std::vector<std::shared_ptr<value<T>>> result(values_.size());
850 
851  std::transform(values_.begin(), values_.end(), result.begin(),
852  [&](std::shared_ptr<base> v) { return v->as<T>(); });
853 
854  return result;
855  }
856 
861  template <class T>
863  {
864  std::vector<T> result;
865  result.reserve(values_.size());
866 
867  for (const auto& val : values_)
868  {
869  if (auto v = val->as<T>())
870  result.push_back(v->get());
871  else
872  return {};
873  }
874 
875  return {std::move(result)};
876  }
877 
882  std::vector<std::shared_ptr<array>> nested_array() const
883  {
884  std::vector<std::shared_ptr<array>> result(values_.size());
885 
886  std::transform(values_.begin(), values_.end(), result.begin(),
887  [&](std::shared_ptr<base> v) -> std::shared_ptr<array> {
888  if (v->is_array())
889  return std::static_pointer_cast<array>(v);
890  return std::shared_ptr<array>{};
891  });
892 
893  return result;
894  }
895 
899  template <class T>
900  void push_back(const std::shared_ptr<value<T>>& val)
901  {
902  if (values_.empty() || values_[0]->as<T>())
903  {
904  values_.push_back(val);
905  }
906  else
907  {
908  throw array_exception{"Arrays must be homogenous."};
909  }
910  }
911 
915  void push_back(const std::shared_ptr<array>& val)
916  {
917  if (values_.empty() || values_[0]->is_array())
918  {
919  values_.push_back(val);
920  }
921  else
922  {
923  throw array_exception{"Arrays must be homogenous."};
924  }
925  }
926 
931  template <class T>
932  void push_back(T&& val, typename value_traits<T>::type* = 0)
933  {
934  push_back(make_value(std::forward<T>(val)));
935  }
936 
940  template <class T>
941  iterator insert(iterator position, const std::shared_ptr<value<T>>& value)
942  {
943  if (values_.empty() || values_[0]->as<T>())
944  {
945  return values_.insert(position, value);
946  }
947  else
948  {
949  throw array_exception{"Arrays must be homogenous."};
950  }
951  }
952 
956  iterator insert(iterator position, const std::shared_ptr<array>& value)
957  {
958  if (values_.empty() || values_[0]->is_array())
959  {
960  return values_.insert(position, value);
961  }
962  else
963  {
964  throw array_exception{"Arrays must be homogenous."};
965  }
966  }
967 
971  template <class T>
972  iterator insert(iterator position, T&& val,
973  typename value_traits<T>::type* = 0)
974  {
975  return insert(position, make_value(std::forward<T>(val)));
976  }
977 
982  {
983  return values_.erase(position);
984  }
985 
989  void clear()
990  {
991  values_.clear();
992  }
993 
998  {
999  values_.reserve(n);
1000  }
1001 
1002  private:
1003 #if defined(CPPTOML_NO_RTTI)
1004  array() : base(base_type::ARRAY)
1005  {
1006  // empty
1007  }
1008 #else
1009  array() = default;
1010 #endif
1011 
1012  template <class InputIterator>
1013  array(InputIterator begin, InputIterator end) : values_{begin, end}
1014  {
1015  // nothing
1016  }
1017 
1018  array(const array& obj) = delete;
1019  array& operator=(const array& obj) = delete;
1020 
1021  std::vector<std::shared_ptr<base>> values_;
1022 };
1023 
1024 inline std::shared_ptr<array> make_array()
1025 {
1026  struct make_shared_enabler : public array
1027  {
1028  make_shared_enabler()
1029  {
1030  // nothing
1031  }
1032  };
1033 
1034  return std::make_shared<make_shared_enabler>();
1035 }
1036 
1037 namespace detail
1038 {
1039 template <>
1040 inline std::shared_ptr<array> make_element<array>()
1041 {
1042  return make_array();
1043 }
1044 } // namespace detail
1045 
1050 template <>
1051 inline typename array_of_trait<array>::return_type
1052 array::get_array_of<array>() const
1053 {
1054  std::vector<std::shared_ptr<array>> result;
1055  result.reserve(values_.size());
1056 
1057  for (const auto& val : values_)
1058  {
1059  if (auto v = val->as_array())
1060  result.push_back(v);
1061  else
1062  return {};
1063  }
1064 
1065  return {std::move(result)};
1066 }
1067 
1068 class table;
1069 
1070 class table_array : public base
1071 {
1072  friend class table;
1073  friend std::shared_ptr<table_array> make_table_array(bool);
1074 
1075  public:
1076  std::shared_ptr<base> clone() const override;
1077 
1078  using size_type = std::size_t;
1079 
1083  using iterator = std::vector<std::shared_ptr<table>>::iterator;
1084 
1088  using const_iterator = std::vector<std::shared_ptr<table>>::const_iterator;
1089 
1091  {
1092  return array_.begin();
1093  }
1094 
1096  {
1097  return array_.begin();
1098  }
1099 
1101  {
1102  return array_.end();
1103  }
1104 
1106  {
1107  return array_.end();
1108  }
1109 
1110  virtual bool is_table_array() const override
1111  {
1112  return true;
1113  }
1114 
1115  std::vector<std::shared_ptr<table>>& get()
1116  {
1117  return array_;
1118  }
1119 
1120  const std::vector<std::shared_ptr<table>>& get() const
1121  {
1122  return array_;
1123  }
1124 
1128  void push_back(const std::shared_ptr<table>& val)
1129  {
1130  array_.push_back(val);
1131  }
1132 
1136  iterator insert(iterator position, const std::shared_ptr<table>& value)
1137  {
1138  return array_.insert(position, value);
1139  }
1140 
1145  {
1146  return array_.erase(position);
1147  }
1148 
1152  void clear()
1153  {
1154  array_.clear();
1155  }
1156 
1161  {
1162  array_.reserve(n);
1163  }
1164 
1170  bool is_inline() const
1171  {
1172  return is_inline_;
1173  }
1174 
1175  private:
1176 #if defined(CPPTOML_NO_RTTI)
1177  table_array(bool is_inline = false)
1178  : base(base_type::TABLE_ARRAY), is_inline_(is_inline)
1179  {
1180  // nothing
1181  }
1182 #else
1183  table_array(bool is_inline = false) : is_inline_(is_inline)
1184  {
1185  // nothing
1186  }
1187 #endif
1188 
1189  table_array(const table_array& obj) = delete;
1190  table_array& operator=(const table_array& rhs) = delete;
1191 
1192  std::vector<std::shared_ptr<table>> array_;
1193  const bool is_inline_ = false;
1194 };
1195 
1196 inline std::shared_ptr<table_array> make_table_array(bool is_inline)
1197 {
1198  struct make_shared_enabler : public table_array
1199  {
1200  make_shared_enabler(bool mse_is_inline) : table_array(mse_is_inline)
1201  {
1202  // nothing
1203  }
1204  };
1205 
1206  return std::make_shared<make_shared_enabler>(is_inline);
1207 }
1208 
1209 namespace detail
1210 {
1211 template <>
1212 inline std::shared_ptr<table_array> make_element<table_array>()
1213 {
1214  return make_table_array(true);
1215 }
1216 } // namespace detail
1217 
1218 // The below are overloads for fetching specific value types out of a value
1219 // where special casting behavior (like bounds checking) is desired
1220 
1221 template <class T>
1222 typename std::enable_if<!std::is_floating_point<T>::value
1223  && std::is_signed<T>::value,
1224  option<T>>::type
1225 get_impl(const std::shared_ptr<base>& elem)
1226 {
1227  if (auto v = elem->as<int64_t>())
1228  {
1229  if (v->get() < (std::numeric_limits<T>::min)())
1230  throw std::underflow_error{
1231  "T cannot represent the value requested in get"};
1232 
1233  if (v->get() > (std::numeric_limits<T>::max)())
1234  throw std::overflow_error{
1235  "T cannot represent the value requested in get"};
1236 
1237  return {static_cast<T>(v->get())};
1238  }
1239  else
1240  {
1241  return {};
1242  }
1243 }
1244 
1245 template <class T>
1246 typename std::enable_if<!std::is_same<T, bool>::value
1247  && std::is_unsigned<T>::value,
1248  option<T>>::type
1249 get_impl(const std::shared_ptr<base>& elem)
1250 {
1251  if (auto v = elem->as<int64_t>())
1252  {
1253  if (v->get() < 0)
1254  throw std::underflow_error{"T cannot store negative value in get"};
1255 
1256  if (static_cast<uint64_t>(v->get()) > (std::numeric_limits<T>::max)())
1257  throw std::overflow_error{
1258  "T cannot represent the value requested in get"};
1259 
1260  return {static_cast<T>(v->get())};
1261  }
1262  else
1263  {
1264  return {};
1265  }
1266 }
1267 
1268 template <class T>
1269 typename std::enable_if<!std::is_integral<T>::value
1270  || std::is_same<T, bool>::value,
1271  option<T>>::type
1272 get_impl(const std::shared_ptr<base>& elem)
1273 {
1274  if (auto v = elem->as<T>())
1275  {
1276  return {v->get()};
1277  }
1278  else
1279  {
1280  return {};
1281  }
1282 }
1283 
1287 class table : public base
1288 {
1289  public:
1290  friend class table_array;
1291  friend std::shared_ptr<table> make_table();
1292 
1293  std::shared_ptr<base> clone() const override;
1294 
1298  using iterator = string_to_base_map::iterator;
1299 
1303  using const_iterator = string_to_base_map::const_iterator;
1304 
1306  {
1307  return map_.begin();
1308  }
1309 
1311  {
1312  return map_.begin();
1313  }
1314 
1316  {
1317  return map_.end();
1318  }
1319 
1321  {
1322  return map_.end();
1323  }
1324 
1325  bool is_table() const override
1326  {
1327  return true;
1328  }
1329 
1330  bool empty() const
1331  {
1332  return map_.empty();
1333  }
1334 
1338  bool contains(const std::string& key) const
1339  {
1340  return map_.find(key) != map_.end();
1341  }
1342 
1348  bool contains_qualified(const std::string& key) const
1349  {
1350  return resolve_qualified(key);
1351  }
1352 
1357  std::shared_ptr<base> get(const std::string& key) const
1358  {
1359  return map_.at(key);
1360  }
1361 
1369  std::shared_ptr<base> get_qualified(const std::string& key) const
1370  {
1371  std::shared_ptr<base> p;
1372  resolve_qualified(key, &p);
1373  return p;
1374  }
1375 
1379  std::shared_ptr<table> get_table(const std::string& key) const
1380  {
1381  if (contains(key) && get(key)->is_table())
1382  return std::static_pointer_cast<table>(get(key));
1383  return nullptr;
1384  }
1385 
1390  std::shared_ptr<table> get_table_qualified(const std::string& key) const
1391  {
1392  if (contains_qualified(key) && get_qualified(key)->is_table())
1393  return std::static_pointer_cast<table>(get_qualified(key));
1394  return nullptr;
1395  }
1396 
1400  std::shared_ptr<array> get_array(const std::string& key) const
1401  {
1402  if (!contains(key))
1403  return nullptr;
1404  return get(key)->as_array();
1405  }
1406 
1410  std::shared_ptr<array> get_array_qualified(const std::string& key) const
1411  {
1412  if (!contains_qualified(key))
1413  return nullptr;
1414  return get_qualified(key)->as_array();
1415  }
1416 
1420  std::shared_ptr<table_array> get_table_array(const std::string& key) const
1421  {
1422  if (!contains(key))
1423  return nullptr;
1424  return get(key)->as_table_array();
1425  }
1426 
1431  std::shared_ptr<table_array>
1432  get_table_array_qualified(const std::string& key) const
1433  {
1434  if (!contains_qualified(key))
1435  return nullptr;
1436  return get_qualified(key)->as_table_array();
1437  }
1438 
1443  template <class T>
1444  option<T> get_as(const std::string& key) const
1445  {
1446  try
1447  {
1448  return get_impl<T>(get(key));
1449  }
1450  catch (const std::out_of_range&)
1451  {
1452  return {};
1453  }
1454  }
1455 
1461  template <class T>
1462  option<T> get_qualified_as(const std::string& key) const
1463  {
1464  try
1465  {
1466  return get_impl<T>(get_qualified(key));
1467  }
1468  catch (const std::out_of_range&)
1469  {
1470  return {};
1471  }
1472  }
1473 
1483  template <class T>
1484  inline typename array_of_trait<T>::return_type
1485  get_array_of(const std::string& key) const
1486  {
1487  if (auto v = get_array(key))
1488  {
1489  std::vector<T> result;
1490  result.reserve(v->get().size());
1491 
1492  for (const auto& b : v->get())
1493  {
1494  if (auto val = b->as<T>())
1495  result.push_back(val->get());
1496  else
1497  return {};
1498  }
1499  return {std::move(result)};
1500  }
1501 
1502  return {};
1503  }
1504 
1515  template <class T>
1516  inline typename array_of_trait<T>::return_type
1517  get_qualified_array_of(const std::string& key) const
1518  {
1519  if (auto v = get_array_qualified(key))
1520  {
1521  std::vector<T> result;
1522  result.reserve(v->get().size());
1523 
1524  for (const auto& b : v->get())
1525  {
1526  if (auto val = b->as<T>())
1527  result.push_back(val->get());
1528  else
1529  return {};
1530  }
1531  return {std::move(result)};
1532  }
1533 
1534  return {};
1535  }
1536 
1540  void insert(const std::string& key, const std::shared_ptr<base>& value)
1541  {
1542  map_[key] = value;
1543  }
1544 
1549  template <class T>
1550  void insert(const std::string& key, T&& val,
1551  typename value_traits<T>::type* = 0)
1552  {
1553  insert(key, make_value(std::forward<T>(val)));
1554  }
1555 
1559  void erase(const std::string& key)
1560  {
1561  map_.erase(key);
1562  }
1563 
1564  private:
1565 #if defined(CPPTOML_NO_RTTI)
1566  table() : base(base_type::TABLE)
1567  {
1568  // nothing
1569  }
1570 #else
1572  {
1573  // nothing
1574  }
1575 #endif
1576 
1577  table(const table& obj) = delete;
1578  table& operator=(const table& rhs) = delete;
1579 
1580  std::vector<std::string> split(const std::string& value,
1581  char separator) const
1582  {
1583  std::vector<std::string> result;
1584  std::string::size_type p = 0;
1585  std::string::size_type q;
1586  while ((q = value.find(separator, p)) != std::string::npos)
1587  {
1588  result.emplace_back(value, p, q - p);
1589  p = q + 1;
1590  }
1591  result.emplace_back(value, p);
1592  return result;
1593  }
1594 
1595  // If output parameter p is specified, fill it with the pointer to the
1596  // specified entry and throw std::out_of_range if it couldn't be found.
1597  //
1598  // Otherwise, just return true if the entry could be found or false
1599  // otherwise and do not throw.
1600  bool resolve_qualified(const std::string& key,
1601  std::shared_ptr<base>* p = nullptr) const
1602  {
1603  auto parts = split(key, '.');
1604  auto last_key = parts.back();
1605  parts.pop_back();
1606 
1607  auto cur_table = this;
1608  for (const auto& part : parts)
1609  {
1610  cur_table = cur_table->get_table(part).get();
1611  if (!cur_table)
1612  {
1613  if (!p)
1614  return false;
1615 
1616  throw std::out_of_range{key + " is not a valid key"};
1617  }
1618  }
1619 
1620  if (!p)
1621  return cur_table->map_.count(last_key) != 0;
1622 
1623  *p = cur_table->map_.at(last_key);
1624  return true;
1625  }
1626 
1628 };
1629 
1639 template <>
1640 inline typename array_of_trait<array>::return_type
1641 table::get_array_of<array>(const std::string& key) const
1642 {
1643  if (auto v = get_array(key))
1644  {
1645  std::vector<std::shared_ptr<array>> result;
1646  result.reserve(v->get().size());
1647 
1648  for (const auto& b : v->get())
1649  {
1650  if (auto val = b->as_array())
1651  result.push_back(val);
1652  else
1653  return {};
1654  }
1655 
1656  return {std::move(result)};
1657  }
1658 
1659  return {};
1660 }
1661 
1671 template <>
1672 inline typename array_of_trait<array>::return_type
1673 table::get_qualified_array_of<array>(const std::string& key) const
1674 {
1675  if (auto v = get_array_qualified(key))
1676  {
1677  std::vector<std::shared_ptr<array>> result;
1678  result.reserve(v->get().size());
1679 
1680  for (const auto& b : v->get())
1681  {
1682  if (auto val = b->as_array())
1683  result.push_back(val);
1684  else
1685  return {};
1686  }
1687 
1688  return {std::move(result)};
1689  }
1690 
1691  return {};
1692 }
1693 
1694 std::shared_ptr<table> make_table()
1695 {
1696  struct make_shared_enabler : public table
1697  {
1698  make_shared_enabler()
1699  {
1700  // nothing
1701  }
1702  };
1703 
1704  return std::make_shared<make_shared_enabler>();
1705 }
1706 
1707 namespace detail
1708 {
1709 template <>
1710 inline std::shared_ptr<table> make_element<table>()
1711 {
1712  return make_table();
1713 }
1714 } // namespace detail
1715 
1716 template <class T>
1717 std::shared_ptr<base> value<T>::clone() const
1718 {
1719  return make_value(data_);
1720 }
1721 
1722 inline std::shared_ptr<base> array::clone() const
1723 {
1724  auto result = make_array();
1725  result->reserve(values_.size());
1726  for (const auto& ptr : values_)
1727  result->values_.push_back(ptr->clone());
1728  return result;
1729 }
1730 
1731 inline std::shared_ptr<base> table_array::clone() const
1732 {
1733  auto result = make_table_array(is_inline());
1734  result->reserve(array_.size());
1735  for (const auto& ptr : array_)
1736  result->array_.push_back(ptr->clone()->as_table());
1737  return result;
1738 }
1739 
1740 inline std::shared_ptr<base> table::clone() const
1741 {
1742  auto result = make_table();
1743  for (const auto& pr : map_)
1744  result->insert(pr.first, pr.second->clone());
1745  return result;
1746 }
1747 
1751 class parse_exception : public std::runtime_error
1752 {
1753  public:
1754  parse_exception(const std::string& err) : std::runtime_error{err}
1755  {
1756  }
1757 
1758  parse_exception(const std::string& err, std::size_t line_number)
1759  : std::runtime_error{err + " at line " + std::to_string(line_number)}
1760  {
1761  }
1762 };
1763 
1764 inline bool is_number(char c)
1765 {
1766  return c >= '0' && c <= '9';
1767 }
1768 
1769 inline bool is_hex(char c)
1770 {
1771  return is_number(c) || (c >= 'a' && c <= 'f') || (c >= 'A' && c <= 'F');
1772 }
1773 
1777 template <class OnError>
1779 {
1780  public:
1781  consumer(std::string::iterator& it, const std::string::iterator& end,
1782  OnError&& on_error)
1783  : it_(it), end_(end), on_error_(std::forward<OnError>(on_error))
1784  {
1785  // nothing
1786  }
1787 
1788  void operator()(char c)
1789  {
1790  if (it_ == end_ || *it_ != c)
1791  on_error_();
1792  ++it_;
1793  }
1794 
1795  template <std::size_t N>
1796  void operator()(const char (&str)[N])
1797  {
1798  std::for_each(std::begin(str), std::end(str) - 1,
1799  [&](char c) { (*this)(c); });
1800  }
1801 
1802  void eat_or(char a, char b)
1803  {
1804  if (it_ == end_ || (*it_ != a && *it_ != b))
1805  on_error_();
1806  ++it_;
1807  }
1808 
1809  int eat_digits(int len)
1810  {
1811  int val = 0;
1812  for (int i = 0; i < len; ++i)
1813  {
1814  if (!is_number(*it_) || it_ == end_)
1815  on_error_();
1816  val = 10 * val + (*it_++ - '0');
1817  }
1818  return val;
1819  }
1820 
1821  void error()
1822  {
1823  on_error_();
1824  }
1825 
1826  private:
1827  std::string::iterator& it_;
1828  const std::string::iterator& end_;
1829  OnError on_error_;
1830 };
1831 
1832 template <class OnError>
1833 consumer<OnError> make_consumer(std::string::iterator& it,
1834  const std::string::iterator& end,
1835  OnError&& on_error)
1836 {
1837  return consumer<OnError>(it, end, std::forward<OnError>(on_error));
1838 }
1839 
1840 // replacement for std::getline to handle incorrectly line-ended files
1841 // https://stackoverflow.com/questions/6089231/getting-std-ifstream-to-handle-lf-cr-and-crlf
1842 namespace detail
1843 {
1844 inline std::istream& getline(std::istream& input, std::string& line)
1845 {
1846  line.clear();
1847 
1848  std::istream::sentry sentry{input, true};
1849  auto sb = input.rdbuf();
1850 
1851  while (true)
1852  {
1853  auto c = sb->sbumpc();
1854  if (c == '\r')
1855  {
1856  if (sb->sgetc() == '\n')
1857  c = sb->sbumpc();
1858  }
1859 
1860  if (c == '\n')
1861  return input;
1862 
1863  if (c == std::istream::traits_type::eof())
1864  {
1865  if (line.empty())
1866  input.setstate(std::ios::eofbit);
1867  return input;
1868  }
1869 
1870  line.push_back(static_cast<char>(c));
1871  }
1872 }
1873 } // namespace detail
1874 
1878 class parser
1879 {
1880  public:
1884  parser(std::istream& stream) : input_(stream)
1885  {
1886  // nothing
1887  }
1888 
1889  parser& operator=(const parser& parser) = delete;
1890 
1895  std::shared_ptr<table> parse()
1896  {
1897  std::shared_ptr<table> root = make_table();
1898 
1899  table* curr_table = root.get();
1900 
1901  while (detail::getline(input_, line_))
1902  {
1903  line_number_++;
1904  auto it = line_.begin();
1905  auto end = line_.end();
1906  consume_whitespace(it, end);
1907  if (it == end || *it == '#')
1908  continue;
1909  if (*it == '[')
1910  {
1911  curr_table = root.get();
1912  parse_table(it, end, curr_table);
1913  }
1914  else
1915  {
1916  parse_key_value(it, end, curr_table);
1917  consume_whitespace(it, end);
1918  eol_or_comment(it, end);
1919  }
1920  }
1921  return root;
1922  }
1923 
1924  private:
1925 #if defined _MSC_VER
1926  __declspec(noreturn)
1927 #elif defined __GNUC__
1928  __attribute__((noreturn))
1929 #endif
1930  void throw_parse_exception(const std::string& err)
1931  {
1932  throw parse_exception{err, line_number_};
1933  }
1934 
1935  void parse_table(std::string::iterator& it,
1936  const std::string::iterator& end, table*& curr_table)
1937  {
1938  // remove the beginning keytable marker
1939  ++it;
1940  if (it == end)
1941  throw_parse_exception("Unexpected end of table");
1942  if (*it == '[')
1943  parse_table_array(it, end, curr_table);
1944  else
1945  parse_single_table(it, end, curr_table);
1946  }
1947 
1948  void parse_single_table(std::string::iterator& it,
1949  const std::string::iterator& end,
1950  table*& curr_table)
1951  {
1952  if (it == end || *it == ']')
1953  throw_parse_exception("Table name cannot be empty");
1954 
1955  std::string full_table_name;
1956  bool inserted = false;
1957 
1958  auto key_end = [](char c) { return c == ']'; };
1959 
1960  auto key_part_handler = [&](const std::string& part) {
1961  if (part.empty())
1962  throw_parse_exception("Empty component of table name");
1963 
1964  if (!full_table_name.empty())
1965  full_table_name += '.';
1966  full_table_name += part;
1967 
1968  if (curr_table->contains(part))
1969  {
1970 #if !defined(__PGI)
1971  auto b = curr_table->get(part);
1972 #else
1973  // Workaround for PGI compiler
1974  std::shared_ptr<base> b = curr_table->get(part);
1975 #endif
1976  if (b->is_table())
1977  curr_table = static_cast<table*>(b.get());
1978  else if (b->is_table_array())
1979  curr_table = std::static_pointer_cast<table_array>(b)
1980  ->get()
1981  .back()
1982  .get();
1983  else
1984  throw_parse_exception("Key " + full_table_name
1985  + "already exists as a value");
1986  }
1987  else
1988  {
1989  inserted = true;
1990  curr_table->insert(part, make_table());
1991  curr_table = static_cast<table*>(curr_table->get(part).get());
1992  }
1993  };
1994 
1995  key_part_handler(parse_key(it, end, key_end, key_part_handler));
1996 
1997  if (it == end)
1998  throw_parse_exception(
1999  "Unterminated table declaration; did you forget a ']'?");
2000 
2001  if (*it != ']')
2002  {
2003  std::string errmsg{"Unexpected character in table definition: "};
2004  errmsg += '"';
2005  errmsg += *it;
2006  errmsg += '"';
2007  throw_parse_exception(errmsg);
2008  }
2009 
2010  // table already existed
2011  if (!inserted)
2012  {
2013  auto is_value
2014  = [](const std::pair<const std::string&,
2015  const std::shared_ptr<base>&>& p) {
2016  return p.second->is_value();
2017  };
2018 
2019  // if there are any values, we can't add values to this table
2020  // since it has already been defined. If there aren't any
2021  // values, then it was implicitly created by something like
2022  // [a.b]
2023  if (curr_table->empty()
2024  || std::any_of(curr_table->begin(), curr_table->end(),
2025  is_value))
2026  {
2027  throw_parse_exception("Redefinition of table "
2028  + full_table_name);
2029  }
2030  }
2031 
2032  ++it;
2033  consume_whitespace(it, end);
2034  eol_or_comment(it, end);
2035  }
2036 
2037  void parse_table_array(std::string::iterator& it,
2038  const std::string::iterator& end, table*& curr_table)
2039  {
2040  ++it;
2041  if (it == end || *it == ']')
2042  throw_parse_exception("Table array name cannot be empty");
2043 
2044  auto key_end = [](char c) { return c == ']'; };
2045 
2046  std::string full_ta_name;
2047  auto key_part_handler = [&](const std::string& part) {
2048  if (part.empty())
2049  throw_parse_exception("Empty component of table array name");
2050 
2051  if (!full_ta_name.empty())
2052  full_ta_name += '.';
2053  full_ta_name += part;
2054 
2055  if (curr_table->contains(part))
2056  {
2057 #if !defined(__PGI)
2058  auto b = curr_table->get(part);
2059 #else
2060  // Workaround for PGI compiler
2061  std::shared_ptr<base> b = curr_table->get(part);
2062 #endif
2063 
2064  // if this is the end of the table array name, add an
2065  // element to the table array that we just looked up,
2066  // provided it was not declared inline
2067  if (it != end && *it == ']')
2068  {
2069  if (!b->is_table_array())
2070  {
2071  throw_parse_exception("Key " + full_ta_name
2072  + " is not a table array");
2073  }
2074 
2075  auto v = b->as_table_array();
2076 
2077  if (v->is_inline())
2078  {
2079  throw_parse_exception("Static array " + full_ta_name
2080  + " cannot be appended to");
2081  }
2082 
2083  v->get().push_back(make_table());
2084  curr_table = v->get().back().get();
2085  }
2086  // otherwise, just keep traversing down the key name
2087  else
2088  {
2089  if (b->is_table())
2090  curr_table = static_cast<table*>(b.get());
2091  else if (b->is_table_array())
2092  curr_table = std::static_pointer_cast<table_array>(b)
2093  ->get()
2094  .back()
2095  .get();
2096  else
2097  throw_parse_exception("Key " + full_ta_name
2098  + " already exists as a value");
2099  }
2100  }
2101  else
2102  {
2103  // if this is the end of the table array name, add a new
2104  // table array and a new table inside that array for us to
2105  // add keys to next
2106  if (it != end && *it == ']')
2107  {
2108  curr_table->insert(part, make_table_array());
2109  auto arr = std::static_pointer_cast<table_array>(
2110  curr_table->get(part));
2111  arr->get().push_back(make_table());
2112  curr_table = arr->get().back().get();
2113  }
2114  // otherwise, create the implicitly defined table and move
2115  // down to it
2116  else
2117  {
2118  curr_table->insert(part, make_table());
2119  curr_table
2120  = static_cast<table*>(curr_table->get(part).get());
2121  }
2122  }
2123  };
2124 
2125  key_part_handler(parse_key(it, end, key_end, key_part_handler));
2126 
2127  // consume the last "]]"
2128  auto eat = make_consumer(it, end, [this]() {
2129  throw_parse_exception("Unterminated table array name");
2130  });
2131  eat(']');
2132  eat(']');
2133 
2134  consume_whitespace(it, end);
2135  eol_or_comment(it, end);
2136  }
2137 
2138  void parse_key_value(std::string::iterator& it, std::string::iterator& end,
2139  table* curr_table)
2140  {
2141  auto key_end = [](char c) { return c == '='; };
2142 
2143  auto key_part_handler = [&](const std::string& part) {
2144  // two cases: this key part exists already, in which case it must
2145  // be a table, or it doesn't exist in which case we must create
2146  // an implicitly defined table
2147  if (curr_table->contains(part))
2148  {
2149  auto val = curr_table->get(part);
2150  if (val->is_table())
2151  {
2152  curr_table = static_cast<table*>(val.get());
2153  }
2154  else
2155  {
2156  throw_parse_exception("Key " + part
2157  + " already exists as a value");
2158  }
2159  }
2160  else
2161  {
2162  auto newtable = make_table();
2163  curr_table->insert(part, newtable);
2164  curr_table = newtable.get();
2165  }
2166  };
2167 
2168  auto key = parse_key(it, end, key_end, key_part_handler);
2169 
2170  if (curr_table->contains(key))
2171  throw_parse_exception("Key " + key + " already present");
2172  if (it == end || *it != '=')
2173  throw_parse_exception("Value must follow after a '='");
2174  ++it;
2175  consume_whitespace(it, end);
2176  curr_table->insert(key, parse_value(it, end));
2177  consume_whitespace(it, end);
2178  }
2179 
2180  template <class KeyEndFinder, class KeyPartHandler>
2181  std::string
2182  parse_key(std::string::iterator& it, const std::string::iterator& end,
2183  KeyEndFinder&& key_end, KeyPartHandler&& key_part_handler)
2184  {
2185  // parse the key as a series of one or more simple-keys joined with '.'
2186  while (it != end && !key_end(*it))
2187  {
2188  auto part = parse_simple_key(it, end);
2189  consume_whitespace(it, end);
2190 
2191  if (it == end || key_end(*it))
2192  {
2193  return part;
2194  }
2195 
2196  if (*it != '.')
2197  {
2198  std::string errmsg{"Unexpected character in key: "};
2199  errmsg += '"';
2200  errmsg += *it;
2201  errmsg += '"';
2202  throw_parse_exception(errmsg);
2203  }
2204 
2205  key_part_handler(part);
2206 
2207  // consume the dot
2208  ++it;
2209  }
2210 
2211  throw_parse_exception("Unexpected end of key");
2212  }
2213 
2214  std::string parse_simple_key(std::string::iterator& it,
2215  const std::string::iterator& end)
2216  {
2217  consume_whitespace(it, end);
2218 
2219  if (it == end)
2220  throw_parse_exception("Unexpected end of key (blank key?)");
2221 
2222  if (*it == '"' || *it == '\'')
2223  {
2224  return string_literal(it, end, *it);
2225  }
2226  else
2227  {
2228  auto bke = std::find_if(it, end, [](char c) {
2229  return c == '.' || c == '=' || c == ']';
2230  });
2231  return parse_bare_key(it, bke);
2232  }
2233  }
2234 
2235  std::string parse_bare_key(std::string::iterator& it,
2236  const std::string::iterator& end)
2237  {
2238  if (it == end)
2239  {
2240  throw_parse_exception("Bare key missing name");
2241  }
2242 
2243  auto key_end = end;
2244  --key_end;
2245  consume_backwards_whitespace(key_end, it);
2246  ++key_end;
2247  std::string key{it, key_end};
2248 
2249  if (std::find(it, key_end, '#') != key_end)
2250  {
2251  throw_parse_exception("Bare key " + key + " cannot contain #");
2252  }
2253 
2254  if (std::find_if(it, key_end,
2255  [](char c) { return c == ' ' || c == '\t'; })
2256  != key_end)
2257  {
2258  throw_parse_exception("Bare key " + key
2259  + " cannot contain whitespace");
2260  }
2261 
2262  if (std::find_if(it, key_end,
2263  [](char c) { return c == '[' || c == ']'; })
2264  != key_end)
2265  {
2266  throw_parse_exception("Bare key " + key
2267  + " cannot contain '[' or ']'");
2268  }
2269 
2270  it = end;
2271  return key;
2272  }
2273 
2274  enum class parse_type
2275  {
2276  STRING = 1,
2277  LOCAL_TIME,
2278  LOCAL_DATE,
2279  LOCAL_DATETIME,
2280  OFFSET_DATETIME,
2281  INT,
2282  FLOAT,
2283  BOOL,
2284  ARRAY,
2285  INLINE_TABLE
2286  };
2287 
2288  std::shared_ptr<base> parse_value(std::string::iterator& it,
2289  std::string::iterator& end)
2290  {
2291  parse_type type = determine_value_type(it, end);
2292  switch (type)
2293  {
2294  case parse_type::STRING:
2295  return parse_string(it, end);
2296  case parse_type::LOCAL_TIME:
2297  return parse_time(it, end);
2298  case parse_type::LOCAL_DATE:
2299  case parse_type::LOCAL_DATETIME:
2300  case parse_type::OFFSET_DATETIME:
2301  return parse_date(it, end);
2302  case parse_type::INT:
2303  case parse_type::FLOAT:
2304  return parse_number(it, end);
2305  case parse_type::BOOL:
2306  return parse_bool(it, end);
2307  case parse_type::ARRAY:
2308  return parse_array(it, end);
2309  case parse_type::INLINE_TABLE:
2310  return parse_inline_table(it, end);
2311  default:
2312  throw_parse_exception("Failed to parse value");
2313  }
2314  }
2315 
2316  parse_type determine_value_type(const std::string::iterator& it,
2317  const std::string::iterator& end)
2318  {
2319  if (it == end)
2320  {
2321  throw_parse_exception("Failed to parse value type");
2322  }
2323  if (*it == '"' || *it == '\'')
2324  {
2325  return parse_type::STRING;
2326  }
2327  else if (is_time(it, end))
2328  {
2329  return parse_type::LOCAL_TIME;
2330  }
2331  else if (auto dtype = date_type(it, end))
2332  {
2333  return *dtype;
2334  }
2335  else if (is_number(*it) || *it == '-' || *it == '+'
2336  || (*it == 'i' && it + 1 != end && it[1] == 'n'
2337  && it + 2 != end && it[2] == 'f')
2338  || (*it == 'n' && it + 1 != end && it[1] == 'a'
2339  && it + 2 != end && it[2] == 'n'))
2340  {
2341  return determine_number_type(it, end);
2342  }
2343  else if (*it == 't' || *it == 'f')
2344  {
2345  return parse_type::BOOL;
2346  }
2347  else if (*it == '[')
2348  {
2349  return parse_type::ARRAY;
2350  }
2351  else if (*it == '{')
2352  {
2353  return parse_type::INLINE_TABLE;
2354  }
2355  throw_parse_exception("Failed to parse value type");
2356  }
2357 
2358  parse_type determine_number_type(const std::string::iterator& it,
2359  const std::string::iterator& end)
2360  {
2361  // determine if we are an integer or a float
2362  auto check_it = it;
2363  if (*check_it == '-' || *check_it == '+')
2364  ++check_it;
2365 
2366  if (check_it == end)
2367  throw_parse_exception("Malformed number");
2368 
2369  if (*check_it == 'i' || *check_it == 'n')
2370  return parse_type::FLOAT;
2371 
2372  while (check_it != end && is_number(*check_it))
2373  ++check_it;
2374  if (check_it != end && *check_it == '.')
2375  {
2376  ++check_it;
2377  while (check_it != end && is_number(*check_it))
2378  ++check_it;
2379  return parse_type::FLOAT;
2380  }
2381  else
2382  {
2383  return parse_type::INT;
2384  }
2385  }
2386 
2387  std::shared_ptr<value<std::string>> parse_string(std::string::iterator& it,
2388  std::string::iterator& end)
2389  {
2390  auto delim = *it;
2391  assert(delim == '"' || delim == '\'');
2392 
2393  // end is non-const here because we have to be able to potentially
2394  // parse multiple lines in a string, not just one
2395  auto check_it = it;
2396  ++check_it;
2397  if (check_it != end && *check_it == delim)
2398  {
2399  ++check_it;
2400  if (check_it != end && *check_it == delim)
2401  {
2402  it = ++check_it;
2403  return parse_multiline_string(it, end, delim);
2404  }
2405  }
2406  return make_value<std::string>(string_literal(it, end, delim));
2407  }
2408 
2409  std::shared_ptr<value<std::string>>
2410  parse_multiline_string(std::string::iterator& it,
2411  std::string::iterator& end, char delim)
2412  {
2413  std::stringstream ss;
2414 
2415  auto is_ws = [](char c) { return c == ' ' || c == '\t'; };
2416 
2417  bool consuming = false;
2418  std::shared_ptr<value<std::string>> ret;
2419 
2420  auto handle_line = [&](std::string::iterator& local_it,
2421  std::string::iterator& local_end) {
2422  if (consuming)
2423  {
2424  local_it = std::find_if_not(local_it, local_end, is_ws);
2425 
2426  // whole line is whitespace
2427  if (local_it == local_end)
2428  return;
2429  }
2430 
2431  consuming = false;
2432 
2433  while (local_it != local_end)
2434  {
2435  // handle escaped characters
2436  if (delim == '"' && *local_it == '\\')
2437  {
2438  auto check = local_it;
2439  // check if this is an actual escape sequence or a
2440  // whitespace escaping backslash
2441  ++check;
2442  consume_whitespace(check, local_end);
2443  if (check == local_end)
2444  {
2445  consuming = true;
2446  break;
2447  }
2448 
2449  ss << parse_escape_code(local_it, local_end);
2450  continue;
2451  }
2452 
2453  // if we can end the string
2454  if (std::distance(local_it, local_end) >= 3)
2455  {
2456  auto check = local_it;
2457  // check for """
2458  if (*check++ == delim && *check++ == delim
2459  && *check++ == delim)
2460  {
2461  local_it = check;
2462  ret = make_value<std::string>(ss.str());
2463  break;
2464  }
2465  }
2466 
2467  ss << *local_it++;
2468  }
2469  };
2470 
2471  // handle the remainder of the current line
2472  handle_line(it, end);
2473  if (ret)
2474  return ret;
2475 
2476  // start eating lines
2477  while (detail::getline(input_, line_))
2478  {
2479  ++line_number_;
2480 
2481  it = line_.begin();
2482  end = line_.end();
2483 
2484  handle_line(it, end);
2485 
2486  if (ret)
2487  return ret;
2488 
2489  if (!consuming)
2490  ss << std::endl;
2491  }
2492 
2493  throw_parse_exception("Unterminated multi-line basic string");
2494  }
2495 
2496  std::string string_literal(std::string::iterator& it,
2497  const std::string::iterator& end, char delim)
2498  {
2499  ++it;
2500  std::string val;
2501  while (it != end)
2502  {
2503  // handle escaped characters
2504  if (delim == '"' && *it == '\\')
2505  {
2506  val += parse_escape_code(it, end);
2507  }
2508  else if (*it == delim)
2509  {
2510  ++it;
2511  consume_whitespace(it, end);
2512  return val;
2513  }
2514  else
2515  {
2516  val += *it++;
2517  }
2518  }
2519  throw_parse_exception("Unterminated string literal");
2520  }
2521 
2522  std::string parse_escape_code(std::string::iterator& it,
2523  const std::string::iterator& end)
2524  {
2525  ++it;
2526  if (it == end)
2527  throw_parse_exception("Invalid escape sequence");
2528  char value;
2529  if (*it == 'b')
2530  {
2531  value = '\b';
2532  }
2533  else if (*it == 't')
2534  {
2535  value = '\t';
2536  }
2537  else if (*it == 'n')
2538  {
2539  value = '\n';
2540  }
2541  else if (*it == 'f')
2542  {
2543  value = '\f';
2544  }
2545  else if (*it == 'r')
2546  {
2547  value = '\r';
2548  }
2549  else if (*it == '"')
2550  {
2551  value = '"';
2552  }
2553  else if (*it == '\\')
2554  {
2555  value = '\\';
2556  }
2557  else if (*it == 'u' || *it == 'U')
2558  {
2559  return parse_unicode(it, end);
2560  }
2561  else
2562  {
2563  throw_parse_exception("Invalid escape sequence");
2564  }
2565  ++it;
2566  return std::string(1, value);
2567  }
2568 
2569  std::string parse_unicode(std::string::iterator& it,
2570  const std::string::iterator& end)
2571  {
2572  bool large = *it++ == 'U';
2573  auto codepoint = parse_hex(it, end, large ? 0x10000000 : 0x1000);
2574 
2575  if ((codepoint > 0xd7ff && codepoint < 0xe000) || codepoint > 0x10ffff)
2576  {
2577  throw_parse_exception(
2578  "Unicode escape sequence is not a Unicode scalar value");
2579  }
2580 
2581  std::string result;
2582  // See Table 3-6 of the Unicode standard
2583  if (codepoint <= 0x7f)
2584  {
2585  // 1-byte codepoints: 00000000 0xxxxxxx
2586  // repr: 0xxxxxxx
2587  result += static_cast<char>(codepoint & 0x7f);
2588  }
2589  else if (codepoint <= 0x7ff)
2590  {
2591  // 2-byte codepoints: 00000yyy yyxxxxxx
2592  // repr: 110yyyyy 10xxxxxx
2593  //
2594  // 0x1f = 00011111
2595  // 0xc0 = 11000000
2596  //
2597  result += static_cast<char>(0xc0 | ((codepoint >> 6) & 0x1f));
2598  //
2599  // 0x80 = 10000000
2600  // 0x3f = 00111111
2601  //
2602  result += static_cast<char>(0x80 | (codepoint & 0x3f));
2603  }
2604  else if (codepoint <= 0xffff)
2605  {
2606  // 3-byte codepoints: zzzzyyyy yyxxxxxx
2607  // repr: 1110zzzz 10yyyyyy 10xxxxxx
2608  //
2609  // 0xe0 = 11100000
2610  // 0x0f = 00001111
2611  //
2612  result += static_cast<char>(0xe0 | ((codepoint >> 12) & 0x0f));
2613  result += static_cast<char>(0x80 | ((codepoint >> 6) & 0x1f));
2614  result += static_cast<char>(0x80 | (codepoint & 0x3f));
2615  }
2616  else
2617  {
2618  // 4-byte codepoints: 000uuuuu zzzzyyyy yyxxxxxx
2619  // repr: 11110uuu 10uuzzzz 10yyyyyy 10xxxxxx
2620  //
2621  // 0xf0 = 11110000
2622  // 0x07 = 00000111
2623  //
2624  result += static_cast<char>(0xf0 | ((codepoint >> 18) & 0x07));
2625  result += static_cast<char>(0x80 | ((codepoint >> 12) & 0x3f));
2626  result += static_cast<char>(0x80 | ((codepoint >> 6) & 0x3f));
2627  result += static_cast<char>(0x80 | (codepoint & 0x3f));
2628  }
2629  return result;
2630  }
2631 
2632  uint32_t parse_hex(std::string::iterator& it,
2633  const std::string::iterator& end, uint32_t place)
2634  {
2635  uint32_t value = 0;
2636  while (place > 0)
2637  {
2638  if (it == end)
2639  throw_parse_exception("Unexpected end of unicode sequence");
2640 
2641  if (!is_hex(*it))
2642  throw_parse_exception("Invalid unicode escape sequence");
2643 
2644  value += place * hex_to_digit(*it++);
2645  place /= 16;
2646  }
2647  return value;
2648  }
2649 
2650  uint32_t hex_to_digit(char c)
2651  {
2652  if (is_number(c))
2653  return static_cast<uint32_t>(c - '0');
2654  return 10
2655  + static_cast<uint32_t>(c
2656  - ((c >= 'a' && c <= 'f') ? 'a' : 'A'));
2657  }
2658 
2659  std::shared_ptr<base> parse_number(std::string::iterator& it,
2660  const std::string::iterator& end)
2661  {
2662  auto check_it = it;
2663  auto check_end = find_end_of_number(it, end);
2664 
2665  auto eat_sign = [&]() {
2666  if (check_it != end && (*check_it == '-' || *check_it == '+'))
2667  ++check_it;
2668  };
2669 
2670  auto check_no_leading_zero = [&]() {
2671  if (check_it != end && *check_it == '0' && check_it + 1 != check_end
2672  && check_it[1] != '.')
2673  {
2674  throw_parse_exception("Numbers may not have leading zeros");
2675  }
2676  };
2677 
2678  auto eat_digits = [&](bool (*check_char)(char)) {
2679  auto beg = check_it;
2680  while (check_it != end && check_char(*check_it))
2681  {
2682  ++check_it;
2683  if (check_it != end && *check_it == '_')
2684  {
2685  ++check_it;
2686  if (check_it == end || !check_char(*check_it))
2687  throw_parse_exception("Malformed number");
2688  }
2689  }
2690 
2691  if (check_it == beg)
2692  throw_parse_exception("Malformed number");
2693  };
2694 
2695  auto eat_hex = [&]() { eat_digits(&is_hex); };
2696 
2697  auto eat_numbers = [&]() { eat_digits(&is_number); };
2698 
2699  if (check_it != end && *check_it == '0' && check_it + 1 != check_end
2700  && (check_it[1] == 'x' || check_it[1] == 'o' || check_it[1] == 'b'))
2701  {
2702  ++check_it;
2703  char base = *check_it;
2704  ++check_it;
2705  if (base == 'x')
2706  {
2707  eat_hex();
2708  return parse_int(it, check_it, 16);
2709  }
2710  else if (base == 'o')
2711  {
2712  auto start = check_it;
2713  eat_numbers();
2714  auto val = parse_int(start, check_it, 8, "0");
2715  it = start;
2716  return val;
2717  }
2718  else // if (base == 'b')
2719  {
2720  auto start = check_it;
2721  eat_numbers();
2722  auto val = parse_int(start, check_it, 2);
2723  it = start;
2724  return val;
2725  }
2726  }
2727 
2728  eat_sign();
2729  check_no_leading_zero();
2730 
2731  if (check_it != end && check_it + 1 != end && check_it + 2 != end)
2732  {
2733  if (check_it[0] == 'i' && check_it[1] == 'n' && check_it[2] == 'f')
2734  {
2735  auto val = std::numeric_limits<double>::infinity();
2736  if (*it == '-')
2737  val = -val;
2738  it = check_it + 3;
2739  return make_value(val);
2740  }
2741  else if (check_it[0] == 'n' && check_it[1] == 'a'
2742  && check_it[2] == 'n')
2743  {
2744  auto val = std::numeric_limits<double>::quiet_NaN();
2745  if (*it == '-')
2746  val = -val;
2747  it = check_it + 3;
2748  return make_value(val);
2749  }
2750  }
2751 
2752  eat_numbers();
2753 
2754  if (check_it != end
2755  && (*check_it == '.' || *check_it == 'e' || *check_it == 'E'))
2756  {
2757  bool is_exp = *check_it == 'e' || *check_it == 'E';
2758 
2759  ++check_it;
2760  if (check_it == end)
2761  throw_parse_exception("Floats must have trailing digits");
2762 
2763  auto eat_exp = [&]() {
2764  eat_sign();
2765  check_no_leading_zero();
2766  eat_numbers();
2767  };
2768 
2769  if (is_exp)
2770  eat_exp();
2771  else
2772  eat_numbers();
2773 
2774  if (!is_exp && check_it != end
2775  && (*check_it == 'e' || *check_it == 'E'))
2776  {
2777  ++check_it;
2778  eat_exp();
2779  }
2780 
2781  return parse_float(it, check_it);
2782  }
2783  else
2784  {
2785  return parse_int(it, check_it);
2786  }
2787  }
2788 
2789  std::shared_ptr<value<int64_t>> parse_int(std::string::iterator& it,
2790  const std::string::iterator& end,
2791  int base = 10,
2792  const char* prefix = "")
2793  {
2794  std::string v{it, end};
2795  v = prefix + v;
2796  v.erase(std::remove(v.begin(), v.end(), '_'), v.end());
2797  it = end;
2798  try
2799  {
2800  return make_value<int64_t>(std::stoll(v, nullptr, base));
2801  }
2802  catch (const std::invalid_argument& ex)
2803  {
2804  throw_parse_exception("Malformed number (invalid argument: "
2805  + std::string{ex.what()} + ")");
2806  }
2807  catch (const std::out_of_range& ex)
2808  {
2809  throw_parse_exception("Malformed number (out of range: "
2810  + std::string{ex.what()} + ")");
2811  }
2812  }
2813 
2814  std::shared_ptr<value<double>> parse_float(std::string::iterator& it,
2815  const std::string::iterator& end)
2816  {
2817  std::string v{it, end};
2818  v.erase(std::remove(v.begin(), v.end(), '_'), v.end());
2819  it = end;
2820  char decimal_point = std::localeconv()->decimal_point[0];
2821  std::replace(v.begin(), v.end(), '.', decimal_point);
2822  try
2823  {
2824  return make_value<double>(std::stod(v));
2825  }
2826  catch (const std::invalid_argument& ex)
2827  {
2828  throw_parse_exception("Malformed number (invalid argument: "
2829  + std::string{ex.what()} + ")");
2830  }
2831  catch (const std::out_of_range& ex)
2832  {
2833  throw_parse_exception("Malformed number (out of range: "
2834  + std::string{ex.what()} + ")");
2835  }
2836  }
2837 
2838  std::shared_ptr<value<bool>> parse_bool(std::string::iterator& it,
2839  const std::string::iterator& end)
2840  {
2841  auto eat = make_consumer(it, end, [this]() {
2842  throw_parse_exception("Attempted to parse invalid boolean value");
2843  });
2844 
2845  if (*it == 't')
2846  {
2847  eat("true");
2848  return make_value<bool>(true);
2849  }
2850  else if (*it == 'f')
2851  {
2852  eat("false");
2853  return make_value<bool>(false);
2854  }
2855 
2856  eat.error();
2857  return nullptr;
2858  }
2859 
2860  std::string::iterator find_end_of_number(std::string::iterator it,
2861  std::string::iterator end)
2862  {
2863  auto ret = std::find_if(it, end, [](char c) {
2864  return !is_number(c) && c != '_' && c != '.' && c != 'e' && c != 'E'
2865  && c != '-' && c != '+' && c != 'x' && c != 'o' && c != 'b';
2866  });
2867  if (ret != end && ret + 1 != end && ret + 2 != end)
2868  {
2869  if ((ret[0] == 'i' && ret[1] == 'n' && ret[2] == 'f')
2870  || (ret[0] == 'n' && ret[1] == 'a' && ret[2] == 'n'))
2871  {
2872  ret = ret + 3;
2873  }
2874  }
2875  return ret;
2876  }
2877 
2878  std::string::iterator find_end_of_date(std::string::iterator it,
2879  std::string::iterator end)
2880  {
2881  auto end_of_date = std::find_if(it, end, [](char c) {
2882  return !is_number(c) && c != '-';
2883  });
2884  if (end_of_date != end && *end_of_date == ' ' && end_of_date + 1 != end
2885  && is_number(end_of_date[1]))
2886  end_of_date++;
2887  return std::find_if(end_of_date, end, [](char c) {
2888  return !is_number(c) && c != 'T' && c != 'Z' && c != ':'
2889  && c != '-' && c != '+' && c != '.';
2890  });
2891  }
2892 
2893  std::string::iterator find_end_of_time(std::string::iterator it,
2894  std::string::iterator end)
2895  {
2896  return std::find_if(it, end, [](char c) {
2897  return !is_number(c) && c != ':' && c != '.';
2898  });
2899  }
2900 
2901  local_time read_time(std::string::iterator& it,
2902  const std::string::iterator& end)
2903  {
2904  auto time_end = find_end_of_time(it, end);
2905 
2906  auto eat = make_consumer(
2907  it, time_end, [&]() { throw_parse_exception("Malformed time"); });
2908 
2909  local_time ltime;
2910 
2911  ltime.hour = eat.eat_digits(2);
2912  eat(':');
2913  ltime.minute = eat.eat_digits(2);
2914  eat(':');
2915  ltime.second = eat.eat_digits(2);
2916 
2917  int power = 100000;
2918  if (it != time_end && *it == '.')
2919  {
2920  ++it;
2921  while (it != time_end && is_number(*it))
2922  {
2923  ltime.microsecond += power * (*it++ - '0');
2924  power /= 10;
2925  }
2926  }
2927 
2928  if (it != time_end)
2929  throw_parse_exception("Malformed time");
2930 
2931  return ltime;
2932  }
2933 
2934  std::shared_ptr<value<local_time>>
2935  parse_time(std::string::iterator& it, const std::string::iterator& end)
2936  {
2937  return make_value(read_time(it, end));
2938  }
2939 
2940  std::shared_ptr<base> parse_date(std::string::iterator& it,
2941  const std::string::iterator& end)
2942  {
2943  auto date_end = find_end_of_date(it, end);
2944 
2945  auto eat = make_consumer(
2946  it, date_end, [&]() { throw_parse_exception("Malformed date"); });
2947 
2948  local_date ldate;
2949  ldate.year = eat.eat_digits(4);
2950  eat('-');
2951  ldate.month = eat.eat_digits(2);
2952  eat('-');
2953  ldate.day = eat.eat_digits(2);
2954 
2955  if (it == date_end)
2956  return make_value(ldate);
2957 
2958  eat.eat_or('T', ' ');
2959 
2960  local_datetime ldt;
2961  static_cast<local_date&>(ldt) = ldate;
2962  static_cast<local_time&>(ldt) = read_time(it, date_end);
2963 
2964  if (it == date_end)
2965  return make_value(ldt);
2966 
2967  offset_datetime dt;
2968  static_cast<local_datetime&>(dt) = ldt;
2969 
2970  int hoff = 0;
2971  int moff = 0;
2972  if (*it == '+' || *it == '-')
2973  {
2974  auto plus = *it == '+';
2975  ++it;
2976 
2977  hoff = eat.eat_digits(2);
2978  dt.hour_offset = (plus) ? hoff : -hoff;
2979  eat(':');
2980  moff = eat.eat_digits(2);
2981  dt.minute_offset = (plus) ? moff : -moff;
2982  }
2983  else if (*it == 'Z')
2984  {
2985  ++it;
2986  }
2987 
2988  if (it != date_end)
2989  throw_parse_exception("Malformed date");
2990 
2991  return make_value(dt);
2992  }
2993 
2994  std::shared_ptr<base> parse_array(std::string::iterator& it,
2995  std::string::iterator& end)
2996  {
2997  // this gets ugly because of the "homogeneity" restriction:
2998  // arrays can either be of only one type, or contain arrays
2999  // (each of those arrays could be of different types, though)
3000  //
3001  // because of the latter portion, we don't really have a choice
3002  // but to represent them as arrays of base values...
3003  ++it;
3004 
3005  // ugh---have to read the first value to determine array type...
3006  skip_whitespace_and_comments(it, end);
3007 
3008  // edge case---empty array
3009  if (*it == ']')
3010  {
3011  ++it;
3012  return make_array();
3013  }
3014 
3015  auto val_end = std::find_if(
3016  it, end, [](char c) { return c == ',' || c == ']' || c == '#'; });
3017  parse_type type = determine_value_type(it, val_end);
3018  switch (type)
3019  {
3020  case parse_type::STRING:
3021  return parse_value_array<std::string>(it, end);
3022  case parse_type::LOCAL_TIME:
3023  return parse_value_array<local_time>(it, end);
3024  case parse_type::LOCAL_DATE:
3025  return parse_value_array<local_date>(it, end);
3026  case parse_type::LOCAL_DATETIME:
3027  return parse_value_array<local_datetime>(it, end);
3028  case parse_type::OFFSET_DATETIME:
3029  return parse_value_array<offset_datetime>(it, end);
3030  case parse_type::INT:
3031  return parse_value_array<int64_t>(it, end);
3032  case parse_type::FLOAT:
3033  return parse_value_array<double>(it, end);
3034  case parse_type::BOOL:
3035  return parse_value_array<bool>(it, end);
3036  case parse_type::ARRAY:
3037  return parse_object_array<array>(&parser::parse_array, '[', it,
3038  end);
3039  case parse_type::INLINE_TABLE:
3040  return parse_object_array<table_array>(
3041  &parser::parse_inline_table, '{', it, end);
3042  default:
3043  throw_parse_exception("Unable to parse array");
3044  }
3045  }
3046 
3047  template <class Value>
3048  std::shared_ptr<array> parse_value_array(std::string::iterator& it,
3049  std::string::iterator& end)
3050  {
3051  auto arr = make_array();
3052  while (it != end && *it != ']')
3053  {
3054  auto val = parse_value(it, end);
3055  if (auto v = val->as<Value>())
3056  arr->get().push_back(val);
3057  else
3058  throw_parse_exception("Arrays must be homogeneous");
3059  skip_whitespace_and_comments(it, end);
3060  if (*it != ',')
3061  break;
3062  ++it;
3063  skip_whitespace_and_comments(it, end);
3064  }
3065  if (it != end)
3066  ++it;
3067  return arr;
3068  }
3069 
3070  template <class Object, class Function>
3071  std::shared_ptr<Object> parse_object_array(Function&& fun, char delim,
3072  std::string::iterator& it,
3073  std::string::iterator& end)
3074  {
3075  auto arr = detail::make_element<Object>();
3076 
3077  while (it != end && *it != ']')
3078  {
3079  if (*it != delim)
3080  throw_parse_exception("Unexpected character in array");
3081 
3082  arr->get().push_back(((*this).*fun)(it, end));
3083  skip_whitespace_and_comments(it, end);
3084 
3085  if (it == end || *it != ',')
3086  break;
3087 
3088  ++it;
3089  skip_whitespace_and_comments(it, end);
3090  }
3091 
3092  if (it == end || *it != ']')
3093  throw_parse_exception("Unterminated array");
3094 
3095  ++it;
3096  return arr;
3097  }
3098 
3099  std::shared_ptr<table> parse_inline_table(std::string::iterator& it,
3100  std::string::iterator& end)
3101  {
3102  auto tbl = make_table();
3103  do
3104  {
3105  ++it;
3106  if (it == end)
3107  throw_parse_exception("Unterminated inline table");
3108 
3109  consume_whitespace(it, end);
3110  if (it != end && *it != '}')
3111  {
3112  parse_key_value(it, end, tbl.get());
3113  consume_whitespace(it, end);
3114  }
3115  } while (*it == ',');
3116 
3117  if (it == end || *it != '}')
3118  throw_parse_exception("Unterminated inline table");
3119 
3120  ++it;
3121  consume_whitespace(it, end);
3122 
3123  return tbl;
3124  }
3125 
3126  void skip_whitespace_and_comments(std::string::iterator& start,
3127  std::string::iterator& end)
3128  {
3129  consume_whitespace(start, end);
3130  while (start == end || *start == '#')
3131  {
3132  if (!detail::getline(input_, line_))
3133  throw_parse_exception("Unclosed array");
3134  line_number_++;
3135  start = line_.begin();
3136  end = line_.end();
3137  consume_whitespace(start, end);
3138  }
3139  }
3140 
3141  void consume_whitespace(std::string::iterator& it,
3142  const std::string::iterator& end)
3143  {
3144  while (it != end && (*it == ' ' || *it == '\t'))
3145  ++it;
3146  }
3147 
3148  void consume_backwards_whitespace(std::string::iterator& back,
3149  const std::string::iterator& front)
3150  {
3151  while (back != front && (*back == ' ' || *back == '\t'))
3152  --back;
3153  }
3154 
3155  void eol_or_comment(const std::string::iterator& it,
3156  const std::string::iterator& end)
3157  {
3158  if (it != end && *it != '#')
3159  throw_parse_exception("Unidentified trailing character '"
3160  + std::string{*it}
3161  + "'---did you forget a '#'?");
3162  }
3163 
3164  bool is_time(const std::string::iterator& it,
3165  const std::string::iterator& end)
3166  {
3167  auto time_end = find_end_of_time(it, end);
3168  auto len = std::distance(it, time_end);
3169 
3170  if (len < 8)
3171  return false;
3172 
3173  if (it[2] != ':' || it[5] != ':')
3174  return false;
3175 
3176  if (len > 8)
3177  return it[8] == '.' && len > 9;
3178 
3179  return true;
3180  }
3181 
3182  option<parse_type> date_type(const std::string::iterator& it,
3183  const std::string::iterator& end)
3184  {
3185  auto date_end = find_end_of_date(it, end);
3186  auto len = std::distance(it, date_end);
3187 
3188  if (len < 10)
3189  return {};
3190 
3191  if (it[4] != '-' || it[7] != '-')
3192  return {};
3193 
3194  if (len >= 19 && (it[10] == 'T' || it[10] == ' ')
3195  && is_time(it + 11, date_end))
3196  {
3197  // datetime type
3198  auto time_end = find_end_of_time(it + 11, date_end);
3199  if (time_end == date_end)
3200  return {parse_type::LOCAL_DATETIME};
3201  else
3202  return {parse_type::OFFSET_DATETIME};
3203  }
3204  else if (len == 10)
3205  {
3206  // just a regular date
3207  return {parse_type::LOCAL_DATE};
3208  }
3209 
3210  return {};
3211  }
3212 
3213  std::istream& input_;
3214  std::string line_;
3215  std::size_t line_number_ = 0;
3216 };
3217 
3222 inline std::shared_ptr<table> parse_file(const std::string& filename)
3223 {
3224 #if defined(BOOST_NOWIDE_FSTREAM_INCLUDED_HPP)
3225  boost::nowide::ifstream file{filename.c_str()};
3226 #elif defined(NOWIDE_FSTREAM_INCLUDED_HPP)
3227  nowide::ifstream file{filename.c_str()};
3228 #else
3229  std::ifstream file{filename};
3230 #endif
3231  if (!file.is_open())
3232  throw parse_exception{filename + " could not be opened for parsing"};
3233  parser p{file};
3234  return p.parse();
3235 }
3236 
3237 template <class... Ts>
3239 
3240 template <>
3242 {
3243  template <class Visitor, class... Args>
3244  static void accept(const base&, Visitor&&, Args&&...)
3245  {
3246  // nothing
3247  }
3248 };
3249 
3250 template <class T, class... Ts>
3251 struct value_accept<T, Ts...>
3252 {
3253  template <class Visitor, class... Args>
3254  static void accept(const base& b, Visitor&& visitor, Args&&... args)
3255  {
3256  if (auto v = b.as<T>())
3257  {
3258  visitor.visit(*v, std::forward<Args>(args)...);
3259  }
3260  else
3261  {
3262  value_accept<Ts...>::accept(b, std::forward<Visitor>(visitor),
3263  std::forward<Args>(args)...);
3264  }
3265  }
3266 };
3267 
3272 template <class Visitor, class... Args>
3273 void base::accept(Visitor&& visitor, Args&&... args) const
3274 {
3275  if (is_value())
3276  {
3277  using value_acceptor
3278  = value_accept<std::string, int64_t, double, bool, local_date,
3279  local_time, local_datetime, offset_datetime>;
3280  value_acceptor::accept(*this, std::forward<Visitor>(visitor),
3281  std::forward<Args>(args)...);
3282  }
3283  else if (is_table())
3284  {
3285  visitor.visit(static_cast<const table&>(*this),
3286  std::forward<Args>(args)...);
3287  }
3288  else if (is_array())
3289  {
3290  visitor.visit(static_cast<const array&>(*this),
3291  std::forward<Args>(args)...);
3292  }
3293  else if (is_table_array())
3294  {
3295  visitor.visit(static_cast<const table_array&>(*this),
3296  std::forward<Args>(args)...);
3297  }
3298 }
3299 
3305 {
3306  public:
3310  toml_writer(std::ostream& s, const std::string& indent_space = "\t")
3311  : stream_(s), indent_(indent_space), has_naked_endline_(false)
3312  {
3313  // nothing
3314  }
3315 
3316  public:
3320  template <class T>
3321  void visit(const value<T>& v, bool = false)
3322  {
3323  write(v);
3324  }
3325 
3329  void visit(const table& t, bool in_array = false)
3330  {
3331  write_table_header(in_array);
3332  std::vector<std::string> values;
3333  std::vector<std::string> tables;
3334 
3335  for (const auto& i : t)
3336  {
3337  if (i.second->is_table() || i.second->is_table_array())
3338  {
3339  tables.push_back(i.first);
3340  }
3341  else
3342  {
3343  values.push_back(i.first);
3344  }
3345  }
3346 
3347  for (unsigned int i = 0; i < values.size(); ++i)
3348  {
3349  path_.push_back(values[i]);
3350 
3351  if (i > 0)
3352  endline();
3353 
3354  write_table_item_header(*t.get(values[i]));
3355  t.get(values[i])->accept(*this, false);
3356  path_.pop_back();
3357  }
3358 
3359  for (unsigned int i = 0; i < tables.size(); ++i)
3360  {
3361  path_.push_back(tables[i]);
3362 
3363  if (values.size() > 0 || i > 0)
3364  endline();
3365 
3366  write_table_item_header(*t.get(tables[i]));
3367  t.get(tables[i])->accept(*this, false);
3368  path_.pop_back();
3369  }
3370 
3371  endline();
3372  }
3373 
3377  void visit(const array& a, bool = false)
3378  {
3379  write("[");
3380 
3381  for (unsigned int i = 0; i < a.get().size(); ++i)
3382  {
3383  if (i > 0)
3384  write(", ");
3385 
3386  if (a.get()[i]->is_array())
3387  {
3388  a.get()[i]->as_array()->accept(*this, true);
3389  }
3390  else
3391  {
3392  a.get()[i]->accept(*this, true);
3393  }
3394  }
3395 
3396  write("]");
3397  }
3398 
3402  void visit(const table_array& t, bool = false)
3403  {
3404  for (unsigned int j = 0; j < t.get().size(); ++j)
3405  {
3406  if (j > 0)
3407  endline();
3408 
3409  t.get()[j]->accept(*this, true);
3410  }
3411 
3412  endline();
3413  }
3414 
3418  static std::string escape_string(const std::string& str)
3419  {
3420  std::string res;
3421  for (auto it = str.begin(); it != str.end(); ++it)
3422  {
3423  if (*it == '\b')
3424  {
3425  res += "\\b";
3426  }
3427  else if (*it == '\t')
3428  {
3429  res += "\\t";
3430  }
3431  else if (*it == '\n')
3432  {
3433  res += "\\n";
3434  }
3435  else if (*it == '\f')
3436  {
3437  res += "\\f";
3438  }
3439  else if (*it == '\r')
3440  {
3441  res += "\\r";
3442  }
3443  else if (*it == '"')
3444  {
3445  res += "\\\"";
3446  }
3447  else if (*it == '\\')
3448  {
3449  res += "\\\\";
3450  }
3451  else if (static_cast<uint32_t>(*it) <= UINT32_C(0x001f))
3452  {
3453  res += "\\u";
3454  std::stringstream ss;
3455  ss << std::hex << static_cast<uint32_t>(*it);
3456  res += ss.str();
3457  }
3458  else
3459  {
3460  res += *it;
3461  }
3462  }
3463  return res;
3464  }
3465 
3466  protected:
3470  void write(const value<std::string>& v)
3471  {
3472  write("\"");
3473  write(escape_string(v.get()));
3474  write("\"");
3475  }
3476 
3480  void write(const value<double>& v)
3481  {
3482  std::stringstream ss;
3483  ss << std::showpoint
3484  << std::setprecision(std::numeric_limits<double>::max_digits10)
3485  << v.get();
3486 
3487  auto double_str = ss.str();
3488  auto pos = double_str.find("e0");
3489  if (pos != std::string::npos)
3490  double_str.replace(pos, 2, "e");
3491  pos = double_str.find("e-0");
3492  if (pos != std::string::npos)
3493  double_str.replace(pos, 3, "e-");
3494 
3495  stream_ << double_str;
3496  has_naked_endline_ = false;
3497  }
3498 
3503  template <class T>
3504  typename std::enable_if<
3505  is_one_of<T, int64_t, local_date, local_time, local_datetime,
3506  offset_datetime>::value>::type
3507  write(const value<T>& v)
3508  {
3509  write(v.get());
3510  }
3511 
3515  void write(const value<bool>& v)
3516  {
3517  write((v.get() ? "true" : "false"));
3518  }
3519 
3523  void write_table_header(bool in_array = false)
3524  {
3525  if (!path_.empty())
3526  {
3527  indent();
3528 
3529  write("[");
3530 
3531  if (in_array)
3532  {
3533  write("[");
3534  }
3535 
3536  for (unsigned int i = 0; i < path_.size(); ++i)
3537  {
3538  if (i > 0)
3539  {
3540  write(".");
3541  }
3542 
3543  if (path_[i].find_first_not_of("ABCDEFGHIJKLMNOPQRSTUVWXYZabcde"
3544  "fghijklmnopqrstuvwxyz0123456789"
3545  "_-")
3546  == std::string::npos)
3547  {
3548  write(path_[i]);
3549  }
3550  else
3551  {
3552  write("\"");
3553  write(escape_string(path_[i]));
3554  write("\"");
3555  }
3556  }
3557 
3558  if (in_array)
3559  {
3560  write("]");
3561  }
3562 
3563  write("]");
3564  endline();
3565  }
3566  }
3567 
3572  {
3573  if (!b.is_table() && !b.is_table_array())
3574  {
3575  indent();
3576 
3577  if (path_.back().find_first_not_of("ABCDEFGHIJKLMNOPQRSTUVWXYZabcde"
3578  "fghijklmnopqrstuvwxyz0123456789"
3579  "_-")
3580  == std::string::npos)
3581  {
3582  write(path_.back());
3583  }
3584  else
3585  {
3586  write("\"");
3587  write(escape_string(path_.back()));
3588  write("\"");
3589  }
3590 
3591  write(" = ");
3592  }
3593  }
3594 
3595  private:
3600  void indent()
3601  {
3602  for (std::size_t i = 1; i < path_.size(); ++i)
3603  write(indent_);
3604  }
3605 
3609  template <class T>
3610  void write(const T& v)
3611  {
3612  stream_ << v;
3613  has_naked_endline_ = false;
3614  }
3615 
3619  void endline()
3620  {
3621  if (!has_naked_endline_)
3622  {
3623  stream_ << "\n";
3624  has_naked_endline_ = true;
3625  }
3626  }
3627 
3628  private:
3629  std::ostream& stream_;
3630  const std::string indent_;
3631  std::vector<std::string> path_;
3633 };
3634 
3635 inline std::ostream& operator<<(std::ostream& stream, const base& b)
3636 {
3637  toml_writer writer{stream};
3638  b.accept(writer);
3639  return stream;
3640 }
3641 
3642 template <class T>
3643 std::ostream& operator<<(std::ostream& stream, const value<T>& v)
3644 {
3645  toml_writer writer{stream};
3646  v.accept(writer);
3647  return stream;
3648 }
3649 
3650 inline std::ostream& operator<<(std::ostream& stream, const table& t)
3651 {
3652  toml_writer writer{stream};
3653  t.accept(writer);
3654  return stream;
3655 }
3656 
3657 inline std::ostream& operator<<(std::ostream& stream, const table_array& t)
3658 {
3659  toml_writer writer{stream};
3660  t.accept(writer);
3661  return stream;
3662 }
3663 
3664 inline std::ostream& operator<<(std::ostream& stream, const array& a)
3665 {
3666  toml_writer writer{stream};
3667  a.accept(writer);
3668  return stream;
3669 }
3670 } // namespace cpptoml
3671 #endif // CPPTOML_H
std::vector< std::shared_ptr< base > > values_
Definition: cpptoml.h:1021
std::shared_ptr< base > clone() const override
Definition: cpptoml.h:1740
option< parse_type > date_type(const std::string::iterator &it, const std::string::iterator &end)
Definition: cpptoml.h:3182
bool resolve_qualified(const std::string &key, std::shared_ptr< base > *p=nullptr) const
Definition: cpptoml.h:1600
std::shared_ptr< base > get_qualified(const std::string &key) const
Definition: cpptoml.h:1369
Definition: cpptoml.h:777
iterator insert(iterator position, const std::shared_ptr< value< T >> &value)
Definition: cpptoml.h:941
void insert(const std::string &key, const std::shared_ptr< base > &value)
Definition: cpptoml.h:1540
std::istream & input_
Definition: cpptoml.h:3213
std::shared_ptr< value< T > > as()
Definition: cpptoml.h:683
iterator insert(iterator position, const std::shared_ptr< array > &value)
Definition: cpptoml.h:956
void reserve(size_type n)
Definition: cpptoml.h:1160
void push_back(const std::shared_ptr< value< T >> &val)
Definition: cpptoml.h:900
int minute
Definition: cpptoml.h:113
void parse_key_value(std::string::iterator &it, std::string::iterator &end, table *curr_table)
Definition: cpptoml.h:2138
array_of_trait< T >::return_type get_qualified_array_of(const std::string &key) const
Definition: cpptoml.h:1517
std::shared_ptr< table_array > make_element< table_array >()
Definition: cpptoml.h:1212
std::shared_ptr< table_array > as_table_array()
Definition: cpptoml.h:562
std::shared_ptr< array > get_array_qualified(const std::string &key) const
Definition: cpptoml.h:1410
consumer(std::string::iterator &it, const std::string::iterator &end, OnError &&on_error)
Definition: cpptoml.h:1781
std::string parse_escape_code(std::string::iterator &it, const std::string::iterator &end)
Definition: cpptoml.h:2522
void parse_single_table(std::string::iterator &it, const std::string::iterator &end, table *&curr_table)
Definition: cpptoml.h:1948
array_exception(const std::string &err)
Definition: cpptoml.h:772
void parse_table(std::string::iterator &it, const std::string::iterator &end, table *&curr_table)
Definition: cpptoml.h:1935
Definition: cpptoml.h:291
std::shared_ptr< base > clone() const override
Definition: cpptoml.h:1731
virtual bool is_table_array() const override
Definition: cpptoml.h:1110
std::enable_if< is_one_of< T, int64_t, local_date, local_time, local_datetime, offset_datetime >::value >::type write(const value< T > &v)
Definition: cpptoml.h:3507
void visit(const value< T > &v, bool=false)
Definition: cpptoml.h:3321
Definition: cpptoml.h:385
static void accept(const base &b, Visitor &&visitor, Args &&... args)
Definition: cpptoml.h:3254
void write(const value< std::string > &v)
Definition: cpptoml.h:3470
void visit(const table &t, bool in_array=false)
Definition: cpptoml.h:3329
const std::string indent_
Definition: cpptoml.h:3630
std::shared_ptr< array > make_array()
Definition: cpptoml.h:1024
static std::string escape_string(const std::string &str)
Definition: cpptoml.h:3418
void consume_backwards_whitespace(std::string::iterator &back, const std::string::iterator &front)
Definition: cpptoml.h:3148
std::shared_ptr< table > make_element< table >()
Definition: cpptoml.h:1710
std::shared_ptr< base > parse_number(std::string::iterator &it, const std::string::iterator &end)
Definition: cpptoml.h:2659
int month
Definition: cpptoml.h:106
iterator erase(iterator position)
Definition: cpptoml.h:1144
std::ostream & os_
Definition: cpptoml.h:185
uint32_t parse_hex(std::string::iterator &it, const std::string::iterator &end, uint32_t place)
Definition: cpptoml.h:2632
std::size_t size_type
Definition: cpptoml.h:1078
std::shared_ptr< base > get(const std::string &key) const
Definition: cpptoml.h:1357
std::shared_ptr< array > make_element< array >()
Definition: cpptoml.h:1040
Definition: cpptoml.h:501
bool has_naked_endline_
Definition: cpptoml.h:3632
int day
Definition: cpptoml.h:107
bool contains(const std::string &key) const
Definition: cpptoml.h:1338
std::shared_ptr< array > as_array()
Definition: cpptoml.h:544
std::shared_ptr< value< std::string > > parse_multiline_string(std::string::iterator &it, std::string::iterator &end, char delim)
Definition: cpptoml.h:2410
std::string::iterator find_end_of_date(std::string::iterator it, std::string::iterator end)
Definition: cpptoml.h:2878
bool is_time(const std::string::iterator &it, const std::string::iterator &end)
Definition: cpptoml.h:3164
virtual bool is_table() const
Definition: cpptoml.h:519
const_iterator begin() const
Definition: cpptoml.h:806
int year
Definition: cpptoml.h:105
void clear()
Definition: cpptoml.h:989
std::vector< std::shared_ptr< table > >::const_iterator const_iterator
Definition: cpptoml.h:1088
std::shared_ptr< table_array > get_table_array(const std::string &key) const
Definition: cpptoml.h:1420
std::shared_ptr< table > make_table()
Definition: cpptoml.h:1694
T data_
Definition: cpptoml.h:654
Definition: cpptoml.h:3304
virtual bool is_array() const override
Definition: cpptoml.h:784
iterator begin()
Definition: cpptoml.h:1090
std::shared_ptr< base > clone() const override
Definition: cpptoml.h:1717
T & get()
Definition: cpptoml.h:640
std::vector< std::shared_ptr< table > > array_
Definition: cpptoml.h:1192
T value_
Definition: cpptoml.h:100
int eat_digits(int len)
Definition: cpptoml.h:1809
virtual bool is_array() const
Definition: cpptoml.h:536
std::string::iterator find_end_of_number(std::string::iterator it, std::string::iterator end)
Definition: cpptoml.h:2860
parse_exception(const std::string &err, std::size_t line_number)
Definition: cpptoml.h:1758
std::string string_literal(std::string::iterator &it, const std::string::iterator &end, char delim)
Definition: cpptoml.h:2496
string_to_base_map::const_iterator const_iterator
Definition: cpptoml.h:1303
Definition: cpptoml.h:103
void write(const value< double > &v)
Definition: cpptoml.h:3480
const_iterator end() const
Definition: cpptoml.h:816
Definition: cpptoml.h:1778
iterator begin()
Definition: cpptoml.h:801
Definition: cpptoml.h:1751
value(const T &val)
Definition: cpptoml.h:664
std::string line_
Definition: cpptoml.h:3214
parse_type determine_number_type(const std::string::iterator &it, const std::string::iterator &end)
Definition: cpptoml.h:2358
void eat_or(char a, char b)
Definition: cpptoml.h:1802
std::shared_ptr< base > at(size_t idx) const
Definition: cpptoml.h:837
void throw_parse_exception(const std::string &err)
Definition: cpptoml.h:1930
Definition: cpptoml.h:1878
std::shared_ptr< value< local_time > > parse_time(std::string::iterator &it, const std::string::iterator &end)
Definition: cpptoml.h:2935
Definition: cpptoml.h:110
std::enable_if<!std::is_floating_point< T >::value &&std::is_signed< T >::value, option< T > >::type get_impl(const std::shared_ptr< base > &elem)
Definition: cpptoml.h:1225
std::shared_ptr< table > get_table_qualified(const std::string &key) const
Definition: cpptoml.h:1390
Definition: cpptoml.h:1287
std::shared_ptr< typename value_traits< T >::type > make_value(T &&val)
Definition: cpptoml.h:674
std::shared_ptr< value< std::string > > parse_string(std::string::iterator &it, std::string::iterator &end)
Definition: cpptoml.h:2387
std::shared_ptr< table > parse()
Definition: cpptoml.h:1895
virtual bool is_value() const
Definition: cpptoml.h:511
Definition: cpptoml.h:1070
const_iterator begin() const
Definition: cpptoml.h:1095
~fill_guard()
Definition: cpptoml.h:179
void push_back(T &&val, typename value_traits< T >::type *=0)
Definition: cpptoml.h:932
uint32_t hex_to_digit(char c)
Definition: cpptoml.h:2650
std::shared_ptr< T > make_element()
parser(std::istream &stream)
Definition: cpptoml.h:1884
bool is_inline() const
Definition: cpptoml.h:1170
void write(const value< bool > &v)
Definition: cpptoml.h:3515
std::unordered_map< std::string, std::shared_ptr< base > > string_to_base_map
Definition: cpptoml.h:54
bool empty() const
Definition: cpptoml.h:1330
std::shared_ptr< base > clone() const override
Definition: cpptoml.h:1722
iterator begin()
Definition: cpptoml.h:1305
table_array(bool is_inline=false)
Definition: cpptoml.h:1183
std::shared_ptr< array > get_array(const std::string &key) const
Definition: cpptoml.h:1400
std::shared_ptr< table > get_table(const std::string &key) const
Definition: cpptoml.h:1379
void operator()(char c)
Definition: cpptoml.h:1788
std::shared_ptr< table_array > get_table_array_qualified(const std::string &key) const
Definition: cpptoml.h:1432
void eol_or_comment(const std::string::iterator &it, const std::string::iterator &end)
Definition: cpptoml.h:3155
void visit(const table_array &t, bool=false)
Definition: cpptoml.h:3402
void reserve(size_type n)
Definition: cpptoml.h:997
std::vector< std::shared_ptr< base > >::const_iterator const_iterator
Definition: cpptoml.h:799
Definition: cpptoml.h:124
base()
Definition: cpptoml.h:599
int microsecond
Definition: cpptoml.h:115
std::istream & getline(std::istream &input, std::string &line)
Definition: cpptoml.h:1844
value(const make_shared_enabler &, const T &val)
Definition: cpptoml.h:626
std::shared_ptr< base > parse_value(std::string::iterator &it, std::string::iterator &end)
Definition: cpptoml.h:2288
const_iterator end() const
Definition: cpptoml.h:1105
void accept(Visitor &&visitor, Args &&... args) const
Definition: cpptoml.h:3273
pure character(:) function, allocatable check(yesno)
Definition: pt.f90:546
void erase(const std::string &key)
Definition: cpptoml.h:1559
std::ostream & stream_
Definition: cpptoml.h:3629
int hour
Definition: cpptoml.h:112
std::shared_ptr< base > parse_array(std::string::iterator &it, std::string::iterator &end)
Definition: cpptoml.h:2994
const T * operator->() const
Definition: cpptoml.h:85
void parse_table_array(std::string::iterator &it, const std::string::iterator &end, table *&curr_table)
Definition: cpptoml.h:2037
Definition: cpptoml.h:284
std::shared_ptr< value< bool > > parse_bool(std::string::iterator &it, const std::string::iterator &end)
Definition: cpptoml.h:2838
array_of_trait< T >::return_type get_array_of() const
Definition: cpptoml.h:862
std::string parse_unicode(std::string::iterator &it, const std::string::iterator &end)
Definition: cpptoml.h:2569
std::shared_ptr< Object > parse_object_array(Function &&fun, char delim, std::string::iterator &it, std::string::iterator &end)
Definition: cpptoml.h:3071
T value_or(U &&alternative) const
Definition: cpptoml.h:91
std::shared_ptr< base > parse_date(std::string::iterator &it, const std::string::iterator &end)
Definition: cpptoml.h:2940
bool contains_qualified(const std::string &key) const
Definition: cpptoml.h:1348
iterator erase(iterator position)
Definition: cpptoml.h:981
iterator end()
Definition: cpptoml.h:1315
toml_writer(std::ostream &s, const std::string &indent_space="\)
Definition: cpptoml.h:3310
Definition: cpptoml.h:266
parse_exception(const std::string &err)
Definition: cpptoml.h:1754
void insert(const std::string &key, T &&val, typename value_traits< T >::type *=0)
Definition: cpptoml.h:1550
std::shared_ptr< value< int64_t > > parse_int(std::string::iterator &it, const std::string::iterator &end, int base=10, const char *prefix="")
Definition: cpptoml.h:2789
std::shared_ptr< table > parse_inline_table(std::string::iterator &it, std::string::iterator &end)
Definition: cpptoml.h:3099
iterator insert(iterator position, const std::shared_ptr< table > &value)
Definition: cpptoml.h:1136
array_of_trait< T >::return_type get_array_of(const std::string &key) const
Definition: cpptoml.h:1485
option(T value)
Definition: cpptoml.h:70
table()
Definition: cpptoml.h:1571
int minute_offset
Definition: cpptoml.h:121
Definition: cpptoml.h:171
const_iterator begin() const
Definition: cpptoml.h:1310
void consume_whitespace(std::string::iterator &it, const std::string::iterator &end)
Definition: cpptoml.h:3141
Definition: cpptoml.h:769
void indent()
Definition: cpptoml.h:3600
std::string parse_key(std::string::iterator &it, const std::string::iterator &end, KeyEndFinder &&key_end, KeyPartHandler &&key_part_handler)
Definition: cpptoml.h:2182
bool is_table() const override
Definition: cpptoml.h:1325
std::vector< std::shared_ptr< table > >::iterator iterator
Definition: cpptoml.h:1083
std::string parse_simple_key(std::string::iterator &it, const std::string::iterator &end)
Definition: cpptoml.h:2214
void visit(const array &a, bool=false)
Definition: cpptoml.h:3377
string_to_base_map::iterator iterator
Definition: cpptoml.h:1298
void write_table_header(bool in_array=false)
Definition: cpptoml.h:3523
std::vector< std::shared_ptr< table > > & get()
Definition: cpptoml.h:1115
void write(const T &v)
Definition: cpptoml.h:3610
std::vector< std::shared_ptr< base > > & get()
Definition: cpptoml.h:824
void push_back(const std::shared_ptr< array > &val)
Definition: cpptoml.h:915
local_time read_time(std::string::iterator &it, const std::string::iterator &end)
Definition: cpptoml.h:2901
Definition: cpptoml.h:118
std::string::iterator find_end_of_time(std::string::iterator it, std::string::iterator end)
Definition: cpptoml.h:2893
std::vector< std::string > path_
Definition: cpptoml.h:3631
Definition: cpptoml.h:128
typename std::conditional< valid_value< typename std::decay< T >::type >::value, typename std::decay< T >::type, std::string >::type value_type
Definition: cpptoml.h:307
std::vector< std::string > split(const std::string &value, char separator) const
Definition: cpptoml.h:1580
std::shared_ptr< value< double > > parse_float(std::string::iterator &it, const std::string::iterator &end)
Definition: cpptoml.h:2814
std::vector< std::shared_ptr< base > >::iterator iterator
Definition: cpptoml.h:794
Definition: cpptoml.h:3238
std::ostream & operator<<(std::ostream &os, const local_date &dt)
Definition: cpptoml.h:189
option()
Definition: cpptoml.h:65
void operator()(const char(&str)[N])
Definition: cpptoml.h:1796
std::ostream::char_type fill_
Definition: cpptoml.h:186
const std::string::iterator & end_
Definition: cpptoml.h:1828
consumer< OnError > make_consumer(std::string::iterator &it, const std::string::iterator &end, OnError &&on_error)
Definition: cpptoml.h:1833
void write_table_item_header(const base &b)
Definition: cpptoml.h:3571
std::string::iterator & it_
Definition: cpptoml.h:1827
void error()
Definition: cpptoml.h:1821
iterator insert(iterator position, T &&val, typename value_traits< T >::type *=0)
Definition: cpptoml.h:972
parse_type
Definition: cpptoml.h:2274
static void accept(const base &, Visitor &&, Args &&...)
Definition: cpptoml.h:3244
void skip_whitespace_and_comments(std::string::iterator &start, std::string::iterator &end)
Definition: cpptoml.h:3126
std::vector< std::shared_ptr< array > > nested_array() const
Definition: cpptoml.h:882
std::size_t size_type
Definition: cpptoml.h:789
option< T > get_qualified_as(const std::string &key) const
Definition: cpptoml.h:1462
std::shared_ptr< table > parse_file(const std::string &filename)
Definition: cpptoml.h:3222
std::shared_ptr< table_array > make_table_array(bool is_inline=false)
Definition: cpptoml.h:1196
Definition: cpptoml.h:281
Definition: cpptoml.h:62
string_to_base_map map_
Definition: cpptoml.h:1627
iterator end()
Definition: cpptoml.h:811
virtual bool is_table_array() const
Definition: cpptoml.h:554
fill_guard(std::ostream &os)
Definition: cpptoml.h:174
OnError on_error_
Definition: cpptoml.h:1829
array(InputIterator begin, InputIterator end)
Definition: cpptoml.h:1013
void endline()
Definition: cpptoml.h:3619
bool is_number(char c)
Definition: cpptoml.h:1764
std::shared_ptr< array > parse_value_array(std::string::iterator &it, std::string::iterator &end)
Definition: cpptoml.h:3048
iterator end()
Definition: cpptoml.h:1100
void clear()
Definition: cpptoml.h:1152
std::vector< std::shared_ptr< value< T > > > array_of() const
Definition: cpptoml.h:847
bool is_hex(char c)
Definition: cpptoml.h:1769
std::string parse_bare_key(std::string::iterator &it, const std::string::iterator &end)
Definition: cpptoml.h:2235
Definition: cpptoml.h:612
const_iterator end() const
Definition: cpptoml.h:1320
int second
Definition: cpptoml.h:114
Definition: cpptoml.h:42
std::shared_ptr< table > as_table()
Definition: cpptoml.h:527
const T & operator*() const
Definition: cpptoml.h:80
option< T > get_as(const std::string &key) const
Definition: cpptoml.h:1444
parse_type determine_value_type(const std::string::iterator &it, const std::string::iterator &end)
Definition: cpptoml.h:2316
bool empty_
Definition: cpptoml.h:99
void push_back(const std::shared_ptr< table > &val)
Definition: cpptoml.h:1128
int hour_offset
Definition: cpptoml.h:120
bool is_value() const override
Definition: cpptoml.h:632