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