/ desktop / includes / toml.hpp
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(&ltime));
 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