toml.hpp
1 #ifndef TOML11_VERSION_HPP 2 #define TOML11_VERSION_HPP 3 4 #define TOML11_VERSION_MAJOR 4 5 #define TOML11_VERSION_MINOR 2 6 #define TOML11_VERSION_PATCH 0 7 8 #ifndef __cplusplus 9 # error "__cplusplus is not defined" 10 #endif 11 12 // Since MSVC does not define `__cplusplus` correctly unless you pass 13 // `/Zc:__cplusplus` when compiling, the workaround macros are added. 14 // 15 // The value of `__cplusplus` macro is defined in the C++ standard spec, but 16 // MSVC ignores the value, maybe because of backward compatibility. Instead, 17 // MSVC defines _MSVC_LANG that has the same value as __cplusplus defined in 18 // the C++ standard. So we check if _MSVC_LANG is defined before using `__cplusplus`. 19 // 20 // FYI: https://docs.microsoft.com/en-us/cpp/build/reference/zc-cplusplus?view=msvc-170 21 // https://docs.microsoft.com/en-us/cpp/preprocessor/predefined-macros?view=msvc-170 22 // 23 24 #if defined(_MSVC_LANG) && defined(_MSC_VER) && 190024210 <= _MSC_FULL_VER 25 # define TOML11_CPLUSPLUS_STANDARD_VERSION _MSVC_LANG 26 #else 27 # define TOML11_CPLUSPLUS_STANDARD_VERSION __cplusplus 28 #endif 29 30 #if TOML11_CPLUSPLUS_STANDARD_VERSION < 201103L 31 # error "toml11 requires C++11 or later." 32 #endif 33 34 #if ! defined(__has_include) 35 # define __has_include(x) 0 36 #endif 37 38 #if ! defined(__has_cpp_attribute) 39 # define __has_cpp_attribute(x) 0 40 #endif 41 42 #if ! defined(__has_builtin) 43 # define __has_builtin(x) 0 44 #endif 45 46 // hard to remember 47 48 #ifndef TOML11_CXX14_VALUE 49 #define TOML11_CXX14_VALUE 201402L 50 #endif//TOML11_CXX14_VALUE 51 52 #ifndef TOML11_CXX17_VALUE 53 #define TOML11_CXX17_VALUE 201703L 54 #endif//TOML11_CXX17_VALUE 55 56 #ifndef TOML11_CXX20_VALUE 57 #define TOML11_CXX20_VALUE 202002L 58 #endif//TOML11_CXX20_VALUE 59 60 #if defined(__cpp_char8_t) 61 # if __cpp_char8_t >= 201811L 62 # define TOML11_HAS_CHAR8_T 1 63 # endif 64 #endif 65 66 #if TOML11_CPLUSPLUS_STANDARD_VERSION >= TOML11_CXX17_VALUE 67 # if __has_include(<string_view>) 68 # define TOML11_HAS_STRING_VIEW 1 69 # endif 70 #endif 71 72 #ifndef TOML11_DISABLE_STD_FILESYSTEM 73 # if TOML11_CPLUSPLUS_STANDARD_VERSION >= TOML11_CXX17_VALUE 74 # if __has_include(<filesystem>) 75 # define TOML11_HAS_FILESYSTEM 1 76 # endif 77 # endif 78 #endif 79 80 #if TOML11_CPLUSPLUS_STANDARD_VERSION >= TOML11_CXX17_VALUE 81 # if __has_include(<optional>) 82 # define TOML11_HAS_OPTIONAL 1 83 # endif 84 #endif 85 86 #if defined(TOML11_COMPILE_SOURCES) 87 # define TOML11_INLINE 88 #else 89 # define TOML11_INLINE inline 90 #endif 91 92 namespace toml 93 { 94 95 inline const char* license_notice() noexcept 96 { 97 return R"(The MIT License (MIT) 98 99 Copyright (c) 2017-now Toru Niina 100 101 Permission is hereby granted, free of charge, to any person obtaining a copy 102 of this software and associated documentation files (the "Software"), to deal 103 in the Software without restriction, including without limitation the rights 104 to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 105 copies of the Software, and to permit persons to whom the Software is 106 furnished to do so, subject to the following conditions: 107 108 The above copyright notice and this permission notice shall be included in 109 all copies or substantial portions of the Software. 110 111 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 112 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 113 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 114 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 115 LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 116 OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 117 THE SOFTWARE.)"; 118 } 119 120 } // toml 121 #endif // TOML11_VERSION_HPP 122 #ifndef TOML11_FORMAT_HPP 123 #define TOML11_FORMAT_HPP 124 125 #ifndef TOML11_FORMAT_FWD_HPP 126 #define TOML11_FORMAT_FWD_HPP 127 128 #include <iosfwd> 129 #include <string> 130 #include <utility> 131 132 #include <cstddef> 133 #include <cstdint> 134 135 namespace toml 136 { 137 138 // toml types with serialization info 139 140 enum class indent_char : std::uint8_t 141 { 142 space, // use space 143 tab, // use tab 144 none // no indent 145 }; 146 147 std::ostream& operator<<(std::ostream& os, const indent_char& c); 148 std::string to_string(const indent_char c); 149 150 // ---------------------------------------------------------------------------- 151 // boolean 152 153 struct boolean_format_info 154 { 155 // nothing, for now 156 }; 157 158 inline bool operator==(const boolean_format_info&, const boolean_format_info&) noexcept 159 { 160 return true; 161 } 162 inline bool operator!=(const boolean_format_info&, const boolean_format_info&) noexcept 163 { 164 return false; 165 } 166 167 // ---------------------------------------------------------------------------- 168 // integer 169 170 enum class integer_format : std::uint8_t 171 { 172 dec = 0, 173 bin = 1, 174 oct = 2, 175 hex = 3, 176 }; 177 178 std::ostream& operator<<(std::ostream& os, const integer_format f); 179 std::string to_string(const integer_format); 180 181 struct integer_format_info 182 { 183 integer_format fmt = integer_format::dec; 184 bool uppercase = true; // hex with uppercase 185 std::size_t width = 0; // minimal width (may exceed) 186 std::size_t spacer = 0; // position of `_` (if 0, no spacer) 187 std::string suffix = ""; // _suffix (library extension) 188 }; 189 190 bool operator==(const integer_format_info&, const integer_format_info&) noexcept; 191 bool operator!=(const integer_format_info&, const integer_format_info&) noexcept; 192 193 // ---------------------------------------------------------------------------- 194 // floating 195 196 enum class floating_format : std::uint8_t 197 { 198 defaultfloat = 0, 199 fixed = 1, // does not include exponential part 200 scientific = 2, // always include exponential part 201 hex = 3 // hexfloat extension 202 }; 203 204 std::ostream& operator<<(std::ostream& os, const floating_format f); 205 std::string to_string(const floating_format); 206 207 struct floating_format_info 208 { 209 floating_format fmt = floating_format::defaultfloat; 210 std::size_t prec = 0; // precision (if 0, use the default) 211 std::string suffix = ""; // 1.0e+2_suffix (library extension) 212 }; 213 214 bool operator==(const floating_format_info&, const floating_format_info&) noexcept; 215 bool operator!=(const floating_format_info&, const floating_format_info&) noexcept; 216 217 // ---------------------------------------------------------------------------- 218 // string 219 220 enum class string_format : std::uint8_t 221 { 222 basic = 0, 223 literal = 1, 224 multiline_basic = 2, 225 multiline_literal = 3 226 }; 227 228 std::ostream& operator<<(std::ostream& os, const string_format f); 229 std::string to_string(const string_format); 230 231 struct string_format_info 232 { 233 string_format fmt = string_format::basic; 234 bool start_with_newline = false; 235 }; 236 237 bool operator==(const string_format_info&, const string_format_info&) noexcept; 238 bool operator!=(const string_format_info&, const string_format_info&) noexcept; 239 240 // ---------------------------------------------------------------------------- 241 // datetime 242 243 enum class datetime_delimiter_kind : std::uint8_t 244 { 245 upper_T = 0, 246 lower_t = 1, 247 space = 2, 248 }; 249 std::ostream& operator<<(std::ostream& os, const datetime_delimiter_kind d); 250 std::string to_string(const datetime_delimiter_kind); 251 252 struct offset_datetime_format_info 253 { 254 datetime_delimiter_kind delimiter = datetime_delimiter_kind::upper_T; 255 bool has_seconds = true; 256 std::size_t subsecond_precision = 6; // [us] 257 }; 258 259 bool operator==(const offset_datetime_format_info&, const offset_datetime_format_info&) noexcept; 260 bool operator!=(const offset_datetime_format_info&, const offset_datetime_format_info&) noexcept; 261 262 struct local_datetime_format_info 263 { 264 datetime_delimiter_kind delimiter = datetime_delimiter_kind::upper_T; 265 bool has_seconds = true; 266 std::size_t subsecond_precision = 6; // [us] 267 }; 268 269 bool operator==(const local_datetime_format_info&, const local_datetime_format_info&) noexcept; 270 bool operator!=(const local_datetime_format_info&, const local_datetime_format_info&) noexcept; 271 272 struct local_date_format_info 273 { 274 // nothing, for now 275 }; 276 277 bool operator==(const local_date_format_info&, const local_date_format_info&) noexcept; 278 bool operator!=(const local_date_format_info&, const local_date_format_info&) noexcept; 279 280 struct local_time_format_info 281 { 282 bool has_seconds = true; 283 std::size_t subsecond_precision = 6; // [us] 284 }; 285 286 bool operator==(const local_time_format_info&, const local_time_format_info&) noexcept; 287 bool operator!=(const local_time_format_info&, const local_time_format_info&) noexcept; 288 289 // ---------------------------------------------------------------------------- 290 // array 291 292 enum class array_format : std::uint8_t 293 { 294 default_format = 0, 295 oneline = 1, 296 multiline = 2, 297 array_of_tables = 3 // [[format.in.this.way]] 298 }; 299 300 std::ostream& operator<<(std::ostream& os, const array_format f); 301 std::string to_string(const array_format); 302 303 struct array_format_info 304 { 305 array_format fmt = array_format::default_format; 306 indent_char indent_type = indent_char::space; 307 std::int32_t body_indent = 4; // indent in case of multiline 308 std::int32_t closing_indent = 0; // indent of `]` 309 }; 310 311 bool operator==(const array_format_info&, const array_format_info&) noexcept; 312 bool operator!=(const array_format_info&, const array_format_info&) noexcept; 313 314 // ---------------------------------------------------------------------------- 315 // table 316 317 enum class table_format : std::uint8_t 318 { 319 multiline = 0, // [foo] \n bar = "baz" 320 oneline = 1, // foo = {bar = "baz"} 321 dotted = 2, // foo.bar = "baz" 322 multiline_oneline = 3, // foo = { \n bar = "baz" \n } 323 implicit = 4 // [x] defined by [x.y.z]. skip in serializer. 324 }; 325 326 std::ostream& operator<<(std::ostream& os, const table_format f); 327 std::string to_string(const table_format); 328 329 struct table_format_info 330 { 331 table_format fmt = table_format::multiline; 332 indent_char indent_type = indent_char::space; 333 std::int32_t body_indent = 0; // indent of values 334 std::int32_t name_indent = 0; // indent of [table] 335 std::int32_t closing_indent = 0; // in case of {inline-table} 336 }; 337 338 bool operator==(const table_format_info&, const table_format_info&) noexcept; 339 bool operator!=(const table_format_info&, const table_format_info&) noexcept; 340 341 // ---------------------------------------------------------------------------- 342 // wrapper 343 344 namespace detail 345 { 346 template<typename T, typename F> 347 struct value_with_format 348 { 349 using value_type = T; 350 using format_type = F; 351 352 value_with_format() = default; 353 ~value_with_format() = default; 354 value_with_format(const value_with_format&) = default; 355 value_with_format(value_with_format&&) = default; 356 value_with_format& operator=(const value_with_format&) = default; 357 value_with_format& operator=(value_with_format&&) = default; 358 359 value_with_format(value_type v, format_type f) 360 : value{std::move(v)}, format{std::move(f)} 361 {} 362 363 template<typename U> 364 value_with_format(value_with_format<U, format_type> other) 365 : value{std::move(other.value)}, format{std::move(other.format)} 366 {} 367 368 value_type value; 369 format_type format; 370 }; 371 } // detail 372 373 } // namespace toml 374 #endif // TOML11_FORMAT_FWD_HPP 375 376 #if ! defined(TOML11_COMPILE_SOURCES) 377 #ifndef TOML11_FORMAT_IMPL_HPP 378 #define TOML11_FORMAT_IMPL_HPP 379 380 381 #include <ostream> 382 #include <sstream> 383 384 namespace toml 385 { 386 387 // toml types with serialization info 388 389 TOML11_INLINE std::ostream& operator<<(std::ostream& os, const indent_char& c) 390 { 391 switch(c) 392 { 393 case indent_char::space: {os << "space" ; break;} 394 case indent_char::tab: {os << "tab" ; break;} 395 case indent_char::none: {os << "none" ; break;} 396 default: 397 { 398 os << "unknown indent char: " << static_cast<std::uint8_t>(c); 399 } 400 } 401 return os; 402 } 403 404 TOML11_INLINE std::string to_string(const indent_char c) 405 { 406 std::ostringstream oss; 407 oss << c; 408 return oss.str(); 409 } 410 411 // ---------------------------------------------------------------------------- 412 // boolean 413 414 // ---------------------------------------------------------------------------- 415 // integer 416 417 TOML11_INLINE std::ostream& operator<<(std::ostream& os, const integer_format f) 418 { 419 switch(f) 420 { 421 case integer_format::dec: {os << "dec"; break;} 422 case integer_format::bin: {os << "bin"; break;} 423 case integer_format::oct: {os << "oct"; break;} 424 case integer_format::hex: {os << "hex"; break;} 425 default: 426 { 427 os << "unknown integer_format: " << static_cast<std::uint8_t>(f); 428 break; 429 } 430 } 431 return os; 432 } 433 TOML11_INLINE std::string to_string(const integer_format c) 434 { 435 std::ostringstream oss; 436 oss << c; 437 return oss.str(); 438 } 439 440 441 TOML11_INLINE bool operator==(const integer_format_info& lhs, const integer_format_info& rhs) noexcept 442 { 443 return lhs.fmt == rhs.fmt && 444 lhs.uppercase == rhs.uppercase && 445 lhs.width == rhs.width && 446 lhs.spacer == rhs.spacer && 447 lhs.suffix == rhs.suffix ; 448 } 449 TOML11_INLINE bool operator!=(const integer_format_info& lhs, const integer_format_info& rhs) noexcept 450 { 451 return !(lhs == rhs); 452 } 453 454 // ---------------------------------------------------------------------------- 455 // floating 456 457 TOML11_INLINE std::ostream& operator<<(std::ostream& os, const floating_format f) 458 { 459 switch(f) 460 { 461 case floating_format::defaultfloat: {os << "defaultfloat"; break;} 462 case floating_format::fixed : {os << "fixed" ; break;} 463 case floating_format::scientific : {os << "scientific" ; break;} 464 case floating_format::hex : {os << "hex" ; break;} 465 default: 466 { 467 os << "unknown floating_format: " << static_cast<std::uint8_t>(f); 468 break; 469 } 470 } 471 return os; 472 } 473 TOML11_INLINE std::string to_string(const floating_format c) 474 { 475 std::ostringstream oss; 476 oss << c; 477 return oss.str(); 478 } 479 480 TOML11_INLINE bool operator==(const floating_format_info& lhs, const floating_format_info& rhs) noexcept 481 { 482 return lhs.fmt == rhs.fmt && 483 lhs.prec == rhs.prec && 484 lhs.suffix == rhs.suffix ; 485 } 486 TOML11_INLINE bool operator!=(const floating_format_info& lhs, const floating_format_info& rhs) noexcept 487 { 488 return !(lhs == rhs); 489 } 490 491 // ---------------------------------------------------------------------------- 492 // string 493 494 TOML11_INLINE std::ostream& operator<<(std::ostream& os, const string_format f) 495 { 496 switch(f) 497 { 498 case string_format::basic : {os << "basic" ; break;} 499 case string_format::literal : {os << "literal" ; break;} 500 case string_format::multiline_basic : {os << "multiline_basic" ; break;} 501 case string_format::multiline_literal: {os << "multiline_literal"; break;} 502 default: 503 { 504 os << "unknown string_format: " << static_cast<std::uint8_t>(f); 505 break; 506 } 507 } 508 return os; 509 } 510 TOML11_INLINE std::string to_string(const string_format c) 511 { 512 std::ostringstream oss; 513 oss << c; 514 return oss.str(); 515 } 516 517 TOML11_INLINE bool operator==(const string_format_info& lhs, const string_format_info& rhs) noexcept 518 { 519 return lhs.fmt == rhs.fmt && 520 lhs.start_with_newline == rhs.start_with_newline ; 521 } 522 TOML11_INLINE bool operator!=(const string_format_info& lhs, const string_format_info& rhs) noexcept 523 { 524 return !(lhs == rhs); 525 } 526 // ---------------------------------------------------------------------------- 527 // datetime 528 529 TOML11_INLINE std::ostream& operator<<(std::ostream& os, const datetime_delimiter_kind d) 530 { 531 switch(d) 532 { 533 case datetime_delimiter_kind::upper_T: { os << "upper_T, "; break; } 534 case datetime_delimiter_kind::lower_t: { os << "lower_t, "; break; } 535 case datetime_delimiter_kind::space: { os << "space, "; break; } 536 default: 537 { 538 os << "unknown datetime delimiter: " << static_cast<std::uint8_t>(d); 539 break; 540 } 541 } 542 return os; 543 } 544 TOML11_INLINE std::string to_string(const datetime_delimiter_kind c) 545 { 546 std::ostringstream oss; 547 oss << c; 548 return oss.str(); 549 } 550 551 TOML11_INLINE bool operator==(const offset_datetime_format_info& lhs, const offset_datetime_format_info& rhs) noexcept 552 { 553 return lhs.delimiter == rhs.delimiter && 554 lhs.has_seconds == rhs.has_seconds && 555 lhs.subsecond_precision == rhs.subsecond_precision ; 556 } 557 TOML11_INLINE bool operator!=(const offset_datetime_format_info& lhs, const offset_datetime_format_info& rhs) noexcept 558 { 559 return !(lhs == rhs); 560 } 561 562 TOML11_INLINE bool operator==(const local_datetime_format_info& lhs, const local_datetime_format_info& rhs) noexcept 563 { 564 return lhs.delimiter == rhs.delimiter && 565 lhs.has_seconds == rhs.has_seconds && 566 lhs.subsecond_precision == rhs.subsecond_precision ; 567 } 568 TOML11_INLINE bool operator!=(const local_datetime_format_info& lhs, const local_datetime_format_info& rhs) noexcept 569 { 570 return !(lhs == rhs); 571 } 572 573 TOML11_INLINE bool operator==(const local_date_format_info&, const local_date_format_info&) noexcept 574 { 575 return true; 576 } 577 TOML11_INLINE bool operator!=(const local_date_format_info& lhs, const local_date_format_info& rhs) noexcept 578 { 579 return !(lhs == rhs); 580 } 581 582 TOML11_INLINE bool operator==(const local_time_format_info& lhs, const local_time_format_info& rhs) noexcept 583 { 584 return lhs.has_seconds == rhs.has_seconds && 585 lhs.subsecond_precision == rhs.subsecond_precision ; 586 } 587 TOML11_INLINE bool operator!=(const local_time_format_info& lhs, const local_time_format_info& rhs) noexcept 588 { 589 return !(lhs == rhs); 590 } 591 592 // ---------------------------------------------------------------------------- 593 // array 594 595 TOML11_INLINE std::ostream& operator<<(std::ostream& os, const array_format f) 596 { 597 switch(f) 598 { 599 case array_format::default_format : {os << "default_format" ; break;} 600 case array_format::oneline : {os << "oneline" ; break;} 601 case array_format::multiline : {os << "multiline" ; break;} 602 case array_format::array_of_tables: {os << "array_of_tables"; break;} 603 default: 604 { 605 os << "unknown array_format: " << static_cast<std::uint8_t>(f); 606 break; 607 } 608 } 609 return os; 610 } 611 TOML11_INLINE std::string to_string(const array_format c) 612 { 613 std::ostringstream oss; 614 oss << c; 615 return oss.str(); 616 } 617 618 TOML11_INLINE bool operator==(const array_format_info& lhs, const array_format_info& rhs) noexcept 619 { 620 return lhs.fmt == rhs.fmt && 621 lhs.indent_type == rhs.indent_type && 622 lhs.body_indent == rhs.body_indent && 623 lhs.closing_indent == rhs.closing_indent ; 624 } 625 TOML11_INLINE bool operator!=(const array_format_info& lhs, const array_format_info& rhs) noexcept 626 { 627 return !(lhs == rhs); 628 } 629 630 // ---------------------------------------------------------------------------- 631 // table 632 633 TOML11_INLINE std::ostream& operator<<(std::ostream& os, const table_format f) 634 { 635 switch(f) 636 { 637 case table_format::multiline : {os << "multiline" ; break;} 638 case table_format::oneline : {os << "oneline" ; break;} 639 case table_format::dotted : {os << "dotted" ; break;} 640 case table_format::multiline_oneline: {os << "multiline_oneline"; break;} 641 case table_format::implicit : {os << "implicit" ; break;} 642 default: 643 { 644 os << "unknown table_format: " << static_cast<std::uint8_t>(f); 645 break; 646 } 647 } 648 return os; 649 } 650 TOML11_INLINE std::string to_string(const table_format c) 651 { 652 std::ostringstream oss; 653 oss << c; 654 return oss.str(); 655 } 656 657 TOML11_INLINE bool operator==(const table_format_info& lhs, const table_format_info& rhs) noexcept 658 { 659 return lhs.fmt == rhs.fmt && 660 lhs.indent_type == rhs.indent_type && 661 lhs.body_indent == rhs.body_indent && 662 lhs.name_indent == rhs.name_indent && 663 lhs.closing_indent == rhs.closing_indent ; 664 } 665 TOML11_INLINE bool operator!=(const table_format_info& lhs, const table_format_info& rhs) noexcept 666 { 667 return !(lhs == rhs); 668 } 669 670 } // namespace toml 671 #endif // TOML11_FORMAT_IMPL_HPP 672 #endif 673 674 #endif// TOML11_FORMAT_HPP 675 #ifndef TOML11_DATETIME_HPP 676 #define TOML11_DATETIME_HPP 677 678 #ifndef TOML11_DATETIME_FWD_HPP 679 #define TOML11_DATETIME_FWD_HPP 680 681 #include <chrono> 682 #include <iosfwd> 683 #include <string> 684 685 #include <cstdint> 686 #include <cstdlib> 687 #include <ctime> 688 689 namespace toml 690 { 691 692 enum class month_t : std::uint8_t 693 { 694 Jan = 0, 695 Feb = 1, 696 Mar = 2, 697 Apr = 3, 698 May = 4, 699 Jun = 5, 700 Jul = 6, 701 Aug = 7, 702 Sep = 8, 703 Oct = 9, 704 Nov = 10, 705 Dec = 11 706 }; 707 708 // ---------------------------------------------------------------------------- 709 710 struct local_date 711 { 712 std::int16_t year{0}; // A.D. (like, 2018) 713 std::uint8_t month{0}; // [0, 11] 714 std::uint8_t day{0}; // [1, 31] 715 716 local_date(int y, month_t m, int d) 717 : year {static_cast<std::int16_t>(y)}, 718 month{static_cast<std::uint8_t>(m)}, 719 day {static_cast<std::uint8_t>(d)} 720 {} 721 722 explicit local_date(const std::tm& t) 723 : year {static_cast<std::int16_t>(t.tm_year + 1900)}, 724 month{static_cast<std::uint8_t>(t.tm_mon)}, 725 day {static_cast<std::uint8_t>(t.tm_mday)} 726 {} 727 728 explicit local_date(const std::chrono::system_clock::time_point& tp); 729 explicit local_date(const std::time_t t); 730 731 operator std::chrono::system_clock::time_point() const; 732 operator std::time_t() const; 733 734 local_date() = default; 735 ~local_date() = default; 736 local_date(local_date const&) = default; 737 local_date(local_date&&) = default; 738 local_date& operator=(local_date const&) = default; 739 local_date& operator=(local_date&&) = default; 740 }; 741 bool operator==(const local_date& lhs, const local_date& rhs); 742 bool operator!=(const local_date& lhs, const local_date& rhs); 743 bool operator< (const local_date& lhs, const local_date& rhs); 744 bool operator<=(const local_date& lhs, const local_date& rhs); 745 bool operator> (const local_date& lhs, const local_date& rhs); 746 bool operator>=(const local_date& lhs, const local_date& rhs); 747 748 std::ostream& operator<<(std::ostream& os, const local_date& date); 749 std::string to_string(const local_date& date); 750 751 // ----------------------------------------------------------------------------- 752 753 struct local_time 754 { 755 std::uint8_t hour{0}; // [0, 23] 756 std::uint8_t minute{0}; // [0, 59] 757 std::uint8_t second{0}; // [0, 60] 758 std::uint16_t millisecond{0}; // [0, 999] 759 std::uint16_t microsecond{0}; // [0, 999] 760 std::uint16_t nanosecond{0}; // [0, 999] 761 762 local_time(int h, int m, int s, 763 int ms = 0, int us = 0, int ns = 0) 764 : hour {static_cast<std::uint8_t>(h)}, 765 minute{static_cast<std::uint8_t>(m)}, 766 second{static_cast<std::uint8_t>(s)}, 767 millisecond{static_cast<std::uint16_t>(ms)}, 768 microsecond{static_cast<std::uint16_t>(us)}, 769 nanosecond {static_cast<std::uint16_t>(ns)} 770 {} 771 772 explicit local_time(const std::tm& t) 773 : hour {static_cast<std::uint8_t>(t.tm_hour)}, 774 minute{static_cast<std::uint8_t>(t.tm_min )}, 775 second{static_cast<std::uint8_t>(t.tm_sec )}, 776 millisecond{0}, microsecond{0}, nanosecond{0} 777 {} 778 779 template<typename Rep, typename Period> 780 explicit local_time(const std::chrono::duration<Rep, Period>& t) 781 { 782 const auto h = std::chrono::duration_cast<std::chrono::hours>(t); 783 this->hour = static_cast<std::uint8_t>(h.count()); 784 const auto t2 = t - h; 785 const auto m = std::chrono::duration_cast<std::chrono::minutes>(t2); 786 this->minute = static_cast<std::uint8_t>(m.count()); 787 const auto t3 = t2 - m; 788 const auto s = std::chrono::duration_cast<std::chrono::seconds>(t3); 789 this->second = static_cast<std::uint8_t>(s.count()); 790 const auto t4 = t3 - s; 791 const auto ms = std::chrono::duration_cast<std::chrono::milliseconds>(t4); 792 this->millisecond = static_cast<std::uint16_t>(ms.count()); 793 const auto t5 = t4 - ms; 794 const auto us = std::chrono::duration_cast<std::chrono::microseconds>(t5); 795 this->microsecond = static_cast<std::uint16_t>(us.count()); 796 const auto t6 = t5 - us; 797 const auto ns = std::chrono::duration_cast<std::chrono::nanoseconds>(t6); 798 this->nanosecond = static_cast<std::uint16_t>(ns.count()); 799 } 800 801 operator std::chrono::nanoseconds() const; 802 803 local_time() = default; 804 ~local_time() = default; 805 local_time(local_time const&) = default; 806 local_time(local_time&&) = default; 807 local_time& operator=(local_time const&) = default; 808 local_time& operator=(local_time&&) = default; 809 }; 810 811 bool operator==(const local_time& lhs, const local_time& rhs); 812 bool operator!=(const local_time& lhs, const local_time& rhs); 813 bool operator< (const local_time& lhs, const local_time& rhs); 814 bool operator<=(const local_time& lhs, const local_time& rhs); 815 bool operator> (const local_time& lhs, const local_time& rhs); 816 bool operator>=(const local_time& lhs, const local_time& rhs); 817 818 std::ostream& operator<<(std::ostream& os, const local_time& time); 819 std::string to_string(const local_time& time); 820 821 // ---------------------------------------------------------------------------- 822 823 struct time_offset 824 { 825 std::int8_t hour{0}; // [-12, 12] 826 std::int8_t minute{0}; // [-59, 59] 827 828 time_offset(int h, int m) 829 : hour {static_cast<std::int8_t>(h)}, 830 minute{static_cast<std::int8_t>(m)} 831 {} 832 833 operator std::chrono::minutes() const; 834 835 time_offset() = default; 836 ~time_offset() = default; 837 time_offset(time_offset const&) = default; 838 time_offset(time_offset&&) = default; 839 time_offset& operator=(time_offset const&) = default; 840 time_offset& operator=(time_offset&&) = default; 841 }; 842 843 bool operator==(const time_offset& lhs, const time_offset& rhs); 844 bool operator!=(const time_offset& lhs, const time_offset& rhs); 845 bool operator< (const time_offset& lhs, const time_offset& rhs); 846 bool operator<=(const time_offset& lhs, const time_offset& rhs); 847 bool operator> (const time_offset& lhs, const time_offset& rhs); 848 bool operator>=(const time_offset& lhs, const time_offset& rhs); 849 850 std::ostream& operator<<(std::ostream& os, const time_offset& offset); 851 852 std::string to_string(const time_offset& offset); 853 854 // ----------------------------------------------------------------------------- 855 856 struct local_datetime 857 { 858 local_date date{}; 859 local_time time{}; 860 861 local_datetime(local_date d, local_time t): date{d}, time{t} {} 862 863 explicit local_datetime(const std::tm& t): date{t}, time{t}{} 864 865 explicit local_datetime(const std::chrono::system_clock::time_point& tp); 866 explicit local_datetime(const std::time_t t); 867 868 operator std::chrono::system_clock::time_point() const; 869 operator std::time_t() const; 870 871 local_datetime() = default; 872 ~local_datetime() = default; 873 local_datetime(local_datetime const&) = default; 874 local_datetime(local_datetime&&) = default; 875 local_datetime& operator=(local_datetime const&) = default; 876 local_datetime& operator=(local_datetime&&) = default; 877 }; 878 879 bool operator==(const local_datetime& lhs, const local_datetime& rhs); 880 bool operator!=(const local_datetime& lhs, const local_datetime& rhs); 881 bool operator< (const local_datetime& lhs, const local_datetime& rhs); 882 bool operator<=(const local_datetime& lhs, const local_datetime& rhs); 883 bool operator> (const local_datetime& lhs, const local_datetime& rhs); 884 bool operator>=(const local_datetime& lhs, const local_datetime& rhs); 885 886 std::ostream& operator<<(std::ostream& os, const local_datetime& dt); 887 888 std::string to_string(const local_datetime& dt); 889 890 // ----------------------------------------------------------------------------- 891 892 struct offset_datetime 893 { 894 local_date date{}; 895 local_time time{}; 896 time_offset offset{}; 897 898 offset_datetime(local_date d, local_time t, time_offset o) 899 : date{d}, time{t}, offset{o} 900 {} 901 offset_datetime(const local_datetime& dt, time_offset o) 902 : date{dt.date}, time{dt.time}, offset{o} 903 {} 904 // use the current local timezone offset 905 explicit offset_datetime(const local_datetime& ld); 906 explicit offset_datetime(const std::chrono::system_clock::time_point& tp); 907 explicit offset_datetime(const std::time_t& t); 908 explicit offset_datetime(const std::tm& t); 909 910 operator std::chrono::system_clock::time_point() const; 911 912 operator std::time_t() const; 913 914 offset_datetime() = default; 915 ~offset_datetime() = default; 916 offset_datetime(offset_datetime const&) = default; 917 offset_datetime(offset_datetime&&) = default; 918 offset_datetime& operator=(offset_datetime const&) = default; 919 offset_datetime& operator=(offset_datetime&&) = default; 920 921 private: 922 923 static time_offset get_local_offset(const std::time_t* tp); 924 }; 925 926 bool operator==(const offset_datetime& lhs, const offset_datetime& rhs); 927 bool operator!=(const offset_datetime& lhs, const offset_datetime& rhs); 928 bool operator< (const offset_datetime& lhs, const offset_datetime& rhs); 929 bool operator<=(const offset_datetime& lhs, const offset_datetime& rhs); 930 bool operator> (const offset_datetime& lhs, const offset_datetime& rhs); 931 bool operator>=(const offset_datetime& lhs, const offset_datetime& rhs); 932 933 std::ostream& operator<<(std::ostream& os, const offset_datetime& dt); 934 935 std::string to_string(const offset_datetime& dt); 936 937 }//toml 938 #endif // TOML11_DATETIME_FWD_HPP 939 940 #if ! defined(TOML11_COMPILE_SOURCES) 941 #ifndef TOML11_DATETIME_IMPL_HPP 942 #define TOML11_DATETIME_IMPL_HPP 943 944 945 #include <array> 946 #include <iomanip> 947 #include <ostream> 948 #include <sstream> 949 #include <tuple> 950 951 #include <cstdlib> 952 #include <ctime> 953 954 namespace toml 955 { 956 957 // To avoid non-threadsafe std::localtime. In C11 (not C++11!), localtime_s is 958 // provided in the absolutely same purpose, but C++11 is actually not compatible 959 // with C11. We need to dispatch the function depending on the OS. 960 namespace detail 961 { 962 // TODO: find more sophisticated way to handle this 963 #if defined(_MSC_VER) 964 TOML11_INLINE std::tm localtime_s(const std::time_t* src) 965 { 966 std::tm dst; 967 const auto result = ::localtime_s(&dst, src); 968 if (result) { throw std::runtime_error("localtime_s failed."); } 969 return dst; 970 } 971 TOML11_INLINE std::tm gmtime_s(const std::time_t* src) 972 { 973 std::tm dst; 974 const auto result = ::gmtime_s(&dst, src); 975 if (result) { throw std::runtime_error("gmtime_s failed."); } 976 return dst; 977 } 978 #elif (defined(_POSIX_C_SOURCE) && _POSIX_C_SOURCE >= 1) || defined(_XOPEN_SOURCE) || defined(_BSD_SOURCE) || defined(_SVID_SOURCE) || defined(_POSIX_SOURCE) 979 TOML11_INLINE std::tm localtime_s(const std::time_t* src) 980 { 981 std::tm dst; 982 const auto result = ::localtime_r(src, &dst); 983 if (!result) { throw std::runtime_error("localtime_r failed."); } 984 return dst; 985 } 986 TOML11_INLINE std::tm gmtime_s(const std::time_t* src) 987 { 988 std::tm dst; 989 const auto result = ::gmtime_r(src, &dst); 990 if (!result) { throw std::runtime_error("gmtime_r failed."); } 991 return dst; 992 } 993 #else // fallback. not threadsafe 994 TOML11_INLINE std::tm localtime_s(const std::time_t* src) 995 { 996 const auto result = std::localtime(src); 997 if (!result) { throw std::runtime_error("localtime failed."); } 998 return *result; 999 } 1000 TOML11_INLINE std::tm gmtime_s(const std::time_t* src) 1001 { 1002 const auto result = std::gmtime(src); 1003 if (!result) { throw std::runtime_error("gmtime failed."); } 1004 return *result; 1005 } 1006 #endif 1007 } // detail 1008 1009 // ---------------------------------------------------------------------------- 1010 1011 TOML11_INLINE local_date::local_date(const std::chrono::system_clock::time_point& tp) 1012 { 1013 const auto t = std::chrono::system_clock::to_time_t(tp); 1014 const auto time = detail::localtime_s(&t); 1015 *this = local_date(time); 1016 } 1017 1018 TOML11_INLINE local_date::local_date(const std::time_t t) 1019 : local_date{std::chrono::system_clock::from_time_t(t)} 1020 {} 1021 1022 TOML11_INLINE local_date::operator std::chrono::system_clock::time_point() const 1023 { 1024 // std::mktime returns date as local time zone. no conversion needed 1025 std::tm t; 1026 t.tm_sec = 0; 1027 t.tm_min = 0; 1028 t.tm_hour = 0; 1029 t.tm_mday = static_cast<int>(this->day); 1030 t.tm_mon = static_cast<int>(this->month); 1031 t.tm_year = static_cast<int>(this->year) - 1900; 1032 t.tm_wday = 0; // the value will be ignored 1033 t.tm_yday = 0; // the value will be ignored 1034 t.tm_isdst = -1; 1035 return std::chrono::system_clock::from_time_t(std::mktime(&t)); 1036 } 1037 1038 TOML11_INLINE local_date::operator std::time_t() const 1039 { 1040 return std::chrono::system_clock::to_time_t( 1041 std::chrono::system_clock::time_point(*this)); 1042 } 1043 1044 TOML11_INLINE bool operator==(const local_date& lhs, const local_date& rhs) 1045 { 1046 return std::make_tuple(lhs.year, lhs.month, lhs.day) == 1047 std::make_tuple(rhs.year, rhs.month, rhs.day); 1048 } 1049 TOML11_INLINE bool operator!=(const local_date& lhs, const local_date& rhs) 1050 { 1051 return !(lhs == rhs); 1052 } 1053 TOML11_INLINE bool operator< (const local_date& lhs, const local_date& rhs) 1054 { 1055 return std::make_tuple(lhs.year, lhs.month, lhs.day) < 1056 std::make_tuple(rhs.year, rhs.month, rhs.day); 1057 } 1058 TOML11_INLINE bool operator<=(const local_date& lhs, const local_date& rhs) 1059 { 1060 return (lhs < rhs) || (lhs == rhs); 1061 } 1062 TOML11_INLINE bool operator> (const local_date& lhs, const local_date& rhs) 1063 { 1064 return !(lhs <= rhs); 1065 } 1066 TOML11_INLINE bool operator>=(const local_date& lhs, const local_date& rhs) 1067 { 1068 return !(lhs < rhs); 1069 } 1070 1071 TOML11_INLINE std::ostream& operator<<(std::ostream& os, const local_date& date) 1072 { 1073 os << std::setfill('0') << std::setw(4) << static_cast<int>(date.year ) << '-'; 1074 os << std::setfill('0') << std::setw(2) << static_cast<int>(date.month) + 1 << '-'; 1075 os << std::setfill('0') << std::setw(2) << static_cast<int>(date.day ) ; 1076 return os; 1077 } 1078 1079 TOML11_INLINE std::string to_string(const local_date& date) 1080 { 1081 std::ostringstream oss; 1082 oss.imbue(std::locale::classic()); 1083 oss << date; 1084 return oss.str(); 1085 } 1086 1087 // ----------------------------------------------------------------------------- 1088 1089 TOML11_INLINE local_time::operator std::chrono::nanoseconds() const 1090 { 1091 return std::chrono::nanoseconds (this->nanosecond) + 1092 std::chrono::microseconds(this->microsecond) + 1093 std::chrono::milliseconds(this->millisecond) + 1094 std::chrono::seconds(this->second) + 1095 std::chrono::minutes(this->minute) + 1096 std::chrono::hours(this->hour); 1097 } 1098 1099 TOML11_INLINE bool operator==(const local_time& lhs, const local_time& rhs) 1100 { 1101 return std::make_tuple(lhs.hour, lhs.minute, lhs.second, lhs.millisecond, lhs.microsecond, lhs.nanosecond) == 1102 std::make_tuple(rhs.hour, rhs.minute, rhs.second, rhs.millisecond, rhs.microsecond, rhs.nanosecond); 1103 } 1104 TOML11_INLINE bool operator!=(const local_time& lhs, const local_time& rhs) 1105 { 1106 return !(lhs == rhs); 1107 } 1108 TOML11_INLINE bool operator< (const local_time& lhs, const local_time& rhs) 1109 { 1110 return std::make_tuple(lhs.hour, lhs.minute, lhs.second, lhs.millisecond, lhs.microsecond, lhs.nanosecond) < 1111 std::make_tuple(rhs.hour, rhs.minute, rhs.second, rhs.millisecond, rhs.microsecond, rhs.nanosecond); 1112 } 1113 TOML11_INLINE bool operator<=(const local_time& lhs, const local_time& rhs) 1114 { 1115 return (lhs < rhs) || (lhs == rhs); 1116 } 1117 TOML11_INLINE bool operator> (const local_time& lhs, const local_time& rhs) 1118 { 1119 return !(lhs <= rhs); 1120 } 1121 TOML11_INLINE bool operator>=(const local_time& lhs, const local_time& rhs) 1122 { 1123 return !(lhs < rhs); 1124 } 1125 1126 TOML11_INLINE std::ostream& operator<<(std::ostream& os, const local_time& time) 1127 { 1128 os << std::setfill('0') << std::setw(2) << static_cast<int>(time.hour ) << ':'; 1129 os << std::setfill('0') << std::setw(2) << static_cast<int>(time.minute) << ':'; 1130 os << std::setfill('0') << std::setw(2) << static_cast<int>(time.second); 1131 if(time.millisecond != 0 || time.microsecond != 0 || time.nanosecond != 0) 1132 { 1133 os << '.'; 1134 os << std::setfill('0') << std::setw(3) << static_cast<int>(time.millisecond); 1135 if(time.microsecond != 0 || time.nanosecond != 0) 1136 { 1137 os << std::setfill('0') << std::setw(3) << static_cast<int>(time.microsecond); 1138 if(time.nanosecond != 0) 1139 { 1140 os << std::setfill('0') << std::setw(3) << static_cast<int>(time.nanosecond); 1141 } 1142 } 1143 } 1144 return os; 1145 } 1146 1147 TOML11_INLINE std::string to_string(const local_time& time) 1148 { 1149 std::ostringstream oss; 1150 oss.imbue(std::locale::classic()); 1151 oss << time; 1152 return oss.str(); 1153 } 1154 1155 // ---------------------------------------------------------------------------- 1156 1157 TOML11_INLINE time_offset::operator std::chrono::minutes() const 1158 { 1159 return std::chrono::minutes(this->minute) + 1160 std::chrono::hours(this->hour); 1161 } 1162 1163 TOML11_INLINE bool operator==(const time_offset& lhs, const time_offset& rhs) 1164 { 1165 return std::make_tuple(lhs.hour, lhs.minute) == 1166 std::make_tuple(rhs.hour, rhs.minute); 1167 } 1168 TOML11_INLINE bool operator!=(const time_offset& lhs, const time_offset& rhs) 1169 { 1170 return !(lhs == rhs); 1171 } 1172 TOML11_INLINE bool operator< (const time_offset& lhs, const time_offset& rhs) 1173 { 1174 return std::make_tuple(lhs.hour, lhs.minute) < 1175 std::make_tuple(rhs.hour, rhs.minute); 1176 } 1177 TOML11_INLINE bool operator<=(const time_offset& lhs, const time_offset& rhs) 1178 { 1179 return (lhs < rhs) || (lhs == rhs); 1180 } 1181 TOML11_INLINE bool operator> (const time_offset& lhs, const time_offset& rhs) 1182 { 1183 return !(lhs <= rhs); 1184 } 1185 TOML11_INLINE bool operator>=(const time_offset& lhs, const time_offset& rhs) 1186 { 1187 return !(lhs < rhs); 1188 } 1189 1190 TOML11_INLINE std::ostream& operator<<(std::ostream& os, const time_offset& offset) 1191 { 1192 if(offset.hour == 0 && offset.minute == 0) 1193 { 1194 os << 'Z'; 1195 return os; 1196 } 1197 int minute = static_cast<int>(offset.hour) * 60 + offset.minute; 1198 if(minute < 0){os << '-'; minute = std::abs(minute);} else {os << '+';} 1199 os << std::setfill('0') << std::setw(2) << minute / 60 << ':'; 1200 os << std::setfill('0') << std::setw(2) << minute % 60; 1201 return os; 1202 } 1203 1204 TOML11_INLINE std::string to_string(const time_offset& offset) 1205 { 1206 std::ostringstream oss; 1207 oss.imbue(std::locale::classic()); 1208 oss << offset; 1209 return oss.str(); 1210 } 1211 1212 // ----------------------------------------------------------------------------- 1213 1214 TOML11_INLINE local_datetime::local_datetime(const std::chrono::system_clock::time_point& tp) 1215 { 1216 const auto t = std::chrono::system_clock::to_time_t(tp); 1217 std::tm ltime = detail::localtime_s(&t); 1218 1219 this->date = local_date(ltime); 1220 this->time = local_time(ltime); 1221 1222 // std::tm lacks subsecond information, so diff between tp and tm 1223 // can be used to get millisecond & microsecond information. 1224 const auto t_diff = tp - 1225 std::chrono::system_clock::from_time_t(std::mktime(<ime)); 1226 this->time.millisecond = static_cast<std::uint16_t>( 1227 std::chrono::duration_cast<std::chrono::milliseconds>(t_diff).count()); 1228 this->time.microsecond = static_cast<std::uint16_t>( 1229 std::chrono::duration_cast<std::chrono::microseconds>(t_diff).count()); 1230 this->time.nanosecond = static_cast<std::uint16_t>( 1231 std::chrono::duration_cast<std::chrono::nanoseconds >(t_diff).count()); 1232 } 1233 1234 TOML11_INLINE local_datetime::local_datetime(const std::time_t t) 1235 : local_datetime{std::chrono::system_clock::from_time_t(t)} 1236 {} 1237 1238 TOML11_INLINE local_datetime::operator std::chrono::system_clock::time_point() const 1239 { 1240 using internal_duration = 1241 typename std::chrono::system_clock::time_point::duration; 1242 1243 // Normally DST begins at A.M. 3 or 4. If we re-use conversion operator 1244 // of local_date and local_time independently, the conversion fails if 1245 // it is the day when DST begins or ends. Since local_date considers the 1246 // time is 00:00 A.M. and local_time does not consider DST because it 1247 // does not have any date information. We need to consider both date and 1248 // time information at the same time to convert it correctly. 1249 1250 std::tm t; 1251 t.tm_sec = static_cast<int>(this->time.second); 1252 t.tm_min = static_cast<int>(this->time.minute); 1253 t.tm_hour = static_cast<int>(this->time.hour); 1254 t.tm_mday = static_cast<int>(this->date.day); 1255 t.tm_mon = static_cast<int>(this->date.month); 1256 t.tm_year = static_cast<int>(this->date.year) - 1900; 1257 t.tm_wday = 0; // the value will be ignored 1258 t.tm_yday = 0; // the value will be ignored 1259 t.tm_isdst = -1; 1260 1261 // std::mktime returns date as local time zone. no conversion needed 1262 auto dt = std::chrono::system_clock::from_time_t(std::mktime(&t)); 1263 dt += std::chrono::duration_cast<internal_duration>( 1264 std::chrono::milliseconds(this->time.millisecond) + 1265 std::chrono::microseconds(this->time.microsecond) + 1266 std::chrono::nanoseconds (this->time.nanosecond)); 1267 return dt; 1268 } 1269 1270 TOML11_INLINE local_datetime::operator std::time_t() const 1271 { 1272 return std::chrono::system_clock::to_time_t( 1273 std::chrono::system_clock::time_point(*this)); 1274 } 1275 1276 TOML11_INLINE bool operator==(const local_datetime& lhs, const local_datetime& rhs) 1277 { 1278 return std::make_tuple(lhs.date, lhs.time) == 1279 std::make_tuple(rhs.date, rhs.time); 1280 } 1281 TOML11_INLINE bool operator!=(const local_datetime& lhs, const local_datetime& rhs) 1282 { 1283 return !(lhs == rhs); 1284 } 1285 TOML11_INLINE bool operator< (const local_datetime& lhs, const local_datetime& rhs) 1286 { 1287 return std::make_tuple(lhs.date, lhs.time) < 1288 std::make_tuple(rhs.date, rhs.time); 1289 } 1290 TOML11_INLINE bool operator<=(const local_datetime& lhs, const local_datetime& rhs) 1291 { 1292 return (lhs < rhs) || (lhs == rhs); 1293 } 1294 TOML11_INLINE bool operator> (const local_datetime& lhs, const local_datetime& rhs) 1295 { 1296 return !(lhs <= rhs); 1297 } 1298 TOML11_INLINE bool operator>=(const local_datetime& lhs, const local_datetime& rhs) 1299 { 1300 return !(lhs < rhs); 1301 } 1302 1303 TOML11_INLINE std::ostream& operator<<(std::ostream& os, const local_datetime& dt) 1304 { 1305 os << dt.date << 'T' << dt.time; 1306 return os; 1307 } 1308 1309 TOML11_INLINE std::string to_string(const local_datetime& dt) 1310 { 1311 std::ostringstream oss; 1312 oss.imbue(std::locale::classic()); 1313 oss << dt; 1314 return oss.str(); 1315 } 1316 1317 // ----------------------------------------------------------------------------- 1318 1319 1320 TOML11_INLINE offset_datetime::offset_datetime(const local_datetime& ld) 1321 : date{ld.date}, time{ld.time}, offset{get_local_offset(nullptr)} 1322 // use the current local timezone offset 1323 {} 1324 TOML11_INLINE offset_datetime::offset_datetime(const std::chrono::system_clock::time_point& tp) 1325 : offset{0, 0} // use gmtime 1326 { 1327 const auto timet = std::chrono::system_clock::to_time_t(tp); 1328 const auto tm = detail::gmtime_s(&timet); 1329 this->date = local_date(tm); 1330 this->time = local_time(tm); 1331 } 1332 TOML11_INLINE offset_datetime::offset_datetime(const std::time_t& t) 1333 : offset{0, 0} // use gmtime 1334 { 1335 const auto tm = detail::gmtime_s(&t); 1336 this->date = local_date(tm); 1337 this->time = local_time(tm); 1338 } 1339 TOML11_INLINE offset_datetime::offset_datetime(const std::tm& t) 1340 : offset{0, 0} // assume gmtime 1341 { 1342 this->date = local_date(t); 1343 this->time = local_time(t); 1344 } 1345 1346 TOML11_INLINE offset_datetime::operator std::chrono::system_clock::time_point() const 1347 { 1348 // get date-time 1349 using internal_duration = 1350 typename std::chrono::system_clock::time_point::duration; 1351 1352 // first, convert it to local date-time information in the same way as 1353 // local_datetime does. later we will use time_t to adjust time offset. 1354 std::tm t; 1355 t.tm_sec = static_cast<int>(this->time.second); 1356 t.tm_min = static_cast<int>(this->time.minute); 1357 t.tm_hour = static_cast<int>(this->time.hour); 1358 t.tm_mday = static_cast<int>(this->date.day); 1359 t.tm_mon = static_cast<int>(this->date.month); 1360 t.tm_year = static_cast<int>(this->date.year) - 1900; 1361 t.tm_wday = 0; // the value will be ignored 1362 t.tm_yday = 0; // the value will be ignored 1363 t.tm_isdst = -1; 1364 const std::time_t tp_loc = std::mktime(std::addressof(t)); 1365 1366 auto tp = std::chrono::system_clock::from_time_t(tp_loc); 1367 tp += std::chrono::duration_cast<internal_duration>( 1368 std::chrono::milliseconds(this->time.millisecond) + 1369 std::chrono::microseconds(this->time.microsecond) + 1370 std::chrono::nanoseconds (this->time.nanosecond)); 1371 1372 // Since mktime uses local time zone, it should be corrected. 1373 // `12:00:00+09:00` means `03:00:00Z`. So mktime returns `03:00:00Z` if 1374 // we are in `+09:00` timezone. To represent `12:00:00Z` there, we need 1375 // to add `+09:00` to `03:00:00Z`. 1376 // Here, it uses the time_t converted from date-time info to handle 1377 // daylight saving time. 1378 const auto ofs = get_local_offset(std::addressof(tp_loc)); 1379 tp += std::chrono::hours (ofs.hour); 1380 tp += std::chrono::minutes(ofs.minute); 1381 1382 // We got `12:00:00Z` by correcting local timezone applied by mktime. 1383 // Then we will apply the offset. Let's say `12:00:00-08:00` is given. 1384 // And now, we have `12:00:00Z`. `12:00:00-08:00` means `20:00:00Z`. 1385 // So we need to subtract the offset. 1386 tp -= std::chrono::minutes(this->offset); 1387 return tp; 1388 } 1389 1390 TOML11_INLINE offset_datetime::operator std::time_t() const 1391 { 1392 return std::chrono::system_clock::to_time_t( 1393 std::chrono::system_clock::time_point(*this)); 1394 } 1395 1396 TOML11_INLINE time_offset offset_datetime::get_local_offset(const std::time_t* tp) 1397 { 1398 // get local timezone with the same date-time information as mktime 1399 const auto t = detail::localtime_s(tp); 1400 1401 std::array<char, 6> buf; 1402 const auto result = std::strftime(buf.data(), 6, "%z", &t); // +hhmm\0 1403 if(result != 5) 1404 { 1405 throw std::runtime_error("toml::offset_datetime: cannot obtain " 1406 "timezone information of current env"); 1407 } 1408 const int ofs = std::atoi(buf.data()); 1409 const int ofs_h = ofs / 100; 1410 const int ofs_m = ofs - (ofs_h * 100); 1411 return time_offset(ofs_h, ofs_m); 1412 } 1413 1414 TOML11_INLINE bool operator==(const offset_datetime& lhs, const offset_datetime& rhs) 1415 { 1416 return std::make_tuple(lhs.date, lhs.time, lhs.offset) == 1417 std::make_tuple(rhs.date, rhs.time, rhs.offset); 1418 } 1419 TOML11_INLINE bool operator!=(const offset_datetime& lhs, const offset_datetime& rhs) 1420 { 1421 return !(lhs == rhs); 1422 } 1423 TOML11_INLINE bool operator< (const offset_datetime& lhs, const offset_datetime& rhs) 1424 { 1425 return std::make_tuple(lhs.date, lhs.time, lhs.offset) < 1426 std::make_tuple(rhs.date, rhs.time, rhs.offset); 1427 } 1428 TOML11_INLINE bool operator<=(const offset_datetime& lhs, const offset_datetime& rhs) 1429 { 1430 return (lhs < rhs) || (lhs == rhs); 1431 } 1432 TOML11_INLINE bool operator> (const offset_datetime& lhs, const offset_datetime& rhs) 1433 { 1434 return !(lhs <= rhs); 1435 } 1436 TOML11_INLINE bool operator>=(const offset_datetime& lhs, const offset_datetime& rhs) 1437 { 1438 return !(lhs < rhs); 1439 } 1440 1441 TOML11_INLINE std::ostream& operator<<(std::ostream& os, const offset_datetime& dt) 1442 { 1443 os << dt.date << 'T' << dt.time << dt.offset; 1444 return os; 1445 } 1446 1447 TOML11_INLINE std::string to_string(const offset_datetime& dt) 1448 { 1449 std::ostringstream oss; 1450 oss.imbue(std::locale::classic()); 1451 oss << dt; 1452 return oss.str(); 1453 } 1454 1455 }//toml 1456 #endif // TOML11_DATETIME_IMPL_HPP 1457 #endif 1458 1459 #endif // TOML11_DATETIME_HPP 1460 #ifndef TOML11_COMPAT_HPP 1461 #define TOML11_COMPAT_HPP 1462 1463 1464 #include <algorithm> 1465 #include <iterator> 1466 #include <memory> 1467 #include <string> 1468 #include <type_traits> 1469 1470 #include <cassert> 1471 1472 #if TOML11_CPLUSPLUS_STANDARD_VERSION >= TOML11_CXX20_VALUE 1473 # if __has_include(<bit>) 1474 # include <bit> 1475 # endif 1476 #endif 1477 1478 #include <cstring> 1479 1480 // ---------------------------------------------------------------------------- 1481 1482 #if TOML11_CPLUSPLUS_STANDARD_VERSION >= TOML11_CXX14_VALUE 1483 # if __has_cpp_attribute(deprecated) 1484 # define TOML11_HAS_ATTR_DEPRECATED 1 1485 # endif 1486 #endif 1487 1488 #if defined(TOML11_HAS_ATTR_DEPRECATED) 1489 # define TOML11_DEPRECATED(msg) [[deprecated(msg)]] 1490 #elif defined(__GNUC__) 1491 # define TOML11_DEPRECATED(msg) __attribute__((deprecated(msg))) 1492 #elif defined(_MSC_VER) 1493 # define TOML11_DEPRECATED(msg) __declspec(deprecated(msg)) 1494 #else 1495 # define TOML11_DEPRECATED(msg) 1496 #endif 1497 1498 // ---------------------------------------------------------------------------- 1499 1500 #if defined(__cpp_if_constexpr) 1501 # if __cpp_if_constexpr >= 201606L 1502 # define TOML11_HAS_CONSTEXPR_IF 1 1503 # endif 1504 #endif 1505 1506 #if defined(TOML11_HAS_CONSTEXPR_IF) 1507 # define TOML11_CONSTEXPR_IF if constexpr 1508 #else 1509 # define TOML11_CONSTEXPR_IF if 1510 #endif 1511 1512 // ---------------------------------------------------------------------------- 1513 1514 #if TOML11_CPLUSPLUS_STANDARD_VERSION >= TOML11_CXX14_VALUE 1515 # if defined(__cpp_lib_make_unique) 1516 # if __cpp_lib_make_unique >= 201304L 1517 # define TOML11_HAS_STD_MAKE_UNIQUE 1 1518 # endif 1519 # endif 1520 #endif 1521 1522 namespace toml 1523 { 1524 namespace cxx 1525 { 1526 1527 #if defined(TOML11_HAS_STD_MAKE_UNIQUE) 1528 1529 using std::make_unique; 1530 1531 #else 1532 1533 template<typename T, typename ... Ts> 1534 std::unique_ptr<T> make_unique(Ts&& ... args) 1535 { 1536 return std::unique_ptr<T>(new T(std::forward<Ts>(args)...)); 1537 } 1538 1539 #endif // TOML11_HAS_STD_MAKE_UNIQUE 1540 1541 } // cxx 1542 } // toml 1543 1544 // --------------------------------------------------------------------------- 1545 1546 #if TOML11_CPLUSPLUS_STANDARD_VERSION >= TOML11_CXX14_VALUE 1547 # if defined(__cpp_lib_make_reverse_iterator) 1548 # if __cpp_lib_make_reverse_iterator >= 201402L 1549 # define TOML11_HAS_STD_MAKE_REVERSE_ITERATOR 1 1550 # endif 1551 # endif 1552 #endif 1553 1554 namespace toml 1555 { 1556 namespace cxx 1557 { 1558 # if defined(TOML11_HAS_STD_MAKE_REVERSE_ITERATOR) 1559 1560 using std::make_reverse_iterator; 1561 1562 #else 1563 1564 template<typename Iterator> 1565 std::reverse_iterator<Iterator> make_reverse_iterator(Iterator iter) 1566 { 1567 return std::reverse_iterator<Iterator>(iter); 1568 } 1569 1570 #endif // TOML11_HAS_STD_MAKE_REVERSE_ITERATOR 1571 1572 } // cxx 1573 } // toml 1574 1575 // --------------------------------------------------------------------------- 1576 1577 #if TOML11_CPLUSPLUS_STANDARD_VERSION >= TOML11_CXX20_VALUE 1578 # if defined(__cpp_lib_clamp) 1579 # if __cpp_lib_clamp >= 201603L 1580 # define TOML11_HAS_STD_CLAMP 1 1581 # endif 1582 # endif 1583 #endif 1584 1585 namespace toml 1586 { 1587 namespace cxx 1588 { 1589 #if defined(TOML11_HAS_STD_CLAMP) 1590 1591 using std::clamp; 1592 1593 #else 1594 1595 template<typename T> 1596 T clamp(const T& x, const T& low, const T& high) noexcept 1597 { 1598 assert(low <= high); 1599 return (std::min)((std::max)(x, low), high); 1600 } 1601 1602 #endif // TOML11_HAS_STD_CLAMP 1603 1604 } // cxx 1605 } // toml 1606 1607 // --------------------------------------------------------------------------- 1608 1609 #if TOML11_CPLUSPLUS_STANDARD_VERSION >= TOML11_CXX20_VALUE 1610 # if defined(__cpp_lib_bit_cast) 1611 # if __cpp_lib_bit_cast >= 201806L 1612 # define TOML11_HAS_STD_BIT_CAST 1 1613 # endif 1614 # endif 1615 #endif 1616 1617 namespace toml 1618 { 1619 namespace cxx 1620 { 1621 #if defined(TOML11_HAS_STD_BIT_CAST) 1622 1623 using std::bit_cast; 1624 1625 #else 1626 1627 template<typename U, typename T> 1628 U bit_cast(const T& x) noexcept 1629 { 1630 static_assert(sizeof(T) == sizeof(U), ""); 1631 static_assert(std::is_default_constructible<T>::value, ""); 1632 1633 U z; 1634 std::memcpy(reinterpret_cast<char*>(std::addressof(z)), 1635 reinterpret_cast<const char*>(std::addressof(x)), 1636 sizeof(T)); 1637 1638 return z; 1639 } 1640 1641 #endif // TOML11_HAS_STD_BIT_CAST 1642 1643 } // cxx 1644 } // toml 1645 1646 // --------------------------------------------------------------------------- 1647 // C++20 remove_cvref_t 1648 1649 #if TOML11_CPLUSPLUS_STANDARD_VERSION >= TOML11_CXX20_VALUE 1650 # if defined(__cpp_lib_remove_cvref) 1651 # if __cpp_lib_remove_cvref >= 201711L 1652 # define TOML11_HAS_STD_REMOVE_CVREF 1 1653 # endif 1654 # endif 1655 #endif 1656 1657 namespace toml 1658 { 1659 namespace cxx 1660 { 1661 #if defined(TOML11_HAS_STD_REMOVE_CVREF) 1662 1663 using std::remove_cvref; 1664 using std::remove_cvref_t; 1665 1666 #else 1667 1668 template<typename T> 1669 struct remove_cvref 1670 { 1671 using type = typename std::remove_cv< 1672 typename std::remove_reference<T>::type>::type; 1673 }; 1674 1675 template<typename T> 1676 using remove_cvref_t = typename remove_cvref<T>::type; 1677 1678 #endif // TOML11_HAS_STD_REMOVE_CVREF 1679 1680 } // cxx 1681 } // toml 1682 1683 // --------------------------------------------------------------------------- 1684 // C++17 and/or/not 1685 1686 #if TOML11_CPLUSPLUS_STANDARD_VERSION >= TOML11_CXX17_VALUE 1687 # if defined(__cpp_lib_logical_traits) 1688 # if __cpp_lib_logical_traits >= 201510L 1689 # define TOML11_HAS_STD_CONJUNCTION 1 1690 # endif 1691 # endif 1692 #endif 1693 1694 namespace toml 1695 { 1696 namespace cxx 1697 { 1698 #if defined(TOML11_HAS_STD_CONJUNCTION) 1699 1700 using std::conjunction; 1701 using std::disjunction; 1702 using std::negation; 1703 1704 #else 1705 1706 template<typename ...> struct conjunction : std::true_type{}; 1707 template<typename T> struct conjunction<T> : T{}; 1708 template<typename T, typename ... Ts> 1709 struct conjunction<T, Ts...> : 1710 std::conditional<static_cast<bool>(T::value), conjunction<Ts...>, T>::type 1711 {}; 1712 1713 template<typename ...> struct disjunction : std::false_type{}; 1714 template<typename T> struct disjunction<T> : T {}; 1715 template<typename T, typename ... Ts> 1716 struct disjunction<T, Ts...> : 1717 std::conditional<static_cast<bool>(T::value), T, disjunction<Ts...>>::type 1718 {}; 1719 1720 template<typename T> 1721 struct negation : std::integral_constant<bool, !static_cast<bool>(T::value)>{}; 1722 1723 #endif // TOML11_HAS_STD_CONJUNCTION 1724 1725 } // cxx 1726 } // toml 1727 1728 // --------------------------------------------------------------------------- 1729 // C++14 index_sequence 1730 1731 #if TOML11_CPLUSPLUS_STANDARD_VERSION >= TOML11_CXX14_VALUE 1732 # if defined(__cpp_lib_integer_sequence) 1733 # if __cpp_lib_integer_sequence >= 201304L 1734 # define TOML11_HAS_STD_INTEGER_SEQUENCE 1 1735 # endif 1736 # endif 1737 #endif 1738 1739 namespace toml 1740 { 1741 namespace cxx 1742 { 1743 #if defined(TOML11_HAS_STD_INTEGER_SEQUENCE) 1744 1745 using std::index_sequence; 1746 using std::make_index_sequence; 1747 1748 #else 1749 1750 template<std::size_t ... Ns> struct index_sequence{}; 1751 1752 template<bool B, std::size_t N, typename T> 1753 struct double_index_sequence; 1754 1755 template<std::size_t N, std::size_t ... Is> 1756 struct double_index_sequence<true, N, index_sequence<Is...>> 1757 { 1758 using type = index_sequence<Is..., (Is+N)..., N*2>; 1759 }; 1760 template<std::size_t N, std::size_t ... Is> 1761 struct double_index_sequence<false, N, index_sequence<Is...>> 1762 { 1763 using type = index_sequence<Is..., (Is+N)...>; 1764 }; 1765 1766 template<std::size_t N> 1767 struct index_sequence_maker 1768 { 1769 using type = typename double_index_sequence< 1770 N % 2 == 1, N/2, typename index_sequence_maker<N/2>::type 1771 >::type; 1772 }; 1773 template<> 1774 struct index_sequence_maker<0> 1775 { 1776 using type = index_sequence<>; 1777 }; 1778 1779 template<std::size_t N> 1780 using make_index_sequence = typename index_sequence_maker<N>::type; 1781 1782 #endif // TOML11_HAS_STD_INTEGER_SEQUENCE 1783 1784 } // cxx 1785 } // toml 1786 1787 // --------------------------------------------------------------------------- 1788 // C++14 enable_if_t 1789 1790 #if TOML11_CPLUSPLUS_STANDARD_VERSION >= TOML11_CXX14_VALUE 1791 # if defined(__cpp_lib_transformation_trait_aliases) 1792 # if __cpp_lib_transformation_trait_aliases >= 201304L 1793 # define TOML11_HAS_STD_ENABLE_IF_T 1 1794 # endif 1795 # endif 1796 #endif 1797 1798 namespace toml 1799 { 1800 namespace cxx 1801 { 1802 #if defined(TOML11_HAS_STD_ENABLE_IF_T) 1803 1804 using std::enable_if_t; 1805 1806 #else 1807 1808 template<bool B, typename T> 1809 using enable_if_t = typename std::enable_if<B, T>::type; 1810 1811 #endif // TOML11_HAS_STD_ENABLE_IF_T 1812 1813 } // cxx 1814 } // toml 1815 1816 // --------------------------------------------------------------------------- 1817 // return_type_of_t 1818 1819 #if TOML11_CPLUSPLUS_STANDARD_VERSION >= TOML11_CXX17_VALUE 1820 # if defined(__cpp_lib_is_invocable) 1821 # if __cpp_lib_is_invocable >= 201703 1822 # define TOML11_HAS_STD_INVOKE_RESULT 1 1823 # endif 1824 # endif 1825 #endif 1826 1827 namespace toml 1828 { 1829 namespace cxx 1830 { 1831 #if defined(TOML11_HAS_STD_INVOKE_RESULT) 1832 1833 template<typename F, typename ... Args> 1834 using return_type_of_t = std::invoke_result_t<F, Args...>; 1835 1836 #else 1837 1838 // result_of is deprecated after C++17 1839 template<typename F, typename ... Args> 1840 using return_type_of_t = typename std::result_of<F(Args...)>::type; 1841 1842 #endif // TOML11_HAS_STD_INVOKE_RESULT 1843 1844 } // cxx 1845 } // toml 1846 1847 // ---------------------------------------------------------------------------- 1848 // (subset of) source_location 1849 1850 #if TOML11_CPLUSPLUS_STANDARD_VERSION >= 202002L 1851 # if __has_include(<source_location>) 1852 # define TOML11_HAS_STD_SOURCE_LOCATION 1853 # endif // has_include 1854 #endif // c++20 1855 1856 #if ! defined(TOML11_HAS_STD_SOURCE_LOCATION) 1857 # if defined(__GNUC__) && ! defined(__clang__) 1858 # if TOML11_CPLUSPLUS_STANDARD_VERSION >= TOML11_CXX14_VALUE 1859 # if __has_include(<experimental/source_location>) 1860 # define TOML11_HAS_EXPERIMENTAL_SOURCE_LOCATION 1861 # endif 1862 # endif 1863 # endif // GNU g++ 1864 #endif // not TOML11_HAS_STD_SOURCE_LOCATION 1865 1866 #if ! defined(TOML11_HAS_STD_SOURCE_LOCATION) && ! defined(TOML11_HAS_EXPERIMENTAL_SOURCE_LOCATION) 1867 # if defined(__GNUC__) && ! defined(__clang__) 1868 # if (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 9)) 1869 # define TOML11_HAS_BUILTIN_FILE_LINE 1 1870 # define TOML11_BUILTIN_LINE_TYPE int 1871 # endif 1872 # elif defined(__clang__) // clang 9.0.0 implements builtin_FILE/LINE 1873 # if __has_builtin(__builtin_FILE) && __has_builtin(__builtin_LINE) 1874 # define TOML11_HAS_BUILTIN_FILE_LINE 1 1875 # define TOML11_BUILTIN_LINE_TYPE unsigned int 1876 # endif 1877 # elif defined(_MSVC_LANG) && defined(_MSC_VER) 1878 # if _MSC_VER > 1926 1879 # define TOML11_HAS_BUILTIN_FILE_LINE 1 1880 # define TOML11_BUILTIN_LINE_TYPE int 1881 # endif 1882 # endif 1883 #endif 1884 1885 #if defined(TOML11_HAS_STD_SOURCE_LOCATION) 1886 #include <source_location> 1887 namespace toml 1888 { 1889 namespace cxx 1890 { 1891 using source_location = std::source_location; 1892 1893 inline std::string to_string(const source_location& loc) 1894 { 1895 return std::string(" at line ") + std::to_string(loc.line()) + 1896 std::string(" in file ") + std::string(loc.file_name()); 1897 } 1898 } // cxx 1899 } // toml 1900 #elif defined(TOML11_HAS_EXPERIMENTAL_SOURCE_LOCATION) 1901 #include <experimental/source_location> 1902 namespace toml 1903 { 1904 namespace cxx 1905 { 1906 using source_location = std::experimental::source_location; 1907 1908 inline std::string to_string(const source_location& loc) 1909 { 1910 return std::string(" at line ") + std::to_string(loc.line()) + 1911 std::string(" in file ") + std::string(loc.file_name()); 1912 } 1913 } // cxx 1914 } // toml 1915 #elif defined(TOML11_HAS_BUILTIN_FILE_LINE) 1916 namespace toml 1917 { 1918 namespace cxx 1919 { 1920 struct source_location 1921 { 1922 using line_type = TOML11_BUILTIN_LINE_TYPE; 1923 static source_location current(const line_type line = __builtin_LINE(), 1924 const char* file = __builtin_FILE()) 1925 { 1926 return source_location(line, file); 1927 } 1928 1929 source_location(const line_type line, const char* file) 1930 : line_(line), file_name_(file) 1931 {} 1932 1933 line_type line() const noexcept {return line_;} 1934 const char* file_name() const noexcept {return file_name_;} 1935 1936 private: 1937 1938 line_type line_; 1939 const char* file_name_; 1940 }; 1941 1942 inline std::string to_string(const source_location& loc) 1943 { 1944 return std::string(" at line ") + std::to_string(loc.line()) + 1945 std::string(" in file ") + std::string(loc.file_name()); 1946 } 1947 } // cxx 1948 } // toml 1949 #else // no builtin 1950 namespace toml 1951 { 1952 namespace cxx 1953 { 1954 struct source_location 1955 { 1956 static source_location current() { return source_location{}; } 1957 }; 1958 1959 inline std::string to_string(const source_location&) 1960 { 1961 return std::string(""); 1962 } 1963 } // cxx 1964 } // toml 1965 #endif // TOML11_HAS_STD_SOURCE_LOCATION 1966 1967 // ---------------------------------------------------------------------------- 1968 // (subset of) optional 1969 1970 #if TOML11_CPLUSPLUS_STANDARD_VERSION >= TOML11_CXX17_VALUE 1971 # if __has_include(<optional>) 1972 # include <optional> 1973 # endif // has_include(optional) 1974 #endif // C++17 1975 1976 #if TOML11_CPLUSPLUS_STANDARD_VERSION >= TOML11_CXX17_VALUE 1977 # if defined(__cpp_lib_optional) 1978 # if __cpp_lib_optional >= 201606L 1979 # define TOML11_HAS_STD_OPTIONAL 1 1980 # endif 1981 # endif 1982 #endif 1983 1984 #if defined(TOML11_HAS_STD_OPTIONAL) 1985 1986 namespace toml 1987 { 1988 namespace cxx 1989 { 1990 using std::optional; 1991 1992 inline std::nullopt_t make_nullopt() {return std::nullopt;} 1993 1994 template<typename charT, typename traitsT> 1995 std::basic_ostream<charT, traitsT>& 1996 operator<<(std::basic_ostream<charT, traitsT>& os, const std::nullopt_t&) 1997 { 1998 os << "nullopt"; 1999 return os; 2000 } 2001 2002 } // cxx 2003 } // toml 2004 2005 #else // TOML11_HAS_STD_OPTIONAL 2006 2007 namespace toml 2008 { 2009 namespace cxx 2010 { 2011 2012 struct nullopt_t{}; 2013 inline nullopt_t make_nullopt() {return nullopt_t{};} 2014 2015 inline bool operator==(const nullopt_t&, const nullopt_t&) noexcept {return true;} 2016 inline bool operator!=(const nullopt_t&, const nullopt_t&) noexcept {return false;} 2017 inline bool operator< (const nullopt_t&, const nullopt_t&) noexcept {return false;} 2018 inline bool operator<=(const nullopt_t&, const nullopt_t&) noexcept {return true;} 2019 inline bool operator> (const nullopt_t&, const nullopt_t&) noexcept {return false;} 2020 inline bool operator>=(const nullopt_t&, const nullopt_t&) noexcept {return true;} 2021 2022 template<typename charT, typename traitsT> 2023 std::basic_ostream<charT, traitsT>& 2024 operator<<(std::basic_ostream<charT, traitsT>& os, const nullopt_t&) 2025 { 2026 os << "nullopt"; 2027 return os; 2028 } 2029 2030 template<typename T> 2031 class optional 2032 { 2033 public: 2034 2035 using value_type = T; 2036 2037 public: 2038 2039 optional() noexcept : has_value_(false), null_('\0') {} 2040 optional(nullopt_t) noexcept : has_value_(false), null_('\0') {} 2041 2042 optional(const T& x): has_value_(true), value_(x) {} 2043 optional(T&& x): has_value_(true), value_(std::move(x)) {} 2044 2045 template<typename U, enable_if_t<std::is_constructible<T, U>::value, std::nullptr_t> = nullptr> 2046 explicit optional(U&& x): has_value_(true), value_(std::forward<U>(x)) {} 2047 2048 optional(const optional& rhs): has_value_(rhs.has_value_) 2049 { 2050 if(rhs.has_value_) 2051 { 2052 this->assigner(rhs.value_); 2053 } 2054 } 2055 optional(optional&& rhs): has_value_(rhs.has_value_) 2056 { 2057 if(this->has_value_) 2058 { 2059 this->assigner(std::move(rhs.value_)); 2060 } 2061 } 2062 2063 optional& operator=(const optional& rhs) 2064 { 2065 if(this == std::addressof(rhs)) {return *this;} 2066 2067 this->cleanup(); 2068 this->has_value_ = rhs.has_value_; 2069 if(this->has_value_) 2070 { 2071 this->assigner(rhs.value_); 2072 } 2073 return *this; 2074 } 2075 optional& operator=(optional&& rhs) 2076 { 2077 if(this == std::addressof(rhs)) {return *this;} 2078 2079 this->cleanup(); 2080 this->has_value_ = rhs.has_value_; 2081 if(this->has_value_) 2082 { 2083 this->assigner(std::move(rhs.value_)); 2084 } 2085 return *this; 2086 } 2087 2088 template<typename U, enable_if_t<conjunction< 2089 negation<std::is_same<T, U>>, std::is_constructible<T, U> 2090 >::value, std::nullptr_t> = nullptr> 2091 explicit optional(const optional<U>& rhs): has_value_(rhs.has_value_), null_('\0') 2092 { 2093 if(rhs.has_value_) 2094 { 2095 this->assigner(rhs.value_); 2096 } 2097 } 2098 template<typename U, enable_if_t<conjunction< 2099 negation<std::is_same<T, U>>, std::is_constructible<T, U> 2100 >::value, std::nullptr_t> = nullptr> 2101 explicit optional(optional<U>&& rhs): has_value_(rhs.has_value_), null_('\0') 2102 { 2103 if(this->has_value_) 2104 { 2105 this->assigner(std::move(rhs.value_)); 2106 } 2107 } 2108 2109 template<typename U, enable_if_t<conjunction< 2110 negation<std::is_same<T, U>>, std::is_constructible<T, U> 2111 >::value, std::nullptr_t> = nullptr> 2112 optional& operator=(const optional<U>& rhs) 2113 { 2114 if(this == std::addressof(rhs)) {return *this;} 2115 2116 this->cleanup(); 2117 this->has_value_ = rhs.has_value_; 2118 if(this->has_value_) 2119 { 2120 this->assigner(rhs.value_); 2121 } 2122 return *this; 2123 } 2124 2125 template<typename U, enable_if_t<conjunction< 2126 negation<std::is_same<T, U>>, std::is_constructible<T, U> 2127 >::value, std::nullptr_t> = nullptr> 2128 optional& operator=(optional<U>&& rhs) 2129 { 2130 if(this == std::addressof(rhs)) {return *this;} 2131 2132 this->cleanup(); 2133 this->has_value_ = rhs.has_value_; 2134 if(this->has_value_) 2135 { 2136 this->assigner(std::move(rhs.value_)); 2137 } 2138 return *this; 2139 } 2140 ~optional() noexcept 2141 { 2142 this->cleanup(); 2143 } 2144 2145 explicit operator bool() const noexcept 2146 { 2147 return has_value_; 2148 } 2149 2150 bool has_value() const noexcept {return has_value_;} 2151 2152 value_type const& value(source_location loc = source_location::current()) const 2153 { 2154 if( ! this->has_value_) 2155 { 2156 throw std::runtime_error("optional::value(): bad_unwrap" + to_string(loc)); 2157 } 2158 return this->value_; 2159 } 2160 value_type& value(source_location loc = source_location::current()) 2161 { 2162 if( ! this->has_value_) 2163 { 2164 throw std::runtime_error("optional::value(): bad_unwrap" + to_string(loc)); 2165 } 2166 return this->value_; 2167 } 2168 2169 value_type const& value_or(const value_type& opt) const 2170 { 2171 if(this->has_value_) {return this->value_;} else {return opt;} 2172 } 2173 value_type& value_or(value_type& opt) 2174 { 2175 if(this->has_value_) {return this->value_;} else {return opt;} 2176 } 2177 2178 private: 2179 2180 void cleanup() noexcept 2181 { 2182 if(this->has_value_) 2183 { 2184 value_.~T(); 2185 } 2186 } 2187 2188 template<typename U> 2189 void assigner(U&& x) 2190 { 2191 const auto tmp = ::new(std::addressof(this->value_)) value_type(std::forward<U>(x)); 2192 assert(tmp == std::addressof(this->value_)); 2193 (void)tmp; 2194 } 2195 2196 private: 2197 2198 bool has_value_; 2199 union 2200 { 2201 char null_; 2202 T value_; 2203 }; 2204 }; 2205 } // cxx 2206 } // toml 2207 #endif // TOML11_HAS_STD_OPTIONAL 2208 2209 #endif // TOML11_COMPAT_HPP 2210 #ifndef TOML11_VALUE_T_HPP 2211 #define TOML11_VALUE_T_HPP 2212 2213 #ifndef TOML11_VALUE_T_FWD_HPP 2214 #define TOML11_VALUE_T_FWD_HPP 2215 2216 2217 #include <iosfwd> 2218 #include <string> 2219 #include <type_traits> 2220 2221 #include <cstdint> 2222 2223 namespace toml 2224 { 2225 2226 // forward decl 2227 template<typename TypeConfig> 2228 class basic_value; 2229 2230 // ---------------------------------------------------------------------------- 2231 // enum representing toml types 2232 2233 enum class value_t : std::uint8_t 2234 { 2235 empty = 0, 2236 boolean = 1, 2237 integer = 2, 2238 floating = 3, 2239 string = 4, 2240 offset_datetime = 5, 2241 local_datetime = 6, 2242 local_date = 7, 2243 local_time = 8, 2244 array = 9, 2245 table = 10 2246 }; 2247 2248 std::ostream& operator<<(std::ostream& os, value_t t); 2249 std::string to_string(value_t t); 2250 2251 2252 // ---------------------------------------------------------------------------- 2253 // meta functions for internal use 2254 2255 namespace detail 2256 { 2257 2258 template<value_t V> 2259 using value_t_constant = std::integral_constant<value_t, V>; 2260 2261 template<typename T, typename Value> 2262 struct type_to_enum : value_t_constant<value_t::empty> {}; 2263 2264 template<typename V> struct type_to_enum<typename V::boolean_type , V> : value_t_constant<value_t::boolean > {}; 2265 template<typename V> struct type_to_enum<typename V::integer_type , V> : value_t_constant<value_t::integer > {}; 2266 template<typename V> struct type_to_enum<typename V::floating_type , V> : value_t_constant<value_t::floating > {}; 2267 template<typename V> struct type_to_enum<typename V::string_type , V> : value_t_constant<value_t::string > {}; 2268 template<typename V> struct type_to_enum<typename V::offset_datetime_type, V> : value_t_constant<value_t::offset_datetime> {}; 2269 template<typename V> struct type_to_enum<typename V::local_datetime_type , V> : value_t_constant<value_t::local_datetime > {}; 2270 template<typename V> struct type_to_enum<typename V::local_date_type , V> : value_t_constant<value_t::local_date > {}; 2271 template<typename V> struct type_to_enum<typename V::local_time_type , V> : value_t_constant<value_t::local_time > {}; 2272 template<typename V> struct type_to_enum<typename V::array_type , V> : value_t_constant<value_t::array > {}; 2273 template<typename V> struct type_to_enum<typename V::table_type , V> : value_t_constant<value_t::table > {}; 2274 2275 template<value_t V, typename Value> 2276 struct enum_to_type { using type = void; }; 2277 2278 template<typename V> struct enum_to_type<value_t::boolean , V> { using type = typename V::boolean_type ; }; 2279 template<typename V> struct enum_to_type<value_t::integer , V> { using type = typename V::integer_type ; }; 2280 template<typename V> struct enum_to_type<value_t::floating , V> { using type = typename V::floating_type ; }; 2281 template<typename V> struct enum_to_type<value_t::string , V> { using type = typename V::string_type ; }; 2282 template<typename V> struct enum_to_type<value_t::offset_datetime, V> { using type = typename V::offset_datetime_type; }; 2283 template<typename V> struct enum_to_type<value_t::local_datetime , V> { using type = typename V::local_datetime_type ; }; 2284 template<typename V> struct enum_to_type<value_t::local_date , V> { using type = typename V::local_date_type ; }; 2285 template<typename V> struct enum_to_type<value_t::local_time , V> { using type = typename V::local_time_type ; }; 2286 template<typename V> struct enum_to_type<value_t::array , V> { using type = typename V::array_type ; }; 2287 template<typename V> struct enum_to_type<value_t::table , V> { using type = typename V::table_type ; }; 2288 2289 template<value_t V, typename Value> 2290 using enum_to_type_t = typename enum_to_type<V, Value>::type; 2291 2292 template<value_t V> 2293 struct enum_to_fmt_type { using type = void; }; 2294 2295 template<> struct enum_to_fmt_type<value_t::boolean > { using type = boolean_format_info ; }; 2296 template<> struct enum_to_fmt_type<value_t::integer > { using type = integer_format_info ; }; 2297 template<> struct enum_to_fmt_type<value_t::floating > { using type = floating_format_info ; }; 2298 template<> struct enum_to_fmt_type<value_t::string > { using type = string_format_info ; }; 2299 template<> struct enum_to_fmt_type<value_t::offset_datetime> { using type = offset_datetime_format_info; }; 2300 template<> struct enum_to_fmt_type<value_t::local_datetime > { using type = local_datetime_format_info ; }; 2301 template<> struct enum_to_fmt_type<value_t::local_date > { using type = local_date_format_info ; }; 2302 template<> struct enum_to_fmt_type<value_t::local_time > { using type = local_time_format_info ; }; 2303 template<> struct enum_to_fmt_type<value_t::array > { using type = array_format_info ; }; 2304 template<> struct enum_to_fmt_type<value_t::table > { using type = table_format_info ; }; 2305 2306 template<value_t V> 2307 using enum_to_fmt_type_t = typename enum_to_fmt_type<V>::type; 2308 2309 template<typename T, typename Value> 2310 struct is_exact_toml_type0 : cxx::disjunction< 2311 std::is_same<T, typename Value::boolean_type >, 2312 std::is_same<T, typename Value::integer_type >, 2313 std::is_same<T, typename Value::floating_type >, 2314 std::is_same<T, typename Value::string_type >, 2315 std::is_same<T, typename Value::offset_datetime_type>, 2316 std::is_same<T, typename Value::local_datetime_type >, 2317 std::is_same<T, typename Value::local_date_type >, 2318 std::is_same<T, typename Value::local_time_type >, 2319 std::is_same<T, typename Value::array_type >, 2320 std::is_same<T, typename Value::table_type > 2321 >{}; 2322 template<typename T, typename V> struct is_exact_toml_type: is_exact_toml_type0<cxx::remove_cvref_t<T>, V> {}; 2323 template<typename T, typename V> struct is_not_toml_type : cxx::negation<is_exact_toml_type<T, V>> {}; 2324 2325 } // namespace detail 2326 } // namespace toml 2327 #endif // TOML11_VALUE_T_FWD_HPP 2328 2329 #if ! defined(TOML11_COMPILE_SOURCES) 2330 #ifndef TOML11_VALUE_T_IMPL_HPP 2331 #define TOML11_VALUE_T_IMPL_HPP 2332 2333 2334 #include <ostream> 2335 #include <sstream> 2336 #include <string> 2337 2338 namespace toml 2339 { 2340 2341 TOML11_INLINE std::ostream& operator<<(std::ostream& os, value_t t) 2342 { 2343 switch(t) 2344 { 2345 case value_t::boolean : os << "boolean"; return os; 2346 case value_t::integer : os << "integer"; return os; 2347 case value_t::floating : os << "floating"; return os; 2348 case value_t::string : os << "string"; return os; 2349 case value_t::offset_datetime : os << "offset_datetime"; return os; 2350 case value_t::local_datetime : os << "local_datetime"; return os; 2351 case value_t::local_date : os << "local_date"; return os; 2352 case value_t::local_time : os << "local_time"; return os; 2353 case value_t::array : os << "array"; return os; 2354 case value_t::table : os << "table"; return os; 2355 case value_t::empty : os << "empty"; return os; 2356 default : os << "unknown"; return os; 2357 } 2358 } 2359 2360 TOML11_INLINE std::string to_string(value_t t) 2361 { 2362 std::ostringstream oss; 2363 oss << t; 2364 return oss.str(); 2365 } 2366 2367 } // namespace toml 2368 #endif // TOML11_VALUE_T_IMPL_HPP 2369 #endif 2370 2371 #endif // TOML11_VALUE_T_HPP 2372 #ifndef TOML11_STORAGE_HPP 2373 #define TOML11_STORAGE_HPP 2374 2375 2376 namespace toml 2377 { 2378 namespace detail 2379 { 2380 2381 // It owns a pointer to T. It does deep-copy when copied. 2382 // This struct is introduced to implement a recursive type. 2383 // 2384 // `toml::value` contains `std::vector<toml::value>` to represent a toml array. 2385 // But, in the definition of `toml::value`, `toml::value` is still incomplete. 2386 // `std::vector` of an incomplete type is not allowed in C++11 (it is allowed 2387 // after C++17). To avoid this, we need to use a pointer to `toml::value`, like 2388 // `std::vector<std::unique_ptr<toml::value>>`. Although `std::unique_ptr` is 2389 // noncopyable, we want to make `toml::value` copyable. `storage` is introduced 2390 // to resolve those problems. 2391 template<typename T> 2392 struct storage 2393 { 2394 using value_type = T; 2395 2396 explicit storage(value_type v): ptr_(cxx::make_unique<T>(std::move(v))) {} 2397 ~storage() = default; 2398 2399 storage(const storage& rhs): ptr_(cxx::make_unique<T>(*rhs.ptr_)) {} 2400 storage& operator=(const storage& rhs) 2401 { 2402 this->ptr_ = cxx::make_unique<T>(*rhs.ptr_); 2403 return *this; 2404 } 2405 2406 storage(storage&&) = default; 2407 storage& operator=(storage&&) = default; 2408 2409 bool is_ok() const noexcept {return static_cast<bool>(ptr_);} 2410 2411 value_type& get() const noexcept {return *ptr_;} 2412 2413 private: 2414 std::unique_ptr<value_type> ptr_; 2415 }; 2416 2417 } // detail 2418 } // toml 2419 #endif // TOML11_STORAGE_HPP 2420 #ifndef TOML11_COMMENTS_HPP 2421 #define TOML11_COMMENTS_HPP 2422 2423 #ifndef TOML11_COMMENTS_FWD_HPP 2424 #define TOML11_COMMENTS_FWD_HPP 2425 2426 // to use __has_builtin 2427 2428 #include <exception> 2429 #include <initializer_list> 2430 #include <iterator> 2431 #include <stdexcept> 2432 #include <string> 2433 #include <type_traits> 2434 #include <utility> 2435 #include <vector> 2436 #include <ostream> 2437 2438 // This file provides mainly two classes, `preserve_comments` and `discard_comments`. 2439 // Those two are a container that have the same interface as `std::vector<std::string>` 2440 // but bahaves in the opposite way. `preserve_comments` is just the same as 2441 // `std::vector<std::string>` and each `std::string` corresponds to a comment line. 2442 // Conversely, `discard_comments` discards all the strings and ignores everything 2443 // assigned in it. `discard_comments` is always empty and you will encounter an 2444 // error whenever you access to the element. 2445 namespace toml 2446 { 2447 class discard_comments; // forward decl 2448 2449 class preserve_comments 2450 { 2451 public: 2452 // `container_type` is not provided in discard_comments. 2453 // do not use this inner-type in a generic code. 2454 using container_type = std::vector<std::string>; 2455 2456 using size_type = container_type::size_type; 2457 using difference_type = container_type::difference_type; 2458 using value_type = container_type::value_type; 2459 using reference = container_type::reference; 2460 using const_reference = container_type::const_reference; 2461 using pointer = container_type::pointer; 2462 using const_pointer = container_type::const_pointer; 2463 using iterator = container_type::iterator; 2464 using const_iterator = container_type::const_iterator; 2465 using reverse_iterator = container_type::reverse_iterator; 2466 using const_reverse_iterator = container_type::const_reverse_iterator; 2467 2468 public: 2469 2470 preserve_comments() = default; 2471 ~preserve_comments() = default; 2472 preserve_comments(preserve_comments const&) = default; 2473 preserve_comments(preserve_comments &&) = default; 2474 preserve_comments& operator=(preserve_comments const&) = default; 2475 preserve_comments& operator=(preserve_comments &&) = default; 2476 2477 explicit preserve_comments(const std::vector<std::string>& c): comments(c){} 2478 explicit preserve_comments(std::vector<std::string>&& c) 2479 : comments(std::move(c)) 2480 {} 2481 preserve_comments& operator=(const std::vector<std::string>& c) 2482 { 2483 comments = c; 2484 return *this; 2485 } 2486 preserve_comments& operator=(std::vector<std::string>&& c) 2487 { 2488 comments = std::move(c); 2489 return *this; 2490 } 2491 2492 explicit preserve_comments(const discard_comments&) {} 2493 2494 explicit preserve_comments(size_type n): comments(n) {} 2495 preserve_comments(size_type n, const std::string& x): comments(n, x) {} 2496 preserve_comments(std::initializer_list<std::string> x): comments(x) {} 2497 template<typename InputIterator> 2498 preserve_comments(InputIterator first, InputIterator last) 2499 : comments(first, last) 2500 {} 2501 2502 template<typename InputIterator> 2503 void assign(InputIterator first, InputIterator last) {comments.assign(first, last);} 2504 void assign(std::initializer_list<std::string> ini) {comments.assign(ini);} 2505 void assign(size_type n, const std::string& val) {comments.assign(n, val);} 2506 2507 // Related to the issue #97. 2508 // 2509 // `std::vector::insert` and `std::vector::erase` in the STL implementation 2510 // included in GCC 4.8.5 takes `std::vector::iterator` instead of 2511 // `std::vector::const_iterator`. It causes compilation error in GCC 4.8.5. 2512 #if defined(__GNUC__) && defined(__GNUC_MINOR__) && defined(__GNUC_PATCHLEVEL__) && !defined(__clang__) 2513 # if (__GNUC__ * 10000 + __GNUC_MINOR__ * 100 + __GNUC_PATCHLEVEL__) <= 40805 2514 # define TOML11_WORKAROUND_GCC_4_8_X_STANDARD_LIBRARY_IMPLEMENTATION 2515 # endif 2516 #endif 2517 2518 #ifdef TOML11_WORKAROUND_GCC_4_8_X_STANDARD_LIBRARY_IMPLEMENTATION 2519 iterator insert(iterator p, const std::string& x) 2520 { 2521 return comments.insert(p, x); 2522 } 2523 iterator insert(iterator p, std::string&& x) 2524 { 2525 return comments.insert(p, std::move(x)); 2526 } 2527 void insert(iterator p, size_type n, const std::string& x) 2528 { 2529 return comments.insert(p, n, x); 2530 } 2531 template<typename InputIterator> 2532 void insert(iterator p, InputIterator first, InputIterator last) 2533 { 2534 return comments.insert(p, first, last); 2535 } 2536 void insert(iterator p, std::initializer_list<std::string> ini) 2537 { 2538 return comments.insert(p, ini); 2539 } 2540 2541 template<typename ... Ts> 2542 iterator emplace(iterator p, Ts&& ... args) 2543 { 2544 return comments.emplace(p, std::forward<Ts>(args)...); 2545 } 2546 2547 iterator erase(iterator pos) {return comments.erase(pos);} 2548 iterator erase(iterator first, iterator last) 2549 { 2550 return comments.erase(first, last); 2551 } 2552 #else 2553 iterator insert(const_iterator p, const std::string& x) 2554 { 2555 return comments.insert(p, x); 2556 } 2557 iterator insert(const_iterator p, std::string&& x) 2558 { 2559 return comments.insert(p, std::move(x)); 2560 } 2561 iterator insert(const_iterator p, size_type n, const std::string& x) 2562 { 2563 return comments.insert(p, n, x); 2564 } 2565 template<typename InputIterator> 2566 iterator insert(const_iterator p, InputIterator first, InputIterator last) 2567 { 2568 return comments.insert(p, first, last); 2569 } 2570 iterator insert(const_iterator p, std::initializer_list<std::string> ini) 2571 { 2572 return comments.insert(p, ini); 2573 } 2574 2575 template<typename ... Ts> 2576 iterator emplace(const_iterator p, Ts&& ... args) 2577 { 2578 return comments.emplace(p, std::forward<Ts>(args)...); 2579 } 2580 2581 iterator erase(const_iterator pos) {return comments.erase(pos);} 2582 iterator erase(const_iterator first, const_iterator last) 2583 { 2584 return comments.erase(first, last); 2585 } 2586 #endif 2587 2588 void swap(preserve_comments& other) {comments.swap(other.comments);} 2589 2590 void push_back(const std::string& v) {comments.push_back(v);} 2591 void push_back(std::string&& v) {comments.push_back(std::move(v));} 2592 void pop_back() {comments.pop_back();} 2593 2594 template<typename ... Ts> 2595 void emplace_back(Ts&& ... args) {comments.emplace_back(std::forward<Ts>(args)...);} 2596 2597 void clear() {comments.clear();} 2598 2599 size_type size() const noexcept {return comments.size();} 2600 size_type max_size() const noexcept {return comments.max_size();} 2601 size_type capacity() const noexcept {return comments.capacity();} 2602 bool empty() const noexcept {return comments.empty();} 2603 2604 void reserve(size_type n) {comments.reserve(n);} 2605 void resize(size_type n) {comments.resize(n);} 2606 void resize(size_type n, const std::string& c) {comments.resize(n, c);} 2607 void shrink_to_fit() {comments.shrink_to_fit();} 2608 2609 reference operator[](const size_type n) noexcept {return comments[n];} 2610 const_reference operator[](const size_type n) const noexcept {return comments[n];} 2611 reference at(const size_type n) {return comments.at(n);} 2612 const_reference at(const size_type n) const {return comments.at(n);} 2613 reference front() noexcept {return comments.front();} 2614 const_reference front() const noexcept {return comments.front();} 2615 reference back() noexcept {return comments.back();} 2616 const_reference back() const noexcept {return comments.back();} 2617 2618 pointer data() noexcept {return comments.data();} 2619 const_pointer data() const noexcept {return comments.data();} 2620 2621 iterator begin() noexcept {return comments.begin();} 2622 iterator end() noexcept {return comments.end();} 2623 const_iterator begin() const noexcept {return comments.begin();} 2624 const_iterator end() const noexcept {return comments.end();} 2625 const_iterator cbegin() const noexcept {return comments.cbegin();} 2626 const_iterator cend() const noexcept {return comments.cend();} 2627 2628 reverse_iterator rbegin() noexcept {return comments.rbegin();} 2629 reverse_iterator rend() noexcept {return comments.rend();} 2630 const_reverse_iterator rbegin() const noexcept {return comments.rbegin();} 2631 const_reverse_iterator rend() const noexcept {return comments.rend();} 2632 const_reverse_iterator crbegin() const noexcept {return comments.crbegin();} 2633 const_reverse_iterator crend() const noexcept {return comments.crend();} 2634 2635 friend bool operator==(const preserve_comments&, const preserve_comments&); 2636 friend bool operator!=(const preserve_comments&, const preserve_comments&); 2637 friend bool operator< (const preserve_comments&, const preserve_comments&); 2638 friend bool operator<=(const preserve_comments&, const preserve_comments&); 2639 friend bool operator> (const preserve_comments&, const preserve_comments&); 2640 friend bool operator>=(const preserve_comments&, const preserve_comments&); 2641 2642 friend void swap(preserve_comments&, std::vector<std::string>&); 2643 friend void swap(std::vector<std::string>&, preserve_comments&); 2644 2645 private: 2646 2647 container_type comments; 2648 }; 2649 2650 bool operator==(const preserve_comments& lhs, const preserve_comments& rhs); 2651 bool operator!=(const preserve_comments& lhs, const preserve_comments& rhs); 2652 bool operator< (const preserve_comments& lhs, const preserve_comments& rhs); 2653 bool operator<=(const preserve_comments& lhs, const preserve_comments& rhs); 2654 bool operator> (const preserve_comments& lhs, const preserve_comments& rhs); 2655 bool operator>=(const preserve_comments& lhs, const preserve_comments& rhs); 2656 2657 void swap(preserve_comments& lhs, preserve_comments& rhs); 2658 void swap(preserve_comments& lhs, std::vector<std::string>& rhs); 2659 void swap(std::vector<std::string>& lhs, preserve_comments& rhs); 2660 2661 std::ostream& operator<<(std::ostream& os, const preserve_comments& com); 2662 2663 namespace detail 2664 { 2665 2666 // To provide the same interface with `preserve_comments`, `discard_comments` 2667 // should have an iterator. But it does not contain anything, so we need to 2668 // add an iterator that points nothing. 2669 // 2670 // It always points null, so DO NOT unwrap this iterator. It always crashes 2671 // your program. 2672 template<typename T, bool is_const> 2673 struct empty_iterator 2674 { 2675 using value_type = T; 2676 using reference_type = typename std::conditional<is_const, T const&, T&>::type; 2677 using pointer_type = typename std::conditional<is_const, T const*, T*>::type; 2678 using difference_type = std::ptrdiff_t; 2679 using iterator_category = std::random_access_iterator_tag; 2680 2681 empty_iterator() = default; 2682 ~empty_iterator() = default; 2683 empty_iterator(empty_iterator const&) = default; 2684 empty_iterator(empty_iterator &&) = default; 2685 empty_iterator& operator=(empty_iterator const&) = default; 2686 empty_iterator& operator=(empty_iterator &&) = default; 2687 2688 // DO NOT call these operators. 2689 reference_type operator*() const noexcept {std::terminate();} 2690 pointer_type operator->() const noexcept {return nullptr;} 2691 reference_type operator[](difference_type) const noexcept {return this->operator*();} 2692 2693 // These operators do nothing. 2694 empty_iterator& operator++() noexcept {return *this;} 2695 empty_iterator operator++(int) noexcept {return *this;} 2696 empty_iterator& operator--() noexcept {return *this;} 2697 empty_iterator operator--(int) noexcept {return *this;} 2698 2699 empty_iterator& operator+=(difference_type) noexcept {return *this;} 2700 empty_iterator& operator-=(difference_type) noexcept {return *this;} 2701 2702 empty_iterator operator+(difference_type) const noexcept {return *this;} 2703 empty_iterator operator-(difference_type) const noexcept {return *this;} 2704 }; 2705 2706 template<typename T, bool C> 2707 bool operator==(const empty_iterator<T, C>&, const empty_iterator<T, C>&) noexcept {return true;} 2708 template<typename T, bool C> 2709 bool operator!=(const empty_iterator<T, C>&, const empty_iterator<T, C>&) noexcept {return false;} 2710 template<typename T, bool C> 2711 bool operator< (const empty_iterator<T, C>&, const empty_iterator<T, C>&) noexcept {return false;} 2712 template<typename T, bool C> 2713 bool operator<=(const empty_iterator<T, C>&, const empty_iterator<T, C>&) noexcept {return true;} 2714 template<typename T, bool C> 2715 bool operator> (const empty_iterator<T, C>&, const empty_iterator<T, C>&) noexcept {return false;} 2716 template<typename T, bool C> 2717 bool operator>=(const empty_iterator<T, C>&, const empty_iterator<T, C>&) noexcept {return true;} 2718 2719 template<typename T, bool C> 2720 typename empty_iterator<T, C>::difference_type 2721 operator-(const empty_iterator<T, C>&, const empty_iterator<T, C>&) noexcept {return 0;} 2722 2723 template<typename T, bool C> 2724 empty_iterator<T, C> 2725 operator+(typename empty_iterator<T, C>::difference_type, const empty_iterator<T, C>& rhs) noexcept {return rhs;} 2726 template<typename T, bool C> 2727 empty_iterator<T, C> 2728 operator+(const empty_iterator<T, C>& lhs, typename empty_iterator<T, C>::difference_type) noexcept {return lhs;} 2729 2730 } // detail 2731 2732 // The default comment type. It discards all the comments. It requires only one 2733 // byte to contain, so the memory footprint is smaller than preserve_comments. 2734 // 2735 // It just ignores `push_back`, `insert`, `erase`, and any other modifications. 2736 // IT always returns size() == 0, the iterator taken by `begin()` is always the 2737 // same as that of `end()`, and accessing through `operator[]` or iterators 2738 // always causes a segmentation fault. DO NOT access to the element of this. 2739 // 2740 // Why this is chose as the default type is because the last version (2.x.y) 2741 // does not contain any comments in a value. To minimize the impact on the 2742 // efficiency, this is chosen as a default. 2743 // 2744 // To reduce the memory footprint, later we can try empty base optimization (EBO). 2745 class discard_comments 2746 { 2747 public: 2748 using size_type = std::size_t; 2749 using difference_type = std::ptrdiff_t; 2750 using value_type = std::string; 2751 using reference = std::string&; 2752 using const_reference = std::string const&; 2753 using pointer = std::string*; 2754 using const_pointer = std::string const*; 2755 using iterator = detail::empty_iterator<std::string, false>; 2756 using const_iterator = detail::empty_iterator<std::string, true>; 2757 using reverse_iterator = detail::empty_iterator<std::string, false>; 2758 using const_reverse_iterator = detail::empty_iterator<std::string, true>; 2759 2760 public: 2761 discard_comments() = default; 2762 ~discard_comments() = default; 2763 discard_comments(discard_comments const&) = default; 2764 discard_comments(discard_comments &&) = default; 2765 discard_comments& operator=(discard_comments const&) = default; 2766 discard_comments& operator=(discard_comments &&) = default; 2767 2768 explicit discard_comments(const std::vector<std::string>&) noexcept {} 2769 explicit discard_comments(std::vector<std::string>&&) noexcept {} 2770 discard_comments& operator=(const std::vector<std::string>&) noexcept {return *this;} 2771 discard_comments& operator=(std::vector<std::string>&&) noexcept {return *this;} 2772 2773 explicit discard_comments(const preserve_comments&) noexcept {} 2774 2775 explicit discard_comments(size_type) noexcept {} 2776 discard_comments(size_type, const std::string&) noexcept {} 2777 discard_comments(std::initializer_list<std::string>) noexcept {} 2778 template<typename InputIterator> 2779 discard_comments(InputIterator, InputIterator) noexcept {} 2780 2781 template<typename InputIterator> 2782 void assign(InputIterator, InputIterator) noexcept {} 2783 void assign(std::initializer_list<std::string>) noexcept {} 2784 void assign(size_type, const std::string&) noexcept {} 2785 2786 iterator insert(const_iterator, const std::string&) {return iterator{};} 2787 iterator insert(const_iterator, std::string&&) {return iterator{};} 2788 iterator insert(const_iterator, size_type, const std::string&) {return iterator{};} 2789 template<typename InputIterator> 2790 iterator insert(const_iterator, InputIterator, InputIterator) {return iterator{};} 2791 iterator insert(const_iterator, std::initializer_list<std::string>) {return iterator{};} 2792 2793 template<typename ... Ts> 2794 iterator emplace(const_iterator, Ts&& ...) {return iterator{};} 2795 iterator erase(const_iterator) {return iterator{};} 2796 iterator erase(const_iterator, const_iterator) {return iterator{};} 2797 2798 void swap(discard_comments&) {return;} 2799 2800 void push_back(const std::string&) {return;} 2801 void push_back(std::string&& ) {return;} 2802 void pop_back() {return;} 2803 2804 template<typename ... Ts> 2805 void emplace_back(Ts&& ...) {return;} 2806 2807 void clear() {return;} 2808 2809 size_type size() const noexcept {return 0;} 2810 size_type max_size() const noexcept {return 0;} 2811 size_type capacity() const noexcept {return 0;} 2812 bool empty() const noexcept {return true;} 2813 2814 void reserve(size_type) {return;} 2815 void resize(size_type) {return;} 2816 void resize(size_type, const std::string&) {return;} 2817 void shrink_to_fit() {return;} 2818 2819 // DO NOT access to the element of this container. This container is always 2820 // empty, so accessing through operator[], front/back, data causes address 2821 // error. 2822 2823 reference operator[](const size_type) noexcept {never_call("toml::discard_comment::operator[]");} 2824 const_reference operator[](const size_type) const noexcept {never_call("toml::discard_comment::operator[]");} 2825 reference at(const size_type) {throw std::out_of_range("toml::discard_comment is always empty.");} 2826 const_reference at(const size_type) const {throw std::out_of_range("toml::discard_comment is always empty.");} 2827 reference front() noexcept {never_call("toml::discard_comment::front");} 2828 const_reference front() const noexcept {never_call("toml::discard_comment::front");} 2829 reference back() noexcept {never_call("toml::discard_comment::back");} 2830 const_reference back() const noexcept {never_call("toml::discard_comment::back");} 2831 2832 pointer data() noexcept {return nullptr;} 2833 const_pointer data() const noexcept {return nullptr;} 2834 2835 iterator begin() noexcept {return iterator{};} 2836 iterator end() noexcept {return iterator{};} 2837 const_iterator begin() const noexcept {return const_iterator{};} 2838 const_iterator end() const noexcept {return const_iterator{};} 2839 const_iterator cbegin() const noexcept {return const_iterator{};} 2840 const_iterator cend() const noexcept {return const_iterator{};} 2841 2842 reverse_iterator rbegin() noexcept {return iterator{};} 2843 reverse_iterator rend() noexcept {return iterator{};} 2844 const_reverse_iterator rbegin() const noexcept {return const_iterator{};} 2845 const_reverse_iterator rend() const noexcept {return const_iterator{};} 2846 const_reverse_iterator crbegin() const noexcept {return const_iterator{};} 2847 const_reverse_iterator crend() const noexcept {return const_iterator{};} 2848 2849 private: 2850 2851 [[noreturn]] static void never_call(const char *const this_function) 2852 { 2853 #if __has_builtin(__builtin_unreachable) 2854 __builtin_unreachable(); 2855 #endif 2856 throw std::logic_error{this_function}; 2857 } 2858 }; 2859 2860 inline bool operator==(const discard_comments&, const discard_comments&) noexcept {return true;} 2861 inline bool operator!=(const discard_comments&, const discard_comments&) noexcept {return false;} 2862 inline bool operator< (const discard_comments&, const discard_comments&) noexcept {return false;} 2863 inline bool operator<=(const discard_comments&, const discard_comments&) noexcept {return true;} 2864 inline bool operator> (const discard_comments&, const discard_comments&) noexcept {return false;} 2865 inline bool operator>=(const discard_comments&, const discard_comments&) noexcept {return true;} 2866 2867 inline void swap(const discard_comments&, const discard_comments&) noexcept {return;} 2868 2869 inline std::ostream& operator<<(std::ostream& os, const discard_comments&) {return os;} 2870 2871 } // toml11 2872 #endif // TOML11_COMMENTS_FWD_HPP 2873 2874 #if ! defined(TOML11_COMPILE_SOURCES) 2875 #ifndef TOML11_COMMENTS_IMPL_HPP 2876 #define TOML11_COMMENTS_IMPL_HPP 2877 2878 2879 namespace toml 2880 { 2881 2882 TOML11_INLINE bool operator==(const preserve_comments& lhs, const preserve_comments& rhs) {return lhs.comments == rhs.comments;} 2883 TOML11_INLINE bool operator!=(const preserve_comments& lhs, const preserve_comments& rhs) {return lhs.comments != rhs.comments;} 2884 TOML11_INLINE bool operator< (const preserve_comments& lhs, const preserve_comments& rhs) {return lhs.comments < rhs.comments;} 2885 TOML11_INLINE bool operator<=(const preserve_comments& lhs, const preserve_comments& rhs) {return lhs.comments <= rhs.comments;} 2886 TOML11_INLINE bool operator> (const preserve_comments& lhs, const preserve_comments& rhs) {return lhs.comments > rhs.comments;} 2887 TOML11_INLINE bool operator>=(const preserve_comments& lhs, const preserve_comments& rhs) {return lhs.comments >= rhs.comments;} 2888 2889 TOML11_INLINE void swap(preserve_comments& lhs, preserve_comments& rhs) 2890 { 2891 lhs.swap(rhs); 2892 return; 2893 } 2894 TOML11_INLINE void swap(preserve_comments& lhs, std::vector<std::string>& rhs) 2895 { 2896 lhs.comments.swap(rhs); 2897 return; 2898 } 2899 TOML11_INLINE void swap(std::vector<std::string>& lhs, preserve_comments& rhs) 2900 { 2901 lhs.swap(rhs.comments); 2902 return; 2903 } 2904 2905 TOML11_INLINE std::ostream& operator<<(std::ostream& os, const preserve_comments& com) 2906 { 2907 for(const auto& c : com) 2908 { 2909 if(c.front() != '#') 2910 { 2911 os << '#'; 2912 } 2913 os << c << '\n'; 2914 } 2915 return os; 2916 } 2917 2918 } // toml11 2919 #endif // TOML11_COMMENTS_IMPL_HPP 2920 #endif 2921 2922 #endif // TOML11_COMMENTS_HPP 2923 #ifndef TOML11_COLOR_HPP 2924 #define TOML11_COLOR_HPP 2925 2926 #ifndef TOML11_COLOR_FWD_HPP 2927 #define TOML11_COLOR_FWD_HPP 2928 2929 #include <iosfwd> 2930 2931 #ifdef TOML11_COLORIZE_ERROR_MESSAGE 2932 #define TOML11_ERROR_MESSAGE_COLORIZED true 2933 #else 2934 #define TOML11_ERROR_MESSAGE_COLORIZED false 2935 #endif 2936 2937 #ifdef TOML11_USE_THREAD_LOCAL_COLORIZATION 2938 #define TOML11_THREAD_LOCAL_COLORIZATION thread_local 2939 #else 2940 #define TOML11_THREAD_LOCAL_COLORIZATION 2941 #endif 2942 2943 namespace toml 2944 { 2945 namespace color 2946 { 2947 // put ANSI escape sequence to ostream 2948 inline namespace ansi 2949 { 2950 namespace detail 2951 { 2952 2953 // Control color mode globally 2954 class color_mode 2955 { 2956 public: 2957 2958 void enable() noexcept 2959 { 2960 should_color_ = true; 2961 } 2962 void disable() noexcept 2963 { 2964 should_color_ = false; 2965 } 2966 bool should_color() const noexcept 2967 { 2968 return should_color_; 2969 } 2970 2971 private: 2972 2973 bool should_color_ = TOML11_ERROR_MESSAGE_COLORIZED; 2974 }; 2975 2976 inline color_mode& color_status() noexcept 2977 { 2978 static TOML11_THREAD_LOCAL_COLORIZATION color_mode status; 2979 return status; 2980 } 2981 2982 } // detail 2983 2984 std::ostream& reset (std::ostream& os); 2985 std::ostream& bold (std::ostream& os); 2986 std::ostream& grey (std::ostream& os); 2987 std::ostream& gray (std::ostream& os); 2988 std::ostream& red (std::ostream& os); 2989 std::ostream& green (std::ostream& os); 2990 std::ostream& yellow (std::ostream& os); 2991 std::ostream& blue (std::ostream& os); 2992 std::ostream& magenta(std::ostream& os); 2993 std::ostream& cyan (std::ostream& os); 2994 std::ostream& white (std::ostream& os); 2995 2996 } // ansi 2997 2998 inline void enable() 2999 { 3000 return detail::color_status().enable(); 3001 } 3002 inline void disable() 3003 { 3004 return detail::color_status().disable(); 3005 } 3006 inline bool should_color() 3007 { 3008 return detail::color_status().should_color(); 3009 } 3010 3011 } // color 3012 } // toml 3013 #endif // TOML11_COLOR_FWD_HPP 3014 3015 #if ! defined(TOML11_COMPILE_SOURCES) 3016 #ifndef TOML11_COLOR_IMPL_HPP 3017 #define TOML11_COLOR_IMPL_HPP 3018 3019 3020 #include <ostream> 3021 3022 namespace toml 3023 { 3024 namespace color 3025 { 3026 // put ANSI escape sequence to ostream 3027 inline namespace ansi 3028 { 3029 3030 TOML11_INLINE std::ostream& reset(std::ostream& os) 3031 { 3032 if(detail::color_status().should_color()) {os << "\033[00m";} 3033 return os; 3034 } 3035 TOML11_INLINE std::ostream& bold(std::ostream& os) 3036 { 3037 if(detail::color_status().should_color()) {os << "\033[01m";} 3038 return os; 3039 } 3040 TOML11_INLINE std::ostream& grey(std::ostream& os) 3041 { 3042 if(detail::color_status().should_color()) {os << "\033[30m";} 3043 return os; 3044 } 3045 TOML11_INLINE std::ostream& gray(std::ostream& os) 3046 { 3047 if(detail::color_status().should_color()) {os << "\033[30m";} 3048 return os; 3049 } 3050 TOML11_INLINE std::ostream& red(std::ostream& os) 3051 { 3052 if(detail::color_status().should_color()) {os << "\033[31m";} 3053 return os; 3054 } 3055 TOML11_INLINE std::ostream& green(std::ostream& os) 3056 { 3057 if(detail::color_status().should_color()) {os << "\033[32m";} 3058 return os; 3059 } 3060 TOML11_INLINE std::ostream& yellow(std::ostream& os) 3061 { 3062 if(detail::color_status().should_color()) {os << "\033[33m";} 3063 return os; 3064 } 3065 TOML11_INLINE std::ostream& blue(std::ostream& os) 3066 { 3067 if(detail::color_status().should_color()) {os << "\033[34m";} 3068 return os; 3069 } 3070 TOML11_INLINE std::ostream& magenta(std::ostream& os) 3071 { 3072 if(detail::color_status().should_color()) {os << "\033[35m";} 3073 return os; 3074 } 3075 TOML11_INLINE std::ostream& cyan (std::ostream& os) 3076 { 3077 if(detail::color_status().should_color()) {os << "\033[36m";} 3078 return os; 3079 } 3080 TOML11_INLINE std::ostream& white (std::ostream& os) 3081 { 3082 if(detail::color_status().should_color()) {os << "\033[37m";} 3083 return os; 3084 } 3085 3086 } // ansi 3087 } // color 3088 } // toml 3089 #endif // TOML11_COLOR_IMPL_HPP 3090 #endif 3091 3092 #endif // TOML11_COLOR_HPP 3093 #ifndef TOML11_SPEC_HPP 3094 #define TOML11_SPEC_HPP 3095 3096 #include <ostream> 3097 #include <sstream> 3098 3099 #include <cstdint> 3100 3101 namespace toml 3102 { 3103 3104 struct semantic_version 3105 { 3106 constexpr semantic_version(std::uint32_t mjr, std::uint32_t mnr, std::uint32_t p) noexcept 3107 : major{mjr}, minor{mnr}, patch{p} 3108 {} 3109 3110 std::uint32_t major; 3111 std::uint32_t minor; 3112 std::uint32_t patch; 3113 }; 3114 3115 constexpr inline semantic_version 3116 make_semver(std::uint32_t mjr, std::uint32_t mnr, std::uint32_t p) noexcept 3117 { 3118 return semantic_version(mjr, mnr, p); 3119 } 3120 3121 constexpr inline bool 3122 operator==(const semantic_version& lhs, const semantic_version& rhs) noexcept 3123 { 3124 return lhs.major == rhs.major && 3125 lhs.minor == rhs.minor && 3126 lhs.patch == rhs.patch; 3127 } 3128 constexpr inline bool 3129 operator!=(const semantic_version& lhs, const semantic_version& rhs) noexcept 3130 { 3131 return !(lhs == rhs); 3132 } 3133 constexpr inline bool 3134 operator<(const semantic_version& lhs, const semantic_version& rhs) noexcept 3135 { 3136 return lhs.major < rhs.major || 3137 (lhs.major == rhs.major && lhs.minor < rhs.minor) || 3138 (lhs.major == rhs.major && lhs.minor == rhs.minor && lhs.patch < rhs.patch); 3139 } 3140 constexpr inline bool 3141 operator>(const semantic_version& lhs, const semantic_version& rhs) noexcept 3142 { 3143 return rhs < lhs; 3144 } 3145 constexpr inline bool 3146 operator<=(const semantic_version& lhs, const semantic_version& rhs) noexcept 3147 { 3148 return !(lhs > rhs); 3149 } 3150 constexpr inline bool 3151 operator>=(const semantic_version& lhs, const semantic_version& rhs) noexcept 3152 { 3153 return !(lhs < rhs); 3154 } 3155 3156 inline std::ostream& operator<<(std::ostream& os, const semantic_version& v) 3157 { 3158 os << v.major << '.' << v.minor << '.' << v.patch; 3159 return os; 3160 } 3161 3162 inline std::string to_string(const semantic_version& v) 3163 { 3164 std::ostringstream oss; 3165 oss << v; 3166 return oss.str(); 3167 } 3168 3169 struct spec 3170 { 3171 constexpr static spec default_version() noexcept 3172 { 3173 return spec::v(1, 0, 0); 3174 } 3175 3176 constexpr static spec v(std::uint32_t mjr, std::uint32_t mnr, std::uint32_t p) noexcept 3177 { 3178 return spec(make_semver(mjr, mnr, p)); 3179 } 3180 3181 constexpr explicit spec(const semantic_version& semver) noexcept 3182 : version{semver}, 3183 v1_1_0_allow_control_characters_in_comments {semantic_version{1, 1, 0} <= semver}, 3184 v1_1_0_allow_newlines_in_inline_tables {semantic_version{1, 1, 0} <= semver}, 3185 v1_1_0_allow_trailing_comma_in_inline_tables{semantic_version{1, 1, 0} <= semver}, 3186 v1_1_0_allow_non_english_in_bare_keys {semantic_version{1, 1, 0} <= semver}, 3187 v1_1_0_add_escape_sequence_e {semantic_version{1, 1, 0} <= semver}, 3188 v1_1_0_add_escape_sequence_x {semantic_version{1, 1, 0} <= semver}, 3189 v1_1_0_make_seconds_optional {semantic_version{1, 1, 0} <= semver}, 3190 ext_hex_float {false}, 3191 ext_num_suffix{false}, 3192 ext_null_value{false} 3193 {} 3194 3195 semantic_version version; // toml version 3196 3197 // diff from v1.0.0 -> v1.1.0 3198 bool v1_1_0_allow_control_characters_in_comments; 3199 bool v1_1_0_allow_newlines_in_inline_tables; 3200 bool v1_1_0_allow_trailing_comma_in_inline_tables; 3201 bool v1_1_0_allow_non_english_in_bare_keys; 3202 bool v1_1_0_add_escape_sequence_e; 3203 bool v1_1_0_add_escape_sequence_x; 3204 bool v1_1_0_make_seconds_optional; 3205 3206 // library extensions 3207 bool ext_hex_float; // allow hex float (in C++ style) 3208 bool ext_num_suffix; // allow number suffix (in C++ style) 3209 bool ext_null_value; // allow `null` as a value 3210 }; 3211 3212 } // namespace toml 3213 #endif // TOML11_SPEC_HPP 3214 #ifndef TOML11_ORDERED_MAP_HPP 3215 #define TOML11_ORDERED_MAP_HPP 3216 3217 #include <algorithm> 3218 #include <stdexcept> 3219 #include <utility> 3220 #include <vector> 3221 3222 namespace toml 3223 { 3224 3225 namespace detail 3226 { 3227 template<typename Cmp> 3228 struct ordered_map_ebo_container 3229 { 3230 Cmp cmp_; // empty base optimization for empty Cmp type 3231 }; 3232 } // detail 3233 3234 template<typename Key, typename Val, typename Cmp = std::equal_to<Key>, 3235 typename Allocator = std::allocator<std::pair<Key, Val>>> 3236 class ordered_map : detail::ordered_map_ebo_container<Cmp> 3237 { 3238 public: 3239 using key_type = Key; 3240 using mapped_type = Val; 3241 using value_type = std::pair<Key, Val>; 3242 3243 using key_compare = Cmp; 3244 using allocator_type = Allocator; 3245 3246 using container_type = std::vector<value_type, Allocator>; 3247 using reference = typename container_type::reference; 3248 using pointer = typename container_type::pointer; 3249 using const_reference = typename container_type::const_reference; 3250 using const_pointer = typename container_type::const_pointer; 3251 using iterator = typename container_type::iterator; 3252 using const_iterator = typename container_type::const_iterator; 3253 using size_type = typename container_type::size_type; 3254 using difference_type = typename container_type::difference_type; 3255 3256 private: 3257 3258 using ebo_base = detail::ordered_map_ebo_container<Cmp>; 3259 3260 public: 3261 3262 ordered_map() = default; 3263 ~ordered_map() = default; 3264 ordered_map(const ordered_map&) = default; 3265 ordered_map(ordered_map&&) = default; 3266 ordered_map& operator=(const ordered_map&) = default; 3267 ordered_map& operator=(ordered_map&&) = default; 3268 3269 ordered_map(const ordered_map& other, const Allocator& alloc) 3270 : container_(other.container_, alloc) 3271 {} 3272 ordered_map(ordered_map&& other, const Allocator& alloc) 3273 : container_(std::move(other.container_), alloc) 3274 {} 3275 3276 explicit ordered_map(const Cmp& cmp, const Allocator& alloc = Allocator()) 3277 : ebo_base{cmp}, container_(alloc) 3278 {} 3279 explicit ordered_map(const Allocator& alloc) 3280 : container_(alloc) 3281 {} 3282 3283 template<typename InputIterator> 3284 ordered_map(InputIterator first, InputIterator last, const Cmp& cmp = Cmp(), const Allocator& alloc = Allocator()) 3285 : ebo_base{cmp}, container_(first, last, alloc) 3286 {} 3287 template<typename InputIterator> 3288 ordered_map(InputIterator first, InputIterator last, const Allocator& alloc) 3289 : container_(first, last, alloc) 3290 {} 3291 3292 ordered_map(std::initializer_list<value_type> v, const Cmp& cmp = Cmp(), const Allocator& alloc = Allocator()) 3293 : ebo_base{cmp}, container_(std::move(v), alloc) 3294 {} 3295 ordered_map(std::initializer_list<value_type> v, const Allocator& alloc) 3296 : container_(std::move(v), alloc) 3297 {} 3298 ordered_map& operator=(std::initializer_list<value_type> v) 3299 { 3300 this->container_ = std::move(v); 3301 return *this; 3302 } 3303 3304 iterator begin() noexcept {return container_.begin();} 3305 iterator end() noexcept {return container_.end();} 3306 const_iterator begin() const noexcept {return container_.begin();} 3307 const_iterator end() const noexcept {return container_.end();} 3308 const_iterator cbegin() const noexcept {return container_.cbegin();} 3309 const_iterator cend() const noexcept {return container_.cend();} 3310 3311 bool empty() const noexcept {return container_.empty();} 3312 std::size_t size() const noexcept {return container_.size();} 3313 std::size_t max_size() const noexcept {return container_.max_size();} 3314 3315 void clear() {container_.clear();} 3316 3317 void push_back(const value_type& v) 3318 { 3319 if(this->contains(v.first)) 3320 { 3321 throw std::out_of_range("ordered_map: value already exists"); 3322 } 3323 container_.push_back(v); 3324 } 3325 void push_back(value_type&& v) 3326 { 3327 if(this->contains(v.first)) 3328 { 3329 throw std::out_of_range("ordered_map: value already exists"); 3330 } 3331 container_.push_back(std::move(v)); 3332 } 3333 void emplace_back(key_type k, mapped_type v) 3334 { 3335 if(this->contains(k)) 3336 { 3337 throw std::out_of_range("ordered_map: value already exists"); 3338 } 3339 container_.emplace_back(std::move(k), std::move(v)); 3340 } 3341 void pop_back() {container_.pop_back();} 3342 3343 void insert(value_type kv) 3344 { 3345 if(this->contains(kv.first)) 3346 { 3347 throw std::out_of_range("ordered_map: value already exists"); 3348 } 3349 container_.push_back(std::move(kv)); 3350 } 3351 void emplace(key_type k, mapped_type v) 3352 { 3353 if(this->contains(k)) 3354 { 3355 throw std::out_of_range("ordered_map: value already exists"); 3356 } 3357 container_.emplace_back(std::move(k), std::move(v)); 3358 } 3359 3360 std::size_t count(const key_type& key) const 3361 { 3362 if(this->find(key) != this->end()) 3363 { 3364 return 1; 3365 } 3366 else 3367 { 3368 return 0; 3369 } 3370 } 3371 bool contains(const key_type& key) const 3372 { 3373 return this->find(key) != this->end(); 3374 } 3375 iterator find(const key_type& key) noexcept 3376 { 3377 return std::find_if(this->begin(), this->end(), 3378 [&key, this](const value_type& v) {return this->cmp_(v.first, key);}); 3379 } 3380 const_iterator find(const key_type& key) const noexcept 3381 { 3382 return std::find_if(this->begin(), this->end(), 3383 [&key, this](const value_type& v) {return this->cmp_(v.first, key);}); 3384 } 3385 3386 mapped_type& at(const key_type& k) 3387 { 3388 const auto iter = this->find(k); 3389 if(iter == this->end()) 3390 { 3391 throw std::out_of_range("ordered_map: no such element"); 3392 } 3393 return iter->second; 3394 } 3395 mapped_type const& at(const key_type& k) const 3396 { 3397 const auto iter = this->find(k); 3398 if(iter == this->end()) 3399 { 3400 throw std::out_of_range("ordered_map: no such element"); 3401 } 3402 return iter->second; 3403 } 3404 3405 mapped_type& operator[](const key_type& k) 3406 { 3407 const auto iter = this->find(k); 3408 if(iter == this->end()) 3409 { 3410 this->container_.emplace_back(k, mapped_type{}); 3411 return this->container_.back().second; 3412 } 3413 return iter->second; 3414 } 3415 3416 mapped_type const& operator[](const key_type& k) const 3417 { 3418 const auto iter = this->find(k); 3419 if(iter == this->end()) 3420 { 3421 throw std::out_of_range("ordered_map: no such element"); 3422 } 3423 return iter->second; 3424 } 3425 3426 key_compare key_comp() const {return this->cmp_;} 3427 3428 void swap(ordered_map& other) 3429 { 3430 container_.swap(other.container_); 3431 } 3432 3433 private: 3434 3435 container_type container_; 3436 }; 3437 3438 template<typename K, typename V, typename C, typename A> 3439 bool operator==(const ordered_map<K,V,C,A>& lhs, const ordered_map<K,V,C,A>& rhs) 3440 { 3441 return lhs.size() == rhs.size() && std::equal(lhs.begin(), lhs.end(), rhs.begin()); 3442 } 3443 template<typename K, typename V, typename C, typename A> 3444 bool operator!=(const ordered_map<K,V,C,A>& lhs, const ordered_map<K,V,C,A>& rhs) 3445 { 3446 return !(lhs == rhs); 3447 } 3448 template<typename K, typename V, typename C, typename A> 3449 bool operator<(const ordered_map<K,V,C,A>& lhs, const ordered_map<K,V,C,A>& rhs) 3450 { 3451 return std::lexicographical_compare(lhs.begin(), lhs.end(), rhs.begin(), rhs.end()); 3452 } 3453 template<typename K, typename V, typename C, typename A> 3454 bool operator>(const ordered_map<K,V,C,A>& lhs, const ordered_map<K,V,C,A>& rhs) 3455 { 3456 return rhs < lhs; 3457 } 3458 template<typename K, typename V, typename C, typename A> 3459 bool operator<=(const ordered_map<K,V,C,A>& lhs, const ordered_map<K,V,C,A>& rhs) 3460 { 3461 return !(lhs > rhs); 3462 } 3463 template<typename K, typename V, typename C, typename A> 3464 bool operator>=(const ordered_map<K,V,C,A>& lhs, const ordered_map<K,V,C,A>& rhs) 3465 { 3466 return !(lhs < rhs); 3467 } 3468 3469 template<typename K, typename V, typename C, typename A> 3470 void swap(ordered_map<K,V,C,A>& lhs, ordered_map<K,V,C,A>& rhs) 3471 { 3472 lhs.swap(rhs); 3473 return; 3474 } 3475 3476 3477 } // toml 3478 #endif // TOML11_ORDERED_MAP_HPP 3479 #ifndef TOML11_INTO_HPP 3480 #define TOML11_INTO_HPP 3481 3482 namespace toml 3483 { 3484 3485 template<typename T> 3486 struct into; 3487 // { 3488 // static toml::value into_toml(const T& user_defined_type) 3489 // { 3490 // // User-defined conversions ... 3491 // } 3492 // }; 3493 3494 } // toml 3495 #endif // TOML11_INTO_HPP 3496 #ifndef TOML11_FROM_HPP 3497 #define TOML11_FROM_HPP 3498 3499 namespace toml 3500 { 3501 3502 template<typename T> 3503 struct from; 3504 // { 3505 // static T from_toml(const toml::value& v) 3506 // { 3507 // // User-defined conversions ... 3508 // } 3509 // }; 3510 3511 } // toml 3512 #endif // TOML11_FROM_HPP 3513 #ifndef TOML11_TRAITS_HPP 3514 #define TOML11_TRAITS_HPP 3515 3516 3517 #include <array> 3518 #include <chrono> 3519 #include <forward_list> 3520 #include <string> 3521 #include <tuple> 3522 #include <type_traits> 3523 #include <utility> 3524 3525 #if defined(TOML11_HAS_STRING_VIEW) 3526 #include <string_view> 3527 #endif 3528 3529 namespace toml 3530 { 3531 template<typename TypeConcig> 3532 class basic_value; 3533 3534 namespace detail 3535 { 3536 // --------------------------------------------------------------------------- 3537 // check whether type T is a kind of container/map class 3538 3539 struct has_iterator_impl 3540 { 3541 template<typename T> static std::true_type check(typename T::iterator*); 3542 template<typename T> static std::false_type check(...); 3543 }; 3544 struct has_value_type_impl 3545 { 3546 template<typename T> static std::true_type check(typename T::value_type*); 3547 template<typename T> static std::false_type check(...); 3548 }; 3549 struct has_key_type_impl 3550 { 3551 template<typename T> static std::true_type check(typename T::key_type*); 3552 template<typename T> static std::false_type check(...); 3553 }; 3554 struct has_mapped_type_impl 3555 { 3556 template<typename T> static std::true_type check(typename T::mapped_type*); 3557 template<typename T> static std::false_type check(...); 3558 }; 3559 struct has_reserve_method_impl 3560 { 3561 template<typename T> static std::false_type check(...); 3562 template<typename T> static std::true_type check( 3563 decltype(std::declval<T>().reserve(std::declval<std::size_t>()))*); 3564 }; 3565 struct has_push_back_method_impl 3566 { 3567 template<typename T> static std::false_type check(...); 3568 template<typename T> static std::true_type check( 3569 decltype(std::declval<T>().push_back(std::declval<typename T::value_type>()))*); 3570 }; 3571 struct is_comparable_impl 3572 { 3573 template<typename T> static std::false_type check(...); 3574 template<typename T> static std::true_type check( 3575 decltype(std::declval<T>() < std::declval<T>())*); 3576 }; 3577 3578 struct has_from_toml_method_impl 3579 { 3580 template<typename T, typename TC> 3581 static std::true_type check( 3582 decltype(std::declval<T>().from_toml(std::declval<::toml::basic_value<TC>>()))*); 3583 3584 template<typename T, typename TC> 3585 static std::false_type check(...); 3586 }; 3587 struct has_into_toml_method_impl 3588 { 3589 template<typename T> 3590 static std::true_type check(decltype(std::declval<T>().into_toml())*); 3591 template<typename T> 3592 static std::false_type check(...); 3593 }; 3594 3595 struct has_template_into_toml_method_impl 3596 { 3597 template<typename T, typename TypeConfig> 3598 static std::true_type check(decltype(std::declval<T>().template into_toml<TypeConfig>())*); 3599 template<typename T, typename TypeConfig> 3600 static std::false_type check(...); 3601 }; 3602 3603 struct has_specialized_from_impl 3604 { 3605 template<typename T> 3606 static std::false_type check(...); 3607 template<typename T, std::size_t S = sizeof(::toml::from<T>)> 3608 static std::true_type check(::toml::from<T>*); 3609 }; 3610 struct has_specialized_into_impl 3611 { 3612 template<typename T> 3613 static std::false_type check(...); 3614 template<typename T, std::size_t S = sizeof(::toml::into<T>)> 3615 static std::true_type check(::toml::into<T>*); 3616 }; 3617 3618 3619 /// Intel C++ compiler can not use decltype in parent class declaration, here 3620 /// is a hack to work around it. https://stackoverflow.com/a/23953090/4692076 3621 #ifdef __INTEL_COMPILER 3622 #define decltype(...) std::enable_if<true, decltype(__VA_ARGS__)>::type 3623 #endif 3624 3625 template<typename T> 3626 struct has_iterator: decltype(has_iterator_impl::check<T>(nullptr)){}; 3627 template<typename T> 3628 struct has_value_type: decltype(has_value_type_impl::check<T>(nullptr)){}; 3629 template<typename T> 3630 struct has_key_type: decltype(has_key_type_impl::check<T>(nullptr)){}; 3631 template<typename T> 3632 struct has_mapped_type: decltype(has_mapped_type_impl::check<T>(nullptr)){}; 3633 template<typename T> 3634 struct has_reserve_method: decltype(has_reserve_method_impl::check<T>(nullptr)){}; 3635 template<typename T> 3636 struct has_push_back_method: decltype(has_push_back_method_impl::check<T>(nullptr)){}; 3637 template<typename T> 3638 struct is_comparable: decltype(is_comparable_impl::check<T>(nullptr)){}; 3639 3640 template<typename T, typename TC> 3641 struct has_from_toml_method: decltype(has_from_toml_method_impl::check<T, TC>(nullptr)){}; 3642 3643 template<typename T> 3644 struct has_into_toml_method: decltype(has_into_toml_method_impl::check<T>(nullptr)){}; 3645 3646 template<typename T, typename TypeConfig> 3647 struct has_template_into_toml_method: decltype(has_template_into_toml_method_impl::check<T, TypeConfig>(nullptr)){}; 3648 3649 template<typename T> 3650 struct has_specialized_from: decltype(has_specialized_from_impl::check<T>(nullptr)){}; 3651 template<typename T> 3652 struct has_specialized_into: decltype(has_specialized_into_impl::check<T>(nullptr)){}; 3653 3654 #ifdef __INTEL_COMPILER 3655 #undef decltype 3656 #endif 3657 3658 // --------------------------------------------------------------------------- 3659 // type checkers 3660 3661 template<typename T> struct is_std_pair_impl : std::false_type{}; 3662 template<typename T1, typename T2> 3663 struct is_std_pair_impl<std::pair<T1, T2>> : std::true_type{}; 3664 template<typename T> 3665 using is_std_pair = is_std_pair_impl<cxx::remove_cvref_t<T>>; 3666 3667 template<typename T> struct is_std_tuple_impl : std::false_type{}; 3668 template<typename ... Ts> 3669 struct is_std_tuple_impl<std::tuple<Ts...>> : std::true_type{}; 3670 template<typename T> 3671 using is_std_tuple = is_std_tuple_impl<cxx::remove_cvref_t<T>>; 3672 3673 template<typename T> struct is_std_array_impl : std::false_type{}; 3674 template<typename T, std::size_t N> 3675 struct is_std_array_impl<std::array<T, N>> : std::true_type{}; 3676 template<typename T> 3677 using is_std_array = is_std_array_impl<cxx::remove_cvref_t<T>>; 3678 3679 template<typename T> struct is_std_forward_list_impl : std::false_type{}; 3680 template<typename T> 3681 struct is_std_forward_list_impl<std::forward_list<T>> : std::true_type{}; 3682 template<typename T> 3683 using is_std_forward_list = is_std_forward_list_impl<cxx::remove_cvref_t<T>>; 3684 3685 template<typename T> struct is_std_basic_string_impl : std::false_type{}; 3686 template<typename C, typename T, typename A> 3687 struct is_std_basic_string_impl<std::basic_string<C, T, A>> : std::true_type{}; 3688 template<typename T> 3689 using is_std_basic_string = is_std_basic_string_impl<cxx::remove_cvref_t<T>>; 3690 3691 template<typename T> struct is_1byte_std_basic_string_impl : std::false_type{}; 3692 template<typename C, typename T, typename A> 3693 struct is_1byte_std_basic_string_impl<std::basic_string<C, T, A>> 3694 : std::integral_constant<bool, sizeof(C) == sizeof(char)> {}; 3695 template<typename T> 3696 using is_1byte_std_basic_string = is_std_basic_string_impl<cxx::remove_cvref_t<T>>; 3697 3698 #if defined(TOML11_HAS_STRING_VIEW) 3699 template<typename T> struct is_std_basic_string_view_impl : std::false_type{}; 3700 template<typename C, typename T> 3701 struct is_std_basic_string_view_impl<std::basic_string_view<C, T>> : std::true_type{}; 3702 template<typename T> 3703 using is_std_basic_string_view = is_std_basic_string_view_impl<cxx::remove_cvref_t<T>>; 3704 3705 template<typename V, typename S> 3706 struct is_string_view_of : std::false_type {}; 3707 template<typename C, typename T> 3708 struct is_string_view_of<std::basic_string_view<C, T>, std::basic_string<C, T>> : std::true_type {}; 3709 #endif 3710 3711 template<typename T> struct is_chrono_duration_impl: std::false_type{}; 3712 template<typename Rep, typename Period> 3713 struct is_chrono_duration_impl<std::chrono::duration<Rep, Period>>: std::true_type{}; 3714 template<typename T> 3715 using is_chrono_duration = is_chrono_duration_impl<cxx::remove_cvref_t<T>>; 3716 3717 template<typename T> 3718 struct is_map_impl : cxx::conjunction< // map satisfies all the following conditions 3719 has_iterator<T>, // has T::iterator 3720 has_value_type<T>, // has T::value_type 3721 has_key_type<T>, // has T::key_type 3722 has_mapped_type<T> // has T::mapped_type 3723 >{}; 3724 template<typename T> 3725 using is_map = is_map_impl<cxx::remove_cvref_t<T>>; 3726 3727 template<typename T> 3728 struct is_container_impl : cxx::conjunction< 3729 cxx::negation<is_map<T>>, // not a map 3730 cxx::negation<std::is_same<T, std::string>>, // not a std::string 3731 #ifdef TOML11_HAS_STRING_VIEW 3732 cxx::negation<std::is_same<T, std::string_view>>, // not a std::string_view 3733 #endif 3734 has_iterator<T>, // has T::iterator 3735 has_value_type<T> // has T::value_type 3736 >{}; 3737 template<typename T> 3738 using is_container = is_container_impl<cxx::remove_cvref_t<T>>; 3739 3740 template<typename T> 3741 struct is_basic_value_impl: std::false_type{}; 3742 template<typename TC> 3743 struct is_basic_value_impl<::toml::basic_value<TC>>: std::true_type{}; 3744 template<typename T> 3745 using is_basic_value = is_basic_value_impl<cxx::remove_cvref_t<T>>; 3746 3747 }// detail 3748 }//toml 3749 #endif // TOML11_TRAITS_HPP 3750 #ifndef TOML11_EXCEPTION_HPP 3751 #define TOML11_EXCEPTION_HPP 3752 3753 #include <exception> 3754 3755 namespace toml 3756 { 3757 3758 struct exception : public std::exception 3759 { 3760 public: 3761 virtual ~exception() noexcept override = default; 3762 virtual const char* what() const noexcept override {return "";} 3763 }; 3764 3765 } // toml 3766 #endif // TOMl11_EXCEPTION_HPP 3767 #ifndef TOML11_RESULT_HPP 3768 #define TOML11_RESULT_HPP 3769 3770 3771 #include <ostream> 3772 #include <string> 3773 #include <type_traits> 3774 #include <utility> 3775 3776 #include <cassert> 3777 3778 namespace toml 3779 { 3780 3781 struct bad_result_access final : public ::toml::exception 3782 { 3783 public: 3784 explicit bad_result_access(std::string what_arg) 3785 : what_(std::move(what_arg)) 3786 {} 3787 ~bad_result_access() noexcept override = default; 3788 const char* what() const noexcept override {return what_.c_str();} 3789 3790 private: 3791 std::string what_; 3792 }; 3793 3794 // ----------------------------------------------------------------------------- 3795 3796 template<typename T> 3797 struct success 3798 { 3799 static_assert( ! std::is_same<T, void>::value, ""); 3800 3801 using value_type = T; 3802 3803 explicit success(value_type v) 3804 noexcept(std::is_nothrow_move_constructible<value_type>::value) 3805 : value(std::move(v)) 3806 {} 3807 3808 template<typename U, cxx::enable_if_t< 3809 std::is_convertible<cxx::remove_cvref_t<U>, T>::value, 3810 std::nullptr_t> = nullptr> 3811 explicit success(U&& v): value(std::forward<U>(v)) {} 3812 3813 template<typename U> 3814 explicit success(success<U> v): value(std::move(v.value)) {} 3815 3816 ~success() = default; 3817 success(const success&) = default; 3818 success(success&&) = default; 3819 success& operator=(const success&) = default; 3820 success& operator=(success&&) = default; 3821 3822 value_type& get() noexcept {return value;} 3823 value_type const& get() const noexcept {return value;} 3824 3825 private: 3826 3827 value_type value; 3828 }; 3829 3830 template<typename T> 3831 struct success<std::reference_wrapper<T>> 3832 { 3833 static_assert( ! std::is_same<T, void>::value, ""); 3834 3835 using value_type = T; 3836 3837 explicit success(std::reference_wrapper<value_type> v) noexcept 3838 : value(std::move(v)) 3839 {} 3840 3841 ~success() = default; 3842 success(const success&) = default; 3843 success(success&&) = default; 3844 success& operator=(const success&) = default; 3845 success& operator=(success&&) = default; 3846 3847 value_type& get() noexcept {return value.get();} 3848 value_type const& get() const noexcept {return value.get();} 3849 3850 private: 3851 3852 std::reference_wrapper<value_type> value; 3853 }; 3854 3855 template<typename T> 3856 success<typename std::decay<T>::type> ok(T&& v) 3857 { 3858 return success<typename std::decay<T>::type>(std::forward<T>(v)); 3859 } 3860 template<std::size_t N> 3861 success<std::string> ok(const char (&literal)[N]) 3862 { 3863 return success<std::string>(std::string(literal)); 3864 } 3865 3866 // ----------------------------------------------------------------------------- 3867 3868 template<typename T> 3869 struct failure 3870 { 3871 using value_type = T; 3872 3873 explicit failure(value_type v) 3874 noexcept(std::is_nothrow_move_constructible<value_type>::value) 3875 : value(std::move(v)) 3876 {} 3877 3878 template<typename U, cxx::enable_if_t< 3879 std::is_convertible<cxx::remove_cvref_t<U>, T>::value, 3880 std::nullptr_t> = nullptr> 3881 explicit failure(U&& v): value(std::forward<U>(v)) {} 3882 3883 template<typename U> 3884 explicit failure(failure<U> v): value(std::move(v.value)) {} 3885 3886 ~failure() = default; 3887 failure(const failure&) = default; 3888 failure(failure&&) = default; 3889 failure& operator=(const failure&) = default; 3890 failure& operator=(failure&&) = default; 3891 3892 value_type& get() noexcept {return value;} 3893 value_type const& get() const noexcept {return value;} 3894 3895 private: 3896 3897 value_type value; 3898 }; 3899 3900 template<typename T> 3901 struct failure<std::reference_wrapper<T>> 3902 { 3903 using value_type = T; 3904 3905 explicit failure(std::reference_wrapper<value_type> v) noexcept 3906 : value(std::move(v)) 3907 {} 3908 3909 ~failure() = default; 3910 failure(const failure&) = default; 3911 failure(failure&&) = default; 3912 failure& operator=(const failure&) = default; 3913 failure& operator=(failure&&) = default; 3914 3915 value_type& get() noexcept {return value.get();} 3916 value_type const& get() const noexcept {return value.get();} 3917 3918 private: 3919 3920 std::reference_wrapper<value_type> value; 3921 }; 3922 3923 template<typename T> 3924 failure<typename std::decay<T>::type> err(T&& v) 3925 { 3926 return failure<typename std::decay<T>::type>(std::forward<T>(v)); 3927 } 3928 3929 template<std::size_t N> 3930 failure<std::string> err(const char (&literal)[N]) 3931 { 3932 return failure<std::string>(std::string(literal)); 3933 } 3934 3935 /* ============================================================================ 3936 * _ _ 3937 * _ _ ___ ____ _| | |_ 3938 * | '_/ -_|_-< || | | _| 3939 * |_| \___/__/\_,_|_|\__| 3940 */ 3941 3942 template<typename T, typename E> 3943 struct result 3944 { 3945 using success_type = success<T>; 3946 using failure_type = failure<E>; 3947 using value_type = typename success_type::value_type; 3948 using error_type = typename failure_type::value_type; 3949 3950 result(success_type s): is_ok_(true), succ_(std::move(s)) {} 3951 result(failure_type f): is_ok_(false), fail_(std::move(f)) {} 3952 3953 template<typename U, cxx::enable_if_t<cxx::conjunction< 3954 cxx::negation<std::is_same<cxx::remove_cvref_t<U>, value_type>>, 3955 std::is_convertible<cxx::remove_cvref_t<U>, value_type> 3956 >::value, std::nullptr_t> = nullptr> 3957 result(success<U> s): is_ok_(true), succ_(std::move(s.value)) {} 3958 3959 template<typename U, cxx::enable_if_t<cxx::conjunction< 3960 cxx::negation<std::is_same<cxx::remove_cvref_t<U>, error_type>>, 3961 std::is_convertible<cxx::remove_cvref_t<U>, error_type> 3962 >::value, std::nullptr_t> = nullptr> 3963 result(failure<U> f): is_ok_(false), fail_(std::move(f.value)) {} 3964 3965 result& operator=(success_type s) 3966 { 3967 this->cleanup(); 3968 this->is_ok_ = true; 3969 auto tmp = ::new(std::addressof(this->succ_)) success_type(std::move(s)); 3970 assert(tmp == std::addressof(this->succ_)); 3971 (void)tmp; 3972 return *this; 3973 } 3974 result& operator=(failure_type f) 3975 { 3976 this->cleanup(); 3977 this->is_ok_ = false; 3978 auto tmp = ::new(std::addressof(this->fail_)) failure_type(std::move(f)); 3979 assert(tmp == std::addressof(this->fail_)); 3980 (void)tmp; 3981 return *this; 3982 } 3983 3984 template<typename U> 3985 result& operator=(success<U> s) 3986 { 3987 this->cleanup(); 3988 this->is_ok_ = true; 3989 auto tmp = ::new(std::addressof(this->succ_)) success_type(std::move(s.value)); 3990 assert(tmp == std::addressof(this->succ_)); 3991 (void)tmp; 3992 return *this; 3993 } 3994 template<typename U> 3995 result& operator=(failure<U> f) 3996 { 3997 this->cleanup(); 3998 this->is_ok_ = false; 3999 auto tmp = ::new(std::addressof(this->fail_)) failure_type(std::move(f.value)); 4000 assert(tmp == std::addressof(this->fail_)); 4001 (void)tmp; 4002 return *this; 4003 } 4004 4005 ~result() noexcept {this->cleanup();} 4006 4007 result(const result& other): is_ok_(other.is_ok()) 4008 { 4009 if(other.is_ok()) 4010 { 4011 auto tmp = ::new(std::addressof(this->succ_)) success_type(other.succ_); 4012 assert(tmp == std::addressof(this->succ_)); 4013 (void)tmp; 4014 } 4015 else 4016 { 4017 auto tmp = ::new(std::addressof(this->fail_)) failure_type(other.fail_); 4018 assert(tmp == std::addressof(this->fail_)); 4019 (void)tmp; 4020 } 4021 } 4022 result(result&& other): is_ok_(other.is_ok()) 4023 { 4024 if(other.is_ok()) 4025 { 4026 auto tmp = ::new(std::addressof(this->succ_)) success_type(std::move(other.succ_)); 4027 assert(tmp == std::addressof(this->succ_)); 4028 (void)tmp; 4029 } 4030 else 4031 { 4032 auto tmp = ::new(std::addressof(this->fail_)) failure_type(std::move(other.fail_)); 4033 assert(tmp == std::addressof(this->fail_)); 4034 (void)tmp; 4035 } 4036 } 4037 4038 result& operator=(const result& other) 4039 { 4040 this->cleanup(); 4041 if(other.is_ok()) 4042 { 4043 auto tmp = ::new(std::addressof(this->succ_)) success_type(other.succ_); 4044 assert(tmp == std::addressof(this->succ_)); 4045 (void)tmp; 4046 } 4047 else 4048 { 4049 auto tmp = ::new(std::addressof(this->fail_)) failure_type(other.fail_); 4050 assert(tmp == std::addressof(this->fail_)); 4051 (void)tmp; 4052 } 4053 is_ok_ = other.is_ok(); 4054 return *this; 4055 } 4056 result& operator=(result&& other) 4057 { 4058 this->cleanup(); 4059 if(other.is_ok()) 4060 { 4061 auto tmp = ::new(std::addressof(this->succ_)) success_type(std::move(other.succ_)); 4062 assert(tmp == std::addressof(this->succ_)); 4063 (void)tmp; 4064 } 4065 else 4066 { 4067 auto tmp = ::new(std::addressof(this->fail_)) failure_type(std::move(other.fail_)); 4068 assert(tmp == std::addressof(this->fail_)); 4069 (void)tmp; 4070 } 4071 is_ok_ = other.is_ok(); 4072 return *this; 4073 } 4074 4075 template<typename U, typename F, cxx::enable_if_t<cxx::conjunction< 4076 cxx::negation<std::is_same<cxx::remove_cvref_t<U>, value_type>>, 4077 cxx::negation<std::is_same<cxx::remove_cvref_t<F>, error_type>>, 4078 std::is_convertible<cxx::remove_cvref_t<U>, value_type>, 4079 std::is_convertible<cxx::remove_cvref_t<F>, error_type> 4080 >::value, std::nullptr_t> = nullptr> 4081 result(result<U, F> other): is_ok_(other.is_ok()) 4082 { 4083 if(other.is_ok()) 4084 { 4085 auto tmp = ::new(std::addressof(this->succ_)) success_type(std::move(other.as_ok())); 4086 assert(tmp == std::addressof(this->succ_)); 4087 (void)tmp; 4088 } 4089 else 4090 { 4091 auto tmp = ::new(std::addressof(this->fail_)) failure_type(std::move(other.as_err())); 4092 assert(tmp == std::addressof(this->fail_)); 4093 (void)tmp; 4094 } 4095 } 4096 4097 template<typename U, typename F, cxx::enable_if_t<cxx::conjunction< 4098 cxx::negation<std::is_same<cxx::remove_cvref_t<U>, value_type>>, 4099 cxx::negation<std::is_same<cxx::remove_cvref_t<F>, error_type>>, 4100 std::is_convertible<cxx::remove_cvref_t<U>, value_type>, 4101 std::is_convertible<cxx::remove_cvref_t<F>, error_type> 4102 >::value, std::nullptr_t> = nullptr> 4103 result& operator=(result<U, F> other) 4104 { 4105 this->cleanup(); 4106 if(other.is_ok()) 4107 { 4108 auto tmp = ::new(std::addressof(this->succ_)) success_type(std::move(other.as_ok())); 4109 assert(tmp == std::addressof(this->succ_)); 4110 (void)tmp; 4111 } 4112 else 4113 { 4114 auto tmp = ::new(std::addressof(this->fail_)) failure_type(std::move(other.as_err())); 4115 assert(tmp == std::addressof(this->fail_)); 4116 (void)tmp; 4117 } 4118 is_ok_ = other.is_ok(); 4119 return *this; 4120 } 4121 4122 bool is_ok() const noexcept {return is_ok_;} 4123 bool is_err() const noexcept {return !is_ok_;} 4124 4125 explicit operator bool() const noexcept {return is_ok_;} 4126 4127 value_type& unwrap(cxx::source_location loc = cxx::source_location::current()) 4128 { 4129 if(this->is_err()) 4130 { 4131 throw bad_result_access("toml::result: bad unwrap" + cxx::to_string(loc)); 4132 } 4133 return this->succ_.get(); 4134 } 4135 value_type const& unwrap(cxx::source_location loc = cxx::source_location::current()) const 4136 { 4137 if(this->is_err()) 4138 { 4139 throw bad_result_access("toml::result: bad unwrap" + cxx::to_string(loc)); 4140 } 4141 return this->succ_.get(); 4142 } 4143 4144 value_type& unwrap_or(value_type& opt) noexcept 4145 { 4146 if(this->is_err()) {return opt;} 4147 return this->succ_.get(); 4148 } 4149 value_type const& unwrap_or(value_type const& opt) const noexcept 4150 { 4151 if(this->is_err()) {return opt;} 4152 return this->succ_.get(); 4153 } 4154 4155 error_type& unwrap_err(cxx::source_location loc = cxx::source_location::current()) 4156 { 4157 if(this->is_ok()) 4158 { 4159 throw bad_result_access("toml::result: bad unwrap_err" + cxx::to_string(loc)); 4160 } 4161 return this->fail_.get(); 4162 } 4163 error_type const& unwrap_err(cxx::source_location loc = cxx::source_location::current()) const 4164 { 4165 if(this->is_ok()) 4166 { 4167 throw bad_result_access("toml::result: bad unwrap_err" + cxx::to_string(loc)); 4168 } 4169 return this->fail_.get(); 4170 } 4171 4172 value_type& as_ok() noexcept 4173 { 4174 assert(this->is_ok()); 4175 return this->succ_.get(); 4176 } 4177 value_type const& as_ok() const noexcept 4178 { 4179 assert(this->is_ok()); 4180 return this->succ_.get(); 4181 } 4182 4183 error_type& as_err() noexcept 4184 { 4185 assert(this->is_err()); 4186 return this->fail_.get(); 4187 } 4188 error_type const& as_err() const noexcept 4189 { 4190 assert(this->is_err()); 4191 return this->fail_.get(); 4192 } 4193 4194 private: 4195 4196 void cleanup() noexcept 4197 { 4198 #if defined(__GNUC__) && ! defined(__clang__) 4199 #pragma GCC diagnostic push 4200 #pragma GCC diagnostic ignored "-Wduplicated-branches" 4201 #endif 4202 4203 if(this->is_ok_) {this->succ_.~success_type();} 4204 else {this->fail_.~failure_type();} 4205 4206 #if defined(__GNUC__) && ! defined(__clang__) 4207 #pragma GCC diagnostic pop 4208 #endif 4209 return; 4210 } 4211 4212 private: 4213 4214 bool is_ok_; 4215 union 4216 { 4217 success_type succ_; 4218 failure_type fail_; 4219 }; 4220 }; 4221 4222 // ---------------------------------------------------------------------------- 4223 4224 namespace detail 4225 { 4226 struct none_t {}; 4227 inline bool operator==(const none_t&, const none_t&) noexcept {return true;} 4228 inline bool operator!=(const none_t&, const none_t&) noexcept {return false;} 4229 inline bool operator< (const none_t&, const none_t&) noexcept {return false;} 4230 inline bool operator<=(const none_t&, const none_t&) noexcept {return true;} 4231 inline bool operator> (const none_t&, const none_t&) noexcept {return false;} 4232 inline bool operator>=(const none_t&, const none_t&) noexcept {return true;} 4233 inline std::ostream& operator<<(std::ostream& os, const none_t&) 4234 { 4235 os << "none"; 4236 return os; 4237 } 4238 } // detail 4239 4240 inline success<detail::none_t> ok() noexcept 4241 { 4242 return success<detail::none_t>(detail::none_t{}); 4243 } 4244 inline failure<detail::none_t> err() noexcept 4245 { 4246 return failure<detail::none_t>(detail::none_t{}); 4247 } 4248 4249 } // toml 4250 #endif // TOML11_RESULT_HPP 4251 #ifndef TOML11_UTILITY_HPP 4252 #define TOML11_UTILITY_HPP 4253 4254 4255 #include <array> 4256 #include <sstream> 4257 4258 #include <cassert> 4259 #include <cctype> 4260 #include <cstring> 4261 4262 namespace toml 4263 { 4264 namespace detail 4265 { 4266 4267 // to output character in an error message. 4268 inline std::string show_char(const int c) 4269 { 4270 using char_type = unsigned char; 4271 if(std::isgraph(c)) 4272 { 4273 return std::string(1, static_cast<char>(c)); 4274 } 4275 else 4276 { 4277 std::array<char, 5> buf; 4278 buf.fill('\0'); 4279 const auto r = std::snprintf(buf.data(), buf.size(), "0x%02x", c & 0xFF); 4280 assert(r == static_cast<int>(buf.size()) - 1); 4281 (void) r; // Unused variable warning 4282 auto in_hex = std::string(buf.data()); 4283 switch(c) 4284 { 4285 case char_type('\0'): {in_hex += "(NUL)"; break;} 4286 case char_type(' ') : {in_hex += "(SPACE)"; break;} 4287 case char_type('\n'): {in_hex += "(LINE FEED)"; break;} 4288 case char_type('\r'): {in_hex += "(CARRIAGE RETURN)"; break;} 4289 case char_type('\t'): {in_hex += "(TAB)"; break;} 4290 case char_type('\v'): {in_hex += "(VERTICAL TAB)"; break;} 4291 case char_type('\f'): {in_hex += "(FORM FEED)"; break;} 4292 case char_type('\x1B'): {in_hex += "(ESCAPE)"; break;} 4293 default: break; 4294 } 4295 return in_hex; 4296 } 4297 } 4298 4299 // --------------------------------------------------------------------------- 4300 4301 template<typename Container> 4302 void try_reserve_impl(Container& container, std::size_t N, std::true_type) 4303 { 4304 container.reserve(N); 4305 return; 4306 } 4307 template<typename Container> 4308 void try_reserve_impl(Container&, std::size_t, std::false_type) noexcept 4309 { 4310 return; 4311 } 4312 4313 template<typename Container> 4314 void try_reserve(Container& container, std::size_t N) 4315 { 4316 try_reserve_impl(container, N, has_reserve_method<Container>{}); 4317 return; 4318 } 4319 4320 // --------------------------------------------------------------------------- 4321 4322 template<typename T> 4323 result<T, none_t> from_string(const std::string& str) 4324 { 4325 T v; 4326 std::istringstream iss(str); 4327 iss >> v; 4328 if(iss.fail()) 4329 { 4330 return err(); 4331 } 4332 return ok(v); 4333 } 4334 4335 // --------------------------------------------------------------------------- 4336 4337 // helper function to avoid std::string(0, 'c') or std::string(iter, iter) 4338 template<typename Iterator> 4339 std::string make_string(Iterator first, Iterator last) 4340 { 4341 if(first == last) {return "";} 4342 return std::string(first, last); 4343 } 4344 inline std::string make_string(std::size_t len, char c) 4345 { 4346 if(len == 0) {return "";} 4347 return std::string(len, c); 4348 } 4349 4350 // --------------------------------------------------------------------------- 4351 4352 template<typename Char, typename Traits, typename Alloc, 4353 typename Char2, typename Traits2, typename Alloc2> 4354 struct string_conv_impl 4355 { 4356 static_assert(sizeof(Char) == sizeof(char), ""); 4357 static_assert(sizeof(Char2) == sizeof(char), ""); 4358 4359 static std::basic_string<Char, Traits, Alloc> invoke(std::basic_string<Char2, Traits2, Alloc2> s) 4360 { 4361 std::basic_string<Char, Traits, Alloc> retval; 4362 std::transform(s.begin(), s.end(), std::back_inserter(retval), 4363 [](const Char2 c) {return static_cast<Char>(c);}); 4364 return retval; 4365 } 4366 template<std::size_t N> 4367 static std::basic_string<Char, Traits, Alloc> invoke(const Char2 (&s)[N]) 4368 { 4369 std::basic_string<Char, Traits, Alloc> retval; 4370 // "string literal" has null-char at the end. to skip it, we use prev. 4371 std::transform(std::begin(s), std::prev(std::end(s)), std::back_inserter(retval), 4372 [](const Char2 c) {return static_cast<Char>(c);}); 4373 return retval; 4374 } 4375 }; 4376 4377 template<typename Char, typename Traits, typename Alloc> 4378 struct string_conv_impl<Char, Traits, Alloc, Char, Traits, Alloc> 4379 { 4380 static_assert(sizeof(Char) == sizeof(char), ""); 4381 4382 static std::basic_string<Char, Traits, Alloc> invoke(std::basic_string<Char, Traits, Alloc> s) 4383 { 4384 return s; 4385 } 4386 template<std::size_t N> 4387 static std::basic_string<Char, Traits, Alloc> invoke(const Char (&s)[N]) 4388 { 4389 return std::basic_string<Char, Traits, Alloc>(s); 4390 } 4391 }; 4392 4393 template<typename S, typename Char2, typename Traits2, typename Alloc2> 4394 cxx::enable_if_t<is_std_basic_string<S>::value, S> 4395 string_conv(std::basic_string<Char2, Traits2, Alloc2> s) 4396 { 4397 using C = typename S::value_type; 4398 using T = typename S::traits_type; 4399 using A = typename S::allocator_type; 4400 return string_conv_impl<C, T, A, Char2, Traits2, Alloc2>::invoke(std::move(s)); 4401 } 4402 template<typename S, std::size_t N> 4403 cxx::enable_if_t<is_std_basic_string<S>::value, S> 4404 string_conv(const char (&s)[N]) 4405 { 4406 using C = typename S::value_type; 4407 using T = typename S::traits_type; 4408 using A = typename S::allocator_type; 4409 using C2 = char; 4410 using T2 = std::char_traits<C2>; 4411 using A2 = std::allocator<C2>; 4412 4413 return string_conv_impl<C, T, A, C2, T2, A2>::template invoke<N>(s); 4414 } 4415 4416 } // namespace detail 4417 } // namespace toml 4418 #endif // TOML11_UTILITY_HPP 4419 #ifndef TOML11_LOCATION_HPP 4420 #define TOML11_LOCATION_HPP 4421 4422 #ifndef TOML11_LOCATION_FWD_HPP 4423 #define TOML11_LOCATION_FWD_HPP 4424 4425 4426 #include <memory> 4427 #include <string> 4428 #include <vector> 4429 4430 namespace toml 4431 { 4432 namespace detail 4433 { 4434 4435 class region; // fwd decl 4436 4437 // 4438 // To represent where we are reading in the parse functions. 4439 // Since it "points" somewhere in the input stream, the length is always 1. 4440 // 4441 class location 4442 { 4443 public: 4444 4445 using char_type = unsigned char; // must be unsigned 4446 using container_type = std::vector<char_type>; 4447 using difference_type = typename container_type::difference_type; // to suppress sign-conversion warning 4448 using source_ptr = std::shared_ptr<const container_type>; 4449 4450 public: 4451 4452 location(source_ptr src, std::string src_name) 4453 : source_(std::move(src)), source_name_(std::move(src_name)), 4454 location_(0), line_number_(1) 4455 {} 4456 4457 location(const location&) = default; 4458 location(location&&) = default; 4459 location& operator=(const location&) = default; 4460 location& operator=(location&&) = default; 4461 ~location() = default; 4462 4463 void advance(std::size_t n = 1) noexcept; 4464 void retrace(std::size_t n = 1) noexcept; 4465 4466 bool is_ok() const noexcept { return static_cast<bool>(this->source_); } 4467 4468 bool eof() const noexcept; 4469 char_type current() const; 4470 4471 char_type peek(); 4472 4473 std::size_t get_location() const noexcept 4474 { 4475 return this->location_; 4476 } 4477 void set_location(const std::size_t loc) noexcept; 4478 4479 std::size_t line_number() const noexcept 4480 { 4481 return this->line_number_; 4482 } 4483 std::string get_line() const; 4484 std::size_t column_number() const noexcept; 4485 4486 source_ptr const& source() const noexcept {return this->source_;} 4487 std::string const& source_name() const noexcept {return this->source_name_;} 4488 4489 private: 4490 4491 void advance_line_number(const std::size_t n); 4492 void retrace_line_number(const std::size_t n); 4493 4494 private: 4495 4496 friend region; 4497 4498 private: 4499 4500 source_ptr source_; 4501 std::string source_name_; 4502 std::size_t location_; // std::vector<>::difference_type is signed 4503 std::size_t line_number_; 4504 }; 4505 4506 bool operator==(const location& lhs, const location& rhs) noexcept; 4507 bool operator!=(const location& lhs, const location& rhs); 4508 4509 location prev(const location& loc); 4510 location next(const location& loc); 4511 location make_temporary_location(const std::string& str) noexcept; 4512 4513 template<typename F> 4514 result<location, none_t> 4515 find_if(const location& first, const location& last, const F& func) noexcept 4516 { 4517 if(first.source() != last.source()) { return err(); } 4518 if(first.get_location() >= last.get_location()) { return err(); } 4519 4520 auto loc = first; 4521 while(loc.get_location() != last.get_location()) 4522 { 4523 if(func(loc.current())) 4524 { 4525 return ok(loc); 4526 } 4527 loc.advance(); 4528 } 4529 return err(); 4530 } 4531 4532 template<typename F> 4533 result<location, none_t> 4534 rfind_if(location first, const location& last, const F& func) 4535 { 4536 if(first.source() != last.source()) { return err(); } 4537 if(first.get_location() >= last.get_location()) { return err(); } 4538 4539 auto loc = last; 4540 while(loc.get_location() != first.get_location()) 4541 { 4542 if(func(loc.current())) 4543 { 4544 return ok(loc); 4545 } 4546 loc.retrace(); 4547 } 4548 if(func(first.current())) 4549 { 4550 return ok(first); 4551 } 4552 return err(); 4553 } 4554 4555 result<location, none_t> find(const location& first, const location& last, 4556 const location::char_type val); 4557 result<location, none_t> rfind(const location& first, const location& last, 4558 const location::char_type val); 4559 4560 std::size_t count(const location& first, const location& last, 4561 const location::char_type& c); 4562 4563 } // detail 4564 } // toml 4565 #endif // TOML11_LOCATION_FWD_HPP 4566 4567 #if ! defined(TOML11_COMPILE_SOURCES) 4568 #ifndef TOML11_LOCATION_IMPL_HPP 4569 #define TOML11_LOCATION_IMPL_HPP 4570 4571 4572 namespace toml 4573 { 4574 namespace detail 4575 { 4576 4577 TOML11_INLINE void location::advance(std::size_t n) noexcept 4578 { 4579 assert(this->is_ok()); 4580 if(this->location_ + n < this->source_->size()) 4581 { 4582 this->advance_line_number(n); 4583 this->location_ += n; 4584 } 4585 else 4586 { 4587 this->advance_line_number(this->source_->size() - this->location_); 4588 this->location_ = this->source_->size(); 4589 } 4590 } 4591 TOML11_INLINE void location::retrace(std::size_t n) noexcept 4592 { 4593 assert(this->is_ok()); 4594 if(this->location_ < n) 4595 { 4596 this->location_ = 0; 4597 this->line_number_ = 1; 4598 } 4599 else 4600 { 4601 this->retrace_line_number(n); 4602 this->location_ -= n; 4603 } 4604 } 4605 4606 TOML11_INLINE bool location::eof() const noexcept 4607 { 4608 assert(this->is_ok()); 4609 return this->location_ >= this->source_->size(); 4610 } 4611 TOML11_INLINE location::char_type location::current() const 4612 { 4613 assert(this->is_ok()); 4614 if(this->eof()) {return '\0';} 4615 4616 assert(this->location_ < this->source_->size()); 4617 return this->source_->at(this->location_); 4618 } 4619 4620 TOML11_INLINE location::char_type location::peek() 4621 { 4622 assert(this->is_ok()); 4623 if(this->location_ >= this->source_->size()) 4624 { 4625 return '\0'; 4626 } 4627 else 4628 { 4629 return this->source_->at(this->location_ + 1); 4630 } 4631 } 4632 4633 TOML11_INLINE void location::set_location(const std::size_t loc) noexcept 4634 { 4635 if(this->location_ == loc) 4636 { 4637 return ; 4638 } 4639 4640 if(loc == 0) 4641 { 4642 this->line_number_ = 1; 4643 } 4644 else if(this->location_ < loc) 4645 { 4646 const auto d = loc - this->location_; 4647 this->advance_line_number(d); 4648 } 4649 else 4650 { 4651 const auto d = this->location_ - loc; 4652 this->retrace_line_number(d); 4653 } 4654 this->location_ = loc; 4655 } 4656 4657 TOML11_INLINE std::string location::get_line() const 4658 { 4659 assert(this->is_ok()); 4660 const auto iter = std::next(this->source_->cbegin(), static_cast<difference_type>(this->location_)); 4661 const auto riter = cxx::make_reverse_iterator(iter); 4662 4663 const auto prev = std::find(riter, this->source_->crend(), char_type('\n')); 4664 const auto next = std::find(iter, this->source_->cend(), char_type('\n')); 4665 4666 return make_string(std::next(prev.base()), next); 4667 } 4668 TOML11_INLINE std::size_t location::column_number() const noexcept 4669 { 4670 assert(this->is_ok()); 4671 const auto iter = std::next(this->source_->cbegin(), static_cast<difference_type>(this->location_)); 4672 const auto riter = cxx::make_reverse_iterator(iter); 4673 const auto prev = std::find(riter, this->source_->crend(), char_type('\n')); 4674 4675 assert(prev.base() <= iter); 4676 return static_cast<std::size_t>(std::distance(prev.base(), iter) + 1); // 1-origin 4677 } 4678 4679 4680 TOML11_INLINE void location::advance_line_number(const std::size_t n) 4681 { 4682 assert(this->is_ok()); 4683 assert(this->location_ + n <= this->source_->size()); 4684 4685 const auto iter = this->source_->cbegin(); 4686 this->line_number_ += static_cast<std::size_t>(std::count( 4687 std::next(iter, static_cast<difference_type>(this->location_)), 4688 std::next(iter, static_cast<difference_type>(this->location_ + n)), 4689 char_type('\n'))); 4690 4691 return; 4692 } 4693 TOML11_INLINE void location::retrace_line_number(const std::size_t n) 4694 { 4695 assert(this->is_ok()); 4696 assert(n <= this->location_); // loc - n >= 0 4697 4698 const auto iter = this->source_->cbegin(); 4699 const auto dline_num = static_cast<std::size_t>(std::count( 4700 std::next(iter, static_cast<difference_type>(this->location_ - n)), 4701 std::next(iter, static_cast<difference_type>(this->location_)), 4702 char_type('\n'))); 4703 4704 if(this->line_number_ <= dline_num) 4705 { 4706 this->line_number_ = 1; 4707 } 4708 else 4709 { 4710 this->line_number_ -= dline_num; 4711 } 4712 return; 4713 } 4714 4715 TOML11_INLINE bool operator==(const location& lhs, const location& rhs) noexcept 4716 { 4717 if( ! lhs.is_ok() || ! rhs.is_ok()) 4718 { 4719 return (!lhs.is_ok()) && (!rhs.is_ok()); 4720 } 4721 return lhs.source() == rhs.source() && 4722 lhs.source_name() == rhs.source_name() && 4723 lhs.get_location() == rhs.get_location(); 4724 } 4725 TOML11_INLINE bool operator!=(const location& lhs, const location& rhs) 4726 { 4727 return !(lhs == rhs); 4728 } 4729 4730 TOML11_INLINE location prev(const location& loc) 4731 { 4732 location p(loc); 4733 p.retrace(1); 4734 return p; 4735 } 4736 TOML11_INLINE location next(const location& loc) 4737 { 4738 location p(loc); 4739 p.advance(1); 4740 return p; 4741 } 4742 4743 TOML11_INLINE location make_temporary_location(const std::string& str) noexcept 4744 { 4745 location::container_type cont(str.size()); 4746 std::transform(str.begin(), str.end(), cont.begin(), 4747 [](const std::string::value_type& c) { 4748 return cxx::bit_cast<location::char_type>(c); 4749 }); 4750 return location(std::make_shared<const location::container_type>( 4751 std::move(cont)), "internal temporary"); 4752 } 4753 4754 TOML11_INLINE result<location, none_t> 4755 find(const location& first, const location& last, const location::char_type val) 4756 { 4757 return find_if(first, last, [val](const location::char_type c) { 4758 return c == val; 4759 }); 4760 } 4761 TOML11_INLINE result<location, none_t> 4762 rfind(const location& first, const location& last, const location::char_type val) 4763 { 4764 return rfind_if(first, last, [val](const location::char_type c) { 4765 return c == val; 4766 }); 4767 } 4768 4769 TOML11_INLINE std::size_t 4770 count(const location& first, const location& last, const location::char_type& c) 4771 { 4772 if(first.source() != last.source()) { return 0; } 4773 if(first.get_location() >= last.get_location()) { return 0; } 4774 4775 auto loc = first; 4776 std::size_t num = 0; 4777 while(loc.get_location() != last.get_location()) 4778 { 4779 if(loc.current() == c) 4780 { 4781 num += 1; 4782 } 4783 loc.advance(); 4784 } 4785 return num; 4786 } 4787 4788 } // detail 4789 } // toml 4790 #endif // TOML11_LOCATION_HPP 4791 #endif 4792 4793 #endif // TOML11_LOCATION_HPP 4794 #ifndef TOML11_REGION_HPP 4795 #define TOML11_REGION_HPP 4796 4797 #ifndef TOML11_REGION_FWD_HPP 4798 #define TOML11_REGION_FWD_HPP 4799 4800 4801 #include <string> 4802 #include <vector> 4803 4804 #include <cassert> 4805 4806 namespace toml 4807 { 4808 namespace detail 4809 { 4810 4811 // 4812 // To represent where is a toml::value defined, or where does an error occur. 4813 // Stored in toml::value. source_location will be constructed based on this. 4814 // 4815 class region 4816 { 4817 public: 4818 4819 using char_type = location::char_type; 4820 using container_type = location::container_type; 4821 using difference_type = location::difference_type; 4822 using source_ptr = location::source_ptr; 4823 4824 using iterator = typename container_type::iterator; 4825 using const_iterator = typename container_type::const_iterator; 4826 4827 public: 4828 4829 // a value that is constructed manually does not have input stream info 4830 region() 4831 : source_(nullptr), source_name_(""), length_(0), 4832 first_line_(0), first_column_(0), last_line_(0), last_column_(0) 4833 {} 4834 4835 // a value defined in [first, last). 4836 // Those source must be the same. Instread, `region` does not make sense. 4837 region(const location& first, const location& last); 4838 4839 // shorthand of [loc, loc+1) 4840 explicit region(const location& loc); 4841 4842 ~region() = default; 4843 region(const region&) = default; 4844 region(region&&) = default; 4845 region& operator=(const region&) = default; 4846 region& operator=(region&&) = default; 4847 4848 bool is_ok() const noexcept { return static_cast<bool>(this->source_); } 4849 4850 operator bool() const noexcept { return this->is_ok(); } 4851 4852 std::size_t length() const noexcept {return this->length_;} 4853 4854 std::size_t first_line_number() const noexcept 4855 { 4856 return this->first_line_; 4857 } 4858 std::size_t first_column_number() const noexcept 4859 { 4860 return this->first_column_; 4861 } 4862 std::size_t last_line_number() const noexcept 4863 { 4864 return this->last_line_; 4865 } 4866 std::size_t last_column_number() const noexcept 4867 { 4868 return this->last_column_; 4869 } 4870 4871 char_type at(std::size_t i) const; 4872 4873 const_iterator begin() const noexcept; 4874 const_iterator end() const noexcept; 4875 const_iterator cbegin() const noexcept; 4876 const_iterator cend() const noexcept; 4877 4878 std::string as_string() const; 4879 std::vector<std::string> as_lines() const; 4880 4881 source_ptr const& source() const noexcept {return this->source_;} 4882 std::string const& source_name() const noexcept {return this->source_name_;} 4883 4884 private: 4885 4886 source_ptr source_; 4887 std::string source_name_; 4888 std::size_t length_; 4889 std::size_t first_; 4890 std::size_t first_line_; 4891 std::size_t first_column_; 4892 std::size_t last_; 4893 std::size_t last_line_; 4894 std::size_t last_column_; 4895 }; 4896 4897 } // namespace detail 4898 } // namespace toml 4899 #endif // TOML11_REGION_FWD_HPP 4900 4901 #if ! defined(TOML11_COMPILE_SOURCES) 4902 #ifndef TOML11_REGION_IMPL_HPP 4903 #define TOML11_REGION_IMPL_HPP 4904 4905 4906 #include <algorithm> 4907 #include <iterator> 4908 #include <string> 4909 #include <sstream> 4910 #include <vector> 4911 #include <cassert> 4912 4913 namespace toml 4914 { 4915 namespace detail 4916 { 4917 4918 // a value defined in [first, last). 4919 // Those source must be the same. Instread, `region` does not make sense. 4920 TOML11_INLINE region::region(const location& first, const location& last) 4921 : source_(first.source()), source_name_(first.source_name()), 4922 length_(last.get_location() - first.get_location()), 4923 first_(first.get_location()), 4924 first_line_(first.line_number()), 4925 first_column_(first.column_number()), 4926 last_(last.get_location()), 4927 last_line_(last.line_number()), 4928 last_column_(last.column_number()) 4929 { 4930 assert(first.source() == last.source()); 4931 assert(first.source_name() == last.source_name()); 4932 } 4933 4934 // shorthand of [loc, loc+1) 4935 TOML11_INLINE region::region(const location& loc) 4936 : source_(loc.source()), source_name_(loc.source_name()), length_(0), 4937 first_line_(0), first_column_(0), last_line_(0), last_column_(0) 4938 { 4939 // if the file ends with LF, the resulting region points no char. 4940 if(loc.eof()) 4941 { 4942 if(loc.get_location() == 0) 4943 { 4944 this->length_ = 0; 4945 this->first_ = 0; 4946 this->first_line_ = 0; 4947 this->first_column_ = 0; 4948 this->last_ = 0; 4949 this->last_line_ = 0; 4950 this->last_column_ = 0; 4951 } 4952 else 4953 { 4954 const auto first = prev(loc); 4955 this->first_ = first.get_location(); 4956 this->first_line_ = first.line_number(); 4957 this->first_column_ = first.column_number(); 4958 this->last_ = loc.get_location(); 4959 this->last_line_ = loc.line_number(); 4960 this->last_column_ = loc.column_number(); 4961 this->length_ = 1; 4962 } 4963 } 4964 else 4965 { 4966 this->first_ = loc.get_location(); 4967 this->first_line_ = loc.line_number(); 4968 this->first_column_ = loc.column_number(); 4969 this->last_ = loc.get_location() + 1; 4970 this->last_line_ = loc.line_number(); 4971 this->last_column_ = loc.column_number() + 1; 4972 this->length_ = 1; 4973 } 4974 } 4975 4976 TOML11_INLINE region::char_type region::at(std::size_t i) const 4977 { 4978 if(this->last_ <= this->first_ + i) 4979 { 4980 throw std::out_of_range("range::at: index " + std::to_string(i) + 4981 " exceeds length " + std::to_string(this->length_)); 4982 } 4983 const auto iter = std::next(this->source_->cbegin(), 4984 static_cast<difference_type>(this->first_ + i)); 4985 return *iter; 4986 } 4987 4988 TOML11_INLINE region::const_iterator region::begin() const noexcept 4989 { 4990 return std::next(this->source_->cbegin(), 4991 static_cast<difference_type>(this->first_)); 4992 } 4993 TOML11_INLINE region::const_iterator region::end() const noexcept 4994 { 4995 return std::next(this->source_->cbegin(), 4996 static_cast<difference_type>(this->last_)); 4997 } 4998 TOML11_INLINE region::const_iterator region::cbegin() const noexcept 4999 { 5000 return std::next(this->source_->cbegin(), 5001 static_cast<difference_type>(this->first_)); 5002 } 5003 TOML11_INLINE region::const_iterator region::cend() const noexcept 5004 { 5005 return std::next(this->source_->cbegin(), 5006 static_cast<difference_type>(this->last_)); 5007 } 5008 5009 TOML11_INLINE std::string region::as_string() const 5010 { 5011 if(this->is_ok()) 5012 { 5013 const auto begin = std::next(this->source_->cbegin(), static_cast<difference_type>(this->first_)); 5014 const auto end = std::next(this->source_->cbegin(), static_cast<difference_type>(this->last_ )); 5015 return ::toml::detail::make_string(begin, end); 5016 } 5017 else 5018 { 5019 return std::string(""); 5020 } 5021 } 5022 5023 TOML11_INLINE std::vector<std::string> region::as_lines() const 5024 { 5025 assert(this->is_ok()); 5026 if(this->length_ == 0) 5027 { 5028 return std::vector<std::string>{""}; 5029 } 5030 5031 // Consider the following toml file 5032 // ``` 5033 // array = [ 5034 // ] # comment 5035 // ``` 5036 // and the region represnets 5037 // ``` 5038 // [ 5039 // ] 5040 // ``` 5041 // but we want to show the following. 5042 // ``` 5043 // array = [ 5044 // ] # comment 5045 // ``` 5046 // So we need to find LFs before `begin` and after `end`. 5047 // 5048 // But, if region ends with LF, it should not include the next line. 5049 // ``` 5050 // a = 42 5051 // ^^^- with the last LF 5052 // ``` 5053 // So we start from `end-1` when looking for LF. 5054 5055 const auto begin_idx = static_cast<difference_type>(this->first_); 5056 const auto end_idx = static_cast<difference_type>(this->last_) - 1; 5057 5058 // length_ != 0, so begin < end. then begin <= end-1 5059 assert(begin_idx <= end_idx); 5060 5061 const auto begin = std::next(this->source_->cbegin(), begin_idx); 5062 const auto end = std::next(this->source_->cbegin(), end_idx); 5063 5064 const auto line_begin = std::find(cxx::make_reverse_iterator(begin), this->source_->crend(), char_type('\n')).base(); 5065 const auto line_end = std::find(end, this->source_->cend(), char_type('\n')); 5066 5067 const auto reg_lines = make_string(line_begin, line_end); 5068 5069 if(reg_lines == "") // the region is an empty line that only contains LF 5070 { 5071 return std::vector<std::string>{""}; 5072 } 5073 5074 std::istringstream iss(reg_lines); 5075 5076 std::vector<std::string> lines; 5077 std::string line; 5078 while(std::getline(iss, line)) 5079 { 5080 lines.push_back(line); 5081 } 5082 return lines; 5083 } 5084 5085 } // namespace detail 5086 } // namespace toml 5087 #endif // TOML11_REGION_IMPL_HPP 5088 #endif 5089 5090 #endif // TOML11_REGION_HPP 5091 #ifndef TOML11_SOURCE_LOCATION_HPP 5092 #define TOML11_SOURCE_LOCATION_HPP 5093 5094 #ifndef TOML11_SOURCE_LOCATION_FWD_HPP 5095 #define TOML11_SOURCE_LOCATION_FWD_HPP 5096 5097 5098 #include <sstream> 5099 #include <string> 5100 #include <vector> 5101 5102 namespace toml 5103 { 5104 5105 // A struct to contain location in a toml file. 5106 struct source_location 5107 { 5108 public: 5109 5110 explicit source_location(const detail::region& r); 5111 ~source_location() = default; 5112 source_location(source_location const&) = default; 5113 source_location(source_location &&) = default; 5114 source_location& operator=(source_location const&) = default; 5115 source_location& operator=(source_location &&) = default; 5116 5117 bool is_ok() const noexcept {return this->is_ok_;} 5118 std::size_t length() const noexcept {return this->length_;} 5119 5120 std::size_t first_line_number() const noexcept {return this->first_line_;} 5121 std::size_t first_column_number() const noexcept {return this->first_column_;} 5122 std::size_t last_line_number() const noexcept {return this->last_line_;} 5123 std::size_t last_column_number() const noexcept {return this->last_column_;} 5124 5125 std::string const& file_name() const noexcept {return this->file_name_;} 5126 5127 std::size_t num_lines() const noexcept {return this->line_str_.size();} 5128 5129 std::string const& first_line() const; 5130 std::string const& last_line() const; 5131 5132 std::vector<std::string> const& lines() const noexcept {return line_str_;} 5133 5134 private: 5135 5136 bool is_ok_; 5137 std::size_t first_line_; 5138 std::size_t first_column_; 5139 std::size_t last_line_; 5140 std::size_t last_column_; 5141 std::size_t length_; 5142 std::string file_name_; 5143 std::vector<std::string> line_str_; 5144 }; 5145 5146 namespace detail 5147 { 5148 5149 std::size_t integer_width_base10(std::size_t i) noexcept; 5150 5151 inline std::size_t line_width() noexcept {return 0;} 5152 5153 template<typename ... Ts> 5154 std::size_t line_width(const source_location& loc, const std::string& /*msg*/, 5155 const Ts& ... tail) noexcept 5156 { 5157 return (std::max)( 5158 integer_width_base10(loc.last_line_number()), line_width(tail...)); 5159 } 5160 5161 std::ostringstream& 5162 format_filename(std::ostringstream& oss, const source_location& loc); 5163 5164 std::ostringstream& 5165 format_empty_line(std::ostringstream& oss, const std::size_t lnw); 5166 5167 std::ostringstream& format_line(std::ostringstream& oss, 5168 const std::size_t lnw, const std::size_t linenum, const std::string& line); 5169 5170 std::ostringstream& format_underline(std::ostringstream& oss, 5171 const std::size_t lnw, const std::size_t col, const std::size_t len, 5172 const std::string& msg); 5173 5174 std::string format_location_impl(const std::size_t lnw, 5175 const std::string& prev_fname, 5176 const source_location& loc, const std::string& msg); 5177 5178 inline std::string format_location_rec(const std::size_t, const std::string&) 5179 { 5180 return ""; 5181 } 5182 5183 template<typename ... Ts> 5184 std::string format_location_rec(const std::size_t lnw, 5185 const std::string& prev_fname, 5186 const source_location& loc, const std::string& msg, 5187 const Ts& ... tail) 5188 { 5189 return format_location_impl(lnw, prev_fname, loc, msg) + 5190 format_location_rec(lnw, loc.file_name(), tail...); 5191 } 5192 5193 } // namespace detail 5194 5195 // format a location info without title 5196 template<typename ... Ts> 5197 std::string format_location( 5198 const source_location& loc, const std::string& msg, const Ts& ... tail) 5199 { 5200 const auto lnw = detail::line_width(loc, msg, tail...); 5201 5202 const std::string f(""); // at the 1st iteration, no prev_filename is given 5203 return detail::format_location_rec(lnw, f, loc, msg, tail...); 5204 } 5205 5206 } // toml 5207 #endif // TOML11_SOURCE_LOCATION_FWD_HPP 5208 5209 #if ! defined(TOML11_COMPILE_SOURCES) 5210 #ifndef TOML11_SOURCE_LOCATION_IMPL_HPP 5211 #define TOML11_SOURCE_LOCATION_IMPL_HPP 5212 5213 5214 5215 #include <iomanip> 5216 #include <sstream> 5217 #include <string> 5218 #include <vector> 5219 5220 #include <cctype> 5221 5222 namespace toml 5223 { 5224 5225 TOML11_INLINE source_location::source_location(const detail::region& r) 5226 : is_ok_(false), 5227 first_line_(1), 5228 first_column_(1), 5229 last_line_(1), 5230 last_column_(1), 5231 length_(0), 5232 file_name_("unknown file") 5233 { 5234 if(r.is_ok()) 5235 { 5236 this->is_ok_ = true; 5237 this->file_name_ = r.source_name(); 5238 this->first_line_ = r.first_line_number(); 5239 this->first_column_ = r.first_column_number(); 5240 this->last_line_ = r.last_line_number(); 5241 this->last_column_ = r.last_column_number(); 5242 this->length_ = r.length(); 5243 this->line_str_ = r.as_lines(); 5244 } 5245 } 5246 5247 TOML11_INLINE std::string const& source_location::first_line() const 5248 { 5249 if(this->line_str_.size() == 0) 5250 { 5251 throw std::out_of_range("toml::source_location::first_line: `lines` is empty"); 5252 } 5253 return this->line_str_.front(); 5254 } 5255 TOML11_INLINE std::string const& source_location::last_line() const 5256 { 5257 if(this->line_str_.size() == 0) 5258 { 5259 throw std::out_of_range("toml::source_location::first_line: `lines` is empty"); 5260 } 5261 return this->line_str_.back(); 5262 } 5263 5264 namespace detail 5265 { 5266 5267 TOML11_INLINE std::size_t integer_width_base10(std::size_t i) noexcept 5268 { 5269 std::size_t width = 0; 5270 while(i != 0) 5271 { 5272 i /= 10; 5273 width += 1; 5274 } 5275 return width; 5276 } 5277 5278 TOML11_INLINE std::ostringstream& 5279 format_filename(std::ostringstream& oss, const source_location& loc) 5280 { 5281 // --> example.toml 5282 oss << color::bold << color::blue << " --> " << color::reset 5283 << color::bold << loc.file_name() << '\n' << color::reset; 5284 return oss; 5285 } 5286 5287 TOML11_INLINE std::ostringstream& format_empty_line(std::ostringstream& oss, 5288 const std::size_t lnw) 5289 { 5290 // | 5291 oss << detail::make_string(lnw + 1, ' ') 5292 << color::bold << color::blue << " |\n" << color::reset; 5293 return oss; 5294 } 5295 5296 TOML11_INLINE std::ostringstream& format_line(std::ostringstream& oss, 5297 const std::size_t lnw, const std::size_t linenum, const std::string& line) 5298 { 5299 // 10 | key = "value" 5300 oss << ' ' << color::bold << color::blue 5301 << std::setw(static_cast<int>(lnw)) 5302 << std::right << linenum << " | " << color::reset; 5303 for(const char c : line) 5304 { 5305 if(std::isgraph(c) || c == ' ') 5306 { 5307 oss << c; 5308 } 5309 else 5310 { 5311 oss << show_char(c); 5312 } 5313 } 5314 oss << '\n'; 5315 return oss; 5316 } 5317 TOML11_INLINE std::ostringstream& format_underline(std::ostringstream& oss, 5318 const std::size_t lnw, const std::size_t col, const std::size_t len, 5319 const std::string& msg) 5320 { 5321 // | ^^^^^^^-- this part 5322 oss << make_string(lnw + 1, ' ') 5323 << color::bold << color::blue << " | " << color::reset; 5324 5325 oss << make_string(col-1 /*1-origin*/, ' ') 5326 << color::bold << color::red 5327 << make_string(len, '^') << "-- " 5328 << color::reset << msg << '\n'; 5329 5330 return oss; 5331 } 5332 5333 TOML11_INLINE std::string format_location_impl(const std::size_t lnw, 5334 const std::string& prev_fname, 5335 const source_location& loc, const std::string& msg) 5336 { 5337 std::ostringstream oss; 5338 5339 if(loc.file_name() != prev_fname) 5340 { 5341 format_filename(oss, loc); 5342 if( ! loc.lines().empty()) 5343 { 5344 format_empty_line(oss, lnw); 5345 } 5346 } 5347 5348 if(loc.lines().size() == 1) 5349 { 5350 // when column points LF, it exceeds the size of the first line. 5351 std::size_t underline_limit = 1; 5352 if(loc.first_line().size() < loc.first_column_number()) 5353 { 5354 underline_limit = 1; 5355 } 5356 else 5357 { 5358 underline_limit = loc.first_line().size() - loc.first_column_number() + 1; 5359 } 5360 const auto underline_len = (std::min)(underline_limit, loc.length()); 5361 5362 format_line(oss, lnw, loc.first_line_number(), loc.first_line()); 5363 format_underline(oss, lnw, loc.first_column_number(), underline_len, msg); 5364 } 5365 else if(loc.lines().size() == 2) 5366 { 5367 const auto first_underline_len = 5368 loc.first_line().size() - loc.first_column_number() + 1; 5369 format_line(oss, lnw, loc.first_line_number(), loc.first_line()); 5370 format_underline(oss, lnw, loc.first_column_number(), 5371 first_underline_len, ""); 5372 5373 format_line(oss, lnw, loc.last_line_number(), loc.last_line()); 5374 format_underline(oss, lnw, 1, loc.last_column_number(), msg); 5375 } 5376 else if(loc.lines().size() > 2) 5377 { 5378 const auto first_underline_len = 5379 loc.first_line().size() - loc.first_column_number() + 1; 5380 format_line(oss, lnw, loc.first_line_number(), loc.first_line()); 5381 format_underline(oss, lnw, loc.first_column_number(), 5382 first_underline_len, "and"); 5383 5384 if(loc.lines().size() == 3) 5385 { 5386 format_line(oss, lnw, loc.first_line_number()+1, loc.lines().at(1)); 5387 format_underline(oss, lnw, 1, loc.lines().at(1).size(), "and"); 5388 } 5389 else 5390 { 5391 format_line(oss, lnw, loc.first_line_number()+1, " ..."); 5392 format_empty_line(oss, lnw); 5393 } 5394 format_line(oss, lnw, loc.last_line_number(), loc.last_line()); 5395 format_underline(oss, lnw, 1, loc.last_column_number(), msg); 5396 } 5397 // if loc is empty, do nothing. 5398 return oss.str(); 5399 } 5400 5401 } // namespace detail 5402 } // toml 5403 #endif // TOML11_SOURCE_LOCATION_IMPL_HPP 5404 #endif 5405 5406 #endif // TOML11_SOURCE_LOCATION_HPP 5407 #ifndef TOML11_ERROR_INFO_HPP 5408 #define TOML11_ERROR_INFO_HPP 5409 5410 #ifndef TOML11_ERROR_INFO_FWD_HPP 5411 #define TOML11_ERROR_INFO_FWD_HPP 5412 5413 5414 namespace toml 5415 { 5416 5417 // error info returned from parser. 5418 struct error_info 5419 { 5420 error_info(std::string t, source_location l, std::string m, std::string s = "") 5421 : title_(std::move(t)), locations_{std::make_pair(std::move(l), std::move(m))}, 5422 suffix_(std::move(s)) 5423 {} 5424 5425 error_info(std::string t, std::vector<std::pair<source_location, std::string>> l, 5426 std::string s = "") 5427 : title_(std::move(t)), locations_(std::move(l)), suffix_(std::move(s)) 5428 {} 5429 5430 std::string const& title() const noexcept {return title_;} 5431 std::string & title() noexcept {return title_;} 5432 5433 std::vector<std::pair<source_location, std::string>> const& 5434 locations() const noexcept {return locations_;} 5435 5436 void add_locations(source_location loc, std::string msg) noexcept 5437 { 5438 locations_.emplace_back(std::move(loc), std::move(msg)); 5439 } 5440 5441 std::string const& suffix() const noexcept {return suffix_;} 5442 std::string & suffix() noexcept {return suffix_;} 5443 5444 private: 5445 5446 std::string title_; 5447 std::vector<std::pair<source_location, std::string>> locations_; 5448 std::string suffix_; // hint or something like that 5449 }; 5450 5451 // forward decl 5452 template<typename TypeConfig> 5453 class basic_value; 5454 5455 namespace detail 5456 { 5457 inline error_info make_error_info_rec(error_info e) 5458 { 5459 return e; 5460 } 5461 inline error_info make_error_info_rec(error_info e, std::string s) 5462 { 5463 e.suffix() = s; 5464 return e; 5465 } 5466 5467 template<typename TC, typename ... Ts> 5468 error_info make_error_info_rec(error_info e, 5469 const basic_value<TC>& v, std::string msg, Ts&& ... tail); 5470 5471 template<typename ... Ts> 5472 error_info make_error_info_rec(error_info e, 5473 source_location loc, std::string msg, Ts&& ... tail) 5474 { 5475 e.add_locations(std::move(loc), std::move(msg)); 5476 return make_error_info_rec(std::move(e), std::forward<Ts>(tail)...); 5477 } 5478 5479 } // detail 5480 5481 template<typename ... Ts> 5482 error_info make_error_info( 5483 std::string title, source_location loc, std::string msg, Ts&& ... tail) 5484 { 5485 error_info ei(std::move(title), std::move(loc), std::move(msg)); 5486 return detail::make_error_info_rec(ei, std::forward<Ts>(tail) ... ); 5487 } 5488 5489 std::string format_error(const std::string& errkind, const error_info& err); 5490 std::string format_error(const error_info& err); 5491 5492 // for custom error message 5493 template<typename ... Ts> 5494 std::string format_error(std::string title, 5495 source_location loc, std::string msg, Ts&& ... tail) 5496 { 5497 return format_error("", make_error_info(std::move(title), 5498 std::move(loc), std::move(msg), std::forward<Ts>(tail)...)); 5499 } 5500 5501 std::ostream& operator<<(std::ostream& os, const error_info& e); 5502 5503 } // toml 5504 #endif // TOML11_ERROR_INFO_FWD_HPP 5505 5506 #if ! defined(TOML11_COMPILE_SOURCES) 5507 #ifndef TOML11_ERROR_INFO_IMPL_HPP 5508 #define TOML11_ERROR_INFO_IMPL_HPP 5509 5510 5511 #include <sstream> 5512 5513 namespace toml 5514 { 5515 5516 TOML11_INLINE std::string format_error(const std::string& errkind, const error_info& err) 5517 { 5518 std::string errmsg; 5519 if( ! errkind.empty()) 5520 { 5521 errmsg = errkind; 5522 errmsg += ' '; 5523 } 5524 errmsg += err.title(); 5525 errmsg += '\n'; 5526 5527 const auto lnw = [&err]() { 5528 std::size_t width = 0; 5529 for(const auto& l : err.locations()) 5530 { 5531 width = (std::max)(detail::integer_width_base10(l.first.last_line_number()), width); 5532 } 5533 return width; 5534 }(); 5535 5536 bool first = true; 5537 std::string prev_fname; 5538 for(const auto& lm : err.locations()) 5539 { 5540 if( ! first) 5541 { 5542 std::ostringstream oss; 5543 oss << detail::make_string(lnw + 1, ' ') 5544 << color::bold << color::blue << " |" << color::reset 5545 << color::bold << " ...\n" << color::reset; 5546 oss << detail::make_string(lnw + 1, ' ') 5547 << color::bold << color::blue << " |\n" << color::reset; 5548 errmsg += oss.str(); 5549 } 5550 5551 const auto& l = lm.first; 5552 const auto& m = lm.second; 5553 5554 errmsg += detail::format_location_impl(lnw, prev_fname, l, m); 5555 5556 prev_fname = l.file_name(); 5557 first = false; 5558 } 5559 5560 errmsg += err.suffix(); 5561 5562 return errmsg; 5563 } 5564 5565 TOML11_INLINE std::string format_error(const error_info& err) 5566 { 5567 std::ostringstream oss; 5568 oss << color::red << color::bold << "[error]" << color::reset; 5569 return format_error(oss.str(), err); 5570 } 5571 5572 TOML11_INLINE std::ostream& operator<<(std::ostream& os, const error_info& e) 5573 { 5574 os << format_error(e); 5575 return os; 5576 } 5577 5578 } // toml 5579 #endif // TOML11_ERROR_INFO_IMPL_HPP 5580 #endif 5581 5582 #endif // TOML11_ERROR_INFO_HPP 5583 #ifndef TOML11_VALUE_HPP 5584 #define TOML11_VALUE_HPP 5585 5586 5587 #ifdef TOML11_HAS_STRING_VIEW 5588 #include <string_view> 5589 #endif 5590 5591 #include <cassert> 5592 5593 namespace toml 5594 { 5595 template<typename TypeConfig> 5596 class basic_value; 5597 5598 struct type_error final : public ::toml::exception 5599 { 5600 public: 5601 type_error(std::string what_arg, source_location loc) 5602 : what_(std::move(what_arg)), loc_(std::move(loc)) 5603 {} 5604 ~type_error() noexcept override = default; 5605 5606 const char* what() const noexcept override {return what_.c_str();} 5607 5608 source_location const& location() const noexcept {return loc_;} 5609 5610 private: 5611 std::string what_; 5612 source_location loc_; 5613 }; 5614 5615 // only for internal use 5616 namespace detail 5617 { 5618 template<typename TC> 5619 error_info make_type_error(const basic_value<TC>&, const std::string&, const value_t); 5620 5621 template<typename TC> 5622 error_info make_not_found_error(const basic_value<TC>&, const std::string&, const typename basic_value<TC>::key_type&); 5623 5624 template<typename TC> 5625 void change_region_of_value(basic_value<TC>&, const basic_value<TC>&); 5626 5627 template<typename TC, value_t V> 5628 struct getter; 5629 } // detail 5630 5631 template<typename TypeConfig> 5632 class basic_value 5633 { 5634 public: 5635 5636 using config_type = TypeConfig; 5637 using key_type = typename config_type::string_type; 5638 using value_type = basic_value<config_type>; 5639 using boolean_type = typename config_type::boolean_type; 5640 using integer_type = typename config_type::integer_type; 5641 using floating_type = typename config_type::floating_type; 5642 using string_type = typename config_type::string_type; 5643 using local_time_type = ::toml::local_time; 5644 using local_date_type = ::toml::local_date; 5645 using local_datetime_type = ::toml::local_datetime; 5646 using offset_datetime_type = ::toml::offset_datetime; 5647 using array_type = typename config_type::template array_type<value_type>; 5648 using table_type = typename config_type::template table_type<key_type, value_type>; 5649 using comment_type = typename config_type::comment_type; 5650 using char_type = typename string_type::value_type; 5651 5652 private: 5653 5654 using region_type = detail::region; 5655 5656 public: 5657 5658 basic_value() noexcept 5659 : type_(value_t::empty), empty_('\0'), region_{}, comments_{} 5660 {} 5661 ~basic_value() noexcept {this->cleanup();} 5662 5663 // copy/move constructor/assigner ===================================== {{{ 5664 5665 basic_value(const basic_value& v) 5666 : type_(v.type_), region_(v.region_), comments_(v.comments_) 5667 { 5668 switch(this->type_) 5669 { 5670 case value_t::boolean : assigner(boolean_ , v.boolean_ ); break; 5671 case value_t::integer : assigner(integer_ , v.integer_ ); break; 5672 case value_t::floating : assigner(floating_ , v.floating_ ); break; 5673 case value_t::string : assigner(string_ , v.string_ ); break; 5674 case value_t::offset_datetime: assigner(offset_datetime_, v.offset_datetime_); break; 5675 case value_t::local_datetime : assigner(local_datetime_ , v.local_datetime_ ); break; 5676 case value_t::local_date : assigner(local_date_ , v.local_date_ ); break; 5677 case value_t::local_time : assigner(local_time_ , v.local_time_ ); break; 5678 case value_t::array : assigner(array_ , v.array_ ); break; 5679 case value_t::table : assigner(table_ , v.table_ ); break; 5680 default : assigner(empty_ , '\0' ); break; 5681 } 5682 } 5683 basic_value(basic_value&& v) 5684 : type_(v.type()), region_(std::move(v.region_)), 5685 comments_(std::move(v.comments_)) 5686 { 5687 switch(this->type_) 5688 { 5689 case value_t::boolean : assigner(boolean_ , std::move(v.boolean_ )); break; 5690 case value_t::integer : assigner(integer_ , std::move(v.integer_ )); break; 5691 case value_t::floating : assigner(floating_ , std::move(v.floating_ )); break; 5692 case value_t::string : assigner(string_ , std::move(v.string_ )); break; 5693 case value_t::offset_datetime: assigner(offset_datetime_, std::move(v.offset_datetime_)); break; 5694 case value_t::local_datetime : assigner(local_datetime_ , std::move(v.local_datetime_ )); break; 5695 case value_t::local_date : assigner(local_date_ , std::move(v.local_date_ )); break; 5696 case value_t::local_time : assigner(local_time_ , std::move(v.local_time_ )); break; 5697 case value_t::array : assigner(array_ , std::move(v.array_ )); break; 5698 case value_t::table : assigner(table_ , std::move(v.table_ )); break; 5699 default : assigner(empty_ , '\0' ); break; 5700 } 5701 } 5702 5703 basic_value& operator=(const basic_value& v) 5704 { 5705 if(this == std::addressof(v)) {return *this;} 5706 5707 this->cleanup(); 5708 this->type_ = v.type_; 5709 this->region_ = v.region_; 5710 this->comments_ = v.comments_; 5711 switch(this->type_) 5712 { 5713 case value_t::boolean : assigner(boolean_ , v.boolean_ ); break; 5714 case value_t::integer : assigner(integer_ , v.integer_ ); break; 5715 case value_t::floating : assigner(floating_ , v.floating_ ); break; 5716 case value_t::string : assigner(string_ , v.string_ ); break; 5717 case value_t::offset_datetime: assigner(offset_datetime_, v.offset_datetime_); break; 5718 case value_t::local_datetime : assigner(local_datetime_ , v.local_datetime_ ); break; 5719 case value_t::local_date : assigner(local_date_ , v.local_date_ ); break; 5720 case value_t::local_time : assigner(local_time_ , v.local_time_ ); break; 5721 case value_t::array : assigner(array_ , v.array_ ); break; 5722 case value_t::table : assigner(table_ , v.table_ ); break; 5723 default : assigner(empty_ , '\0' ); break; 5724 } 5725 return *this; 5726 } 5727 basic_value& operator=(basic_value&& v) 5728 { 5729 if(this == std::addressof(v)) {return *this;} 5730 5731 this->cleanup(); 5732 this->type_ = v.type_; 5733 this->region_ = std::move(v.region_); 5734 this->comments_ = std::move(v.comments_); 5735 switch(this->type_) 5736 { 5737 case value_t::boolean : assigner(boolean_ , std::move(v.boolean_ )); break; 5738 case value_t::integer : assigner(integer_ , std::move(v.integer_ )); break; 5739 case value_t::floating : assigner(floating_ , std::move(v.floating_ )); break; 5740 case value_t::string : assigner(string_ , std::move(v.string_ )); break; 5741 case value_t::offset_datetime: assigner(offset_datetime_, std::move(v.offset_datetime_)); break; 5742 case value_t::local_datetime : assigner(local_datetime_ , std::move(v.local_datetime_ )); break; 5743 case value_t::local_date : assigner(local_date_ , std::move(v.local_date_ )); break; 5744 case value_t::local_time : assigner(local_time_ , std::move(v.local_time_ )); break; 5745 case value_t::array : assigner(array_ , std::move(v.array_ )); break; 5746 case value_t::table : assigner(table_ , std::move(v.table_ )); break; 5747 default : assigner(empty_ , '\0' ); break; 5748 } 5749 return *this; 5750 } 5751 // }}} 5752 5753 // constructor to overwrite commnets ================================== {{{ 5754 5755 basic_value(basic_value v, std::vector<std::string> com) 5756 : type_(v.type()), region_(std::move(v.region_)), 5757 comments_(std::move(com)) 5758 { 5759 switch(this->type_) 5760 { 5761 case value_t::boolean : assigner(boolean_ , std::move(v.boolean_ )); break; 5762 case value_t::integer : assigner(integer_ , std::move(v.integer_ )); break; 5763 case value_t::floating : assigner(floating_ , std::move(v.floating_ )); break; 5764 case value_t::string : assigner(string_ , std::move(v.string_ )); break; 5765 case value_t::offset_datetime: assigner(offset_datetime_, std::move(v.offset_datetime_)); break; 5766 case value_t::local_datetime : assigner(local_datetime_ , std::move(v.local_datetime_ )); break; 5767 case value_t::local_date : assigner(local_date_ , std::move(v.local_date_ )); break; 5768 case value_t::local_time : assigner(local_time_ , std::move(v.local_time_ )); break; 5769 case value_t::array : assigner(array_ , std::move(v.array_ )); break; 5770 case value_t::table : assigner(table_ , std::move(v.table_ )); break; 5771 default : assigner(empty_ , '\0' ); break; 5772 } 5773 } 5774 // }}} 5775 5776 // conversion between different basic_values ========================== {{{ 5777 5778 template<typename TI> 5779 basic_value(basic_value<TI> other) 5780 : type_(other.type_), 5781 region_(std::move(other.region_)), 5782 comments_(std::move(other.comments_)) 5783 { 5784 switch(other.type_) 5785 { 5786 // use auto-convert in constructor 5787 case value_t::boolean : assigner(boolean_ , std::move(other.boolean_ )); break; 5788 case value_t::integer : assigner(integer_ , std::move(other.integer_ )); break; 5789 case value_t::floating : assigner(floating_ , std::move(other.floating_ )); break; 5790 case value_t::string : assigner(string_ , std::move(other.string_ )); break; 5791 case value_t::offset_datetime: assigner(offset_datetime_, std::move(other.offset_datetime_)); break; 5792 case value_t::local_datetime : assigner(local_datetime_ , std::move(other.local_datetime_ )); break; 5793 case value_t::local_date : assigner(local_date_ , std::move(other.local_date_ )); break; 5794 case value_t::local_time : assigner(local_time_ , std::move(other.local_time_ )); break; 5795 5796 // may have different container type 5797 case value_t::array : 5798 { 5799 array_type tmp( 5800 std::make_move_iterator(other.array_.value.get().begin()), 5801 std::make_move_iterator(other.array_.value.get().end())); 5802 assigner(array_, array_storage( 5803 detail::storage<array_type>(std::move(tmp)), 5804 other.array_.format 5805 )); 5806 break; 5807 } 5808 case value_t::table : 5809 { 5810 table_type tmp( 5811 std::make_move_iterator(other.table_.value.get().begin()), 5812 std::make_move_iterator(other.table_.value.get().end())); 5813 assigner(table_, table_storage( 5814 detail::storage<table_type>(std::move(tmp)), 5815 other.table_.format 5816 )); 5817 break; 5818 } 5819 default: break; 5820 } 5821 } 5822 5823 template<typename TI> 5824 basic_value(basic_value<TI> other, std::vector<std::string> com) 5825 : type_(other.type_), 5826 region_(std::move(other.region_)), 5827 comments_(std::move(com)) 5828 { 5829 switch(other.type_) 5830 { 5831 // use auto-convert in constructor 5832 case value_t::boolean : assigner(boolean_ , std::move(other.boolean_ )); break; 5833 case value_t::integer : assigner(integer_ , std::move(other.integer_ )); break; 5834 case value_t::floating : assigner(floating_ , std::move(other.floating_ )); break; 5835 case value_t::string : assigner(string_ , std::move(other.string_ )); break; 5836 case value_t::offset_datetime: assigner(offset_datetime_, std::move(other.offset_datetime_)); break; 5837 case value_t::local_datetime : assigner(local_datetime_ , std::move(other.local_datetime_ )); break; 5838 case value_t::local_date : assigner(local_date_ , std::move(other.local_date_ )); break; 5839 case value_t::local_time : assigner(local_time_ , std::move(other.local_time_ )); break; 5840 5841 // may have different container type 5842 case value_t::array : 5843 { 5844 array_type tmp( 5845 std::make_move_iterator(other.array_.value.get().begin()), 5846 std::make_move_iterator(other.array_.value.get().end())); 5847 assigner(array_, array_storage( 5848 detail::storage<array_type>(std::move(tmp)), 5849 other.array_.format 5850 )); 5851 break; 5852 } 5853 case value_t::table : 5854 { 5855 table_type tmp( 5856 std::make_move_iterator(other.table_.value.get().begin()), 5857 std::make_move_iterator(other.table_.value.get().end())); 5858 assigner(table_, table_storage( 5859 detail::storage<table_type>(std::move(tmp)), 5860 other.table_.format 5861 )); 5862 break; 5863 } 5864 default: break; 5865 } 5866 } 5867 template<typename TI> 5868 basic_value& operator=(basic_value<TI> other) 5869 { 5870 this->cleanup(); 5871 this->region_ = other.region_; 5872 this->comments_ = comment_type(other.comments_); 5873 this->type_ = other.type_; 5874 switch(other.type_) 5875 { 5876 // use auto-convert in constructor 5877 case value_t::boolean : assigner(boolean_ , std::move(other.boolean_ )); break; 5878 case value_t::integer : assigner(integer_ , std::move(other.integer_ )); break; 5879 case value_t::floating : assigner(floating_ , std::move(other.floating_ )); break; 5880 case value_t::string : assigner(string_ , std::move(other.string_ )); break; 5881 case value_t::offset_datetime: assigner(offset_datetime_, std::move(other.offset_datetime_)); break; 5882 case value_t::local_datetime : assigner(local_datetime_ , std::move(other.local_datetime_ )); break; 5883 case value_t::local_date : assigner(local_date_ , std::move(other.local_date_ )); break; 5884 case value_t::local_time : assigner(local_time_ , std::move(other.local_time_ )); break; 5885 5886 // may have different container type 5887 case value_t::array : 5888 { 5889 array_type tmp( 5890 std::make_move_iterator(other.array_.value.get().begin()), 5891 std::make_move_iterator(other.array_.value.get().end())); 5892 assigner(array_, array_storage( 5893 detail::storage<array_type>(std::move(tmp)), 5894 other.array_.format 5895 )); 5896 break; 5897 } 5898 case value_t::table : 5899 { 5900 table_type tmp( 5901 std::make_move_iterator(other.table_.value.get().begin()), 5902 std::make_move_iterator(other.table_.value.get().end())); 5903 assigner(table_, table_storage( 5904 detail::storage<table_type>(std::move(tmp)), 5905 other.table_.format 5906 )); 5907 break; 5908 } 5909 default: break; 5910 } 5911 return *this; 5912 } 5913 // }}} 5914 5915 // constructor (boolean) ============================================== {{{ 5916 5917 basic_value(boolean_type x) 5918 : basic_value(x, boolean_format_info{}, std::vector<std::string>{}, region_type{}) 5919 {} 5920 basic_value(boolean_type x, boolean_format_info fmt) 5921 : basic_value(x, fmt, std::vector<std::string>{}, region_type{}) 5922 {} 5923 basic_value(boolean_type x, std::vector<std::string> com) 5924 : basic_value(x, boolean_format_info{}, std::move(com), region_type{}) 5925 {} 5926 basic_value(boolean_type x, boolean_format_info fmt, std::vector<std::string> com) 5927 : basic_value(x, fmt, std::move(com), region_type{}) 5928 {} 5929 basic_value(boolean_type x, boolean_format_info fmt, 5930 std::vector<std::string> com, region_type reg) 5931 : type_(value_t::boolean), boolean_(boolean_storage(x, fmt)), 5932 region_(std::move(reg)), comments_(std::move(com)) 5933 {} 5934 basic_value& operator=(boolean_type x) 5935 { 5936 boolean_format_info fmt; 5937 if(this->is_boolean()) 5938 { 5939 fmt = this->as_boolean_fmt(); 5940 } 5941 this->cleanup(); 5942 this->type_ = value_t::boolean; 5943 this->region_ = region_type{}; 5944 assigner(this->boolean_, boolean_storage(x, fmt)); 5945 return *this; 5946 } 5947 5948 // }}} 5949 5950 // constructor (integer) ============================================== {{{ 5951 5952 basic_value(integer_type x) 5953 : basic_value(std::move(x), integer_format_info{}, std::vector<std::string>{}, region_type{}) 5954 {} 5955 basic_value(integer_type x, integer_format_info fmt) 5956 : basic_value(std::move(x), std::move(fmt), std::vector<std::string>{}, region_type{}) 5957 {} 5958 basic_value(integer_type x, std::vector<std::string> com) 5959 : basic_value(std::move(x), integer_format_info{}, std::move(com), region_type{}) 5960 {} 5961 basic_value(integer_type x, integer_format_info fmt, std::vector<std::string> com) 5962 : basic_value(std::move(x), std::move(fmt), std::move(com), region_type{}) 5963 {} 5964 basic_value(integer_type x, integer_format_info fmt, std::vector<std::string> com, region_type reg) 5965 : type_(value_t::integer), integer_(integer_storage(std::move(x), std::move(fmt))), 5966 region_(std::move(reg)), comments_(std::move(com)) 5967 {} 5968 basic_value& operator=(integer_type x) 5969 { 5970 integer_format_info fmt; 5971 if(this->is_integer()) 5972 { 5973 fmt = this->as_integer_fmt(); 5974 } 5975 this->cleanup(); 5976 this->type_ = value_t::integer; 5977 this->region_ = region_type{}; 5978 assigner(this->integer_, integer_storage(std::move(x), std::move(fmt))); 5979 return *this; 5980 } 5981 5982 private: 5983 5984 template<typename T> 5985 using enable_if_integer_like_t = cxx::enable_if_t<cxx::conjunction< 5986 cxx::negation<std::is_same<cxx::remove_cvref_t<T>, boolean_type>>, 5987 cxx::negation<std::is_same<cxx::remove_cvref_t<T>, integer_type>>, 5988 std::is_integral<cxx::remove_cvref_t<T>> 5989 >::value, std::nullptr_t>; 5990 5991 public: 5992 5993 template<typename T, enable_if_integer_like_t<T> = nullptr> 5994 basic_value(T x) 5995 : basic_value(std::move(x), integer_format_info{}, std::vector<std::string>{}, region_type{}) 5996 {} 5997 template<typename T, enable_if_integer_like_t<T> = nullptr> 5998 basic_value(T x, integer_format_info fmt) 5999 : basic_value(std::move(x), std::move(fmt), std::vector<std::string>{}, region_type{}) 6000 {} 6001 template<typename T, enable_if_integer_like_t<T> = nullptr> 6002 basic_value(T x, std::vector<std::string> com) 6003 : basic_value(std::move(x), integer_format_info{}, std::move(com), region_type{}) 6004 {} 6005 template<typename T, enable_if_integer_like_t<T> = nullptr> 6006 basic_value(T x, integer_format_info fmt, std::vector<std::string> com) 6007 : basic_value(std::move(x), std::move(fmt), std::move(com), region_type{}) 6008 {} 6009 template<typename T, enable_if_integer_like_t<T> = nullptr> 6010 basic_value(T x, integer_format_info fmt, std::vector<std::string> com, region_type reg) 6011 : type_(value_t::integer), integer_(integer_storage(std::move(x), std::move(fmt))), 6012 region_(std::move(reg)), comments_(std::move(com)) 6013 {} 6014 template<typename T, enable_if_integer_like_t<T> = nullptr> 6015 basic_value& operator=(T x) 6016 { 6017 integer_format_info fmt; 6018 if(this->is_integer()) 6019 { 6020 fmt = this->as_integer_fmt(); 6021 } 6022 this->cleanup(); 6023 this->type_ = value_t::integer; 6024 this->region_ = region_type{}; 6025 assigner(this->integer_, integer_storage(x, std::move(fmt))); 6026 return *this; 6027 } 6028 6029 // }}} 6030 6031 // constructor (floating) ============================================= {{{ 6032 6033 basic_value(floating_type x) 6034 : basic_value(std::move(x), floating_format_info{}, std::vector<std::string>{}, region_type{}) 6035 {} 6036 basic_value(floating_type x, floating_format_info fmt) 6037 : basic_value(std::move(x), std::move(fmt), std::vector<std::string>{}, region_type{}) 6038 {} 6039 basic_value(floating_type x, std::vector<std::string> com) 6040 : basic_value(std::move(x), floating_format_info{}, std::move(com), region_type{}) 6041 {} 6042 basic_value(floating_type x, floating_format_info fmt, std::vector<std::string> com) 6043 : basic_value(std::move(x), std::move(fmt), std::move(com), region_type{}) 6044 {} 6045 basic_value(floating_type x, floating_format_info fmt, std::vector<std::string> com, region_type reg) 6046 : type_(value_t::floating), floating_(floating_storage(std::move(x), std::move(fmt))), 6047 region_(std::move(reg)), comments_(std::move(com)) 6048 {} 6049 basic_value& operator=(floating_type x) 6050 { 6051 floating_format_info fmt; 6052 if(this->is_floating()) 6053 { 6054 fmt = this->as_floating_fmt(); 6055 } 6056 this->cleanup(); 6057 this->type_ = value_t::floating; 6058 this->region_ = region_type{}; 6059 assigner(this->floating_, floating_storage(std::move(x), std::move(fmt))); 6060 return *this; 6061 } 6062 6063 private: 6064 6065 template<typename T> 6066 using enable_if_floating_like_t = cxx::enable_if_t<cxx::conjunction< 6067 cxx::negation<std::is_same<cxx::remove_cvref_t<T>, floating_type>>, 6068 std::is_floating_point<cxx::remove_cvref_t<T>> 6069 >::value, std::nullptr_t>; 6070 6071 public: 6072 6073 template<typename T, enable_if_floating_like_t<T> = nullptr> 6074 basic_value(T x) 6075 : basic_value(x, floating_format_info{}, std::vector<std::string>{}, region_type{}) 6076 {} 6077 6078 template<typename T, enable_if_floating_like_t<T> = nullptr> 6079 basic_value(T x, floating_format_info fmt) 6080 : basic_value(x, std::move(fmt), std::vector<std::string>{}, region_type{}) 6081 {} 6082 6083 template<typename T, enable_if_floating_like_t<T> = nullptr> 6084 basic_value(T x, std::vector<std::string> com) 6085 : basic_value(x, floating_format_info{}, std::move(com), region_type{}) 6086 {} 6087 6088 template<typename T, enable_if_floating_like_t<T> = nullptr> 6089 basic_value(T x, floating_format_info fmt, std::vector<std::string> com) 6090 : basic_value(x, std::move(fmt), std::move(com), region_type{}) 6091 {} 6092 6093 template<typename T, enable_if_floating_like_t<T> = nullptr> 6094 basic_value(T x, floating_format_info fmt, std::vector<std::string> com, region_type reg) 6095 : type_(value_t::floating), floating_(floating_storage(x, std::move(fmt))), 6096 region_(std::move(reg)), comments_(std::move(com)) 6097 {} 6098 6099 template<typename T, enable_if_floating_like_t<T> = nullptr> 6100 basic_value& operator=(T x) 6101 { 6102 floating_format_info fmt; 6103 if(this->is_floating()) 6104 { 6105 fmt = this->as_floating_fmt(); 6106 } 6107 this->cleanup(); 6108 this->type_ = value_t::floating; 6109 this->region_ = region_type{}; 6110 assigner(this->floating_, floating_storage(x, std::move(fmt))); 6111 return *this; 6112 } 6113 6114 // }}} 6115 6116 // constructor (string) =============================================== {{{ 6117 6118 basic_value(string_type x) 6119 : basic_value(std::move(x), string_format_info{}, std::vector<std::string>{}, region_type{}) 6120 {} 6121 basic_value(string_type x, string_format_info fmt) 6122 : basic_value(std::move(x), std::move(fmt), std::vector<std::string>{}, region_type{}) 6123 {} 6124 basic_value(string_type x, std::vector<std::string> com) 6125 : basic_value(std::move(x), string_format_info{}, std::move(com), region_type{}) 6126 {} 6127 basic_value(string_type x, string_format_info fmt, std::vector<std::string> com) 6128 : basic_value(std::move(x), std::move(fmt), std::move(com), region_type{}) 6129 {} 6130 basic_value(string_type x, string_format_info fmt, 6131 std::vector<std::string> com, region_type reg) 6132 : type_(value_t::string), string_(string_storage(std::move(x), std::move(fmt))), 6133 region_(std::move(reg)), comments_(std::move(com)) 6134 {} 6135 basic_value& operator=(string_type x) 6136 { 6137 string_format_info fmt; 6138 if(this->is_string()) 6139 { 6140 fmt = this->as_string_fmt(); 6141 } 6142 this->cleanup(); 6143 this->type_ = value_t::string; 6144 this->region_ = region_type{}; 6145 assigner(this->string_, string_storage(x, std::move(fmt))); 6146 return *this; 6147 } 6148 6149 // "string literal" 6150 6151 basic_value(const typename string_type::value_type* x) 6152 : basic_value(x, string_format_info{}, std::vector<std::string>{}, region_type{}) 6153 {} 6154 basic_value(const typename string_type::value_type* x, string_format_info fmt) 6155 : basic_value(x, std::move(fmt), std::vector<std::string>{}, region_type{}) 6156 {} 6157 basic_value(const typename string_type::value_type* x, std::vector<std::string> com) 6158 : basic_value(x, string_format_info{}, std::move(com), region_type{}) 6159 {} 6160 basic_value(const typename string_type::value_type* x, string_format_info fmt, std::vector<std::string> com) 6161 : basic_value(x, std::move(fmt), std::move(com), region_type{}) 6162 {} 6163 basic_value(const typename string_type::value_type* x, string_format_info fmt, 6164 std::vector<std::string> com, region_type reg) 6165 : type_(value_t::string), string_(string_storage(string_type(x), std::move(fmt))), 6166 region_(std::move(reg)), comments_(std::move(com)) 6167 {} 6168 basic_value& operator=(const typename string_type::value_type* x) 6169 { 6170 string_format_info fmt; 6171 if(this->is_string()) 6172 { 6173 fmt = this->as_string_fmt(); 6174 } 6175 this->cleanup(); 6176 this->type_ = value_t::string; 6177 this->region_ = region_type{}; 6178 assigner(this->string_, string_storage(string_type(x), std::move(fmt))); 6179 return *this; 6180 } 6181 6182 #if defined(TOML11_HAS_STRING_VIEW) 6183 using string_view_type = std::basic_string_view< 6184 typename string_type::value_type, typename string_type::traits_type>; 6185 6186 basic_value(string_view_type x) 6187 : basic_value(x, string_format_info{}, std::vector<std::string>{}, region_type{}) 6188 {} 6189 basic_value(string_view_type x, string_format_info fmt) 6190 : basic_value(x, std::move(fmt), std::vector<std::string>{}, region_type{}) 6191 {} 6192 basic_value(string_view_type x, std::vector<std::string> com) 6193 : basic_value(x, string_format_info{}, std::move(com), region_type{}) 6194 {} 6195 basic_value(string_view_type x, string_format_info fmt, std::vector<std::string> com) 6196 : basic_value(x, std::move(fmt), std::move(com), region_type{}) 6197 {} 6198 basic_value(string_view_type x, string_format_info fmt, 6199 std::vector<std::string> com, region_type reg) 6200 : type_(value_t::string), string_(string_storage(string_type(x), std::move(fmt))), 6201 region_(std::move(reg)), comments_(std::move(com)) 6202 {} 6203 basic_value& operator=(string_view_type x) 6204 { 6205 string_format_info fmt; 6206 if(this->is_string()) 6207 { 6208 fmt = this->as_string_fmt(); 6209 } 6210 this->cleanup(); 6211 this->type_ = value_t::string; 6212 this->region_ = region_type{}; 6213 assigner(this->string_, string_storage(string_type(x), std::move(fmt))); 6214 return *this; 6215 } 6216 6217 #endif // TOML11_HAS_STRING_VIEW 6218 6219 template<typename T, cxx::enable_if_t<cxx::conjunction< 6220 cxx::negation<std::is_same<cxx::remove_cvref_t<T>, string_type>>, 6221 detail::is_1byte_std_basic_string<T> 6222 >::value, std::nullptr_t> = nullptr> 6223 basic_value(const T& x) 6224 : basic_value(x, string_format_info{}, std::vector<std::string>{}, region_type{}) 6225 {} 6226 template<typename T, cxx::enable_if_t<cxx::conjunction< 6227 cxx::negation<std::is_same<cxx::remove_cvref_t<T>, string_type>>, 6228 detail::is_1byte_std_basic_string<T> 6229 >::value, std::nullptr_t> = nullptr> 6230 basic_value(const T& x, string_format_info fmt) 6231 : basic_value(x, std::move(fmt), std::vector<std::string>{}, region_type{}) 6232 {} 6233 template<typename T, cxx::enable_if_t<cxx::conjunction< 6234 cxx::negation<std::is_same<cxx::remove_cvref_t<T>, string_type>>, 6235 detail::is_1byte_std_basic_string<T> 6236 >::value, std::nullptr_t> = nullptr> 6237 basic_value(const T& x, std::vector<std::string> com) 6238 : basic_value(x, string_format_info{}, std::move(com), region_type{}) 6239 {} 6240 template<typename T, cxx::enable_if_t<cxx::conjunction< 6241 cxx::negation<std::is_same<cxx::remove_cvref_t<T>, string_type>>, 6242 detail::is_1byte_std_basic_string<T> 6243 >::value, std::nullptr_t> = nullptr> 6244 basic_value(const T& x, string_format_info fmt, std::vector<std::string> com) 6245 : basic_value(x, std::move(fmt), std::move(com), region_type{}) 6246 {} 6247 template<typename T, cxx::enable_if_t<cxx::conjunction< 6248 cxx::negation<std::is_same<cxx::remove_cvref_t<T>, string_type>>, 6249 detail::is_1byte_std_basic_string<T> 6250 >::value, std::nullptr_t> = nullptr> 6251 basic_value(const T& x, string_format_info fmt, 6252 std::vector<std::string> com, region_type reg) 6253 : type_(value_t::string), 6254 string_(string_storage(detail::string_conv<string_type>(x), std::move(fmt))), 6255 region_(std::move(reg)), comments_(std::move(com)) 6256 {} 6257 template<typename T, cxx::enable_if_t<cxx::conjunction< 6258 cxx::negation<std::is_same<cxx::remove_cvref_t<T>, string_type>>, 6259 detail::is_1byte_std_basic_string<T> 6260 >::value, std::nullptr_t> = nullptr> 6261 basic_value& operator=(const T& x) 6262 { 6263 string_format_info fmt; 6264 if(this->is_string()) 6265 { 6266 fmt = this->as_string_fmt(); 6267 } 6268 this->cleanup(); 6269 this->type_ = value_t::string; 6270 this->region_ = region_type{}; 6271 assigner(this->string_, string_storage(detail::string_conv<string_type>(x), std::move(fmt))); 6272 return *this; 6273 } 6274 6275 // }}} 6276 6277 // constructor (local_date) =========================================== {{{ 6278 6279 basic_value(local_date_type x) 6280 : basic_value(x, local_date_format_info{}, std::vector<std::string>{}, region_type{}) 6281 {} 6282 basic_value(local_date_type x, local_date_format_info fmt) 6283 : basic_value(x, fmt, std::vector<std::string>{}, region_type{}) 6284 {} 6285 basic_value(local_date_type x, std::vector<std::string> com) 6286 : basic_value(x, local_date_format_info{}, std::move(com), region_type{}) 6287 {} 6288 basic_value(local_date_type x, local_date_format_info fmt, std::vector<std::string> com) 6289 : basic_value(x, fmt, std::move(com), region_type{}) 6290 {} 6291 basic_value(local_date_type x, local_date_format_info fmt, 6292 std::vector<std::string> com, region_type reg) 6293 : type_(value_t::local_date), local_date_(local_date_storage(x, fmt)), 6294 region_(std::move(reg)), comments_(std::move(com)) 6295 {} 6296 basic_value& operator=(local_date_type x) 6297 { 6298 local_date_format_info fmt; 6299 if(this->is_local_date()) 6300 { 6301 fmt = this->as_local_date_fmt(); 6302 } 6303 this->cleanup(); 6304 this->type_ = value_t::local_date; 6305 this->region_ = region_type{}; 6306 assigner(this->local_date_, local_date_storage(x, fmt)); 6307 return *this; 6308 } 6309 6310 // }}} 6311 6312 // constructor (local_time) =========================================== {{{ 6313 6314 basic_value(local_time_type x) 6315 : basic_value(x, local_time_format_info{}, std::vector<std::string>{}, region_type{}) 6316 {} 6317 basic_value(local_time_type x, local_time_format_info fmt) 6318 : basic_value(x, fmt, std::vector<std::string>{}, region_type{}) 6319 {} 6320 basic_value(local_time_type x, std::vector<std::string> com) 6321 : basic_value(x, local_time_format_info{}, std::move(com), region_type{}) 6322 {} 6323 basic_value(local_time_type x, local_time_format_info fmt, std::vector<std::string> com) 6324 : basic_value(x, fmt, std::move(com), region_type{}) 6325 {} 6326 basic_value(local_time_type x, local_time_format_info fmt, 6327 std::vector<std::string> com, region_type reg) 6328 : type_(value_t::local_time), local_time_(local_time_storage(x, fmt)), 6329 region_(std::move(reg)), comments_(std::move(com)) 6330 {} 6331 basic_value& operator=(local_time_type x) 6332 { 6333 local_time_format_info fmt; 6334 if(this->is_local_time()) 6335 { 6336 fmt = this->as_local_time_fmt(); 6337 } 6338 this->cleanup(); 6339 this->type_ = value_t::local_time; 6340 this->region_ = region_type{}; 6341 assigner(this->local_time_, local_time_storage(x, fmt)); 6342 return *this; 6343 } 6344 6345 template<typename Rep, typename Period> 6346 basic_value(const std::chrono::duration<Rep, Period>& x) 6347 : basic_value(local_time_type(x), local_time_format_info{}, std::vector<std::string>{}, region_type{}) 6348 {} 6349 template<typename Rep, typename Period> 6350 basic_value(const std::chrono::duration<Rep, Period>& x, local_time_format_info fmt) 6351 : basic_value(local_time_type(x), std::move(fmt), std::vector<std::string>{}, region_type{}) 6352 {} 6353 template<typename Rep, typename Period> 6354 basic_value(const std::chrono::duration<Rep, Period>& x, std::vector<std::string> com) 6355 : basic_value(local_time_type(x), local_time_format_info{}, std::move(com), region_type{}) 6356 {} 6357 template<typename Rep, typename Period> 6358 basic_value(const std::chrono::duration<Rep, Period>& x, local_time_format_info fmt, std::vector<std::string> com) 6359 : basic_value(local_time_type(x), std::move(fmt), std::move(com), region_type{}) 6360 {} 6361 template<typename Rep, typename Period> 6362 basic_value(const std::chrono::duration<Rep, Period>& x, 6363 local_time_format_info fmt, 6364 std::vector<std::string> com, region_type reg) 6365 : basic_value(local_time_type(x), std::move(fmt), std::move(com), std::move(reg)) 6366 {} 6367 template<typename Rep, typename Period> 6368 basic_value& operator=(const std::chrono::duration<Rep, Period>& x) 6369 { 6370 local_time_format_info fmt; 6371 if(this->is_local_time()) 6372 { 6373 fmt = this->as_local_time_fmt(); 6374 } 6375 this->cleanup(); 6376 this->type_ = value_t::local_time; 6377 this->region_ = region_type{}; 6378 assigner(this->local_time_, local_time_storage(local_time_type(x), std::move(fmt))); 6379 return *this; 6380 } 6381 6382 // }}} 6383 6384 // constructor (local_datetime) =========================================== {{{ 6385 6386 basic_value(local_datetime_type x) 6387 : basic_value(x, local_datetime_format_info{}, std::vector<std::string>{}, region_type{}) 6388 {} 6389 basic_value(local_datetime_type x, local_datetime_format_info fmt) 6390 : basic_value(x, fmt, std::vector<std::string>{}, region_type{}) 6391 {} 6392 basic_value(local_datetime_type x, std::vector<std::string> com) 6393 : basic_value(x, local_datetime_format_info{}, std::move(com), region_type{}) 6394 {} 6395 basic_value(local_datetime_type x, local_datetime_format_info fmt, std::vector<std::string> com) 6396 : basic_value(x, fmt, std::move(com), region_type{}) 6397 {} 6398 basic_value(local_datetime_type x, local_datetime_format_info fmt, 6399 std::vector<std::string> com, region_type reg) 6400 : type_(value_t::local_datetime), local_datetime_(local_datetime_storage(x, fmt)), 6401 region_(std::move(reg)), comments_(std::move(com)) 6402 {} 6403 basic_value& operator=(local_datetime_type x) 6404 { 6405 local_datetime_format_info fmt; 6406 if(this->is_local_datetime()) 6407 { 6408 fmt = this->as_local_datetime_fmt(); 6409 } 6410 this->cleanup(); 6411 this->type_ = value_t::local_datetime; 6412 this->region_ = region_type{}; 6413 assigner(this->local_datetime_, local_datetime_storage(x, fmt)); 6414 return *this; 6415 } 6416 6417 // }}} 6418 6419 // constructor (offset_datetime) =========================================== {{{ 6420 6421 basic_value(offset_datetime_type x) 6422 : basic_value(x, offset_datetime_format_info{}, std::vector<std::string>{}, region_type{}) 6423 {} 6424 basic_value(offset_datetime_type x, offset_datetime_format_info fmt) 6425 : basic_value(x, fmt, std::vector<std::string>{}, region_type{}) 6426 {} 6427 basic_value(offset_datetime_type x, std::vector<std::string> com) 6428 : basic_value(x, offset_datetime_format_info{}, std::move(com), region_type{}) 6429 {} 6430 basic_value(offset_datetime_type x, offset_datetime_format_info fmt, std::vector<std::string> com) 6431 : basic_value(x, fmt, std::move(com), region_type{}) 6432 {} 6433 basic_value(offset_datetime_type x, offset_datetime_format_info fmt, 6434 std::vector<std::string> com, region_type reg) 6435 : type_(value_t::offset_datetime), offset_datetime_(offset_datetime_storage(x, fmt)), 6436 region_(std::move(reg)), comments_(std::move(com)) 6437 {} 6438 basic_value& operator=(offset_datetime_type x) 6439 { 6440 offset_datetime_format_info fmt; 6441 if(this->is_offset_datetime()) 6442 { 6443 fmt = this->as_offset_datetime_fmt(); 6444 } 6445 this->cleanup(); 6446 this->type_ = value_t::offset_datetime; 6447 this->region_ = region_type{}; 6448 assigner(this->offset_datetime_, offset_datetime_storage(x, fmt)); 6449 return *this; 6450 } 6451 6452 // system_clock::time_point 6453 6454 basic_value(std::chrono::system_clock::time_point x) 6455 : basic_value(offset_datetime_type(x), offset_datetime_format_info{}, std::vector<std::string>{}, region_type{}) 6456 {} 6457 basic_value(std::chrono::system_clock::time_point x, offset_datetime_format_info fmt) 6458 : basic_value(offset_datetime_type(x), fmt, std::vector<std::string>{}, region_type{}) 6459 {} 6460 basic_value(std::chrono::system_clock::time_point x, std::vector<std::string> com) 6461 : basic_value(offset_datetime_type(x), offset_datetime_format_info{}, std::move(com), region_type{}) 6462 {} 6463 basic_value(std::chrono::system_clock::time_point x, offset_datetime_format_info fmt, std::vector<std::string> com) 6464 : basic_value(offset_datetime_type(x), fmt, std::move(com), region_type{}) 6465 {} 6466 basic_value(std::chrono::system_clock::time_point x, offset_datetime_format_info fmt, 6467 std::vector<std::string> com, region_type reg) 6468 : basic_value(offset_datetime_type(x), std::move(fmt), std::move(com), std::move(reg)) 6469 {} 6470 basic_value& operator=(std::chrono::system_clock::time_point x) 6471 { 6472 offset_datetime_format_info fmt; 6473 if(this->is_offset_datetime()) 6474 { 6475 fmt = this->as_offset_datetime_fmt(); 6476 } 6477 this->cleanup(); 6478 this->type_ = value_t::offset_datetime; 6479 this->region_ = region_type{}; 6480 assigner(this->offset_datetime_, offset_datetime_storage(offset_datetime_type(x), fmt)); 6481 return *this; 6482 } 6483 6484 // }}} 6485 6486 // constructor (array) ================================================ {{{ 6487 6488 basic_value(array_type x) 6489 : basic_value(std::move(x), array_format_info{}, std::vector<std::string>{}, region_type{}) 6490 {} 6491 basic_value(array_type x, array_format_info fmt) 6492 : basic_value(std::move(x), std::move(fmt), std::vector<std::string>{}, region_type{}) 6493 {} 6494 basic_value(array_type x, std::vector<std::string> com) 6495 : basic_value(std::move(x), array_format_info{}, std::move(com), region_type{}) 6496 {} 6497 basic_value(array_type x, array_format_info fmt, std::vector<std::string> com) 6498 : basic_value(std::move(x), fmt, std::move(com), region_type{}) 6499 {} 6500 basic_value(array_type x, array_format_info fmt, 6501 std::vector<std::string> com, region_type reg) 6502 : type_(value_t::array), array_(array_storage( 6503 detail::storage<array_type>(std::move(x)), std::move(fmt) 6504 )), region_(std::move(reg)), comments_(std::move(com)) 6505 {} 6506 basic_value& operator=(array_type x) 6507 { 6508 array_format_info fmt; 6509 if(this->is_array()) 6510 { 6511 fmt = this->as_array_fmt(); 6512 } 6513 this->cleanup(); 6514 this->type_ = value_t::array; 6515 this->region_ = region_type{}; 6516 assigner(this->array_, array_storage( 6517 detail::storage<array_type>(std::move(x)), std::move(fmt))); 6518 return *this; 6519 } 6520 6521 private: 6522 6523 template<typename T> 6524 using enable_if_array_like_t = cxx::enable_if_t<cxx::conjunction< 6525 detail::is_container<T>, 6526 cxx::negation<std::is_same<T, array_type>>, 6527 cxx::negation<detail::is_std_basic_string<T>>, 6528 #if defined(TOML11_HAS_STRING_VIEW) 6529 cxx::negation<detail::is_std_basic_string_view<T>>, 6530 #endif 6531 cxx::negation<detail::has_from_toml_method<T, config_type>>, 6532 cxx::negation<detail::has_specialized_from<T>> 6533 >::value, std::nullptr_t>; 6534 6535 public: 6536 6537 template<typename T, enable_if_array_like_t<T> = nullptr> 6538 basic_value(T x) 6539 : basic_value(std::move(x), array_format_info{}, std::vector<std::string>{}, region_type{}) 6540 {} 6541 template<typename T, enable_if_array_like_t<T> = nullptr> 6542 basic_value(T x, array_format_info fmt) 6543 : basic_value(std::move(x), std::move(fmt), std::vector<std::string>{}, region_type{}) 6544 {} 6545 template<typename T, enable_if_array_like_t<T> = nullptr> 6546 basic_value(T x, std::vector<std::string> com) 6547 : basic_value(std::move(x), array_format_info{}, std::move(com), region_type{}) 6548 {} 6549 template<typename T, enable_if_array_like_t<T> = nullptr> 6550 basic_value(T x, array_format_info fmt, std::vector<std::string> com) 6551 : basic_value(std::move(x), fmt, std::move(com), region_type{}) 6552 {} 6553 template<typename T, enable_if_array_like_t<T> = nullptr> 6554 basic_value(T x, array_format_info fmt, 6555 std::vector<std::string> com, region_type reg) 6556 : type_(value_t::array), array_(array_storage( 6557 detail::storage<array_type>(array_type( 6558 std::make_move_iterator(x.begin()), 6559 std::make_move_iterator(x.end())) 6560 ), std::move(fmt) 6561 )), region_(std::move(reg)), comments_(std::move(com)) 6562 {} 6563 template<typename T, enable_if_array_like_t<T> = nullptr> 6564 basic_value& operator=(T x) 6565 { 6566 array_format_info fmt; 6567 if(this->is_array()) 6568 { 6569 fmt = this->as_array_fmt(); 6570 } 6571 this->cleanup(); 6572 this->type_ = value_t::array; 6573 this->region_ = region_type{}; 6574 6575 array_type a(std::make_move_iterator(x.begin()), 6576 std::make_move_iterator(x.end())); 6577 assigner(this->array_, array_storage( 6578 detail::storage<array_type>(std::move(a)), std::move(fmt))); 6579 return *this; 6580 } 6581 6582 // }}} 6583 6584 // constructor (table) ================================================ {{{ 6585 6586 basic_value(table_type x) 6587 : basic_value(std::move(x), table_format_info{}, std::vector<std::string>{}, region_type{}) 6588 {} 6589 basic_value(table_type x, table_format_info fmt) 6590 : basic_value(std::move(x), std::move(fmt), std::vector<std::string>{}, region_type{}) 6591 {} 6592 basic_value(table_type x, std::vector<std::string> com) 6593 : basic_value(std::move(x), table_format_info{}, std::move(com), region_type{}) 6594 {} 6595 basic_value(table_type x, table_format_info fmt, std::vector<std::string> com) 6596 : basic_value(std::move(x), fmt, std::move(com), region_type{}) 6597 {} 6598 basic_value(table_type x, table_format_info fmt, 6599 std::vector<std::string> com, region_type reg) 6600 : type_(value_t::table), table_(table_storage( 6601 detail::storage<table_type>(std::move(x)), std::move(fmt) 6602 )), region_(std::move(reg)), comments_(std::move(com)) 6603 {} 6604 basic_value& operator=(table_type x) 6605 { 6606 table_format_info fmt; 6607 if(this->is_table()) 6608 { 6609 fmt = this->as_table_fmt(); 6610 } 6611 this->cleanup(); 6612 this->type_ = value_t::table; 6613 this->region_ = region_type{}; 6614 assigner(this->table_, table_storage( 6615 detail::storage<table_type>(std::move(x)), std::move(fmt))); 6616 return *this; 6617 } 6618 6619 // table-like 6620 6621 private: 6622 6623 template<typename T> 6624 using enable_if_table_like_t = cxx::enable_if_t<cxx::conjunction< 6625 cxx::negation<std::is_same<T, table_type>>, 6626 detail::is_map<T>, 6627 cxx::negation<detail::has_from_toml_method<T, config_type>>, 6628 cxx::negation<detail::has_specialized_from<T>> 6629 >::value, std::nullptr_t>; 6630 6631 public: 6632 6633 template<typename T, enable_if_table_like_t<T> = nullptr> 6634 basic_value(T x) 6635 : basic_value(std::move(x), table_format_info{}, std::vector<std::string>{}, region_type{}) 6636 {} 6637 template<typename T, enable_if_table_like_t<T> = nullptr> 6638 basic_value(T x, table_format_info fmt) 6639 : basic_value(std::move(x), std::move(fmt), std::vector<std::string>{}, region_type{}) 6640 {} 6641 template<typename T, enable_if_table_like_t<T> = nullptr> 6642 basic_value(T x, std::vector<std::string> com) 6643 : basic_value(std::move(x), table_format_info{}, std::move(com), region_type{}) 6644 {} 6645 template<typename T, enable_if_table_like_t<T> = nullptr> 6646 basic_value(T x, table_format_info fmt, std::vector<std::string> com) 6647 : basic_value(std::move(x), fmt, std::move(com), region_type{}) 6648 {} 6649 template<typename T, enable_if_table_like_t<T> = nullptr> 6650 basic_value(T x, table_format_info fmt, 6651 std::vector<std::string> com, region_type reg) 6652 : type_(value_t::table), table_(table_storage( 6653 detail::storage<table_type>(table_type( 6654 std::make_move_iterator(x.begin()), 6655 std::make_move_iterator(x.end()) 6656 )), std::move(fmt) 6657 )), region_(std::move(reg)), comments_(std::move(com)) 6658 {} 6659 template<typename T, enable_if_table_like_t<T> = nullptr> 6660 basic_value& operator=(T x) 6661 { 6662 table_format_info fmt; 6663 if(this->is_table()) 6664 { 6665 fmt = this->as_table_fmt(); 6666 } 6667 this->cleanup(); 6668 this->type_ = value_t::table; 6669 this->region_ = region_type{}; 6670 6671 table_type t(std::make_move_iterator(x.begin()), 6672 std::make_move_iterator(x.end())); 6673 assigner(this->table_, table_storage( 6674 detail::storage<table_type>(std::move(t)), std::move(fmt))); 6675 return *this; 6676 } 6677 6678 // }}} 6679 6680 // constructor (user_defined) ========================================= {{{ 6681 6682 template<typename T, cxx::enable_if_t< 6683 detail::has_specialized_into<T>::value, std::nullptr_t> = nullptr> 6684 basic_value(const T& ud) 6685 : basic_value( 6686 into<cxx::remove_cvref_t<T>>::template into_toml<config_type>(ud)) 6687 {} 6688 template<typename T, cxx::enable_if_t< 6689 detail::has_specialized_into<T>::value, std::nullptr_t> = nullptr> 6690 basic_value(const T& ud, std::vector<std::string> com) 6691 : basic_value( 6692 into<cxx::remove_cvref_t<T>>::template into_toml<config_type>(ud), 6693 std::move(com)) 6694 {} 6695 template<typename T, cxx::enable_if_t< 6696 detail::has_specialized_into<T>::value, std::nullptr_t> = nullptr> 6697 basic_value& operator=(const T& ud) 6698 { 6699 *this = into<cxx::remove_cvref_t<T>>::template into_toml<config_type>(ud); 6700 return *this; 6701 } 6702 6703 template<typename T, cxx::enable_if_t<cxx::conjunction< 6704 detail::has_into_toml_method<T>, 6705 cxx::negation<detail::has_specialized_into<T>> 6706 >::value, std::nullptr_t> = nullptr> 6707 basic_value(const T& ud): basic_value(ud.into_toml()) {} 6708 6709 template<typename T, cxx::enable_if_t<cxx::conjunction< 6710 detail::has_into_toml_method<T>, 6711 cxx::negation<detail::has_specialized_into<T>> 6712 >::value, std::nullptr_t> = nullptr> 6713 basic_value(const T& ud, std::vector<std::string> com) 6714 : basic_value(ud.into_toml(), std::move(com)) 6715 {} 6716 template<typename T, cxx::enable_if_t<cxx::conjunction< 6717 detail::has_into_toml_method<T>, 6718 cxx::negation<detail::has_specialized_into<T>> 6719 >::value, std::nullptr_t> = nullptr> 6720 basic_value& operator=(const T& ud) 6721 { 6722 *this = ud.into_toml(); 6723 return *this; 6724 } 6725 6726 template<typename T, cxx::enable_if_t<cxx::conjunction< 6727 detail::has_template_into_toml_method<T, TypeConfig>, 6728 cxx::negation<detail::has_specialized_into<T>> 6729 >::value, std::nullptr_t> = nullptr> 6730 basic_value(const T& ud): basic_value(ud.template into_toml<TypeConfig>()) {} 6731 6732 template<typename T, cxx::enable_if_t<cxx::conjunction< 6733 detail::has_template_into_toml_method<T, TypeConfig>, 6734 cxx::negation<detail::has_specialized_into<T>> 6735 >::value, std::nullptr_t> = nullptr> 6736 basic_value(const T& ud, std::vector<std::string> com) 6737 : basic_value(ud.template into_toml<TypeConfig>(), std::move(com)) 6738 {} 6739 template<typename T, cxx::enable_if_t<cxx::conjunction< 6740 detail::has_template_into_toml_method<T, TypeConfig>, 6741 cxx::negation<detail::has_specialized_into<T>> 6742 >::value, std::nullptr_t> = nullptr> 6743 basic_value& operator=(const T& ud) 6744 { 6745 *this = ud.template into_toml<TypeConfig>(); 6746 return *this; 6747 } 6748 // }}} 6749 6750 // empty value with region info ======================================= {{{ 6751 6752 // mainly for `null` extension 6753 basic_value(detail::none_t, region_type reg) noexcept 6754 : type_(value_t::empty), empty_('\0'), region_(std::move(reg)), comments_{} 6755 {} 6756 6757 // }}} 6758 6759 // type checking ====================================================== {{{ 6760 6761 template<typename T, cxx::enable_if_t< 6762 detail::is_exact_toml_type<cxx::remove_cvref_t<T>, value_type>::value, 6763 std::nullptr_t> = nullptr> 6764 bool is() const noexcept 6765 { 6766 return detail::type_to_enum<T, value_type>::value == this->type_; 6767 } 6768 bool is(value_t t) const noexcept {return t == this->type_;} 6769 6770 bool is_empty() const noexcept {return this->is(value_t::empty );} 6771 bool is_boolean() const noexcept {return this->is(value_t::boolean );} 6772 bool is_integer() const noexcept {return this->is(value_t::integer );} 6773 bool is_floating() const noexcept {return this->is(value_t::floating );} 6774 bool is_string() const noexcept {return this->is(value_t::string );} 6775 bool is_offset_datetime() const noexcept {return this->is(value_t::offset_datetime);} 6776 bool is_local_datetime() const noexcept {return this->is(value_t::local_datetime );} 6777 bool is_local_date() const noexcept {return this->is(value_t::local_date );} 6778 bool is_local_time() const noexcept {return this->is(value_t::local_time );} 6779 bool is_array() const noexcept {return this->is(value_t::array );} 6780 bool is_table() const noexcept {return this->is(value_t::table );} 6781 6782 bool is_array_of_tables() const noexcept 6783 { 6784 if( ! this->is_array()) {return false;} 6785 const auto& a = this->as_array(std::nothrow); // already checked. 6786 6787 // when you define [[array.of.tables]], at least one empty table will be 6788 // assigned. In case of array of inline tables, `array_of_tables = []`, 6789 // there is no reason to consider this as an array of *tables*. 6790 // So empty array is not an array-of-tables. 6791 if(a.empty()) {return false;} 6792 6793 // since toml v1.0.0 allows array of heterogeneous types, we need to 6794 // check all the elements. if any of the elements is not a table, it 6795 // is a heterogeneous array and cannot be expressed by `[[aot]]` form. 6796 for(const auto& e : a) 6797 { 6798 if( ! e.is_table()) {return false;} 6799 } 6800 return true; 6801 } 6802 6803 value_t type() const noexcept {return type_;} 6804 6805 // }}} 6806 6807 // as_xxx (noexcept) version ========================================== {{{ 6808 6809 template<value_t T> 6810 detail::enum_to_type_t<T, basic_value<config_type>> const& 6811 as(const std::nothrow_t&) const noexcept 6812 { 6813 return detail::getter<config_type, T>::get_nothrow(*this); 6814 } 6815 template<value_t T> 6816 detail::enum_to_type_t<T, basic_value<config_type>>& 6817 as(const std::nothrow_t&) noexcept 6818 { 6819 return detail::getter<config_type, T>::get_nothrow(*this); 6820 } 6821 6822 boolean_type const& as_boolean (const std::nothrow_t&) const noexcept {return this->boolean_.value;} 6823 integer_type const& as_integer (const std::nothrow_t&) const noexcept {return this->integer_.value;} 6824 floating_type const& as_floating (const std::nothrow_t&) const noexcept {return this->floating_.value;} 6825 string_type const& as_string (const std::nothrow_t&) const noexcept {return this->string_.value;} 6826 offset_datetime_type const& as_offset_datetime(const std::nothrow_t&) const noexcept {return this->offset_datetime_.value;} 6827 local_datetime_type const& as_local_datetime (const std::nothrow_t&) const noexcept {return this->local_datetime_.value;} 6828 local_date_type const& as_local_date (const std::nothrow_t&) const noexcept {return this->local_date_.value;} 6829 local_time_type const& as_local_time (const std::nothrow_t&) const noexcept {return this->local_time_.value;} 6830 array_type const& as_array (const std::nothrow_t&) const noexcept {return this->array_.value.get();} 6831 table_type const& as_table (const std::nothrow_t&) const noexcept {return this->table_.value.get();} 6832 6833 boolean_type & as_boolean (const std::nothrow_t&) noexcept {return this->boolean_.value;} 6834 integer_type & as_integer (const std::nothrow_t&) noexcept {return this->integer_.value;} 6835 floating_type & as_floating (const std::nothrow_t&) noexcept {return this->floating_.value;} 6836 string_type & as_string (const std::nothrow_t&) noexcept {return this->string_.value;} 6837 offset_datetime_type& as_offset_datetime(const std::nothrow_t&) noexcept {return this->offset_datetime_.value;} 6838 local_datetime_type & as_local_datetime (const std::nothrow_t&) noexcept {return this->local_datetime_.value;} 6839 local_date_type & as_local_date (const std::nothrow_t&) noexcept {return this->local_date_.value;} 6840 local_time_type & as_local_time (const std::nothrow_t&) noexcept {return this->local_time_.value;} 6841 array_type & as_array (const std::nothrow_t&) noexcept {return this->array_.value.get();} 6842 table_type & as_table (const std::nothrow_t&) noexcept {return this->table_.value.get();} 6843 6844 // }}} 6845 6846 // as_xxx (throw) ===================================================== {{{ 6847 6848 template<value_t T> 6849 detail::enum_to_type_t<T, basic_value<config_type>> const& as() const 6850 { 6851 return detail::getter<config_type, T>::get(*this); 6852 } 6853 template<value_t T> 6854 detail::enum_to_type_t<T, basic_value<config_type>>& as() 6855 { 6856 return detail::getter<config_type, T>::get(*this); 6857 } 6858 6859 boolean_type const& as_boolean() const 6860 { 6861 if(this->type_ != value_t::boolean) 6862 { 6863 this->throw_bad_cast("toml::value::as_boolean()", value_t::boolean); 6864 } 6865 return this->boolean_.value; 6866 } 6867 integer_type const& as_integer() const 6868 { 6869 if(this->type_ != value_t::integer) 6870 { 6871 this->throw_bad_cast("toml::value::as_integer()", value_t::integer); 6872 } 6873 return this->integer_.value; 6874 } 6875 floating_type const& as_floating() const 6876 { 6877 if(this->type_ != value_t::floating) 6878 { 6879 this->throw_bad_cast("toml::value::as_floating()", value_t::floating); 6880 } 6881 return this->floating_.value; 6882 } 6883 string_type const& as_string() const 6884 { 6885 if(this->type_ != value_t::string) 6886 { 6887 this->throw_bad_cast("toml::value::as_string()", value_t::string); 6888 } 6889 return this->string_.value; 6890 } 6891 offset_datetime_type const& as_offset_datetime() const 6892 { 6893 if(this->type_ != value_t::offset_datetime) 6894 { 6895 this->throw_bad_cast("toml::value::as_offset_datetime()", value_t::offset_datetime); 6896 } 6897 return this->offset_datetime_.value; 6898 } 6899 local_datetime_type const& as_local_datetime() const 6900 { 6901 if(this->type_ != value_t::local_datetime) 6902 { 6903 this->throw_bad_cast("toml::value::as_local_datetime()", value_t::local_datetime); 6904 } 6905 return this->local_datetime_.value; 6906 } 6907 local_date_type const& as_local_date() const 6908 { 6909 if(this->type_ != value_t::local_date) 6910 { 6911 this->throw_bad_cast("toml::value::as_local_date()", value_t::local_date); 6912 } 6913 return this->local_date_.value; 6914 } 6915 local_time_type const& as_local_time() const 6916 { 6917 if(this->type_ != value_t::local_time) 6918 { 6919 this->throw_bad_cast("toml::value::as_local_time()", value_t::local_time); 6920 } 6921 return this->local_time_.value; 6922 } 6923 array_type const& as_array() const 6924 { 6925 if(this->type_ != value_t::array) 6926 { 6927 this->throw_bad_cast("toml::value::as_array()", value_t::array); 6928 } 6929 return this->array_.value.get(); 6930 } 6931 table_type const& as_table() const 6932 { 6933 if(this->type_ != value_t::table) 6934 { 6935 this->throw_bad_cast("toml::value::as_table()", value_t::table); 6936 } 6937 return this->table_.value.get(); 6938 } 6939 6940 // ------------------------------------------------------------------------ 6941 // nonconst reference 6942 6943 boolean_type& as_boolean() 6944 { 6945 if(this->type_ != value_t::boolean) 6946 { 6947 this->throw_bad_cast("toml::value::as_boolean()", value_t::boolean); 6948 } 6949 return this->boolean_.value; 6950 } 6951 integer_type& as_integer() 6952 { 6953 if(this->type_ != value_t::integer) 6954 { 6955 this->throw_bad_cast("toml::value::as_integer()", value_t::integer); 6956 } 6957 return this->integer_.value; 6958 } 6959 floating_type& as_floating() 6960 { 6961 if(this->type_ != value_t::floating) 6962 { 6963 this->throw_bad_cast("toml::value::as_floating()", value_t::floating); 6964 } 6965 return this->floating_.value; 6966 } 6967 string_type& as_string() 6968 { 6969 if(this->type_ != value_t::string) 6970 { 6971 this->throw_bad_cast("toml::value::as_string()", value_t::string); 6972 } 6973 return this->string_.value; 6974 } 6975 offset_datetime_type& as_offset_datetime() 6976 { 6977 if(this->type_ != value_t::offset_datetime) 6978 { 6979 this->throw_bad_cast("toml::value::as_offset_datetime()", value_t::offset_datetime); 6980 } 6981 return this->offset_datetime_.value; 6982 } 6983 local_datetime_type& as_local_datetime() 6984 { 6985 if(this->type_ != value_t::local_datetime) 6986 { 6987 this->throw_bad_cast("toml::value::as_local_datetime()", value_t::local_datetime); 6988 } 6989 return this->local_datetime_.value; 6990 } 6991 local_date_type& as_local_date() 6992 { 6993 if(this->type_ != value_t::local_date) 6994 { 6995 this->throw_bad_cast("toml::value::as_local_date()", value_t::local_date); 6996 } 6997 return this->local_date_.value; 6998 } 6999 local_time_type& as_local_time() 7000 { 7001 if(this->type_ != value_t::local_time) 7002 { 7003 this->throw_bad_cast("toml::value::as_local_time()", value_t::local_time); 7004 } 7005 return this->local_time_.value; 7006 } 7007 array_type& as_array() 7008 { 7009 if(this->type_ != value_t::array) 7010 { 7011 this->throw_bad_cast("toml::value::as_array()", value_t::array); 7012 } 7013 return this->array_.value.get(); 7014 } 7015 table_type& as_table() 7016 { 7017 if(this->type_ != value_t::table) 7018 { 7019 this->throw_bad_cast("toml::value::as_table()", value_t::table); 7020 } 7021 return this->table_.value.get(); 7022 } 7023 7024 // }}} 7025 7026 // format accessors (noexcept) ======================================== {{{ 7027 7028 template<value_t T> 7029 detail::enum_to_fmt_type_t<T> const& 7030 as_fmt(const std::nothrow_t&) const noexcept 7031 { 7032 return detail::getter<config_type, T>::get_fmt_nothrow(*this); 7033 } 7034 template<value_t T> 7035 detail::enum_to_fmt_type_t<T>& 7036 as_fmt(const std::nothrow_t&) noexcept 7037 { 7038 return detail::getter<config_type, T>::get_fmt_nothrow(*this); 7039 } 7040 7041 boolean_format_info & as_boolean_fmt (const std::nothrow_t&) noexcept {return this->boolean_.format;} 7042 integer_format_info & as_integer_fmt (const std::nothrow_t&) noexcept {return this->integer_.format;} 7043 floating_format_info & as_floating_fmt (const std::nothrow_t&) noexcept {return this->floating_.format;} 7044 string_format_info & as_string_fmt (const std::nothrow_t&) noexcept {return this->string_.format;} 7045 offset_datetime_format_info& as_offset_datetime_fmt(const std::nothrow_t&) noexcept {return this->offset_datetime_.format;} 7046 local_datetime_format_info & as_local_datetime_fmt (const std::nothrow_t&) noexcept {return this->local_datetime_.format;} 7047 local_date_format_info & as_local_date_fmt (const std::nothrow_t&) noexcept {return this->local_date_.format;} 7048 local_time_format_info & as_local_time_fmt (const std::nothrow_t&) noexcept {return this->local_time_.format;} 7049 array_format_info & as_array_fmt (const std::nothrow_t&) noexcept {return this->array_.format;} 7050 table_format_info & as_table_fmt (const std::nothrow_t&) noexcept {return this->table_.format;} 7051 7052 boolean_format_info const& as_boolean_fmt (const std::nothrow_t&) const noexcept {return this->boolean_.format;} 7053 integer_format_info const& as_integer_fmt (const std::nothrow_t&) const noexcept {return this->integer_.format;} 7054 floating_format_info const& as_floating_fmt (const std::nothrow_t&) const noexcept {return this->floating_.format;} 7055 string_format_info const& as_string_fmt (const std::nothrow_t&) const noexcept {return this->string_.format;} 7056 offset_datetime_format_info const& as_offset_datetime_fmt(const std::nothrow_t&) const noexcept {return this->offset_datetime_.format;} 7057 local_datetime_format_info const& as_local_datetime_fmt (const std::nothrow_t&) const noexcept {return this->local_datetime_.format;} 7058 local_date_format_info const& as_local_date_fmt (const std::nothrow_t&) const noexcept {return this->local_date_.format;} 7059 local_time_format_info const& as_local_time_fmt (const std::nothrow_t&) const noexcept {return this->local_time_.format;} 7060 array_format_info const& as_array_fmt (const std::nothrow_t&) const noexcept {return this->array_.format;} 7061 table_format_info const& as_table_fmt (const std::nothrow_t&) const noexcept {return this->table_.format;} 7062 7063 // }}} 7064 7065 // format accessors (throw) =========================================== {{{ 7066 7067 template<value_t T> 7068 detail::enum_to_fmt_type_t<T> const& as_fmt() const 7069 { 7070 return detail::getter<config_type, T>::get_fmt(*this); 7071 } 7072 template<value_t T> 7073 detail::enum_to_fmt_type_t<T>& as_fmt() 7074 { 7075 return detail::getter<config_type, T>::get_fmt(*this); 7076 } 7077 7078 boolean_format_info const& as_boolean_fmt() const 7079 { 7080 if(this->type_ != value_t::boolean) 7081 { 7082 this->throw_bad_cast("toml::value::as_boolean_fmt()", value_t::boolean); 7083 } 7084 return this->boolean_.format; 7085 } 7086 integer_format_info const& as_integer_fmt() const 7087 { 7088 if(this->type_ != value_t::integer) 7089 { 7090 this->throw_bad_cast("toml::value::as_integer_fmt()", value_t::integer); 7091 } 7092 return this->integer_.format; 7093 } 7094 floating_format_info const& as_floating_fmt() const 7095 { 7096 if(this->type_ != value_t::floating) 7097 { 7098 this->throw_bad_cast("toml::value::as_floating_fmt()", value_t::floating); 7099 } 7100 return this->floating_.format; 7101 } 7102 string_format_info const& as_string_fmt() const 7103 { 7104 if(this->type_ != value_t::string) 7105 { 7106 this->throw_bad_cast("toml::value::as_string_fmt()", value_t::string); 7107 } 7108 return this->string_.format; 7109 } 7110 offset_datetime_format_info const& as_offset_datetime_fmt() const 7111 { 7112 if(this->type_ != value_t::offset_datetime) 7113 { 7114 this->throw_bad_cast("toml::value::as_offset_datetime_fmt()", value_t::offset_datetime); 7115 } 7116 return this->offset_datetime_.format; 7117 } 7118 local_datetime_format_info const& as_local_datetime_fmt() const 7119 { 7120 if(this->type_ != value_t::local_datetime) 7121 { 7122 this->throw_bad_cast("toml::value::as_local_datetime_fmt()", value_t::local_datetime); 7123 } 7124 return this->local_datetime_.format; 7125 } 7126 local_date_format_info const& as_local_date_fmt() const 7127 { 7128 if(this->type_ != value_t::local_date) 7129 { 7130 this->throw_bad_cast("toml::value::as_local_date_fmt()", value_t::local_date); 7131 } 7132 return this->local_date_.format; 7133 } 7134 local_time_format_info const& as_local_time_fmt() const 7135 { 7136 if(this->type_ != value_t::local_time) 7137 { 7138 this->throw_bad_cast("toml::value::as_local_time_fmt()", value_t::local_time); 7139 } 7140 return this->local_time_.format; 7141 } 7142 array_format_info const& as_array_fmt() const 7143 { 7144 if(this->type_ != value_t::array) 7145 { 7146 this->throw_bad_cast("toml::value::as_array_fmt()", value_t::array); 7147 } 7148 return this->array_.format; 7149 } 7150 table_format_info const& as_table_fmt() const 7151 { 7152 if(this->type_ != value_t::table) 7153 { 7154 this->throw_bad_cast("toml::value::as_table_fmt()", value_t::table); 7155 } 7156 return this->table_.format; 7157 } 7158 7159 // ------------------------------------------------------------------------ 7160 // nonconst reference 7161 7162 boolean_format_info& as_boolean_fmt() 7163 { 7164 if(this->type_ != value_t::boolean) 7165 { 7166 this->throw_bad_cast("toml::value::as_boolean_fmt()", value_t::boolean); 7167 } 7168 return this->boolean_.format; 7169 } 7170 integer_format_info& as_integer_fmt() 7171 { 7172 if(this->type_ != value_t::integer) 7173 { 7174 this->throw_bad_cast("toml::value::as_integer_fmt()", value_t::integer); 7175 } 7176 return this->integer_.format; 7177 } 7178 floating_format_info& as_floating_fmt() 7179 { 7180 if(this->type_ != value_t::floating) 7181 { 7182 this->throw_bad_cast("toml::value::as_floating_fmt()", value_t::floating); 7183 } 7184 return this->floating_.format; 7185 } 7186 string_format_info& as_string_fmt() 7187 { 7188 if(this->type_ != value_t::string) 7189 { 7190 this->throw_bad_cast("toml::value::as_string_fmt()", value_t::string); 7191 } 7192 return this->string_.format; 7193 } 7194 offset_datetime_format_info& as_offset_datetime_fmt() 7195 { 7196 if(this->type_ != value_t::offset_datetime) 7197 { 7198 this->throw_bad_cast("toml::value::as_offset_datetime_fmt()", value_t::offset_datetime); 7199 } 7200 return this->offset_datetime_.format; 7201 } 7202 local_datetime_format_info& as_local_datetime_fmt() 7203 { 7204 if(this->type_ != value_t::local_datetime) 7205 { 7206 this->throw_bad_cast("toml::value::as_local_datetime_fmt()", value_t::local_datetime); 7207 } 7208 return this->local_datetime_.format; 7209 } 7210 local_date_format_info& as_local_date_fmt() 7211 { 7212 if(this->type_ != value_t::local_date) 7213 { 7214 this->throw_bad_cast("toml::value::as_local_date_fmt()", value_t::local_date); 7215 } 7216 return this->local_date_.format; 7217 } 7218 local_time_format_info& as_local_time_fmt() 7219 { 7220 if(this->type_ != value_t::local_time) 7221 { 7222 this->throw_bad_cast("toml::value::as_local_time_fmt()", value_t::local_time); 7223 } 7224 return this->local_time_.format; 7225 } 7226 array_format_info& as_array_fmt() 7227 { 7228 if(this->type_ != value_t::array) 7229 { 7230 this->throw_bad_cast("toml::value::as_array_fmt()", value_t::array); 7231 } 7232 return this->array_.format; 7233 } 7234 table_format_info& as_table_fmt() 7235 { 7236 if(this->type_ != value_t::table) 7237 { 7238 this->throw_bad_cast("toml::value::as_table_fmt()", value_t::table); 7239 } 7240 return this->table_.format; 7241 } 7242 // }}} 7243 7244 // table accessors ==================================================== {{{ 7245 7246 value_type& at(const key_type& k) 7247 { 7248 if(!this->is_table()) 7249 { 7250 this->throw_bad_cast("toml::value::at(key_type)", value_t::table); 7251 } 7252 auto& table = this->as_table(std::nothrow); 7253 const auto found = table.find(k); 7254 if(found == table.end()) 7255 { 7256 this->throw_key_not_found_error("toml::value::at", k); 7257 } 7258 assert(found->first == k); 7259 return found->second; 7260 } 7261 value_type const& at(const key_type& k) const 7262 { 7263 if(!this->is_table()) 7264 { 7265 this->throw_bad_cast("toml::value::at(key_type)", value_t::table); 7266 } 7267 const auto& table = this->as_table(std::nothrow); 7268 const auto found = table.find(k); 7269 if(found == table.end()) 7270 { 7271 this->throw_key_not_found_error("toml::value::at", k); 7272 } 7273 assert(found->first == k); 7274 return found->second; 7275 } 7276 value_type& operator[](const key_type& k) 7277 { 7278 if(this->is_empty()) 7279 { 7280 (*this) = table_type{}; 7281 } 7282 else if( ! this->is_table()) // initialized, but not a table 7283 { 7284 this->throw_bad_cast("toml::value::operator[](key_type)", value_t::table); 7285 } 7286 return (this->as_table(std::nothrow))[k]; 7287 } 7288 std::size_t count(const key_type& k) const 7289 { 7290 if(!this->is_table()) 7291 { 7292 this->throw_bad_cast("toml::value::count(key_type)", value_t::table); 7293 } 7294 return this->as_table(std::nothrow).count(k); 7295 } 7296 bool contains(const key_type& k) const 7297 { 7298 if(!this->is_table()) 7299 { 7300 this->throw_bad_cast("toml::value::contains(key_type)", value_t::table); 7301 } 7302 const auto& table = this->as_table(std::nothrow); 7303 return table.find(k) != table.end(); 7304 } 7305 // }}} 7306 7307 // array accessors ==================================================== {{{ 7308 7309 value_type& at(const std::size_t idx) 7310 { 7311 if(!this->is_array()) 7312 { 7313 this->throw_bad_cast("toml::value::at(idx)", value_t::array); 7314 } 7315 auto& ar = this->as_array(std::nothrow); 7316 7317 if(ar.size() <= idx) 7318 { 7319 std::ostringstream oss; 7320 oss << "actual length (" << ar.size() 7321 << ") is shorter than the specified index (" << idx << ")."; 7322 throw std::out_of_range(format_error( 7323 "toml::value::at(idx): no element corresponding to the index", 7324 this->location(), oss.str() 7325 )); 7326 } 7327 return ar.at(idx); 7328 } 7329 value_type const& at(const std::size_t idx) const 7330 { 7331 if(!this->is_array()) 7332 { 7333 this->throw_bad_cast("toml::value::at(idx)", value_t::array); 7334 } 7335 const auto& ar = this->as_array(std::nothrow); 7336 7337 if(ar.size() <= idx) 7338 { 7339 std::ostringstream oss; 7340 oss << "actual length (" << ar.size() 7341 << ") is shorter than the specified index (" << idx << ")."; 7342 7343 throw std::out_of_range(format_error( 7344 "toml::value::at(idx): no element corresponding to the index", 7345 this->location(), oss.str() 7346 )); 7347 } 7348 return ar.at(idx); 7349 } 7350 7351 value_type& operator[](const std::size_t idx) noexcept 7352 { 7353 // no check... 7354 return this->as_array(std::nothrow)[idx]; 7355 } 7356 value_type const& operator[](const std::size_t idx) const noexcept 7357 { 7358 // no check... 7359 return this->as_array(std::nothrow)[idx]; 7360 } 7361 7362 void push_back(const value_type& x) 7363 { 7364 if(!this->is_array()) 7365 { 7366 this->throw_bad_cast("toml::value::push_back(idx)", value_t::array); 7367 } 7368 this->as_array(std::nothrow).push_back(x); 7369 return; 7370 } 7371 void push_back(value_type&& x) 7372 { 7373 if(!this->is_array()) 7374 { 7375 this->throw_bad_cast("toml::value::push_back(idx)", value_t::array); 7376 } 7377 this->as_array(std::nothrow).push_back(std::move(x)); 7378 return; 7379 } 7380 7381 template<typename ... Ts> 7382 value_type& emplace_back(Ts&& ... args) 7383 { 7384 if(!this->is_array()) 7385 { 7386 this->throw_bad_cast("toml::value::emplace_back(idx)", value_t::array); 7387 } 7388 auto& ar = this->as_array(std::nothrow); 7389 ar.emplace_back(std::forward<Ts>(args) ...); 7390 return ar.back(); 7391 } 7392 7393 std::size_t size() const 7394 { 7395 switch(this->type_) 7396 { 7397 case value_t::array: 7398 { 7399 return this->as_array(std::nothrow).size(); 7400 } 7401 case value_t::table: 7402 { 7403 return this->as_table(std::nothrow).size(); 7404 } 7405 case value_t::string: 7406 { 7407 return this->as_string(std::nothrow).size(); 7408 } 7409 default: 7410 { 7411 throw type_error(format_error( 7412 "toml::value::size(): bad_cast to container types", 7413 this->location(), 7414 "the actual type is " + to_string(this->type_) 7415 ), this->location()); 7416 } 7417 } 7418 } 7419 7420 // }}} 7421 7422 source_location location() const 7423 { 7424 return source_location(this->region_); 7425 } 7426 7427 comment_type const& comments() const noexcept {return this->comments_;} 7428 comment_type& comments() noexcept {return this->comments_;} 7429 7430 private: 7431 7432 // private helper functions =========================================== {{{ 7433 7434 void cleanup() noexcept 7435 { 7436 switch(this->type_) 7437 { 7438 case value_t::boolean : { boolean_ .~boolean_storage (); break; } 7439 case value_t::integer : { integer_ .~integer_storage (); break; } 7440 case value_t::floating : { floating_ .~floating_storage (); break; } 7441 case value_t::string : { string_ .~string_storage (); break; } 7442 case value_t::offset_datetime : { offset_datetime_.~offset_datetime_storage (); break; } 7443 case value_t::local_datetime : { local_datetime_ .~local_datetime_storage (); break; } 7444 case value_t::local_date : { local_date_ .~local_date_storage (); break; } 7445 case value_t::local_time : { local_time_ .~local_time_storage (); break; } 7446 case value_t::array : { array_ .~array_storage (); break; } 7447 case value_t::table : { table_ .~table_storage (); break; } 7448 default : { break; } 7449 } 7450 this->type_ = value_t::empty; 7451 return; 7452 } 7453 7454 template<typename T, typename U> 7455 static void assigner(T& dst, U&& v) 7456 { 7457 const auto tmp = ::new(std::addressof(dst)) T(std::forward<U>(v)); 7458 assert(tmp == std::addressof(dst)); 7459 (void)tmp; 7460 } 7461 7462 [[noreturn]] 7463 void throw_bad_cast(const std::string& funcname, const value_t ty) const 7464 { 7465 throw type_error(format_error(detail::make_type_error(*this, funcname, ty)), 7466 this->location()); 7467 } 7468 7469 [[noreturn]] 7470 void throw_key_not_found_error(const std::string& funcname, const key_type& key) const 7471 { 7472 throw std::out_of_range(format_error( 7473 detail::make_not_found_error(*this, funcname, key))); 7474 } 7475 7476 template<typename TC> 7477 friend void detail::change_region_of_value(basic_value<TC>&, const basic_value<TC>&); 7478 7479 template<typename TC> 7480 friend class basic_value; 7481 7482 // }}} 7483 7484 private: 7485 7486 using boolean_storage = detail::value_with_format<boolean_type, boolean_format_info >; 7487 using integer_storage = detail::value_with_format<integer_type, integer_format_info >; 7488 using floating_storage = detail::value_with_format<floating_type, floating_format_info >; 7489 using string_storage = detail::value_with_format<string_type, string_format_info >; 7490 using offset_datetime_storage = detail::value_with_format<offset_datetime_type, offset_datetime_format_info>; 7491 using local_datetime_storage = detail::value_with_format<local_datetime_type, local_datetime_format_info >; 7492 using local_date_storage = detail::value_with_format<local_date_type, local_date_format_info >; 7493 using local_time_storage = detail::value_with_format<local_time_type, local_time_format_info >; 7494 using array_storage = detail::value_with_format<detail::storage<array_type>, array_format_info >; 7495 using table_storage = detail::value_with_format<detail::storage<table_type>, table_format_info >; 7496 7497 private: 7498 7499 value_t type_; 7500 union 7501 { 7502 char empty_; // the smallest type 7503 boolean_storage boolean_; 7504 integer_storage integer_; 7505 floating_storage floating_; 7506 string_storage string_; 7507 offset_datetime_storage offset_datetime_; 7508 local_datetime_storage local_datetime_; 7509 local_date_storage local_date_; 7510 local_time_storage local_time_; 7511 array_storage array_; 7512 table_storage table_; 7513 }; 7514 region_type region_; 7515 comment_type comments_; 7516 }; 7517 7518 template<typename TC> 7519 bool operator==(const basic_value<TC>& lhs, const basic_value<TC>& rhs) 7520 { 7521 if(lhs.type() != rhs.type()) {return false;} 7522 if(lhs.comments() != rhs.comments()) {return false;} 7523 7524 switch(lhs.type()) 7525 { 7526 case value_t::boolean : 7527 { 7528 return lhs.as_boolean() == rhs.as_boolean(); 7529 } 7530 case value_t::integer : 7531 { 7532 return lhs.as_integer() == rhs.as_integer(); 7533 } 7534 case value_t::floating : 7535 { 7536 return lhs.as_floating() == rhs.as_floating(); 7537 } 7538 case value_t::string : 7539 { 7540 return lhs.as_string() == rhs.as_string(); 7541 } 7542 case value_t::offset_datetime: 7543 { 7544 return lhs.as_offset_datetime() == rhs.as_offset_datetime(); 7545 } 7546 case value_t::local_datetime: 7547 { 7548 return lhs.as_local_datetime() == rhs.as_local_datetime(); 7549 } 7550 case value_t::local_date: 7551 { 7552 return lhs.as_local_date() == rhs.as_local_date(); 7553 } 7554 case value_t::local_time: 7555 { 7556 return lhs.as_local_time() == rhs.as_local_time(); 7557 } 7558 case value_t::array : 7559 { 7560 return lhs.as_array() == rhs.as_array(); 7561 } 7562 case value_t::table : 7563 { 7564 return lhs.as_table() == rhs.as_table(); 7565 } 7566 case value_t::empty : {return true; } 7567 default: {return false;} 7568 } 7569 } 7570 7571 template<typename TC> 7572 bool operator!=(const basic_value<TC>& lhs, const basic_value<TC>& rhs) 7573 { 7574 return !(lhs == rhs); 7575 } 7576 7577 template<typename TC> 7578 cxx::enable_if_t<cxx::conjunction< 7579 detail::is_comparable<typename basic_value<TC>::array_type>, 7580 detail::is_comparable<typename basic_value<TC>::table_type> 7581 >::value, bool> 7582 operator<(const basic_value<TC>& lhs, const basic_value<TC>& rhs) 7583 { 7584 if(lhs.type() != rhs.type()) 7585 { 7586 return (lhs.type() < rhs.type()); 7587 } 7588 switch(lhs.type()) 7589 { 7590 case value_t::boolean : 7591 { 7592 return lhs.as_boolean() < rhs.as_boolean() || 7593 (lhs.as_boolean() == rhs.as_boolean() && 7594 lhs.comments() < rhs.comments()); 7595 } 7596 case value_t::integer : 7597 { 7598 return lhs.as_integer() < rhs.as_integer() || 7599 (lhs.as_integer() == rhs.as_integer() && 7600 lhs.comments() < rhs.comments()); 7601 } 7602 case value_t::floating : 7603 { 7604 return lhs.as_floating() < rhs.as_floating() || 7605 (lhs.as_floating() == rhs.as_floating() && 7606 lhs.comments() < rhs.comments()); 7607 } 7608 case value_t::string : 7609 { 7610 return lhs.as_string() < rhs.as_string() || 7611 (lhs.as_string() == rhs.as_string() && 7612 lhs.comments() < rhs.comments()); 7613 } 7614 case value_t::offset_datetime: 7615 { 7616 return lhs.as_offset_datetime() < rhs.as_offset_datetime() || 7617 (lhs.as_offset_datetime() == rhs.as_offset_datetime() && 7618 lhs.comments() < rhs.comments()); 7619 } 7620 case value_t::local_datetime: 7621 { 7622 return lhs.as_local_datetime() < rhs.as_local_datetime() || 7623 (lhs.as_local_datetime() == rhs.as_local_datetime() && 7624 lhs.comments() < rhs.comments()); 7625 } 7626 case value_t::local_date: 7627 { 7628 return lhs.as_local_date() < rhs.as_local_date() || 7629 (lhs.as_local_date() == rhs.as_local_date() && 7630 lhs.comments() < rhs.comments()); 7631 } 7632 case value_t::local_time: 7633 { 7634 return lhs.as_local_time() < rhs.as_local_time() || 7635 (lhs.as_local_time() == rhs.as_local_time() && 7636 lhs.comments() < rhs.comments()); 7637 } 7638 case value_t::array : 7639 { 7640 return lhs.as_array() < rhs.as_array() || 7641 (lhs.as_array() == rhs.as_array() && 7642 lhs.comments() < rhs.comments()); 7643 } 7644 case value_t::table : 7645 { 7646 return lhs.as_table() < rhs.as_table() || 7647 (lhs.as_table() == rhs.as_table() && 7648 lhs.comments() < rhs.comments()); 7649 } 7650 case value_t::empty : 7651 { 7652 return lhs.comments() < rhs.comments(); 7653 } 7654 default: 7655 { 7656 return lhs.comments() < rhs.comments(); 7657 } 7658 } 7659 } 7660 7661 template<typename TC> 7662 cxx::enable_if_t<cxx::conjunction< 7663 detail::is_comparable<typename basic_value<TC>::array_type>, 7664 detail::is_comparable<typename basic_value<TC>::table_type> 7665 >::value, bool> 7666 operator<=(const basic_value<TC>& lhs, const basic_value<TC>& rhs) 7667 { 7668 return (lhs < rhs) || (lhs == rhs); 7669 } 7670 template<typename TC> 7671 cxx::enable_if_t<cxx::conjunction< 7672 detail::is_comparable<typename basic_value<TC>::array_type>, 7673 detail::is_comparable<typename basic_value<TC>::table_type> 7674 >::value, bool> 7675 operator>(const basic_value<TC>& lhs, const basic_value<TC>& rhs) 7676 { 7677 return !(lhs <= rhs); 7678 } 7679 template<typename TC> 7680 cxx::enable_if_t<cxx::conjunction< 7681 detail::is_comparable<typename basic_value<TC>::array_type>, 7682 detail::is_comparable<typename basic_value<TC>::table_type> 7683 >::value, bool> 7684 operator>=(const basic_value<TC>& lhs, const basic_value<TC>& rhs) 7685 { 7686 return !(lhs < rhs); 7687 } 7688 7689 // error_info helper 7690 namespace detail 7691 { 7692 template<typename TC, typename ... Ts> 7693 error_info make_error_info_rec(error_info e, 7694 const basic_value<TC>& v, std::string msg, Ts&& ... tail) 7695 { 7696 return make_error_info_rec(std::move(e), v.location(), std::move(msg), std::forward<Ts>(tail)...); 7697 } 7698 } // detail 7699 7700 template<typename TC, typename ... Ts> 7701 error_info make_error_info( 7702 std::string title, const basic_value<TC>& v, std::string msg, Ts&& ... tail) 7703 { 7704 return make_error_info(std::move(title), 7705 v.location(), std::move(msg), std::forward<Ts>(tail)...); 7706 } 7707 template<typename TC, typename ... Ts> 7708 std::string format_error(std::string title, 7709 const basic_value<TC>& v, std::string msg, Ts&& ... tail) 7710 { 7711 return format_error(std::move(title), 7712 v.location(), std::move(msg), std::forward<Ts>(tail)...); 7713 } 7714 7715 namespace detail 7716 { 7717 7718 template<typename TC> 7719 error_info make_type_error(const basic_value<TC>& v, const std::string& fname, const value_t ty) 7720 { 7721 return make_error_info(fname + ": bad_cast to " + to_string(ty), 7722 v.location(), "the actual type is " + to_string(v.type())); 7723 } 7724 template<typename TC> 7725 error_info make_not_found_error(const basic_value<TC>& v, const std::string& fname, const typename basic_value<TC>::key_type& key) 7726 { 7727 const auto loc = v.location(); 7728 const std::string title = fname + ": key \"" + string_conv<std::string>(key) + "\" not found"; 7729 7730 std::vector<std::pair<source_location, std::string>> locs; 7731 if( ! loc.is_ok()) 7732 { 7733 return error_info(title, locs); 7734 } 7735 7736 if(loc.first_line_number() == 1 && loc.first_column_number() == 1 && loc.length() == 1) 7737 { 7738 // The top-level table has its region at the 0th character of the file. 7739 // That means that, in the case when a key is not found in the top-level 7740 // table, the error message points to the first character. If the file has 7741 // the first table at the first line, the error message would be like this. 7742 // ```console 7743 // [error] key "a" not found 7744 // --> example.toml 7745 // | 7746 // 1 | [table] 7747 // | ^------ in this table 7748 // ``` 7749 // It actually points to the top-level table at the first character, not 7750 // `[table]`. But it is too confusing. To avoid the confusion, the error 7751 // message should explicitly say "key not found in the top-level table". 7752 locs.emplace_back(v.location(), "at the top-level table"); 7753 } 7754 else 7755 { 7756 locs.emplace_back(v.location(), "in this table"); 7757 } 7758 return error_info(title, locs); 7759 } 7760 7761 #define TOML11_DETAIL_GENERATE_COMPTIME_GETTER(ty) \ 7762 template<typename TC> \ 7763 struct getter<TC, value_t::ty> \ 7764 { \ 7765 using value_type = basic_value<TC>; \ 7766 using result_type = enum_to_type_t<value_t::ty, value_type>; \ 7767 using format_type = enum_to_fmt_type_t<value_t::ty>; \ 7768 \ 7769 static result_type& get(value_type& v) \ 7770 { \ 7771 return v.as_ ## ty(); \ 7772 } \ 7773 static result_type const& get(const value_type& v) \ 7774 { \ 7775 return v.as_ ## ty(); \ 7776 } \ 7777 \ 7778 static result_type& get_nothrow(value_type& v) noexcept \ 7779 { \ 7780 return v.as_ ## ty(std::nothrow); \ 7781 } \ 7782 static result_type const& get_nothrow(const value_type& v) noexcept \ 7783 { \ 7784 return v.as_ ## ty(std::nothrow); \ 7785 } \ 7786 \ 7787 static format_type& get_fmt(value_type& v) \ 7788 { \ 7789 return v.as_ ## ty ## _fmt(); \ 7790 } \ 7791 static format_type const& get_fmt(const value_type& v) \ 7792 { \ 7793 return v.as_ ## ty ## _fmt(); \ 7794 } \ 7795 \ 7796 static format_type& get_fmt_nothrow(value_type& v) noexcept \ 7797 { \ 7798 return v.as_ ## ty ## _fmt(std::nothrow); \ 7799 } \ 7800 static format_type const& get_fmt_nothrow(const value_type& v) noexcept \ 7801 { \ 7802 return v.as_ ## ty ## _fmt(std::nothrow); \ 7803 } \ 7804 }; 7805 7806 TOML11_DETAIL_GENERATE_COMPTIME_GETTER(boolean ) 7807 TOML11_DETAIL_GENERATE_COMPTIME_GETTER(integer ) 7808 TOML11_DETAIL_GENERATE_COMPTIME_GETTER(floating ) 7809 TOML11_DETAIL_GENERATE_COMPTIME_GETTER(string ) 7810 TOML11_DETAIL_GENERATE_COMPTIME_GETTER(offset_datetime) 7811 TOML11_DETAIL_GENERATE_COMPTIME_GETTER(local_datetime ) 7812 TOML11_DETAIL_GENERATE_COMPTIME_GETTER(local_date ) 7813 TOML11_DETAIL_GENERATE_COMPTIME_GETTER(local_time ) 7814 TOML11_DETAIL_GENERATE_COMPTIME_GETTER(array ) 7815 TOML11_DETAIL_GENERATE_COMPTIME_GETTER(table ) 7816 7817 #undef TOML11_DETAIL_GENERATE_COMPTIME_GETTER 7818 7819 template<typename TC> 7820 void change_region_of_value(basic_value<TC>& dst, const basic_value<TC>& src) 7821 { 7822 dst.region_ = std::move(src.region_); 7823 return; 7824 } 7825 7826 } // namespace detail 7827 } // namespace toml 7828 #endif // TOML11_VALUE_HPP 7829 #ifndef TOML11_VISIT_HPP 7830 #define TOML11_VISIT_HPP 7831 7832 7833 namespace toml 7834 { 7835 7836 template<typename Visitor, typename TC> 7837 cxx::return_type_of_t<Visitor, const typename basic_value<TC>::boolean_type&> 7838 visit(Visitor&& visitor, const basic_value<TC>& v) 7839 { 7840 switch(v.type()) 7841 { 7842 case value_t::boolean : {return visitor(v.as_boolean ());} 7843 case value_t::integer : {return visitor(v.as_integer ());} 7844 case value_t::floating : {return visitor(v.as_floating ());} 7845 case value_t::string : {return visitor(v.as_string ());} 7846 case value_t::offset_datetime: {return visitor(v.as_offset_datetime());} 7847 case value_t::local_datetime : {return visitor(v.as_local_datetime ());} 7848 case value_t::local_date : {return visitor(v.as_local_date ());} 7849 case value_t::local_time : {return visitor(v.as_local_time ());} 7850 case value_t::array : {return visitor(v.as_array ());} 7851 case value_t::table : {return visitor(v.as_table ());} 7852 case value_t::empty : break; 7853 default: break; 7854 } 7855 throw type_error(format_error("[error] toml::visit: toml::basic_value " 7856 "does not have any valid type.", v.location(), "here"), v.location()); 7857 } 7858 7859 template<typename Visitor, typename TC> 7860 cxx::return_type_of_t<Visitor, typename basic_value<TC>::boolean_type&> 7861 visit(Visitor&& visitor, basic_value<TC>& v) 7862 { 7863 switch(v.type()) 7864 { 7865 case value_t::boolean : {return visitor(v.as_boolean ());} 7866 case value_t::integer : {return visitor(v.as_integer ());} 7867 case value_t::floating : {return visitor(v.as_floating ());} 7868 case value_t::string : {return visitor(v.as_string ());} 7869 case value_t::offset_datetime: {return visitor(v.as_offset_datetime());} 7870 case value_t::local_datetime : {return visitor(v.as_local_datetime ());} 7871 case value_t::local_date : {return visitor(v.as_local_date ());} 7872 case value_t::local_time : {return visitor(v.as_local_time ());} 7873 case value_t::array : {return visitor(v.as_array ());} 7874 case value_t::table : {return visitor(v.as_table ());} 7875 case value_t::empty : break; 7876 default: break; 7877 } 7878 throw type_error(format_error("[error] toml::visit: toml::basic_value " 7879 "does not have any valid type.", v.location(), "here"), v.location()); 7880 } 7881 7882 template<typename Visitor, typename TC> 7883 cxx::return_type_of_t<Visitor, typename basic_value<TC>::boolean_type&&> 7884 visit(Visitor&& visitor, basic_value<TC>&& v) 7885 { 7886 switch(v.type()) 7887 { 7888 case value_t::boolean : {return visitor(std::move(v.as_boolean ()));} 7889 case value_t::integer : {return visitor(std::move(v.as_integer ()));} 7890 case value_t::floating : {return visitor(std::move(v.as_floating ()));} 7891 case value_t::string : {return visitor(std::move(v.as_string ()));} 7892 case value_t::offset_datetime: {return visitor(std::move(v.as_offset_datetime()));} 7893 case value_t::local_datetime : {return visitor(std::move(v.as_local_datetime ()));} 7894 case value_t::local_date : {return visitor(std::move(v.as_local_date ()));} 7895 case value_t::local_time : {return visitor(std::move(v.as_local_time ()));} 7896 case value_t::array : {return visitor(std::move(v.as_array ()));} 7897 case value_t::table : {return visitor(std::move(v.as_table ()));} 7898 case value_t::empty : break; 7899 default: break; 7900 } 7901 throw type_error(format_error("[error] toml::visit: toml::basic_value " 7902 "does not have any valid type.", v.location(), "here"), v.location()); 7903 } 7904 7905 } // toml 7906 #endif // TOML11_VISIT_HPP 7907 #ifndef TOML11_TYPES_HPP 7908 #define TOML11_TYPES_HPP 7909 7910 7911 #include <ostream> 7912 #include <sstream> 7913 #include <string> 7914 #include <type_traits> 7915 #include <unordered_map> 7916 #include <vector> 7917 7918 #include <cstdint> 7919 #include <cstdio> 7920 7921 namespace toml 7922 { 7923 7924 // forward decl 7925 template<typename TypeConfig> 7926 class basic_value; 7927 7928 // when you use a special integer type as toml::value::integer_type, parse must 7929 // be able to read it. So, type_config has static member functions that read the 7930 // integer_type as {dec, hex, oct, bin}-integer. But, in most cases, operator<< 7931 // is enough. To make config easy, we provide the default read functions. 7932 // 7933 // Before this functions is called, syntax is checked and prefix(`0x` etc) and 7934 // spacer(`_`) are removed. 7935 7936 template<typename T> 7937 result<T, error_info> 7938 read_dec_int(const std::string& str, const source_location src) 7939 { 7940 constexpr auto max_digits = std::numeric_limits<T>::digits; 7941 assert( ! str.empty()); 7942 7943 T val{0}; 7944 std::istringstream iss(str); 7945 iss >> val; 7946 if(iss.fail()) 7947 { 7948 return err(make_error_info("toml::parse_dec_integer: " 7949 "too large integer: current max digits = 2^" + std::to_string(max_digits), 7950 std::move(src), "must be < 2^" + std::to_string(max_digits))); 7951 } 7952 return ok(val); 7953 } 7954 7955 template<typename T> 7956 result<T, error_info> 7957 read_hex_int(const std::string& str, const source_location src) 7958 { 7959 constexpr auto max_digits = std::numeric_limits<T>::digits; 7960 assert( ! str.empty()); 7961 7962 T val{0}; 7963 std::istringstream iss(str); 7964 iss >> std::hex >> val; 7965 if(iss.fail()) 7966 { 7967 return err(make_error_info("toml::parse_hex_integer: " 7968 "too large integer: current max value = 2^" + std::to_string(max_digits), 7969 std::move(src), "must be < 2^" + std::to_string(max_digits))); 7970 } 7971 return ok(val); 7972 } 7973 7974 template<typename T> 7975 result<T, error_info> 7976 read_oct_int(const std::string& str, const source_location src) 7977 { 7978 constexpr auto max_digits = std::numeric_limits<T>::digits; 7979 assert( ! str.empty()); 7980 7981 T val{0}; 7982 std::istringstream iss(str); 7983 iss >> std::oct >> val; 7984 if(iss.fail()) 7985 { 7986 return err(make_error_info("toml::parse_oct_integer: " 7987 "too large integer: current max value = 2^" + std::to_string(max_digits), 7988 std::move(src), "must be < 2^" + std::to_string(max_digits))); 7989 } 7990 return ok(val); 7991 } 7992 7993 template<typename T> 7994 result<T, error_info> 7995 read_bin_int(const std::string& str, const source_location src) 7996 { 7997 constexpr auto is_bounded = std::numeric_limits<T>::is_bounded; 7998 constexpr auto max_digits = std::numeric_limits<T>::digits; 7999 const auto max_value = (std::numeric_limits<T>::max)(); 8000 8001 T val{0}; 8002 T base{1}; 8003 for(auto i = str.rbegin(); i != str.rend(); ++i) 8004 { 8005 const auto c = *i; 8006 if(c == '1') 8007 { 8008 val += base; 8009 // prevent `base` from overflow 8010 if(is_bounded && max_value / 2 < base && std::next(i) != str.rend()) 8011 { 8012 base = 0; 8013 } 8014 else 8015 { 8016 base *= 2; 8017 } 8018 } 8019 else 8020 { 8021 assert(c == '0'); 8022 8023 if(is_bounded && max_value / 2 < base && std::next(i) != str.rend()) 8024 { 8025 base = 0; 8026 } 8027 else 8028 { 8029 base *= 2; 8030 } 8031 } 8032 } 8033 if(base == 0) 8034 { 8035 return err(make_error_info("toml::parse_bin_integer: " 8036 "too large integer: current max value = 2^" + std::to_string(max_digits), 8037 std::move(src), "must be < 2^" + std::to_string(max_digits))); 8038 } 8039 return ok(val); 8040 } 8041 8042 template<typename T> 8043 result<T, error_info> 8044 read_int(const std::string& str, const source_location src, const std::uint8_t base) 8045 { 8046 assert(base == 10 || base == 16 || base == 8 || base == 2); 8047 switch(base) 8048 { 8049 case 2: { return read_bin_int<T>(str, src); } 8050 case 8: { return read_oct_int<T>(str, src); } 8051 case 16: { return read_hex_int<T>(str, src); } 8052 default: 8053 { 8054 assert(base == 10); 8055 return read_dec_int<T>(str, src); 8056 } 8057 } 8058 } 8059 8060 inline result<float, error_info> 8061 read_hex_float(const std::string& str, const source_location src, float val) 8062 { 8063 #if defined(_MSC_VER) && ! defined(__clang__) 8064 const auto res = ::sscanf_s(str.c_str(), "%a", std::addressof(val)); 8065 #else 8066 const auto res = std::sscanf(str.c_str(), "%a", std::addressof(val)); 8067 #endif 8068 if(res != 1) 8069 { 8070 return err(make_error_info("toml::parse_floating: " 8071 "failed to read hexadecimal floating point value ", 8072 std::move(src), "here")); 8073 } 8074 return ok(val); 8075 } 8076 inline result<double, error_info> 8077 read_hex_float(const std::string& str, const source_location src, double val) 8078 { 8079 #if defined(_MSC_VER) && ! defined(__clang__) 8080 const auto res = ::sscanf_s(str.c_str(), "%la", std::addressof(val)); 8081 #else 8082 const auto res = std::sscanf(str.c_str(), "%la", std::addressof(val)); 8083 #endif 8084 if(res != 1) 8085 { 8086 return err(make_error_info("toml::parse_floating: " 8087 "failed to read hexadecimal floating point value ", 8088 std::move(src), "here")); 8089 } 8090 return ok(val); 8091 } 8092 template<typename T> 8093 cxx::enable_if_t<cxx::conjunction< 8094 cxx::negation<std::is_same<cxx::remove_cvref_t<T>, double>>, 8095 cxx::negation<std::is_same<cxx::remove_cvref_t<T>, float>> 8096 >::value, result<T, error_info>> 8097 read_hex_float(const std::string&, const source_location src, T) 8098 { 8099 return err(make_error_info("toml::parse_floating: failed to read " 8100 "floating point value because of unknown type in type_config", 8101 std::move(src), "here")); 8102 } 8103 8104 template<typename T> 8105 result<T, error_info> 8106 read_dec_float(const std::string& str, const source_location src) 8107 { 8108 T val; 8109 std::istringstream iss(str); 8110 iss >> val; 8111 if(iss.fail()) 8112 { 8113 return err(make_error_info("toml::parse_floating: " 8114 "failed to read floating point value from stream", 8115 std::move(src), "here")); 8116 } 8117 return ok(val); 8118 } 8119 8120 template<typename T> 8121 result<T, error_info> 8122 read_float(const std::string& str, const source_location src, const bool is_hex) 8123 { 8124 if(is_hex) 8125 { 8126 return read_hex_float(str, src, T{}); 8127 } 8128 else 8129 { 8130 return read_dec_float<T>(str, src); 8131 } 8132 } 8133 8134 struct type_config 8135 { 8136 using comment_type = preserve_comments; 8137 8138 using boolean_type = bool; 8139 using integer_type = std::int64_t; 8140 using floating_type = double; 8141 using string_type = std::string; 8142 8143 template<typename T> 8144 using array_type = std::vector<T>; 8145 template<typename K, typename T> 8146 using table_type = std::unordered_map<K, T>; 8147 8148 static result<integer_type, error_info> 8149 parse_int(const std::string& str, const source_location src, const std::uint8_t base) 8150 { 8151 return read_int<integer_type>(str, src, base); 8152 } 8153 static result<floating_type, error_info> 8154 parse_float(const std::string& str, const source_location src, const bool is_hex) 8155 { 8156 return read_float<floating_type>(str, src, is_hex); 8157 } 8158 }; 8159 8160 using value = basic_value<type_config>; 8161 using table = typename value::table_type; 8162 using array = typename value::array_type; 8163 8164 struct ordered_type_config 8165 { 8166 using comment_type = preserve_comments; 8167 8168 using boolean_type = bool; 8169 using integer_type = std::int64_t; 8170 using floating_type = double; 8171 using string_type = std::string; 8172 8173 template<typename T> 8174 using array_type = std::vector<T>; 8175 template<typename K, typename T> 8176 using table_type = ordered_map<K, T>; 8177 8178 static result<integer_type, error_info> 8179 parse_int(const std::string& str, const source_location src, const std::uint8_t base) 8180 { 8181 return read_int<integer_type>(str, src, base); 8182 } 8183 static result<floating_type, error_info> 8184 parse_float(const std::string& str, const source_location src, const bool is_hex) 8185 { 8186 return read_float<floating_type>(str, src, is_hex); 8187 } 8188 }; 8189 8190 using ordered_value = basic_value<ordered_type_config>; 8191 using ordered_table = typename ordered_value::table_type; 8192 using ordered_array = typename ordered_value::array_type; 8193 8194 // ---------------------------------------------------------------------------- 8195 // meta functions for internal use 8196 8197 namespace detail 8198 { 8199 8200 // ---------------------------------------------------------------------------- 8201 // check if type T has all the needed member types 8202 8203 struct has_comment_type_impl 8204 { 8205 template<typename T> static std::true_type check(typename T::comment_type*); 8206 template<typename T> static std::false_type check(...); 8207 }; 8208 template<typename T> 8209 using has_comment_type = decltype(has_comment_type_impl::check<T>(nullptr)); 8210 8211 struct has_integer_type_impl 8212 { 8213 template<typename T> static std::true_type check(typename T::integer_type*); 8214 template<typename T> static std::false_type check(...); 8215 }; 8216 template<typename T> 8217 using has_integer_type = decltype(has_integer_type_impl::check<T>(nullptr)); 8218 8219 struct has_floating_type_impl 8220 { 8221 template<typename T> static std::true_type check(typename T::floating_type*); 8222 template<typename T> static std::false_type check(...); 8223 }; 8224 template<typename T> 8225 using has_floating_type = decltype(has_floating_type_impl::check<T>(nullptr)); 8226 8227 struct has_string_type_impl 8228 { 8229 template<typename T> static std::true_type check(typename T::string_type*); 8230 template<typename T> static std::false_type check(...); 8231 }; 8232 template<typename T> 8233 using has_string_type = decltype(has_string_type_impl::check<T>(nullptr)); 8234 8235 struct has_array_type_impl 8236 { 8237 template<typename T> static std::true_type check(typename T::template array_type<int>*); 8238 template<typename T> static std::false_type check(...); 8239 }; 8240 template<typename T> 8241 using has_array_type = decltype(has_array_type_impl::check<T>(nullptr)); 8242 8243 struct has_table_type_impl 8244 { 8245 template<typename T> static std::true_type check(typename T::template table_type<int, int>*); 8246 template<typename T> static std::false_type check(...); 8247 }; 8248 template<typename T> 8249 using has_table_type = decltype(has_table_type_impl::check<T>(nullptr)); 8250 8251 struct has_parse_int_impl 8252 { 8253 template<typename T> static std::true_type check(decltype(std::declval<T>().parse_int( 8254 std::declval<const std::string&>(), 8255 std::declval<const source_location>(), 8256 std::declval<const std::uint8_t>() 8257 ))*); 8258 template<typename T> static std::false_type check(...); 8259 }; 8260 template<typename T> 8261 using has_parse_int = decltype(has_parse_int_impl::check<T>(nullptr)); 8262 8263 struct has_parse_float_impl 8264 { 8265 template<typename T> static std::true_type check(decltype(std::declval<T>().parse_float( 8266 std::declval<const std::string&>(), 8267 std::declval<const source_location>(), 8268 std::declval<const bool>() 8269 ))*); 8270 template<typename T> static std::false_type check(...); 8271 }; 8272 template<typename T> 8273 using has_parse_float = decltype(has_parse_float_impl::check<T>(nullptr)); 8274 8275 template<typename T> 8276 using is_type_config = cxx::conjunction< 8277 has_comment_type<T>, 8278 has_integer_type<T>, 8279 has_floating_type<T>, 8280 has_string_type<T>, 8281 has_array_type<T>, 8282 has_table_type<T>, 8283 has_parse_int<T>, 8284 has_parse_float<T> 8285 >; 8286 8287 } // namespace detail 8288 } // namespace toml 8289 8290 #if defined(TOML11_COMPILE_SOURCES) 8291 namespace toml 8292 { 8293 extern template class basic_value<type_config>; 8294 extern template class basic_value<ordered_type_config>; 8295 } // toml 8296 #endif // TOML11_COMPILE_SOURCES 8297 8298 #endif // TOML11_TYPES_HPP 8299 #ifndef TOML11_GET_HPP 8300 #define TOML11_GET_HPP 8301 8302 #include <algorithm> 8303 8304 8305 #if defined(TOML11_HAS_STRING_VIEW) 8306 #include <string_view> 8307 #endif // string_view 8308 8309 namespace toml 8310 { 8311 8312 // ============================================================================ 8313 // T is toml::value; identity transformation. 8314 8315 template<typename T, typename TC> 8316 cxx::enable_if_t<std::is_same<T, basic_value<TC>>::value, T>& 8317 get(basic_value<TC>& v) 8318 { 8319 return v; 8320 } 8321 8322 template<typename T, typename TC> 8323 cxx::enable_if_t<std::is_same<T, basic_value<TC>>::value, T> const& 8324 get(const basic_value<TC>& v) 8325 { 8326 return v; 8327 } 8328 8329 template<typename T, typename TC> 8330 cxx::enable_if_t<std::is_same<T, basic_value<TC>>::value, T> 8331 get(basic_value<TC>&& v) 8332 { 8333 return basic_value<TC>(std::move(v)); 8334 } 8335 8336 // ============================================================================ 8337 // exact toml::* type 8338 8339 template<typename T, typename TC> 8340 cxx::enable_if_t<detail::is_exact_toml_type<T, basic_value<TC>>::value, T> & 8341 get(basic_value<TC>& v) 8342 { 8343 constexpr auto ty = detail::type_to_enum<T, basic_value<TC>>::value; 8344 return detail::getter<TC, ty>::get(v); 8345 } 8346 8347 template<typename T, typename TC> 8348 cxx::enable_if_t<detail::is_exact_toml_type<T, basic_value<TC>>::value, T> const& 8349 get(const basic_value<TC>& v) 8350 { 8351 constexpr auto ty = detail::type_to_enum<T, basic_value<TC>>::value; 8352 return detail::getter<TC, ty>::get(v); 8353 } 8354 8355 template<typename T, typename TC> 8356 cxx::enable_if_t<detail::is_exact_toml_type<T, basic_value<TC>>::value, T> 8357 get(basic_value<TC>&& v) 8358 { 8359 constexpr auto ty = detail::type_to_enum<T, basic_value<TC>>::value; 8360 return detail::getter<TC, ty>::get(std::move(v)); 8361 } 8362 8363 // ============================================================================ 8364 // T is toml::basic_value<U> 8365 8366 template<typename T, typename TC> 8367 cxx::enable_if_t<cxx::conjunction< 8368 detail::is_basic_value<T>, 8369 cxx::negation<std::is_same<T, basic_value<TC>>> 8370 >::value, T> 8371 get(basic_value<TC> v) 8372 { 8373 return T(std::move(v)); 8374 } 8375 8376 // ============================================================================ 8377 // integer convertible from toml::value::integer_type 8378 8379 template<typename T, typename TC> 8380 cxx::enable_if_t<cxx::conjunction< 8381 std::is_integral<T>, 8382 cxx::negation<std::is_same<T, bool>>, 8383 detail::is_not_toml_type<T, basic_value<TC>>, 8384 cxx::negation<detail::has_from_toml_method<T, TC>>, 8385 cxx::negation<detail::has_specialized_from<T>> 8386 >::value, T> 8387 get(const basic_value<TC>& v) 8388 { 8389 return static_cast<T>(v.as_integer()); 8390 } 8391 8392 // ============================================================================ 8393 // floating point convertible from toml::value::floating_type 8394 8395 template<typename T, typename TC> 8396 cxx::enable_if_t<cxx::conjunction< 8397 std::is_floating_point<T>, 8398 detail::is_not_toml_type<T, basic_value<TC>>, 8399 cxx::negation<detail::has_from_toml_method<T, TC>>, 8400 cxx::negation<detail::has_specialized_from<T>> 8401 >::value, T> 8402 get(const basic_value<TC>& v) 8403 { 8404 return static_cast<T>(v.as_floating()); 8405 } 8406 8407 // ============================================================================ 8408 // std::string with different char/trait/allocator 8409 8410 template<typename T, typename TC> 8411 cxx::enable_if_t<cxx::conjunction< 8412 detail::is_not_toml_type<T, basic_value<TC>>, 8413 detail::is_1byte_std_basic_string<T> 8414 >::value, T> 8415 get(const basic_value<TC>& v) 8416 { 8417 return detail::string_conv<cxx::remove_cvref_t<T>>(v.as_string()); 8418 } 8419 8420 // ============================================================================ 8421 // std::string_view 8422 8423 #if defined(TOML11_HAS_STRING_VIEW) 8424 8425 template<typename T, typename TC> 8426 cxx::enable_if_t<detail::is_string_view_of<T, typename basic_value<TC>::string_type>::value, T> 8427 get(const basic_value<TC>& v) 8428 { 8429 return T(v.as_string()); 8430 } 8431 8432 #endif // string_view 8433 8434 // ============================================================================ 8435 // std::chrono::duration from toml::local_time 8436 8437 template<typename T, typename TC> 8438 cxx::enable_if_t<detail::is_chrono_duration<T>::value, T> 8439 get(const basic_value<TC>& v) 8440 { 8441 return std::chrono::duration_cast<T>( 8442 std::chrono::nanoseconds(v.as_local_time())); 8443 } 8444 8445 // ============================================================================ 8446 // std::chrono::system_clock::time_point from toml::datetime variants 8447 8448 template<typename T, typename TC> 8449 cxx::enable_if_t< 8450 std::is_same<std::chrono::system_clock::time_point, T>::value, T> 8451 get(const basic_value<TC>& v) 8452 { 8453 switch(v.type()) 8454 { 8455 case value_t::local_date: 8456 { 8457 return std::chrono::system_clock::time_point(v.as_local_date()); 8458 } 8459 case value_t::local_datetime: 8460 { 8461 return std::chrono::system_clock::time_point(v.as_local_datetime()); 8462 } 8463 case value_t::offset_datetime: 8464 { 8465 return std::chrono::system_clock::time_point(v.as_offset_datetime()); 8466 } 8467 default: 8468 { 8469 const auto loc = v.location(); 8470 throw type_error(format_error("toml::get: " 8471 "bad_cast to std::chrono::system_clock::time_point", loc, 8472 "the actual type is " + to_string(v.type())), loc); 8473 } 8474 } 8475 } 8476 8477 // ============================================================================ 8478 // forward declaration to use this recursively. ignore this and go ahead. 8479 8480 // array-like (w/ push_back) 8481 template<typename T, typename TC> 8482 cxx::enable_if_t<cxx::conjunction< 8483 detail::is_container<T>, // T is a container 8484 detail::has_push_back_method<T>, // .push_back() works 8485 detail::is_not_toml_type<T, basic_value<TC>>, // but not toml::array 8486 cxx::negation<detail::is_std_basic_string<T>>, // but not std::basic_string<CharT> 8487 #if defined(TOML11_HAS_STRING_VIEW) 8488 cxx::negation<detail::is_std_basic_string_view<T>>, // but not std::basic_string_view<CharT> 8489 #endif 8490 cxx::negation<detail::has_from_toml_method<T, TC>>, // no T.from_toml() 8491 cxx::negation<detail::has_specialized_from<T>>, // no toml::from<T> 8492 cxx::negation<std::is_constructible<T, const basic_value<TC>&>> 8493 >::value, T> 8494 get(const basic_value<TC>&); 8495 8496 // std::array 8497 template<typename T, typename TC> 8498 cxx::enable_if_t<detail::is_std_array<T>::value, T> 8499 get(const basic_value<TC>&); 8500 8501 // std::forward_list 8502 template<typename T, typename TC> 8503 cxx::enable_if_t<detail::is_std_forward_list<T>::value, T> 8504 get(const basic_value<TC>&); 8505 8506 // std::pair<T1, T2> 8507 template<typename T, typename TC> 8508 cxx::enable_if_t<detail::is_std_pair<T>::value, T> 8509 get(const basic_value<TC>&); 8510 8511 // std::tuple<T1, T2, ...> 8512 template<typename T, typename TC> 8513 cxx::enable_if_t<detail::is_std_tuple<T>::value, T> 8514 get(const basic_value<TC>&); 8515 8516 // std::map<key, value> (key is convertible from toml::value::key_type) 8517 template<typename T, typename TC> 8518 cxx::enable_if_t<cxx::conjunction< 8519 detail::is_map<T>, // T is map 8520 detail::is_not_toml_type<T, basic_value<TC>>, // but not toml::table 8521 std::is_convertible<typename basic_value<TC>::key_type, 8522 typename T::key_type>, // keys are convertible 8523 cxx::negation<detail::has_from_toml_method<T, TC>>, // no T.from_toml() 8524 cxx::negation<detail::has_specialized_from<T>>, // no toml::from<T> 8525 cxx::negation<std::is_constructible<T, const basic_value<TC>&>> 8526 >::value, T> 8527 get(const basic_value<TC>& v); 8528 8529 // std::map<key, value> (key is not convertible from toml::value::key_type, but 8530 // is a std::basic_string) 8531 template<typename T, typename TC> 8532 cxx::enable_if_t<cxx::conjunction< 8533 detail::is_map<T>, // T is map 8534 detail::is_not_toml_type<T, basic_value<TC>>, // but not toml::table 8535 cxx::negation<std::is_convertible<typename basic_value<TC>::key_type, 8536 typename T::key_type>>, // keys are NOT convertible 8537 detail::is_1byte_std_basic_string<typename T::key_type>, // is std::basic_string 8538 cxx::negation<detail::has_from_toml_method<T, TC>>, // no T.from_toml() 8539 cxx::negation<detail::has_specialized_from<T>>, // no toml::from<T> 8540 cxx::negation<std::is_constructible<T, const basic_value<TC>&>> 8541 >::value, T> 8542 get(const basic_value<TC>& v); 8543 8544 // toml::from<T>::from_toml(v) 8545 template<typename T, typename TC> 8546 cxx::enable_if_t<detail::has_specialized_from<T>::value, T> 8547 get(const basic_value<TC>&); 8548 8549 // has T.from_toml(v) but no from<T> 8550 template<typename T, typename TC> 8551 cxx::enable_if_t<cxx::conjunction< 8552 detail::has_from_toml_method<T, TC>, // has T.from_toml() 8553 cxx::negation<detail::has_specialized_from<T>>, // no toml::from<T> 8554 std::is_default_constructible<T> // T{} works 8555 >::value, T> 8556 get(const basic_value<TC>&); 8557 8558 // T(const toml::value&) and T is not toml::basic_value, 8559 // and it does not have `from<T>` nor `from_toml`. 8560 template<typename T, typename TC> 8561 cxx::enable_if_t<cxx::conjunction< 8562 std::is_constructible<T, const basic_value<TC>&>, // has T(const basic_value&) 8563 cxx::negation<detail::is_basic_value<T>>, // but not basic_value itself 8564 cxx::negation<detail::has_from_toml_method<T, TC>>, // no .from_toml() 8565 cxx::negation<detail::has_specialized_from<T>> // no toml::from<T> 8566 >::value, T> 8567 get(const basic_value<TC>&); 8568 8569 // ============================================================================ 8570 // array-like types; most likely STL container, like std::vector, etc. 8571 8572 template<typename T, typename TC> 8573 cxx::enable_if_t<cxx::conjunction< 8574 detail::is_container<T>, // T is a container 8575 detail::has_push_back_method<T>, // .push_back() works 8576 detail::is_not_toml_type<T, basic_value<TC>>, // but not toml::array 8577 cxx::negation<detail::is_std_basic_string<T>>, // but not std::basic_string<CharT> 8578 #if defined(TOML11_HAS_STRING_VIEW) 8579 cxx::negation<detail::is_std_basic_string_view<T>>, // but not std::basic_string_view<CharT> 8580 #endif 8581 cxx::negation<detail::has_from_toml_method<T, TC>>, // no T.from_toml() 8582 cxx::negation<detail::has_specialized_from<T>>, // no toml::from<T> 8583 cxx::negation<std::is_constructible<T, const basic_value<TC>&>> 8584 >::value, T> 8585 get(const basic_value<TC>& v) 8586 { 8587 using value_type = typename T::value_type; 8588 const auto& a = v.as_array(); 8589 8590 T container; 8591 detail::try_reserve(container, a.size()); // if T has .reserve(), call it 8592 8593 for(const auto& elem : a) 8594 { 8595 container.push_back(get<value_type>(elem)); 8596 } 8597 return container; 8598 } 8599 8600 // ============================================================================ 8601 // std::array 8602 8603 template<typename T, typename TC> 8604 cxx::enable_if_t<detail::is_std_array<T>::value, T> 8605 get(const basic_value<TC>& v) 8606 { 8607 using value_type = typename T::value_type; 8608 const auto& a = v.as_array(); 8609 8610 T container; 8611 if(a.size() != container.size()) 8612 { 8613 const auto loc = v.location(); 8614 throw std::out_of_range(format_error("toml::get: while converting to an array: " 8615 " array size is " + std::to_string(container.size()) + 8616 " but there are " + std::to_string(a.size()) + " elements in toml array.", 8617 loc, "here")); 8618 } 8619 for(std::size_t i=0; i<a.size(); ++i) 8620 { 8621 container.at(i) = ::toml::get<value_type>(a.at(i)); 8622 } 8623 return container; 8624 } 8625 8626 // ============================================================================ 8627 // std::forward_list 8628 8629 template<typename T, typename TC> 8630 cxx::enable_if_t<detail::is_std_forward_list<T>::value, T> 8631 get(const basic_value<TC>& v) 8632 { 8633 using value_type = typename T::value_type; 8634 8635 T container; 8636 for(const auto& elem : v.as_array()) 8637 { 8638 container.push_front(get<value_type>(elem)); 8639 } 8640 container.reverse(); 8641 return container; 8642 } 8643 8644 // ============================================================================ 8645 // std::pair 8646 8647 template<typename T, typename TC> 8648 cxx::enable_if_t<detail::is_std_pair<T>::value, T> 8649 get(const basic_value<TC>& v) 8650 { 8651 using first_type = typename T::first_type; 8652 using second_type = typename T::second_type; 8653 8654 const auto& ar = v.as_array(); 8655 if(ar.size() != 2) 8656 { 8657 const auto loc = v.location(); 8658 throw std::out_of_range(format_error("toml::get: while converting std::pair: " 8659 " but there are " + std::to_string(ar.size()) + " > 2 elements in toml array.", 8660 loc, "here")); 8661 } 8662 return std::make_pair(::toml::get<first_type >(ar.at(0)), 8663 ::toml::get<second_type>(ar.at(1))); 8664 } 8665 8666 // ============================================================================ 8667 // std::tuple. 8668 8669 namespace detail 8670 { 8671 template<typename T, typename Array, std::size_t ... I> 8672 T get_tuple_impl(const Array& a, cxx::index_sequence<I...>) 8673 { 8674 return std::make_tuple( 8675 ::toml::get<typename std::tuple_element<I, T>::type>(a.at(I))...); 8676 } 8677 } // detail 8678 8679 template<typename T, typename TC> 8680 cxx::enable_if_t<detail::is_std_tuple<T>::value, T> 8681 get(const basic_value<TC>& v) 8682 { 8683 const auto& ar = v.as_array(); 8684 if(ar.size() != std::tuple_size<T>::value) 8685 { 8686 const auto loc = v.location(); 8687 throw std::out_of_range(format_error("toml::get: while converting std::tuple: " 8688 " there are " + std::to_string(ar.size()) + " > " + 8689 std::to_string(std::tuple_size<T>::value) + " elements in toml array.", 8690 loc, "here")); 8691 } 8692 return detail::get_tuple_impl<T>(ar, 8693 cxx::make_index_sequence<std::tuple_size<T>::value>{}); 8694 } 8695 8696 // ============================================================================ 8697 // map-like types; most likely STL map, like std::map or std::unordered_map. 8698 8699 // key is convertible from toml::value::key_type 8700 template<typename T, typename TC> 8701 cxx::enable_if_t<cxx::conjunction< 8702 detail::is_map<T>, // T is map 8703 detail::is_not_toml_type<T, basic_value<TC>>, // but not toml::table 8704 std::is_convertible<typename basic_value<TC>::key_type, 8705 typename T::key_type>, // keys are convertible 8706 cxx::negation<detail::has_from_toml_method<T, TC>>, // no T.from_toml() 8707 cxx::negation<detail::has_specialized_from<T>>, // no toml::from<T> 8708 cxx::negation<std::is_constructible<T, const basic_value<TC>&>> 8709 >::value, T> 8710 get(const basic_value<TC>& v) 8711 { 8712 using key_type = typename T::key_type; 8713 using mapped_type = typename T::mapped_type; 8714 static_assert( 8715 std::is_convertible<typename basic_value<TC>::key_type, key_type>::value, 8716 "toml::get only supports map type of which key_type is " 8717 "convertible from toml::basic_value::key_type."); 8718 8719 T m; 8720 for(const auto& kv : v.as_table()) 8721 { 8722 m.emplace(key_type(kv.first), get<mapped_type>(kv.second)); 8723 } 8724 return m; 8725 } 8726 8727 // key is NOT convertible from toml::value::key_type but std::basic_string 8728 template<typename T, typename TC> 8729 cxx::enable_if_t<cxx::conjunction< 8730 detail::is_map<T>, // T is map 8731 detail::is_not_toml_type<T, basic_value<TC>>, // but not toml::table 8732 cxx::negation<std::is_convertible<typename basic_value<TC>::key_type, 8733 typename T::key_type>>, // keys are NOT convertible 8734 detail::is_1byte_std_basic_string<typename T::key_type>, // is std::basic_string 8735 cxx::negation<detail::has_from_toml_method<T, TC>>, // no T.from_toml() 8736 cxx::negation<detail::has_specialized_from<T>>, // no toml::from<T> 8737 cxx::negation<std::is_constructible<T, const basic_value<TC>&>> 8738 >::value, T> 8739 get(const basic_value<TC>& v) 8740 { 8741 using key_type = typename T::key_type; 8742 using mapped_type = typename T::mapped_type; 8743 8744 T m; 8745 for(const auto& kv : v.as_table()) 8746 { 8747 m.emplace(detail::string_conv<key_type>(kv.first), get<mapped_type>(kv.second)); 8748 } 8749 return m; 8750 } 8751 8752 // ============================================================================ 8753 // user-defined, but convertible types. 8754 8755 // toml::from<T> 8756 template<typename T, typename TC> 8757 cxx::enable_if_t<detail::has_specialized_from<T>::value, T> 8758 get(const basic_value<TC>& v) 8759 { 8760 return ::toml::from<T>::from_toml(v); 8761 } 8762 8763 // has T.from_toml(v) but no from<T> 8764 template<typename T, typename TC> 8765 cxx::enable_if_t<cxx::conjunction< 8766 detail::has_from_toml_method<T, TC>, // has T.from_toml() 8767 cxx::negation<detail::has_specialized_from<T>>, // no toml::from<T> 8768 std::is_default_constructible<T> // T{} works 8769 >::value, T> 8770 get(const basic_value<TC>& v) 8771 { 8772 T ud; 8773 ud.from_toml(v); 8774 return ud; 8775 } 8776 8777 // T(const toml::value&) and T is not toml::basic_value, 8778 // and it does not have `from<T>` nor `from_toml`. 8779 template<typename T, typename TC> 8780 cxx::enable_if_t<cxx::conjunction< 8781 std::is_constructible<T, const basic_value<TC>&>, // has T(const basic_value&) 8782 cxx::negation<detail::is_basic_value<T>>, // but not basic_value itself 8783 cxx::negation<detail::has_from_toml_method<T, TC>>, // no .from_toml() 8784 cxx::negation<detail::has_specialized_from<T>> // no toml::from<T> 8785 >::value, T> 8786 get(const basic_value<TC>& v) 8787 { 8788 return T(v); 8789 } 8790 8791 // ============================================================================ 8792 // get_or(value, fallback) 8793 8794 template<typename TC> 8795 cxx::enable_if_t<detail::is_type_config<TC>::value, basic_value<TC>> const& 8796 get_or(const basic_value<TC>& v, const basic_value<TC>&) 8797 { 8798 return v; 8799 } 8800 8801 template<typename TC> 8802 cxx::enable_if_t<detail::is_type_config<TC>::value, basic_value<TC>>& 8803 get_or(basic_value<TC>& v, basic_value<TC>&) 8804 { 8805 return v; 8806 } 8807 8808 template<typename TC> 8809 cxx::enable_if_t<detail::is_type_config<TC>::value, basic_value<TC>> 8810 get_or(basic_value<TC>&& v, basic_value<TC>&&) 8811 { 8812 return v; 8813 } 8814 8815 // ---------------------------------------------------------------------------- 8816 // specialization for the exact toml types (return type becomes lvalue ref) 8817 8818 template<typename T, typename TC> 8819 cxx::enable_if_t< 8820 detail::is_exact_toml_type<T, basic_value<TC>>::value, T> const& 8821 get_or(const basic_value<TC>& v, const T& opt) noexcept 8822 { 8823 try 8824 { 8825 return get<cxx::remove_cvref_t<T>>(v); 8826 } 8827 catch(...) 8828 { 8829 return opt; 8830 } 8831 } 8832 template<typename T, typename TC> 8833 cxx::enable_if_t<cxx::conjunction< 8834 cxx::negation<std::is_const<T>>, 8835 detail::is_exact_toml_type<T, basic_value<TC>> 8836 >::value, T>& 8837 get_or(basic_value<TC>& v, T& opt) noexcept 8838 { 8839 try 8840 { 8841 return get<cxx::remove_cvref_t<T>>(v); 8842 } 8843 catch(...) 8844 { 8845 return opt; 8846 } 8847 } 8848 template<typename T, typename TC> 8849 cxx::enable_if_t<detail::is_exact_toml_type<cxx::remove_cvref_t<T>, 8850 basic_value<TC>>::value, cxx::remove_cvref_t<T>> 8851 get_or(basic_value<TC>&& v, T&& opt) noexcept 8852 { 8853 try 8854 { 8855 return get<cxx::remove_cvref_t<T>>(std::move(v)); 8856 } 8857 catch(...) 8858 { 8859 return cxx::remove_cvref_t<T>(std::forward<T>(opt)); 8860 } 8861 } 8862 8863 // ---------------------------------------------------------------------------- 8864 // specialization for string literal 8865 8866 // template<std::size_t N, typename TC> 8867 // typename basic_value<TC>::string_type 8868 // get_or(const basic_value<TC>& v, 8869 // const typename basic_value<TC>::string_type::value_type (&opt)[N]) 8870 // { 8871 // try 8872 // { 8873 // return v.as_string(); 8874 // } 8875 // catch(...) 8876 // { 8877 // return typename basic_value<TC>::string_type(opt); 8878 // } 8879 // } 8880 // 8881 // The above only matches to the literal, like `get_or(v, "foo");` but not 8882 // ```cpp 8883 // const auto opt = "foo"; 8884 // const auto str = get_or(v, opt); 8885 // ``` 8886 // . And the latter causes an error. 8887 // To match to both `"foo"` and `const auto opt = "foo"`, we take a pointer to 8888 // a character here. 8889 8890 template<typename TC> 8891 typename basic_value<TC>::string_type 8892 get_or(const basic_value<TC>& v, 8893 const typename basic_value<TC>::string_type::value_type* opt) 8894 { 8895 try 8896 { 8897 return v.as_string(); 8898 } 8899 catch(...) 8900 { 8901 return typename basic_value<TC>::string_type(opt); 8902 } 8903 } 8904 8905 // ---------------------------------------------------------------------------- 8906 // others (require type conversion and return type cannot be lvalue reference) 8907 8908 template<typename T, typename TC> 8909 cxx::enable_if_t<cxx::conjunction< 8910 cxx::negation<detail::is_basic_value<T>>, 8911 cxx::negation<detail::is_exact_toml_type<T, basic_value<TC>>>, 8912 cxx::negation<std::is_same<cxx::remove_cvref_t<T>, typename basic_value<TC>::string_type::value_type const*>> 8913 >::value, cxx::remove_cvref_t<T>> 8914 get_or(const basic_value<TC>& v, T&& opt) 8915 { 8916 try 8917 { 8918 return get<cxx::remove_cvref_t<T>>(v); 8919 } 8920 catch(...) 8921 { 8922 return cxx::remove_cvref_t<T>(std::forward<T>(opt)); 8923 } 8924 } 8925 8926 } // toml 8927 #endif // TOML11_GET_HPP 8928 #ifndef TOML11_FIND_HPP 8929 #define TOML11_FIND_HPP 8930 8931 #include <algorithm> 8932 8933 8934 #if defined(TOML11_HAS_STRING_VIEW) 8935 #include <string_view> 8936 #endif 8937 8938 namespace toml 8939 { 8940 8941 // ---------------------------------------------------------------------------- 8942 // find<T>(value, key); 8943 8944 template<typename T, typename TC> 8945 decltype(::toml::get<T>(std::declval<basic_value<TC> const&>())) 8946 find(const basic_value<TC>& v, const typename basic_value<TC>::key_type& ky) 8947 { 8948 return ::toml::get<T>(v.at(ky)); 8949 } 8950 8951 template<typename T, typename TC> 8952 decltype(::toml::get<T>(std::declval<basic_value<TC>&>())) 8953 find(basic_value<TC>& v, const typename basic_value<TC>::key_type& ky) 8954 { 8955 return ::toml::get<T>(v.at(ky)); 8956 } 8957 8958 template<typename T, typename TC> 8959 decltype(::toml::get<T>(std::declval<basic_value<TC>&&>())) 8960 find(basic_value<TC>&& v, const typename basic_value<TC>::key_type& ky) 8961 { 8962 return ::toml::get<T>(std::move(v.at(ky))); 8963 } 8964 8965 // ---------------------------------------------------------------------------- 8966 // find<T>(value, idx) 8967 8968 template<typename T, typename TC> 8969 decltype(::toml::get<T>(std::declval<basic_value<TC> const&>())) 8970 find(const basic_value<TC>& v, const std::size_t idx) 8971 { 8972 return ::toml::get<T>(v.at(idx)); 8973 } 8974 template<typename T, typename TC> 8975 decltype(::toml::get<T>(std::declval<basic_value<TC>&>())) 8976 find(basic_value<TC>& v, const std::size_t idx) 8977 { 8978 return ::toml::get<T>(v.at(idx)); 8979 } 8980 template<typename T, typename TC> 8981 decltype(::toml::get<T>(std::declval<basic_value<TC>&&>())) 8982 find(basic_value<TC>&& v, const std::size_t idx) 8983 { 8984 return ::toml::get<T>(std::move(v.at(idx))); 8985 } 8986 8987 // ---------------------------------------------------------------------------- 8988 // find(value, key/idx), w/o conversion 8989 8990 template<typename TC> 8991 cxx::enable_if_t<detail::is_type_config<TC>::value, basic_value<TC>>& 8992 find(basic_value<TC>& v, const typename basic_value<TC>::key_type& ky) 8993 { 8994 return v.at(ky); 8995 } 8996 template<typename TC> 8997 cxx::enable_if_t<detail::is_type_config<TC>::value, basic_value<TC>> const& 8998 find(basic_value<TC> const& v, const typename basic_value<TC>::key_type& ky) 8999 { 9000 return v.at(ky); 9001 } 9002 template<typename TC> 9003 cxx::enable_if_t<detail::is_type_config<TC>::value, basic_value<TC>> 9004 find(basic_value<TC>&& v, const typename basic_value<TC>::key_type& ky) 9005 { 9006 return basic_value<TC>(std::move(v.at(ky))); 9007 } 9008 9009 template<typename TC> 9010 cxx::enable_if_t<detail::is_type_config<TC>::value, basic_value<TC>>& 9011 find(basic_value<TC>& v, const std::size_t idx) 9012 { 9013 return v.at(idx); 9014 } 9015 template<typename TC> 9016 cxx::enable_if_t<detail::is_type_config<TC>::value, basic_value<TC>> const& 9017 find(basic_value<TC> const& v, const std::size_t idx) 9018 { 9019 return v.at(idx); 9020 } 9021 template<typename TC> 9022 cxx::enable_if_t<detail::is_type_config<TC>::value, basic_value<TC>> 9023 find(basic_value<TC>&& v, const std::size_t idx) 9024 { 9025 return basic_value<TC>(std::move(v.at(idx))); 9026 } 9027 9028 // -------------------------------------------------------------------------- 9029 // toml::find(toml::value, toml::key, Ts&& ... keys) 9030 9031 namespace detail 9032 { 9033 9034 // It suppresses warnings by -Wsign-conversion when we pass integer literal 9035 // to toml::find. integer literal `0` is deduced as an int, and will be 9036 // converted to std::size_t. This causes sign-conversion. 9037 9038 template<typename TC> 9039 std::size_t key_cast(const std::size_t& v) noexcept 9040 { 9041 return v; 9042 } 9043 template<typename TC, typename T> 9044 cxx::enable_if_t<std::is_integral<cxx::remove_cvref_t<T>>::value, std::size_t> 9045 key_cast(const T& v) noexcept 9046 { 9047 return static_cast<std::size_t>(v); 9048 } 9049 9050 // for string-like (string, string literal, string_view) 9051 9052 template<typename TC> 9053 typename basic_value<TC>::key_type const& 9054 key_cast(const typename basic_value<TC>::key_type& v) noexcept 9055 { 9056 return v; 9057 } 9058 template<typename TC> 9059 typename basic_value<TC>::key_type 9060 key_cast(const typename basic_value<TC>::key_type::value_type* v) 9061 { 9062 return typename basic_value<TC>::key_type(v); 9063 } 9064 #if defined(TOML11_HAS_STRING_VIEW) 9065 template<typename TC> 9066 typename basic_value<TC>::key_type 9067 key_cast(const std::string_view v) 9068 { 9069 return typename basic_value<TC>::key_type(v); 9070 } 9071 #endif // string_view 9072 9073 } // detail 9074 9075 // ---------------------------------------------------------------------------- 9076 // find(v, keys...) 9077 9078 template<typename TC, typename K1, typename K2, typename ... Ks> 9079 cxx::enable_if_t<detail::is_type_config<TC>::value, basic_value<TC>> const& 9080 find(const basic_value<TC>& v, const K1& k1, const K2& k2, const Ks& ... ks) 9081 { 9082 return find(v.at(detail::key_cast<TC>(k1)), detail::key_cast<TC>(k2), ks...); 9083 } 9084 template<typename TC, typename K1, typename K2, typename ... Ks> 9085 cxx::enable_if_t<detail::is_type_config<TC>::value, basic_value<TC>>& 9086 find(basic_value<TC>& v, const K1& k1, const K2& k2, const Ks& ... ks) 9087 { 9088 return find(v.at(detail::key_cast<TC>(k1)), detail::key_cast<TC>(k2), ks...); 9089 } 9090 template<typename TC, typename K1, typename K2, typename ... Ks> 9091 cxx::enable_if_t<detail::is_type_config<TC>::value, basic_value<TC>> 9092 find(basic_value<TC>&& v, const K1& k1, const K2& k2, const Ks& ... ks) 9093 { 9094 return find(std::move(v.at(detail::key_cast<TC>(k1))), detail::key_cast<TC>(k2), ks...); 9095 } 9096 9097 // ---------------------------------------------------------------------------- 9098 // find<T>(v, keys...) 9099 9100 template<typename T, typename TC, typename K1, typename K2, typename ... Ks> 9101 decltype(::toml::get<T>(std::declval<const basic_value<TC>&>())) 9102 find(const basic_value<TC>& v, const K1& k1, const K2& k2, const Ks& ... ks) 9103 { 9104 return find<T>(v.at(detail::key_cast<TC>(k1)), detail::key_cast<TC>(k2), ks...); 9105 } 9106 template<typename T, typename TC, typename K1, typename K2, typename ... Ks> 9107 decltype(::toml::get<T>(std::declval<basic_value<TC>&>())) 9108 find(basic_value<TC>& v, const K1& k1, const K2& k2, const Ks& ... ks) 9109 { 9110 return find<T>(v.at(detail::key_cast<TC>(k1)), detail::key_cast<TC>(k2), ks...); 9111 } 9112 template<typename T, typename TC, typename K1, typename K2, typename ... Ks> 9113 decltype(::toml::get<T>(std::declval<basic_value<TC>&&>())) 9114 find(basic_value<TC>&& v, const K1& k1, const K2& k2, const Ks& ... ks) 9115 { 9116 return find<T>(std::move(v.at(detail::key_cast<TC>(k1))), detail::key_cast<TC>(k2), ks...); 9117 } 9118 9119 // =========================================================================== 9120 // find_or<T>(value, key, fallback) 9121 9122 // --------------------------------------------------------------------------- 9123 // find_or(v, key, other_v) 9124 9125 template<typename TC, typename K> 9126 cxx::enable_if_t<detail::is_type_config<TC>::value, basic_value<TC>>& 9127 find_or(basic_value<TC>& v, const K& k, basic_value<TC>& opt) noexcept 9128 { 9129 try 9130 { 9131 return ::toml::find(v, detail::key_cast<TC>(k)); 9132 } 9133 catch(...) 9134 { 9135 return opt; 9136 } 9137 } 9138 template<typename TC, typename K> 9139 cxx::enable_if_t<detail::is_type_config<TC>::value, basic_value<TC>> const& 9140 find_or(const basic_value<TC>& v, const K& k, const basic_value<TC>& opt) noexcept 9141 { 9142 try 9143 { 9144 return ::toml::find(v, detail::key_cast<TC>(k)); 9145 } 9146 catch(...) 9147 { 9148 return opt; 9149 } 9150 } 9151 template<typename TC, typename K> 9152 cxx::enable_if_t<detail::is_type_config<TC>::value, basic_value<TC>> 9153 find_or(basic_value<TC>&& v, const K& k, basic_value<TC>&& opt) noexcept 9154 { 9155 try 9156 { 9157 return ::toml::find(v, detail::key_cast<TC>(k)); 9158 } 9159 catch(...) 9160 { 9161 return opt; 9162 } 9163 } 9164 9165 // --------------------------------------------------------------------------- 9166 // toml types (return type can be a reference) 9167 9168 template<typename T, typename TC, typename K> 9169 cxx::enable_if_t<detail::is_exact_toml_type<T, basic_value<TC>>::value, 9170 cxx::remove_cvref_t<T> const&> 9171 find_or(const basic_value<TC>& v, const K& k, const T& opt) 9172 { 9173 try 9174 { 9175 return ::toml::get<T>(v.at(detail::key_cast<TC>(k))); 9176 } 9177 catch(...) 9178 { 9179 return opt; 9180 } 9181 } 9182 9183 template<typename T, typename TC, typename K> 9184 cxx::enable_if_t<cxx::conjunction< 9185 cxx::negation<std::is_const<T>>, 9186 detail::is_exact_toml_type<T, basic_value<TC>> 9187 >::value, cxx::remove_cvref_t<T>&> 9188 find_or(basic_value<TC>& v, const K& k, T& opt) 9189 { 9190 try 9191 { 9192 return ::toml::get<T>(v.at(detail::key_cast<TC>(k))); 9193 } 9194 catch(...) 9195 { 9196 return opt; 9197 } 9198 } 9199 9200 template<typename T, typename TC, typename K> 9201 cxx::enable_if_t<detail::is_exact_toml_type<T, basic_value<TC>>::value, 9202 cxx::remove_cvref_t<T>> 9203 find_or(basic_value<TC>&& v, const K& k, T opt) 9204 { 9205 try 9206 { 9207 return ::toml::get<T>(std::move(v.at(detail::key_cast<TC>(k)))); 9208 } 9209 catch(...) 9210 { 9211 return T(std::move(opt)); 9212 } 9213 } 9214 9215 // --------------------------------------------------------------------------- 9216 // string literal (deduced as std::string) 9217 9218 // XXX to avoid confusion when T is explicitly specified in find_or<T>(), 9219 // we restrict the string type as std::string. 9220 template<typename TC, typename K> 9221 cxx::enable_if_t<detail::is_type_config<TC>::value, std::string> 9222 find_or(const basic_value<TC>& v, const K& k, const char* opt) 9223 { 9224 try 9225 { 9226 return ::toml::get<std::string>(v.at(detail::key_cast<TC>(k))); 9227 } 9228 catch(...) 9229 { 9230 return std::string(opt); 9231 } 9232 } 9233 9234 // --------------------------------------------------------------------------- 9235 // other types (requires type conversion and return type cannot be a reference) 9236 9237 template<typename T, typename TC, typename K> 9238 cxx::enable_if_t<cxx::conjunction< 9239 cxx::negation<detail::is_basic_value<cxx::remove_cvref_t<T>>>, 9240 detail::is_not_toml_type<cxx::remove_cvref_t<T>, basic_value<TC>>, 9241 cxx::negation<std::is_same<cxx::remove_cvref_t<T>, 9242 const typename basic_value<TC>::string_type::value_type*>> 9243 >::value, cxx::remove_cvref_t<T>> 9244 find_or(const basic_value<TC>& v, const K& ky, T opt) 9245 { 9246 try 9247 { 9248 return ::toml::get<cxx::remove_cvref_t<T>>(v.at(detail::key_cast<TC>(ky))); 9249 } 9250 catch(...) 9251 { 9252 return cxx::remove_cvref_t<T>(std::move(opt)); 9253 } 9254 } 9255 9256 // ---------------------------------------------------------------------------- 9257 // recursive 9258 9259 namespace detail 9260 { 9261 9262 template<typename ...Ts> 9263 auto last_one(Ts&&... args) 9264 -> decltype(std::get<sizeof...(Ts)-1>(std::forward_as_tuple(std::forward<Ts>(args)...))) 9265 { 9266 return std::get<sizeof...(Ts)-1>(std::forward_as_tuple(std::forward<Ts>(args)...)); 9267 } 9268 9269 } // detail 9270 9271 template<typename Value, typename K1, typename K2, typename K3, typename ... Ks> 9272 auto find_or(Value&& v, const K1& k1, const K2& k2, K3&& k3, Ks&& ... keys) noexcept 9273 -> cxx::enable_if_t< 9274 detail::is_basic_value<cxx::remove_cvref_t<Value>>::value, 9275 decltype(find_or(v, k2, std::forward<K3>(k3), std::forward<Ks>(keys)...)) 9276 > 9277 { 9278 try 9279 { 9280 return find_or(v.at(k1), k2, std::forward<K3>(k3), std::forward<Ks>(keys)...); 9281 } 9282 catch(...) 9283 { 9284 return detail::last_one(k3, keys...); 9285 } 9286 } 9287 9288 template<typename T, typename TC, typename K1, typename K2, typename K3, typename ... Ks> 9289 T find_or(const basic_value<TC>& v, const K1& k1, const K2& k2, const K3& k3, const Ks& ... keys) noexcept 9290 { 9291 try 9292 { 9293 return find_or<T>(v.at(k1), k2, k3, keys...); 9294 } 9295 catch(...) 9296 { 9297 return static_cast<T>(detail::last_one(k3, keys...)); 9298 } 9299 } 9300 9301 } // toml 9302 #endif // TOML11_FIND_HPP 9303 #ifndef TOML11_CONVERSION_HPP 9304 #define TOML11_CONVERSION_HPP 9305 9306 9307 #if defined(TOML11_HAS_OPTIONAL) 9308 9309 #include <optional> 9310 9311 namespace toml 9312 { 9313 namespace detail 9314 { 9315 9316 template<typename T> 9317 inline constexpr bool is_optional_v = false; 9318 9319 template<typename T> 9320 inline constexpr bool is_optional_v<std::optional<T>> = true; 9321 9322 template<typename T, typename TC> 9323 void find_member_variable_from_value(T& obj, const basic_value<TC>& v, const char* var_name) 9324 { 9325 if constexpr(is_optional_v<T>) 9326 { 9327 if(v.contains(var_name)) 9328 { 9329 obj = toml::find<typename T::value_type>(v, var_name); 9330 } 9331 else 9332 { 9333 obj = std::nullopt; 9334 } 9335 } 9336 else 9337 { 9338 obj = toml::find<T>(v, var_name); 9339 } 9340 } 9341 9342 template<typename T, typename TC> 9343 void assign_member_variable_to_value(const T& obj, basic_value<TC>& v, const char* var_name) 9344 { 9345 if constexpr(is_optional_v<T>) 9346 { 9347 if(obj.has_value()) 9348 { 9349 v[var_name] = obj.value(); 9350 } 9351 } 9352 else 9353 { 9354 v[var_name] = obj; 9355 } 9356 } 9357 9358 } // detail 9359 } // toml 9360 9361 #else 9362 9363 namespace toml 9364 { 9365 namespace detail 9366 { 9367 9368 template<typename T, typename TC> 9369 void find_member_variable_from_value(T& obj, const basic_value<TC>& v, const char* var_name) 9370 { 9371 obj = toml::find<T>(v, var_name); 9372 } 9373 9374 template<typename T, typename TC> 9375 void assign_member_variable_to_value(const T& obj, basic_value<TC>& v, const char* var_name) 9376 { 9377 v[var_name] = obj; 9378 } 9379 9380 } // detail 9381 } // toml 9382 9383 #endif // optional 9384 9385 // use it in the following way. 9386 // ```cpp 9387 // namespace foo 9388 // { 9389 // struct Foo 9390 // { 9391 // std::string s; 9392 // double d; 9393 // int i; 9394 // }; 9395 // } // foo 9396 // 9397 // TOML11_DEFINE_CONVERSION_NON_INTRUSIVE(foo::Foo, s, d, i) 9398 // ``` 9399 // 9400 // And then you can use `toml::get<foo::Foo>(v)` and `toml::find<foo::Foo>(file, "foo");` 9401 // 9402 9403 #define TOML11_STRINGIZE_AUX(x) #x 9404 #define TOML11_STRINGIZE(x) TOML11_STRINGIZE_AUX(x) 9405 9406 #define TOML11_CONCATENATE_AUX(x, y) x##y 9407 #define TOML11_CONCATENATE(x, y) TOML11_CONCATENATE_AUX(x, y) 9408 9409 // ============================================================================ 9410 // TOML11_DEFINE_CONVERSION_NON_INTRUSIVE 9411 9412 #ifndef TOML11_WITHOUT_DEFINE_NON_INTRUSIVE 9413 9414 // ---------------------------------------------------------------------------- 9415 // TOML11_ARGS_SIZE 9416 9417 #define TOML11_INDEX_RSEQ() \ 9418 32, 31, 30, 29, 28, 27, 26, 25, 24, 23, 22, 21, 20, 19, 18, 17, \ 9419 16, 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0 9420 #define TOML11_ARGS_SIZE_IMPL(\ 9421 ARG1, ARG2, ARG3, ARG4, ARG5, ARG6, ARG7, ARG8, ARG9, ARG10, \ 9422 ARG11, ARG12, ARG13, ARG14, ARG15, ARG16, ARG17, ARG18, ARG19, ARG20, \ 9423 ARG21, ARG22, ARG23, ARG24, ARG25, ARG26, ARG27, ARG28, ARG29, ARG30, \ 9424 ARG31, ARG32, N, ...) N 9425 #define TOML11_ARGS_SIZE_AUX(...) TOML11_ARGS_SIZE_IMPL(__VA_ARGS__) 9426 #define TOML11_ARGS_SIZE(...) TOML11_ARGS_SIZE_AUX(__VA_ARGS__, TOML11_INDEX_RSEQ()) 9427 9428 // ---------------------------------------------------------------------------- 9429 // TOML11_FOR_EACH_VA_ARGS 9430 9431 #define TOML11_FOR_EACH_VA_ARGS_AUX_1( FUNCTOR, ARG1 ) FUNCTOR(ARG1) 9432 #define TOML11_FOR_EACH_VA_ARGS_AUX_2( FUNCTOR, ARG1, ...) FUNCTOR(ARG1) TOML11_FOR_EACH_VA_ARGS_AUX_1( FUNCTOR, __VA_ARGS__) 9433 #define TOML11_FOR_EACH_VA_ARGS_AUX_3( FUNCTOR, ARG1, ...) FUNCTOR(ARG1) TOML11_FOR_EACH_VA_ARGS_AUX_2( FUNCTOR, __VA_ARGS__) 9434 #define TOML11_FOR_EACH_VA_ARGS_AUX_4( FUNCTOR, ARG1, ...) FUNCTOR(ARG1) TOML11_FOR_EACH_VA_ARGS_AUX_3( FUNCTOR, __VA_ARGS__) 9435 #define TOML11_FOR_EACH_VA_ARGS_AUX_5( FUNCTOR, ARG1, ...) FUNCTOR(ARG1) TOML11_FOR_EACH_VA_ARGS_AUX_4( FUNCTOR, __VA_ARGS__) 9436 #define TOML11_FOR_EACH_VA_ARGS_AUX_6( FUNCTOR, ARG1, ...) FUNCTOR(ARG1) TOML11_FOR_EACH_VA_ARGS_AUX_5( FUNCTOR, __VA_ARGS__) 9437 #define TOML11_FOR_EACH_VA_ARGS_AUX_7( FUNCTOR, ARG1, ...) FUNCTOR(ARG1) TOML11_FOR_EACH_VA_ARGS_AUX_6( FUNCTOR, __VA_ARGS__) 9438 #define TOML11_FOR_EACH_VA_ARGS_AUX_8( FUNCTOR, ARG1, ...) FUNCTOR(ARG1) TOML11_FOR_EACH_VA_ARGS_AUX_7( FUNCTOR, __VA_ARGS__) 9439 #define TOML11_FOR_EACH_VA_ARGS_AUX_9( FUNCTOR, ARG1, ...) FUNCTOR(ARG1) TOML11_FOR_EACH_VA_ARGS_AUX_8( FUNCTOR, __VA_ARGS__) 9440 #define TOML11_FOR_EACH_VA_ARGS_AUX_10(FUNCTOR, ARG1, ...) FUNCTOR(ARG1) TOML11_FOR_EACH_VA_ARGS_AUX_9( FUNCTOR, __VA_ARGS__) 9441 #define TOML11_FOR_EACH_VA_ARGS_AUX_11(FUNCTOR, ARG1, ...) FUNCTOR(ARG1) TOML11_FOR_EACH_VA_ARGS_AUX_10(FUNCTOR, __VA_ARGS__) 9442 #define TOML11_FOR_EACH_VA_ARGS_AUX_12(FUNCTOR, ARG1, ...) FUNCTOR(ARG1) TOML11_FOR_EACH_VA_ARGS_AUX_11(FUNCTOR, __VA_ARGS__) 9443 #define TOML11_FOR_EACH_VA_ARGS_AUX_13(FUNCTOR, ARG1, ...) FUNCTOR(ARG1) TOML11_FOR_EACH_VA_ARGS_AUX_12(FUNCTOR, __VA_ARGS__) 9444 #define TOML11_FOR_EACH_VA_ARGS_AUX_14(FUNCTOR, ARG1, ...) FUNCTOR(ARG1) TOML11_FOR_EACH_VA_ARGS_AUX_13(FUNCTOR, __VA_ARGS__) 9445 #define TOML11_FOR_EACH_VA_ARGS_AUX_15(FUNCTOR, ARG1, ...) FUNCTOR(ARG1) TOML11_FOR_EACH_VA_ARGS_AUX_14(FUNCTOR, __VA_ARGS__) 9446 #define TOML11_FOR_EACH_VA_ARGS_AUX_16(FUNCTOR, ARG1, ...) FUNCTOR(ARG1) TOML11_FOR_EACH_VA_ARGS_AUX_15(FUNCTOR, __VA_ARGS__) 9447 #define TOML11_FOR_EACH_VA_ARGS_AUX_17(FUNCTOR, ARG1, ...) FUNCTOR(ARG1) TOML11_FOR_EACH_VA_ARGS_AUX_16(FUNCTOR, __VA_ARGS__) 9448 #define TOML11_FOR_EACH_VA_ARGS_AUX_18(FUNCTOR, ARG1, ...) FUNCTOR(ARG1) TOML11_FOR_EACH_VA_ARGS_AUX_17(FUNCTOR, __VA_ARGS__) 9449 #define TOML11_FOR_EACH_VA_ARGS_AUX_19(FUNCTOR, ARG1, ...) FUNCTOR(ARG1) TOML11_FOR_EACH_VA_ARGS_AUX_18(FUNCTOR, __VA_ARGS__) 9450 #define TOML11_FOR_EACH_VA_ARGS_AUX_20(FUNCTOR, ARG1, ...) FUNCTOR(ARG1) TOML11_FOR_EACH_VA_ARGS_AUX_19(FUNCTOR, __VA_ARGS__) 9451 #define TOML11_FOR_EACH_VA_ARGS_AUX_21(FUNCTOR, ARG1, ...) FUNCTOR(ARG1) TOML11_FOR_EACH_VA_ARGS_AUX_20(FUNCTOR, __VA_ARGS__) 9452 #define TOML11_FOR_EACH_VA_ARGS_AUX_22(FUNCTOR, ARG1, ...) FUNCTOR(ARG1) TOML11_FOR_EACH_VA_ARGS_AUX_21(FUNCTOR, __VA_ARGS__) 9453 #define TOML11_FOR_EACH_VA_ARGS_AUX_23(FUNCTOR, ARG1, ...) FUNCTOR(ARG1) TOML11_FOR_EACH_VA_ARGS_AUX_22(FUNCTOR, __VA_ARGS__) 9454 #define TOML11_FOR_EACH_VA_ARGS_AUX_24(FUNCTOR, ARG1, ...) FUNCTOR(ARG1) TOML11_FOR_EACH_VA_ARGS_AUX_23(FUNCTOR, __VA_ARGS__) 9455 #define TOML11_FOR_EACH_VA_ARGS_AUX_25(FUNCTOR, ARG1, ...) FUNCTOR(ARG1) TOML11_FOR_EACH_VA_ARGS_AUX_24(FUNCTOR, __VA_ARGS__) 9456 #define TOML11_FOR_EACH_VA_ARGS_AUX_26(FUNCTOR, ARG1, ...) FUNCTOR(ARG1) TOML11_FOR_EACH_VA_ARGS_AUX_25(FUNCTOR, __VA_ARGS__) 9457 #define TOML11_FOR_EACH_VA_ARGS_AUX_27(FUNCTOR, ARG1, ...) FUNCTOR(ARG1) TOML11_FOR_EACH_VA_ARGS_AUX_26(FUNCTOR, __VA_ARGS__) 9458 #define TOML11_FOR_EACH_VA_ARGS_AUX_28(FUNCTOR, ARG1, ...) FUNCTOR(ARG1) TOML11_FOR_EACH_VA_ARGS_AUX_27(FUNCTOR, __VA_ARGS__) 9459 #define TOML11_FOR_EACH_VA_ARGS_AUX_29(FUNCTOR, ARG1, ...) FUNCTOR(ARG1) TOML11_FOR_EACH_VA_ARGS_AUX_28(FUNCTOR, __VA_ARGS__) 9460 #define TOML11_FOR_EACH_VA_ARGS_AUX_30(FUNCTOR, ARG1, ...) FUNCTOR(ARG1) TOML11_FOR_EACH_VA_ARGS_AUX_29(FUNCTOR, __VA_ARGS__) 9461 #define TOML11_FOR_EACH_VA_ARGS_AUX_31(FUNCTOR, ARG1, ...) FUNCTOR(ARG1) TOML11_FOR_EACH_VA_ARGS_AUX_30(FUNCTOR, __VA_ARGS__) 9462 #define TOML11_FOR_EACH_VA_ARGS_AUX_32(FUNCTOR, ARG1, ...) FUNCTOR(ARG1) TOML11_FOR_EACH_VA_ARGS_AUX_31(FUNCTOR, __VA_ARGS__) 9463 9464 #define TOML11_FOR_EACH_VA_ARGS(FUNCTOR, ...)\ 9465 TOML11_CONCATENATE(TOML11_FOR_EACH_VA_ARGS_AUX_, TOML11_ARGS_SIZE(__VA_ARGS__))(FUNCTOR, __VA_ARGS__) 9466 9467 9468 #define TOML11_FIND_MEMBER_VARIABLE_FROM_VALUE(VAR_NAME)\ 9469 toml::detail::find_member_variable_from_value(obj.VAR_NAME, v, TOML11_STRINGIZE(VAR_NAME)); 9470 9471 #define TOML11_ASSIGN_MEMBER_VARIABLE_TO_VALUE(VAR_NAME)\ 9472 toml::detail::assign_member_variable_to_value(obj.VAR_NAME, v, TOML11_STRINGIZE(VAR_NAME)); 9473 9474 #define TOML11_DEFINE_CONVERSION_NON_INTRUSIVE(NAME, ...)\ 9475 namespace toml { \ 9476 template<> \ 9477 struct from<NAME> \ 9478 { \ 9479 template<typename TC> \ 9480 static NAME from_toml(const basic_value<TC>& v) \ 9481 { \ 9482 NAME obj; \ 9483 TOML11_FOR_EACH_VA_ARGS(TOML11_FIND_MEMBER_VARIABLE_FROM_VALUE, __VA_ARGS__) \ 9484 return obj; \ 9485 } \ 9486 }; \ 9487 template<> \ 9488 struct into<NAME> \ 9489 { \ 9490 template<typename TC> \ 9491 static basic_value<TC> into_toml(const NAME& obj) \ 9492 { \ 9493 ::toml::basic_value<TC> v = typename ::toml::basic_value<TC>::table_type{}; \ 9494 TOML11_FOR_EACH_VA_ARGS(TOML11_ASSIGN_MEMBER_VARIABLE_TO_VALUE, __VA_ARGS__) \ 9495 return v; \ 9496 } \ 9497 }; \ 9498 } /* toml */ 9499 9500 #endif// TOML11_WITHOUT_DEFINE_NON_INTRUSIVE 9501 9502 #endif // TOML11_CONVERSION_HPP 9503 #ifndef TOML11_CONTEXT_HPP 9504 #define TOML11_CONTEXT_HPP 9505 9506 9507 #include <vector> 9508 9509 namespace toml 9510 { 9511 namespace detail 9512 { 9513 9514 template<typename TypeConfig> 9515 class context 9516 { 9517 public: 9518 9519 explicit context(const spec& toml_spec) 9520 : toml_spec_(toml_spec), errors_{} 9521 {} 9522 9523 bool has_error() const noexcept {return !errors_.empty();} 9524 9525 std::vector<error_info> const& errors() const noexcept {return errors_;} 9526 9527 semantic_version& toml_version() noexcept {return toml_spec_.version;} 9528 semantic_version const& toml_version() const noexcept {return toml_spec_.version;} 9529 9530 spec& toml_spec() noexcept {return toml_spec_;} 9531 spec const& toml_spec() const noexcept {return toml_spec_;} 9532 9533 void report_error(error_info err) 9534 { 9535 this->errors_.push_back(std::move(err)); 9536 } 9537 9538 error_info pop_last_error() 9539 { 9540 assert( ! errors_.empty()); 9541 auto e = std::move(errors_.back()); 9542 errors_.pop_back(); 9543 return e; 9544 } 9545 9546 private: 9547 9548 spec toml_spec_; 9549 std::vector<error_info> errors_; 9550 }; 9551 9552 } // detail 9553 } // toml 9554 9555 #if defined(TOML11_COMPILE_SOURCES) 9556 namespace toml 9557 { 9558 struct type_config; 9559 struct ordered_type_config; 9560 namespace detail 9561 { 9562 extern template class context<::toml::type_config>; 9563 extern template class context<::toml::ordered_type_config>; 9564 } // detail 9565 } // toml 9566 #endif // TOML11_COMPILE_SOURCES 9567 9568 #endif // TOML11_CONTEXT_HPP 9569 #ifndef TOML11_SCANNER_HPP 9570 #define TOML11_SCANNER_HPP 9571 9572 #ifndef TOML11_SCANNER_FWD_HPP 9573 #define TOML11_SCANNER_FWD_HPP 9574 9575 9576 #include <memory> 9577 #include <string> 9578 #include <utility> 9579 #include <vector> 9580 9581 #include <cassert> 9582 #include <cstdio> 9583 #include <cctype> 9584 9585 namespace toml 9586 { 9587 namespace detail 9588 { 9589 9590 class scanner_base 9591 { 9592 public: 9593 virtual ~scanner_base() = default; 9594 virtual region scan(location& loc) const = 0; 9595 virtual scanner_base* clone() const = 0; 9596 9597 // returns expected character or set of characters or literal. 9598 // to show the error location, it changes loc (in `sequence`, especially). 9599 virtual std::string expected_chars(location& loc) const = 0; 9600 virtual std::string name() const = 0; 9601 }; 9602 9603 // make `scanner*` copyable 9604 struct scanner_storage 9605 { 9606 template<typename Scanner, cxx::enable_if_t< 9607 std::is_base_of<scanner_base, cxx::remove_cvref_t<Scanner>>::value, 9608 std::nullptr_t> = nullptr> 9609 explicit scanner_storage(Scanner&& s) 9610 : scanner_(cxx::make_unique<cxx::remove_cvref_t<Scanner>>(std::forward<Scanner>(s))) 9611 {} 9612 ~scanner_storage() = default; 9613 9614 scanner_storage(const scanner_storage& other); 9615 scanner_storage& operator=(const scanner_storage& other); 9616 scanner_storage(scanner_storage&&) = default; 9617 scanner_storage& operator=(scanner_storage&&) = default; 9618 9619 bool is_ok() const noexcept {return static_cast<bool>(scanner_);} 9620 9621 region scan(location& loc) const; 9622 9623 std::string expected_chars(location& loc) const; 9624 9625 scanner_base& get() const noexcept; 9626 9627 std::string name() const; 9628 9629 private: 9630 9631 std::unique_ptr<scanner_base> scanner_; 9632 }; 9633 9634 // ---------------------------------------------------------------------------- 9635 9636 class character final : public scanner_base 9637 { 9638 public: 9639 9640 using char_type = location::char_type; 9641 9642 public: 9643 9644 explicit character(const char_type c) noexcept 9645 : value_(c) 9646 {} 9647 ~character() override = default; 9648 9649 region scan(location& loc) const override; 9650 9651 std::string expected_chars(location&) const override; 9652 9653 scanner_base* clone() const override; 9654 9655 std::string name() const override; 9656 9657 private: 9658 char_type value_; 9659 }; 9660 9661 // ---------------------------------------------------------------------------- 9662 9663 class character_either final : public scanner_base 9664 { 9665 public: 9666 9667 using char_type = location::char_type; 9668 9669 public: 9670 9671 explicit character_either(std::initializer_list<char_type> cs) noexcept 9672 : chars_(std::move(cs)) 9673 { 9674 assert(! this->chars_.empty()); 9675 } 9676 9677 template<std::size_t N> 9678 explicit character_either(const char (&cs)[N]) noexcept 9679 : chars_(N-1, '\0') 9680 { 9681 static_assert(N >= 1, ""); 9682 for(std::size_t i=0; i+1<N; ++i) 9683 { 9684 chars_.at(i) = char_type(cs[i]); 9685 } 9686 } 9687 ~character_either() override = default; 9688 9689 region scan(location& loc) const override; 9690 9691 std::string expected_chars(location&) const override; 9692 9693 scanner_base* clone() const override; 9694 9695 void push_back(const char_type c); 9696 9697 std::string name() const override; 9698 9699 private: 9700 std::vector<char_type> chars_; 9701 }; 9702 9703 // ---------------------------------------------------------------------------- 9704 9705 class character_in_range final : public scanner_base 9706 { 9707 public: 9708 9709 using char_type = location::char_type; 9710 9711 public: 9712 9713 explicit character_in_range(const char_type from, const char_type to) noexcept 9714 : from_(from), to_(to) 9715 {} 9716 ~character_in_range() override = default; 9717 9718 region scan(location& loc) const override; 9719 9720 std::string expected_chars(location&) const override; 9721 9722 scanner_base* clone() const override; 9723 9724 std::string name() const override; 9725 9726 private: 9727 char_type from_; 9728 char_type to_; 9729 }; 9730 9731 // ---------------------------------------------------------------------------- 9732 9733 class literal final : public scanner_base 9734 { 9735 public: 9736 9737 using char_type = location::char_type; 9738 9739 public: 9740 9741 template<std::size_t N> 9742 explicit literal(const char (&cs)[N]) noexcept 9743 : value_(cs), size_(N-1) // remove null character at the end 9744 {} 9745 ~literal() override = default; 9746 9747 region scan(location& loc) const override; 9748 9749 std::string expected_chars(location&) const override; 9750 9751 scanner_base* clone() const override; 9752 9753 std::string name() const override; 9754 9755 private: 9756 const char* value_; 9757 std::size_t size_; 9758 }; 9759 9760 // ---------------------------------------------------------------------------- 9761 9762 class sequence final: public scanner_base 9763 { 9764 public: 9765 using char_type = location::char_type; 9766 9767 public: 9768 9769 template<typename ... Ts> 9770 explicit sequence(Ts&& ... args) 9771 { 9772 push_back_all(std::forward<Ts>(args)...); 9773 } 9774 sequence(const sequence&) = default; 9775 sequence(sequence&&) = default; 9776 sequence& operator=(const sequence&) = default; 9777 sequence& operator=(sequence&&) = default; 9778 ~sequence() override = default; 9779 9780 region scan(location& loc) const override; 9781 9782 std::string expected_chars(location& loc) const override; 9783 9784 scanner_base* clone() const override; 9785 9786 template<typename Scanner> 9787 void push_back(Scanner&& other_scanner) 9788 { 9789 this->others_.emplace_back(std::forward<Scanner>(other_scanner)); 9790 } 9791 9792 std::string name() const override; 9793 9794 private: 9795 9796 void push_back_all() 9797 { 9798 return; 9799 } 9800 template<typename T, typename ... Ts> 9801 void push_back_all(T&& head, Ts&& ... args) 9802 { 9803 others_.emplace_back(std::forward<T>(head)); 9804 push_back_all(std::forward<Ts>(args)...); 9805 return; 9806 } 9807 9808 private: 9809 std::vector<scanner_storage> others_; 9810 }; 9811 9812 // ---------------------------------------------------------------------------- 9813 9814 class either final: public scanner_base 9815 { 9816 public: 9817 using char_type = location::char_type; 9818 9819 public: 9820 9821 template<typename ... Ts> 9822 explicit either(Ts&& ... args) 9823 { 9824 push_back_all(std::forward<Ts>(args)...); 9825 } 9826 either(const either&) = default; 9827 either(either&&) = default; 9828 either& operator=(const either&) = default; 9829 either& operator=(either&&) = default; 9830 ~either() override = default; 9831 9832 region scan(location& loc) const override; 9833 9834 std::string expected_chars(location& loc) const override; 9835 9836 scanner_base* clone() const override; 9837 9838 template<typename Scanner> 9839 void push_back(Scanner&& other_scanner) 9840 { 9841 this->others_.emplace_back(std::forward<Scanner>(other_scanner)); 9842 } 9843 9844 std::string name() const override; 9845 9846 private: 9847 9848 void push_back_all() 9849 { 9850 return; 9851 } 9852 template<typename T, typename ... Ts> 9853 void push_back_all(T&& head, Ts&& ... args) 9854 { 9855 others_.emplace_back(std::forward<T>(head)); 9856 push_back_all(std::forward<Ts>(args)...); 9857 return; 9858 } 9859 9860 private: 9861 std::vector<scanner_storage> others_; 9862 }; 9863 9864 // ---------------------------------------------------------------------------- 9865 9866 class repeat_exact final: public scanner_base 9867 { 9868 public: 9869 using char_type = location::char_type; 9870 9871 public: 9872 9873 template<typename Scanner> 9874 repeat_exact(const std::size_t length, Scanner&& other) 9875 : length_(length), other_(std::forward<Scanner>(other)) 9876 {} 9877 repeat_exact(const repeat_exact&) = default; 9878 repeat_exact(repeat_exact&&) = default; 9879 repeat_exact& operator=(const repeat_exact&) = default; 9880 repeat_exact& operator=(repeat_exact&&) = default; 9881 ~repeat_exact() override = default; 9882 9883 region scan(location& loc) const override; 9884 9885 std::string expected_chars(location& loc) const override; 9886 9887 scanner_base* clone() const override; 9888 9889 std::string name() const override; 9890 9891 private: 9892 std::size_t length_; 9893 scanner_storage other_; 9894 }; 9895 9896 // ---------------------------------------------------------------------------- 9897 9898 class repeat_at_least final: public scanner_base 9899 { 9900 public: 9901 using char_type = location::char_type; 9902 9903 public: 9904 9905 template<typename Scanner> 9906 repeat_at_least(const std::size_t length, Scanner&& s) 9907 : length_(length), other_(std::forward<Scanner>(s)) 9908 {} 9909 repeat_at_least(const repeat_at_least&) = default; 9910 repeat_at_least(repeat_at_least&&) = default; 9911 repeat_at_least& operator=(const repeat_at_least&) = default; 9912 repeat_at_least& operator=(repeat_at_least&&) = default; 9913 ~repeat_at_least() override = default; 9914 9915 region scan(location& loc) const override; 9916 9917 std::string expected_chars(location& loc) const override; 9918 9919 scanner_base* clone() const override; 9920 9921 std::string name() const override; 9922 9923 private: 9924 std::size_t length_; 9925 scanner_storage other_; 9926 }; 9927 9928 // ---------------------------------------------------------------------------- 9929 9930 class maybe final: public scanner_base 9931 { 9932 public: 9933 using char_type = location::char_type; 9934 9935 public: 9936 9937 template<typename Scanner> 9938 explicit maybe(Scanner&& s) 9939 : other_(std::forward<Scanner>(s)) 9940 {} 9941 maybe(const maybe&) = default; 9942 maybe(maybe&&) = default; 9943 maybe& operator=(const maybe&) = default; 9944 maybe& operator=(maybe&&) = default; 9945 ~maybe() override = default; 9946 9947 region scan(location& loc) const override; 9948 9949 std::string expected_chars(location&) const override; 9950 9951 scanner_base* clone() const override; 9952 9953 std::string name() const override; 9954 9955 private: 9956 scanner_storage other_; 9957 }; 9958 9959 } // detail 9960 } // toml 9961 #endif // TOML11_SCANNER_FWD_HPP 9962 9963 #if ! defined(TOML11_COMPILE_SOURCES) 9964 #ifndef TOML11_SCANNER_IMPL_HPP 9965 #define TOML11_SCANNER_IMPL_HPP 9966 9967 9968 namespace toml 9969 { 9970 namespace detail 9971 { 9972 9973 TOML11_INLINE scanner_storage::scanner_storage(const scanner_storage& other) 9974 : scanner_(nullptr) 9975 { 9976 if(other.is_ok()) 9977 { 9978 scanner_.reset(other.get().clone()); 9979 } 9980 } 9981 TOML11_INLINE scanner_storage& scanner_storage::operator=(const scanner_storage& other) 9982 { 9983 if(this == std::addressof(other)) {return *this;} 9984 if(other.is_ok()) 9985 { 9986 scanner_.reset(other.get().clone()); 9987 } 9988 return *this; 9989 } 9990 9991 TOML11_INLINE region scanner_storage::scan(location& loc) const 9992 { 9993 assert(this->is_ok()); 9994 return this->scanner_->scan(loc); 9995 } 9996 9997 TOML11_INLINE std::string scanner_storage::expected_chars(location& loc) const 9998 { 9999 assert(this->is_ok()); 10000 return this->scanner_->expected_chars(loc); 10001 } 10002 10003 TOML11_INLINE scanner_base& scanner_storage::get() const noexcept 10004 { 10005 assert(this->is_ok()); 10006 return *scanner_; 10007 } 10008 10009 TOML11_INLINE std::string scanner_storage::name() const 10010 { 10011 assert(this->is_ok()); 10012 return this->scanner_->name(); 10013 } 10014 10015 // ---------------------------------------------------------------------------- 10016 10017 TOML11_INLINE region character::scan(location& loc) const 10018 { 10019 if(loc.eof()) {return region{};} 10020 10021 if(loc.current() == this->value_) 10022 { 10023 const auto first = loc; 10024 loc.advance(1); 10025 return region(first, loc); 10026 } 10027 return region{}; 10028 } 10029 10030 TOML11_INLINE std::string character::expected_chars(location&) const 10031 { 10032 return show_char(value_); 10033 } 10034 10035 TOML11_INLINE scanner_base* character::clone() const 10036 { 10037 return new character(*this); 10038 } 10039 10040 TOML11_INLINE std::string character::name() const 10041 { 10042 return "character{" + show_char(value_) + "}"; 10043 } 10044 10045 // ---------------------------------------------------------------------------- 10046 10047 TOML11_INLINE region character_either::scan(location& loc) const 10048 { 10049 if(loc.eof()) {return region{};} 10050 10051 for(const auto c : this->chars_) 10052 { 10053 if(loc.current() == c) 10054 { 10055 const auto first = loc; 10056 loc.advance(1); 10057 return region(first, loc); 10058 } 10059 } 10060 return region{}; 10061 } 10062 10063 TOML11_INLINE std::string character_either::expected_chars(location&) const 10064 { 10065 assert( ! chars_.empty()); 10066 10067 std::string expected; 10068 if(chars_.size() == 1) 10069 { 10070 expected += show_char(chars_.at(0)); 10071 } 10072 else if(chars_.size() == 2) 10073 { 10074 expected += show_char(chars_.at(0)) + " or " + show_char(chars_.at(1)); 10075 } 10076 else 10077 { 10078 for(std::size_t i=0; i<chars_.size(); ++i) 10079 { 10080 if(i != 0) 10081 { 10082 expected += ", "; 10083 } 10084 if(i + 1 == chars_.size()) 10085 { 10086 expected += "or "; 10087 } 10088 expected += show_char(chars_.at(i)); 10089 } 10090 } 10091 return expected; 10092 } 10093 10094 TOML11_INLINE scanner_base* character_either::clone() const 10095 { 10096 return new character_either(*this); 10097 } 10098 10099 TOML11_INLINE void character_either::push_back(const char_type c) 10100 { 10101 chars_.push_back(c); 10102 } 10103 10104 TOML11_INLINE std::string character_either::name() const 10105 { 10106 std::string n("character_either{"); 10107 for(const auto c : this->chars_) 10108 { 10109 n += show_char(c); 10110 n += ", "; 10111 } 10112 if( ! this->chars_.empty()) 10113 { 10114 n.pop_back(); 10115 n.pop_back(); 10116 } 10117 n += "}"; 10118 return n; 10119 } 10120 10121 // ---------------------------------------------------------------------------- 10122 // character_in_range 10123 10124 TOML11_INLINE region character_in_range::scan(location& loc) const 10125 { 10126 if(loc.eof()) {return region{};} 10127 10128 const auto curr = loc.current(); 10129 if(this->from_ <= curr && curr <= this->to_) 10130 { 10131 const auto first = loc; 10132 loc.advance(1); 10133 return region(first, loc); 10134 } 10135 return region{}; 10136 } 10137 10138 TOML11_INLINE std::string character_in_range::expected_chars(location&) const 10139 { 10140 std::string expected("from `"); 10141 expected += show_char(from_); 10142 expected += "` to `"; 10143 expected += show_char(to_); 10144 expected += "`"; 10145 return expected; 10146 } 10147 10148 TOML11_INLINE scanner_base* character_in_range::clone() const 10149 { 10150 return new character_in_range(*this); 10151 } 10152 10153 TOML11_INLINE std::string character_in_range::name() const 10154 { 10155 return "character_in_range{" + show_char(from_) + "," + show_char(to_) + "}"; 10156 } 10157 10158 // ---------------------------------------------------------------------------- 10159 // literal 10160 10161 TOML11_INLINE region literal::scan(location& loc) const 10162 { 10163 const auto first = loc; 10164 for(std::size_t i=0; i<size_; ++i) 10165 { 10166 if(loc.eof() || char_type(value_[i]) != loc.current()) 10167 { 10168 loc = first; 10169 return region{}; 10170 } 10171 loc.advance(1); 10172 } 10173 return region(first, loc); 10174 } 10175 10176 TOML11_INLINE std::string literal::expected_chars(location&) const 10177 { 10178 return std::string(value_); 10179 } 10180 10181 TOML11_INLINE scanner_base* literal::clone() const 10182 { 10183 return new literal(*this); 10184 } 10185 10186 TOML11_INLINE std::string literal::name() const 10187 { 10188 return std::string("literal{") + std::string(value_, size_) + "}"; 10189 } 10190 10191 // ---------------------------------------------------------------------------- 10192 // sequence 10193 10194 TOML11_INLINE region sequence::scan(location& loc) const 10195 { 10196 const auto first = loc; 10197 for(const auto& other : others_) 10198 { 10199 const auto reg = other.scan(loc); 10200 if( ! reg.is_ok()) 10201 { 10202 loc = first; 10203 return region{}; 10204 } 10205 } 10206 return region(first, loc); 10207 } 10208 10209 TOML11_INLINE std::string sequence::expected_chars(location& loc) const 10210 { 10211 const auto first = loc; 10212 for(const auto& other : others_) 10213 { 10214 const auto reg = other.scan(loc); 10215 if( ! reg.is_ok()) 10216 { 10217 return other.expected_chars(loc); 10218 } 10219 } 10220 assert(false); 10221 return ""; // XXX 10222 } 10223 10224 TOML11_INLINE scanner_base* sequence::clone() const 10225 { 10226 return new sequence(*this); 10227 } 10228 10229 TOML11_INLINE std::string sequence::name() const 10230 { 10231 std::string n("sequence{"); 10232 for(const auto& other : others_) 10233 { 10234 n += other.name(); 10235 n += ", "; 10236 } 10237 if( ! this->others_.empty()) 10238 { 10239 n.pop_back(); 10240 n.pop_back(); 10241 } 10242 n += "}"; 10243 return n; 10244 } 10245 10246 // ---------------------------------------------------------------------------- 10247 // either 10248 10249 TOML11_INLINE region either::scan(location& loc) const 10250 { 10251 for(const auto& other : others_) 10252 { 10253 const auto reg = other.scan(loc); 10254 if(reg.is_ok()) 10255 { 10256 return reg; 10257 } 10258 } 10259 return region{}; 10260 } 10261 10262 TOML11_INLINE std::string either::expected_chars(location& loc) const 10263 { 10264 assert( ! others_.empty()); 10265 10266 std::string expected = others_.at(0).expected_chars(loc); 10267 if(others_.size() == 2) 10268 { 10269 expected += " or "; 10270 expected += others_.at(1).expected_chars(loc); 10271 } 10272 else 10273 { 10274 for(std::size_t i=1; i<others_.size(); ++i) 10275 { 10276 expected += ", "; 10277 if(i + 1 == others_.size()) 10278 { 10279 expected += "or "; 10280 } 10281 expected += others_.at(i).expected_chars(loc); 10282 } 10283 } 10284 return expected; 10285 } 10286 10287 TOML11_INLINE scanner_base* either::clone() const 10288 { 10289 return new either(*this); 10290 } 10291 10292 TOML11_INLINE std::string either::name() const 10293 { 10294 std::string n("either{"); 10295 for(const auto& other : others_) 10296 { 10297 n += other.name(); 10298 n += ", "; 10299 } 10300 if( ! this->others_.empty()) 10301 { 10302 n.pop_back(); 10303 n.pop_back(); 10304 } 10305 n += "}"; 10306 return n; 10307 } 10308 10309 // ---------------------------------------------------------------------------- 10310 // repeat_exact 10311 10312 TOML11_INLINE region repeat_exact::scan(location& loc) const 10313 { 10314 const auto first = loc; 10315 for(std::size_t i=0; i<length_; ++i) 10316 { 10317 const auto reg = other_.scan(loc); 10318 if( ! reg.is_ok()) 10319 { 10320 loc = first; 10321 return region{}; 10322 } 10323 } 10324 return region(first, loc); 10325 } 10326 10327 TOML11_INLINE std::string repeat_exact::expected_chars(location& loc) const 10328 { 10329 for(std::size_t i=0; i<length_; ++i) 10330 { 10331 const auto reg = other_.scan(loc); 10332 if( ! reg.is_ok()) 10333 { 10334 return other_.expected_chars(loc); 10335 } 10336 } 10337 assert(false); 10338 return ""; 10339 } 10340 10341 TOML11_INLINE scanner_base* repeat_exact::clone() const 10342 { 10343 return new repeat_exact(*this); 10344 } 10345 10346 TOML11_INLINE std::string repeat_exact::name() const 10347 { 10348 return "repeat_exact{" + std::to_string(length_) + ", " + other_.name() + "}"; 10349 } 10350 10351 // ---------------------------------------------------------------------------- 10352 // repeat_at_least 10353 10354 TOML11_INLINE region repeat_at_least::scan(location& loc) const 10355 { 10356 const auto first = loc; 10357 for(std::size_t i=0; i<length_; ++i) 10358 { 10359 const auto reg = other_.scan(loc); 10360 if( ! reg.is_ok()) 10361 { 10362 loc = first; 10363 return region{}; 10364 } 10365 } 10366 while( ! loc.eof()) 10367 { 10368 const auto checkpoint = loc; 10369 const auto reg = other_.scan(loc); 10370 if( ! reg.is_ok()) 10371 { 10372 loc = checkpoint; 10373 return region(first, loc); 10374 } 10375 } 10376 return region(first, loc); 10377 } 10378 10379 TOML11_INLINE std::string repeat_at_least::expected_chars(location& loc) const 10380 { 10381 for(std::size_t i=0; i<length_; ++i) 10382 { 10383 const auto reg = other_.scan(loc); 10384 if( ! reg.is_ok()) 10385 { 10386 return other_.expected_chars(loc); 10387 } 10388 } 10389 assert(false); 10390 return ""; 10391 } 10392 10393 TOML11_INLINE scanner_base* repeat_at_least::clone() const 10394 { 10395 return new repeat_at_least(*this); 10396 } 10397 10398 TOML11_INLINE std::string repeat_at_least::name() const 10399 { 10400 return "repeat_at_least{" + std::to_string(length_) + ", " + other_.name() + "}"; 10401 } 10402 10403 // ---------------------------------------------------------------------------- 10404 // maybe 10405 10406 TOML11_INLINE region maybe::scan(location& loc) const 10407 { 10408 const auto first = loc; 10409 const auto reg = other_.scan(loc); 10410 if( ! reg.is_ok()) 10411 { 10412 loc = first; 10413 } 10414 return region(first, loc); 10415 } 10416 10417 TOML11_INLINE std::string maybe::expected_chars(location&) const 10418 { 10419 return ""; 10420 } 10421 10422 TOML11_INLINE scanner_base* maybe::clone() const 10423 { 10424 return new maybe(*this); 10425 } 10426 10427 TOML11_INLINE std::string maybe::name() const 10428 { 10429 return "maybe{" + other_.name() + "}"; 10430 } 10431 10432 } // detail 10433 } // toml 10434 #endif // TOML11_SCANNER_IMPL_HPP 10435 #endif 10436 10437 #endif // TOML11_SCANNER_HPP 10438 #ifndef TOML11_SYNTAX_HPP 10439 #define TOML11_SYNTAX_HPP 10440 10441 #ifndef TOML11_SYNTAX_FWD_HPP 10442 #define TOML11_SYNTAX_FWD_HPP 10443 10444 10445 namespace toml 10446 { 10447 namespace detail 10448 { 10449 namespace syntax 10450 { 10451 10452 using char_type = location::char_type; 10453 10454 // =========================================================================== 10455 // UTF-8 10456 10457 // avoid redundant representation and out-of-unicode sequence 10458 10459 character_in_range utf8_1byte (const spec&); 10460 sequence utf8_2bytes(const spec&); 10461 sequence utf8_3bytes(const spec&); 10462 sequence utf8_4bytes(const spec&); 10463 10464 class non_ascii final : public scanner_base 10465 { 10466 public: 10467 10468 using char_type = location::char_type; 10469 10470 public: 10471 10472 explicit non_ascii(const spec& s) noexcept; 10473 ~non_ascii() override = default; 10474 10475 region scan(location& loc) const override 10476 { 10477 return scanner_.scan(loc); 10478 } 10479 10480 std::string expected_chars(location&) const override 10481 { 10482 return "non-ascii utf-8 bytes"; 10483 } 10484 10485 scanner_base* clone() const override 10486 { 10487 return new non_ascii(*this); 10488 } 10489 10490 std::string name() const override 10491 { 10492 return "non_ascii"; 10493 } 10494 10495 private: 10496 10497 either scanner_; 10498 }; 10499 10500 // =========================================================================== 10501 // Whitespace 10502 10503 character_either wschar(const spec&); 10504 10505 repeat_at_least ws(const spec& s); 10506 10507 // =========================================================================== 10508 // Newline 10509 10510 either newline(const spec&); 10511 10512 // =========================================================================== 10513 // Comments 10514 10515 either allowed_comment_char(const spec& s); 10516 10517 // XXX Note that it does not take newline 10518 sequence comment(const spec& s); 10519 10520 // =========================================================================== 10521 // Boolean 10522 10523 either boolean(const spec&); 10524 10525 // =========================================================================== 10526 // Integer 10527 10528 class digit final : public scanner_base 10529 { 10530 public: 10531 10532 using char_type = location::char_type; 10533 10534 public: 10535 10536 explicit digit(const spec&) noexcept; 10537 ~digit() override = default; 10538 10539 region scan(location& loc) const override 10540 { 10541 return scanner_.scan(loc); 10542 } 10543 10544 std::string expected_chars(location&) const override 10545 { 10546 return "digit [0-9]"; 10547 } 10548 10549 scanner_base* clone() const override 10550 { 10551 return new digit(*this); 10552 } 10553 10554 std::string name() const override 10555 { 10556 return "digit"; 10557 } 10558 10559 private: 10560 10561 character_in_range scanner_; 10562 }; 10563 10564 class alpha final : public scanner_base 10565 { 10566 public: 10567 10568 using char_type = location::char_type; 10569 10570 public: 10571 10572 explicit alpha(const spec&) noexcept; 10573 ~alpha() override = default; 10574 10575 region scan(location& loc) const override 10576 { 10577 return scanner_.scan(loc); 10578 } 10579 10580 std::string expected_chars(location&) const override 10581 { 10582 return "alpha [a-zA-Z]"; 10583 } 10584 10585 scanner_base* clone() const override 10586 { 10587 return new alpha(*this); 10588 } 10589 10590 std::string name() const override 10591 { 10592 return "alpha"; 10593 } 10594 10595 private: 10596 10597 either scanner_; 10598 }; 10599 10600 class hexdig final : public scanner_base 10601 { 10602 public: 10603 10604 using char_type = location::char_type; 10605 10606 public: 10607 10608 explicit hexdig(const spec& s) noexcept; 10609 ~hexdig() override = default; 10610 10611 region scan(location& loc) const override 10612 { 10613 return scanner_.scan(loc); 10614 } 10615 10616 std::string expected_chars(location&) const override 10617 { 10618 return "hex [0-9a-fA-F]"; 10619 } 10620 10621 scanner_base* clone() const override 10622 { 10623 return new hexdig(*this); 10624 } 10625 10626 std::string name() const override 10627 { 10628 return "hexdig"; 10629 } 10630 10631 private: 10632 10633 either scanner_; 10634 }; 10635 10636 sequence num_suffix(const spec& s); 10637 10638 sequence dec_int(const spec& s); 10639 sequence hex_int(const spec& s); 10640 sequence oct_int(const spec&); 10641 sequence bin_int(const spec&); 10642 either integer(const spec& s); 10643 10644 // =========================================================================== 10645 // Floating 10646 10647 sequence zero_prefixable_int(const spec& s); 10648 sequence fractional_part(const spec& s); 10649 sequence exponent_part(const spec& s); 10650 sequence hex_floating(const spec& s); 10651 either floating(const spec& s); 10652 10653 // =========================================================================== 10654 // Datetime 10655 10656 sequence local_date(const spec& s); 10657 sequence local_time(const spec& s); 10658 either time_offset(const spec& s); 10659 sequence full_time(const spec& s); 10660 character_either time_delim(const spec&); 10661 sequence local_datetime(const spec& s); 10662 sequence offset_datetime(const spec& s); 10663 10664 // =========================================================================== 10665 // String 10666 10667 sequence escaped(const spec& s); 10668 10669 either basic_char(const spec& s); 10670 10671 sequence basic_string(const spec& s); 10672 10673 // --------------------------------------------------------------------------- 10674 // multiline string 10675 10676 sequence escaped_newline(const spec& s); 10677 sequence ml_basic_string(const spec& s); 10678 10679 // --------------------------------------------------------------------------- 10680 // literal string 10681 10682 either literal_char(const spec& s); 10683 sequence literal_string(const spec& s); 10684 10685 sequence ml_literal_string(const spec& s); 10686 10687 either string(const spec& s); 10688 10689 // =========================================================================== 10690 // Keys 10691 10692 // to keep `expected_chars` simple 10693 class non_ascii_key_char final : public scanner_base 10694 { 10695 public: 10696 10697 using char_type = location::char_type; 10698 10699 private: 10700 10701 using in_range = character_in_range; // make definition short 10702 10703 public: 10704 10705 explicit non_ascii_key_char(const spec& s) noexcept; 10706 ~non_ascii_key_char() override = default; 10707 10708 region scan(location& loc) const override; 10709 10710 std::string expected_chars(location&) const override 10711 { 10712 return "bare key non-ASCII script"; 10713 } 10714 10715 scanner_base* clone() const override 10716 { 10717 return new non_ascii_key_char(*this); 10718 } 10719 10720 std::string name() const override 10721 { 10722 return "non-ASCII bare key"; 10723 } 10724 10725 private: 10726 10727 std::uint32_t read_utf8(location& loc) const; 10728 }; 10729 10730 10731 repeat_at_least unquoted_key(const spec& s); 10732 10733 either quoted_key(const spec& s); 10734 10735 either simple_key(const spec& s); 10736 10737 sequence dot_sep(const spec& s); 10738 10739 sequence dotted_key(const spec& s); 10740 10741 10742 class key final : public scanner_base 10743 { 10744 public: 10745 10746 using char_type = location::char_type; 10747 10748 public: 10749 10750 explicit key(const spec& s) noexcept; 10751 ~key() override = default; 10752 10753 region scan(location& loc) const override 10754 { 10755 return scanner_.scan(loc); 10756 } 10757 10758 std::string expected_chars(location&) const override 10759 { 10760 return "basic key([a-zA-Z0-9_-]) or quoted key(\" or ')"; 10761 } 10762 10763 scanner_base* clone() const override 10764 { 10765 return new key(*this); 10766 } 10767 10768 std::string name() const override 10769 { 10770 return "key"; 10771 } 10772 10773 private: 10774 10775 either scanner_; 10776 }; 10777 10778 sequence keyval_sep(const spec& s); 10779 10780 // =========================================================================== 10781 // Table key 10782 10783 sequence std_table(const spec& s); 10784 10785 sequence array_table(const spec& s); 10786 10787 // =========================================================================== 10788 // extension: null 10789 10790 literal null_value(const spec&); 10791 10792 } // namespace syntax 10793 } // namespace detail 10794 } // namespace toml 10795 #endif // TOML11_SYNTAX_FWD_HPP 10796 10797 #if ! defined(TOML11_COMPILE_SOURCES) 10798 #ifndef TOML11_SYNTAX_IMPL_HPP 10799 #define TOML11_SYNTAX_IMPL_HPP 10800 10801 10802 namespace toml 10803 { 10804 namespace detail 10805 { 10806 namespace syntax 10807 { 10808 10809 using char_type = location::char_type; 10810 10811 // =========================================================================== 10812 // UTF-8 10813 10814 // avoid redundant representation and out-of-unicode sequence 10815 10816 TOML11_INLINE character_in_range utf8_1byte(const spec&) 10817 { 10818 return character_in_range(0x00, 0x7F); 10819 } 10820 10821 TOML11_INLINE sequence utf8_2bytes(const spec&) 10822 { 10823 return sequence(character_in_range(0xC2, 0xDF), 10824 character_in_range(0x80, 0xBF)); 10825 } 10826 10827 TOML11_INLINE sequence utf8_3bytes(const spec&) 10828 { 10829 return sequence(/*1~2 bytes = */either( 10830 sequence(character (0xE0), character_in_range(0xA0, 0xBF)), 10831 sequence(character_in_range(0xE1, 0xEC), character_in_range(0x80, 0xBF)), 10832 sequence(character (0xED), character_in_range(0x80, 0x9F)), 10833 sequence(character_in_range(0xEE, 0xEF), character_in_range(0x80, 0xBF)) 10834 ), /*3rd byte = */ character_in_range(0x80, 0xBF)); 10835 } 10836 10837 TOML11_INLINE sequence utf8_4bytes(const spec&) 10838 { 10839 return sequence(/*1~2 bytes = */either( 10840 sequence(character (0xF0), character_in_range(0x90, 0xBF)), 10841 sequence(character_in_range(0xF1, 0xF3), character_in_range(0x80, 0xBF)), 10842 sequence(character (0xF4), character_in_range(0x80, 0x8F)) 10843 ), character_in_range(0x80, 0xBF), character_in_range(0x80, 0xBF)); 10844 } 10845 10846 TOML11_INLINE non_ascii::non_ascii(const spec& s) noexcept 10847 : scanner_(utf8_2bytes(s), utf8_3bytes(s), utf8_4bytes(s)) 10848 {} 10849 10850 10851 // =========================================================================== 10852 // Whitespace 10853 10854 TOML11_INLINE character_either wschar(const spec&) 10855 { 10856 return character_either{char_type(' '), char_type('\t')}; 10857 } 10858 10859 TOML11_INLINE repeat_at_least ws(const spec& s) 10860 { 10861 return repeat_at_least(0, wschar(s)); 10862 } 10863 10864 // =========================================================================== 10865 // Newline 10866 10867 TOML11_INLINE either newline(const spec&) 10868 { 10869 return either(character(char_type('\n')), literal("\r\n")); 10870 } 10871 10872 // =========================================================================== 10873 // Comments 10874 10875 TOML11_INLINE either allowed_comment_char(const spec& s) 10876 { 10877 if(s.v1_1_0_allow_control_characters_in_comments) 10878 { 10879 return either( 10880 character_in_range(0x01, 0x09), 10881 character_in_range(0x0E, 0x7F), 10882 non_ascii(s) 10883 ); 10884 } 10885 else 10886 { 10887 return either( 10888 character(0x09), 10889 character_in_range(0x20, 0x7E), 10890 non_ascii(s) 10891 ); 10892 } 10893 } 10894 10895 // XXX Note that it does not take newline 10896 TOML11_INLINE sequence comment(const spec& s) 10897 { 10898 return sequence(character(char_type('#')), 10899 repeat_at_least(0, allowed_comment_char(s))); 10900 } 10901 10902 // =========================================================================== 10903 // Boolean 10904 10905 TOML11_INLINE either boolean(const spec&) 10906 { 10907 return either(literal("true"), literal("false")); 10908 } 10909 10910 // =========================================================================== 10911 // Integer 10912 10913 TOML11_INLINE digit::digit(const spec&) noexcept 10914 : scanner_(char_type('0'), char_type('9')) 10915 {} 10916 10917 TOML11_INLINE alpha::alpha(const spec&) noexcept 10918 : scanner_( 10919 character_in_range(char_type('a'), char_type('z')), 10920 character_in_range(char_type('A'), char_type('Z')) 10921 ) 10922 {} 10923 10924 TOML11_INLINE hexdig::hexdig(const spec& s) noexcept 10925 : scanner_( 10926 digit(s), 10927 character_in_range(char_type('a'), char_type('f')), 10928 character_in_range(char_type('A'), char_type('F')) 10929 ) 10930 {} 10931 10932 // non-digit-graph = ([a-zA-Z]|unicode mb char) 10933 // graph = ([a-zA-Z0-9]|unicode mb char) 10934 // suffix = _ non-digit-graph (graph | _graph) 10935 TOML11_INLINE sequence num_suffix(const spec& s) 10936 { 10937 const auto non_digit_graph = [&s]() { 10938 return either( 10939 alpha(s), 10940 non_ascii(s) 10941 ); 10942 }; 10943 const auto graph = [&s]() { 10944 return either( 10945 alpha(s), 10946 digit(s), 10947 non_ascii(s) 10948 ); 10949 }; 10950 10951 return sequence( 10952 character(char_type('_')), 10953 non_digit_graph(), 10954 repeat_at_least(0, 10955 either( 10956 sequence(character(char_type('_')), graph()), 10957 graph() 10958 ) 10959 ) 10960 ); 10961 } 10962 10963 TOML11_INLINE sequence dec_int(const spec& s) 10964 { 10965 const auto digit19 = []() { 10966 return character_in_range(char_type('1'), char_type('9')); 10967 }; 10968 return sequence( 10969 maybe(character_either{char_type('-'), char_type('+')}), 10970 either( 10971 sequence( 10972 digit19(), 10973 repeat_at_least(1, 10974 either( 10975 digit(s), 10976 sequence(character(char_type('_')), digit(s)) 10977 ) 10978 ) 10979 ), 10980 digit(s) 10981 ) 10982 ); 10983 } 10984 10985 TOML11_INLINE sequence hex_int(const spec& s) 10986 { 10987 return sequence( 10988 literal("0x"), 10989 hexdig(s), 10990 repeat_at_least(0, 10991 either( 10992 hexdig(s), 10993 sequence(character(char_type('_')), hexdig(s)) 10994 ) 10995 ) 10996 ); 10997 } 10998 10999 TOML11_INLINE sequence oct_int(const spec&) 11000 { 11001 const auto digit07 = []() { 11002 return character_in_range(char_type('0'), char_type('7')); 11003 }; 11004 return sequence( 11005 literal("0o"), 11006 digit07(), 11007 repeat_at_least(0, 11008 either( 11009 digit07(), 11010 sequence(character(char_type('_')), digit07()) 11011 ) 11012 ) 11013 ); 11014 } 11015 11016 TOML11_INLINE sequence bin_int(const spec&) 11017 { 11018 const auto digit01 = []() { 11019 return character_either{char_type('0'), char_type('1')}; 11020 }; 11021 return sequence( 11022 literal("0b"), 11023 digit01(), 11024 repeat_at_least(0, 11025 either( 11026 digit01(), 11027 sequence(character(char_type('_')), digit01()) 11028 ) 11029 ) 11030 ); 11031 } 11032 11033 TOML11_INLINE either integer(const spec& s) 11034 { 11035 return either( 11036 hex_int(s), 11037 oct_int(s), 11038 bin_int(s), 11039 dec_int(s) 11040 ); 11041 } 11042 11043 11044 // =========================================================================== 11045 // Floating 11046 11047 TOML11_INLINE sequence zero_prefixable_int(const spec& s) 11048 { 11049 return sequence( 11050 digit(s), 11051 repeat_at_least(0, 11052 either( 11053 digit(s), 11054 sequence(character('_'), digit(s)) 11055 ) 11056 ) 11057 ); 11058 } 11059 11060 TOML11_INLINE sequence fractional_part(const spec& s) 11061 { 11062 return sequence( 11063 character('.'), 11064 zero_prefixable_int(s) 11065 ); 11066 } 11067 11068 TOML11_INLINE sequence exponent_part(const spec& s) 11069 { 11070 return sequence( 11071 character_either{char_type('e'), char_type('E')}, 11072 maybe(character_either{char_type('+'), char_type('-')}), 11073 zero_prefixable_int(s) 11074 ); 11075 } 11076 11077 TOML11_INLINE sequence hex_floating(const spec& s) 11078 { 11079 // C99 hexfloat (%a) 11080 // [+-]? 0x ( [0-9a-fA-F]*\.[0-9a-fA-F]+ | [0-9a-fA-F]+\.? ) [pP] [+-]? [0-9]+ 11081 11082 // - 0x(int).(frac)p[+-](int) 11083 // - 0x(int).p[+-](int) 11084 // - 0x.(frac)p[+-](int) 11085 // - 0x(int)p[+-](int) 11086 11087 return sequence( 11088 maybe(character_either{char_type('+'), char_type('-')}), 11089 character('0'), 11090 character_either{char_type('x'), char_type('X')}, 11091 either( 11092 sequence( 11093 repeat_at_least(0, hexdig(s)), 11094 character('.'), 11095 repeat_at_least(1, hexdig(s)) 11096 ), 11097 sequence( 11098 repeat_at_least(1, hexdig(s)), 11099 maybe(character('.')) 11100 ) 11101 ), 11102 character_either{char_type('p'), char_type('P')}, 11103 maybe(character_either{char_type('+'), char_type('-')}), 11104 repeat_at_least(1, character_in_range('0', '9')) 11105 ); 11106 } 11107 11108 TOML11_INLINE either floating(const spec& s) 11109 { 11110 return either( 11111 sequence( 11112 dec_int(s), 11113 either( 11114 exponent_part(s), 11115 sequence(fractional_part(s), maybe(exponent_part(s))) 11116 ) 11117 ), 11118 sequence( 11119 maybe(character_either{char_type('-'), char_type('+')}), 11120 either(literal("inf"), literal("nan")) 11121 ) 11122 ); 11123 } 11124 11125 // =========================================================================== 11126 // Datetime 11127 11128 TOML11_INLINE sequence local_date(const spec& s) 11129 { 11130 return sequence( 11131 repeat_exact(4, digit(s)), 11132 character('-'), 11133 repeat_exact(2, digit(s)), 11134 character('-'), 11135 repeat_exact(2, digit(s)) 11136 ); 11137 } 11138 TOML11_INLINE sequence local_time(const spec& s) 11139 { 11140 auto time = sequence( 11141 repeat_exact(2, digit(s)), 11142 character(':'), 11143 repeat_exact(2, digit(s)) 11144 ); 11145 11146 if(s.v1_1_0_make_seconds_optional) 11147 { 11148 time.push_back(maybe(sequence( 11149 character(':'), 11150 repeat_exact(2, digit(s)), 11151 maybe(sequence(character('.'), repeat_at_least(1, digit(s)))) 11152 ))); 11153 } 11154 else 11155 { 11156 time.push_back(character(':')); 11157 time.push_back(repeat_exact(2, digit(s))); 11158 time.push_back( 11159 maybe(sequence(character('.'), repeat_at_least(1, digit(s)))) 11160 ); 11161 } 11162 11163 return time; 11164 } 11165 TOML11_INLINE either time_offset(const spec& s) 11166 { 11167 return either( 11168 character_either{'Z', 'z'}, 11169 sequence(character_either{'+', '-'}, 11170 repeat_exact(2, digit(s)), 11171 character(':'), 11172 repeat_exact(2, digit(s)) 11173 ) 11174 ); 11175 } 11176 TOML11_INLINE sequence full_time(const spec& s) 11177 { 11178 return sequence(local_time(s), time_offset(s)); 11179 } 11180 TOML11_INLINE character_either time_delim(const spec&) 11181 { 11182 return character_either{'T', 't', ' '}; 11183 } 11184 TOML11_INLINE sequence local_datetime(const spec& s) 11185 { 11186 return sequence(local_date(s), time_delim(s), local_time(s)); 11187 } 11188 TOML11_INLINE sequence offset_datetime(const spec& s) 11189 { 11190 return sequence(local_date(s), time_delim(s), full_time(s)); 11191 } 11192 11193 // =========================================================================== 11194 // String 11195 11196 TOML11_INLINE sequence escaped(const spec& s) 11197 { 11198 character_either escape_char{ 11199 '\"','\\', 'b', 'f', 'n', 'r', 't' 11200 }; 11201 if(s.v1_1_0_add_escape_sequence_e) 11202 { 11203 escape_char.push_back(char_type('e')); 11204 } 11205 11206 either escape_seq( 11207 std::move(escape_char), 11208 sequence(character('u'), repeat_exact(4, hexdig(s))), 11209 sequence(character('U'), repeat_exact(8, hexdig(s))) 11210 ); 11211 11212 if(s.v1_1_0_add_escape_sequence_x) 11213 { 11214 escape_seq.push_back( 11215 sequence(character('x'), repeat_exact(2, hexdig(s))) 11216 ); 11217 } 11218 11219 return sequence( 11220 character('\\'), 11221 std::move(escape_seq) 11222 ); 11223 } 11224 11225 TOML11_INLINE either basic_char(const spec& s) 11226 { 11227 const auto basic_unescaped = [&s]() { 11228 return either( 11229 wschar(s), 11230 character(0x21), // 22 is " 11231 character_in_range(0x23, 0x5B), // 5C is backslash 11232 character_in_range(0x5D, 0x7E), // 7F is DEL 11233 non_ascii(s) 11234 ); 11235 }; 11236 return either(basic_unescaped(), escaped(s)); 11237 } 11238 11239 TOML11_INLINE sequence basic_string(const spec& s) 11240 { 11241 return sequence( 11242 character('"'), 11243 repeat_at_least(0, basic_char(s)), 11244 character('"') 11245 ); 11246 } 11247 11248 // --------------------------------------------------------------------------- 11249 // multiline string 11250 11251 TOML11_INLINE sequence escaped_newline(const spec& s) 11252 { 11253 return sequence( 11254 character('\\'), ws(s), newline(s), 11255 repeat_at_least(0, either(wschar(s), newline(s))) 11256 ); 11257 } 11258 11259 TOML11_INLINE sequence ml_basic_string(const spec& s) 11260 { 11261 const auto mlb_content = [&s]() { 11262 return either(basic_char(s), newline(s), escaped_newline(s)); 11263 }; 11264 const auto mlb_quotes = []() { 11265 return either(literal("\"\""), character('\"')); 11266 }; 11267 11268 return sequence( 11269 literal("\"\"\""), 11270 maybe(newline(s)), 11271 repeat_at_least(0, mlb_content()), 11272 repeat_at_least(0, 11273 sequence( 11274 mlb_quotes(), 11275 repeat_at_least(1, mlb_content()) 11276 ) 11277 ), 11278 // XXX """ and mlb_quotes are intentionally reordered to avoid 11279 // unexpected match of mlb_quotes 11280 literal("\"\"\""), 11281 maybe(mlb_quotes()) 11282 ); 11283 } 11284 11285 // --------------------------------------------------------------------------- 11286 // literal string 11287 11288 TOML11_INLINE either literal_char(const spec& s) 11289 { 11290 return either( 11291 character (0x09), 11292 character_in_range(0x20, 0x26), 11293 character_in_range(0x28, 0x7E), 11294 non_ascii(s) 11295 ); 11296 } 11297 11298 TOML11_INLINE sequence literal_string(const spec& s) 11299 { 11300 return sequence( 11301 character('\''), 11302 repeat_at_least(0, literal_char(s)), 11303 character('\'') 11304 ); 11305 } 11306 11307 TOML11_INLINE sequence ml_literal_string(const spec& s) 11308 { 11309 const auto mll_quotes = []() { 11310 return either(literal("''"), character('\'')); 11311 }; 11312 const auto mll_content = [&s]() { 11313 return either(literal_char(s), newline(s)); 11314 }; 11315 11316 return sequence( 11317 literal("'''"), 11318 maybe(newline(s)), 11319 repeat_at_least(0, mll_content()), 11320 repeat_at_least(0, sequence( 11321 mll_quotes(), 11322 repeat_at_least(1, mll_content()) 11323 ) 11324 ), 11325 literal("'''"), 11326 maybe(mll_quotes()) 11327 // XXX ''' and mll_quotes are intentionally reordered to avoid 11328 // unexpected match of mll_quotes 11329 ); 11330 } 11331 11332 TOML11_INLINE either string(const spec& s) 11333 { 11334 return either( 11335 ml_basic_string(s), 11336 ml_literal_string(s), 11337 basic_string(s), 11338 literal_string(s) 11339 ); 11340 } 11341 11342 // =========================================================================== 11343 // Keys 11344 11345 // to keep `expected_chars` simple 11346 TOML11_INLINE non_ascii_key_char::non_ascii_key_char(const spec& s) noexcept 11347 { 11348 assert(s.v1_1_0_allow_non_english_in_bare_keys); 11349 (void)s; // for NDEBUG 11350 } 11351 11352 TOML11_INLINE std::uint32_t non_ascii_key_char::read_utf8(location& loc) const 11353 { 11354 // U+0000 ... U+0079 ; 0xxx_xxxx 11355 // U+0080 ... U+07FF ; 110y_yyyx 10xx_xxxx; 11356 // U+0800 ... U+FFFF ; 1110_yyyy 10yx_xxxx 10xx_xxxx 11357 // U+010000 ... U+10FFFF; 1111_0yyy 10yy_xxxx 10xx_xxxx 10xx_xxxx 11358 11359 const unsigned char b1 = loc.current(); loc.advance(1); 11360 if(b1 < 0x80) 11361 { 11362 return static_cast<std::uint32_t>(b1); 11363 } 11364 else if((b1 >> 5) == 6) // 0b110 == 6 11365 { 11366 const auto b2 = loc.current(); loc.advance(1); 11367 11368 const std::uint32_t c1 = b1 & ((1 << 5) - 1); 11369 const std::uint32_t c2 = b2 & ((1 << 6) - 1); 11370 const std::uint32_t codep = (c1 << 6) + c2; 11371 11372 if(codep < 0x80) 11373 { 11374 return 0xFFFFFFFF; 11375 } 11376 return codep; 11377 } 11378 else if((b1 >> 4) == 14) // 0b1110 == 14 11379 { 11380 const auto b2 = loc.current(); loc.advance(1); if(loc.eof()) {return 0xFFFFFFFF;} 11381 const auto b3 = loc.current(); loc.advance(1); 11382 11383 const std::uint32_t c1 = b1 & ((1 << 4) - 1); 11384 const std::uint32_t c2 = b2 & ((1 << 6) - 1); 11385 const std::uint32_t c3 = b3 & ((1 << 6) - 1); 11386 11387 const std::uint32_t codep = (c1 << 12) + (c2 << 6) + c3; 11388 if(codep < 0x800) 11389 { 11390 return 0xFFFFFFFF; 11391 } 11392 return codep; 11393 } 11394 else if((b1 >> 3) == 30) // 0b11110 == 30 11395 { 11396 const auto b2 = loc.current(); loc.advance(1); if(loc.eof()) {return 0xFFFFFFFF;} 11397 const auto b3 = loc.current(); loc.advance(1); if(loc.eof()) {return 0xFFFFFFFF;} 11398 const auto b4 = loc.current(); loc.advance(1); 11399 11400 const std::uint32_t c1 = b1 & ((1 << 3) - 1); 11401 const std::uint32_t c2 = b2 & ((1 << 6) - 1); 11402 const std::uint32_t c3 = b3 & ((1 << 6) - 1); 11403 const std::uint32_t c4 = b4 & ((1 << 6) - 1); 11404 const std::uint32_t codep = (c1 << 18) + (c2 << 12) + (c3 << 6) + c4; 11405 11406 if(codep < 0x10000) 11407 { 11408 return 0xFFFFFFFF; 11409 } 11410 return codep; 11411 } 11412 else // not a Unicode codepoint in UTF-8 11413 { 11414 return 0xFFFFFFFF; 11415 } 11416 } 11417 11418 TOML11_INLINE region non_ascii_key_char::scan(location& loc) const 11419 { 11420 if(loc.eof()) {return region{};} 11421 11422 const auto first = loc; 11423 11424 const auto cp = read_utf8(loc); 11425 11426 if(cp == 0xFFFFFFFF) 11427 { 11428 return region{}; 11429 } 11430 11431 // ALPHA / DIGIT / %x2D / %x5F ; a-z A-Z 0-9 - _ 11432 // / %xB2 / %xB3 / %xB9 / %xBC-BE ; superscript digits, fractions 11433 // / %xC0-D6 / %xD8-F6 / %xF8-37D ; non-symbol chars in Latin block 11434 // / %x37F-1FFF ; exclude GREEK QUESTION MARK, which is basically a semi-colon 11435 // / %x200C-200D / %x203F-2040 ; from General Punctuation Block, include the two tie symbols and ZWNJ, ZWJ 11436 // / %x2070-218F / %x2460-24FF ; include super-/subscripts, letterlike/numberlike forms, enclosed alphanumerics 11437 // / %x2C00-2FEF / %x3001-D7FF ; skip arrows, math, box drawing etc, skip 2FF0-3000 ideographic up/down markers and spaces 11438 // / %xF900-FDCF / %xFDF0-FFFD ; skip D800-DFFF surrogate block, E000-F8FF Private Use area, FDD0-FDEF intended for process-internal use (unicode) 11439 // / %x10000-EFFFF ; all chars outside BMP range, excluding Private Use planes (F0000-10FFFF) 11440 11441 if(cp == 0xB2 || cp == 0xB3 || cp == 0xB9 || (0xBC <= cp && cp <= 0xBE) || 11442 (0xC0 <= cp && cp <= 0xD6 ) || (0xD8 <= cp && cp <= 0xF6) || (0xF8 <= cp && cp <= 0x37D) || 11443 (0x37F <= cp && cp <= 0x1FFF) || 11444 (0x200C <= cp && cp <= 0x200D) || (0x203F <= cp && cp <= 0x2040) || 11445 (0x2070 <= cp && cp <= 0x218F) || (0x2460 <= cp && cp <= 0x24FF) || 11446 (0x2C00 <= cp && cp <= 0x2FEF) || (0x3001 <= cp && cp <= 0xD7FF) || 11447 (0xF900 <= cp && cp <= 0xFDCF) || (0xFDF0 <= cp && cp <= 0xFFFD) || 11448 (0x10000 <= cp && cp <= 0xEFFFF) ) 11449 { 11450 return region(first, loc); 11451 } 11452 loc = first; 11453 return region{}; 11454 } 11455 11456 TOML11_INLINE repeat_at_least unquoted_key(const spec& s) 11457 { 11458 auto keychar = either( 11459 alpha(s), digit(s), character{0x2D}, character{0x5F} 11460 ); 11461 11462 if(s.v1_1_0_allow_non_english_in_bare_keys) 11463 { 11464 keychar.push_back(non_ascii_key_char(s)); 11465 } 11466 11467 return repeat_at_least(1, std::move(keychar)); 11468 } 11469 11470 TOML11_INLINE either quoted_key(const spec& s) 11471 { 11472 return either(basic_string(s), literal_string(s)); 11473 } 11474 11475 TOML11_INLINE either simple_key(const spec& s) 11476 { 11477 return either(unquoted_key(s), quoted_key(s)); 11478 } 11479 11480 TOML11_INLINE sequence dot_sep(const spec& s) 11481 { 11482 return sequence(ws(s), character('.'), ws(s)); 11483 } 11484 11485 TOML11_INLINE sequence dotted_key(const spec& s) 11486 { 11487 return sequence( 11488 simple_key(s), 11489 repeat_at_least(1, sequence(dot_sep(s), simple_key(s))) 11490 ); 11491 } 11492 11493 TOML11_INLINE key::key(const spec& s) noexcept 11494 : scanner_(dotted_key(s), simple_key(s)) 11495 {} 11496 11497 TOML11_INLINE sequence keyval_sep(const spec& s) 11498 { 11499 return sequence(ws(s), character('='), ws(s)); 11500 } 11501 11502 // =========================================================================== 11503 // Table key 11504 11505 TOML11_INLINE sequence std_table(const spec& s) 11506 { 11507 return sequence(character('['), ws(s), key(s), ws(s), character(']')); 11508 } 11509 11510 TOML11_INLINE sequence array_table(const spec& s) 11511 { 11512 return sequence(literal("[["), ws(s), key(s), ws(s), literal("]]")); 11513 } 11514 11515 // =========================================================================== 11516 // extension: null 11517 11518 TOML11_INLINE literal null_value(const spec&) 11519 { 11520 return literal("null"); 11521 } 11522 11523 } // namespace syntax 11524 } // namespace detail 11525 } // namespace toml 11526 #endif // TOML11_SYNTAX_IMPL_HPP 11527 #endif 11528 11529 #endif// TOML11_SYNTAX_HPP 11530 #ifndef TOML11_SKIP_HPP 11531 #define TOML11_SKIP_HPP 11532 11533 11534 #include <cassert> 11535 11536 namespace toml 11537 { 11538 namespace detail 11539 { 11540 11541 template<typename TC> 11542 bool skip_whitespace(location& loc, const context<TC>& ctx) 11543 { 11544 return syntax::ws(ctx.toml_spec()).scan(loc).is_ok(); 11545 } 11546 11547 template<typename TC> 11548 bool skip_empty_lines(location& loc, const context<TC>& ctx) 11549 { 11550 return repeat_at_least(1, sequence( 11551 syntax::ws(ctx.toml_spec()), 11552 syntax::newline(ctx.toml_spec()) 11553 )).scan(loc).is_ok(); 11554 } 11555 11556 // For error recovery. 11557 // 11558 // In case if a comment line contains an invalid character, we need to skip it 11559 // to advance parsing. 11560 template<typename TC> 11561 void skip_comment_block(location& loc, const context<TC>& ctx) 11562 { 11563 while( ! loc.eof()) 11564 { 11565 skip_whitespace(loc, ctx); 11566 if(loc.current() == '#') 11567 { 11568 while( ! loc.eof()) 11569 { 11570 // both CRLF and LF ends with LF. 11571 if(loc.current() == '\n') 11572 { 11573 loc.advance(); 11574 break; 11575 } 11576 } 11577 } 11578 else if(syntax::newline(ctx.toml_spec()).scan(loc).is_ok()) 11579 { 11580 ; // an empty line. skip this also 11581 } 11582 else 11583 { 11584 // the next token is neither a comment nor empty line. 11585 return ; 11586 } 11587 } 11588 return ; 11589 } 11590 11591 template<typename TC> 11592 void skip_empty_or_comment_lines(location& loc, const context<TC>& ctx) 11593 { 11594 const auto& spec = ctx.toml_spec(); 11595 repeat_at_least(0, sequence( 11596 syntax::ws(spec), 11597 maybe(syntax::comment(spec)), 11598 syntax::newline(spec)) 11599 ).scan(loc); 11600 return ; 11601 } 11602 11603 // For error recovery. 11604 // 11605 // Sometimes we need to skip a value and find a delimiter, like `,`, `]`, or `}`. 11606 // To find delimiter, we need to skip delimiters in a string. 11607 // Since we are skipping invalid value while error recovery, we don't need 11608 // to check the syntax. Here we just skip string-like region until closing quote 11609 // is found. 11610 template<typename TC> 11611 void skip_string_like(location& loc, const context<TC>&) 11612 { 11613 // if """ is found, skip until the closing """ is found. 11614 if(literal("\"\"\"").scan(loc).is_ok()) 11615 { 11616 while( ! loc.eof()) 11617 { 11618 if(literal("\"\"\"").scan(loc).is_ok()) 11619 { 11620 return; 11621 } 11622 loc.advance(); 11623 } 11624 } 11625 else if(literal("'''").scan(loc).is_ok()) 11626 { 11627 while( ! loc.eof()) 11628 { 11629 if(literal("'''").scan(loc).is_ok()) 11630 { 11631 return; 11632 } 11633 loc.advance(); 11634 } 11635 } 11636 // if " is found, skip until the closing " or newline is found. 11637 else if(loc.current() == '"') 11638 { 11639 while( ! loc.eof()) 11640 { 11641 loc.advance(); 11642 if(loc.current() == '"' || loc.current() == '\n') 11643 { 11644 loc.advance(); 11645 return; 11646 } 11647 } 11648 } 11649 else if(loc.current() == '\'') 11650 { 11651 while( ! loc.eof()) 11652 { 11653 loc.advance(); 11654 if(loc.current() == '\'' || loc.current() == '\n') 11655 { 11656 loc.advance(); 11657 return ; 11658 } 11659 } 11660 } 11661 return; 11662 } 11663 11664 template<typename TC> 11665 void skip_value(location& loc, const context<TC>& ctx); 11666 template<typename TC> 11667 void skip_array_like(location& loc, const context<TC>& ctx); 11668 template<typename TC> 11669 void skip_inline_table_like(location& loc, const context<TC>& ctx); 11670 template<typename TC> 11671 void skip_key_value_pair(location& loc, const context<TC>& ctx); 11672 11673 template<typename TC> 11674 result<value_t, error_info> 11675 guess_value_type(const location& loc, const context<TC>& ctx); 11676 11677 template<typename TC> 11678 void skip_array_like(location& loc, const context<TC>& ctx) 11679 { 11680 const auto& spec = ctx.toml_spec(); 11681 assert(loc.current() == '['); 11682 loc.advance(); 11683 11684 while( ! loc.eof()) 11685 { 11686 if(loc.current() == '\"' || loc.current() == '\'') 11687 { 11688 skip_string_like(loc, ctx); 11689 } 11690 else if(loc.current() == '#') 11691 { 11692 skip_comment_block(loc, ctx); 11693 } 11694 else if(loc.current() == '{') 11695 { 11696 skip_inline_table_like(loc, ctx); 11697 } 11698 else if(loc.current() == '[') 11699 { 11700 const auto checkpoint = loc; 11701 if(syntax::std_table(spec).scan(loc).is_ok() || 11702 syntax::array_table(spec).scan(loc).is_ok()) 11703 { 11704 loc = checkpoint; 11705 break; 11706 } 11707 // if it is not a table-definition, then it is an array. 11708 skip_array_like(loc, ctx); 11709 } 11710 else if(loc.current() == '=') 11711 { 11712 // key-value pair cannot be inside the array. 11713 // guessing the error is "missing closing bracket `]`". 11714 // find the previous key just before `=`. 11715 while(loc.get_location() != 0) 11716 { 11717 loc.retrace(); 11718 if(loc.current() == '\n') 11719 { 11720 loc.advance(); 11721 break; 11722 } 11723 } 11724 break; 11725 } 11726 else if(loc.current() == ']') 11727 { 11728 break; // found closing bracket 11729 } 11730 else 11731 { 11732 loc.advance(); 11733 } 11734 } 11735 return ; 11736 } 11737 11738 template<typename TC> 11739 void skip_inline_table_like(location& loc, const context<TC>& ctx) 11740 { 11741 assert(loc.current() == '{'); 11742 loc.advance(); 11743 11744 const auto& spec = ctx.toml_spec(); 11745 11746 while( ! loc.eof()) 11747 { 11748 if(loc.current() == '\n' && ! spec.v1_1_0_allow_newlines_in_inline_tables) 11749 { 11750 break; // missing closing `}`. 11751 } 11752 else if(loc.current() == '\"' || loc.current() == '\'') 11753 { 11754 skip_string_like(loc, ctx); 11755 } 11756 else if(loc.current() == '#') 11757 { 11758 skip_comment_block(loc, ctx); 11759 if( ! spec.v1_1_0_allow_newlines_in_inline_tables) 11760 { 11761 // comment must end with newline. 11762 break; // missing closing `}`. 11763 } 11764 } 11765 else if(loc.current() == '[') 11766 { 11767 const auto checkpoint = loc; 11768 if(syntax::std_table(spec).scan(loc).is_ok() || 11769 syntax::array_table(spec).scan(loc).is_ok()) 11770 { 11771 loc = checkpoint; 11772 break; // missing closing `}`. 11773 } 11774 // if it is not a table-definition, then it is an array. 11775 skip_array_like(loc, ctx); 11776 } 11777 else if(loc.current() == '{') 11778 { 11779 skip_inline_table_like(loc, ctx); 11780 } 11781 else if(loc.current() == '}') 11782 { 11783 // closing brace found. guessing the error is inside the table. 11784 break; 11785 } 11786 else 11787 { 11788 // skip otherwise. 11789 loc.advance(); 11790 } 11791 } 11792 return ; 11793 } 11794 11795 template<typename TC> 11796 void skip_value(location& loc, const context<TC>& ctx) 11797 { 11798 value_t ty = guess_value_type(loc, ctx).unwrap_or(value_t::empty); 11799 if(ty == value_t::string) 11800 { 11801 skip_string_like(loc, ctx); 11802 } 11803 else if(ty == value_t::array) 11804 { 11805 skip_array_like(loc, ctx); 11806 } 11807 else if(ty == value_t::table) 11808 { 11809 // In case of multiline tables, it may skip key-value pair but not the 11810 // whole table. 11811 skip_inline_table_like(loc, ctx); 11812 } 11813 else // others are an "in-line" values. skip until the next line 11814 { 11815 while( ! loc.eof()) 11816 { 11817 if(loc.current() == '\n') 11818 { 11819 break; 11820 } 11821 else if(loc.current() == ',' || loc.current() == ']' || loc.current() == '}') 11822 { 11823 break; 11824 } 11825 loc.advance(); 11826 } 11827 } 11828 return; 11829 } 11830 11831 template<typename TC> 11832 void skip_key_value_pair(location& loc, const context<TC>& ctx) 11833 { 11834 while( ! loc.eof()) 11835 { 11836 if(loc.current() == '=') 11837 { 11838 skip_whitespace(loc, ctx); 11839 skip_value(loc, ctx); 11840 return; 11841 } 11842 else if(loc.current() == '\n') 11843 { 11844 // newline is found before finding `=`. assuming "missing `=`". 11845 return; 11846 } 11847 loc.advance(); 11848 } 11849 return ; 11850 } 11851 11852 template<typename TC> 11853 void skip_until_next_table(location& loc, const context<TC>& ctx) 11854 { 11855 const auto& spec = ctx.toml_spec(); 11856 while( ! loc.eof()) 11857 { 11858 if(loc.current() == '\n') 11859 { 11860 loc.advance(); 11861 const auto line_begin = loc; 11862 11863 skip_whitespace(loc, ctx); 11864 if(syntax::std_table(spec).scan(loc).is_ok()) 11865 { 11866 loc = line_begin; 11867 return ; 11868 } 11869 if(syntax::array_table(spec).scan(loc).is_ok()) 11870 { 11871 loc = line_begin; 11872 return ; 11873 } 11874 } 11875 loc.advance(); 11876 } 11877 } 11878 11879 } // namespace detail 11880 } // namespace toml 11881 11882 #if defined(TOML11_COMPILE_SOURCES) 11883 namespace toml 11884 { 11885 struct type_config; 11886 struct ordered_type_config; 11887 11888 namespace detail 11889 { 11890 extern template bool skip_whitespace <type_config>(location& loc, const context<type_config>&); 11891 extern template bool skip_empty_lines <type_config>(location& loc, const context<type_config>&); 11892 extern template void skip_comment_block <type_config>(location& loc, const context<type_config>&); 11893 extern template void skip_empty_or_comment_lines<type_config>(location& loc, const context<type_config>&); 11894 extern template void skip_string_like <type_config>(location& loc, const context<type_config>&); 11895 extern template void skip_array_like <type_config>(location& loc, const context<type_config>&); 11896 extern template void skip_inline_table_like <type_config>(location& loc, const context<type_config>&); 11897 extern template void skip_value <type_config>(location& loc, const context<type_config>&); 11898 extern template void skip_key_value_pair <type_config>(location& loc, const context<type_config>&); 11899 extern template void skip_until_next_table <type_config>(location& loc, const context<type_config>&); 11900 11901 extern template bool skip_whitespace <ordered_type_config>(location& loc, const context<ordered_type_config>&); 11902 extern template bool skip_empty_lines <ordered_type_config>(location& loc, const context<ordered_type_config>&); 11903 extern template void skip_comment_block <ordered_type_config>(location& loc, const context<ordered_type_config>&); 11904 extern template void skip_empty_or_comment_lines<ordered_type_config>(location& loc, const context<ordered_type_config>&); 11905 extern template void skip_string_like <ordered_type_config>(location& loc, const context<ordered_type_config>&); 11906 extern template void skip_array_like <ordered_type_config>(location& loc, const context<ordered_type_config>&); 11907 extern template void skip_inline_table_like <ordered_type_config>(location& loc, const context<ordered_type_config>&); 11908 extern template void skip_value <ordered_type_config>(location& loc, const context<ordered_type_config>&); 11909 extern template void skip_key_value_pair <ordered_type_config>(location& loc, const context<ordered_type_config>&); 11910 extern template void skip_until_next_table <ordered_type_config>(location& loc, const context<ordered_type_config>&); 11911 11912 } // detail 11913 } // toml 11914 #endif // TOML11_COMPILE_SOURCES 11915 11916 #endif // TOML11_SKIP_HPP 11917 #ifndef TOML11_PARSER_HPP 11918 #define TOML11_PARSER_HPP 11919 11920 11921 #include <fstream> 11922 #include <sstream> 11923 11924 #include <cassert> 11925 #include <cmath> 11926 11927 #if defined(TOML11_HAS_FILESYSTEM) && TOML11_HAS_FILESYSTEM 11928 #include <filesystem> 11929 #endif 11930 11931 namespace toml 11932 { 11933 11934 struct syntax_error final : public ::toml::exception 11935 { 11936 public: 11937 syntax_error(std::string what_arg, std::vector<error_info> err) 11938 : what_(std::move(what_arg)), err_(std::move(err)) 11939 {} 11940 ~syntax_error() noexcept override = default; 11941 11942 const char* what() const noexcept override {return what_.c_str();} 11943 11944 std::vector<error_info> const& errors() const noexcept 11945 { 11946 return err_; 11947 } 11948 11949 private: 11950 std::string what_; 11951 std::vector<error_info> err_; 11952 }; 11953 11954 struct file_io_error final : public ::toml::exception 11955 { 11956 public: 11957 11958 file_io_error(const std::string& msg, const std::string& fname) 11959 : errno_(cxx::make_nullopt()), 11960 what_(msg + " \"" + fname + "\"") 11961 {} 11962 file_io_error(int errnum, const std::string& msg, const std::string& fname) 11963 : errno_(errnum), 11964 what_(msg + " \"" + fname + "\": errno=" + std::to_string(errnum)) 11965 {} 11966 ~file_io_error() noexcept override = default; 11967 11968 const char* what() const noexcept override {return what_.c_str();} 11969 11970 bool has_errno() const noexcept {return errno_.has_value();} 11971 int get_errno() const noexcept {return errno_.value_or(0);} 11972 11973 private: 11974 11975 cxx::optional<int> errno_; 11976 std::string what_; 11977 }; 11978 11979 namespace detail 11980 { 11981 11982 /* ============================================================================ 11983 * __ ___ _ __ _ __ ___ _ _ 11984 * / _/ _ \ ' \| ' \/ _ \ ' \ 11985 * \__\___/_|_|_|_|_|_\___/_||_| 11986 */ 11987 11988 template<typename S> 11989 error_info make_syntax_error(std::string title, 11990 const S& scanner, location loc, std::string suffix = "") 11991 { 11992 auto msg = std::string("expected ") + scanner.expected_chars(loc); 11993 auto src = source_location(region(loc)); 11994 return make_error_info( 11995 std::move(title), std::move(src), std::move(msg), std::move(suffix)); 11996 } 11997 11998 11999 /* ============================================================================ 12000 * _ 12001 * __ ___ _ __ _ __ ___ _ _| |_ 12002 * / _/ _ \ ' \| ' \/ -_) ' \ _| 12003 * \__\___/_|_|_|_|_|_\___|_||_\__| 12004 */ 12005 12006 template<typename TC> 12007 result<cxx::optional<std::string>, error_info> 12008 parse_comment_line(location& loc, context<TC>& ctx) 12009 { 12010 const auto& spec = ctx.toml_spec(); 12011 const auto first = loc; 12012 12013 skip_whitespace(loc, ctx); 12014 12015 const auto com_reg = syntax::comment(spec).scan(loc); 12016 if(com_reg.is_ok()) 12017 { 12018 // once comment started, newline must follow (or reach EOF). 12019 if( ! loc.eof() && ! syntax::newline(spec).scan(loc).is_ok()) 12020 { 12021 while( ! loc.eof()) // skip until newline to continue parsing 12022 { 12023 loc.advance(); 12024 if(loc.current() == '\n') { /*skip LF*/ loc.advance(); break; } 12025 } 12026 return err(make_error_info("toml::parse_comment_line: " 12027 "newline (LF / CRLF) or EOF is expected", 12028 source_location(region(loc)), "but got this", 12029 "Hint: most of the control characters are not allowed in comments")); 12030 } 12031 return ok(cxx::optional<std::string>(com_reg.as_string())); 12032 } 12033 else 12034 { 12035 loc = first; // rollback whitespace to parse indent 12036 return ok(cxx::optional<std::string>(cxx::make_nullopt())); 12037 } 12038 } 12039 12040 /* ============================================================================ 12041 * ___ _ 12042 * | _ ) ___ ___| |___ __ _ _ _ 12043 * | _ \/ _ \/ _ \ / -_) _` | ' \ 12044 * |___/\___/\___/_\___\__,_|_||_| 12045 */ 12046 12047 template<typename TC> 12048 result<basic_value<TC>, error_info> 12049 parse_boolean(location& loc, const context<TC>& ctx) 12050 { 12051 const auto& spec = ctx.toml_spec(); 12052 12053 // ---------------------------------------------------------------------- 12054 // check syntax 12055 auto reg = syntax::boolean(spec).scan(loc); 12056 if( ! reg.is_ok()) 12057 { 12058 return err(make_syntax_error("toml::parse_boolean: " 12059 "invalid boolean: boolean must be `true` or `false`, in lowercase. " 12060 "string must be surrounded by `\"`", syntax::boolean(spec), loc)); 12061 } 12062 12063 // ---------------------------------------------------------------------- 12064 // it matches. gen value 12065 const auto str = reg.as_string(); 12066 const auto val = [&str]() { 12067 if(str == "true") 12068 { 12069 return true; 12070 } 12071 else 12072 { 12073 assert(str == "false"); 12074 return false; 12075 } 12076 }(); 12077 12078 // ---------------------------------------------------------------------- 12079 // no format info for boolean 12080 boolean_format_info fmt; 12081 12082 return ok(basic_value<TC>(val, std::move(fmt), {}, std::move(reg))); 12083 } 12084 12085 /* ============================================================================ 12086 * ___ _ 12087 * |_ _|_ _| |_ ___ __ _ ___ _ _ 12088 * | || ' \ _/ -_) _` / -_) '_| 12089 * |___|_||_\__\___\__, \___|_| 12090 * |___/ 12091 */ 12092 12093 template<typename TC> 12094 result<basic_value<TC>, error_info> 12095 parse_bin_integer(location& loc, const context<TC>& ctx) 12096 { 12097 const auto first = loc; 12098 const auto& spec = ctx.toml_spec(); 12099 auto reg = syntax::bin_int(spec).scan(loc); 12100 if( ! reg.is_ok()) 12101 { 12102 return err(make_syntax_error("toml::parse_bin_integer: " 12103 "invalid integer: bin_int must be like: 0b0101, 0b1111_0000", 12104 syntax::bin_int(spec), loc)); 12105 } 12106 12107 auto str = reg.as_string(); 12108 12109 integer_format_info fmt; 12110 fmt.fmt = integer_format::bin; 12111 fmt.width = str.size() - 2 - static_cast<std::size_t>(std::count(str.begin(), str.end(), '_')); 12112 12113 const auto first_underscore = std::find(str.rbegin(), str.rend(), '_'); 12114 if(first_underscore != str.rend()) 12115 { 12116 fmt.spacer = static_cast<std::size_t>(std::distance(str.rbegin(), first_underscore)); 12117 } 12118 12119 // skip prefix `0b` and zeros and underscores at the MSB 12120 str.erase(str.begin(), std::find(std::next(str.begin(), 2), str.end(), '1')); 12121 12122 // remove all `_` before calling TC::parse_int 12123 str.erase(std::remove(str.begin(), str.end(), '_'), str.end()); 12124 12125 // 0b0000_0000 becomes empty. 12126 if(str.empty()) { str = "0"; } 12127 12128 const auto val = TC::parse_int(str, source_location(region(loc)), 2); 12129 if(val.is_ok()) 12130 { 12131 return ok(basic_value<TC>(val.as_ok(), std::move(fmt), {}, std::move(reg))); 12132 } 12133 else 12134 { 12135 loc = first; 12136 return err(val.as_err()); 12137 } 12138 } 12139 12140 // ---------------------------------------------------------------------------- 12141 12142 template<typename TC> 12143 result<basic_value<TC>, error_info> 12144 parse_oct_integer(location& loc, const context<TC>& ctx) 12145 { 12146 const auto first = loc; 12147 const auto& spec = ctx.toml_spec(); 12148 auto reg = syntax::oct_int(spec).scan(loc); 12149 if( ! reg.is_ok()) 12150 { 12151 return err(make_syntax_error("toml::parse_oct_integer: " 12152 "invalid integer: oct_int must be like: 0o775, 0o04_44", 12153 syntax::oct_int(spec), loc)); 12154 } 12155 12156 auto str = reg.as_string(); 12157 12158 integer_format_info fmt; 12159 fmt.fmt = integer_format::oct; 12160 fmt.width = str.size() - 2 - static_cast<std::size_t>(std::count(str.begin(), str.end(), '_')); 12161 12162 const auto first_underscore = std::find(str.rbegin(), str.rend(), '_'); 12163 if(first_underscore != str.rend()) 12164 { 12165 fmt.spacer = static_cast<std::size_t>(std::distance(str.rbegin(), first_underscore)); 12166 } 12167 12168 // skip prefix `0o` and zeros and underscores at the MSB 12169 str.erase(str.begin(), std::find_if( 12170 std::next(str.begin(), 2), str.end(), [](const char c) { 12171 return c != '0' && c != '_'; 12172 })); 12173 12174 // remove all `_` before calling TC::parse_int 12175 str.erase(std::remove(str.begin(), str.end(), '_'), str.end()); 12176 12177 // 0o0000_0000 becomes empty. 12178 if(str.empty()) { str = "0"; } 12179 12180 const auto val = TC::parse_int(str, source_location(region(loc)), 8); 12181 if(val.is_ok()) 12182 { 12183 return ok(basic_value<TC>(val.as_ok(), std::move(fmt), {}, std::move(reg))); 12184 } 12185 else 12186 { 12187 loc = first; 12188 return err(val.as_err()); 12189 } 12190 } 12191 12192 template<typename TC> 12193 result<basic_value<TC>, error_info> 12194 parse_hex_integer(location& loc, const context<TC>& ctx) 12195 { 12196 const auto first = loc; 12197 const auto& spec = ctx.toml_spec(); 12198 auto reg = syntax::hex_int(spec).scan(loc); 12199 if( ! reg.is_ok()) 12200 { 12201 return err(make_syntax_error("toml::parse_hex_integer: " 12202 "invalid integer: hex_int must be like: 0xC0FFEE, 0xdead_beef", 12203 syntax::hex_int(spec), loc)); 12204 } 12205 12206 auto str = reg.as_string(); 12207 12208 integer_format_info fmt; 12209 fmt.fmt = integer_format::hex; 12210 fmt.width = str.size() - 2 - static_cast<std::size_t>(std::count(str.begin(), str.end(), '_')); 12211 12212 const auto first_underscore = std::find(str.rbegin(), str.rend(), '_'); 12213 if(first_underscore != str.rend()) 12214 { 12215 fmt.spacer = static_cast<std::size_t>(std::distance(str.rbegin(), first_underscore)); 12216 } 12217 12218 // skip prefix `0x` and zeros and underscores at the MSB 12219 str.erase(str.begin(), std::find_if( 12220 std::next(str.begin(), 2), str.end(), [](const char c) { 12221 return c != '0' && c != '_'; 12222 })); 12223 12224 // remove all `_` before calling TC::parse_int 12225 str.erase(std::remove(str.begin(), str.end(), '_'), str.end()); 12226 12227 // 0x0000_0000 becomes empty. 12228 if(str.empty()) { str = "0"; } 12229 12230 // prefix zero and _ is removed. check if it uses upper/lower case. 12231 // if both upper and lower case letters are found, set upper=true. 12232 const auto lower_not_found = std::find_if(str.begin(), str.end(), 12233 [](const char c) { return std::islower(static_cast<int>(c)) != 0; }) == str.end(); 12234 const auto upper_found = std::find_if(str.begin(), str.end(), 12235 [](const char c) { return std::isupper(static_cast<int>(c)) != 0; }) != str.end(); 12236 fmt.uppercase = lower_not_found || upper_found; 12237 12238 const auto val = TC::parse_int(str, source_location(region(loc)), 16); 12239 if(val.is_ok()) 12240 { 12241 return ok(basic_value<TC>(val.as_ok(), std::move(fmt), {}, std::move(reg))); 12242 } 12243 else 12244 { 12245 loc = first; 12246 return err(val.as_err()); 12247 } 12248 } 12249 12250 template<typename TC> 12251 result<basic_value<TC>, error_info> 12252 parse_dec_integer(location& loc, const context<TC>& ctx) 12253 { 12254 const auto first = loc; 12255 const auto& spec = ctx.toml_spec(); 12256 12257 // ---------------------------------------------------------------------- 12258 // check syntax 12259 auto reg = syntax::dec_int(spec).scan(loc); 12260 if( ! reg.is_ok()) 12261 { 12262 return err(make_syntax_error("toml::parse_dec_integer: " 12263 "invalid integer: dec_int must be like: 42, 123_456_789", 12264 syntax::dec_int(spec), loc)); 12265 } 12266 12267 // ---------------------------------------------------------------------- 12268 // it matches. gen value 12269 auto str = reg.as_string(); 12270 12271 integer_format_info fmt; 12272 fmt.fmt = integer_format::dec; 12273 fmt.width = str.size() - static_cast<std::size_t>(std::count(str.begin(), str.end(), '_')); 12274 12275 const auto first_underscore = std::find(str.rbegin(), str.rend(), '_'); 12276 if(first_underscore != str.rend()) 12277 { 12278 fmt.spacer = static_cast<std::size_t>(std::distance(str.rbegin(), first_underscore)); 12279 } 12280 12281 // remove all `_` before calling TC::parse_int 12282 str.erase(std::remove(str.begin(), str.end(), '_'), str.end()); 12283 12284 auto src = source_location(region(loc)); 12285 const auto val = TC::parse_int(str, src, 10); 12286 if(val.is_err()) 12287 { 12288 loc = first; 12289 return err(val.as_err()); 12290 } 12291 12292 // ---------------------------------------------------------------------- 12293 // parse suffix (extension) 12294 12295 if(spec.ext_num_suffix && loc.current() == '_') 12296 { 12297 const auto sfx_reg = syntax::num_suffix(spec).scan(loc); 12298 if( ! sfx_reg.is_ok()) 12299 { 12300 loc = first; 12301 return err(make_error_info("toml::parse_dec_integer: " 12302 "invalid suffix: should be `_ non-digit-graph (graph | _graph)`", 12303 source_location(region(loc)), "here")); 12304 } 12305 auto sfx = sfx_reg.as_string(); 12306 assert( ! sfx.empty() && sfx.front() == '_'); 12307 sfx.erase(sfx.begin()); // remove the first `_` 12308 12309 fmt.suffix = sfx; 12310 } 12311 12312 return ok(basic_value<TC>(val.as_ok(), std::move(fmt), {}, std::move(reg))); 12313 } 12314 12315 template<typename TC> 12316 result<basic_value<TC>, error_info> 12317 parse_integer(location& loc, const context<TC>& ctx) 12318 { 12319 const auto first = loc; 12320 12321 if( ! loc.eof() && (loc.current() == '+' || loc.current() == '-')) 12322 { 12323 // skip +/- to diagnose +0xDEADBEEF or -0b0011 (invalid). 12324 // without this, +0xDEAD_BEEF will be parsed as a decimal int and 12325 // unexpected "xDEAD_BEEF" will appear after integer "+0". 12326 loc.advance(); 12327 } 12328 12329 if( ! loc.eof() && loc.current() == '0') 12330 { 12331 loc.advance(); 12332 if(loc.eof()) 12333 { 12334 // `[+-]?0`. parse as an decimal integer. 12335 loc = first; 12336 return parse_dec_integer(loc, ctx); 12337 } 12338 12339 const auto prefix = loc.current(); 12340 auto prefix_src = source_location(region(loc)); 12341 12342 loc = first; 12343 12344 if(prefix == 'b') {return parse_bin_integer(loc, ctx);} 12345 if(prefix == 'o') {return parse_oct_integer(loc, ctx);} 12346 if(prefix == 'x') {return parse_hex_integer(loc, ctx);} 12347 12348 if(std::isdigit(prefix)) 12349 { 12350 auto src = source_location(region(loc)); 12351 return err(make_error_info("toml::parse_integer: " 12352 "leading zero in an decimal integer is not allowed", 12353 std::move(src), "leading zero")); 12354 } 12355 } 12356 12357 loc = first; 12358 return parse_dec_integer(loc, ctx); 12359 } 12360 12361 /* ============================================================================ 12362 * ___ _ _ _ 12363 * | __| |___ __ _| |_(_)_ _ __ _ 12364 * | _|| / _ \/ _` | _| | ' \/ _` | 12365 * |_| |_\___/\__,_|\__|_|_||_\__, | 12366 * |___/ 12367 */ 12368 12369 template<typename TC> 12370 result<basic_value<TC>, error_info> 12371 parse_floating(location& loc, const context<TC>& ctx) 12372 { 12373 using floating_type = typename basic_value<TC>::floating_type; 12374 12375 const auto first = loc; 12376 const auto& spec = ctx.toml_spec(); 12377 12378 // ---------------------------------------------------------------------- 12379 // check syntax 12380 bool is_hex = false; 12381 std::string str; 12382 region reg; 12383 if(spec.ext_hex_float && sequence(character('0'), character('x')).scan(loc).is_ok()) 12384 { 12385 loc = first; 12386 is_hex = true; 12387 12388 reg = syntax::hex_floating(spec).scan(loc); 12389 if( ! reg.is_ok()) 12390 { 12391 return err(make_syntax_error("toml::parse_floating: " 12392 "invalid hex floating: float must be like: 0xABCp-3f", 12393 syntax::floating(spec), loc)); 12394 } 12395 str = reg.as_string(); 12396 } 12397 else 12398 { 12399 reg = syntax::floating(spec).scan(loc); 12400 if( ! reg.is_ok()) 12401 { 12402 return err(make_syntax_error("toml::parse_floating: " 12403 "invalid floating: float must be like: -3.14159_26535, 6.022e+23, " 12404 "inf, or nan (lowercase).", syntax::floating(spec), loc)); 12405 } 12406 str = reg.as_string(); 12407 } 12408 12409 // ---------------------------------------------------------------------- 12410 // it matches. gen value 12411 12412 floating_format_info fmt; 12413 12414 if(is_hex) 12415 { 12416 fmt.fmt = floating_format::hex; 12417 } 12418 else 12419 { 12420 // since we already checked that the string conforms the TOML standard. 12421 if(std::find(str.begin(), str.end(), 'e') != str.end() || 12422 std::find(str.begin(), str.end(), 'E') != str.end()) 12423 { 12424 fmt.fmt = floating_format::scientific; // use exponent part 12425 } 12426 else 12427 { 12428 fmt.fmt = floating_format::fixed; // do not use exponent part 12429 } 12430 } 12431 12432 str.erase(std::remove(str.begin(), str.end(), '_'), str.end()); 12433 12434 floating_type val{0}; 12435 12436 if(str == "inf" || str == "+inf") 12437 { 12438 TOML11_CONSTEXPR_IF(std::numeric_limits<floating_type>::has_infinity) 12439 { 12440 val = std::numeric_limits<floating_type>::infinity(); 12441 } 12442 else 12443 { 12444 return err(make_error_info("toml::parse_floating: inf value found" 12445 " but the current environment does not support inf. Please" 12446 " make sure that the floating-point implementation conforms" 12447 " IEEE 754/ISO 60559 international standard.", 12448 source_location(region(loc)), 12449 "floating_type: inf is not supported")); 12450 } 12451 } 12452 else if(str == "-inf") 12453 { 12454 TOML11_CONSTEXPR_IF(std::numeric_limits<floating_type>::has_infinity) 12455 { 12456 val = -std::numeric_limits<floating_type>::infinity(); 12457 } 12458 else 12459 { 12460 return err(make_error_info("toml::parse_floating: inf value found" 12461 " but the current environment does not support inf. Please" 12462 " make sure that the floating-point implementation conforms" 12463 " IEEE 754/ISO 60559 international standard.", 12464 source_location(region(loc)), 12465 "floating_type: inf is not supported")); 12466 } 12467 } 12468 else if(str == "nan" || str == "+nan") 12469 { 12470 TOML11_CONSTEXPR_IF(std::numeric_limits<floating_type>::has_quiet_NaN) 12471 { 12472 val = std::numeric_limits<floating_type>::quiet_NaN(); 12473 } 12474 else TOML11_CONSTEXPR_IF(std::numeric_limits<floating_type>::has_signaling_NaN) 12475 { 12476 val = std::numeric_limits<floating_type>::signaling_NaN(); 12477 } 12478 else 12479 { 12480 return err(make_error_info("toml::parse_floating: NaN value found" 12481 " but the current environment does not support NaN. Please" 12482 " make sure that the floating-point implementation conforms" 12483 " IEEE 754/ISO 60559 international standard.", 12484 source_location(region(loc)), 12485 "floating_type: NaN is not supported")); 12486 } 12487 } 12488 else if(str == "-nan") 12489 { 12490 using std::copysign; 12491 TOML11_CONSTEXPR_IF(std::numeric_limits<floating_type>::has_quiet_NaN) 12492 { 12493 val = copysign(std::numeric_limits<floating_type>::quiet_NaN(), floating_type(-1)); 12494 } 12495 else TOML11_CONSTEXPR_IF(std::numeric_limits<floating_type>::has_signaling_NaN) 12496 { 12497 val = copysign(std::numeric_limits<floating_type>::signaling_NaN(), floating_type(-1)); 12498 } 12499 else 12500 { 12501 return err(make_error_info("toml::parse_floating: NaN value found" 12502 " but the current environment does not support NaN. Please" 12503 " make sure that the floating-point implementation conforms" 12504 " IEEE 754/ISO 60559 international standard.", 12505 source_location(region(loc)), 12506 "floating_type: NaN is not supported")); 12507 } 12508 } 12509 else 12510 { 12511 // set precision 12512 const auto has_sign = ! str.empty() && (str.front() == '+' || str.front() == '-'); 12513 const auto decpoint = std::find(str.begin(), str.end(), '.'); 12514 const auto exponent = std::find_if(str.begin(), str.end(), 12515 [](const char c) { return c == 'e' || c == 'E'; }); 12516 if(decpoint != str.end() && exponent != str.end()) 12517 { 12518 assert(decpoint < exponent); 12519 } 12520 12521 if(fmt.fmt == floating_format::scientific) 12522 { 12523 // total width 12524 fmt.prec = static_cast<std::size_t>(std::distance(str.begin(), exponent)); 12525 if(has_sign) 12526 { 12527 fmt.prec -= 1; 12528 } 12529 if(decpoint != str.end()) 12530 { 12531 fmt.prec -= 1; 12532 } 12533 } 12534 else if(fmt.fmt == floating_format::hex) 12535 { 12536 fmt.prec = std::numeric_limits<floating_type>::max_digits10; 12537 } 12538 else 12539 { 12540 // width after decimal point 12541 fmt.prec = static_cast<std::size_t>(std::distance(std::next(decpoint), exponent)); 12542 } 12543 12544 auto src = source_location(region(loc)); 12545 const auto res = TC::parse_float(str, src, is_hex); 12546 if(res.is_ok()) 12547 { 12548 val = res.as_ok(); 12549 } 12550 else 12551 { 12552 return err(res.as_err()); 12553 } 12554 } 12555 12556 // ---------------------------------------------------------------------- 12557 // parse suffix (extension) 12558 12559 if(spec.ext_num_suffix && loc.current() == '_') 12560 { 12561 const auto sfx_reg = syntax::num_suffix(spec).scan(loc); 12562 if( ! sfx_reg.is_ok()) 12563 { 12564 auto src = source_location(region(loc)); 12565 loc = first; 12566 return err(make_error_info("toml::parse_floating: " 12567 "invalid suffix: should be `_ non-digit-graph (graph | _graph)`", 12568 std::move(src), "here")); 12569 } 12570 auto sfx = sfx_reg.as_string(); 12571 assert( ! sfx.empty() && sfx.front() == '_'); 12572 sfx.erase(sfx.begin()); // remove the first `_` 12573 12574 fmt.suffix = sfx; 12575 } 12576 12577 return ok(basic_value<TC>(val, std::move(fmt), {}, std::move(reg))); 12578 } 12579 12580 /* ============================================================================ 12581 * ___ _ _ _ 12582 * | \ __ _| |_ ___| |_(_)_ __ ___ 12583 * | |) / _` | _/ -_) _| | ' \/ -_) 12584 * |___/\__,_|\__\___|\__|_|_|_|_\___| 12585 */ 12586 12587 // all the offset_datetime, local_datetime, local_date parses date part. 12588 template<typename TC> 12589 result<std::tuple<local_date, local_date_format_info, region>, error_info> 12590 parse_local_date_only(location& loc, const context<TC>& ctx) 12591 { 12592 const auto first = loc; 12593 const auto& spec = ctx.toml_spec(); 12594 12595 local_date_format_info fmt; 12596 12597 // ---------------------------------------------------------------------- 12598 // check syntax 12599 auto reg = syntax::local_date(spec).scan(loc); 12600 if( ! reg.is_ok()) 12601 { 12602 return err(make_syntax_error("toml::parse_local_date: " 12603 "invalid date: date must be like: 1234-05-06, yyyy-mm-dd.", 12604 syntax::local_date(spec), loc)); 12605 } 12606 12607 // ---------------------------------------------------------------------- 12608 // it matches. gen value 12609 const auto str = reg.as_string(); 12610 12611 // 0123456789 12612 // yyyy-mm-dd 12613 const auto year_r = from_string<int>(str.substr(0, 4)); 12614 const auto month_r = from_string<int>(str.substr(5, 2)); 12615 const auto day_r = from_string<int>(str.substr(8, 2)); 12616 12617 if(year_r.is_err()) 12618 { 12619 auto src = source_location(region(first)); 12620 return err(make_error_info("toml::parse_local_date: " 12621 "failed to read year `" + str.substr(0, 4) + "`", 12622 std::move(src), "here")); 12623 } 12624 if(month_r.is_err()) 12625 { 12626 auto src = source_location(region(first)); 12627 return err(make_error_info("toml::parse_local_date: " 12628 "failed to read month `" + str.substr(5, 2) + "`", 12629 std::move(src), "here")); 12630 } 12631 if(day_r.is_err()) 12632 { 12633 auto src = source_location(region(first)); 12634 return err(make_error_info("toml::parse_local_date: " 12635 "failed to read day `" + str.substr(8, 2) + "`", 12636 std::move(src), "here")); 12637 } 12638 12639 const auto year = year_r.unwrap(); 12640 const auto month = month_r.unwrap(); 12641 const auto day = day_r.unwrap(); 12642 12643 { 12644 // We briefly check whether the input date is valid or not. 12645 // Actually, because of the historical reasons, there are several 12646 // edge cases, such as 1582/10/5-1582/10/14 (only in several countries). 12647 // But here, we do not care about it. 12648 // It makes the code complicated and there is only low probability 12649 // that such a specific date is needed in practice. If someone need to 12650 // validate date accurately, that means that the one need a specialized 12651 // library for their purpose in another layer. 12652 12653 const bool is_leap = (year % 4 == 0) && ((year % 100 != 0) || (year % 400 == 0)); 12654 const auto max_day = [month, is_leap]() { 12655 if(month == 2) 12656 { 12657 return is_leap ? 29 : 28; 12658 } 12659 if(month == 4 || month == 6 || month == 9 || month == 11) 12660 { 12661 return 30; 12662 } 12663 return 31; 12664 }(); 12665 12666 if((month < 1 || 12 < month) || (day < 1 || max_day < day)) 12667 { 12668 auto src = source_location(region(first)); 12669 return err(make_error_info("toml::parse_local_date: invalid date.", 12670 std::move(src), "month must be 01-12, day must be any of " 12671 "01-28,29,30,31 depending on the month/year.")); 12672 } 12673 } 12674 12675 return ok(std::make_tuple( 12676 local_date(year, static_cast<month_t>(month - 1), day), 12677 std::move(fmt), std::move(reg) 12678 )); 12679 } 12680 12681 template<typename TC> 12682 result<basic_value<TC>, error_info> 12683 parse_local_date(location& loc, const context<TC>& ctx) 12684 { 12685 auto val_fmt_reg = parse_local_date_only(loc, ctx); 12686 if(val_fmt_reg.is_err()) 12687 { 12688 return err(val_fmt_reg.unwrap_err()); 12689 } 12690 12691 auto val = std::move(std::get<0>(val_fmt_reg.unwrap())); 12692 auto fmt = std::move(std::get<1>(val_fmt_reg.unwrap())); 12693 auto reg = std::move(std::get<2>(val_fmt_reg.unwrap())); 12694 12695 return ok(basic_value<TC>(std::move(val), std::move(fmt), {}, std::move(reg))); 12696 } 12697 12698 // all the offset_datetime, local_datetime, local_time parses date part. 12699 template<typename TC> 12700 result<std::tuple<local_time, local_time_format_info, region>, error_info> 12701 parse_local_time_only(location& loc, const context<TC>& ctx) 12702 { 12703 const auto first = loc; 12704 const auto& spec = ctx.toml_spec(); 12705 12706 local_time_format_info fmt; 12707 12708 // ---------------------------------------------------------------------- 12709 // check syntax 12710 auto reg = syntax::local_time(spec).scan(loc); 12711 if( ! reg.is_ok()) 12712 { 12713 if(spec.v1_1_0_make_seconds_optional) 12714 { 12715 return err(make_syntax_error("toml::parse_local_time: " 12716 "invalid time: time must be HH:MM(:SS.sss) (seconds are optional)", 12717 syntax::local_time(spec), loc)); 12718 } 12719 else 12720 { 12721 return err(make_syntax_error("toml::parse_local_time: " 12722 "invalid time: time must be HH:MM:SS(.sss) (subseconds are optional)", 12723 syntax::local_time(spec), loc)); 12724 } 12725 } 12726 12727 // ---------------------------------------------------------------------- 12728 // it matches. gen value 12729 const auto str = reg.as_string(); 12730 12731 // at least we have HH:MM. 12732 // 01234 12733 // HH:MM 12734 const auto hour_r = from_string<int>(str.substr(0, 2)); 12735 const auto minute_r = from_string<int>(str.substr(3, 2)); 12736 12737 if(hour_r.is_err()) 12738 { 12739 auto src = source_location(region(first)); 12740 return err(make_error_info("toml::parse_local_time: " 12741 "failed to read hour `" + str.substr(0, 2) + "`", 12742 std::move(src), "here")); 12743 } 12744 if(minute_r.is_err()) 12745 { 12746 auto src = source_location(region(first)); 12747 return err(make_error_info("toml::parse_local_time: " 12748 "failed to read minute `" + str.substr(3, 2) + "`", 12749 std::move(src), "here")); 12750 } 12751 12752 const auto hour = hour_r.unwrap(); 12753 const auto minute = minute_r.unwrap(); 12754 12755 if((hour < 0 || 24 <= hour) || (minute < 0 || 60 <= minute)) 12756 { 12757 auto src = source_location(region(first)); 12758 return err(make_error_info("toml::parse_local_time: invalid time.", 12759 std::move(src), "hour must be 00-23, minute must be 00-59.")); 12760 } 12761 12762 // ----------------------------------------------------------------------- 12763 // we have hour and minute. 12764 // Since toml v1.1.0, second and subsecond part becomes optional. 12765 // Check the version and return if second does not exist. 12766 12767 if(str.size() == 5 && spec.v1_1_0_make_seconds_optional) 12768 { 12769 fmt.has_seconds = false; 12770 fmt.subsecond_precision = 0; 12771 return ok(std::make_tuple(local_time(hour, minute, 0), std::move(fmt), std::move(reg))); 12772 } 12773 assert(str.at(5) == ':'); 12774 12775 // we have at least `:SS` part. `.subseconds` are optional. 12776 12777 // 0 1 12778 // 012345678901234 12779 // HH:MM:SS.subsec 12780 const auto sec_r = from_string<int>(str.substr(6, 2)); 12781 if(sec_r.is_err()) 12782 { 12783 auto src = source_location(region(first)); 12784 return err(make_error_info("toml::parse_local_time: " 12785 "failed to read second `" + str.substr(6, 2) + "`", 12786 std::move(src), "here")); 12787 } 12788 const auto sec = sec_r.unwrap(); 12789 12790 if(sec < 0 || 60 < sec) // :60 is allowed 12791 { 12792 auto src = source_location(region(first)); 12793 return err(make_error_info("toml::parse_local_time: invalid time.", 12794 std::move(src), "second must be 00-60.")); 12795 } 12796 12797 if(str.size() == 8) 12798 { 12799 fmt.has_seconds = true; 12800 fmt.subsecond_precision = 0; 12801 return ok(std::make_tuple(local_time(hour, minute, sec), std::move(fmt), std::move(reg))); 12802 } 12803 12804 assert(str.at(8) == '.'); 12805 12806 auto secfrac = str.substr(9, str.size() - 9); 12807 12808 fmt.has_seconds = true; 12809 fmt.subsecond_precision = secfrac.size(); 12810 12811 while(secfrac.size() < 9) 12812 { 12813 secfrac += '0'; 12814 } 12815 assert(9 <= secfrac.size()); 12816 const auto ms_r = from_string<int>(secfrac.substr(0, 3)); 12817 const auto us_r = from_string<int>(secfrac.substr(3, 3)); 12818 const auto ns_r = from_string<int>(secfrac.substr(6, 3)); 12819 12820 if(ms_r.is_err()) 12821 { 12822 auto src = source_location(region(first)); 12823 return err(make_error_info("toml::parse_local_time: " 12824 "failed to read milliseconds `" + secfrac.substr(0, 3) + "`", 12825 std::move(src), "here")); 12826 } 12827 if(us_r.is_err()) 12828 { 12829 auto src = source_location(region(first)); 12830 return err(make_error_info("toml::parse_local_time: " 12831 "failed to read microseconds`" + str.substr(3, 3) + "`", 12832 std::move(src), "here")); 12833 } 12834 if(ns_r.is_err()) 12835 { 12836 auto src = source_location(region(first)); 12837 return err(make_error_info("toml::parse_local_time: " 12838 "failed to read nanoseconds`" + str.substr(6, 3) + "`", 12839 std::move(src), "here")); 12840 } 12841 const auto ms = ms_r.unwrap(); 12842 const auto us = us_r.unwrap(); 12843 const auto ns = ns_r.unwrap(); 12844 12845 return ok(std::make_tuple(local_time(hour, minute, sec, ms, us, ns), std::move(fmt), std::move(reg))); 12846 } 12847 12848 template<typename TC> 12849 result<basic_value<TC>, error_info> 12850 parse_local_time(location& loc, const context<TC>& ctx) 12851 { 12852 const auto first = loc; 12853 12854 auto val_fmt_reg = parse_local_time_only(loc, ctx); 12855 if(val_fmt_reg.is_err()) 12856 { 12857 return err(val_fmt_reg.unwrap_err()); 12858 } 12859 12860 auto val = std::move(std::get<0>(val_fmt_reg.unwrap())); 12861 auto fmt = std::move(std::get<1>(val_fmt_reg.unwrap())); 12862 auto reg = std::move(std::get<2>(val_fmt_reg.unwrap())); 12863 12864 return ok(basic_value<TC>(std::move(val), std::move(fmt), {}, std::move(reg))); 12865 } 12866 12867 template<typename TC> 12868 result<basic_value<TC>, error_info> 12869 parse_local_datetime(location& loc, const context<TC>& ctx) 12870 { 12871 using char_type = location::char_type; 12872 12873 const auto first = loc; 12874 12875 local_datetime_format_info fmt; 12876 12877 // ---------------------------------------------------------------------- 12878 12879 auto date_fmt_reg = parse_local_date_only(loc, ctx); 12880 if(date_fmt_reg.is_err()) 12881 { 12882 return err(date_fmt_reg.unwrap_err()); 12883 } 12884 12885 if(loc.current() == char_type('T')) 12886 { 12887 loc.advance(); 12888 fmt.delimiter = datetime_delimiter_kind::upper_T; 12889 } 12890 else if(loc.current() == char_type('t')) 12891 { 12892 loc.advance(); 12893 fmt.delimiter = datetime_delimiter_kind::lower_t; 12894 } 12895 else if(loc.current() == char_type(' ')) 12896 { 12897 loc.advance(); 12898 fmt.delimiter = datetime_delimiter_kind::space; 12899 } 12900 else 12901 { 12902 auto src = source_location(region(loc)); 12903 return err(make_error_info("toml::parse_local_datetime: " 12904 "expect date-time delimiter `T`, `t` or ` `(space).", 12905 std::move(src), "here")); 12906 } 12907 12908 auto time_fmt_reg = parse_local_time_only(loc, ctx); 12909 if(time_fmt_reg.is_err()) 12910 { 12911 return err(time_fmt_reg.unwrap_err()); 12912 } 12913 12914 fmt.has_seconds = std::get<1>(time_fmt_reg.unwrap()).has_seconds; 12915 fmt.subsecond_precision = std::get<1>(time_fmt_reg.unwrap()).subsecond_precision; 12916 12917 // ---------------------------------------------------------------------- 12918 12919 region reg(first, loc); 12920 local_datetime val(std::get<0>(date_fmt_reg.unwrap()), 12921 std::get<0>(time_fmt_reg.unwrap())); 12922 12923 return ok(basic_value<TC>(val, std::move(fmt), {}, std::move(reg))); 12924 } 12925 12926 template<typename TC> 12927 result<basic_value<TC>, error_info> 12928 parse_offset_datetime(location& loc, const context<TC>& ctx) 12929 { 12930 using char_type = location::char_type; 12931 12932 const auto first = loc; 12933 const auto& spec = ctx.toml_spec(); 12934 12935 offset_datetime_format_info fmt; 12936 12937 // ---------------------------------------------------------------------- 12938 // date part 12939 12940 auto date_fmt_reg = parse_local_date_only(loc, ctx); 12941 if(date_fmt_reg.is_err()) 12942 { 12943 return err(date_fmt_reg.unwrap_err()); 12944 } 12945 12946 // ---------------------------------------------------------------------- 12947 // delimiter 12948 12949 if(loc.current() == char_type('T')) 12950 { 12951 loc.advance(); 12952 fmt.delimiter = datetime_delimiter_kind::upper_T; 12953 } 12954 else if(loc.current() == char_type('t')) 12955 { 12956 loc.advance(); 12957 fmt.delimiter = datetime_delimiter_kind::lower_t; 12958 } 12959 else if(loc.current() == char_type(' ')) 12960 { 12961 loc.advance(); 12962 fmt.delimiter = datetime_delimiter_kind::space; 12963 } 12964 else 12965 { 12966 auto src = source_location(region(loc)); 12967 return err(make_error_info("toml::parse_offset_datetime: " 12968 "expect date-time delimiter `T` or ` `(space).", std::move(src), "here" 12969 )); 12970 } 12971 12972 // ---------------------------------------------------------------------- 12973 // time part 12974 12975 auto time_fmt_reg = parse_local_time_only(loc, ctx); 12976 if(time_fmt_reg.is_err()) 12977 { 12978 return err(time_fmt_reg.unwrap_err()); 12979 } 12980 12981 fmt.has_seconds = std::get<1>(time_fmt_reg.unwrap()).has_seconds; 12982 fmt.subsecond_precision = std::get<1>(time_fmt_reg.unwrap()).subsecond_precision; 12983 12984 // ---------------------------------------------------------------------- 12985 // offset part 12986 12987 const auto ofs_reg = syntax::time_offset(spec).scan(loc); 12988 if( ! ofs_reg.is_ok()) 12989 { 12990 return err(make_syntax_error("toml::parse_offset_datetime: " 12991 "invalid offset: offset must be like: Z, +01:00, or -10:00.", 12992 syntax::time_offset(spec), loc)); 12993 } 12994 12995 const auto ofs_str = ofs_reg.as_string(); 12996 12997 time_offset offset(0, 0); 12998 12999 assert(ofs_str.size() != 0); 13000 13001 if(ofs_str.at(0) == char_type('+') || ofs_str.at(0) == char_type('-')) 13002 { 13003 const auto hour_r = from_string<int>(ofs_str.substr(1, 2)); 13004 const auto minute_r = from_string<int>(ofs_str.substr(4, 2)); 13005 if(hour_r.is_err()) 13006 { 13007 auto src = source_location(region(loc)); 13008 return err(make_error_info("toml::parse_offset_datetime: " 13009 "Failed to read offset hour part", std::move(src), "here")); 13010 } 13011 if(minute_r.is_err()) 13012 { 13013 auto src = source_location(region(loc)); 13014 return err(make_error_info("toml::parse_offset_datetime: " 13015 "Failed to read offset minute part", std::move(src), "here")); 13016 } 13017 const auto hour = hour_r.unwrap(); 13018 const auto minute = minute_r.unwrap(); 13019 13020 if(ofs_str.at(0) == '+') 13021 { 13022 offset = time_offset(hour, minute); 13023 } 13024 else 13025 { 13026 offset = time_offset(-hour, -minute); 13027 } 13028 } 13029 else 13030 { 13031 assert(ofs_str.at(0) == char_type('Z') || ofs_str.at(0) == char_type('z')); 13032 } 13033 13034 if (offset.hour < -24 || 24 < offset.hour || 13035 offset.minute < -60 || 60 < offset.minute) 13036 { 13037 return err(make_error_info("toml::parse_offset_datetime: " 13038 "too large offset: |hour| <= 24, |minute| <= 60", 13039 source_location(region(first, loc)), "here")); 13040 } 13041 13042 13043 // ---------------------------------------------------------------------- 13044 13045 region reg(first, loc); 13046 offset_datetime val(local_datetime(std::get<0>(date_fmt_reg.unwrap()), 13047 std::get<0>(time_fmt_reg.unwrap())), 13048 offset); 13049 13050 return ok(basic_value<TC>(val, std::move(fmt), {}, std::move(reg))); 13051 } 13052 13053 /* ============================================================================ 13054 * ___ _ _ 13055 * / __| |_ _ _(_)_ _ __ _ 13056 * \__ \ _| '_| | ' \/ _` | 13057 * |___/\__|_| |_|_||_\__, | 13058 * |___/ 13059 */ 13060 13061 template<typename TC> 13062 result<typename basic_value<TC>::string_type, error_info> 13063 parse_utf8_codepoint(const region& reg) 13064 { 13065 using string_type = typename basic_value<TC>::string_type; 13066 using char_type = typename string_type::value_type; 13067 13068 // assert(reg.as_lines().size() == 1); // XXX heavy check 13069 13070 const auto str = reg.as_string(); 13071 assert( ! str.empty()); 13072 assert(str.front() == 'u' || str.front() == 'U' || str.front() == 'x'); 13073 13074 std::uint_least32_t codepoint; 13075 std::istringstream iss(str.substr(1)); 13076 iss >> std::hex >> codepoint; 13077 13078 const auto to_char = [](const std::uint_least32_t i) noexcept -> char_type { 13079 const auto uc = static_cast<unsigned char>(i & 0xFF); 13080 return cxx::bit_cast<char_type>(uc); 13081 }; 13082 13083 string_type character; 13084 if(codepoint < 0x80) // U+0000 ... U+0079 ; just an ASCII. 13085 { 13086 character += static_cast<char>(codepoint); 13087 } 13088 else if(codepoint < 0x800) //U+0080 ... U+07FF 13089 { 13090 // 110yyyyx 10xxxxxx; 0x3f == 0b0011'1111 13091 character += to_char(0xC0|(codepoint >> 6 )); 13092 character += to_char(0x80|(codepoint & 0x3F)); 13093 } 13094 else if(codepoint < 0x10000) // U+0800...U+FFFF 13095 { 13096 if(0xD800 <= codepoint && codepoint <= 0xDFFF) 13097 { 13098 auto src = source_location(reg); 13099 return err(make_error_info("toml::parse_utf8_codepoint: " 13100 "[0xD800, 0xDFFF] is not a valid UTF-8", 13101 std::move(src), "here")); 13102 } 13103 assert(codepoint < 0xD800 || 0xDFFF < codepoint); 13104 // 1110yyyy 10yxxxxx 10xxxxxx 13105 character += to_char(0xE0| (codepoint >> 12)); 13106 character += to_char(0x80|((codepoint >> 6 ) & 0x3F)); 13107 character += to_char(0x80|((codepoint ) & 0x3F)); 13108 } 13109 else if(codepoint < 0x110000) // U+010000 ... U+10FFFF 13110 { 13111 // 11110yyy 10yyxxxx 10xxxxxx 10xxxxxx 13112 character += to_char(0xF0| (codepoint >> 18)); 13113 character += to_char(0x80|((codepoint >> 12) & 0x3F)); 13114 character += to_char(0x80|((codepoint >> 6 ) & 0x3F)); 13115 character += to_char(0x80|((codepoint ) & 0x3F)); 13116 } 13117 else // out of UTF-8 region 13118 { 13119 auto src = source_location(reg); 13120 return err(make_error_info("toml::parse_utf8_codepoint: " 13121 "input codepoint is too large.", 13122 std::move(src), "must be in range [0x00, 0x10FFFF]")); 13123 } 13124 return ok(character); 13125 } 13126 13127 template<typename TC> 13128 result<typename basic_value<TC>::string_type, error_info> 13129 parse_escape_sequence(location& loc, const context<TC>& ctx) 13130 { 13131 using string_type = typename basic_value<TC>::string_type; 13132 using char_type = typename string_type::value_type; 13133 13134 const auto& spec = ctx.toml_spec(); 13135 13136 assert( ! loc.eof()); 13137 assert(loc.current() == '\\'); 13138 loc.advance(); // consume the first backslash 13139 13140 string_type retval; 13141 13142 if (loc.current() == '\\') { retval += char_type('\\'); loc.advance(); } 13143 else if(loc.current() == '"') { retval += char_type('\"'); loc.advance(); } 13144 else if(loc.current() == 'b') { retval += char_type('\b'); loc.advance(); } 13145 else if(loc.current() == 'f') { retval += char_type('\f'); loc.advance(); } 13146 else if(loc.current() == 'n') { retval += char_type('\n'); loc.advance(); } 13147 else if(loc.current() == 'r') { retval += char_type('\r'); loc.advance(); } 13148 else if(loc.current() == 't') { retval += char_type('\t'); loc.advance(); } 13149 else if(spec.v1_1_0_add_escape_sequence_e && loc.current() == 'e') 13150 { 13151 retval += char_type('\x1b'); 13152 loc.advance(); 13153 } 13154 else if(spec.v1_1_0_add_escape_sequence_x && loc.current() == 'x') 13155 { 13156 auto scanner = sequence(character('x'), repeat_exact(2, syntax::hexdig(spec))); 13157 const auto reg = scanner.scan(loc); 13158 if( ! reg.is_ok()) 13159 { 13160 auto src = source_location(region(loc)); 13161 return err(make_error_info("toml::parse_escape_sequence: " 13162 "invalid token found in UTF-8 codepoint \\xhh", 13163 std::move(src), "here")); 13164 } 13165 const auto utf8 = parse_utf8_codepoint<TC>(reg); 13166 if(utf8.is_err()) 13167 { 13168 return err(utf8.as_err()); 13169 } 13170 retval += utf8.unwrap(); 13171 } 13172 else if(loc.current() == 'u') 13173 { 13174 auto scanner = sequence(character('u'), repeat_exact(4, syntax::hexdig(spec))); 13175 const auto reg = scanner.scan(loc); 13176 if( ! reg.is_ok()) 13177 { 13178 auto src = source_location(region(loc)); 13179 return err(make_error_info("toml::parse_escape_sequence: " 13180 "invalid token found in UTF-8 codepoint \\uhhhh", 13181 std::move(src), "here")); 13182 } 13183 const auto utf8 = parse_utf8_codepoint<TC>(reg); 13184 if(utf8.is_err()) 13185 { 13186 return err(utf8.as_err()); 13187 } 13188 retval += utf8.unwrap(); 13189 } 13190 else if(loc.current() == 'U') 13191 { 13192 auto scanner = sequence(character('U'), repeat_exact(8, syntax::hexdig(spec))); 13193 const auto reg = scanner.scan(loc); 13194 if( ! reg.is_ok()) 13195 { 13196 auto src = source_location(region(loc)); 13197 return err(make_error_info("toml::parse_escape_sequence: " 13198 "invalid token found in UTF-8 codepoint \\Uhhhhhhhh", 13199 std::move(src), "here")); 13200 } 13201 const auto utf8 = parse_utf8_codepoint<TC>(reg); 13202 if(utf8.is_err()) 13203 { 13204 return err(utf8.as_err()); 13205 } 13206 retval += utf8.unwrap(); 13207 } 13208 else 13209 { 13210 auto src = source_location(region(loc)); 13211 std::string escape_seqs = "allowed escape seqs: \\\\, \\\", \\b, \\f, \\n, \\r, \\t"; 13212 if(spec.v1_1_0_add_escape_sequence_e) 13213 { 13214 escape_seqs += ", \\e"; 13215 } 13216 if(spec.v1_1_0_add_escape_sequence_x) 13217 { 13218 escape_seqs += ", \\xhh"; 13219 } 13220 escape_seqs += ", \\uhhhh, or \\Uhhhhhhhh"; 13221 13222 return err(make_error_info("toml::parse_escape_sequence: " 13223 "unknown escape sequence.", std::move(src), escape_seqs)); 13224 } 13225 return ok(retval); 13226 } 13227 13228 template<typename TC> 13229 result<basic_value<TC>, error_info> 13230 parse_ml_basic_string(location& loc, const context<TC>& ctx) 13231 { 13232 const auto first = loc; 13233 const auto& spec = ctx.toml_spec(); 13234 13235 string_format_info fmt; 13236 fmt.fmt = string_format::multiline_basic; 13237 13238 auto reg = syntax::ml_basic_string(spec).scan(loc); 13239 if( ! reg.is_ok()) 13240 { 13241 return err(make_syntax_error("toml::parse_ml_basic_string: " 13242 "invalid string format", 13243 syntax::ml_basic_string(spec), loc)); 13244 } 13245 13246 // ---------------------------------------------------------------------- 13247 // it matches. gen value 13248 13249 auto str = reg.as_string(); 13250 13251 // we already checked that it starts with """ and ends with """. 13252 assert(str.substr(0, 3) == "\"\"\""); 13253 str.erase(0, 3); 13254 13255 assert(str.size() >= 3); 13256 assert(str.substr(str.size()-3, 3) == "\"\"\""); 13257 str.erase(str.size()-3, 3); 13258 13259 // the first newline just after """ is trimmed 13260 if(str.size() >= 1 && str.at(0) == '\n') 13261 { 13262 str.erase(0, 1); 13263 fmt.start_with_newline = true; 13264 } 13265 else if(str.size() >= 2 && str.at(0) == '\r' && str.at(1) == '\n') 13266 { 13267 str.erase(0, 2); 13268 fmt.start_with_newline = true; 13269 } 13270 13271 using string_type = typename basic_value<TC>::string_type; 13272 string_type val; 13273 { 13274 auto iter = str.cbegin(); 13275 while(iter != str.cend()) 13276 { 13277 if(*iter == '\\') // remove whitespaces around escaped-newline 13278 { 13279 // we assume that the string is not too long to copy 13280 auto loc2 = make_temporary_location(make_string(iter, str.cend())); 13281 if(syntax::escaped_newline(spec).scan(loc2).is_ok()) 13282 { 13283 std::advance(iter, loc2.get_location()); // skip escaped newline and indent 13284 // now iter points non-WS char 13285 assert(iter == str.end() || (*iter != ' ' && *iter != '\t')); 13286 } 13287 else // normal escape seq. 13288 { 13289 auto esc = parse_escape_sequence(loc2, ctx); 13290 13291 // syntax does not check its value. the unicode codepoint may be 13292 // invalid, e.g. out-of-bound, [0xD800, 0xDFFF] 13293 if(esc.is_err()) 13294 { 13295 return err(esc.unwrap_err()); 13296 } 13297 13298 val += esc.unwrap(); 13299 std::advance(iter, loc2.get_location()); 13300 } 13301 } 13302 else // we already checked the syntax. we don't need to check it again. 13303 { 13304 val += static_cast<typename string_type::value_type>(*iter); 13305 ++iter; 13306 } 13307 } 13308 } 13309 13310 return ok(basic_value<TC>( 13311 std::move(val), std::move(fmt), {}, std::move(reg) 13312 )); 13313 } 13314 13315 template<typename TC> 13316 result<std::pair<typename basic_value<TC>::string_type, region>, error_info> 13317 parse_basic_string_only(location& loc, const context<TC>& ctx) 13318 { 13319 const auto first = loc; 13320 const auto& spec = ctx.toml_spec(); 13321 13322 auto reg = syntax::basic_string(spec).scan(loc); 13323 if( ! reg.is_ok()) 13324 { 13325 return err(make_syntax_error("toml::parse_basic_string: " 13326 "invalid string format", 13327 syntax::basic_string(spec), loc)); 13328 } 13329 13330 // ---------------------------------------------------------------------- 13331 // it matches. gen value 13332 13333 auto str = reg.as_string(); 13334 13335 assert(str.back() == '\"'); 13336 str.pop_back(); 13337 assert(str.at(0) == '\"'); 13338 str.erase(0, 1); 13339 13340 using string_type = typename basic_value<TC>::string_type; 13341 using char_type = typename string_type::value_type; 13342 string_type val; 13343 13344 { 13345 auto iter = str.begin(); 13346 while(iter != str.end()) 13347 { 13348 if(*iter == '\\') 13349 { 13350 auto loc2 = make_temporary_location(make_string(iter, str.end())); 13351 13352 auto esc = parse_escape_sequence(loc2, ctx); 13353 13354 // syntax does not check its value. the unicode codepoint may be 13355 // invalid, e.g. out-of-bound, [0xD800, 0xDFFF] 13356 if(esc.is_err()) 13357 { 13358 return err(esc.unwrap_err()); 13359 } 13360 13361 val += esc.unwrap(); 13362 std::advance(iter, loc2.get_location()); 13363 } 13364 else 13365 { 13366 val += char_type(*iter); // we already checked the syntax. 13367 ++iter; 13368 } 13369 } 13370 } 13371 return ok(std::make_pair(val, reg)); 13372 } 13373 13374 template<typename TC> 13375 result<basic_value<TC>, error_info> 13376 parse_basic_string(location& loc, const context<TC>& ctx) 13377 { 13378 const auto first = loc; 13379 13380 string_format_info fmt; 13381 fmt.fmt = string_format::basic; 13382 13383 auto val_res = parse_basic_string_only(loc, ctx); 13384 if(val_res.is_err()) 13385 { 13386 return err(std::move(val_res.unwrap_err())); 13387 } 13388 auto val = std::move(val_res.unwrap().first ); 13389 auto reg = std::move(val_res.unwrap().second); 13390 13391 return ok(basic_value<TC>(std::move(val), std::move(fmt), {}, std::move(reg))); 13392 } 13393 13394 template<typename TC> 13395 result<basic_value<TC>, error_info> 13396 parse_ml_literal_string(location& loc, const context<TC>& ctx) 13397 { 13398 const auto first = loc; 13399 const auto& spec = ctx.toml_spec(); 13400 13401 string_format_info fmt; 13402 fmt.fmt = string_format::multiline_literal; 13403 13404 auto reg = syntax::ml_literal_string(spec).scan(loc); 13405 if( ! reg.is_ok()) 13406 { 13407 return err(make_syntax_error("toml::parse_ml_literal_string: " 13408 "invalid string format", 13409 syntax::ml_literal_string(spec), loc)); 13410 } 13411 13412 // ---------------------------------------------------------------------- 13413 // it matches. gen value 13414 13415 auto str = reg.as_string(); 13416 13417 assert(str.substr(0, 3) == "'''"); 13418 assert(str.substr(str.size()-3, 3) == "'''"); 13419 str.erase(0, 3); 13420 str.erase(str.size()-3, 3); 13421 13422 // the first newline just after """ is trimmed 13423 if(str.size() >= 1 && str.at(0) == '\n') 13424 { 13425 str.erase(0, 1); 13426 fmt.start_with_newline = true; 13427 } 13428 else if(str.size() >= 2 && str.at(0) == '\r' && str.at(1) == '\n') 13429 { 13430 str.erase(0, 2); 13431 fmt.start_with_newline = true; 13432 } 13433 13434 using string_type = typename basic_value<TC>::string_type; 13435 string_type val(str.begin(), str.end()); 13436 13437 return ok(basic_value<TC>( 13438 std::move(val), std::move(fmt), {}, std::move(reg) 13439 )); 13440 } 13441 13442 template<typename TC> 13443 result<std::pair<typename basic_value<TC>::string_type, region>, error_info> 13444 parse_literal_string_only(location& loc, const context<TC>& ctx) 13445 { 13446 const auto first = loc; 13447 const auto& spec = ctx.toml_spec(); 13448 13449 auto reg = syntax::literal_string(spec).scan(loc); 13450 if( ! reg.is_ok()) 13451 { 13452 return err(make_syntax_error("toml::parse_literal_string: " 13453 "invalid string format", 13454 syntax::literal_string(spec), loc)); 13455 } 13456 13457 // ---------------------------------------------------------------------- 13458 // it matches. gen value 13459 13460 auto str = reg.as_string(); 13461 13462 assert(str.back() == '\''); 13463 str.pop_back(); 13464 assert(str.at(0) == '\''); 13465 str.erase(0, 1); 13466 13467 using string_type = typename basic_value<TC>::string_type; 13468 string_type val(str.begin(), str.end()); 13469 13470 return ok(std::make_pair(std::move(val), std::move(reg))); 13471 } 13472 13473 template<typename TC> 13474 result<basic_value<TC>, error_info> 13475 parse_literal_string(location& loc, const context<TC>& ctx) 13476 { 13477 const auto first = loc; 13478 13479 string_format_info fmt; 13480 fmt.fmt = string_format::literal; 13481 13482 auto val_res = parse_literal_string_only(loc, ctx); 13483 if(val_res.is_err()) 13484 { 13485 return err(std::move(val_res.unwrap_err())); 13486 } 13487 auto val = std::move(val_res.unwrap().first ); 13488 auto reg = std::move(val_res.unwrap().second); 13489 13490 return ok(basic_value<TC>( 13491 std::move(val), std::move(fmt), {}, std::move(reg) 13492 )); 13493 } 13494 13495 template<typename TC> 13496 result<basic_value<TC>, error_info> 13497 parse_string(location& loc, const context<TC>& ctx) 13498 { 13499 const auto first = loc; 13500 13501 if( ! loc.eof() && loc.current() == '"') 13502 { 13503 if(literal("\"\"\"").scan(loc).is_ok()) 13504 { 13505 loc = first; 13506 return parse_ml_basic_string(loc, ctx); 13507 } 13508 else 13509 { 13510 loc = first; 13511 return parse_basic_string(loc, ctx); 13512 } 13513 } 13514 else if( ! loc.eof() && loc.current() == '\'') 13515 { 13516 if(literal("'''").scan(loc).is_ok()) 13517 { 13518 loc = first; 13519 return parse_ml_literal_string(loc, ctx); 13520 } 13521 else 13522 { 13523 loc = first; 13524 return parse_literal_string(loc, ctx); 13525 } 13526 } 13527 else 13528 { 13529 auto src = source_location(region(loc)); 13530 return err(make_error_info("toml::parse_string: " 13531 "not a string", std::move(src), "here")); 13532 } 13533 } 13534 13535 template<typename TC> 13536 result<basic_value<TC>, error_info> 13537 parse_null(location& loc, const context<TC>& ctx) 13538 { 13539 const auto& spec = ctx.toml_spec(); 13540 if( ! spec.ext_null_value) 13541 { 13542 return err(make_error_info("toml::parse_null: " 13543 "invalid spec: spec.ext_null_value must be true.", 13544 source_location(region(loc)), "here")); 13545 } 13546 13547 // ---------------------------------------------------------------------- 13548 // check syntax 13549 auto reg = syntax::null_value(spec).scan(loc); 13550 if( ! reg.is_ok()) 13551 { 13552 return err(make_syntax_error("toml::parse_null: " 13553 "invalid null: null must be lowercase. ", 13554 syntax::null_value(spec), loc)); 13555 } 13556 13557 // ---------------------------------------------------------------------- 13558 // it matches. gen value 13559 13560 // ---------------------------------------------------------------------- 13561 // no format info for boolean 13562 13563 return ok(basic_value<TC>(detail::none_t{}, std::move(reg))); 13564 } 13565 13566 /* ============================================================================ 13567 * _ __ 13568 * | |/ /___ _ _ 13569 * | ' </ -_) || | 13570 * |_|\_\___|\_, | 13571 * |__/ 13572 */ 13573 13574 // non-dotted key. 13575 template<typename TC> 13576 result<typename basic_value<TC>::key_type, error_info> 13577 parse_simple_key(location& loc, const context<TC>& ctx) 13578 { 13579 using key_type = typename basic_value<TC>::key_type; 13580 const auto& spec = ctx.toml_spec(); 13581 13582 if(loc.current() == '\"') 13583 { 13584 auto str_res = parse_basic_string_only(loc, ctx); 13585 if(str_res.is_ok()) 13586 { 13587 return ok(std::move(str_res.unwrap().first)); 13588 } 13589 else 13590 { 13591 return err(std::move(str_res.unwrap_err())); 13592 } 13593 } 13594 else if(loc.current() == '\'') 13595 { 13596 auto str_res = parse_literal_string_only(loc, ctx); 13597 if(str_res.is_ok()) 13598 { 13599 return ok(std::move(str_res.unwrap().first)); 13600 } 13601 else 13602 { 13603 return err(std::move(str_res.unwrap_err())); 13604 } 13605 } 13606 13607 // bare key. 13608 13609 if(const auto bare = syntax::unquoted_key(spec).scan(loc)) 13610 { 13611 return ok(string_conv<key_type>(bare.as_string())); 13612 } 13613 else 13614 { 13615 std::string postfix; 13616 if(spec.v1_1_0_allow_non_english_in_bare_keys) 13617 { 13618 postfix = "Hint: Not all Unicode characters are allowed as bare key.\n"; 13619 } 13620 else 13621 { 13622 postfix = "Hint: non-ASCII scripts are allowed in toml v1.1.0, but not in v1.0.0.\n"; 13623 } 13624 return err(make_syntax_error("toml::parse_simple_key: " 13625 "invalid key: key must be \"quoted\", 'quoted-literal', or bare key.", 13626 syntax::unquoted_key(spec), loc, postfix)); 13627 } 13628 } 13629 13630 // dotted key become vector of keys 13631 template<typename TC> 13632 result<std::pair<std::vector<typename basic_value<TC>::key_type>, region>, error_info> 13633 parse_key(location& loc, const context<TC>& ctx) 13634 { 13635 const auto first = loc; 13636 const auto& spec = ctx.toml_spec(); 13637 13638 using key_type = typename basic_value<TC>::key_type; 13639 std::vector<key_type> keys; 13640 while( ! loc.eof()) 13641 { 13642 auto key = parse_simple_key(loc, ctx); 13643 if( ! key.is_ok()) 13644 { 13645 return err(key.unwrap_err()); 13646 } 13647 keys.push_back(std::move(key.unwrap())); 13648 13649 auto reg = syntax::dot_sep(spec).scan(loc); 13650 if( ! reg.is_ok()) 13651 { 13652 break; 13653 } 13654 } 13655 if(keys.empty()) 13656 { 13657 auto src = source_location(region(first)); 13658 return err(make_error_info("toml::parse_key: expected a new key, " 13659 "but got nothing", std::move(src), "reached EOF")); 13660 } 13661 13662 return ok(std::make_pair(std::move(keys), region(first, loc))); 13663 } 13664 13665 // ============================================================================ 13666 13667 // forward-decl to implement parse_array and parse_table 13668 template<typename TC> 13669 result<basic_value<TC>, error_info> 13670 parse_value(location&, context<TC>& ctx); 13671 13672 template<typename TC> 13673 result<std::pair< 13674 std::pair<std::vector<typename basic_value<TC>::key_type>, region>, 13675 basic_value<TC> 13676 >, error_info> 13677 parse_key_value_pair(location& loc, context<TC>& ctx) 13678 { 13679 const auto first = loc; 13680 const auto& spec = ctx.toml_spec(); 13681 13682 auto key_res = parse_key(loc, ctx); 13683 if(key_res.is_err()) 13684 { 13685 loc = first; 13686 return err(key_res.unwrap_err()); 13687 } 13688 13689 if( ! syntax::keyval_sep(spec).scan(loc).is_ok()) 13690 { 13691 auto e = make_syntax_error("toml::parse_key_value_pair: " 13692 "invalid key value separator `=`", syntax::keyval_sep(spec), loc); 13693 loc = first; 13694 return err(std::move(e)); 13695 } 13696 13697 auto v_res = parse_value(loc, ctx); 13698 if(v_res.is_err()) 13699 { 13700 // loc = first; 13701 return err(v_res.unwrap_err()); 13702 } 13703 return ok(std::make_pair(std::move(key_res.unwrap()), std::move(v_res.unwrap()))); 13704 } 13705 13706 /* ============================================================================ 13707 * __ _ _ _ _ _ __ _ _ _ 13708 * / _` | '_| '_/ _` | || | 13709 * \__,_|_| |_| \__,_|\_, | 13710 * |__/ 13711 */ 13712 13713 // array(and multiline inline table with `{` and `}`) has the following format. 13714 // `[` 13715 // (ws|newline|comment-line)? (value) (ws|newline|comment-line)? `,` 13716 // (ws|newline|comment-line)? (value) (ws|newline|comment-line)? `,` 13717 // ... 13718 // (ws|newline|comment-line)? (value) (ws|newline|comment-line)? (`,`)? 13719 // (ws|newline|comment-line)? `]` 13720 // it skips (ws|newline|comment-line) and returns the token. 13721 template<typename TC> 13722 struct multiline_spacer 13723 { 13724 using comment_type = typename TC::comment_type; 13725 bool newline_found; 13726 indent_char indent_type; 13727 std::int32_t indent; 13728 comment_type comments; 13729 }; 13730 template<typename T> 13731 std::ostream& operator<<(std::ostream& os, const multiline_spacer<T>& sp) 13732 { 13733 os << "{newline=" << sp.newline_found << ", "; 13734 os << "indent_type=" << sp.indent_type << ", "; 13735 os << "indent=" << sp.indent << ", "; 13736 os << "comments=" << sp.comments.size() << "}"; 13737 return os; 13738 } 13739 13740 template<typename TC> 13741 cxx::optional<multiline_spacer<TC>> 13742 skip_multiline_spacer(location& loc, context<TC>& ctx, const bool newline_found = false) 13743 { 13744 const auto& spec = ctx.toml_spec(); 13745 13746 multiline_spacer<TC> spacer; 13747 spacer.newline_found = newline_found; 13748 spacer.indent_type = indent_char::none; 13749 spacer.indent = 0; 13750 spacer.comments.clear(); 13751 13752 bool spacer_found = false; 13753 while( ! loc.eof()) 13754 { 13755 if(auto comm = sequence(syntax::comment(spec), syntax::newline(spec)).scan(loc)) 13756 { 13757 spacer.newline_found = true; 13758 auto comment = comm.as_string(); 13759 if( ! comment.empty() && comment.back() == '\n') 13760 { 13761 comment.pop_back(); 13762 if (!comment.empty() && comment.back() == '\r') 13763 { 13764 comment.pop_back(); 13765 } 13766 } 13767 13768 spacer.comments.push_back(std::move(comment)); 13769 spacer.indent_type = indent_char::none; 13770 spacer.indent = 0; 13771 spacer_found = true; 13772 } 13773 else if(auto nl = syntax::newline(spec).scan(loc)) 13774 { 13775 spacer.newline_found = true; 13776 spacer.comments.clear(); 13777 spacer.indent_type = indent_char::none; 13778 spacer.indent = 0; 13779 spacer_found = true; 13780 } 13781 else if(auto sp = repeat_at_least(1, character(cxx::bit_cast<location::char_type>(' '))).scan(loc)) 13782 { 13783 spacer.indent_type = indent_char::space; 13784 spacer.indent = static_cast<std::int32_t>(sp.length()); 13785 spacer_found = true; 13786 } 13787 else if(auto tabs = repeat_at_least(1, character(cxx::bit_cast<location::char_type>('\t'))).scan(loc)) 13788 { 13789 spacer.indent_type = indent_char::tab; 13790 spacer.indent = static_cast<std::int32_t>(tabs.length()); 13791 spacer_found = true; 13792 } 13793 else 13794 { 13795 break; // done 13796 } 13797 } 13798 if( ! spacer_found) 13799 { 13800 return cxx::make_nullopt(); 13801 } 13802 return spacer; 13803 } 13804 13805 // not an [[array.of.tables]]. It parses ["this", "type"] 13806 template<typename TC> 13807 result<basic_value<TC>, error_info> 13808 parse_array(location& loc, context<TC>& ctx) 13809 { 13810 const auto num_errors = ctx.errors().size(); 13811 13812 const auto first = loc; 13813 13814 if(loc.eof() || loc.current() != '[') 13815 { 13816 auto src = source_location(region(loc)); 13817 return err(make_error_info("toml::parse_array: " 13818 "The next token is not an array", std::move(src), "here")); 13819 } 13820 loc.advance(); 13821 13822 typename basic_value<TC>::array_type val; 13823 13824 array_format_info fmt; 13825 fmt.fmt = array_format::oneline; 13826 fmt.indent_type = indent_char::none; 13827 13828 auto spacer = skip_multiline_spacer(loc, ctx); 13829 if(spacer.has_value() && spacer.value().newline_found) 13830 { 13831 fmt.fmt = array_format::multiline; 13832 } 13833 13834 bool comma_found = true; 13835 while( ! loc.eof()) 13836 { 13837 if(loc.current() == location::char_type(']')) 13838 { 13839 if(spacer.has_value() && spacer.value().newline_found && 13840 spacer.value().indent_type != indent_char::none) 13841 { 13842 fmt.indent_type = spacer.value().indent_type; 13843 fmt.closing_indent = spacer.value().indent; 13844 } 13845 break; 13846 } 13847 13848 if( ! comma_found) 13849 { 13850 auto src = source_location(region(loc)); 13851 return err(make_error_info("toml::parse_array: " 13852 "expected value-separator `,` or closing `]`", 13853 std::move(src), "here")); 13854 } 13855 13856 if(spacer.has_value() && spacer.value().newline_found && 13857 spacer.value().indent_type != indent_char::none) 13858 { 13859 fmt.indent_type = spacer.value().indent_type; 13860 fmt.body_indent = spacer.value().indent; 13861 } 13862 13863 if(auto elem_res = parse_value(loc, ctx)) 13864 { 13865 auto elem = std::move(elem_res.unwrap()); 13866 13867 if(spacer.has_value()) // copy previous comments to value 13868 { 13869 elem.comments() = std::move(spacer.value().comments); 13870 } 13871 13872 // parse spaces between a value and a comma 13873 // array = [ 13874 // 42 , # the answer 13875 // ^^^^ 13876 // 3.14 # pi 13877 // , 2.71 ^^^^ 13878 // ^^ 13879 spacer = skip_multiline_spacer(loc, ctx); 13880 if(spacer.has_value()) 13881 { 13882 for(std::size_t i=0; i<spacer.value().comments.size(); ++i) 13883 { 13884 elem.comments().push_back(std::move(spacer.value().comments.at(i))); 13885 } 13886 if(spacer.value().newline_found) 13887 { 13888 fmt.fmt = array_format::multiline; 13889 } 13890 } 13891 13892 comma_found = character(',').scan(loc).is_ok(); 13893 13894 // parse comment after a comma 13895 // array = [ 13896 // 42 , # the answer 13897 // ^^^^^^^^^^^^ 13898 // 3.14 # pi 13899 // ^^^^ 13900 // ] 13901 auto com_res = parse_comment_line(loc, ctx); 13902 if(com_res.is_err()) 13903 { 13904 ctx.report_error(com_res.unwrap_err()); 13905 } 13906 13907 const bool comment_found = com_res.is_ok() && com_res.unwrap().has_value(); 13908 if(comment_found) 13909 { 13910 fmt.fmt = array_format::multiline; 13911 elem.comments().push_back(com_res.unwrap().value()); 13912 } 13913 if(comma_found) 13914 { 13915 spacer = skip_multiline_spacer(loc, ctx, comment_found); 13916 if(spacer.has_value() && spacer.value().newline_found) 13917 { 13918 fmt.fmt = array_format::multiline; 13919 } 13920 } 13921 val.push_back(std::move(elem)); 13922 } 13923 else 13924 { 13925 // if err, push error to ctx and try recovery. 13926 ctx.report_error(std::move(elem_res.unwrap_err())); 13927 13928 // if it looks like some value, then skip the value. 13929 // otherwise, it may be a new key-value pair or a new table and 13930 // the error is "missing closing ]". stop parsing. 13931 13932 const auto before_skip = loc.get_location(); 13933 skip_value(loc, ctx); 13934 if(before_skip == loc.get_location()) // cannot skip! break... 13935 { 13936 break; 13937 } 13938 } 13939 } 13940 13941 if(loc.current() != ']') 13942 { 13943 auto src = source_location(region(loc)); 13944 return err(make_error_info("toml::parse_array: missing closing bracket `]`", 13945 std::move(src), "expected `]`, reached EOF")); 13946 } 13947 else 13948 { 13949 loc.advance(); 13950 } 13951 // any error reported from this function 13952 if(num_errors != ctx.errors().size()) 13953 { 13954 assert(ctx.has_error()); // already reported 13955 return err(ctx.errors().back()); 13956 } 13957 13958 return ok(basic_value<TC>( 13959 std::move(val), std::move(fmt), {}, region(first, loc) 13960 )); 13961 } 13962 13963 /* ============================================================================ 13964 * _ _ _ _ _ _ 13965 * (_)_ _ | (_)_ _ ___ | |_ __ _| |__| |___ 13966 * | | ' \| | | ' \/ -_) | _/ _` | '_ \ / -_) 13967 * |_|_||_|_|_|_||_\___| \__\__,_|_.__/_\___| 13968 */ 13969 13970 // ---------------------------------------------------------------------------- 13971 // insert_value is the most complicated part of the toml spec. 13972 // 13973 // To parse a toml file correctly, we sometimes need to check an exising value 13974 // is appendable or not. 13975 // 13976 // For example while parsing an inline array of tables, 13977 // 13978 // ```toml 13979 // aot = [ 13980 // {a = "foo"}, 13981 // {a = "bar", b = "baz"}, 13982 // ] 13983 // ``` 13984 // 13985 // this `aot` is appendable until parser reaches to `]`. After that, it becomes 13986 // non-appendable. 13987 // 13988 // On the other hand, a normal array of tables, such as 13989 // 13990 // ```toml 13991 // [[aot]] 13992 // a = "foo" 13993 // 13994 // [[aot]] 13995 // a = "bar" 13996 // b = "baz" 13997 // ``` 13998 // This `[[aot]]` is appendable until the parser reaches to the EOF. 13999 // 14000 // 14001 // It becomes a bit more difficult in case of dotted keys. 14002 // In TOML, it is allowed to append a key-value pair to a table that is 14003 // *implicitly* defined by a subtable definitino. 14004 // 14005 // ```toml 14006 // [x.y.z] 14007 // w = 123 14008 // 14009 // [x] 14010 // a = "foo" # OK. x is defined implicitly by `[x.y.z]`. 14011 // ``` 14012 // 14013 // But if the table is defined by a dotted keys, it is not appendable. 14014 // 14015 // ```toml 14016 // [x] 14017 // y.z.w = 123 14018 // 14019 // [x.y] 14020 // # ERROR. x.y is already defined by a dotted table in the previous table. 14021 // ``` 14022 // 14023 // Also, reopening a table using dotted keys is invalid. 14024 // 14025 // ```toml 14026 // [x.y.z] 14027 // w = 123 14028 // 14029 // [x] 14030 // y.z.v = 42 # ERROR. [x.y.z] is already defined. 14031 // ``` 14032 // 14033 // 14034 // ```toml 14035 // [a] 14036 // b.c = "foo" 14037 // b.d = "bar" 14038 // ``` 14039 // 14040 // 14041 // ```toml 14042 // a.b = "foo" 14043 // [a] 14044 // c = "bar" # ERROR 14045 // ``` 14046 // 14047 // In summary, 14048 // - a table must be defined only once. 14049 // - assignment to an exising table is possible only when: 14050 // - defining a subtable [x.y] to an existing table [x]. 14051 // - defining supertable [x] explicitly after [x.y]. 14052 // - adding dotted keys in the same table. 14053 14054 enum class inserting_value_kind : std::uint8_t 14055 { 14056 std_table, // insert [standard.table] 14057 array_table, // insert [[array.of.tables]] 14058 dotted_keys // insert a.b.c = "this" 14059 }; 14060 14061 template<typename TC> 14062 result<basic_value<TC>*, error_info> 14063 insert_value(const inserting_value_kind kind, 14064 typename basic_value<TC>::table_type* current_table_ptr, 14065 const std::vector<typename basic_value<TC>::key_type>& keys, region key_reg, 14066 basic_value<TC> val) 14067 { 14068 using value_type = basic_value<TC>; 14069 using array_type = typename basic_value<TC>::array_type; 14070 using table_type = typename basic_value<TC>::table_type; 14071 14072 auto key_loc = source_location(key_reg); 14073 14074 assert( ! keys.empty()); 14075 14076 // dotted key can insert to dotted key tables defined at the same level. 14077 // dotted key can NOT reopen a table even if it is implcitly-defined one. 14078 // 14079 // [x.y.z] # define x and x.y implicitly. 14080 // a = 42 14081 // 14082 // [x] # reopening implcitly defined table 14083 // r.s.t = 3.14 # VALID r and r.s are new tables. 14084 // r.s.u = 2.71 # VALID r and r.s are dotted-key tables. valid. 14085 // 14086 // y.z.b = "foo" # INVALID x.y.z are multiline table, not a dotted key. 14087 // y.c = "bar" # INVALID x.y is implicit multiline table, not a dotted key. 14088 14089 // a table cannot reopen dotted-key tables. 14090 // 14091 // [t1] 14092 // t2.t3.v = 0 14093 // [t1.t2] # INVALID t1.t2 is defined as a dotted-key table. 14094 14095 for(std::size_t i=0; i<keys.size(); ++i) 14096 { 14097 const auto& key = keys.at(i); 14098 table_type& current_table = *current_table_ptr; 14099 14100 if(i+1 < keys.size()) // there are more keys. go down recursively... 14101 { 14102 const auto found = current_table.find(key); 14103 if(found == current_table.end()) // not found. add new table 14104 { 14105 table_format_info fmt; 14106 fmt.indent_type = indent_char::none; 14107 if(kind == inserting_value_kind::dotted_keys) 14108 { 14109 fmt.fmt = table_format::dotted; 14110 } 14111 else // table / array of tables 14112 { 14113 fmt.fmt = table_format::implicit; 14114 } 14115 current_table.emplace(key, value_type( 14116 table_type{}, fmt, std::vector<std::string>{}, key_reg)); 14117 14118 assert(current_table.at(key).is_table()); 14119 current_table_ptr = std::addressof(current_table.at(key).as_table()); 14120 } 14121 else if (found->second.is_table()) 14122 { 14123 const auto fmt = found->second.as_table_fmt().fmt; 14124 if(fmt == table_format::oneline || fmt == table_format::multiline_oneline) 14125 { 14126 // foo = {bar = "baz"} or foo = { \n bar = "baz" \n } 14127 return err(make_error_info("toml::insert_value: " 14128 "failed to insert a value: inline table is immutable", 14129 key_loc, "inserting this", 14130 found->second.location(), "to this table")); 14131 } 14132 // dotted key cannot reopen a table. 14133 if(kind ==inserting_value_kind::dotted_keys && fmt != table_format::dotted) 14134 { 14135 return err(make_error_info("toml::insert_value: " 14136 "reopening a table using dotted keys", 14137 key_loc, "dotted key cannot reopen a table", 14138 found->second.location(), "this table is already closed")); 14139 } 14140 assert(found->second.is_table()); 14141 current_table_ptr = std::addressof(found->second.as_table()); 14142 } 14143 else if(found->second.is_array_of_tables()) 14144 { 14145 // aot = [{this = "type", of = "aot"}] # cannot be reopened 14146 if(found->second.as_array_fmt().fmt != array_format::array_of_tables) 14147 { 14148 return err(make_error_info("toml::insert_value:" 14149 "inline array of tables are immutable", 14150 key_loc, "inserting this", 14151 found->second.location(), "inline array of tables")); 14152 } 14153 // appending to [[aot]] 14154 14155 if(kind == inserting_value_kind::dotted_keys) 14156 { 14157 // [[array.of.tables]] 14158 // [array.of] # reopening supertable is okay 14159 // tables.x = "foo" # appending `x` to the first table 14160 return err(make_error_info("toml::insert_value:" 14161 "dotted key cannot reopen an array-of-tables", 14162 key_loc, "inserting this", 14163 found->second.location(), "to this array-of-tables.")); 14164 } 14165 14166 // insert_value_by_dotkeys::std_table 14167 // [[array.of.tables]] 14168 // [array.of.tables.subtable] # appending to the last aot 14169 // 14170 // insert_value_by_dotkeys::array_table 14171 // [[array.of.tables]] 14172 // [[array.of.tables.subtable]] # appending to the last aot 14173 auto& current_array_table = found->second.as_array().back(); 14174 14175 assert(current_array_table.is_table()); 14176 current_table_ptr = std::addressof(current_array_table.as_table()); 14177 } 14178 else 14179 { 14180 return err(make_error_info("toml::insert_value: " 14181 "failed to insert a value, value already exists", 14182 key_loc, "while inserting this", 14183 found->second.location(), "non-table value already exists")); 14184 } 14185 } 14186 else // this is the last key. insert a new value. 14187 { 14188 switch(kind) 14189 { 14190 case inserting_value_kind::dotted_keys: 14191 { 14192 if(current_table.find(key) != current_table.end()) 14193 { 14194 return err(make_error_info("toml::insert_value: " 14195 "failed to insert a value, value already exists", 14196 key_loc, "inserting this", 14197 current_table.at(key).location(), "but value already exists")); 14198 } 14199 current_table.emplace(key, std::move(val)); 14200 return ok(std::addressof(current_table.at(key))); 14201 } 14202 case inserting_value_kind::std_table: 14203 { 14204 // defining a new table or reopening supertable 14205 auto found = current_table.find(key); 14206 if(found == current_table.end()) // define a new aot 14207 { 14208 current_table.emplace(key, std::move(val)); 14209 return ok(std::addressof(current_table.at(key))); 14210 } 14211 else // the table is already defined, reopen it 14212 { 14213 // assigning a [std.table]. it must be an implicit table. 14214 auto& target = found->second; 14215 if( ! target.is_table() || // could be an array-of-tables 14216 target.as_table_fmt().fmt != table_format::implicit) 14217 { 14218 return err(make_error_info("toml::insert_value: " 14219 "failed to insert a table, table already defined", 14220 key_loc, "inserting this", 14221 target.location(), "this table is explicitly defined")); 14222 } 14223 14224 // merge table 14225 for(const auto& kv : val.as_table()) 14226 { 14227 if(target.contains(kv.first)) 14228 { 14229 // [x.y.z] 14230 // w = "foo" 14231 // [x] 14232 // y = "bar" 14233 return err(make_error_info("toml::insert_value: " 14234 "failed to insert a table, table keys conflict to each other", 14235 key_loc, "inserting this table", 14236 kv.second.location(), "having this value", 14237 target.at(kv.first).location(), "already defined here")); 14238 } 14239 else 14240 { 14241 target[kv.first] = kv.second; 14242 } 14243 } 14244 // change implicit -> explicit 14245 target.as_table_fmt().fmt = table_format::multiline; 14246 // change definition region 14247 change_region_of_value(target, val); 14248 14249 return ok(std::addressof(current_table.at(key))); 14250 } 14251 } 14252 case inserting_value_kind::array_table: 14253 { 14254 auto found = current_table.find(key); 14255 if(found == current_table.end()) // define a new aot 14256 { 14257 array_format_info fmt; 14258 fmt.fmt = array_format::array_of_tables; 14259 fmt.indent_type = indent_char::none; 14260 14261 current_table.emplace(key, value_type( 14262 array_type{ std::move(val) }, std::move(fmt), 14263 std::vector<std::string>{}, std::move(key_reg) 14264 )); 14265 14266 assert( ! current_table.at(key).as_array().empty()); 14267 return ok(std::addressof(current_table.at(key).as_array().back())); 14268 } 14269 else // the array is already defined, append to it 14270 { 14271 if( ! found->second.is_array_of_tables()) 14272 { 14273 return err(make_error_info("toml::insert_value: " 14274 "failed to insert an array of tables, value already exists", 14275 key_loc, "while inserting this", 14276 found->second.location(), "non-table value already exists")); 14277 } 14278 if(found->second.as_array_fmt().fmt != array_format::array_of_tables) 14279 { 14280 return err(make_error_info("toml::insert_value: " 14281 "failed to insert a table, inline array of tables is immutable", 14282 key_loc, "while inserting this", 14283 found->second.location(), "this is inline array-of-tables")); 14284 } 14285 found->second.as_array().push_back(std::move(val)); 14286 assert( ! current_table.at(key).as_array().empty()); 14287 return ok(std::addressof(current_table.at(key).as_array().back())); 14288 } 14289 } 14290 default: {assert(false);} 14291 } 14292 } 14293 } 14294 return err(make_error_info("toml::insert_key: no keys found", 14295 std::move(key_loc), "here")); 14296 } 14297 14298 // ---------------------------------------------------------------------------- 14299 14300 template<typename TC> 14301 result<basic_value<TC>, error_info> 14302 parse_inline_table(location& loc, context<TC>& ctx) 14303 { 14304 using table_type = typename basic_value<TC>::table_type; 14305 14306 const auto num_errors = ctx.errors().size(); 14307 14308 const auto first = loc; 14309 const auto& spec = ctx.toml_spec(); 14310 14311 if(loc.eof() || loc.current() != '{') 14312 { 14313 auto src = source_location(region(loc)); 14314 return err(make_error_info("toml::parse_inline_table: " 14315 "The next token is not an inline table", std::move(src), "here")); 14316 } 14317 loc.advance(); 14318 14319 table_type table; 14320 table_format_info fmt; 14321 fmt.fmt = table_format::oneline; 14322 fmt.indent_type = indent_char::none; 14323 14324 cxx::optional<multiline_spacer<TC>> spacer(cxx::make_nullopt()); 14325 14326 if(spec.v1_1_0_allow_newlines_in_inline_tables) 14327 { 14328 spacer = skip_multiline_spacer(loc, ctx); 14329 if(spacer.has_value() && spacer.value().newline_found) 14330 { 14331 fmt.fmt = table_format::multiline_oneline; 14332 } 14333 } 14334 else 14335 { 14336 skip_whitespace(loc, ctx); 14337 } 14338 14339 bool still_empty = true; 14340 bool comma_found = false; 14341 while( ! loc.eof()) 14342 { 14343 // closing! 14344 if(loc.current() == '}') 14345 { 14346 if(comma_found && ! spec.v1_1_0_allow_trailing_comma_in_inline_tables) 14347 { 14348 auto src = source_location(region(loc)); 14349 return err(make_error_info("toml::parse_inline_table: trailing " 14350 "comma is not allowed in TOML-v1.0.0)", std::move(src), "here")); 14351 } 14352 14353 if(spec.v1_1_0_allow_newlines_in_inline_tables) 14354 { 14355 if(spacer.has_value() && spacer.value().newline_found && 14356 spacer.value().indent_type != indent_char::none) 14357 { 14358 fmt.indent_type = spacer.value().indent_type; 14359 fmt.closing_indent = spacer.value().indent; 14360 } 14361 } 14362 break; 14363 } 14364 14365 // if we already found a value and didn't found `,` nor `}`, error. 14366 if( ! comma_found && ! still_empty) 14367 { 14368 auto src = source_location(region(loc)); 14369 return err(make_error_info("toml::parse_inline_table: " 14370 "expected value-separator `,` or closing `}`", 14371 std::move(src), "here")); 14372 } 14373 14374 // parse indent. 14375 if(spacer.has_value() && spacer.value().newline_found && 14376 spacer.value().indent_type != indent_char::none) 14377 { 14378 fmt.indent_type = spacer.value().indent_type; 14379 fmt.body_indent = spacer.value().indent; 14380 } 14381 14382 still_empty = false; // parsing a value... 14383 if(auto kv_res = parse_key_value_pair<TC>(loc, ctx)) 14384 { 14385 auto keys = std::move(kv_res.unwrap().first.first); 14386 auto key_reg = std::move(kv_res.unwrap().first.second); 14387 auto val = std::move(kv_res.unwrap().second); 14388 14389 auto ins_res = insert_value(inserting_value_kind::dotted_keys, 14390 std::addressof(table), keys, std::move(key_reg), std::move(val)); 14391 if(ins_res.is_err()) 14392 { 14393 ctx.report_error(std::move(ins_res.unwrap_err())); 14394 // we need to skip until the next value (or end of the table) 14395 // because we don't have valid kv pair. 14396 while( ! loc.eof()) 14397 { 14398 const auto c = loc.current(); 14399 if(c == ',' || c == '\n' || c == '}') 14400 { 14401 comma_found = (c == ','); 14402 break; 14403 } 14404 loc.advance(); 14405 } 14406 continue; 14407 } 14408 14409 // if comment line follows immediately(without newline) after `,`, then 14410 // the comment is for the elem. we need to check if comment follows `,`. 14411 // 14412 // (key) = (val) (ws|newline|comment-line)? `,` (ws)? (comment)? 14413 14414 if(spec.v1_1_0_allow_newlines_in_inline_tables) 14415 { 14416 if(spacer.has_value()) // copy previous comments to value 14417 { 14418 for(std::size_t i=0; i<spacer.value().comments.size(); ++i) 14419 { 14420 ins_res.unwrap()->comments().push_back(spacer.value().comments.at(i)); 14421 } 14422 } 14423 spacer = skip_multiline_spacer(loc, ctx); 14424 if(spacer.has_value()) 14425 { 14426 for(std::size_t i=0; i<spacer.value().comments.size(); ++i) 14427 { 14428 ins_res.unwrap()->comments().push_back(spacer.value().comments.at(i)); 14429 } 14430 if(spacer.value().newline_found) 14431 { 14432 fmt.fmt = table_format::multiline_oneline; 14433 if(spacer.value().indent_type != indent_char::none) 14434 { 14435 fmt.indent_type = spacer.value().indent_type; 14436 fmt.body_indent = spacer.value().indent; 14437 } 14438 } 14439 } 14440 } 14441 else 14442 { 14443 skip_whitespace(loc, ctx); 14444 } 14445 14446 comma_found = character(',').scan(loc).is_ok(); 14447 14448 if(spec.v1_1_0_allow_newlines_in_inline_tables) 14449 { 14450 auto com_res = parse_comment_line(loc, ctx); 14451 if(com_res.is_err()) 14452 { 14453 ctx.report_error(com_res.unwrap_err()); 14454 } 14455 const bool comment_found = com_res.is_ok() && com_res.unwrap().has_value(); 14456 if(comment_found) 14457 { 14458 fmt.fmt = table_format::multiline_oneline; 14459 ins_res.unwrap()->comments().push_back(com_res.unwrap().value()); 14460 } 14461 if(comma_found) 14462 { 14463 spacer = skip_multiline_spacer(loc, ctx, comment_found); 14464 if(spacer.has_value() && spacer.value().newline_found) 14465 { 14466 fmt.fmt = table_format::multiline_oneline; 14467 } 14468 } 14469 } 14470 else 14471 { 14472 skip_whitespace(loc, ctx); 14473 } 14474 } 14475 else 14476 { 14477 ctx.report_error(std::move(kv_res.unwrap_err())); 14478 while( ! loc.eof()) 14479 { 14480 if(loc.current() == '}') 14481 { 14482 break; 14483 } 14484 if( ! spec.v1_1_0_allow_newlines_in_inline_tables && loc.current() == '\n') 14485 { 14486 break; 14487 } 14488 loc.advance(); 14489 } 14490 break; 14491 } 14492 } 14493 14494 if(loc.current() != '}') 14495 { 14496 auto src = source_location(region(loc)); 14497 return err(make_error_info("toml::parse_inline_table: " 14498 "missing closing bracket `}`", 14499 std::move(src), "expected `}`, reached line end")); 14500 } 14501 else 14502 { 14503 loc.advance(); // skip } 14504 } 14505 14506 // any error reported from this function 14507 if(num_errors < ctx.errors().size()) 14508 { 14509 assert(ctx.has_error()); // already reported 14510 return err(ctx.pop_last_error()); 14511 } 14512 14513 basic_value<TC> retval( 14514 std::move(table), std::move(fmt), {}, region(first, loc)); 14515 14516 return ok(std::move(retval)); 14517 } 14518 14519 /* ============================================================================ 14520 * _ 14521 * __ ____ _| |_ _ ___ 14522 * \ V / _` | | || / -_) 14523 * \_/\__,_|_|\_,_\___| 14524 */ 14525 14526 template<typename TC> 14527 result<value_t, error_info> 14528 guess_number_type(const location& first, const context<TC>& ctx) 14529 { 14530 const auto& spec = ctx.toml_spec(); 14531 location loc = first; 14532 14533 if(syntax::offset_datetime(spec).scan(loc).is_ok()) 14534 { 14535 return ok(value_t::offset_datetime); 14536 } 14537 loc = first; 14538 14539 if(syntax::local_datetime(spec).scan(loc).is_ok()) 14540 { 14541 const auto curr = loc.current(); 14542 // if offset_datetime contains bad offset, it syntax::offset_datetime 14543 // fails to scan it. 14544 if(curr == '+' || curr == '-') 14545 { 14546 return err(make_syntax_error("bad offset: must be [+-]HH:MM or Z", 14547 syntax::time_offset(spec), loc, std::string( 14548 "Hint: valid : +09:00, -05:30\n" 14549 "Hint: invalid: +9:00, -5:30\n"))); 14550 } 14551 return ok(value_t::local_datetime); 14552 } 14553 loc = first; 14554 14555 if(syntax::local_date(spec).scan(loc).is_ok()) 14556 { 14557 // bad time may appear after this. 14558 14559 if( ! loc.eof()) 14560 { 14561 const auto c = loc.current(); 14562 if(c == 'T' || c == 't') 14563 { 14564 loc.advance(); 14565 14566 return err(make_syntax_error("bad time: must be HH:MM:SS.subsec", 14567 syntax::local_time(spec), loc, std::string( 14568 "Hint: valid : 1979-05-27T07:32:00, 1979-05-27 07:32:00.999999\n" 14569 "Hint: invalid: 1979-05-27T7:32:00, 1979-05-27 17:32\n"))); 14570 } 14571 if(c == ' ') 14572 { 14573 // A space is allowed as a delimiter between local time. 14574 // But there is a case where bad time follows a space. 14575 // - invalid: 2019-06-16 7:00:00 14576 // - valid : 2019-06-16 07:00:00 14577 loc.advance(); 14578 if( ! loc.eof() && ('0' <= loc.current() && loc.current() <= '9')) 14579 { 14580 return err(make_syntax_error("bad time: must be HH:MM:SS.subsec", 14581 syntax::local_time(spec), loc, std::string( 14582 "Hint: valid : 1979-05-27T07:32:00, 1979-05-27 07:32:00.999999\n" 14583 "Hint: invalid: 1979-05-27T7:32:00, 1979-05-27 17:32\n"))); 14584 } 14585 } 14586 if('0' <= c && c <= '9') 14587 { 14588 return err(make_syntax_error("bad datetime: missing T or space", 14589 character_either{'T', 't', ' '}, loc, std::string( 14590 "Hint: valid : 1979-05-27T07:32:00, 1979-05-27 07:32:00.999999\n" 14591 "Hint: invalid: 1979-05-27T7:32:00, 1979-05-27 17:32\n"))); 14592 } 14593 } 14594 return ok(value_t::local_date); 14595 } 14596 loc = first; 14597 14598 if(syntax::local_time(spec).scan(loc).is_ok()) 14599 { 14600 return ok(value_t::local_time); 14601 } 14602 loc = first; 14603 14604 if(syntax::floating(spec).scan(loc).is_ok()) 14605 { 14606 if( ! loc.eof() && loc.current() == '_') 14607 { 14608 if(spec.ext_num_suffix && syntax::num_suffix(spec).scan(loc).is_ok()) 14609 { 14610 return ok(value_t::floating); 14611 } 14612 auto src = source_location(region(loc)); 14613 return err(make_error_info( 14614 "bad float: `_` must be surrounded by digits", 14615 std::move(src), "invalid underscore", 14616 "Hint: valid : +1.0, -2e-2, 3.141_592_653_589, inf, nan\n" 14617 "Hint: invalid: .0, 1., _1.0, 1.0_, 1_.0, 1.0__0\n")); 14618 } 14619 return ok(value_t::floating); 14620 } 14621 loc = first; 14622 14623 if(spec.ext_hex_float) 14624 { 14625 if(syntax::hex_floating(spec).scan(loc).is_ok()) 14626 { 14627 if( ! loc.eof() && loc.current() == '_') 14628 { 14629 if(spec.ext_num_suffix && syntax::num_suffix(spec).scan(loc).is_ok()) 14630 { 14631 return ok(value_t::floating); 14632 } 14633 auto src = source_location(region(loc)); 14634 return err(make_error_info( 14635 "bad float: `_` must be surrounded by digits", 14636 std::move(src), "invalid underscore", 14637 "Hint: valid : +1.0, -2e-2, 3.141_592_653_589, inf, nan\n" 14638 "Hint: invalid: .0, 1., _1.0, 1.0_, 1_.0, 1.0__0\n")); 14639 } 14640 return ok(value_t::floating); 14641 } 14642 loc = first; 14643 } 14644 14645 if(auto int_reg = syntax::integer(spec).scan(loc)) 14646 { 14647 if( ! loc.eof()) 14648 { 14649 const auto c = loc.current(); 14650 if(c == '_') 14651 { 14652 if(spec.ext_num_suffix && syntax::num_suffix(spec).scan(loc).is_ok()) 14653 { 14654 return ok(value_t::integer); 14655 } 14656 14657 if(int_reg.length() <= 2 && (int_reg.as_string() == "0" || 14658 int_reg.as_string() == "-0" || int_reg.as_string() == "+0")) 14659 { 14660 auto src = source_location(region(loc)); 14661 return err(make_error_info( 14662 "bad integer: leading zero is not allowed in decimal int", 14663 std::move(src), "leading zero", 14664 "Hint: valid : -42, 1_000, 1_2_3_4_5, 0xC0FFEE, 0b0010, 0o755\n" 14665 "Hint: invalid: _42, 1__000, 0123\n")); 14666 } 14667 else 14668 { 14669 auto src = source_location(region(loc)); 14670 return err(make_error_info( 14671 "bad integer: `_` must be surrounded by digits", 14672 std::move(src), "invalid underscore", 14673 "Hint: valid : -42, 1_000, 1_2_3_4_5, 0xC0FFEE, 0b0010, 0o755\n" 14674 "Hint: invalid: _42, 1__000, 0123\n")); 14675 } 14676 } 14677 if('0' <= c && c <= '9') 14678 { 14679 if(loc.current() == '0') 14680 { 14681 loc.retrace(); 14682 return err(make_error_info( 14683 "bad integer: leading zero", 14684 source_location(region(loc)), "leading zero is not allowed", 14685 std::string("Hint: valid : -42, 1_000, 1_2_3_4_5, 0xC0FFEE, 0b0010, 0o755\n" 14686 "Hint: invalid: _42, 1__000, 0123\n") 14687 )); 14688 } 14689 else // invalid digits, especially in oct/bin ints. 14690 { 14691 return err(make_error_info( 14692 "bad integer: invalid digit after an integer", 14693 source_location(region(loc)), "this digit is not allowed", 14694 std::string("Hint: valid : -42, 1_000, 1_2_3_4_5, 0xC0FFEE, 0b0010, 0o755\n" 14695 "Hint: invalid: _42, 1__000, 0123\n") 14696 )); 14697 } 14698 } 14699 if(c == ':' || c == '-') 14700 { 14701 auto src = source_location(region(loc)); 14702 return err(make_error_info("bad datetime: invalid format", 14703 std::move(src), "here", 14704 std::string("Hint: valid : 1979-05-27T07:32:00-07:00, 1979-05-27 07:32:00.999999Z\n" 14705 "Hint: invalid: 1979-05-27T7:32:00-7:00, 1979-05-27 7:32-00:30") 14706 )); 14707 } 14708 if(c == '.' || c == 'e' || c == 'E') 14709 { 14710 auto src = source_location(region(loc)); 14711 return err(make_error_info("bad float: invalid format", 14712 std::move(src), "here", std::string( 14713 "Hint: valid : +1.0, -2e-2, 3.141_592_653_589, inf, nan\n" 14714 "Hint: invalid: .0, 1., _1.0, 1.0_, 1_.0, 1.0__0\n"))); 14715 } 14716 } 14717 return ok(value_t::integer); 14718 } 14719 if( ! loc.eof() && loc.current() == '.') 14720 { 14721 auto src = source_location(region(loc)); 14722 return err(make_error_info("bad float: integer part is required before decimal point", 14723 std::move(src), "missing integer part", std::string( 14724 "Hint: valid : +1.0, -2e-2, 3.141_592_653_589, inf, nan\n" 14725 "Hint: invalid: .0, 1., _1.0, 1.0_, 1_.0, 1.0__0\n") 14726 )); 14727 } 14728 if( ! loc.eof() && loc.current() == '_') 14729 { 14730 auto src = source_location(region(loc)); 14731 return err(make_error_info("bad number: `_` must be surrounded by digits", 14732 std::move(src), "digits required before `_`", std::string( 14733 "Hint: valid : -42, 1_000, 1_2_3_4_5, 0xC0FFEE, 0b0010, 0o755\n" 14734 "Hint: invalid: _42, 1__000, 0123\n") 14735 )); 14736 } 14737 14738 auto src = source_location(region(loc)); 14739 return err(make_error_info("bad format: unknown value appeared", 14740 std::move(src), "here")); 14741 } 14742 14743 template<typename TC> 14744 result<value_t, error_info> 14745 guess_value_type(const location& loc, const context<TC>& ctx) 14746 { 14747 const auto& sp = ctx.toml_spec(); 14748 location inner(loc); 14749 14750 switch(loc.current()) 14751 { 14752 case '"' : {return ok(value_t::string); } 14753 case '\'': {return ok(value_t::string); } 14754 case '[' : {return ok(value_t::array); } 14755 case '{' : {return ok(value_t::table); } 14756 case 't' : 14757 { 14758 return ok(value_t::boolean); 14759 } 14760 case 'f' : 14761 { 14762 return ok(value_t::boolean); 14763 } 14764 case 'T' : // invalid boolean. 14765 { 14766 return err(make_syntax_error("toml::parse_value: " 14767 "`true` must be in lowercase. " 14768 "A string must be surrounded by quotes.", 14769 syntax::boolean(sp), inner)); 14770 } 14771 case 'F' : 14772 { 14773 return err(make_syntax_error("toml::parse_value: " 14774 "`false` must be in lowercase. " 14775 "A string must be surrounded by quotes.", 14776 syntax::boolean(sp), inner)); 14777 } 14778 case 'i' : // inf or string without quotes(syntax error). 14779 { 14780 if(literal("inf").scan(inner).is_ok()) 14781 { 14782 return ok(value_t::floating); 14783 } 14784 else 14785 { 14786 return err(make_syntax_error("toml::parse_value: " 14787 "`inf` must be in lowercase. " 14788 "A string must be surrounded by quotes.", 14789 syntax::floating(sp), inner)); 14790 } 14791 } 14792 case 'I' : // Inf or string without quotes(syntax error). 14793 { 14794 return err(make_syntax_error("toml::parse_value: " 14795 "`inf` must be in lowercase. " 14796 "A string must be surrounded by quotes.", 14797 syntax::floating(sp), inner)); 14798 } 14799 case 'n' : // nan or null-extension 14800 { 14801 if(sp.ext_null_value) 14802 { 14803 if(literal("nan").scan(inner).is_ok()) 14804 { 14805 return ok(value_t::floating); 14806 } 14807 else if(literal("null").scan(inner).is_ok()) 14808 { 14809 return ok(value_t::empty); 14810 } 14811 else 14812 { 14813 return err(make_syntax_error("toml::parse_value: " 14814 "Both `nan` and `null` must be in lowercase. " 14815 "A string must be surrounded by quotes.", 14816 syntax::floating(sp), inner)); 14817 } 14818 } 14819 else // must be nan. 14820 { 14821 if(literal("nan").scan(inner).is_ok()) 14822 { 14823 return ok(value_t::floating); 14824 } 14825 else 14826 { 14827 return err(make_syntax_error("toml::parse_value: " 14828 "`nan` must be in lowercase. " 14829 "A string must be surrounded by quotes.", 14830 syntax::floating(sp), inner)); 14831 } 14832 } 14833 } 14834 case 'N' : // nan or null-extension 14835 { 14836 if(sp.ext_null_value) 14837 { 14838 return err(make_syntax_error("toml::parse_value: " 14839 "Both `nan` and `null` must be in lowercase. " 14840 "A string must be surrounded by quotes.", 14841 syntax::floating(sp), inner)); 14842 } 14843 else 14844 { 14845 return err(make_syntax_error("toml::parse_value: " 14846 "`nan` must be in lowercase. " 14847 "A string must be surrounded by quotes.", 14848 syntax::floating(sp), inner)); 14849 } 14850 } 14851 default : 14852 { 14853 return guess_number_type(loc, ctx); 14854 } 14855 } 14856 } 14857 14858 template<typename TC> 14859 result<basic_value<TC>, error_info> 14860 parse_value(location& loc, context<TC>& ctx) 14861 { 14862 const auto ty_res = guess_value_type(loc, ctx); 14863 if(ty_res.is_err()) 14864 { 14865 return err(ty_res.unwrap_err()); 14866 } 14867 14868 switch(ty_res.unwrap()) 14869 { 14870 case value_t::empty: 14871 { 14872 if(ctx.toml_spec().ext_null_value) 14873 { 14874 return parse_null(loc, ctx); 14875 } 14876 else 14877 { 14878 auto src = source_location(region(loc)); 14879 return err(make_error_info("toml::parse_value: unknown value appeared", 14880 std::move(src), "here")); 14881 } 14882 } 14883 case value_t::boolean : {return parse_boolean (loc, ctx);} 14884 case value_t::integer : {return parse_integer (loc, ctx);} 14885 case value_t::floating : {return parse_floating (loc, ctx);} 14886 case value_t::string : {return parse_string (loc, ctx);} 14887 case value_t::offset_datetime: {return parse_offset_datetime(loc, ctx);} 14888 case value_t::local_datetime : {return parse_local_datetime (loc, ctx);} 14889 case value_t::local_date : {return parse_local_date (loc, ctx);} 14890 case value_t::local_time : {return parse_local_time (loc, ctx);} 14891 case value_t::array : {return parse_array (loc, ctx);} 14892 case value_t::table : {return parse_inline_table (loc, ctx);} 14893 default: 14894 { 14895 auto src = source_location(region(loc)); 14896 return err(make_error_info("toml::parse_value: unknown value appeared", 14897 std::move(src), "here")); 14898 } 14899 } 14900 } 14901 14902 /* ============================================================================ 14903 * _____ _ _ 14904 * |_ _|_ _| |__| |___ 14905 * | |/ _` | '_ \ / -_) 14906 * |_|\__,_|_.__/_\___| 14907 */ 14908 14909 template<typename TC> 14910 result<std::pair<std::vector<typename basic_value<TC>::key_type>, region>, error_info> 14911 parse_table_key(location& loc, context<TC>& ctx) 14912 { 14913 const auto first = loc; 14914 const auto& spec = ctx.toml_spec(); 14915 14916 auto reg = syntax::std_table(spec).scan(loc); 14917 if(!reg.is_ok()) 14918 { 14919 return err(make_syntax_error("toml::parse_table_key: invalid table key", 14920 syntax::std_table(spec), loc)); 14921 } 14922 14923 loc = first; 14924 loc.advance(); // skip [ 14925 skip_whitespace(loc, ctx); 14926 14927 auto keys_res = parse_key(loc, ctx); 14928 if(keys_res.is_err()) 14929 { 14930 return err(std::move(keys_res.unwrap_err())); 14931 } 14932 14933 skip_whitespace(loc, ctx); 14934 loc.advance(); // ] 14935 14936 return ok(std::make_pair(std::move(keys_res.unwrap().first), std::move(reg))); 14937 } 14938 14939 template<typename TC> 14940 result<std::pair<std::vector<typename basic_value<TC>::key_type>, region>, error_info> 14941 parse_array_table_key(location& loc, context<TC>& ctx) 14942 { 14943 const auto first = loc; 14944 const auto& spec = ctx.toml_spec(); 14945 14946 auto reg = syntax::array_table(spec).scan(loc); 14947 if(!reg.is_ok()) 14948 { 14949 return err(make_syntax_error("toml::parse_array_table_key: invalid array-of-tables key", 14950 syntax::array_table(spec), loc)); 14951 } 14952 14953 loc = first; 14954 loc.advance(); // [ 14955 loc.advance(); // [ 14956 skip_whitespace(loc, ctx); 14957 14958 auto keys_res = parse_key(loc, ctx); 14959 if(keys_res.is_err()) 14960 { 14961 return err(std::move(keys_res.unwrap_err())); 14962 } 14963 14964 skip_whitespace(loc, ctx); 14965 loc.advance(); // ] 14966 loc.advance(); // ] 14967 14968 return ok(std::make_pair(std::move(keys_res.unwrap().first), std::move(reg))); 14969 } 14970 14971 // called after reading [table.keys] and comments around it. 14972 // Since table may already contain a subtable ([x.y.z] can be defined before [x]), 14973 // the table that is being parsed is passed as an argument. 14974 template<typename TC> 14975 result<none_t, error_info> 14976 parse_table(location& loc, context<TC>& ctx, basic_value<TC>& table) 14977 { 14978 assert(table.is_table()); 14979 14980 const auto num_errors = ctx.errors().size(); 14981 const auto& spec = ctx.toml_spec(); 14982 14983 // clear indent info 14984 table.as_table_fmt().indent_type = indent_char::none; 14985 14986 bool newline_found = true; 14987 while( ! loc.eof()) 14988 { 14989 const auto start = loc; 14990 14991 auto sp = skip_multiline_spacer(loc, ctx, newline_found); 14992 14993 // if reached to EOF, the table ends here. return. 14994 if(loc.eof()) 14995 { 14996 break; 14997 } 14998 // if next table is comming, return. 14999 if(sequence(syntax::ws(spec), character('[')).scan(loc).is_ok()) 15000 { 15001 loc = start; 15002 break; 15003 } 15004 // otherwise, it should be a key-value pair. 15005 newline_found = newline_found || (sp.has_value() && sp.value().newline_found); 15006 if( ! newline_found) 15007 { 15008 return err(make_error_info("toml::parse_table: " 15009 "newline (LF / CRLF) or EOF is expected", 15010 source_location(region(loc)), "here")); 15011 } 15012 if(sp.has_value() && sp.value().indent_type != indent_char::none) 15013 { 15014 table.as_table_fmt().indent_type = sp.value().indent_type; 15015 table.as_table_fmt().body_indent = sp.value().indent; 15016 } 15017 15018 newline_found = false; // reset 15019 if(auto kv_res = parse_key_value_pair(loc, ctx)) 15020 { 15021 auto keys = std::move(kv_res.unwrap().first.first); 15022 auto key_reg = std::move(kv_res.unwrap().first.second); 15023 auto val = std::move(kv_res.unwrap().second); 15024 15025 if(sp.has_value()) 15026 { 15027 for(const auto& com : sp.value().comments) 15028 { 15029 val.comments().push_back(com); 15030 } 15031 } 15032 15033 if(auto com_res = parse_comment_line(loc, ctx)) 15034 { 15035 if(auto com_opt = com_res.unwrap()) 15036 { 15037 val.comments().push_back(com_opt.value()); 15038 newline_found = true; // comment includes newline at the end 15039 } 15040 } 15041 else 15042 { 15043 ctx.report_error(std::move(com_res.unwrap_err())); 15044 } 15045 15046 auto ins_res = insert_value(inserting_value_kind::dotted_keys, 15047 std::addressof(table.as_table()), 15048 keys, std::move(key_reg), std::move(val)); 15049 if(ins_res.is_err()) 15050 { 15051 ctx.report_error(std::move(ins_res.unwrap_err())); 15052 } 15053 } 15054 else 15055 { 15056 ctx.report_error(std::move(kv_res.unwrap_err())); 15057 skip_key_value_pair(loc, ctx); 15058 } 15059 } 15060 15061 if(num_errors < ctx.errors().size()) 15062 { 15063 assert(ctx.has_error()); // already reported 15064 return err(ctx.pop_last_error()); 15065 } 15066 return ok(); 15067 } 15068 15069 template<typename TC> 15070 result<basic_value<TC>, std::vector<error_info>> 15071 parse_file(location& loc, context<TC>& ctx) 15072 { 15073 using value_type = basic_value<TC>; 15074 using table_type = typename value_type::table_type; 15075 15076 const auto first = loc; 15077 const auto& spec = ctx.toml_spec(); 15078 15079 if(loc.eof()) 15080 { 15081 return ok(value_type(table_type(), table_format_info{}, {}, region(loc))); 15082 } 15083 15084 value_type root(table_type(), table_format_info{}, {}, region(loc)); 15085 root.as_table_fmt().fmt = table_format::multiline; 15086 root.as_table_fmt().indent_type = indent_char::none; 15087 15088 // parse top comment. 15089 // 15090 // ```toml 15091 // # this is a comment for the top-level table. 15092 // 15093 // key = "the first value" 15094 // ``` 15095 // 15096 // ```toml 15097 // # this is a comment for "the first value". 15098 // key = "the first value" 15099 // ``` 15100 while( ! loc.eof()) 15101 { 15102 if(auto com_res = parse_comment_line(loc, ctx)) 15103 { 15104 if(auto com_opt = com_res.unwrap()) 15105 { 15106 root.comments().push_back(std::move(com_opt.value())); 15107 } 15108 else // no comment found. 15109 { 15110 // if it is not an empty line, clear the root comment. 15111 if( ! sequence(syntax::ws(spec), syntax::newline(spec)).scan(loc).is_ok()) 15112 { 15113 loc = first; 15114 root.comments().clear(); 15115 } 15116 break; 15117 } 15118 } 15119 else 15120 { 15121 ctx.report_error(std::move(com_res.unwrap_err())); 15122 skip_comment_block(loc, ctx); 15123 } 15124 } 15125 15126 // parse root table 15127 { 15128 const auto res = parse_table(loc, ctx, root); 15129 if(res.is_err()) 15130 { 15131 ctx.report_error(std::move(res.unwrap_err())); 15132 skip_until_next_table(loc, ctx); 15133 } 15134 } 15135 15136 // parse tables 15137 15138 while( ! loc.eof()) 15139 { 15140 auto sp = skip_multiline_spacer(loc, ctx, /*newline_found=*/true); 15141 15142 if(auto key_res = parse_array_table_key(loc, ctx)) 15143 { 15144 auto key = std::move(std::get<0>(key_res.unwrap())); 15145 auto reg = std::move(std::get<1>(key_res.unwrap())); 15146 15147 std::vector<std::string> com; 15148 if(sp.has_value()) 15149 { 15150 for(std::size_t i=0; i<sp.value().comments.size(); ++i) 15151 { 15152 com.push_back(std::move(sp.value().comments.at(i))); 15153 } 15154 } 15155 15156 // [table.def] must be followed by one of 15157 // - a comment line 15158 // - whitespace + newline 15159 // - EOF 15160 if(auto com_res = parse_comment_line(loc, ctx)) 15161 { 15162 if(auto com_opt = com_res.unwrap()) 15163 { 15164 com.push_back(com_opt.value()); 15165 } 15166 else // if there is no comment, ws+newline must exist (or EOF) 15167 { 15168 skip_whitespace(loc, ctx); 15169 if( ! loc.eof() && ! syntax::newline(ctx.toml_spec()).scan(loc).is_ok()) 15170 { 15171 ctx.report_error(make_syntax_error("toml::parse_file: " 15172 "newline (or EOF) expected", 15173 syntax::newline(ctx.toml_spec()), loc)); 15174 skip_until_next_table(loc, ctx); 15175 continue; 15176 } 15177 } 15178 } 15179 else // comment syntax error (rare) 15180 { 15181 ctx.report_error(com_res.unwrap_err()); 15182 skip_until_next_table(loc, ctx); 15183 continue; 15184 } 15185 15186 table_format_info fmt; 15187 fmt.fmt = table_format::multiline; 15188 fmt.indent_type = indent_char::none; 15189 auto tab = value_type(table_type{}, std::move(fmt), std::move(com), reg); 15190 15191 auto inserted = insert_value(inserting_value_kind::array_table, 15192 std::addressof(root.as_table()), 15193 key, std::move(reg), std::move(tab)); 15194 15195 if(inserted.is_err()) 15196 { 15197 ctx.report_error(inserted.unwrap_err()); 15198 15199 // check errors in the table 15200 auto tmp = basic_value<TC>(table_type()); 15201 auto res = parse_table(loc, ctx, tmp); 15202 if(res.is_err()) 15203 { 15204 ctx.report_error(res.unwrap_err()); 15205 skip_until_next_table(loc, ctx); 15206 } 15207 continue; 15208 } 15209 15210 auto tab_ptr = inserted.unwrap(); 15211 assert(tab_ptr); 15212 15213 const auto tab_res = parse_table(loc, ctx, *tab_ptr); 15214 if(tab_res.is_err()) 15215 { 15216 ctx.report_error(tab_res.unwrap_err()); 15217 skip_until_next_table(loc, ctx); 15218 } 15219 15220 // parse_table first clears `indent_type`. 15221 // to keep header indent info, we must store it later. 15222 if(sp.has_value() && sp.value().indent_type != indent_char::none) 15223 { 15224 tab_ptr->as_table_fmt().indent_type = sp.value().indent_type; 15225 tab_ptr->as_table_fmt().name_indent = sp.value().indent; 15226 } 15227 continue; 15228 } 15229 if(auto key_res = parse_table_key(loc, ctx)) 15230 { 15231 auto key = std::move(std::get<0>(key_res.unwrap())); 15232 auto reg = std::move(std::get<1>(key_res.unwrap())); 15233 15234 std::vector<std::string> com; 15235 if(sp.has_value()) 15236 { 15237 for(std::size_t i=0; i<sp.value().comments.size(); ++i) 15238 { 15239 com.push_back(std::move(sp.value().comments.at(i))); 15240 } 15241 } 15242 15243 // [table.def] must be followed by one of 15244 // - a comment line 15245 // - whitespace + newline 15246 // - EOF 15247 if(auto com_res = parse_comment_line(loc, ctx)) 15248 { 15249 if(auto com_opt = com_res.unwrap()) 15250 { 15251 com.push_back(com_opt.value()); 15252 } 15253 else // if there is no comment, ws+newline must exist (or EOF) 15254 { 15255 skip_whitespace(loc, ctx); 15256 if( ! loc.eof() && ! syntax::newline(ctx.toml_spec()).scan(loc).is_ok()) 15257 { 15258 ctx.report_error(make_syntax_error("toml::parse_file: " 15259 "newline (or EOF) expected", 15260 syntax::newline(ctx.toml_spec()), loc)); 15261 skip_until_next_table(loc, ctx); 15262 continue; 15263 } 15264 } 15265 } 15266 else // comment syntax error (rare) 15267 { 15268 ctx.report_error(com_res.unwrap_err()); 15269 skip_until_next_table(loc, ctx); 15270 continue; 15271 } 15272 15273 table_format_info fmt; 15274 fmt.fmt = table_format::multiline; 15275 fmt.indent_type = indent_char::none; 15276 auto tab = value_type(table_type{}, std::move(fmt), std::move(com), reg); 15277 15278 auto inserted = insert_value(inserting_value_kind::std_table, 15279 std::addressof(root.as_table()), 15280 key, std::move(reg), std::move(tab)); 15281 15282 if(inserted.is_err()) 15283 { 15284 ctx.report_error(inserted.unwrap_err()); 15285 15286 // check errors in the table 15287 auto tmp = basic_value<TC>(table_type()); 15288 auto res = parse_table(loc, ctx, tmp); 15289 if(res.is_err()) 15290 { 15291 ctx.report_error(res.unwrap_err()); 15292 skip_until_next_table(loc, ctx); 15293 } 15294 continue; 15295 } 15296 15297 auto tab_ptr = inserted.unwrap(); 15298 assert(tab_ptr); 15299 15300 const auto tab_res = parse_table(loc, ctx, *tab_ptr); 15301 if(tab_res.is_err()) 15302 { 15303 ctx.report_error(tab_res.unwrap_err()); 15304 skip_until_next_table(loc, ctx); 15305 } 15306 if(sp.has_value() && sp.value().indent_type != indent_char::none) 15307 { 15308 tab_ptr->as_table_fmt().indent_type = sp.value().indent_type; 15309 tab_ptr->as_table_fmt().name_indent = sp.value().indent; 15310 } 15311 continue; 15312 } 15313 15314 // does not match array_table nor std_table. report an error. 15315 const auto keytop = loc; 15316 const auto maybe_array_of_tables = literal("[[").scan(loc).is_ok(); 15317 loc = keytop; 15318 15319 if(maybe_array_of_tables) 15320 { 15321 ctx.report_error(make_syntax_error("toml::parse_file: invalid array-table key", 15322 syntax::array_table(spec), loc)); 15323 } 15324 else 15325 { 15326 ctx.report_error(make_syntax_error("toml::parse_file: invalid table key", 15327 syntax::std_table(spec), loc)); 15328 } 15329 skip_until_next_table(loc, ctx); 15330 } 15331 15332 if( ! ctx.errors().empty()) 15333 { 15334 return err(std::move(ctx.errors())); 15335 } 15336 return ok(std::move(root)); 15337 } 15338 15339 template<typename TC> 15340 result<basic_value<TC>, std::vector<error_info>> 15341 parse_impl(std::vector<location::char_type> cs, std::string fname, const spec& s) 15342 { 15343 using value_type = basic_value<TC>; 15344 using table_type = typename value_type::table_type; 15345 15346 // an empty file is a valid toml file. 15347 if(cs.empty()) 15348 { 15349 auto src = std::make_shared<std::vector<location::char_type>>(std::move(cs)); 15350 location loc(std::move(src), std::move(fname)); 15351 return ok(value_type(table_type(), table_format_info{}, std::vector<std::string>{}, region(loc))); 15352 } 15353 15354 // to simplify parser, add newline at the end if there is no LF. 15355 // But, if it has raw CR, the file is invalid (in TOML, CR is not a valid 15356 // newline char). if it ends with CR, do not add LF and report it. 15357 if(cs.back() != '\n' && cs.back() != '\r') 15358 { 15359 cs.push_back('\n'); 15360 } 15361 15362 auto src = std::make_shared<std::vector<location::char_type>>(std::move(cs)); 15363 15364 location loc(std::move(src), std::move(fname)); 15365 15366 // skip BOM if found 15367 if(loc.source()->size() >= 3) 15368 { 15369 auto first = loc.get_location(); 15370 15371 const auto c0 = loc.current(); loc.advance(); 15372 const auto c1 = loc.current(); loc.advance(); 15373 const auto c2 = loc.current(); loc.advance(); 15374 15375 const auto bom_found = (c0 == 0xEF) && (c1 == 0xBB) && (c2 == 0xBF); 15376 if( ! bom_found) 15377 { 15378 loc.set_location(first); 15379 } 15380 } 15381 15382 context<TC> ctx(s); 15383 15384 return parse_file(loc, ctx); 15385 } 15386 15387 } // detail 15388 15389 // ----------------------------------------------------------------------------- 15390 // parse(byte array) 15391 15392 template<typename TC = type_config> 15393 result<basic_value<TC>, std::vector<error_info>> 15394 try_parse(std::vector<unsigned char> content, std::string filename, 15395 spec s = spec::default_version()) 15396 { 15397 return detail::parse_impl<TC>(std::move(content), std::move(filename), std::move(s)); 15398 } 15399 template<typename TC = type_config> 15400 basic_value<TC> 15401 parse(std::vector<unsigned char> content, std::string filename, 15402 spec s = spec::default_version()) 15403 { 15404 auto res = try_parse<TC>(std::move(content), std::move(filename), std::move(s)); 15405 if(res.is_ok()) 15406 { 15407 return res.unwrap(); 15408 } 15409 else 15410 { 15411 std::string msg; 15412 for(const auto& err : res.unwrap_err()) 15413 { 15414 msg += format_error(err); 15415 } 15416 throw syntax_error(std::move(msg), std::move(res.unwrap_err())); 15417 } 15418 } 15419 15420 // ----------------------------------------------------------------------------- 15421 // parse(istream) 15422 15423 template<typename TC = type_config> 15424 result<basic_value<TC>, std::vector<error_info>> 15425 try_parse(std::istream& is, std::string fname = "unknown file", spec s = spec::default_version()) 15426 { 15427 const auto beg = is.tellg(); 15428 is.seekg(0, std::ios::end); 15429 const auto end = is.tellg(); 15430 const auto fsize = end - beg; 15431 is.seekg(beg); 15432 15433 // read whole file as a sequence of char 15434 assert(fsize >= 0); 15435 std::vector<detail::location::char_type> letters(static_cast<std::size_t>(fsize), '\0'); 15436 is.read(reinterpret_cast<char*>(letters.data()), static_cast<std::streamsize>(fsize)); 15437 15438 return detail::parse_impl<TC>(std::move(letters), std::move(fname), std::move(s)); 15439 } 15440 15441 template<typename TC = type_config> 15442 basic_value<TC> parse(std::istream& is, std::string fname = "unknown file", spec s = spec::default_version()) 15443 { 15444 auto res = try_parse<TC>(is, std::move(fname), std::move(s)); 15445 if(res.is_ok()) 15446 { 15447 return res.unwrap(); 15448 } 15449 else 15450 { 15451 std::string msg; 15452 for(const auto& err : res.unwrap_err()) 15453 { 15454 msg += format_error(err); 15455 } 15456 throw syntax_error(std::move(msg), std::move(res.unwrap_err())); 15457 } 15458 } 15459 15460 // ----------------------------------------------------------------------------- 15461 // parse(filename) 15462 15463 template<typename TC = type_config> 15464 result<basic_value<TC>, std::vector<error_info>> 15465 try_parse(std::string fname, spec s = spec::default_version()) 15466 { 15467 std::ifstream ifs(fname, std::ios_base::binary); 15468 if(!ifs.good()) 15469 { 15470 std::vector<error_info> e; 15471 e.push_back(error_info("toml::parse: Error opening file \"" + fname + "\"", {})); 15472 return err(std::move(e)); 15473 } 15474 ifs.exceptions(std::ifstream::failbit | std::ifstream::badbit); 15475 15476 return try_parse<TC>(ifs, std::move(fname), std::move(s)); 15477 } 15478 15479 template<typename TC = type_config> 15480 basic_value<TC> parse(std::string fname, spec s = spec::default_version()) 15481 { 15482 std::ifstream ifs(fname, std::ios_base::binary); 15483 if(!ifs.good()) 15484 { 15485 throw file_io_error("toml::parse: error opening file", fname); 15486 } 15487 ifs.exceptions(std::ifstream::failbit | std::ifstream::badbit); 15488 15489 return parse<TC>(ifs, std::move(fname), std::move(s)); 15490 } 15491 15492 template<typename TC = type_config, std::size_t N> 15493 result<basic_value<TC>, std::vector<error_info>> 15494 try_parse(const char (&fname)[N], spec s = spec::default_version()) 15495 { 15496 return try_parse<TC>(std::string(fname), std::move(s)); 15497 } 15498 15499 template<typename TC = type_config, std::size_t N> 15500 basic_value<TC> parse(const char (&fname)[N], spec s = spec::default_version()) 15501 { 15502 return parse<TC>(std::string(fname), std::move(s)); 15503 } 15504 15505 // ---------------------------------------------------------------------------- 15506 // parse_str 15507 15508 template<typename TC = type_config> 15509 result<basic_value<TC>, std::vector<error_info>> 15510 try_parse_str(std::string content, spec s = spec::default_version(), 15511 cxx::source_location loc = cxx::source_location::current()) 15512 { 15513 std::istringstream iss(std::move(content)); 15514 std::string name("internal string" + cxx::to_string(loc)); 15515 return try_parse<TC>(iss, std::move(name), std::move(s)); 15516 } 15517 15518 template<typename TC = type_config> 15519 basic_value<TC> parse_str(std::string content, spec s = spec::default_version(), 15520 cxx::source_location loc = cxx::source_location::current()) 15521 { 15522 auto res = try_parse_str<TC>(std::move(content), std::move(s), std::move(loc)); 15523 if(res.is_ok()) 15524 { 15525 return res.unwrap(); 15526 } 15527 else 15528 { 15529 std::string msg; 15530 for(const auto& err : res.unwrap_err()) 15531 { 15532 msg += format_error(err); 15533 } 15534 throw syntax_error(std::move(msg), std::move(res.unwrap_err())); 15535 } 15536 } 15537 15538 // ---------------------------------------------------------------------------- 15539 // filesystem 15540 15541 #if defined(TOML11_HAS_FILESYSTEM) 15542 15543 template<typename TC = type_config, typename FSPATH> 15544 cxx::enable_if_t<std::is_same<FSPATH, std::filesystem::path>::value, 15545 result<basic_value<TC>, std::vector<error_info>>> 15546 try_parse(const FSPATH& fpath, spec s = spec::default_version()) 15547 { 15548 std::ifstream ifs(fpath, std::ios_base::binary); 15549 if(!ifs.good()) 15550 { 15551 std::vector<error_info> e; 15552 e.push_back(error_info("toml::parse: Error opening file \"" + fpath.string() + "\"", {})); 15553 return err(std::move(e)); 15554 } 15555 ifs.exceptions(std::ifstream::failbit | std::ifstream::badbit); 15556 15557 return try_parse<TC>(ifs, fpath.string(), std::move(s)); 15558 } 15559 15560 template<typename TC = type_config, typename FSPATH> 15561 cxx::enable_if_t<std::is_same<FSPATH, std::filesystem::path>::value, 15562 basic_value<TC>> 15563 parse(const FSPATH& fpath, spec s = spec::default_version()) 15564 { 15565 std::ifstream ifs(fpath, std::ios_base::binary); 15566 if(!ifs.good()) 15567 { 15568 throw file_io_error("toml::parse: error opening file", fpath.string()); 15569 } 15570 ifs.exceptions(std::ifstream::failbit | std::ifstream::badbit); 15571 15572 return parse<TC>(ifs, fpath.string(), std::move(s)); 15573 } 15574 #endif 15575 15576 // ----------------------------------------------------------------------------- 15577 // FILE* 15578 15579 template<typename TC = type_config> 15580 result<basic_value<TC>, std::vector<error_info>> 15581 try_parse(FILE* fp, std::string filename, spec s = spec::default_version()) 15582 { 15583 const long beg = std::ftell(fp); 15584 if (beg == -1L) 15585 { 15586 return err(std::vector<error_info>{error_info( 15587 std::string("Failed to access: \"") + filename + 15588 "\", errno = " + std::to_string(errno), {} 15589 )}); 15590 } 15591 15592 const int res_seekend = std::fseek(fp, 0, SEEK_END); 15593 if (res_seekend != 0) 15594 { 15595 return err(std::vector<error_info>{error_info( 15596 std::string("Failed to seek: \"") + filename + 15597 "\", errno = " + std::to_string(errno), {} 15598 )}); 15599 } 15600 15601 const long end = std::ftell(fp); 15602 if (end == -1L) 15603 { 15604 return err(std::vector<error_info>{error_info( 15605 std::string("Failed to access: \"") + filename + 15606 "\", errno = " + std::to_string(errno), {} 15607 )}); 15608 } 15609 15610 const auto fsize = end - beg; 15611 15612 const auto res_seekbeg = std::fseek(fp, beg, SEEK_SET); 15613 if (res_seekbeg != 0) 15614 { 15615 return err(std::vector<error_info>{error_info( 15616 std::string("Failed to seek: \"") + filename + 15617 "\", errno = " + std::to_string(errno), {} 15618 )}); 15619 15620 } 15621 15622 // read whole file as a sequence of char 15623 assert(fsize >= 0); 15624 std::vector<detail::location::char_type> letters(static_cast<std::size_t>(fsize)); 15625 const auto actual = std::fread(letters.data(), sizeof(char), static_cast<std::size_t>(fsize), fp); 15626 if(actual != static_cast<std::size_t>(fsize)) 15627 { 15628 return err(std::vector<error_info>{error_info( 15629 std::string("File size changed: \"") + filename + 15630 std::string("\" make sure that FILE* is in binary mode " 15631 "to avoid LF <-> CRLF conversion"), {} 15632 )}); 15633 } 15634 15635 return detail::parse_impl<TC>(std::move(letters), std::move(filename), std::move(s)); 15636 } 15637 15638 template<typename TC = type_config> 15639 basic_value<TC> 15640 parse(FILE* fp, std::string filename, spec s = spec::default_version()) 15641 { 15642 const long beg = std::ftell(fp); 15643 if (beg == -1L) 15644 { 15645 throw file_io_error(errno, "Failed to access", filename); 15646 } 15647 15648 const int res_seekend = std::fseek(fp, 0, SEEK_END); 15649 if (res_seekend != 0) 15650 { 15651 throw file_io_error(errno, "Failed to seek", filename); 15652 } 15653 15654 const long end = std::ftell(fp); 15655 if (end == -1L) 15656 { 15657 throw file_io_error(errno, "Failed to access", filename); 15658 } 15659 15660 const auto fsize = end - beg; 15661 15662 const auto res_seekbeg = std::fseek(fp, beg, SEEK_SET); 15663 if (res_seekbeg != 0) 15664 { 15665 throw file_io_error(errno, "Failed to seek", filename); 15666 } 15667 15668 // read whole file as a sequence of char 15669 assert(fsize >= 0); 15670 std::vector<detail::location::char_type> letters(static_cast<std::size_t>(fsize)); 15671 const auto actual = std::fread(letters.data(), sizeof(char), static_cast<std::size_t>(fsize), fp); 15672 if(actual != static_cast<std::size_t>(fsize)) 15673 { 15674 throw file_io_error(errno, "File size changed; make sure that " 15675 "FILE* is in binary mode to avoid LF <-> CRLF conversion", filename); 15676 } 15677 15678 auto res = detail::parse_impl<TC>(std::move(letters), std::move(filename), std::move(s)); 15679 if(res.is_ok()) 15680 { 15681 return res.unwrap(); 15682 } 15683 else 15684 { 15685 std::string msg; 15686 for(const auto& err : res.unwrap_err()) 15687 { 15688 msg += format_error(err); 15689 } 15690 throw syntax_error(std::move(msg), std::move(res.unwrap_err())); 15691 } 15692 } 15693 15694 } // namespace toml 15695 15696 #if defined(TOML11_COMPILE_SOURCES) 15697 namespace toml 15698 { 15699 struct type_config; 15700 struct ordered_type_config; 15701 15702 extern template result<basic_value<type_config>, std::vector<error_info>> try_parse<type_config>(std::vector<unsigned char>, std::string, spec); 15703 extern template result<basic_value<type_config>, std::vector<error_info>> try_parse<type_config>(std::istream&, std::string, spec); 15704 extern template result<basic_value<type_config>, std::vector<error_info>> try_parse<type_config>(std::string, spec); 15705 extern template result<basic_value<type_config>, std::vector<error_info>> try_parse<type_config>(FILE*, std::string, spec); 15706 extern template result<basic_value<type_config>, std::vector<error_info>> try_parse_str<type_config>(std::string, spec, cxx::source_location); 15707 15708 extern template basic_value<type_config> parse<type_config>(std::vector<unsigned char>, std::string, spec); 15709 extern template basic_value<type_config> parse<type_config>(std::istream&, std::string, spec); 15710 extern template basic_value<type_config> parse<type_config>(std::string, spec); 15711 extern template basic_value<type_config> parse<type_config>(FILE*, std::string, spec); 15712 extern template basic_value<type_config> parse_str<type_config>(std::string, spec, cxx::source_location); 15713 15714 extern template result<basic_value<ordered_type_config>, std::vector<error_info>> try_parse<ordered_type_config>(std::vector<unsigned char>, std::string, spec); 15715 extern template result<basic_value<ordered_type_config>, std::vector<error_info>> try_parse<ordered_type_config>(std::istream&, std::string, spec); 15716 extern template result<basic_value<ordered_type_config>, std::vector<error_info>> try_parse<ordered_type_config>(std::string, spec); 15717 extern template result<basic_value<ordered_type_config>, std::vector<error_info>> try_parse<ordered_type_config>(FILE*, std::string, spec); 15718 extern template result<basic_value<ordered_type_config>, std::vector<error_info>> try_parse_str<ordered_type_config>(std::string, spec, cxx::source_location); 15719 15720 extern template basic_value<ordered_type_config> parse<ordered_type_config>(std::vector<unsigned char>, std::string, spec); 15721 extern template basic_value<ordered_type_config> parse<ordered_type_config>(std::istream&, std::string, spec); 15722 extern template basic_value<ordered_type_config> parse<ordered_type_config>(std::string, spec); 15723 extern template basic_value<ordered_type_config> parse<ordered_type_config>(FILE*, std::string, spec); 15724 extern template basic_value<ordered_type_config> parse_str<ordered_type_config>(std::string, spec, cxx::source_location); 15725 15726 #if defined(TOML11_HAS_FILESYSTEM) 15727 extern template cxx::enable_if_t<std::is_same<std::filesystem::path, std::filesystem::path>::value, result<basic_value<type_config>, std::vector<error_info>>> try_parse<type_config, std::filesystem::path>(const std::filesystem::path&, spec); 15728 extern template cxx::enable_if_t<std::is_same<std::filesystem::path, std::filesystem::path>::value, result<basic_value<ordered_type_config>, std::vector<error_info>>> try_parse<ordered_type_config, std::filesystem::path>(const std::filesystem::path&, spec); 15729 extern template cxx::enable_if_t<std::is_same<std::filesystem::path, std::filesystem::path>::value, basic_value<type_config> > parse <type_config, std::filesystem::path>(const std::filesystem::path&, spec); 15730 extern template cxx::enable_if_t<std::is_same<std::filesystem::path, std::filesystem::path>::value, basic_value<ordered_type_config> > parse <ordered_type_config, std::filesystem::path>(const std::filesystem::path&, spec); 15731 #endif // filesystem 15732 15733 } // toml 15734 #endif // TOML11_COMPILE_SOURCES 15735 15736 #endif // TOML11_PARSER_HPP 15737 #ifndef TOML11_LITERAL_HPP 15738 #define TOML11_LITERAL_HPP 15739 15740 #ifndef TOML11_LITERAL_FWD_HPP 15741 #define TOML11_LITERAL_FWD_HPP 15742 15743 15744 namespace toml 15745 { 15746 15747 namespace detail 15748 { 15749 // implementation 15750 ::toml::value literal_internal_impl(location loc); 15751 } // detail 15752 15753 inline namespace literals 15754 { 15755 inline namespace toml_literals 15756 { 15757 15758 ::toml::value operator"" _toml(const char* str, std::size_t len); 15759 15760 #if defined(TOML11_HAS_CHAR8_T) 15761 // value of u8"" literal has been changed from char to char8_t and char8_t is 15762 // NOT compatible to char 15763 ::toml::value operator"" _toml(const char8_t* str, std::size_t len); 15764 #endif 15765 15766 } // toml_literals 15767 } // literals 15768 } // toml 15769 #endif // TOML11_LITERAL_FWD_HPP 15770 15771 #if ! defined(TOML11_COMPILE_SOURCES) 15772 #ifndef TOML11_LITERAL_IMPL_HPP 15773 #define TOML11_LITERAL_IMPL_HPP 15774 15775 15776 namespace toml 15777 { 15778 15779 namespace detail 15780 { 15781 // implementation 15782 TOML11_INLINE ::toml::value literal_internal_impl(location loc) 15783 { 15784 const auto s = ::toml::spec::default_version(); 15785 context<type_config> ctx(s); 15786 15787 const auto front = loc; 15788 15789 // ------------------------------------------------------------------------ 15790 // check if it is a raw value. 15791 15792 // skip empty lines and comment lines 15793 auto sp = skip_multiline_spacer(loc, ctx); 15794 if(loc.eof()) 15795 { 15796 ::toml::value val; 15797 if(sp.has_value()) 15798 { 15799 for(std::size_t i=0; i<sp.value().comments.size(); ++i) 15800 { 15801 val.comments().push_back(std::move(sp.value().comments.at(i))); 15802 } 15803 } 15804 return val; 15805 } 15806 15807 // to distinguish arrays and tables, first check it is a table or not. 15808 // 15809 // "[1,2,3]"_toml; // json: [1, 2, 3] 15810 // "[table]"_toml; // json: {"table": {}} 15811 // "[[1,2,3]]"_toml; // json: [[1, 2, 3]] 15812 // "[[table]]"_toml; // json: {"table": [{}]} 15813 // 15814 // "[[1]]"_toml; // json: {"1": [{}]} 15815 // "1 = [{}]"_toml; // json: {"1": [{}]} 15816 // "[[1,]]"_toml; // json: [[1]] 15817 // "[[1],]"_toml; // json: [[1]] 15818 const auto val_start = loc; 15819 15820 const bool is_table_key = syntax::std_table(s).scan(loc).is_ok(); 15821 loc = val_start; 15822 const bool is_aots_key = syntax::array_table(s).scan(loc).is_ok(); 15823 loc = val_start; 15824 15825 // If it is neither a table-key or a array-of-table-key, it may be a value. 15826 if(!is_table_key && !is_aots_key) 15827 { 15828 auto data = parse_value(loc, ctx); 15829 if(data.is_ok()) 15830 { 15831 auto val = std::move(data.unwrap()); 15832 if(sp.has_value()) 15833 { 15834 for(std::size_t i=0; i<sp.value().comments.size(); ++i) 15835 { 15836 val.comments().push_back(std::move(sp.value().comments.at(i))); 15837 } 15838 } 15839 auto com_res = parse_comment_line(loc, ctx); 15840 if(com_res.is_ok() && com_res.unwrap().has_value()) 15841 { 15842 val.comments().push_back(com_res.unwrap().value()); 15843 } 15844 return val; 15845 } 15846 } 15847 15848 // ------------------------------------------------------------------------- 15849 // Note that still it can be a table, because the literal might be something 15850 // like the following. 15851 // ```cpp 15852 // // c++11 raw-string literal 15853 // const auto val = R"( 15854 // key = "value" 15855 // int = 42 15856 // )"_toml; 15857 // ``` 15858 // It is a valid toml file. 15859 // It should be parsed as if we parse a file with this content. 15860 15861 loc = front; 15862 auto data = parse_file(loc, ctx); 15863 if(data.is_ok()) 15864 { 15865 return data.unwrap(); 15866 } 15867 else // not a value && not a file. error. 15868 { 15869 std::string msg; 15870 for(const auto& err : data.unwrap_err()) 15871 { 15872 msg += format_error(err); 15873 } 15874 throw ::toml::syntax_error(std::move(msg), std::move(data.unwrap_err())); 15875 } 15876 } 15877 15878 } // detail 15879 15880 inline namespace literals 15881 { 15882 inline namespace toml_literals 15883 { 15884 15885 TOML11_INLINE ::toml::value 15886 operator"" _toml(const char* str, std::size_t len) 15887 { 15888 if(len == 0) 15889 { 15890 return ::toml::value{}; 15891 } 15892 15893 ::toml::detail::location::container_type c(len); 15894 std::copy(reinterpret_cast<const ::toml::detail::location::char_type*>(str), 15895 reinterpret_cast<const ::toml::detail::location::char_type*>(str + len), 15896 c.begin()); 15897 if( ! c.empty() && c.back()) 15898 { 15899 c.push_back('\n'); // to make it easy to parse comment, we add newline 15900 } 15901 15902 return literal_internal_impl(::toml::detail::location( 15903 std::make_shared<const toml::detail::location::container_type>(std::move(c)), 15904 "TOML literal encoded in a C++ code")); 15905 } 15906 15907 #if defined(__cpp_char8_t) 15908 # if __cpp_char8_t >= 201811L 15909 # define TOML11_HAS_CHAR8_T 1 15910 # endif 15911 #endif 15912 15913 #if defined(TOML11_HAS_CHAR8_T) 15914 // value of u8"" literal has been changed from char to char8_t and char8_t is 15915 // NOT compatible to char 15916 TOML11_INLINE ::toml::value 15917 operator"" _toml(const char8_t* str, std::size_t len) 15918 { 15919 if(len == 0) 15920 { 15921 return ::toml::value{}; 15922 } 15923 15924 ::toml::detail::location::container_type c(len); 15925 std::copy(reinterpret_cast<const ::toml::detail::location::char_type*>(str), 15926 reinterpret_cast<const ::toml::detail::location::char_type*>(str + len), 15927 c.begin()); 15928 if( ! c.empty() && c.back()) 15929 { 15930 c.push_back('\n'); // to make it easy to parse comment, we add newline 15931 } 15932 15933 return literal_internal_impl(::toml::detail::location( 15934 std::make_shared<const toml::detail::location::container_type>(std::move(c)), 15935 "TOML literal encoded in a C++ code")); 15936 } 15937 #endif 15938 15939 } // toml_literals 15940 } // literals 15941 } // toml 15942 #endif // TOML11_LITERAL_IMPL_HPP 15943 #endif 15944 15945 #endif // TOML11_LITERAL_HPP 15946 #ifndef TOML11_SERIALIZER_HPP 15947 #define TOML11_SERIALIZER_HPP 15948 15949 15950 #include <iomanip> 15951 #include <iterator> 15952 #include <sstream> 15953 15954 #include <cmath> 15955 #include <cstdio> 15956 15957 namespace toml 15958 { 15959 15960 struct serialization_error final : public ::toml::exception 15961 { 15962 public: 15963 explicit serialization_error(std::string what_arg, source_location loc) 15964 : what_(std::move(what_arg)), loc_(std::move(loc)) 15965 {} 15966 ~serialization_error() noexcept override = default; 15967 15968 const char* what() const noexcept override {return what_.c_str();} 15969 source_location const& location() const noexcept {return loc_;} 15970 15971 private: 15972 std::string what_; 15973 source_location loc_; 15974 }; 15975 15976 namespace detail 15977 { 15978 template<typename TC> 15979 class serializer 15980 { 15981 public: 15982 15983 using value_type = basic_value<TC>; 15984 15985 using key_type = typename value_type::key_type ; 15986 using comment_type = typename value_type::comment_type ; 15987 using boolean_type = typename value_type::boolean_type ; 15988 using integer_type = typename value_type::integer_type ; 15989 using floating_type = typename value_type::floating_type ; 15990 using string_type = typename value_type::string_type ; 15991 using local_time_type = typename value_type::local_time_type ; 15992 using local_date_type = typename value_type::local_date_type ; 15993 using local_datetime_type = typename value_type::local_datetime_type ; 15994 using offset_datetime_type = typename value_type::offset_datetime_type; 15995 using array_type = typename value_type::array_type ; 15996 using table_type = typename value_type::table_type ; 15997 15998 using char_type = typename string_type::value_type; 15999 16000 public: 16001 16002 explicit serializer(const spec& sp) 16003 : spec_(sp), force_inline_(false), current_indent_(0) 16004 {} 16005 16006 string_type operator()(const std::vector<key_type>& ks, const value_type& v) 16007 { 16008 for(const auto& k : ks) 16009 { 16010 this->keys_.push_back(k); 16011 } 16012 return (*this)(v); 16013 } 16014 16015 string_type operator()(const key_type& k, const value_type& v) 16016 { 16017 this->keys_.push_back(k); 16018 return (*this)(v); 16019 } 16020 16021 string_type operator()(const value_type& v) 16022 { 16023 switch(v.type()) 16024 { 16025 case value_t::boolean : {return (*this)(v.as_boolean (), v.as_boolean_fmt (), v.location());} 16026 case value_t::integer : {return (*this)(v.as_integer (), v.as_integer_fmt (), v.location());} 16027 case value_t::floating : {return (*this)(v.as_floating (), v.as_floating_fmt (), v.location());} 16028 case value_t::string : {return (*this)(v.as_string (), v.as_string_fmt (), v.location());} 16029 case value_t::offset_datetime: {return (*this)(v.as_offset_datetime(), v.as_offset_datetime_fmt(), v.location());} 16030 case value_t::local_datetime : {return (*this)(v.as_local_datetime (), v.as_local_datetime_fmt (), v.location());} 16031 case value_t::local_date : {return (*this)(v.as_local_date (), v.as_local_date_fmt (), v.location());} 16032 case value_t::local_time : {return (*this)(v.as_local_time (), v.as_local_time_fmt (), v.location());} 16033 case value_t::array : 16034 { 16035 return (*this)(v.as_array(), v.as_array_fmt(), v.comments(), v.location()); 16036 } 16037 case value_t::table : 16038 { 16039 string_type retval; 16040 if(this->keys_.empty()) // it might be the root table. emit comments here. 16041 { 16042 retval += format_comments(v.comments(), v.as_table_fmt().indent_type); 16043 } 16044 if( ! retval.empty()) // we have comment. 16045 { 16046 retval += char_type('\n'); 16047 } 16048 16049 retval += (*this)(v.as_table(), v.as_table_fmt(), v.comments(), v.location()); 16050 return retval; 16051 } 16052 case value_t::empty: 16053 { 16054 if(this->spec_.ext_null_value) 16055 { 16056 return string_conv<string_type>("null"); 16057 } 16058 break; 16059 } 16060 default: 16061 { 16062 break; 16063 } 16064 } 16065 throw serialization_error(format_error( 16066 "[error] toml::serializer: toml::basic_value " 16067 "does not have any valid type.", v.location(), "here"), v.location()); 16068 } 16069 16070 private: 16071 16072 string_type operator()(const boolean_type& b, const boolean_format_info&, const source_location&) // {{{ 16073 { 16074 if(b) 16075 { 16076 return string_conv<string_type>("true"); 16077 } 16078 else 16079 { 16080 return string_conv<string_type>("false"); 16081 } 16082 } // }}} 16083 16084 string_type operator()(const integer_type i, const integer_format_info& fmt, const source_location& loc) // {{{ 16085 { 16086 std::ostringstream oss; 16087 this->set_locale(oss); 16088 16089 const auto insert_spacer = [&fmt](std::string s) -> std::string { 16090 if(fmt.spacer == 0) {return s;} 16091 16092 std::string sign; 16093 if( ! s.empty() && (s.at(0) == '+' || s.at(0) == '-')) 16094 { 16095 sign += s.at(0); 16096 s.erase(s.begin()); 16097 } 16098 16099 std::string spaced; 16100 std::size_t counter = 0; 16101 for(auto iter = s.rbegin(); iter != s.rend(); ++iter) 16102 { 16103 if(counter != 0 && counter % fmt.spacer == 0) 16104 { 16105 spaced += '_'; 16106 } 16107 spaced += *iter; 16108 counter += 1; 16109 } 16110 if(!spaced.empty() && spaced.back() == '_') {spaced.pop_back();} 16111 16112 s.clear(); 16113 std::copy(spaced.rbegin(), spaced.rend(), std::back_inserter(s)); 16114 return sign + s; 16115 }; 16116 16117 std::string retval; 16118 if(fmt.fmt == integer_format::dec) 16119 { 16120 oss << std::setw(static_cast<int>(fmt.width)) << std::dec << i; 16121 retval = insert_spacer(oss.str()); 16122 16123 if(this->spec_.ext_num_suffix && ! fmt.suffix.empty()) 16124 { 16125 retval += '_'; 16126 retval += fmt.suffix; 16127 } 16128 } 16129 else 16130 { 16131 if(i < 0) 16132 { 16133 throw serialization_error(format_error("binary, octal, hexadecimal " 16134 "integer does not allow negative value", loc, "here"), loc); 16135 } 16136 switch(fmt.fmt) 16137 { 16138 case integer_format::hex: 16139 { 16140 oss << std::noshowbase 16141 << std::setw(static_cast<int>(fmt.width)) 16142 << std::setfill('0') 16143 << std::hex; 16144 if(fmt.uppercase) 16145 { 16146 oss << std::uppercase; 16147 } 16148 else 16149 { 16150 oss << std::nouppercase; 16151 } 16152 oss << i; 16153 retval = std::string("0x") + insert_spacer(oss.str()); 16154 break; 16155 } 16156 case integer_format::oct: 16157 { 16158 oss << std::setw(static_cast<int>(fmt.width)) << std::setfill('0') << std::oct << i; 16159 retval = std::string("0o") + insert_spacer(oss.str()); 16160 break; 16161 } 16162 case integer_format::bin: 16163 { 16164 integer_type x{i}; 16165 std::string tmp; 16166 std::size_t bits(0); 16167 while(x != 0) 16168 { 16169 if(fmt.spacer != 0) 16170 { 16171 if(bits != 0 && (bits % fmt.spacer) == 0) {tmp += '_';} 16172 } 16173 if(x % 2 == 1) { tmp += '1'; } else { tmp += '0'; } 16174 x >>= 1; 16175 bits += 1; 16176 } 16177 for(; bits < fmt.width; ++bits) 16178 { 16179 if(fmt.spacer != 0) 16180 { 16181 if(bits != 0 && (bits % fmt.spacer) == 0) {tmp += '_';} 16182 } 16183 tmp += '0'; 16184 } 16185 for(auto iter = tmp.rbegin(); iter != tmp.rend(); ++iter) 16186 { 16187 oss << *iter; 16188 } 16189 retval = std::string("0b") + oss.str(); 16190 break; 16191 } 16192 default: 16193 { 16194 throw serialization_error(format_error( 16195 "none of dec, hex, oct, bin: " + to_string(fmt.fmt), 16196 loc, "here"), loc); 16197 } 16198 } 16199 } 16200 return string_conv<string_type>(retval); 16201 } // }}} 16202 16203 string_type operator()(const floating_type f, const floating_format_info& fmt, const source_location&) // {{{ 16204 { 16205 using std::isnan; 16206 using std::isinf; 16207 using std::signbit; 16208 16209 std::ostringstream oss; 16210 this->set_locale(oss); 16211 16212 if(isnan(f)) 16213 { 16214 if(signbit(f)) 16215 { 16216 oss << '-'; 16217 } 16218 oss << "nan"; 16219 if(this->spec_.ext_num_suffix && ! fmt.suffix.empty()) 16220 { 16221 oss << '_'; 16222 oss << fmt.suffix; 16223 } 16224 return string_conv<string_type>(oss.str()); 16225 } 16226 16227 if(isinf(f)) 16228 { 16229 if(signbit(f)) 16230 { 16231 oss << '-'; 16232 } 16233 oss << "inf"; 16234 if(this->spec_.ext_num_suffix && ! fmt.suffix.empty()) 16235 { 16236 oss << '_'; 16237 oss << fmt.suffix; 16238 } 16239 return string_conv<string_type>(oss.str()); 16240 } 16241 16242 switch(fmt.fmt) 16243 { 16244 case floating_format::defaultfloat: 16245 { 16246 if(fmt.prec != 0) 16247 { 16248 oss << std::setprecision(static_cast<int>(fmt.prec)); 16249 } 16250 oss << f; 16251 // since defaultfloat may omit point, we need to add it 16252 std::string s = oss.str(); 16253 if (s.find('.') == std::string::npos && 16254 s.find('e') == std::string::npos && 16255 s.find('E') == std::string::npos ) 16256 { 16257 s += ".0"; 16258 } 16259 if(this->spec_.ext_num_suffix && ! fmt.suffix.empty()) 16260 { 16261 s += '_'; 16262 s += fmt.suffix; 16263 } 16264 return string_conv<string_type>(s); 16265 } 16266 case floating_format::fixed: 16267 { 16268 if(fmt.prec != 0) 16269 { 16270 oss << std::setprecision(static_cast<int>(fmt.prec)); 16271 } 16272 oss << std::fixed << f; 16273 if(this->spec_.ext_num_suffix && ! fmt.suffix.empty()) 16274 { 16275 oss << '_' << fmt.suffix; 16276 } 16277 return string_conv<string_type>(oss.str()); 16278 } 16279 case floating_format::scientific: 16280 { 16281 if(fmt.prec != 0) 16282 { 16283 oss << std::setprecision(static_cast<int>(fmt.prec)); 16284 } 16285 oss << std::scientific << f; 16286 if(this->spec_.ext_num_suffix && ! fmt.suffix.empty()) 16287 { 16288 oss << '_' << fmt.suffix; 16289 } 16290 return string_conv<string_type>(oss.str()); 16291 } 16292 case floating_format::hex: 16293 { 16294 if(this->spec_.ext_hex_float) 16295 { 16296 oss << std::hexfloat << f; 16297 // suffix is only for decimal numbers. 16298 return string_conv<string_type>(oss.str()); 16299 } 16300 else // no hex allowed. output with max precision. 16301 { 16302 oss << std::setprecision(std::numeric_limits<floating_type>::max_digits10) 16303 << std::scientific << f; 16304 // suffix is only for decimal numbers. 16305 return string_conv<string_type>(oss.str()); 16306 } 16307 } 16308 default: 16309 { 16310 if(this->spec_.ext_num_suffix && ! fmt.suffix.empty()) 16311 { 16312 oss << '_' << fmt.suffix; 16313 } 16314 return string_conv<string_type>(oss.str()); 16315 } 16316 } 16317 } // }}} 16318 16319 string_type operator()(string_type s, const string_format_info& fmt, const source_location& loc) // {{{ 16320 { 16321 string_type retval; 16322 switch(fmt.fmt) 16323 { 16324 case string_format::basic: 16325 { 16326 retval += char_type('"'); 16327 retval += this->escape_basic_string(s); 16328 retval += char_type('"'); 16329 return retval; 16330 } 16331 case string_format::literal: 16332 { 16333 if(std::find(s.begin(), s.end(), char_type('\n')) != s.end()) 16334 { 16335 throw serialization_error(format_error("toml::serializer: " 16336 "(non-multiline) literal string cannot have a newline", 16337 loc, "here"), loc); 16338 } 16339 retval += char_type('\''); 16340 retval += s; 16341 retval += char_type('\''); 16342 return retval; 16343 } 16344 case string_format::multiline_basic: 16345 { 16346 retval += string_conv<string_type>("\"\"\""); 16347 if(fmt.start_with_newline) 16348 { 16349 retval += char_type('\n'); 16350 } 16351 16352 retval += this->escape_ml_basic_string(s); 16353 16354 retval += string_conv<string_type>("\"\"\""); 16355 return retval; 16356 } 16357 case string_format::multiline_literal: 16358 { 16359 retval += string_conv<string_type>("'''"); 16360 if(fmt.start_with_newline) 16361 { 16362 retval += char_type('\n'); 16363 } 16364 retval += s; 16365 retval += string_conv<string_type>("'''"); 16366 return retval; 16367 } 16368 default: 16369 { 16370 throw serialization_error(format_error( 16371 "[error] toml::serializer::operator()(string): " 16372 "invalid string_format value", loc, "here"), loc); 16373 } 16374 } 16375 } // }}} 16376 16377 string_type operator()(const local_date_type& d, const local_date_format_info&, const source_location&) // {{{ 16378 { 16379 std::ostringstream oss; 16380 oss << d; 16381 return string_conv<string_type>(oss.str()); 16382 } // }}} 16383 16384 string_type operator()(const local_time_type& t, const local_time_format_info& fmt, const source_location&) // {{{ 16385 { 16386 return this->format_local_time(t, fmt.has_seconds, fmt.subsecond_precision); 16387 } // }}} 16388 16389 string_type operator()(const local_datetime_type& dt, const local_datetime_format_info& fmt, const source_location&) // {{{ 16390 { 16391 std::ostringstream oss; 16392 oss << dt.date; 16393 switch(fmt.delimiter) 16394 { 16395 case datetime_delimiter_kind::upper_T: { oss << 'T'; break; } 16396 case datetime_delimiter_kind::lower_t: { oss << 't'; break; } 16397 case datetime_delimiter_kind::space: { oss << ' '; break; } 16398 default: { oss << 'T'; break; } 16399 } 16400 return string_conv<string_type>(oss.str()) + 16401 this->format_local_time(dt.time, fmt.has_seconds, fmt.subsecond_precision); 16402 } // }}} 16403 16404 string_type operator()(const offset_datetime_type& odt, const offset_datetime_format_info& fmt, const source_location&) // {{{ 16405 { 16406 std::ostringstream oss; 16407 oss << odt.date; 16408 switch(fmt.delimiter) 16409 { 16410 case datetime_delimiter_kind::upper_T: { oss << 'T'; break; } 16411 case datetime_delimiter_kind::lower_t: { oss << 't'; break; } 16412 case datetime_delimiter_kind::space: { oss << ' '; break; } 16413 default: { oss << 'T'; break; } 16414 } 16415 oss << string_conv<std::string>(this->format_local_time(odt.time, fmt.has_seconds, fmt.subsecond_precision)); 16416 oss << odt.offset; 16417 return string_conv<string_type>(oss.str()); 16418 } // }}} 16419 16420 string_type operator()(const array_type& a, const array_format_info& fmt, const comment_type& com, const source_location& loc) // {{{ 16421 { 16422 array_format f = fmt.fmt; 16423 if(fmt.fmt == array_format::default_format) 16424 { 16425 // [[in.this.form]], you cannot add a comment to the array itself 16426 // (but you can add a comment to each table). 16427 // To keep comments, we need to avoid multiline array-of-tables 16428 // if array itself has a comment. 16429 if( ! this->keys_.empty() && 16430 ! a.empty() && 16431 com.empty() && 16432 std::all_of(a.begin(), a.end(), [](const value_type& e) {return e.is_table();})) 16433 { 16434 f = array_format::array_of_tables; 16435 } 16436 else 16437 { 16438 f = array_format::oneline; 16439 16440 // check if it becomes long 16441 std::size_t approx_len = 0; 16442 for(const auto& e : a) 16443 { 16444 // have a comment. cannot be inlined 16445 if( ! e.comments().empty()) 16446 { 16447 f = array_format::multiline; 16448 break; 16449 } 16450 // possibly long types ... 16451 if(e.is_array() || e.is_table() || e.is_offset_datetime() || e.is_local_datetime()) 16452 { 16453 f = array_format::multiline; 16454 break; 16455 } 16456 else if(e.is_boolean()) 16457 { 16458 approx_len += (*this)(e.as_boolean(), e.as_boolean_fmt(), e.location()).size(); 16459 } 16460 else if(e.is_integer()) 16461 { 16462 approx_len += (*this)(e.as_integer(), e.as_integer_fmt(), e.location()).size(); 16463 } 16464 else if(e.is_floating()) 16465 { 16466 approx_len += (*this)(e.as_floating(), e.as_floating_fmt(), e.location()).size(); 16467 } 16468 else if(e.is_string()) 16469 { 16470 if(e.as_string_fmt().fmt == string_format::multiline_basic || 16471 e.as_string_fmt().fmt == string_format::multiline_literal) 16472 { 16473 f = array_format::multiline; 16474 break; 16475 } 16476 approx_len += 2 + (*this)(e.as_string(), e.as_string_fmt(), e.location()).size(); 16477 } 16478 else if(e.is_local_date()) 16479 { 16480 approx_len += 10; // 1234-56-78 16481 } 16482 else if(e.is_local_time()) 16483 { 16484 approx_len += 15; // 12:34:56.789012 16485 } 16486 16487 if(approx_len > 60) // key, ` = `, `[...]` < 80 16488 { 16489 f = array_format::multiline; 16490 break; 16491 } 16492 approx_len += 2; // `, ` 16493 } 16494 } 16495 } 16496 if(this->force_inline_ && f == array_format::array_of_tables) 16497 { 16498 f = array_format::multiline; 16499 } 16500 if(a.empty() && f == array_format::array_of_tables) 16501 { 16502 f = array_format::oneline; 16503 } 16504 16505 // -------------------------------------------------------------------- 16506 16507 if(f == array_format::array_of_tables) 16508 { 16509 if(this->keys_.empty()) 16510 { 16511 throw serialization_error("array of table must have its key. " 16512 "use format(key, v)", loc); 16513 } 16514 string_type retval; 16515 for(const auto& e : a) 16516 { 16517 assert(e.is_table()); 16518 16519 this->current_indent_ += e.as_table_fmt().name_indent; 16520 retval += this->format_comments(e.comments(), e.as_table_fmt().indent_type); 16521 retval += this->format_indent(e.as_table_fmt().indent_type); 16522 this->current_indent_ -= e.as_table_fmt().name_indent; 16523 16524 retval += string_conv<string_type>("[["); 16525 retval += this->format_keys(this->keys_).value(); 16526 retval += string_conv<string_type>("]]\n"); 16527 16528 retval += this->format_ml_table(e.as_table(), e.as_table_fmt()); 16529 } 16530 return retval; 16531 } 16532 else if(f == array_format::oneline) 16533 { 16534 // ignore comments. we cannot emit comments 16535 string_type retval; 16536 retval += char_type('['); 16537 for(const auto& e : a) 16538 { 16539 this->force_inline_ = true; 16540 retval += (*this)(e); 16541 retval += string_conv<string_type>(", "); 16542 } 16543 if( ! a.empty()) 16544 { 16545 retval.pop_back(); // ` ` 16546 retval.pop_back(); // `,` 16547 } 16548 retval += char_type(']'); 16549 this->force_inline_ = false; 16550 return retval; 16551 } 16552 else 16553 { 16554 assert(f == array_format::multiline); 16555 16556 string_type retval; 16557 retval += string_conv<string_type>("[\n"); 16558 16559 for(const auto& e : a) 16560 { 16561 this->current_indent_ += fmt.body_indent; 16562 retval += this->format_comments(e.comments(), fmt.indent_type); 16563 retval += this->format_indent(fmt.indent_type); 16564 this->current_indent_ -= fmt.body_indent; 16565 16566 this->force_inline_ = true; 16567 retval += (*this)(e); 16568 retval += string_conv<string_type>(",\n"); 16569 } 16570 this->force_inline_ = false; 16571 16572 this->current_indent_ += fmt.closing_indent; 16573 retval += this->format_indent(fmt.indent_type); 16574 this->current_indent_ -= fmt.closing_indent; 16575 16576 retval += char_type(']'); 16577 return retval; 16578 } 16579 } // }}} 16580 16581 string_type operator()(const table_type& t, const table_format_info& fmt, const comment_type& com, const source_location& loc) // {{{ 16582 { 16583 if(this->force_inline_) 16584 { 16585 if(fmt.fmt == table_format::multiline_oneline) 16586 { 16587 return this->format_ml_inline_table(t, fmt); 16588 } 16589 else 16590 { 16591 return this->format_inline_table(t, fmt); 16592 } 16593 } 16594 else 16595 { 16596 if(fmt.fmt == table_format::multiline) 16597 { 16598 string_type retval; 16599 // comment is emitted inside format_ml_table 16600 if(auto k = this->format_keys(this->keys_)) 16601 { 16602 this->current_indent_ += fmt.name_indent; 16603 retval += this->format_comments(com, fmt.indent_type); 16604 retval += this->format_indent(fmt.indent_type); 16605 this->current_indent_ -= fmt.name_indent; 16606 retval += char_type('['); 16607 retval += k.value(); 16608 retval += string_conv<string_type>("]\n"); 16609 } 16610 // otherwise, its the root. 16611 16612 retval += this->format_ml_table(t, fmt); 16613 return retval; 16614 } 16615 else if(fmt.fmt == table_format::oneline) 16616 { 16617 return this->format_inline_table(t, fmt); 16618 } 16619 else if(fmt.fmt == table_format::multiline_oneline) 16620 { 16621 return this->format_ml_inline_table(t, fmt); 16622 } 16623 else if(fmt.fmt == table_format::dotted) 16624 { 16625 std::vector<string_type> keys; 16626 if(this->keys_.empty()) 16627 { 16628 throw serialization_error(format_error("toml::serializer: " 16629 "dotted table must have its key. use format(key, v)", 16630 loc, "here"), loc); 16631 } 16632 keys.push_back(this->keys_.back()); 16633 16634 const auto retval = this->format_dotted_table(t, fmt, loc, keys); 16635 keys.pop_back(); 16636 return retval; 16637 } 16638 else 16639 { 16640 assert(fmt.fmt == table_format::implicit); 16641 16642 string_type retval; 16643 for(const auto& kv : t) 16644 { 16645 const auto& k = kv.first; 16646 const auto& v = kv.second; 16647 16648 if( ! v.is_table() && ! v.is_array_of_tables()) 16649 { 16650 throw serialization_error(format_error("toml::serializer: " 16651 "an implicit table cannot have non-table value.", 16652 v.location(), "here"), v.location()); 16653 } 16654 if(v.is_table()) 16655 { 16656 if(v.as_table_fmt().fmt != table_format::multiline && 16657 v.as_table_fmt().fmt != table_format::implicit) 16658 { 16659 throw serialization_error(format_error("toml::serializer: " 16660 "an implicit table cannot have non-multiline table", 16661 v.location(), "here"), v.location()); 16662 } 16663 } 16664 else 16665 { 16666 assert(v.is_array()); 16667 for(const auto& e : v.as_array()) 16668 { 16669 if(e.as_table_fmt().fmt != table_format::multiline && 16670 v.as_table_fmt().fmt != table_format::implicit) 16671 { 16672 throw serialization_error(format_error("toml::serializer: " 16673 "an implicit table cannot have non-multiline table", 16674 e.location(), "here"), e.location()); 16675 } 16676 } 16677 } 16678 16679 keys_.push_back(k); 16680 retval += (*this)(v); 16681 keys_.pop_back(); 16682 } 16683 return retval; 16684 } 16685 } 16686 } // }}} 16687 16688 private: 16689 16690 string_type escape_basic_string(const string_type& s) const // {{{ 16691 { 16692 string_type retval; 16693 for(const char_type c : s) 16694 { 16695 switch(c) 16696 { 16697 case char_type('\\'): {retval += string_conv<string_type>("\\\\"); break;} 16698 case char_type('\"'): {retval += string_conv<string_type>("\\\""); break;} 16699 case char_type('\b'): {retval += string_conv<string_type>("\\b" ); break;} 16700 case char_type('\t'): {retval += string_conv<string_type>("\\t" ); break;} 16701 case char_type('\f'): {retval += string_conv<string_type>("\\f" ); break;} 16702 case char_type('\n'): {retval += string_conv<string_type>("\\n" ); break;} 16703 case char_type('\r'): {retval += string_conv<string_type>("\\r" ); break;} 16704 default : 16705 { 16706 if(c == char_type(0x1B) && spec_.v1_1_0_add_escape_sequence_e) 16707 { 16708 retval += string_conv<string_type>("\\e"); 16709 } 16710 else if((char_type(0x00) <= c && c <= char_type(0x08)) || 16711 (char_type(0x0A) <= c && c <= char_type(0x1F)) || 16712 c == char_type(0x7F)) 16713 { 16714 if(spec_.v1_1_0_add_escape_sequence_x) 16715 { 16716 retval += string_conv<string_type>("\\x"); 16717 } 16718 else 16719 { 16720 retval += string_conv<string_type>("\\u00"); 16721 } 16722 const auto c1 = c / 16; 16723 const auto c2 = c % 16; 16724 retval += static_cast<char_type>('0' + c1); 16725 if(c2 < 10) 16726 { 16727 retval += static_cast<char_type>('0' + c2); 16728 } 16729 else // 10 <= c2 16730 { 16731 retval += static_cast<char_type>('A' + (c2 - 10)); 16732 } 16733 } 16734 else 16735 { 16736 retval += c; 16737 } 16738 } 16739 } 16740 } 16741 return retval; 16742 } // }}} 16743 16744 string_type escape_ml_basic_string(const string_type& s) // {{{ 16745 { 16746 string_type retval; 16747 for(const char_type c : s) 16748 { 16749 switch(c) 16750 { 16751 case char_type('\\'): {retval += string_conv<string_type>("\\\\"); break;} 16752 case char_type('\b'): {retval += string_conv<string_type>("\\b" ); break;} 16753 case char_type('\t'): {retval += string_conv<string_type>("\\t" ); break;} 16754 case char_type('\f'): {retval += string_conv<string_type>("\\f" ); break;} 16755 case char_type('\n'): {retval += string_conv<string_type>("\n" ); break;} 16756 case char_type('\r'): {retval += string_conv<string_type>("\\r" ); break;} 16757 default : 16758 { 16759 if(c == char_type(0x1B) && spec_.v1_1_0_add_escape_sequence_e) 16760 { 16761 retval += string_conv<string_type>("\\e"); 16762 } 16763 else if((char_type(0x00) <= c && c <= char_type(0x08)) || 16764 (char_type(0x0A) <= c && c <= char_type(0x1F)) || 16765 c == char_type(0x7F)) 16766 { 16767 if(spec_.v1_1_0_add_escape_sequence_x) 16768 { 16769 retval += string_conv<string_type>("\\x"); 16770 } 16771 else 16772 { 16773 retval += string_conv<string_type>("\\u00"); 16774 } 16775 const auto c1 = c / 16; 16776 const auto c2 = c % 16; 16777 retval += static_cast<char_type>('0' + c1); 16778 if(c2 < 10) 16779 { 16780 retval += static_cast<char_type>('0' + c2); 16781 } 16782 else // 10 <= c2 16783 { 16784 retval += static_cast<char_type>('A' + (c2 - 10)); 16785 } 16786 } 16787 else 16788 { 16789 retval += c; 16790 } 16791 } 16792 } 16793 } 16794 // Only 1 or 2 consecutive `"`s are allowed in multiline basic string. 16795 // 3 consecutive `"`s are considered as a closing delimiter. 16796 // We need to check if there are 3 or more consecutive `"`s and insert 16797 // backslash to break them down into several short `"`s like the `str6` 16798 // in the following example. 16799 // ```toml 16800 // str4 = """Here are two quotation marks: "". Simple enough.""" 16801 // # str5 = """Here are three quotation marks: """.""" # INVALID 16802 // str5 = """Here are three quotation marks: ""\".""" 16803 // str6 = """Here are fifteen quotation marks: ""\"""\"""\"""\"""\".""" 16804 // ``` 16805 auto found_3_quotes = retval.find(string_conv<string_type>("\"\"\"")); 16806 while(found_3_quotes != string_type::npos) 16807 { 16808 retval.replace(found_3_quotes, 3, string_conv<string_type>("\"\"\\\"")); 16809 found_3_quotes = retval.find(string_conv<string_type>("\"\"\"")); 16810 } 16811 return retval; 16812 } // }}} 16813 16814 string_type format_local_time(const local_time_type& t, const bool has_seconds, const std::size_t subsec_prec) // {{{ 16815 { 16816 std::ostringstream oss; 16817 oss << std::setfill('0') << std::setw(2) << static_cast<int>(t.hour); 16818 oss << ':'; 16819 oss << std::setfill('0') << std::setw(2) << static_cast<int>(t.minute); 16820 if(has_seconds) 16821 { 16822 oss << ':'; 16823 oss << std::setfill('0') << std::setw(2) << static_cast<int>(t.second); 16824 if(subsec_prec != 0) 16825 { 16826 std::ostringstream subsec; 16827 subsec << std::setfill('0') << std::setw(3) << static_cast<int>(t.millisecond); 16828 subsec << std::setfill('0') << std::setw(3) << static_cast<int>(t.microsecond); 16829 subsec << std::setfill('0') << std::setw(3) << static_cast<int>(t.nanosecond); 16830 std::string subsec_str = subsec.str(); 16831 oss << '.' << subsec_str.substr(0, subsec_prec); 16832 } 16833 } 16834 return string_conv<string_type>(oss.str()); 16835 } // }}} 16836 16837 string_type format_ml_table(const table_type& t, const table_format_info& fmt) // {{{ 16838 { 16839 const auto format_later = [](const value_type& v) -> bool { 16840 16841 const bool is_ml_table = v.is_table() && 16842 v.as_table_fmt().fmt != table_format::oneline && 16843 v.as_table_fmt().fmt != table_format::multiline_oneline && 16844 v.as_table_fmt().fmt != table_format::dotted ; 16845 16846 const bool is_ml_array_table = v.is_array_of_tables() && 16847 v.as_array_fmt().fmt != array_format::oneline && 16848 v.as_array_fmt().fmt != array_format::multiline; 16849 16850 return is_ml_table || is_ml_array_table; 16851 }; 16852 16853 string_type retval; 16854 this->current_indent_ += fmt.body_indent; 16855 for(const auto& kv : t) 16856 { 16857 const auto& key = kv.first; 16858 const auto& val = kv.second; 16859 if(format_later(val)) 16860 { 16861 continue; 16862 } 16863 this->keys_.push_back(key); 16864 16865 retval += format_comments(val.comments(), fmt.indent_type); 16866 retval += format_indent(fmt.indent_type); 16867 if(val.is_table() && val.as_table_fmt().fmt == table_format::dotted) 16868 { 16869 retval += (*this)(val); 16870 } 16871 else 16872 { 16873 retval += format_key(key); 16874 retval += string_conv<string_type>(" = "); 16875 retval += (*this)(val); 16876 retval += char_type('\n'); 16877 } 16878 this->keys_.pop_back(); 16879 } 16880 this->current_indent_ -= fmt.body_indent; 16881 16882 if( ! retval.empty()) 16883 { 16884 retval += char_type('\n'); // for readability, add empty line between tables 16885 } 16886 for(const auto& kv : t) 16887 { 16888 if( ! format_later(kv.second)) 16889 { 16890 continue; 16891 } 16892 // must be a [multiline.table] or [[multiline.array.of.tables]]. 16893 // comments will be generated inside it. 16894 this->keys_.push_back(kv.first); 16895 retval += (*this)(kv.second); 16896 this->keys_.pop_back(); 16897 } 16898 return retval; 16899 } // }}} 16900 16901 string_type format_inline_table(const table_type& t, const table_format_info&) // {{{ 16902 { 16903 // comments are ignored because we cannot write without newline 16904 string_type retval; 16905 retval += char_type('{'); 16906 for(const auto& kv : t) 16907 { 16908 this->force_inline_ = true; 16909 retval += this->format_key(kv.first); 16910 retval += string_conv<string_type>(" = "); 16911 retval += (*this)(kv.second); 16912 retval += string_conv<string_type>(", "); 16913 } 16914 if( ! t.empty()) 16915 { 16916 retval.pop_back(); // ' ' 16917 retval.pop_back(); // ',' 16918 } 16919 retval += char_type('}'); 16920 this->force_inline_ = false; 16921 return retval; 16922 } // }}} 16923 16924 string_type format_ml_inline_table(const table_type& t, const table_format_info& fmt) // {{{ 16925 { 16926 string_type retval; 16927 retval += string_conv<string_type>("{\n"); 16928 this->current_indent_ += fmt.body_indent; 16929 for(const auto& kv : t) 16930 { 16931 this->force_inline_ = true; 16932 retval += format_comments(kv.second.comments(), fmt.indent_type); 16933 retval += format_indent(fmt.indent_type); 16934 retval += kv.first; 16935 retval += string_conv<string_type>(" = "); 16936 16937 this->force_inline_ = true; 16938 retval += (*this)(kv.second); 16939 16940 retval += string_conv<string_type>(",\n"); 16941 } 16942 if( ! t.empty()) 16943 { 16944 retval.pop_back(); // '\n' 16945 retval.pop_back(); // ',' 16946 } 16947 this->current_indent_ -= fmt.body_indent; 16948 this->force_inline_ = false; 16949 16950 this->current_indent_ += fmt.closing_indent; 16951 retval += format_indent(fmt.indent_type); 16952 this->current_indent_ -= fmt.closing_indent; 16953 16954 retval += char_type('}'); 16955 return retval; 16956 } // }}} 16957 16958 string_type format_dotted_table(const table_type& t, const table_format_info& fmt, // {{{ 16959 const source_location&, std::vector<string_type>& keys) 16960 { 16961 // lets say we have: `{"a": {"b": {"c": {"d": "foo", "e": "bar"} } }` 16962 // and `a` and `b` are `dotted`. 16963 // 16964 // - in case if `c` is `oneline`: 16965 // ```toml 16966 // a.b.c = {d = "foo", e = "bar"} 16967 // ``` 16968 // 16969 // - in case if and `c` is `dotted`: 16970 // ```toml 16971 // a.b.c.d = "foo" 16972 // a.b.c.e = "bar" 16973 // ``` 16974 16975 string_type retval; 16976 16977 for(const auto& kv : t) 16978 { 16979 const auto& key = kv.first; 16980 const auto& val = kv.second; 16981 16982 keys.push_back(key); 16983 16984 // format recursive dotted table? 16985 if (val.is_table() && 16986 val.as_table_fmt().fmt != table_format::oneline && 16987 val.as_table_fmt().fmt != table_format::multiline_oneline) 16988 { 16989 retval += this->format_dotted_table(val.as_table(), val.as_table_fmt(), val.location(), keys); 16990 } 16991 else // non-table or inline tables. format normally 16992 { 16993 retval += format_comments(val.comments(), fmt.indent_type); 16994 retval += format_indent(fmt.indent_type); 16995 retval += format_keys(keys).value(); 16996 retval += string_conv<string_type>(" = "); 16997 this->force_inline_ = true; // sub-table must be inlined 16998 retval += (*this)(val); 16999 retval += char_type('\n'); 17000 this->force_inline_ = false; 17001 } 17002 keys.pop_back(); 17003 } 17004 return retval; 17005 } // }}} 17006 17007 string_type format_key(const key_type& key) // {{{ 17008 { 17009 if(key.empty()) 17010 { 17011 return string_conv<string_type>("\"\""); 17012 } 17013 17014 // check the key can be a bare (unquoted) key 17015 auto loc = detail::make_temporary_location(string_conv<std::string>(key)); 17016 auto reg = detail::syntax::unquoted_key(this->spec_).scan(loc); 17017 if(reg.is_ok() && loc.eof()) 17018 { 17019 return key; 17020 } 17021 17022 //if it includes special characters, then format it in a "quoted" key. 17023 string_type formatted = string_conv<string_type>("\""); 17024 for(const char_type c : key) 17025 { 17026 switch(c) 17027 { 17028 case char_type('\\'): {formatted += string_conv<string_type>("\\\\"); break;} 17029 case char_type('\"'): {formatted += string_conv<string_type>("\\\""); break;} 17030 case char_type('\b'): {formatted += string_conv<string_type>("\\b" ); break;} 17031 case char_type('\t'): {formatted += string_conv<string_type>("\\t" ); break;} 17032 case char_type('\f'): {formatted += string_conv<string_type>("\\f" ); break;} 17033 case char_type('\n'): {formatted += string_conv<string_type>("\\n" ); break;} 17034 case char_type('\r'): {formatted += string_conv<string_type>("\\r" ); break;} 17035 default : 17036 { 17037 // ASCII ctrl char 17038 if( (char_type(0x00) <= c && c <= char_type(0x08)) || 17039 (char_type(0x0A) <= c && c <= char_type(0x1F)) || 17040 c == char_type(0x7F)) 17041 { 17042 if(spec_.v1_1_0_add_escape_sequence_x) 17043 { 17044 formatted += string_conv<string_type>("\\x"); 17045 } 17046 else 17047 { 17048 formatted += string_conv<string_type>("\\u00"); 17049 } 17050 const auto c1 = c / 16; 17051 const auto c2 = c % 16; 17052 formatted += static_cast<char_type>('0' + c1); 17053 if(c2 < 10) 17054 { 17055 formatted += static_cast<char_type>('0' + c2); 17056 } 17057 else // 10 <= c2 17058 { 17059 formatted += static_cast<char_type>('A' + (c2 - 10)); 17060 } 17061 } 17062 else 17063 { 17064 formatted += c; 17065 } 17066 break; 17067 } 17068 } 17069 } 17070 formatted += string_conv<string_type>("\""); 17071 return formatted; 17072 } // }}} 17073 cxx::optional<string_type> format_keys(const std::vector<key_type>& keys) // {{{ 17074 { 17075 if(keys.empty()) 17076 { 17077 return cxx::make_nullopt(); 17078 } 17079 17080 string_type formatted; 17081 for(const auto& ky : keys) 17082 { 17083 formatted += format_key(ky); 17084 formatted += char_type('.'); 17085 } 17086 formatted.pop_back(); // remove the last dot '.' 17087 return formatted; 17088 } // }}} 17089 17090 string_type format_comments(const discard_comments&, const indent_char) const // {{{ 17091 { 17092 return string_conv<string_type>(""); 17093 } // }}} 17094 string_type format_comments(const preserve_comments& comments, const indent_char indent_type) const // {{{ 17095 { 17096 string_type retval; 17097 for(const auto& c : comments) 17098 { 17099 if(c.empty()) {continue;} 17100 retval += format_indent(indent_type); 17101 if(c.front() != '#') {retval += char_type('#');} 17102 retval += string_conv<string_type>(c); 17103 if(c.back() != '\n') {retval += char_type('\n');} 17104 } 17105 return retval; 17106 } // }}} 17107 17108 string_type format_indent(const indent_char indent_type) const // {{{ 17109 { 17110 const auto indent = static_cast<std::size_t>((std::max)(0, this->current_indent_)); 17111 if(indent_type == indent_char::space) 17112 { 17113 return string_conv<string_type>(make_string(indent, ' ')); 17114 } 17115 else if(indent_type == indent_char::tab) 17116 { 17117 return string_conv<string_type>(make_string(indent, '\t')); 17118 } 17119 else 17120 { 17121 return string_type{}; 17122 } 17123 } // }}} 17124 17125 std::locale set_locale(std::ostream& os) const 17126 { 17127 return os.imbue(std::locale::classic()); 17128 } 17129 17130 private: 17131 17132 spec spec_; 17133 bool force_inline_; // table inside an array without fmt specification 17134 std::int32_t current_indent_; 17135 std::vector<key_type> keys_; 17136 }; 17137 } // detail 17138 17139 template<typename TC> 17140 typename basic_value<TC>::string_type 17141 format(const basic_value<TC>& v, const spec s = spec::default_version()) 17142 { 17143 detail::serializer<TC> ser(s); 17144 return ser(v); 17145 } 17146 template<typename TC> 17147 typename basic_value<TC>::string_type 17148 format(const typename basic_value<TC>::key_type& k, 17149 const basic_value<TC>& v, 17150 const spec s = spec::default_version()) 17151 { 17152 detail::serializer<TC> ser(s); 17153 return ser(k, v); 17154 } 17155 template<typename TC> 17156 typename basic_value<TC>::string_type 17157 format(const std::vector<typename basic_value<TC>::key_type>& ks, 17158 const basic_value<TC>& v, 17159 const spec s = spec::default_version()) 17160 { 17161 detail::serializer<TC> ser(s); 17162 return ser(ks, v); 17163 } 17164 17165 template<typename TC> 17166 std::ostream& operator<<(std::ostream& os, const basic_value<TC>& v) 17167 { 17168 os << format(v); 17169 return os; 17170 } 17171 17172 } // toml 17173 17174 #if defined(TOML11_COMPILE_SOURCES) 17175 namespace toml 17176 { 17177 struct type_config; 17178 struct ordered_type_config; 17179 17180 extern template typename basic_value<type_config>::string_type 17181 format<type_config>(const basic_value<type_config>&, const spec); 17182 17183 extern template typename basic_value<type_config>::string_type 17184 format<type_config>(const typename basic_value<type_config>::key_type& k, 17185 const basic_value<type_config>& v, const spec); 17186 17187 extern template typename basic_value<type_config>::string_type 17188 format<type_config>(const std::vector<typename basic_value<type_config>::key_type>& ks, 17189 const basic_value<type_config>& v, const spec s); 17190 17191 extern template typename basic_value<type_config>::string_type 17192 format<ordered_type_config>(const basic_value<ordered_type_config>&, const spec); 17193 17194 extern template typename basic_value<type_config>::string_type 17195 format<ordered_type_config>(const typename basic_value<ordered_type_config>::key_type& k, 17196 const basic_value<ordered_type_config>& v, const spec); 17197 17198 extern template typename basic_value<type_config>::string_type 17199 format<ordered_type_config>(const std::vector<typename basic_value<ordered_type_config>::key_type>& ks, 17200 const basic_value<ordered_type_config>& v, const spec s); 17201 17202 namespace detail 17203 { 17204 extern template class serializer<::toml::type_config>; 17205 extern template class serializer<::toml::ordered_type_config>; 17206 } // detail 17207 } // toml 17208 #endif // TOML11_COMPILE_SOURCES 17209 17210 17211 #endif // TOML11_SERIALIZER_HPP 17212 #ifndef TOML11_TOML_HPP 17213 #define TOML11_TOML_HPP 17214 17215 // The MIT License (MIT) 17216 // 17217 // Copyright (c) 2017-now Toru Niina 17218 // 17219 // Permission is hereby granted, free of charge, to any person obtaining a copy 17220 // of this software and associated documentation files (the "Software"), to deal 17221 // in the Software without restriction, including without limitation the rights 17222 // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 17223 // copies of the Software, and to permit persons to whom the Software is 17224 // furnished to do so, subject to the following conditions: 17225 // 17226 // The above copyright notice and this permission notice shall be included in 17227 // all copies or substantial portions of the Software. 17228 // 17229 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17230 // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17231 // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17232 // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17233 // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 17234 // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 17235 // THE SOFTWARE. 17236 17237 // IWYU pragma: begin_exports 17238 // IWYU pragma: end_exports 17239 17240 #endif// TOML11_TOML_HPP