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