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