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