diff --git a/3party/toml11/toml.hpp b/3party/toml11/toml.hpp index e975f4e..afdaf5b 100644 --- a/3party/toml11/toml.hpp +++ b/3party/toml11/toml.hpp @@ -1,38 +1,17241 @@ -/* - * The MIT License (MIT) - * - * Copyright (c) 2017 Toru Niina - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. +#ifndef TOML11_VERSION_HPP +#define TOML11_VERSION_HPP + +#define TOML11_VERSION_MAJOR 4 +#define TOML11_VERSION_MINOR 2 +#define TOML11_VERSION_PATCH 0 + +#ifndef __cplusplus +# error "__cplusplus is not defined" +#endif + +// Since MSVC does not define `__cplusplus` correctly unless you pass +// `/Zc:__cplusplus` when compiling, the workaround macros are added. +// +// The value of `__cplusplus` macro is defined in the C++ standard spec, but +// MSVC ignores the value, maybe because of backward compatibility. Instead, +// MSVC defines _MSVC_LANG that has the same value as __cplusplus defined in +// the C++ standard. So we check if _MSVC_LANG is defined before using `__cplusplus`. +// +// FYI: https://docs.microsoft.com/en-us/cpp/build/reference/zc-cplusplus?view=msvc-170 +// https://docs.microsoft.com/en-us/cpp/preprocessor/predefined-macros?view=msvc-170 +// + +#if defined(_MSVC_LANG) && defined(_MSC_VER) && 190024210 <= _MSC_FULL_VER +# define TOML11_CPLUSPLUS_STANDARD_VERSION _MSVC_LANG +#else +# define TOML11_CPLUSPLUS_STANDARD_VERSION __cplusplus +#endif + +#if TOML11_CPLUSPLUS_STANDARD_VERSION < 201103L +# error "toml11 requires C++11 or later." +#endif + +#if ! defined(__has_include) +# define __has_include(x) 0 +#endif + +#if ! defined(__has_cpp_attribute) +# define __has_cpp_attribute(x) 0 +#endif + +#if ! defined(__has_builtin) +# define __has_builtin(x) 0 +#endif + +// hard to remember + +#ifndef TOML11_CXX14_VALUE +#define TOML11_CXX14_VALUE 201402L +#endif//TOML11_CXX14_VALUE + +#ifndef TOML11_CXX17_VALUE +#define TOML11_CXX17_VALUE 201703L +#endif//TOML11_CXX17_VALUE + +#ifndef TOML11_CXX20_VALUE +#define TOML11_CXX20_VALUE 202002L +#endif//TOML11_CXX20_VALUE + +#if defined(__cpp_char8_t) +# if __cpp_char8_t >= 201811L +# define TOML11_HAS_CHAR8_T 1 +# endif +#endif + +#if TOML11_CPLUSPLUS_STANDARD_VERSION >= TOML11_CXX17_VALUE +# if __has_include() +# define TOML11_HAS_STRING_VIEW 1 +# endif +#endif + +#ifndef TOML11_DISABLE_STD_FILESYSTEM +# if TOML11_CPLUSPLUS_STANDARD_VERSION >= TOML11_CXX17_VALUE +# if __has_include() +# define TOML11_HAS_FILESYSTEM 1 +# endif +# endif +#endif + +#if TOML11_CPLUSPLUS_STANDARD_VERSION >= TOML11_CXX17_VALUE +# if __has_include() +# define TOML11_HAS_OPTIONAL 1 +# endif +#endif + +#if defined(TOML11_COMPILE_SOURCES) +# define TOML11_INLINE +#else +# define TOML11_INLINE inline +#endif + +namespace toml +{ + +inline const char* license_notice() noexcept +{ + return R"(The MIT License (MIT) + +Copyright (c) 2017-now Toru Niina + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE.)"; +} + +} // toml +#endif // TOML11_VERSION_HPP +#ifndef TOML11_FORMAT_HPP +#define TOML11_FORMAT_HPP + +#ifndef TOML11_FORMAT_FWD_HPP +#define TOML11_FORMAT_FWD_HPP + +#include +#include +#include + +#include +#include + +namespace toml +{ + +// toml types with serialization info + +enum class indent_char : std::uint8_t +{ + space, // use space + tab, // use tab + none // no indent +}; + +std::ostream& operator<<(std::ostream& os, const indent_char& c); +std::string to_string(const indent_char c); + +// ---------------------------------------------------------------------------- +// boolean + +struct boolean_format_info +{ + // nothing, for now +}; + +inline bool operator==(const boolean_format_info&, const boolean_format_info&) noexcept +{ + return true; +} +inline bool operator!=(const boolean_format_info&, const boolean_format_info&) noexcept +{ + return false; +} + +// ---------------------------------------------------------------------------- +// integer + +enum class integer_format : std::uint8_t +{ + dec = 0, + bin = 1, + oct = 2, + hex = 3, +}; + +std::ostream& operator<<(std::ostream& os, const integer_format f); +std::string to_string(const integer_format); + +struct integer_format_info +{ + integer_format fmt = integer_format::dec; + bool uppercase = true; // hex with uppercase + std::size_t width = 0; // minimal width (may exceed) + std::size_t spacer = 0; // position of `_` (if 0, no spacer) + std::string suffix = ""; // _suffix (library extension) +}; + +bool operator==(const integer_format_info&, const integer_format_info&) noexcept; +bool operator!=(const integer_format_info&, const integer_format_info&) noexcept; + +// ---------------------------------------------------------------------------- +// floating + +enum class floating_format : std::uint8_t +{ + defaultfloat = 0, + fixed = 1, // does not include exponential part + scientific = 2, // always include exponential part + hex = 3 // hexfloat extension +}; + +std::ostream& operator<<(std::ostream& os, const floating_format f); +std::string to_string(const floating_format); + +struct floating_format_info +{ + floating_format fmt = floating_format::defaultfloat; + std::size_t prec = 0; // precision (if 0, use the default) + std::string suffix = ""; // 1.0e+2_suffix (library extension) +}; + +bool operator==(const floating_format_info&, const floating_format_info&) noexcept; +bool operator!=(const floating_format_info&, const floating_format_info&) noexcept; + +// ---------------------------------------------------------------------------- +// string + +enum class string_format : std::uint8_t +{ + basic = 0, + literal = 1, + multiline_basic = 2, + multiline_literal = 3 +}; + +std::ostream& operator<<(std::ostream& os, const string_format f); +std::string to_string(const string_format); + +struct string_format_info +{ + string_format fmt = string_format::basic; + bool start_with_newline = false; +}; + +bool operator==(const string_format_info&, const string_format_info&) noexcept; +bool operator!=(const string_format_info&, const string_format_info&) noexcept; + +// ---------------------------------------------------------------------------- +// datetime + +enum class datetime_delimiter_kind : std::uint8_t +{ + upper_T = 0, + lower_t = 1, + space = 2, +}; +std::ostream& operator<<(std::ostream& os, const datetime_delimiter_kind d); +std::string to_string(const datetime_delimiter_kind); + +struct offset_datetime_format_info +{ + datetime_delimiter_kind delimiter = datetime_delimiter_kind::upper_T; + bool has_seconds = true; + std::size_t subsecond_precision = 6; // [us] +}; + +bool operator==(const offset_datetime_format_info&, const offset_datetime_format_info&) noexcept; +bool operator!=(const offset_datetime_format_info&, const offset_datetime_format_info&) noexcept; + +struct local_datetime_format_info +{ + datetime_delimiter_kind delimiter = datetime_delimiter_kind::upper_T; + bool has_seconds = true; + std::size_t subsecond_precision = 6; // [us] +}; + +bool operator==(const local_datetime_format_info&, const local_datetime_format_info&) noexcept; +bool operator!=(const local_datetime_format_info&, const local_datetime_format_info&) noexcept; + +struct local_date_format_info +{ + // nothing, for now +}; + +bool operator==(const local_date_format_info&, const local_date_format_info&) noexcept; +bool operator!=(const local_date_format_info&, const local_date_format_info&) noexcept; + +struct local_time_format_info +{ + bool has_seconds = true; + std::size_t subsecond_precision = 6; // [us] +}; + +bool operator==(const local_time_format_info&, const local_time_format_info&) noexcept; +bool operator!=(const local_time_format_info&, const local_time_format_info&) noexcept; + +// ---------------------------------------------------------------------------- +// array + +enum class array_format : std::uint8_t +{ + default_format = 0, + oneline = 1, + multiline = 2, + array_of_tables = 3 // [[format.in.this.way]] +}; + +std::ostream& operator<<(std::ostream& os, const array_format f); +std::string to_string(const array_format); + +struct array_format_info +{ + array_format fmt = array_format::default_format; + indent_char indent_type = indent_char::space; + std::int32_t body_indent = 4; // indent in case of multiline + std::int32_t closing_indent = 0; // indent of `]` +}; + +bool operator==(const array_format_info&, const array_format_info&) noexcept; +bool operator!=(const array_format_info&, const array_format_info&) noexcept; + +// ---------------------------------------------------------------------------- +// table + +enum class table_format : std::uint8_t +{ + multiline = 0, // [foo] \n bar = "baz" + oneline = 1, // foo = {bar = "baz"} + dotted = 2, // foo.bar = "baz" + multiline_oneline = 3, // foo = { \n bar = "baz" \n } + implicit = 4 // [x] defined by [x.y.z]. skip in serializer. +}; + +std::ostream& operator<<(std::ostream& os, const table_format f); +std::string to_string(const table_format); + +struct table_format_info +{ + table_format fmt = table_format::multiline; + indent_char indent_type = indent_char::space; + std::int32_t body_indent = 0; // indent of values + std::int32_t name_indent = 0; // indent of [table] + std::int32_t closing_indent = 0; // in case of {inline-table} +}; + +bool operator==(const table_format_info&, const table_format_info&) noexcept; +bool operator!=(const table_format_info&, const table_format_info&) noexcept; + +// ---------------------------------------------------------------------------- +// wrapper + +namespace detail +{ +template +struct value_with_format +{ + using value_type = T; + using format_type = F; + + value_with_format() = default; + ~value_with_format() = default; + value_with_format(const value_with_format&) = default; + value_with_format(value_with_format&&) = default; + value_with_format& operator=(const value_with_format&) = default; + value_with_format& operator=(value_with_format&&) = default; + + value_with_format(value_type v, format_type f) + : value{std::move(v)}, format{std::move(f)} + {} + + template + value_with_format(value_with_format other) + : value{std::move(other.value)}, format{std::move(other.format)} + {} + + value_type value; + format_type format; +}; +} // detail + +} // namespace toml +#endif // TOML11_FORMAT_FWD_HPP + +#if ! defined(TOML11_COMPILE_SOURCES) +#ifndef TOML11_FORMAT_IMPL_HPP +#define TOML11_FORMAT_IMPL_HPP + + +#include +#include + +namespace toml +{ + +// toml types with serialization info + +TOML11_INLINE std::ostream& operator<<(std::ostream& os, const indent_char& c) +{ + switch(c) + { + case indent_char::space: {os << "space" ; break;} + case indent_char::tab: {os << "tab" ; break;} + case indent_char::none: {os << "none" ; break;} + default: + { + os << "unknown indent char: " << static_cast(c); + } + } + return os; +} + +TOML11_INLINE std::string to_string(const indent_char c) +{ + std::ostringstream oss; + oss << c; + return oss.str(); +} + +// ---------------------------------------------------------------------------- +// boolean + +// ---------------------------------------------------------------------------- +// integer + +TOML11_INLINE std::ostream& operator<<(std::ostream& os, const integer_format f) +{ + switch(f) + { + case integer_format::dec: {os << "dec"; break;} + case integer_format::bin: {os << "bin"; break;} + case integer_format::oct: {os << "oct"; break;} + case integer_format::hex: {os << "hex"; break;} + default: + { + os << "unknown integer_format: " << static_cast(f); + break; + } + } + return os; +} +TOML11_INLINE std::string to_string(const integer_format c) +{ + std::ostringstream oss; + oss << c; + return oss.str(); +} + + +TOML11_INLINE bool operator==(const integer_format_info& lhs, const integer_format_info& rhs) noexcept +{ + return lhs.fmt == rhs.fmt && + lhs.uppercase == rhs.uppercase && + lhs.width == rhs.width && + lhs.spacer == rhs.spacer && + lhs.suffix == rhs.suffix ; +} +TOML11_INLINE bool operator!=(const integer_format_info& lhs, const integer_format_info& rhs) noexcept +{ + return !(lhs == rhs); +} + +// ---------------------------------------------------------------------------- +// floating + +TOML11_INLINE std::ostream& operator<<(std::ostream& os, const floating_format f) +{ + switch(f) + { + case floating_format::defaultfloat: {os << "defaultfloat"; break;} + case floating_format::fixed : {os << "fixed" ; break;} + case floating_format::scientific : {os << "scientific" ; break;} + case floating_format::hex : {os << "hex" ; break;} + default: + { + os << "unknown floating_format: " << static_cast(f); + break; + } + } + return os; +} +TOML11_INLINE std::string to_string(const floating_format c) +{ + std::ostringstream oss; + oss << c; + return oss.str(); +} + +TOML11_INLINE bool operator==(const floating_format_info& lhs, const floating_format_info& rhs) noexcept +{ + return lhs.fmt == rhs.fmt && + lhs.prec == rhs.prec && + lhs.suffix == rhs.suffix ; +} +TOML11_INLINE bool operator!=(const floating_format_info& lhs, const floating_format_info& rhs) noexcept +{ + return !(lhs == rhs); +} + +// ---------------------------------------------------------------------------- +// string + +TOML11_INLINE std::ostream& operator<<(std::ostream& os, const string_format f) +{ + switch(f) + { + case string_format::basic : {os << "basic" ; break;} + case string_format::literal : {os << "literal" ; break;} + case string_format::multiline_basic : {os << "multiline_basic" ; break;} + case string_format::multiline_literal: {os << "multiline_literal"; break;} + default: + { + os << "unknown string_format: " << static_cast(f); + break; + } + } + return os; +} +TOML11_INLINE std::string to_string(const string_format c) +{ + std::ostringstream oss; + oss << c; + return oss.str(); +} + +TOML11_INLINE bool operator==(const string_format_info& lhs, const string_format_info& rhs) noexcept +{ + return lhs.fmt == rhs.fmt && + lhs.start_with_newline == rhs.start_with_newline ; +} +TOML11_INLINE bool operator!=(const string_format_info& lhs, const string_format_info& rhs) noexcept +{ + return !(lhs == rhs); +} +// ---------------------------------------------------------------------------- +// datetime + +TOML11_INLINE std::ostream& operator<<(std::ostream& os, const datetime_delimiter_kind d) +{ + switch(d) + { + case datetime_delimiter_kind::upper_T: { os << "upper_T, "; break; } + case datetime_delimiter_kind::lower_t: { os << "lower_t, "; break; } + case datetime_delimiter_kind::space: { os << "space, "; break; } + default: + { + os << "unknown datetime delimiter: " << static_cast(d); + break; + } + } + return os; +} +TOML11_INLINE std::string to_string(const datetime_delimiter_kind c) +{ + std::ostringstream oss; + oss << c; + return oss.str(); +} + +TOML11_INLINE bool operator==(const offset_datetime_format_info& lhs, const offset_datetime_format_info& rhs) noexcept +{ + return lhs.delimiter == rhs.delimiter && + lhs.has_seconds == rhs.has_seconds && + lhs.subsecond_precision == rhs.subsecond_precision ; +} +TOML11_INLINE bool operator!=(const offset_datetime_format_info& lhs, const offset_datetime_format_info& rhs) noexcept +{ + return !(lhs == rhs); +} + +TOML11_INLINE bool operator==(const local_datetime_format_info& lhs, const local_datetime_format_info& rhs) noexcept +{ + return lhs.delimiter == rhs.delimiter && + lhs.has_seconds == rhs.has_seconds && + lhs.subsecond_precision == rhs.subsecond_precision ; +} +TOML11_INLINE bool operator!=(const local_datetime_format_info& lhs, const local_datetime_format_info& rhs) noexcept +{ + return !(lhs == rhs); +} + +TOML11_INLINE bool operator==(const local_date_format_info&, const local_date_format_info&) noexcept +{ + return true; +} +TOML11_INLINE bool operator!=(const local_date_format_info& lhs, const local_date_format_info& rhs) noexcept +{ + return !(lhs == rhs); +} + +TOML11_INLINE bool operator==(const local_time_format_info& lhs, const local_time_format_info& rhs) noexcept +{ + return lhs.has_seconds == rhs.has_seconds && + lhs.subsecond_precision == rhs.subsecond_precision ; +} +TOML11_INLINE bool operator!=(const local_time_format_info& lhs, const local_time_format_info& rhs) noexcept +{ + return !(lhs == rhs); +} + +// ---------------------------------------------------------------------------- +// array + +TOML11_INLINE std::ostream& operator<<(std::ostream& os, const array_format f) +{ + switch(f) + { + case array_format::default_format : {os << "default_format" ; break;} + case array_format::oneline : {os << "oneline" ; break;} + case array_format::multiline : {os << "multiline" ; break;} + case array_format::array_of_tables: {os << "array_of_tables"; break;} + default: + { + os << "unknown array_format: " << static_cast(f); + break; + } + } + return os; +} +TOML11_INLINE std::string to_string(const array_format c) +{ + std::ostringstream oss; + oss << c; + return oss.str(); +} + +TOML11_INLINE bool operator==(const array_format_info& lhs, const array_format_info& rhs) noexcept +{ + return lhs.fmt == rhs.fmt && + lhs.indent_type == rhs.indent_type && + lhs.body_indent == rhs.body_indent && + lhs.closing_indent == rhs.closing_indent ; +} +TOML11_INLINE bool operator!=(const array_format_info& lhs, const array_format_info& rhs) noexcept +{ + return !(lhs == rhs); +} + +// ---------------------------------------------------------------------------- +// table + +TOML11_INLINE std::ostream& operator<<(std::ostream& os, const table_format f) +{ + switch(f) + { + case table_format::multiline : {os << "multiline" ; break;} + case table_format::oneline : {os << "oneline" ; break;} + case table_format::dotted : {os << "dotted" ; break;} + case table_format::multiline_oneline: {os << "multiline_oneline"; break;} + case table_format::implicit : {os << "implicit" ; break;} + default: + { + os << "unknown table_format: " << static_cast(f); + break; + } + } + return os; +} +TOML11_INLINE std::string to_string(const table_format c) +{ + std::ostringstream oss; + oss << c; + return oss.str(); +} + +TOML11_INLINE bool operator==(const table_format_info& lhs, const table_format_info& rhs) noexcept +{ + return lhs.fmt == rhs.fmt && + lhs.indent_type == rhs.indent_type && + lhs.body_indent == rhs.body_indent && + lhs.name_indent == rhs.name_indent && + lhs.closing_indent == rhs.closing_indent ; +} +TOML11_INLINE bool operator!=(const table_format_info& lhs, const table_format_info& rhs) noexcept +{ + return !(lhs == rhs); +} + +} // namespace toml +#endif // TOML11_FORMAT_IMPL_HPP +#endif + +#endif// TOML11_FORMAT_HPP +#ifndef TOML11_DATETIME_HPP +#define TOML11_DATETIME_HPP + +#ifndef TOML11_DATETIME_FWD_HPP +#define TOML11_DATETIME_FWD_HPP + +#include +#include +#include + +#include +#include +#include + +namespace toml +{ + +enum class month_t : std::uint8_t +{ + Jan = 0, + Feb = 1, + Mar = 2, + Apr = 3, + May = 4, + Jun = 5, + Jul = 6, + Aug = 7, + Sep = 8, + Oct = 9, + Nov = 10, + Dec = 11 +}; + +// ---------------------------------------------------------------------------- + +struct local_date +{ + std::int16_t year{0}; // A.D. (like, 2018) + std::uint8_t month{0}; // [0, 11] + std::uint8_t day{0}; // [1, 31] + + local_date(int y, month_t m, int d) + : year {static_cast(y)}, + month{static_cast(m)}, + day {static_cast(d)} + {} + + explicit local_date(const std::tm& t) + : year {static_cast(t.tm_year + 1900)}, + month{static_cast(t.tm_mon)}, + day {static_cast(t.tm_mday)} + {} + + explicit local_date(const std::chrono::system_clock::time_point& tp); + explicit local_date(const std::time_t t); + + operator std::chrono::system_clock::time_point() const; + operator std::time_t() const; + + local_date() = default; + ~local_date() = default; + local_date(local_date const&) = default; + local_date(local_date&&) = default; + local_date& operator=(local_date const&) = default; + local_date& operator=(local_date&&) = default; +}; +bool operator==(const local_date& lhs, const local_date& rhs); +bool operator!=(const local_date& lhs, const local_date& rhs); +bool operator< (const local_date& lhs, const local_date& rhs); +bool operator<=(const local_date& lhs, const local_date& rhs); +bool operator> (const local_date& lhs, const local_date& rhs); +bool operator>=(const local_date& lhs, const local_date& rhs); + +std::ostream& operator<<(std::ostream& os, const local_date& date); +std::string to_string(const local_date& date); + +// ----------------------------------------------------------------------------- + +struct local_time +{ + std::uint8_t hour{0}; // [0, 23] + std::uint8_t minute{0}; // [0, 59] + std::uint8_t second{0}; // [0, 60] + std::uint16_t millisecond{0}; // [0, 999] + std::uint16_t microsecond{0}; // [0, 999] + std::uint16_t nanosecond{0}; // [0, 999] + + local_time(int h, int m, int s, + int ms = 0, int us = 0, int ns = 0) + : hour {static_cast(h)}, + minute{static_cast(m)}, + second{static_cast(s)}, + millisecond{static_cast(ms)}, + microsecond{static_cast(us)}, + nanosecond {static_cast(ns)} + {} + + explicit local_time(const std::tm& t) + : hour {static_cast(t.tm_hour)}, + minute{static_cast(t.tm_min )}, + second{static_cast(t.tm_sec )}, + millisecond{0}, microsecond{0}, nanosecond{0} + {} + + template + explicit local_time(const std::chrono::duration& t) + { + const auto h = std::chrono::duration_cast(t); + this->hour = static_cast(h.count()); + const auto t2 = t - h; + const auto m = std::chrono::duration_cast(t2); + this->minute = static_cast(m.count()); + const auto t3 = t2 - m; + const auto s = std::chrono::duration_cast(t3); + this->second = static_cast(s.count()); + const auto t4 = t3 - s; + const auto ms = std::chrono::duration_cast(t4); + this->millisecond = static_cast(ms.count()); + const auto t5 = t4 - ms; + const auto us = std::chrono::duration_cast(t5); + this->microsecond = static_cast(us.count()); + const auto t6 = t5 - us; + const auto ns = std::chrono::duration_cast(t6); + this->nanosecond = static_cast(ns.count()); + } + + operator std::chrono::nanoseconds() const; + + local_time() = default; + ~local_time() = default; + local_time(local_time const&) = default; + local_time(local_time&&) = default; + local_time& operator=(local_time const&) = default; + local_time& operator=(local_time&&) = default; +}; + +bool operator==(const local_time& lhs, const local_time& rhs); +bool operator!=(const local_time& lhs, const local_time& rhs); +bool operator< (const local_time& lhs, const local_time& rhs); +bool operator<=(const local_time& lhs, const local_time& rhs); +bool operator> (const local_time& lhs, const local_time& rhs); +bool operator>=(const local_time& lhs, const local_time& rhs); + +std::ostream& operator<<(std::ostream& os, const local_time& time); +std::string to_string(const local_time& time); + +// ---------------------------------------------------------------------------- + +struct time_offset +{ + std::int8_t hour{0}; // [-12, 12] + std::int8_t minute{0}; // [-59, 59] + + time_offset(int h, int m) + : hour {static_cast(h)}, + minute{static_cast(m)} + {} + + operator std::chrono::minutes() const; + + time_offset() = default; + ~time_offset() = default; + time_offset(time_offset const&) = default; + time_offset(time_offset&&) = default; + time_offset& operator=(time_offset const&) = default; + time_offset& operator=(time_offset&&) = default; +}; + +bool operator==(const time_offset& lhs, const time_offset& rhs); +bool operator!=(const time_offset& lhs, const time_offset& rhs); +bool operator< (const time_offset& lhs, const time_offset& rhs); +bool operator<=(const time_offset& lhs, const time_offset& rhs); +bool operator> (const time_offset& lhs, const time_offset& rhs); +bool operator>=(const time_offset& lhs, const time_offset& rhs); + +std::ostream& operator<<(std::ostream& os, const time_offset& offset); + +std::string to_string(const time_offset& offset); + +// ----------------------------------------------------------------------------- + +struct local_datetime +{ + local_date date{}; + local_time time{}; + + local_datetime(local_date d, local_time t): date{d}, time{t} {} + + explicit local_datetime(const std::tm& t): date{t}, time{t}{} + + explicit local_datetime(const std::chrono::system_clock::time_point& tp); + explicit local_datetime(const std::time_t t); + + operator std::chrono::system_clock::time_point() const; + operator std::time_t() const; + + local_datetime() = default; + ~local_datetime() = default; + local_datetime(local_datetime const&) = default; + local_datetime(local_datetime&&) = default; + local_datetime& operator=(local_datetime const&) = default; + local_datetime& operator=(local_datetime&&) = default; +}; + +bool operator==(const local_datetime& lhs, const local_datetime& rhs); +bool operator!=(const local_datetime& lhs, const local_datetime& rhs); +bool operator< (const local_datetime& lhs, const local_datetime& rhs); +bool operator<=(const local_datetime& lhs, const local_datetime& rhs); +bool operator> (const local_datetime& lhs, const local_datetime& rhs); +bool operator>=(const local_datetime& lhs, const local_datetime& rhs); + +std::ostream& operator<<(std::ostream& os, const local_datetime& dt); + +std::string to_string(const local_datetime& dt); + +// ----------------------------------------------------------------------------- + +struct offset_datetime +{ + local_date date{}; + local_time time{}; + time_offset offset{}; + + offset_datetime(local_date d, local_time t, time_offset o) + : date{d}, time{t}, offset{o} + {} + offset_datetime(const local_datetime& dt, time_offset o) + : date{dt.date}, time{dt.time}, offset{o} + {} + // use the current local timezone offset + explicit offset_datetime(const local_datetime& ld); + explicit offset_datetime(const std::chrono::system_clock::time_point& tp); + explicit offset_datetime(const std::time_t& t); + explicit offset_datetime(const std::tm& t); + + operator std::chrono::system_clock::time_point() const; + + operator std::time_t() const; + + offset_datetime() = default; + ~offset_datetime() = default; + offset_datetime(offset_datetime const&) = default; + offset_datetime(offset_datetime&&) = default; + offset_datetime& operator=(offset_datetime const&) = default; + offset_datetime& operator=(offset_datetime&&) = default; + + private: + + static time_offset get_local_offset(const std::time_t* tp); +}; + +bool operator==(const offset_datetime& lhs, const offset_datetime& rhs); +bool operator!=(const offset_datetime& lhs, const offset_datetime& rhs); +bool operator< (const offset_datetime& lhs, const offset_datetime& rhs); +bool operator<=(const offset_datetime& lhs, const offset_datetime& rhs); +bool operator> (const offset_datetime& lhs, const offset_datetime& rhs); +bool operator>=(const offset_datetime& lhs, const offset_datetime& rhs); + +std::ostream& operator<<(std::ostream& os, const offset_datetime& dt); + +std::string to_string(const offset_datetime& dt); + +}//toml +#endif // TOML11_DATETIME_FWD_HPP + +#if ! defined(TOML11_COMPILE_SOURCES) +#ifndef TOML11_DATETIME_IMPL_HPP +#define TOML11_DATETIME_IMPL_HPP + + +#include +#include +#include +#include +#include + +#include +#include + +namespace toml +{ + +// To avoid non-threadsafe std::localtime. In C11 (not C++11!), localtime_s is +// provided in the absolutely same purpose, but C++11 is actually not compatible +// with C11. We need to dispatch the function depending on the OS. +namespace detail +{ +// TODO: find more sophisticated way to handle this +#if defined(_MSC_VER) +TOML11_INLINE std::tm localtime_s(const std::time_t* src) +{ + std::tm dst; + const auto result = ::localtime_s(&dst, src); + if (result) { throw std::runtime_error("localtime_s failed."); } + return dst; +} +TOML11_INLINE std::tm gmtime_s(const std::time_t* src) +{ + std::tm dst; + const auto result = ::gmtime_s(&dst, src); + if (result) { throw std::runtime_error("gmtime_s failed."); } + return dst; +} +#elif (defined(_POSIX_C_SOURCE) && _POSIX_C_SOURCE >= 1) || defined(_XOPEN_SOURCE) || defined(_BSD_SOURCE) || defined(_SVID_SOURCE) || defined(_POSIX_SOURCE) +TOML11_INLINE std::tm localtime_s(const std::time_t* src) +{ + std::tm dst; + const auto result = ::localtime_r(src, &dst); + if (!result) { throw std::runtime_error("localtime_r failed."); } + return dst; +} +TOML11_INLINE std::tm gmtime_s(const std::time_t* src) +{ + std::tm dst; + const auto result = ::gmtime_r(src, &dst); + if (!result) { throw std::runtime_error("gmtime_r failed."); } + return dst; +} +#else // fallback. not threadsafe +TOML11_INLINE std::tm localtime_s(const std::time_t* src) +{ + const auto result = std::localtime(src); + if (!result) { throw std::runtime_error("localtime failed."); } + return *result; +} +TOML11_INLINE std::tm gmtime_s(const std::time_t* src) +{ + const auto result = std::gmtime(src); + if (!result) { throw std::runtime_error("gmtime failed."); } + return *result; +} +#endif +} // detail + +// ---------------------------------------------------------------------------- + +TOML11_INLINE local_date::local_date(const std::chrono::system_clock::time_point& tp) +{ + const auto t = std::chrono::system_clock::to_time_t(tp); + const auto time = detail::localtime_s(&t); + *this = local_date(time); +} + +TOML11_INLINE local_date::local_date(const std::time_t t) + : local_date{std::chrono::system_clock::from_time_t(t)} +{} + +TOML11_INLINE local_date::operator std::chrono::system_clock::time_point() const +{ + // std::mktime returns date as local time zone. no conversion needed + std::tm t; + t.tm_sec = 0; + t.tm_min = 0; + t.tm_hour = 0; + t.tm_mday = static_cast(this->day); + t.tm_mon = static_cast(this->month); + t.tm_year = static_cast(this->year) - 1900; + t.tm_wday = 0; // the value will be ignored + t.tm_yday = 0; // the value will be ignored + t.tm_isdst = -1; + return std::chrono::system_clock::from_time_t(std::mktime(&t)); +} + +TOML11_INLINE local_date::operator std::time_t() const +{ + return std::chrono::system_clock::to_time_t( + std::chrono::system_clock::time_point(*this)); +} + +TOML11_INLINE bool operator==(const local_date& lhs, const local_date& rhs) +{ + return std::make_tuple(lhs.year, lhs.month, lhs.day) == + std::make_tuple(rhs.year, rhs.month, rhs.day); +} +TOML11_INLINE bool operator!=(const local_date& lhs, const local_date& rhs) +{ + return !(lhs == rhs); +} +TOML11_INLINE bool operator< (const local_date& lhs, const local_date& rhs) +{ + return std::make_tuple(lhs.year, lhs.month, lhs.day) < + std::make_tuple(rhs.year, rhs.month, rhs.day); +} +TOML11_INLINE bool operator<=(const local_date& lhs, const local_date& rhs) +{ + return (lhs < rhs) || (lhs == rhs); +} +TOML11_INLINE bool operator> (const local_date& lhs, const local_date& rhs) +{ + return !(lhs <= rhs); +} +TOML11_INLINE bool operator>=(const local_date& lhs, const local_date& rhs) +{ + return !(lhs < rhs); +} + +TOML11_INLINE std::ostream& operator<<(std::ostream& os, const local_date& date) +{ + os << std::setfill('0') << std::setw(4) << static_cast(date.year ) << '-'; + os << std::setfill('0') << std::setw(2) << static_cast(date.month) + 1 << '-'; + os << std::setfill('0') << std::setw(2) << static_cast(date.day ) ; + return os; +} + +TOML11_INLINE std::string to_string(const local_date& date) +{ + std::ostringstream oss; + oss.imbue(std::locale::classic()); + oss << date; + return oss.str(); +} + +// ----------------------------------------------------------------------------- + +TOML11_INLINE local_time::operator std::chrono::nanoseconds() const +{ + return std::chrono::nanoseconds (this->nanosecond) + + std::chrono::microseconds(this->microsecond) + + std::chrono::milliseconds(this->millisecond) + + std::chrono::seconds(this->second) + + std::chrono::minutes(this->minute) + + std::chrono::hours(this->hour); +} + +TOML11_INLINE bool operator==(const local_time& lhs, const local_time& rhs) +{ + return std::make_tuple(lhs.hour, lhs.minute, lhs.second, lhs.millisecond, lhs.microsecond, lhs.nanosecond) == + std::make_tuple(rhs.hour, rhs.minute, rhs.second, rhs.millisecond, rhs.microsecond, rhs.nanosecond); +} +TOML11_INLINE bool operator!=(const local_time& lhs, const local_time& rhs) +{ + return !(lhs == rhs); +} +TOML11_INLINE bool operator< (const local_time& lhs, const local_time& rhs) +{ + return std::make_tuple(lhs.hour, lhs.minute, lhs.second, lhs.millisecond, lhs.microsecond, lhs.nanosecond) < + std::make_tuple(rhs.hour, rhs.minute, rhs.second, rhs.millisecond, rhs.microsecond, rhs.nanosecond); +} +TOML11_INLINE bool operator<=(const local_time& lhs, const local_time& rhs) +{ + return (lhs < rhs) || (lhs == rhs); +} +TOML11_INLINE bool operator> (const local_time& lhs, const local_time& rhs) +{ + return !(lhs <= rhs); +} +TOML11_INLINE bool operator>=(const local_time& lhs, const local_time& rhs) +{ + return !(lhs < rhs); +} + +TOML11_INLINE std::ostream& operator<<(std::ostream& os, const local_time& time) +{ + os << std::setfill('0') << std::setw(2) << static_cast(time.hour ) << ':'; + os << std::setfill('0') << std::setw(2) << static_cast(time.minute) << ':'; + os << std::setfill('0') << std::setw(2) << static_cast(time.second); + if(time.millisecond != 0 || time.microsecond != 0 || time.nanosecond != 0) + { + os << '.'; + os << std::setfill('0') << std::setw(3) << static_cast(time.millisecond); + if(time.microsecond != 0 || time.nanosecond != 0) + { + os << std::setfill('0') << std::setw(3) << static_cast(time.microsecond); + if(time.nanosecond != 0) + { + os << std::setfill('0') << std::setw(3) << static_cast(time.nanosecond); + } + } + } + return os; +} + +TOML11_INLINE std::string to_string(const local_time& time) +{ + std::ostringstream oss; + oss.imbue(std::locale::classic()); + oss << time; + return oss.str(); +} + +// ---------------------------------------------------------------------------- + +TOML11_INLINE time_offset::operator std::chrono::minutes() const +{ + return std::chrono::minutes(this->minute) + + std::chrono::hours(this->hour); +} + +TOML11_INLINE bool operator==(const time_offset& lhs, const time_offset& rhs) +{ + return std::make_tuple(lhs.hour, lhs.minute) == + std::make_tuple(rhs.hour, rhs.minute); +} +TOML11_INLINE bool operator!=(const time_offset& lhs, const time_offset& rhs) +{ + return !(lhs == rhs); +} +TOML11_INLINE bool operator< (const time_offset& lhs, const time_offset& rhs) +{ + return std::make_tuple(lhs.hour, lhs.minute) < + std::make_tuple(rhs.hour, rhs.minute); +} +TOML11_INLINE bool operator<=(const time_offset& lhs, const time_offset& rhs) +{ + return (lhs < rhs) || (lhs == rhs); +} +TOML11_INLINE bool operator> (const time_offset& lhs, const time_offset& rhs) +{ + return !(lhs <= rhs); +} +TOML11_INLINE bool operator>=(const time_offset& lhs, const time_offset& rhs) +{ + return !(lhs < rhs); +} + +TOML11_INLINE std::ostream& operator<<(std::ostream& os, const time_offset& offset) +{ + if(offset.hour == 0 && offset.minute == 0) + { + os << 'Z'; + return os; + } + int minute = static_cast(offset.hour) * 60 + offset.minute; + if(minute < 0){os << '-'; minute = std::abs(minute);} else {os << '+';} + os << std::setfill('0') << std::setw(2) << minute / 60 << ':'; + os << std::setfill('0') << std::setw(2) << minute % 60; + return os; +} + +TOML11_INLINE std::string to_string(const time_offset& offset) +{ + std::ostringstream oss; + oss.imbue(std::locale::classic()); + oss << offset; + return oss.str(); +} + +// ----------------------------------------------------------------------------- + +TOML11_INLINE local_datetime::local_datetime(const std::chrono::system_clock::time_point& tp) +{ + const auto t = std::chrono::system_clock::to_time_t(tp); + std::tm ltime = detail::localtime_s(&t); + + this->date = local_date(ltime); + this->time = local_time(ltime); + + // std::tm lacks subsecond information, so diff between tp and tm + // can be used to get millisecond & microsecond information. + const auto t_diff = tp - + std::chrono::system_clock::from_time_t(std::mktime(<ime)); + this->time.millisecond = static_cast( + std::chrono::duration_cast(t_diff).count()); + this->time.microsecond = static_cast( + std::chrono::duration_cast(t_diff).count()); + this->time.nanosecond = static_cast( + std::chrono::duration_cast(t_diff).count()); +} + +TOML11_INLINE local_datetime::local_datetime(const std::time_t t) + : local_datetime{std::chrono::system_clock::from_time_t(t)} +{} + +TOML11_INLINE local_datetime::operator std::chrono::system_clock::time_point() const +{ + using internal_duration = + typename std::chrono::system_clock::time_point::duration; + + // Normally DST begins at A.M. 3 or 4. If we re-use conversion operator + // of local_date and local_time independently, the conversion fails if + // it is the day when DST begins or ends. Since local_date considers the + // time is 00:00 A.M. and local_time does not consider DST because it + // does not have any date information. We need to consider both date and + // time information at the same time to convert it correctly. + + std::tm t; + t.tm_sec = static_cast(this->time.second); + t.tm_min = static_cast(this->time.minute); + t.tm_hour = static_cast(this->time.hour); + t.tm_mday = static_cast(this->date.day); + t.tm_mon = static_cast(this->date.month); + t.tm_year = static_cast(this->date.year) - 1900; + t.tm_wday = 0; // the value will be ignored + t.tm_yday = 0; // the value will be ignored + t.tm_isdst = -1; + + // std::mktime returns date as local time zone. no conversion needed + auto dt = std::chrono::system_clock::from_time_t(std::mktime(&t)); + dt += std::chrono::duration_cast( + std::chrono::milliseconds(this->time.millisecond) + + std::chrono::microseconds(this->time.microsecond) + + std::chrono::nanoseconds (this->time.nanosecond)); + return dt; +} + +TOML11_INLINE local_datetime::operator std::time_t() const +{ + return std::chrono::system_clock::to_time_t( + std::chrono::system_clock::time_point(*this)); +} + +TOML11_INLINE bool operator==(const local_datetime& lhs, const local_datetime& rhs) +{ + return std::make_tuple(lhs.date, lhs.time) == + std::make_tuple(rhs.date, rhs.time); +} +TOML11_INLINE bool operator!=(const local_datetime& lhs, const local_datetime& rhs) +{ + return !(lhs == rhs); +} +TOML11_INLINE bool operator< (const local_datetime& lhs, const local_datetime& rhs) +{ + return std::make_tuple(lhs.date, lhs.time) < + std::make_tuple(rhs.date, rhs.time); +} +TOML11_INLINE bool operator<=(const local_datetime& lhs, const local_datetime& rhs) +{ + return (lhs < rhs) || (lhs == rhs); +} +TOML11_INLINE bool operator> (const local_datetime& lhs, const local_datetime& rhs) +{ + return !(lhs <= rhs); +} +TOML11_INLINE bool operator>=(const local_datetime& lhs, const local_datetime& rhs) +{ + return !(lhs < rhs); +} + +TOML11_INLINE std::ostream& operator<<(std::ostream& os, const local_datetime& dt) +{ + os << dt.date << 'T' << dt.time; + return os; +} + +TOML11_INLINE std::string to_string(const local_datetime& dt) +{ + std::ostringstream oss; + oss.imbue(std::locale::classic()); + oss << dt; + return oss.str(); +} + +// ----------------------------------------------------------------------------- + + +TOML11_INLINE offset_datetime::offset_datetime(const local_datetime& ld) + : date{ld.date}, time{ld.time}, offset{get_local_offset(nullptr)} + // use the current local timezone offset +{} +TOML11_INLINE offset_datetime::offset_datetime(const std::chrono::system_clock::time_point& tp) + : offset{0, 0} // use gmtime +{ + const auto timet = std::chrono::system_clock::to_time_t(tp); + const auto tm = detail::gmtime_s(&timet); + this->date = local_date(tm); + this->time = local_time(tm); +} +TOML11_INLINE offset_datetime::offset_datetime(const std::time_t& t) + : offset{0, 0} // use gmtime +{ + const auto tm = detail::gmtime_s(&t); + this->date = local_date(tm); + this->time = local_time(tm); +} +TOML11_INLINE offset_datetime::offset_datetime(const std::tm& t) + : offset{0, 0} // assume gmtime +{ + this->date = local_date(t); + this->time = local_time(t); +} + +TOML11_INLINE offset_datetime::operator std::chrono::system_clock::time_point() const +{ + // get date-time + using internal_duration = + typename std::chrono::system_clock::time_point::duration; + + // first, convert it to local date-time information in the same way as + // local_datetime does. later we will use time_t to adjust time offset. + std::tm t; + t.tm_sec = static_cast(this->time.second); + t.tm_min = static_cast(this->time.minute); + t.tm_hour = static_cast(this->time.hour); + t.tm_mday = static_cast(this->date.day); + t.tm_mon = static_cast(this->date.month); + t.tm_year = static_cast(this->date.year) - 1900; + t.tm_wday = 0; // the value will be ignored + t.tm_yday = 0; // the value will be ignored + t.tm_isdst = -1; + const std::time_t tp_loc = std::mktime(std::addressof(t)); + + auto tp = std::chrono::system_clock::from_time_t(tp_loc); + tp += std::chrono::duration_cast( + std::chrono::milliseconds(this->time.millisecond) + + std::chrono::microseconds(this->time.microsecond) + + std::chrono::nanoseconds (this->time.nanosecond)); + + // Since mktime uses local time zone, it should be corrected. + // `12:00:00+09:00` means `03:00:00Z`. So mktime returns `03:00:00Z` if + // we are in `+09:00` timezone. To represent `12:00:00Z` there, we need + // to add `+09:00` to `03:00:00Z`. + // Here, it uses the time_t converted from date-time info to handle + // daylight saving time. + const auto ofs = get_local_offset(std::addressof(tp_loc)); + tp += std::chrono::hours (ofs.hour); + tp += std::chrono::minutes(ofs.minute); + + // We got `12:00:00Z` by correcting local timezone applied by mktime. + // Then we will apply the offset. Let's say `12:00:00-08:00` is given. + // And now, we have `12:00:00Z`. `12:00:00-08:00` means `20:00:00Z`. + // So we need to subtract the offset. + tp -= std::chrono::minutes(this->offset); + return tp; +} + +TOML11_INLINE offset_datetime::operator std::time_t() const +{ + return std::chrono::system_clock::to_time_t( + std::chrono::system_clock::time_point(*this)); +} + +TOML11_INLINE time_offset offset_datetime::get_local_offset(const std::time_t* tp) +{ + // get local timezone with the same date-time information as mktime + const auto t = detail::localtime_s(tp); + + std::array buf; + const auto result = std::strftime(buf.data(), 6, "%z", &t); // +hhmm\0 + if(result != 5) + { + throw std::runtime_error("toml::offset_datetime: cannot obtain " + "timezone information of current env"); + } + const int ofs = std::atoi(buf.data()); + const int ofs_h = ofs / 100; + const int ofs_m = ofs - (ofs_h * 100); + return time_offset(ofs_h, ofs_m); +} + +TOML11_INLINE bool operator==(const offset_datetime& lhs, const offset_datetime& rhs) +{ + return std::make_tuple(lhs.date, lhs.time, lhs.offset) == + std::make_tuple(rhs.date, rhs.time, rhs.offset); +} +TOML11_INLINE bool operator!=(const offset_datetime& lhs, const offset_datetime& rhs) +{ + return !(lhs == rhs); +} +TOML11_INLINE bool operator< (const offset_datetime& lhs, const offset_datetime& rhs) +{ + return std::make_tuple(lhs.date, lhs.time, lhs.offset) < + std::make_tuple(rhs.date, rhs.time, rhs.offset); +} +TOML11_INLINE bool operator<=(const offset_datetime& lhs, const offset_datetime& rhs) +{ + return (lhs < rhs) || (lhs == rhs); +} +TOML11_INLINE bool operator> (const offset_datetime& lhs, const offset_datetime& rhs) +{ + return !(lhs <= rhs); +} +TOML11_INLINE bool operator>=(const offset_datetime& lhs, const offset_datetime& rhs) +{ + return !(lhs < rhs); +} + +TOML11_INLINE std::ostream& operator<<(std::ostream& os, const offset_datetime& dt) +{ + os << dt.date << 'T' << dt.time << dt.offset; + return os; +} + +TOML11_INLINE std::string to_string(const offset_datetime& dt) +{ + std::ostringstream oss; + oss.imbue(std::locale::classic()); + oss << dt; + return oss.str(); +} + +}//toml +#endif // TOML11_DATETIME_IMPL_HPP +#endif + +#endif // TOML11_DATETIME_HPP +#ifndef TOML11_COMPAT_HPP +#define TOML11_COMPAT_HPP + + +#include +#include +#include +#include +#include + +#include + +#if TOML11_CPLUSPLUS_STANDARD_VERSION >= TOML11_CXX20_VALUE +# if __has_include() +# include +# endif +#endif + +#include + +// ---------------------------------------------------------------------------- + +#if TOML11_CPLUSPLUS_STANDARD_VERSION >= TOML11_CXX14_VALUE +# if __has_cpp_attribute(deprecated) +# define TOML11_HAS_ATTR_DEPRECATED 1 +# endif +#endif + +#if defined(TOML11_HAS_ATTR_DEPRECATED) +# define TOML11_DEPRECATED(msg) [[deprecated(msg)]] +#elif defined(__GNUC__) +# define TOML11_DEPRECATED(msg) __attribute__((deprecated(msg))) +#elif defined(_MSC_VER) +# define TOML11_DEPRECATED(msg) __declspec(deprecated(msg)) +#else +# define TOML11_DEPRECATED(msg) +#endif + +// ---------------------------------------------------------------------------- + +#if defined(__cpp_if_constexpr) +# if __cpp_if_constexpr >= 201606L +# define TOML11_HAS_CONSTEXPR_IF 1 +# endif +#endif + +#if defined(TOML11_HAS_CONSTEXPR_IF) +# define TOML11_CONSTEXPR_IF if constexpr +#else +# define TOML11_CONSTEXPR_IF if +#endif + +// ---------------------------------------------------------------------------- + +#if TOML11_CPLUSPLUS_STANDARD_VERSION >= TOML11_CXX14_VALUE +# if defined(__cpp_lib_make_unique) +# if __cpp_lib_make_unique >= 201304L +# define TOML11_HAS_STD_MAKE_UNIQUE 1 +# endif +# endif +#endif + +namespace toml +{ +namespace cxx +{ + +#if defined(TOML11_HAS_STD_MAKE_UNIQUE) + +using std::make_unique; + +#else + +template +std::unique_ptr make_unique(Ts&& ... args) +{ + return std::unique_ptr(new T(std::forward(args)...)); +} + +#endif // TOML11_HAS_STD_MAKE_UNIQUE + +} // cxx +} // toml + +// --------------------------------------------------------------------------- + +#if TOML11_CPLUSPLUS_STANDARD_VERSION >= TOML11_CXX14_VALUE +# if defined(__cpp_lib_make_reverse_iterator) +# if __cpp_lib_make_reverse_iterator >= 201402L +# define TOML11_HAS_STD_MAKE_REVERSE_ITERATOR 1 +# endif +# endif +#endif + +namespace toml +{ +namespace cxx +{ +# if defined(TOML11_HAS_STD_MAKE_REVERSE_ITERATOR) + +using std::make_reverse_iterator; + +#else + +template +std::reverse_iterator make_reverse_iterator(Iterator iter) +{ + return std::reverse_iterator(iter); +} + +#endif // TOML11_HAS_STD_MAKE_REVERSE_ITERATOR + +} // cxx +} // toml + +// --------------------------------------------------------------------------- + +#if TOML11_CPLUSPLUS_STANDARD_VERSION >= TOML11_CXX20_VALUE +# if defined(__cpp_lib_clamp) +# if __cpp_lib_clamp >= 201603L +# define TOML11_HAS_STD_CLAMP 1 +# endif +# endif +#endif + +namespace toml +{ +namespace cxx +{ +#if defined(TOML11_HAS_STD_CLAMP) + +using std::clamp; + +#else + +template +T clamp(const T& x, const T& low, const T& high) noexcept +{ + assert(low <= high); + return (std::min)((std::max)(x, low), high); +} + +#endif // TOML11_HAS_STD_CLAMP + +} // cxx +} // toml + +// --------------------------------------------------------------------------- + +#if TOML11_CPLUSPLUS_STANDARD_VERSION >= TOML11_CXX20_VALUE +# if defined(__cpp_lib_bit_cast) +# if __cpp_lib_bit_cast >= 201806L +# define TOML11_HAS_STD_BIT_CAST 1 +# endif +# endif +#endif + +namespace toml +{ +namespace cxx +{ +#if defined(TOML11_HAS_STD_BIT_CAST) + +using std::bit_cast; + +#else + +template +U bit_cast(const T& x) noexcept +{ + static_assert(sizeof(T) == sizeof(U), ""); + static_assert(std::is_default_constructible::value, ""); + + U z; + std::memcpy(reinterpret_cast(std::addressof(z)), + reinterpret_cast(std::addressof(x)), + sizeof(T)); + + return z; +} + +#endif // TOML11_HAS_STD_BIT_CAST + +} // cxx +} // toml + +// --------------------------------------------------------------------------- +// C++20 remove_cvref_t + +#if TOML11_CPLUSPLUS_STANDARD_VERSION >= TOML11_CXX20_VALUE +# if defined(__cpp_lib_remove_cvref) +# if __cpp_lib_remove_cvref >= 201711L +# define TOML11_HAS_STD_REMOVE_CVREF 1 +# endif +# endif +#endif + +namespace toml +{ +namespace cxx +{ +#if defined(TOML11_HAS_STD_REMOVE_CVREF) + +using std::remove_cvref; +using std::remove_cvref_t; + +#else + +template +struct remove_cvref +{ + using type = typename std::remove_cv< + typename std::remove_reference::type>::type; +}; + +template +using remove_cvref_t = typename remove_cvref::type; + +#endif // TOML11_HAS_STD_REMOVE_CVREF + +} // cxx +} // toml + +// --------------------------------------------------------------------------- +// C++17 and/or/not + +#if TOML11_CPLUSPLUS_STANDARD_VERSION >= TOML11_CXX17_VALUE +# if defined(__cpp_lib_logical_traits) +# if __cpp_lib_logical_traits >= 201510L +# define TOML11_HAS_STD_CONJUNCTION 1 +# endif +# endif +#endif + +namespace toml +{ +namespace cxx +{ +#if defined(TOML11_HAS_STD_CONJUNCTION) + +using std::conjunction; +using std::disjunction; +using std::negation; + +#else + +template struct conjunction : std::true_type{}; +template struct conjunction : T{}; +template +struct conjunction : + std::conditional(T::value), conjunction, T>::type +{}; + +template struct disjunction : std::false_type{}; +template struct disjunction : T {}; +template +struct disjunction : + std::conditional(T::value), T, disjunction>::type +{}; + +template +struct negation : std::integral_constant(T::value)>{}; + +#endif // TOML11_HAS_STD_CONJUNCTION + +} // cxx +} // toml + +// --------------------------------------------------------------------------- +// C++14 index_sequence + +#if TOML11_CPLUSPLUS_STANDARD_VERSION >= TOML11_CXX14_VALUE +# if defined(__cpp_lib_integer_sequence) +# if __cpp_lib_integer_sequence >= 201304L +# define TOML11_HAS_STD_INTEGER_SEQUENCE 1 +# endif +# endif +#endif + +namespace toml +{ +namespace cxx +{ +#if defined(TOML11_HAS_STD_INTEGER_SEQUENCE) + +using std::index_sequence; +using std::make_index_sequence; + +#else + +template struct index_sequence{}; + +template +struct double_index_sequence; + +template +struct double_index_sequence> +{ + using type = index_sequence; +}; +template +struct double_index_sequence> +{ + using type = index_sequence; +}; + +template +struct index_sequence_maker +{ + using type = typename double_index_sequence< + N % 2 == 1, N/2, typename index_sequence_maker::type + >::type; +}; +template<> +struct index_sequence_maker<0> +{ + using type = index_sequence<>; +}; + +template +using make_index_sequence = typename index_sequence_maker::type; + +#endif // TOML11_HAS_STD_INTEGER_SEQUENCE + +} // cxx +} // toml + +// --------------------------------------------------------------------------- +// C++14 enable_if_t + +#if TOML11_CPLUSPLUS_STANDARD_VERSION >= TOML11_CXX14_VALUE +# if defined(__cpp_lib_transformation_trait_aliases) +# if __cpp_lib_transformation_trait_aliases >= 201304L +# define TOML11_HAS_STD_ENABLE_IF_T 1 +# endif +# endif +#endif + +namespace toml +{ +namespace cxx +{ +#if defined(TOML11_HAS_STD_ENABLE_IF_T) + +using std::enable_if_t; + +#else + +template +using enable_if_t = typename std::enable_if::type; + +#endif // TOML11_HAS_STD_ENABLE_IF_T + +} // cxx +} // toml + +// --------------------------------------------------------------------------- +// return_type_of_t + +#if TOML11_CPLUSPLUS_STANDARD_VERSION >= TOML11_CXX17_VALUE +# if defined(__cpp_lib_is_invocable) +# if __cpp_lib_is_invocable >= 201703 +# define TOML11_HAS_STD_INVOKE_RESULT 1 +# endif +# endif +#endif + +namespace toml +{ +namespace cxx +{ +#if defined(TOML11_HAS_STD_INVOKE_RESULT) + +template +using return_type_of_t = std::invoke_result_t; + +#else + +// result_of is deprecated after C++17 +template +using return_type_of_t = typename std::result_of::type; + +#endif // TOML11_HAS_STD_INVOKE_RESULT + +} // cxx +} // toml + +// ---------------------------------------------------------------------------- +// (subset of) source_location + +#if TOML11_CPLUSPLUS_STANDARD_VERSION >= 202002L +# if __has_include() +# define TOML11_HAS_STD_SOURCE_LOCATION +# endif // has_include +#endif // c++20 + +#if ! defined(TOML11_HAS_STD_SOURCE_LOCATION) +# if defined(__GNUC__) && ! defined(__clang__) +# if TOML11_CPLUSPLUS_STANDARD_VERSION >= TOML11_CXX14_VALUE +# if __has_include() +# define TOML11_HAS_EXPERIMENTAL_SOURCE_LOCATION +# endif +# endif +# endif // GNU g++ +#endif // not TOML11_HAS_STD_SOURCE_LOCATION + +#if ! defined(TOML11_HAS_STD_SOURCE_LOCATION) && ! defined(TOML11_HAS_EXPERIMENTAL_SOURCE_LOCATION) +# if defined(__GNUC__) && ! defined(__clang__) +# if (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 9)) +# define TOML11_HAS_BUILTIN_FILE_LINE 1 +# define TOML11_BUILTIN_LINE_TYPE int +# endif +# elif defined(__clang__) // clang 9.0.0 implements builtin_FILE/LINE +# if __has_builtin(__builtin_FILE) && __has_builtin(__builtin_LINE) +# define TOML11_HAS_BUILTIN_FILE_LINE 1 +# define TOML11_BUILTIN_LINE_TYPE unsigned int +# endif +# elif defined(_MSVC_LANG) && defined(_MSC_VER) +# if _MSC_VER > 1926 +# define TOML11_HAS_BUILTIN_FILE_LINE 1 +# define TOML11_BUILTIN_LINE_TYPE int +# endif +# endif +#endif + +#if defined(TOML11_HAS_STD_SOURCE_LOCATION) +#include +namespace toml +{ +namespace cxx +{ +using source_location = std::source_location; + +inline std::string to_string(const source_location& loc) +{ + return std::string(" at line ") + std::to_string(loc.line()) + + std::string(" in file ") + std::string(loc.file_name()); +} +} // cxx +} // toml +#elif defined(TOML11_HAS_EXPERIMENTAL_SOURCE_LOCATION) +#include +namespace toml +{ +namespace cxx +{ +using source_location = std::experimental::source_location; + +inline std::string to_string(const source_location& loc) +{ + return std::string(" at line ") + std::to_string(loc.line()) + + std::string(" in file ") + std::string(loc.file_name()); +} +} // cxx +} // toml +#elif defined(TOML11_HAS_BUILTIN_FILE_LINE) +namespace toml +{ +namespace cxx +{ +struct source_location +{ + using line_type = TOML11_BUILTIN_LINE_TYPE; + static source_location current(const line_type line = __builtin_LINE(), + const char* file = __builtin_FILE()) + { + return source_location(line, file); + } + + source_location(const line_type line, const char* file) + : line_(line), file_name_(file) + {} + + line_type line() const noexcept {return line_;} + const char* file_name() const noexcept {return file_name_;} + + private: + + line_type line_; + const char* file_name_; +}; + +inline std::string to_string(const source_location& loc) +{ + return std::string(" at line ") + std::to_string(loc.line()) + + std::string(" in file ") + std::string(loc.file_name()); +} +} // cxx +} // toml +#else // no builtin +namespace toml +{ +namespace cxx +{ +struct source_location +{ + static source_location current() { return source_location{}; } +}; + +inline std::string to_string(const source_location&) +{ + return std::string(""); +} +} // cxx +} // toml +#endif // TOML11_HAS_STD_SOURCE_LOCATION + +// ---------------------------------------------------------------------------- +// (subset of) optional + +#if TOML11_CPLUSPLUS_STANDARD_VERSION >= TOML11_CXX17_VALUE +# if __has_include() +# include +# endif // has_include(optional) +#endif // C++17 + +#if TOML11_CPLUSPLUS_STANDARD_VERSION >= TOML11_CXX17_VALUE +# if defined(__cpp_lib_optional) +# if __cpp_lib_optional >= 201606L +# define TOML11_HAS_STD_OPTIONAL 1 +# endif +# endif +#endif + +#if defined(TOML11_HAS_STD_OPTIONAL) + +namespace toml +{ +namespace cxx +{ +using std::optional; + +inline std::nullopt_t make_nullopt() {return std::nullopt;} + +template +std::basic_ostream& +operator<<(std::basic_ostream& os, const std::nullopt_t&) +{ + os << "nullopt"; + return os; +} + +} // cxx +} // toml + +#else // TOML11_HAS_STD_OPTIONAL + +namespace toml +{ +namespace cxx +{ + +struct nullopt_t{}; +inline nullopt_t make_nullopt() {return nullopt_t{};} + +inline bool operator==(const nullopt_t&, const nullopt_t&) noexcept {return true;} +inline bool operator!=(const nullopt_t&, const nullopt_t&) noexcept {return false;} +inline bool operator< (const nullopt_t&, const nullopt_t&) noexcept {return false;} +inline bool operator<=(const nullopt_t&, const nullopt_t&) noexcept {return true;} +inline bool operator> (const nullopt_t&, const nullopt_t&) noexcept {return false;} +inline bool operator>=(const nullopt_t&, const nullopt_t&) noexcept {return true;} + +template +std::basic_ostream& +operator<<(std::basic_ostream& os, const nullopt_t&) +{ + os << "nullopt"; + return os; +} + +template +class optional +{ + public: + + using value_type = T; + + public: + + optional() noexcept : has_value_(false), null_('\0') {} + optional(nullopt_t) noexcept : has_value_(false), null_('\0') {} + + optional(const T& x): has_value_(true), value_(x) {} + optional(T&& x): has_value_(true), value_(std::move(x)) {} + + template::value, std::nullptr_t> = nullptr> + explicit optional(U&& x): has_value_(true), value_(std::forward(x)) {} + + optional(const optional& rhs): has_value_(rhs.has_value_) + { + if(rhs.has_value_) + { + this->assigner(rhs.value_); + } + } + optional(optional&& rhs): has_value_(rhs.has_value_) + { + if(this->has_value_) + { + this->assigner(std::move(rhs.value_)); + } + } + + optional& operator=(const optional& rhs) + { + if(this == std::addressof(rhs)) {return *this;} + + this->cleanup(); + this->has_value_ = rhs.has_value_; + if(this->has_value_) + { + this->assigner(rhs.value_); + } + return *this; + } + optional& operator=(optional&& rhs) + { + if(this == std::addressof(rhs)) {return *this;} + + this->cleanup(); + this->has_value_ = rhs.has_value_; + if(this->has_value_) + { + this->assigner(std::move(rhs.value_)); + } + return *this; + } + + template>, std::is_constructible + >::value, std::nullptr_t> = nullptr> + explicit optional(const optional& rhs): has_value_(rhs.has_value_), null_('\0') + { + if(rhs.has_value_) + { + this->assigner(rhs.value_); + } + } + template>, std::is_constructible + >::value, std::nullptr_t> = nullptr> + explicit optional(optional&& rhs): has_value_(rhs.has_value_), null_('\0') + { + if(this->has_value_) + { + this->assigner(std::move(rhs.value_)); + } + } + + template>, std::is_constructible + >::value, std::nullptr_t> = nullptr> + optional& operator=(const optional& rhs) + { + if(this == std::addressof(rhs)) {return *this;} + + this->cleanup(); + this->has_value_ = rhs.has_value_; + if(this->has_value_) + { + this->assigner(rhs.value_); + } + return *this; + } + + template>, std::is_constructible + >::value, std::nullptr_t> = nullptr> + optional& operator=(optional&& rhs) + { + if(this == std::addressof(rhs)) {return *this;} + + this->cleanup(); + this->has_value_ = rhs.has_value_; + if(this->has_value_) + { + this->assigner(std::move(rhs.value_)); + } + return *this; + } + ~optional() noexcept + { + this->cleanup(); + } + + explicit operator bool() const noexcept + { + return has_value_; + } + + bool has_value() const noexcept {return has_value_;} + + value_type const& value(source_location loc = source_location::current()) const + { + if( ! this->has_value_) + { + throw std::runtime_error("optional::value(): bad_unwrap" + to_string(loc)); + } + return this->value_; + } + value_type& value(source_location loc = source_location::current()) + { + if( ! this->has_value_) + { + throw std::runtime_error("optional::value(): bad_unwrap" + to_string(loc)); + } + return this->value_; + } + + value_type const& value_or(const value_type& opt) const + { + if(this->has_value_) {return this->value_;} else {return opt;} + } + value_type& value_or(value_type& opt) + { + if(this->has_value_) {return this->value_;} else {return opt;} + } + + private: + + void cleanup() noexcept + { + if(this->has_value_) + { + value_.~T(); + } + } + + template + void assigner(U&& x) + { + const auto tmp = ::new(std::addressof(this->value_)) value_type(std::forward(x)); + assert(tmp == std::addressof(this->value_)); + (void)tmp; + } + + private: + + bool has_value_; + union + { + char null_; + T value_; + }; +}; +} // cxx +} // toml +#endif // TOML11_HAS_STD_OPTIONAL + +#endif // TOML11_COMPAT_HPP +#ifndef TOML11_VALUE_T_HPP +#define TOML11_VALUE_T_HPP + +#ifndef TOML11_VALUE_T_FWD_HPP +#define TOML11_VALUE_T_FWD_HPP + + +#include +#include +#include + +#include + +namespace toml +{ + +// forward decl +template +class basic_value; + +// ---------------------------------------------------------------------------- +// enum representing toml types + +enum class value_t : std::uint8_t +{ + empty = 0, + boolean = 1, + integer = 2, + floating = 3, + string = 4, + offset_datetime = 5, + local_datetime = 6, + local_date = 7, + local_time = 8, + array = 9, + table = 10 +}; + +std::ostream& operator<<(std::ostream& os, value_t t); +std::string to_string(value_t t); + + +// ---------------------------------------------------------------------------- +// meta functions for internal use + +namespace detail +{ + +template +using value_t_constant = std::integral_constant; + +template +struct type_to_enum : value_t_constant {}; + +template struct type_to_enum : value_t_constant {}; +template struct type_to_enum : value_t_constant {}; +template struct type_to_enum : value_t_constant {}; +template struct type_to_enum : value_t_constant {}; +template struct type_to_enum : value_t_constant {}; +template struct type_to_enum : value_t_constant {}; +template struct type_to_enum : value_t_constant {}; +template struct type_to_enum : value_t_constant {}; +template struct type_to_enum : value_t_constant {}; +template struct type_to_enum : value_t_constant {}; + +template +struct enum_to_type { using type = void; }; + +template struct enum_to_type { using type = typename V::boolean_type ; }; +template struct enum_to_type { using type = typename V::integer_type ; }; +template struct enum_to_type { using type = typename V::floating_type ; }; +template struct enum_to_type { using type = typename V::string_type ; }; +template struct enum_to_type { using type = typename V::offset_datetime_type; }; +template struct enum_to_type { using type = typename V::local_datetime_type ; }; +template struct enum_to_type { using type = typename V::local_date_type ; }; +template struct enum_to_type { using type = typename V::local_time_type ; }; +template struct enum_to_type { using type = typename V::array_type ; }; +template struct enum_to_type { using type = typename V::table_type ; }; + +template +using enum_to_type_t = typename enum_to_type::type; + +template +struct enum_to_fmt_type { using type = void; }; + +template<> struct enum_to_fmt_type { using type = boolean_format_info ; }; +template<> struct enum_to_fmt_type { using type = integer_format_info ; }; +template<> struct enum_to_fmt_type { using type = floating_format_info ; }; +template<> struct enum_to_fmt_type { using type = string_format_info ; }; +template<> struct enum_to_fmt_type { using type = offset_datetime_format_info; }; +template<> struct enum_to_fmt_type { using type = local_datetime_format_info ; }; +template<> struct enum_to_fmt_type { using type = local_date_format_info ; }; +template<> struct enum_to_fmt_type { using type = local_time_format_info ; }; +template<> struct enum_to_fmt_type { using type = array_format_info ; }; +template<> struct enum_to_fmt_type { using type = table_format_info ; }; + +template +using enum_to_fmt_type_t = typename enum_to_fmt_type::type; + +template +struct is_exact_toml_type0 : cxx::disjunction< + std::is_same, + std::is_same, + std::is_same, + std::is_same, + std::is_same, + std::is_same, + std::is_same, + std::is_same, + std::is_same, + std::is_same + >{}; +template struct is_exact_toml_type: is_exact_toml_type0, V> {}; +template struct is_not_toml_type : cxx::negation> {}; + +} // namespace detail +} // namespace toml +#endif // TOML11_VALUE_T_FWD_HPP + +#if ! defined(TOML11_COMPILE_SOURCES) +#ifndef TOML11_VALUE_T_IMPL_HPP +#define TOML11_VALUE_T_IMPL_HPP + + +#include +#include +#include + +namespace toml +{ + +TOML11_INLINE std::ostream& operator<<(std::ostream& os, value_t t) +{ + switch(t) + { + case value_t::boolean : os << "boolean"; return os; + case value_t::integer : os << "integer"; return os; + case value_t::floating : os << "floating"; return os; + case value_t::string : os << "string"; return os; + case value_t::offset_datetime : os << "offset_datetime"; return os; + case value_t::local_datetime : os << "local_datetime"; return os; + case value_t::local_date : os << "local_date"; return os; + case value_t::local_time : os << "local_time"; return os; + case value_t::array : os << "array"; return os; + case value_t::table : os << "table"; return os; + case value_t::empty : os << "empty"; return os; + default : os << "unknown"; return os; + } +} + +TOML11_INLINE std::string to_string(value_t t) +{ + std::ostringstream oss; + oss << t; + return oss.str(); +} + +} // namespace toml +#endif // TOML11_VALUE_T_IMPL_HPP +#endif + +#endif // TOML11_VALUE_T_HPP +#ifndef TOML11_STORAGE_HPP +#define TOML11_STORAGE_HPP + + +namespace toml +{ +namespace detail +{ + +// It owns a pointer to T. It does deep-copy when copied. +// This struct is introduced to implement a recursive type. +// +// `toml::value` contains `std::vector` to represent a toml array. +// But, in the definition of `toml::value`, `toml::value` is still incomplete. +// `std::vector` of an incomplete type is not allowed in C++11 (it is allowed +// after C++17). To avoid this, we need to use a pointer to `toml::value`, like +// `std::vector>`. Although `std::unique_ptr` is +// noncopyable, we want to make `toml::value` copyable. `storage` is introduced +// to resolve those problems. +template +struct storage +{ + using value_type = T; + + explicit storage(value_type v): ptr_(cxx::make_unique(std::move(v))) {} + ~storage() = default; + + storage(const storage& rhs): ptr_(cxx::make_unique(*rhs.ptr_)) {} + storage& operator=(const storage& rhs) + { + this->ptr_ = cxx::make_unique(*rhs.ptr_); + return *this; + } + + storage(storage&&) = default; + storage& operator=(storage&&) = default; + + bool is_ok() const noexcept {return static_cast(ptr_);} + + value_type& get() const noexcept {return *ptr_;} + + private: + std::unique_ptr ptr_; +}; + +} // detail +} // toml +#endif // TOML11_STORAGE_HPP +#ifndef TOML11_COMMENTS_HPP +#define TOML11_COMMENTS_HPP + +#ifndef TOML11_COMMENTS_FWD_HPP +#define TOML11_COMMENTS_FWD_HPP + +// to use __has_builtin + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +// This file provides mainly two classes, `preserve_comments` and `discard_comments`. +// Those two are a container that have the same interface as `std::vector` +// but bahaves in the opposite way. `preserve_comments` is just the same as +// `std::vector` and each `std::string` corresponds to a comment line. +// Conversely, `discard_comments` discards all the strings and ignores everything +// assigned in it. `discard_comments` is always empty and you will encounter an +// error whenever you access to the element. +namespace toml +{ +class discard_comments; // forward decl + +class preserve_comments +{ + public: + // `container_type` is not provided in discard_comments. + // do not use this inner-type in a generic code. + using container_type = std::vector; + + using size_type = container_type::size_type; + using difference_type = container_type::difference_type; + using value_type = container_type::value_type; + using reference = container_type::reference; + using const_reference = container_type::const_reference; + using pointer = container_type::pointer; + using const_pointer = container_type::const_pointer; + using iterator = container_type::iterator; + using const_iterator = container_type::const_iterator; + using reverse_iterator = container_type::reverse_iterator; + using const_reverse_iterator = container_type::const_reverse_iterator; + + public: + + preserve_comments() = default; + ~preserve_comments() = default; + preserve_comments(preserve_comments const&) = default; + preserve_comments(preserve_comments &&) = default; + preserve_comments& operator=(preserve_comments const&) = default; + preserve_comments& operator=(preserve_comments &&) = default; + + explicit preserve_comments(const std::vector& c): comments(c){} + explicit preserve_comments(std::vector&& c) + : comments(std::move(c)) + {} + preserve_comments& operator=(const std::vector& c) + { + comments = c; + return *this; + } + preserve_comments& operator=(std::vector&& c) + { + comments = std::move(c); + return *this; + } + + explicit preserve_comments(const discard_comments&) {} + + explicit preserve_comments(size_type n): comments(n) {} + preserve_comments(size_type n, const std::string& x): comments(n, x) {} + preserve_comments(std::initializer_list x): comments(x) {} + template + preserve_comments(InputIterator first, InputIterator last) + : comments(first, last) + {} + + template + void assign(InputIterator first, InputIterator last) {comments.assign(first, last);} + void assign(std::initializer_list ini) {comments.assign(ini);} + void assign(size_type n, const std::string& val) {comments.assign(n, val);} + + // Related to the issue #97. + // + // `std::vector::insert` and `std::vector::erase` in the STL implementation + // included in GCC 4.8.5 takes `std::vector::iterator` instead of + // `std::vector::const_iterator`. It causes compilation error in GCC 4.8.5. +#if defined(__GNUC__) && defined(__GNUC_MINOR__) && defined(__GNUC_PATCHLEVEL__) && !defined(__clang__) +# if (__GNUC__ * 10000 + __GNUC_MINOR__ * 100 + __GNUC_PATCHLEVEL__) <= 40805 +# define TOML11_WORKAROUND_GCC_4_8_X_STANDARD_LIBRARY_IMPLEMENTATION +# endif +#endif + +#ifdef TOML11_WORKAROUND_GCC_4_8_X_STANDARD_LIBRARY_IMPLEMENTATION + iterator insert(iterator p, const std::string& x) + { + return comments.insert(p, x); + } + iterator insert(iterator p, std::string&& x) + { + return comments.insert(p, std::move(x)); + } + void insert(iterator p, size_type n, const std::string& x) + { + return comments.insert(p, n, x); + } + template + void insert(iterator p, InputIterator first, InputIterator last) + { + return comments.insert(p, first, last); + } + void insert(iterator p, std::initializer_list ini) + { + return comments.insert(p, ini); + } + + template + iterator emplace(iterator p, Ts&& ... args) + { + return comments.emplace(p, std::forward(args)...); + } + + iterator erase(iterator pos) {return comments.erase(pos);} + iterator erase(iterator first, iterator last) + { + return comments.erase(first, last); + } +#else + iterator insert(const_iterator p, const std::string& x) + { + return comments.insert(p, x); + } + iterator insert(const_iterator p, std::string&& x) + { + return comments.insert(p, std::move(x)); + } + iterator insert(const_iterator p, size_type n, const std::string& x) + { + return comments.insert(p, n, x); + } + template + iterator insert(const_iterator p, InputIterator first, InputIterator last) + { + return comments.insert(p, first, last); + } + iterator insert(const_iterator p, std::initializer_list ini) + { + return comments.insert(p, ini); + } + + template + iterator emplace(const_iterator p, Ts&& ... args) + { + return comments.emplace(p, std::forward(args)...); + } + + iterator erase(const_iterator pos) {return comments.erase(pos);} + iterator erase(const_iterator first, const_iterator last) + { + return comments.erase(first, last); + } +#endif + + void swap(preserve_comments& other) {comments.swap(other.comments);} + + void push_back(const std::string& v) {comments.push_back(v);} + void push_back(std::string&& v) {comments.push_back(std::move(v));} + void pop_back() {comments.pop_back();} + + template + void emplace_back(Ts&& ... args) {comments.emplace_back(std::forward(args)...);} + + void clear() {comments.clear();} + + size_type size() const noexcept {return comments.size();} + size_type max_size() const noexcept {return comments.max_size();} + size_type capacity() const noexcept {return comments.capacity();} + bool empty() const noexcept {return comments.empty();} + + void reserve(size_type n) {comments.reserve(n);} + void resize(size_type n) {comments.resize(n);} + void resize(size_type n, const std::string& c) {comments.resize(n, c);} + void shrink_to_fit() {comments.shrink_to_fit();} + + reference operator[](const size_type n) noexcept {return comments[n];} + const_reference operator[](const size_type n) const noexcept {return comments[n];} + reference at(const size_type n) {return comments.at(n);} + const_reference at(const size_type n) const {return comments.at(n);} + reference front() noexcept {return comments.front();} + const_reference front() const noexcept {return comments.front();} + reference back() noexcept {return comments.back();} + const_reference back() const noexcept {return comments.back();} + + pointer data() noexcept {return comments.data();} + const_pointer data() const noexcept {return comments.data();} + + iterator begin() noexcept {return comments.begin();} + iterator end() noexcept {return comments.end();} + const_iterator begin() const noexcept {return comments.begin();} + const_iterator end() const noexcept {return comments.end();} + const_iterator cbegin() const noexcept {return comments.cbegin();} + const_iterator cend() const noexcept {return comments.cend();} + + reverse_iterator rbegin() noexcept {return comments.rbegin();} + reverse_iterator rend() noexcept {return comments.rend();} + const_reverse_iterator rbegin() const noexcept {return comments.rbegin();} + const_reverse_iterator rend() const noexcept {return comments.rend();} + const_reverse_iterator crbegin() const noexcept {return comments.crbegin();} + const_reverse_iterator crend() const noexcept {return comments.crend();} + + friend bool operator==(const preserve_comments&, const preserve_comments&); + friend bool operator!=(const preserve_comments&, const preserve_comments&); + friend bool operator< (const preserve_comments&, const preserve_comments&); + friend bool operator<=(const preserve_comments&, const preserve_comments&); + friend bool operator> (const preserve_comments&, const preserve_comments&); + friend bool operator>=(const preserve_comments&, const preserve_comments&); + + friend void swap(preserve_comments&, std::vector&); + friend void swap(std::vector&, preserve_comments&); + + private: + + container_type comments; +}; + +bool operator==(const preserve_comments& lhs, const preserve_comments& rhs); +bool operator!=(const preserve_comments& lhs, const preserve_comments& rhs); +bool operator< (const preserve_comments& lhs, const preserve_comments& rhs); +bool operator<=(const preserve_comments& lhs, const preserve_comments& rhs); +bool operator> (const preserve_comments& lhs, const preserve_comments& rhs); +bool operator>=(const preserve_comments& lhs, const preserve_comments& rhs); + +void swap(preserve_comments& lhs, preserve_comments& rhs); +void swap(preserve_comments& lhs, std::vector& rhs); +void swap(std::vector& lhs, preserve_comments& rhs); + +std::ostream& operator<<(std::ostream& os, const preserve_comments& com); + +namespace detail +{ + +// To provide the same interface with `preserve_comments`, `discard_comments` +// should have an iterator. But it does not contain anything, so we need to +// add an iterator that points nothing. +// +// It always points null, so DO NOT unwrap this iterator. It always crashes +// your program. +template +struct empty_iterator +{ + using value_type = T; + using reference_type = typename std::conditional::type; + using pointer_type = typename std::conditional::type; + using difference_type = std::ptrdiff_t; + using iterator_category = std::random_access_iterator_tag; + + empty_iterator() = default; + ~empty_iterator() = default; + empty_iterator(empty_iterator const&) = default; + empty_iterator(empty_iterator &&) = default; + empty_iterator& operator=(empty_iterator const&) = default; + empty_iterator& operator=(empty_iterator &&) = default; + + // DO NOT call these operators. + reference_type operator*() const noexcept {std::terminate();} + pointer_type operator->() const noexcept {return nullptr;} + reference_type operator[](difference_type) const noexcept {return this->operator*();} + + // These operators do nothing. + empty_iterator& operator++() noexcept {return *this;} + empty_iterator operator++(int) noexcept {return *this;} + empty_iterator& operator--() noexcept {return *this;} + empty_iterator operator--(int) noexcept {return *this;} + + empty_iterator& operator+=(difference_type) noexcept {return *this;} + empty_iterator& operator-=(difference_type) noexcept {return *this;} + + empty_iterator operator+(difference_type) const noexcept {return *this;} + empty_iterator operator-(difference_type) const noexcept {return *this;} +}; + +template +bool operator==(const empty_iterator&, const empty_iterator&) noexcept {return true;} +template +bool operator!=(const empty_iterator&, const empty_iterator&) noexcept {return false;} +template +bool operator< (const empty_iterator&, const empty_iterator&) noexcept {return false;} +template +bool operator<=(const empty_iterator&, const empty_iterator&) noexcept {return true;} +template +bool operator> (const empty_iterator&, const empty_iterator&) noexcept {return false;} +template +bool operator>=(const empty_iterator&, const empty_iterator&) noexcept {return true;} + +template +typename empty_iterator::difference_type +operator-(const empty_iterator&, const empty_iterator&) noexcept {return 0;} + +template +empty_iterator +operator+(typename empty_iterator::difference_type, const empty_iterator& rhs) noexcept {return rhs;} +template +empty_iterator +operator+(const empty_iterator& lhs, typename empty_iterator::difference_type) noexcept {return lhs;} + +} // detail + +// The default comment type. It discards all the comments. It requires only one +// byte to contain, so the memory footprint is smaller than preserve_comments. +// +// It just ignores `push_back`, `insert`, `erase`, and any other modifications. +// IT always returns size() == 0, the iterator taken by `begin()` is always the +// same as that of `end()`, and accessing through `operator[]` or iterators +// always causes a segmentation fault. DO NOT access to the element of this. +// +// Why this is chose as the default type is because the last version (2.x.y) +// does not contain any comments in a value. To minimize the impact on the +// efficiency, this is chosen as a default. +// +// To reduce the memory footprint, later we can try empty base optimization (EBO). +class discard_comments +{ + public: + using size_type = std::size_t; + using difference_type = std::ptrdiff_t; + using value_type = std::string; + using reference = std::string&; + using const_reference = std::string const&; + using pointer = std::string*; + using const_pointer = std::string const*; + using iterator = detail::empty_iterator; + using const_iterator = detail::empty_iterator; + using reverse_iterator = detail::empty_iterator; + using const_reverse_iterator = detail::empty_iterator; + + public: + discard_comments() = default; + ~discard_comments() = default; + discard_comments(discard_comments const&) = default; + discard_comments(discard_comments &&) = default; + discard_comments& operator=(discard_comments const&) = default; + discard_comments& operator=(discard_comments &&) = default; + + explicit discard_comments(const std::vector&) noexcept {} + explicit discard_comments(std::vector&&) noexcept {} + discard_comments& operator=(const std::vector&) noexcept {return *this;} + discard_comments& operator=(std::vector&&) noexcept {return *this;} + + explicit discard_comments(const preserve_comments&) noexcept {} + + explicit discard_comments(size_type) noexcept {} + discard_comments(size_type, const std::string&) noexcept {} + discard_comments(std::initializer_list) noexcept {} + template + discard_comments(InputIterator, InputIterator) noexcept {} + + template + void assign(InputIterator, InputIterator) noexcept {} + void assign(std::initializer_list) noexcept {} + void assign(size_type, const std::string&) noexcept {} + + iterator insert(const_iterator, const std::string&) {return iterator{};} + iterator insert(const_iterator, std::string&&) {return iterator{};} + iterator insert(const_iterator, size_type, const std::string&) {return iterator{};} + template + iterator insert(const_iterator, InputIterator, InputIterator) {return iterator{};} + iterator insert(const_iterator, std::initializer_list) {return iterator{};} + + template + iterator emplace(const_iterator, Ts&& ...) {return iterator{};} + iterator erase(const_iterator) {return iterator{};} + iterator erase(const_iterator, const_iterator) {return iterator{};} + + void swap(discard_comments&) {return;} + + void push_back(const std::string&) {return;} + void push_back(std::string&& ) {return;} + void pop_back() {return;} + + template + void emplace_back(Ts&& ...) {return;} + + void clear() {return;} + + size_type size() const noexcept {return 0;} + size_type max_size() const noexcept {return 0;} + size_type capacity() const noexcept {return 0;} + bool empty() const noexcept {return true;} + + void reserve(size_type) {return;} + void resize(size_type) {return;} + void resize(size_type, const std::string&) {return;} + void shrink_to_fit() {return;} + + // DO NOT access to the element of this container. This container is always + // empty, so accessing through operator[], front/back, data causes address + // error. + + reference operator[](const size_type) noexcept {never_call("toml::discard_comment::operator[]");} + const_reference operator[](const size_type) const noexcept {never_call("toml::discard_comment::operator[]");} + reference at(const size_type) {throw std::out_of_range("toml::discard_comment is always empty.");} + const_reference at(const size_type) const {throw std::out_of_range("toml::discard_comment is always empty.");} + reference front() noexcept {never_call("toml::discard_comment::front");} + const_reference front() const noexcept {never_call("toml::discard_comment::front");} + reference back() noexcept {never_call("toml::discard_comment::back");} + const_reference back() const noexcept {never_call("toml::discard_comment::back");} + + pointer data() noexcept {return nullptr;} + const_pointer data() const noexcept {return nullptr;} + + iterator begin() noexcept {return iterator{};} + iterator end() noexcept {return iterator{};} + const_iterator begin() const noexcept {return const_iterator{};} + const_iterator end() const noexcept {return const_iterator{};} + const_iterator cbegin() const noexcept {return const_iterator{};} + const_iterator cend() const noexcept {return const_iterator{};} + + reverse_iterator rbegin() noexcept {return iterator{};} + reverse_iterator rend() noexcept {return iterator{};} + const_reverse_iterator rbegin() const noexcept {return const_iterator{};} + const_reverse_iterator rend() const noexcept {return const_iterator{};} + const_reverse_iterator crbegin() const noexcept {return const_iterator{};} + const_reverse_iterator crend() const noexcept {return const_iterator{};} + + private: + + [[noreturn]] static void never_call(const char *const this_function) + { +#if __has_builtin(__builtin_unreachable) + __builtin_unreachable(); +#endif + throw std::logic_error{this_function}; + } +}; + +inline bool operator==(const discard_comments&, const discard_comments&) noexcept {return true;} +inline bool operator!=(const discard_comments&, const discard_comments&) noexcept {return false;} +inline bool operator< (const discard_comments&, const discard_comments&) noexcept {return false;} +inline bool operator<=(const discard_comments&, const discard_comments&) noexcept {return true;} +inline bool operator> (const discard_comments&, const discard_comments&) noexcept {return false;} +inline bool operator>=(const discard_comments&, const discard_comments&) noexcept {return true;} + +inline void swap(const discard_comments&, const discard_comments&) noexcept {return;} + +inline std::ostream& operator<<(std::ostream& os, const discard_comments&) {return os;} + +} // toml11 +#endif // TOML11_COMMENTS_FWD_HPP + +#if ! defined(TOML11_COMPILE_SOURCES) +#ifndef TOML11_COMMENTS_IMPL_HPP +#define TOML11_COMMENTS_IMPL_HPP + + +namespace toml +{ + +TOML11_INLINE bool operator==(const preserve_comments& lhs, const preserve_comments& rhs) {return lhs.comments == rhs.comments;} +TOML11_INLINE bool operator!=(const preserve_comments& lhs, const preserve_comments& rhs) {return lhs.comments != rhs.comments;} +TOML11_INLINE bool operator< (const preserve_comments& lhs, const preserve_comments& rhs) {return lhs.comments < rhs.comments;} +TOML11_INLINE bool operator<=(const preserve_comments& lhs, const preserve_comments& rhs) {return lhs.comments <= rhs.comments;} +TOML11_INLINE bool operator> (const preserve_comments& lhs, const preserve_comments& rhs) {return lhs.comments > rhs.comments;} +TOML11_INLINE bool operator>=(const preserve_comments& lhs, const preserve_comments& rhs) {return lhs.comments >= rhs.comments;} + +TOML11_INLINE void swap(preserve_comments& lhs, preserve_comments& rhs) +{ + lhs.swap(rhs); + return; +} +TOML11_INLINE void swap(preserve_comments& lhs, std::vector& rhs) +{ + lhs.comments.swap(rhs); + return; +} +TOML11_INLINE void swap(std::vector& lhs, preserve_comments& rhs) +{ + lhs.swap(rhs.comments); + return; +} + +TOML11_INLINE std::ostream& operator<<(std::ostream& os, const preserve_comments& com) +{ + for(const auto& c : com) + { + if(c.front() != '#') + { + os << '#'; + } + os << c << '\n'; + } + return os; +} + +} // toml11 +#endif // TOML11_COMMENTS_IMPL_HPP +#endif + +#endif // TOML11_COMMENTS_HPP +#ifndef TOML11_COLOR_HPP +#define TOML11_COLOR_HPP + +#ifndef TOML11_COLOR_FWD_HPP +#define TOML11_COLOR_FWD_HPP + +#include + +#ifdef TOML11_COLORIZE_ERROR_MESSAGE +#define TOML11_ERROR_MESSAGE_COLORIZED true +#else +#define TOML11_ERROR_MESSAGE_COLORIZED false +#endif + +#ifdef TOML11_USE_THREAD_LOCAL_COLORIZATION +#define TOML11_THREAD_LOCAL_COLORIZATION thread_local +#else +#define TOML11_THREAD_LOCAL_COLORIZATION +#endif + +namespace toml +{ +namespace color +{ +// put ANSI escape sequence to ostream +inline namespace ansi +{ +namespace detail +{ + +// Control color mode globally +class color_mode +{ + public: + + void enable() noexcept + { + should_color_ = true; + } + void disable() noexcept + { + should_color_ = false; + } + bool should_color() const noexcept + { + return should_color_; + } + + private: + + bool should_color_ = TOML11_ERROR_MESSAGE_COLORIZED; +}; + +inline color_mode& color_status() noexcept +{ + static TOML11_THREAD_LOCAL_COLORIZATION color_mode status; + return status; +} + +} // detail + +std::ostream& reset (std::ostream& os); +std::ostream& bold (std::ostream& os); +std::ostream& grey (std::ostream& os); +std::ostream& gray (std::ostream& os); +std::ostream& red (std::ostream& os); +std::ostream& green (std::ostream& os); +std::ostream& yellow (std::ostream& os); +std::ostream& blue (std::ostream& os); +std::ostream& magenta(std::ostream& os); +std::ostream& cyan (std::ostream& os); +std::ostream& white (std::ostream& os); + +} // ansi + +inline void enable() +{ + return detail::color_status().enable(); +} +inline void disable() +{ + return detail::color_status().disable(); +} +inline bool should_color() +{ + return detail::color_status().should_color(); +} + +} // color +} // toml +#endif // TOML11_COLOR_FWD_HPP + +#if ! defined(TOML11_COMPILE_SOURCES) +#ifndef TOML11_COLOR_IMPL_HPP +#define TOML11_COLOR_IMPL_HPP + + +#include + +namespace toml +{ +namespace color +{ +// put ANSI escape sequence to ostream +inline namespace ansi +{ + +TOML11_INLINE std::ostream& reset(std::ostream& os) +{ + if(detail::color_status().should_color()) {os << "\033[00m";} + return os; +} +TOML11_INLINE std::ostream& bold(std::ostream& os) +{ + if(detail::color_status().should_color()) {os << "\033[01m";} + return os; +} +TOML11_INLINE std::ostream& grey(std::ostream& os) +{ + if(detail::color_status().should_color()) {os << "\033[30m";} + return os; +} +TOML11_INLINE std::ostream& gray(std::ostream& os) +{ + if(detail::color_status().should_color()) {os << "\033[30m";} + return os; +} +TOML11_INLINE std::ostream& red(std::ostream& os) +{ + if(detail::color_status().should_color()) {os << "\033[31m";} + return os; +} +TOML11_INLINE std::ostream& green(std::ostream& os) +{ + if(detail::color_status().should_color()) {os << "\033[32m";} + return os; +} +TOML11_INLINE std::ostream& yellow(std::ostream& os) +{ + if(detail::color_status().should_color()) {os << "\033[33m";} + return os; +} +TOML11_INLINE std::ostream& blue(std::ostream& os) +{ + if(detail::color_status().should_color()) {os << "\033[34m";} + return os; +} +TOML11_INLINE std::ostream& magenta(std::ostream& os) +{ + if(detail::color_status().should_color()) {os << "\033[35m";} + return os; +} +TOML11_INLINE std::ostream& cyan (std::ostream& os) +{ + if(detail::color_status().should_color()) {os << "\033[36m";} + return os; +} +TOML11_INLINE std::ostream& white (std::ostream& os) +{ + if(detail::color_status().should_color()) {os << "\033[37m";} + return os; +} + +} // ansi +} // color +} // toml +#endif // TOML11_COLOR_IMPL_HPP +#endif + +#endif // TOML11_COLOR_HPP +#ifndef TOML11_SPEC_HPP +#define TOML11_SPEC_HPP + +#include +#include + +#include + +namespace toml +{ + +struct semantic_version +{ + constexpr semantic_version(std::uint32_t mjr, std::uint32_t mnr, std::uint32_t p) noexcept + : major{mjr}, minor{mnr}, patch{p} + {} + + std::uint32_t major; + std::uint32_t minor; + std::uint32_t patch; +}; + +constexpr inline semantic_version +make_semver(std::uint32_t mjr, std::uint32_t mnr, std::uint32_t p) noexcept +{ + return semantic_version(mjr, mnr, p); +} + +constexpr inline bool +operator==(const semantic_version& lhs, const semantic_version& rhs) noexcept +{ + return lhs.major == rhs.major && + lhs.minor == rhs.minor && + lhs.patch == rhs.patch; +} +constexpr inline bool +operator!=(const semantic_version& lhs, const semantic_version& rhs) noexcept +{ + return !(lhs == rhs); +} +constexpr inline bool +operator<(const semantic_version& lhs, const semantic_version& rhs) noexcept +{ + return lhs.major < rhs.major || + (lhs.major == rhs.major && lhs.minor < rhs.minor) || + (lhs.major == rhs.major && lhs.minor == rhs.minor && lhs.patch < rhs.patch); +} +constexpr inline bool +operator>(const semantic_version& lhs, const semantic_version& rhs) noexcept +{ + return rhs < lhs; +} +constexpr inline bool +operator<=(const semantic_version& lhs, const semantic_version& rhs) noexcept +{ + return !(lhs > rhs); +} +constexpr inline bool +operator>=(const semantic_version& lhs, const semantic_version& rhs) noexcept +{ + return !(lhs < rhs); +} + +inline std::ostream& operator<<(std::ostream& os, const semantic_version& v) +{ + os << v.major << '.' << v.minor << '.' << v.patch; + return os; +} + +inline std::string to_string(const semantic_version& v) +{ + std::ostringstream oss; + oss << v; + return oss.str(); +} + +struct spec +{ + constexpr static spec default_version() noexcept + { + return spec::v(1, 0, 0); + } + + constexpr static spec v(std::uint32_t mjr, std::uint32_t mnr, std::uint32_t p) noexcept + { + return spec(make_semver(mjr, mnr, p)); + } + + constexpr explicit spec(const semantic_version& semver) noexcept + : version{semver}, + v1_1_0_allow_control_characters_in_comments {semantic_version{1, 1, 0} <= semver}, + v1_1_0_allow_newlines_in_inline_tables {semantic_version{1, 1, 0} <= semver}, + v1_1_0_allow_trailing_comma_in_inline_tables{semantic_version{1, 1, 0} <= semver}, + v1_1_0_allow_non_english_in_bare_keys {semantic_version{1, 1, 0} <= semver}, + v1_1_0_add_escape_sequence_e {semantic_version{1, 1, 0} <= semver}, + v1_1_0_add_escape_sequence_x {semantic_version{1, 1, 0} <= semver}, + v1_1_0_make_seconds_optional {semantic_version{1, 1, 0} <= semver}, + ext_hex_float {false}, + ext_num_suffix{false}, + ext_null_value{false} + {} + + semantic_version version; // toml version + + // diff from v1.0.0 -> v1.1.0 + bool v1_1_0_allow_control_characters_in_comments; + bool v1_1_0_allow_newlines_in_inline_tables; + bool v1_1_0_allow_trailing_comma_in_inline_tables; + bool v1_1_0_allow_non_english_in_bare_keys; + bool v1_1_0_add_escape_sequence_e; + bool v1_1_0_add_escape_sequence_x; + bool v1_1_0_make_seconds_optional; + + // library extensions + bool ext_hex_float; // allow hex float (in C++ style) + bool ext_num_suffix; // allow number suffix (in C++ style) + bool ext_null_value; // allow `null` as a value +}; + +} // namespace toml +#endif // TOML11_SPEC_HPP +#ifndef TOML11_ORDERED_MAP_HPP +#define TOML11_ORDERED_MAP_HPP + +#include +#include +#include +#include + +namespace toml +{ + +namespace detail +{ +template +struct ordered_map_ebo_container +{ + Cmp cmp_; // empty base optimization for empty Cmp type +}; +} // detail + +template, + typename Allocator = std::allocator>> +class ordered_map : detail::ordered_map_ebo_container +{ + public: + using key_type = Key; + using mapped_type = Val; + using value_type = std::pair; + + using key_compare = Cmp; + using allocator_type = Allocator; + + using container_type = std::vector; + using reference = typename container_type::reference; + using pointer = typename container_type::pointer; + using const_reference = typename container_type::const_reference; + using const_pointer = typename container_type::const_pointer; + using iterator = typename container_type::iterator; + using const_iterator = typename container_type::const_iterator; + using size_type = typename container_type::size_type; + using difference_type = typename container_type::difference_type; + + private: + + using ebo_base = detail::ordered_map_ebo_container; + + public: + + ordered_map() = default; + ~ordered_map() = default; + ordered_map(const ordered_map&) = default; + ordered_map(ordered_map&&) = default; + ordered_map& operator=(const ordered_map&) = default; + ordered_map& operator=(ordered_map&&) = default; + + ordered_map(const ordered_map& other, const Allocator& alloc) + : container_(other.container_, alloc) + {} + ordered_map(ordered_map&& other, const Allocator& alloc) + : container_(std::move(other.container_), alloc) + {} + + explicit ordered_map(const Cmp& cmp, const Allocator& alloc = Allocator()) + : ebo_base{cmp}, container_(alloc) + {} + explicit ordered_map(const Allocator& alloc) + : container_(alloc) + {} + + template + ordered_map(InputIterator first, InputIterator last, const Cmp& cmp = Cmp(), const Allocator& alloc = Allocator()) + : ebo_base{cmp}, container_(first, last, alloc) + {} + template + ordered_map(InputIterator first, InputIterator last, const Allocator& alloc) + : container_(first, last, alloc) + {} + + ordered_map(std::initializer_list v, const Cmp& cmp = Cmp(), const Allocator& alloc = Allocator()) + : ebo_base{cmp}, container_(std::move(v), alloc) + {} + ordered_map(std::initializer_list v, const Allocator& alloc) + : container_(std::move(v), alloc) + {} + ordered_map& operator=(std::initializer_list v) + { + this->container_ = std::move(v); + return *this; + } + + iterator begin() noexcept {return container_.begin();} + iterator end() noexcept {return container_.end();} + const_iterator begin() const noexcept {return container_.begin();} + const_iterator end() const noexcept {return container_.end();} + const_iterator cbegin() const noexcept {return container_.cbegin();} + const_iterator cend() const noexcept {return container_.cend();} + + bool empty() const noexcept {return container_.empty();} + std::size_t size() const noexcept {return container_.size();} + std::size_t max_size() const noexcept {return container_.max_size();} + + void clear() {container_.clear();} + + void push_back(const value_type& v) + { + if(this->contains(v.first)) + { + throw std::out_of_range("ordered_map: value already exists"); + } + container_.push_back(v); + } + void push_back(value_type&& v) + { + if(this->contains(v.first)) + { + throw std::out_of_range("ordered_map: value already exists"); + } + container_.push_back(std::move(v)); + } + void emplace_back(key_type k, mapped_type v) + { + if(this->contains(k)) + { + throw std::out_of_range("ordered_map: value already exists"); + } + container_.emplace_back(std::move(k), std::move(v)); + } + void pop_back() {container_.pop_back();} + + void insert(value_type kv) + { + if(this->contains(kv.first)) + { + throw std::out_of_range("ordered_map: value already exists"); + } + container_.push_back(std::move(kv)); + } + void emplace(key_type k, mapped_type v) + { + if(this->contains(k)) + { + throw std::out_of_range("ordered_map: value already exists"); + } + container_.emplace_back(std::move(k), std::move(v)); + } + + std::size_t count(const key_type& key) const + { + if(this->find(key) != this->end()) + { + return 1; + } + else + { + return 0; + } + } + bool contains(const key_type& key) const + { + return this->find(key) != this->end(); + } + iterator find(const key_type& key) noexcept + { + return std::find_if(this->begin(), this->end(), + [&key, this](const value_type& v) {return this->cmp_(v.first, key);}); + } + const_iterator find(const key_type& key) const noexcept + { + return std::find_if(this->begin(), this->end(), + [&key, this](const value_type& v) {return this->cmp_(v.first, key);}); + } + + mapped_type& at(const key_type& k) + { + const auto iter = this->find(k); + if(iter == this->end()) + { + throw std::out_of_range("ordered_map: no such element"); + } + return iter->second; + } + mapped_type const& at(const key_type& k) const + { + const auto iter = this->find(k); + if(iter == this->end()) + { + throw std::out_of_range("ordered_map: no such element"); + } + return iter->second; + } + + mapped_type& operator[](const key_type& k) + { + const auto iter = this->find(k); + if(iter == this->end()) + { + this->container_.emplace_back(k, mapped_type{}); + return this->container_.back().second; + } + return iter->second; + } + + mapped_type const& operator[](const key_type& k) const + { + const auto iter = this->find(k); + if(iter == this->end()) + { + throw std::out_of_range("ordered_map: no such element"); + } + return iter->second; + } + + key_compare key_comp() const {return this->cmp_;} + + void swap(ordered_map& other) + { + container_.swap(other.container_); + } + + private: + + container_type container_; +}; + +template +bool operator==(const ordered_map& lhs, const ordered_map& rhs) +{ + return lhs.size() == rhs.size() && std::equal(lhs.begin(), lhs.end(), rhs.begin()); +} +template +bool operator!=(const ordered_map& lhs, const ordered_map& rhs) +{ + return !(lhs == rhs); +} +template +bool operator<(const ordered_map& lhs, const ordered_map& rhs) +{ + return std::lexicographical_compare(lhs.begin(), lhs.end(), rhs.begin(), rhs.end()); +} +template +bool operator>(const ordered_map& lhs, const ordered_map& rhs) +{ + return rhs < lhs; +} +template +bool operator<=(const ordered_map& lhs, const ordered_map& rhs) +{ + return !(lhs > rhs); +} +template +bool operator>=(const ordered_map& lhs, const ordered_map& rhs) +{ + return !(lhs < rhs); +} + +template +void swap(ordered_map& lhs, ordered_map& rhs) +{ + lhs.swap(rhs); + return; +} + + +} // toml +#endif // TOML11_ORDERED_MAP_HPP +#ifndef TOML11_INTO_HPP +#define TOML11_INTO_HPP + +namespace toml +{ + +template +struct into; +// { +// static toml::value into_toml(const T& user_defined_type) +// { +// // User-defined conversions ... +// } +// }; + +} // toml +#endif // TOML11_INTO_HPP +#ifndef TOML11_FROM_HPP +#define TOML11_FROM_HPP + +namespace toml +{ + +template +struct from; +// { +// static T from_toml(const toml::value& v) +// { +// // User-defined conversions ... +// } +// }; + +} // toml +#endif // TOML11_FROM_HPP +#ifndef TOML11_TRAITS_HPP +#define TOML11_TRAITS_HPP + + +#include +#include +#include +#include +#include +#include +#include + +#if defined(TOML11_HAS_STRING_VIEW) +#include +#endif + +namespace toml +{ +template +class basic_value; + +namespace detail +{ +// --------------------------------------------------------------------------- +// check whether type T is a kind of container/map class + +struct has_iterator_impl +{ + template static std::true_type check(typename T::iterator*); + template static std::false_type check(...); +}; +struct has_value_type_impl +{ + template static std::true_type check(typename T::value_type*); + template static std::false_type check(...); +}; +struct has_key_type_impl +{ + template static std::true_type check(typename T::key_type*); + template static std::false_type check(...); +}; +struct has_mapped_type_impl +{ + template static std::true_type check(typename T::mapped_type*); + template static std::false_type check(...); +}; +struct has_reserve_method_impl +{ + template static std::false_type check(...); + template static std::true_type check( + decltype(std::declval().reserve(std::declval()))*); +}; +struct has_push_back_method_impl +{ + template static std::false_type check(...); + template static std::true_type check( + decltype(std::declval().push_back(std::declval()))*); +}; +struct is_comparable_impl +{ + template static std::false_type check(...); + template static std::true_type check( + decltype(std::declval() < std::declval())*); +}; + +struct has_from_toml_method_impl +{ + template + static std::true_type check( + decltype(std::declval().from_toml(std::declval<::toml::basic_value>()))*); + + template + static std::false_type check(...); +}; +struct has_into_toml_method_impl +{ + template + static std::true_type check(decltype(std::declval().into_toml())*); + template + static std::false_type check(...); +}; + +struct has_template_into_toml_method_impl +{ + template + static std::true_type check(decltype(std::declval().template into_toml())*); + template + static std::false_type check(...); +}; + +struct has_specialized_from_impl +{ + template + static std::false_type check(...); + template)> + static std::true_type check(::toml::from*); +}; +struct has_specialized_into_impl +{ + template + static std::false_type check(...); + template)> + static std::true_type check(::toml::into*); +}; + + +/// Intel C++ compiler can not use decltype in parent class declaration, here +/// is a hack to work around it. https://stackoverflow.com/a/23953090/4692076 +#ifdef __INTEL_COMPILER +#define decltype(...) std::enable_if::type +#endif + +template +struct has_iterator: decltype(has_iterator_impl::check(nullptr)){}; +template +struct has_value_type: decltype(has_value_type_impl::check(nullptr)){}; +template +struct has_key_type: decltype(has_key_type_impl::check(nullptr)){}; +template +struct has_mapped_type: decltype(has_mapped_type_impl::check(nullptr)){}; +template +struct has_reserve_method: decltype(has_reserve_method_impl::check(nullptr)){}; +template +struct has_push_back_method: decltype(has_push_back_method_impl::check(nullptr)){}; +template +struct is_comparable: decltype(is_comparable_impl::check(nullptr)){}; + +template +struct has_from_toml_method: decltype(has_from_toml_method_impl::check(nullptr)){}; + +template +struct has_into_toml_method: decltype(has_into_toml_method_impl::check(nullptr)){}; + +template +struct has_template_into_toml_method: decltype(has_template_into_toml_method_impl::check(nullptr)){}; + +template +struct has_specialized_from: decltype(has_specialized_from_impl::check(nullptr)){}; +template +struct has_specialized_into: decltype(has_specialized_into_impl::check(nullptr)){}; + +#ifdef __INTEL_COMPILER +#undef decltype +#endif + +// --------------------------------------------------------------------------- +// type checkers + +template struct is_std_pair_impl : std::false_type{}; +template +struct is_std_pair_impl> : std::true_type{}; +template +using is_std_pair = is_std_pair_impl>; + +template struct is_std_tuple_impl : std::false_type{}; +template +struct is_std_tuple_impl> : std::true_type{}; +template +using is_std_tuple = is_std_tuple_impl>; + +template struct is_std_array_impl : std::false_type{}; +template +struct is_std_array_impl> : std::true_type{}; +template +using is_std_array = is_std_array_impl>; + +template struct is_std_forward_list_impl : std::false_type{}; +template +struct is_std_forward_list_impl> : std::true_type{}; +template +using is_std_forward_list = is_std_forward_list_impl>; + +template struct is_std_basic_string_impl : std::false_type{}; +template +struct is_std_basic_string_impl> : std::true_type{}; +template +using is_std_basic_string = is_std_basic_string_impl>; + +template struct is_1byte_std_basic_string_impl : std::false_type{}; +template +struct is_1byte_std_basic_string_impl> + : std::integral_constant {}; +template +using is_1byte_std_basic_string = is_std_basic_string_impl>; + +#if defined(TOML11_HAS_STRING_VIEW) +template struct is_std_basic_string_view_impl : std::false_type{}; +template +struct is_std_basic_string_view_impl> : std::true_type{}; +template +using is_std_basic_string_view = is_std_basic_string_view_impl>; + +template +struct is_string_view_of : std::false_type {}; +template +struct is_string_view_of, std::basic_string> : std::true_type {}; +#endif + +template struct is_chrono_duration_impl: std::false_type{}; +template +struct is_chrono_duration_impl>: std::true_type{}; +template +using is_chrono_duration = is_chrono_duration_impl>; + +template +struct is_map_impl : cxx::conjunction< // map satisfies all the following conditions + has_iterator, // has T::iterator + has_value_type, // has T::value_type + has_key_type, // has T::key_type + has_mapped_type // has T::mapped_type + >{}; +template +using is_map = is_map_impl>; + +template +struct is_container_impl : cxx::conjunction< + cxx::negation>, // not a map + cxx::negation>, // not a std::string +#ifdef TOML11_HAS_STRING_VIEW + cxx::negation>, // not a std::string_view +#endif + has_iterator, // has T::iterator + has_value_type // has T::value_type + >{}; +template +using is_container = is_container_impl>; + +template +struct is_basic_value_impl: std::false_type{}; +template +struct is_basic_value_impl<::toml::basic_value>: std::true_type{}; +template +using is_basic_value = is_basic_value_impl>; + +}// detail +}//toml +#endif // TOML11_TRAITS_HPP +#ifndef TOML11_EXCEPTION_HPP +#define TOML11_EXCEPTION_HPP + +#include + +namespace toml +{ + +struct exception : public std::exception +{ + public: + virtual ~exception() noexcept override = default; + virtual const char* what() const noexcept override {return "";} +}; + +} // toml +#endif // TOMl11_EXCEPTION_HPP +#ifndef TOML11_RESULT_HPP +#define TOML11_RESULT_HPP + + +#include +#include +#include +#include + +#include + +namespace toml +{ + +struct bad_result_access final : public ::toml::exception +{ + public: + explicit bad_result_access(std::string what_arg) + : what_(std::move(what_arg)) + {} + ~bad_result_access() noexcept override = default; + const char* what() const noexcept override {return what_.c_str();} + + private: + std::string what_; +}; + +// ----------------------------------------------------------------------------- + +template +struct success +{ + static_assert( ! std::is_same::value, ""); + + using value_type = T; + + explicit success(value_type v) + noexcept(std::is_nothrow_move_constructible::value) + : value(std::move(v)) + {} + + template, T>::value, + std::nullptr_t> = nullptr> + explicit success(U&& v): value(std::forward(v)) {} + + template + explicit success(success v): value(std::move(v.value)) {} + + ~success() = default; + success(const success&) = default; + success(success&&) = default; + success& operator=(const success&) = default; + success& operator=(success&&) = default; + + value_type& get() noexcept {return value;} + value_type const& get() const noexcept {return value;} + + private: + + value_type value; +}; + +template +struct success> +{ + static_assert( ! std::is_same::value, ""); + + using value_type = T; + + explicit success(std::reference_wrapper v) noexcept + : value(std::move(v)) + {} + + ~success() = default; + success(const success&) = default; + success(success&&) = default; + success& operator=(const success&) = default; + success& operator=(success&&) = default; + + value_type& get() noexcept {return value.get();} + value_type const& get() const noexcept {return value.get();} + + private: + + std::reference_wrapper value; +}; + +template +success::type> ok(T&& v) +{ + return success::type>(std::forward(v)); +} +template +success ok(const char (&literal)[N]) +{ + return success(std::string(literal)); +} + +// ----------------------------------------------------------------------------- + +template +struct failure +{ + using value_type = T; + + explicit failure(value_type v) + noexcept(std::is_nothrow_move_constructible::value) + : value(std::move(v)) + {} + + template, T>::value, + std::nullptr_t> = nullptr> + explicit failure(U&& v): value(std::forward(v)) {} + + template + explicit failure(failure v): value(std::move(v.value)) {} + + ~failure() = default; + failure(const failure&) = default; + failure(failure&&) = default; + failure& operator=(const failure&) = default; + failure& operator=(failure&&) = default; + + value_type& get() noexcept {return value;} + value_type const& get() const noexcept {return value;} + + private: + + value_type value; +}; + +template +struct failure> +{ + using value_type = T; + + explicit failure(std::reference_wrapper v) noexcept + : value(std::move(v)) + {} + + ~failure() = default; + failure(const failure&) = default; + failure(failure&&) = default; + failure& operator=(const failure&) = default; + failure& operator=(failure&&) = default; + + value_type& get() noexcept {return value.get();} + value_type const& get() const noexcept {return value.get();} + + private: + + std::reference_wrapper value; +}; + +template +failure::type> err(T&& v) +{ + return failure::type>(std::forward(v)); +} + +template +failure err(const char (&literal)[N]) +{ + return failure(std::string(literal)); +} + +/* ============================================================================ + * _ _ + * _ _ ___ ____ _| | |_ + * | '_/ -_|_-< || | | _| + * |_| \___/__/\_,_|_|\__| */ -#ifndef TOML_FOR_MODERN_CPP -#define TOML_FOR_MODERN_CPP +template +struct result +{ + using success_type = success; + using failure_type = failure; + using value_type = typename success_type::value_type; + using error_type = typename failure_type::value_type; -#define TOML11_VERSION_MAJOR 3 -#define TOML11_VERSION_MINOR 8 -#define TOML11_VERSION_PATCH 1 + result(success_type s): is_ok_(true), succ_(std::move(s)) {} + result(failure_type f): is_ok_(false), fail_(std::move(f)) {} -#include "toml/parser.hpp" -#include "toml/literal.hpp" -#include "toml/serializer.hpp" -#include "toml/get.hpp" -#include "toml/macros.hpp" + template, value_type>>, + std::is_convertible, value_type> + >::value, std::nullptr_t> = nullptr> + result(success s): is_ok_(true), succ_(std::move(s.value)) {} -#endif// TOML_FOR_MODERN_CPP + template, error_type>>, + std::is_convertible, error_type> + >::value, std::nullptr_t> = nullptr> + result(failure f): is_ok_(false), fail_(std::move(f.value)) {} + + result& operator=(success_type s) + { + this->cleanup(); + this->is_ok_ = true; + auto tmp = ::new(std::addressof(this->succ_)) success_type(std::move(s)); + assert(tmp == std::addressof(this->succ_)); + (void)tmp; + return *this; + } + result& operator=(failure_type f) + { + this->cleanup(); + this->is_ok_ = false; + auto tmp = ::new(std::addressof(this->fail_)) failure_type(std::move(f)); + assert(tmp == std::addressof(this->fail_)); + (void)tmp; + return *this; + } + + template + result& operator=(success s) + { + this->cleanup(); + this->is_ok_ = true; + auto tmp = ::new(std::addressof(this->succ_)) success_type(std::move(s.value)); + assert(tmp == std::addressof(this->succ_)); + (void)tmp; + return *this; + } + template + result& operator=(failure f) + { + this->cleanup(); + this->is_ok_ = false; + auto tmp = ::new(std::addressof(this->fail_)) failure_type(std::move(f.value)); + assert(tmp == std::addressof(this->fail_)); + (void)tmp; + return *this; + } + + ~result() noexcept {this->cleanup();} + + result(const result& other): is_ok_(other.is_ok()) + { + if(other.is_ok()) + { + auto tmp = ::new(std::addressof(this->succ_)) success_type(other.succ_); + assert(tmp == std::addressof(this->succ_)); + (void)tmp; + } + else + { + auto tmp = ::new(std::addressof(this->fail_)) failure_type(other.fail_); + assert(tmp == std::addressof(this->fail_)); + (void)tmp; + } + } + result(result&& other): is_ok_(other.is_ok()) + { + if(other.is_ok()) + { + auto tmp = ::new(std::addressof(this->succ_)) success_type(std::move(other.succ_)); + assert(tmp == std::addressof(this->succ_)); + (void)tmp; + } + else + { + auto tmp = ::new(std::addressof(this->fail_)) failure_type(std::move(other.fail_)); + assert(tmp == std::addressof(this->fail_)); + (void)tmp; + } + } + + result& operator=(const result& other) + { + this->cleanup(); + if(other.is_ok()) + { + auto tmp = ::new(std::addressof(this->succ_)) success_type(other.succ_); + assert(tmp == std::addressof(this->succ_)); + (void)tmp; + } + else + { + auto tmp = ::new(std::addressof(this->fail_)) failure_type(other.fail_); + assert(tmp == std::addressof(this->fail_)); + (void)tmp; + } + is_ok_ = other.is_ok(); + return *this; + } + result& operator=(result&& other) + { + this->cleanup(); + if(other.is_ok()) + { + auto tmp = ::new(std::addressof(this->succ_)) success_type(std::move(other.succ_)); + assert(tmp == std::addressof(this->succ_)); + (void)tmp; + } + else + { + auto tmp = ::new(std::addressof(this->fail_)) failure_type(std::move(other.fail_)); + assert(tmp == std::addressof(this->fail_)); + (void)tmp; + } + is_ok_ = other.is_ok(); + return *this; + } + + template, value_type>>, + cxx::negation, error_type>>, + std::is_convertible, value_type>, + std::is_convertible, error_type> + >::value, std::nullptr_t> = nullptr> + result(result other): is_ok_(other.is_ok()) + { + if(other.is_ok()) + { + auto tmp = ::new(std::addressof(this->succ_)) success_type(std::move(other.as_ok())); + assert(tmp == std::addressof(this->succ_)); + (void)tmp; + } + else + { + auto tmp = ::new(std::addressof(this->fail_)) failure_type(std::move(other.as_err())); + assert(tmp == std::addressof(this->fail_)); + (void)tmp; + } + } + + template, value_type>>, + cxx::negation, error_type>>, + std::is_convertible, value_type>, + std::is_convertible, error_type> + >::value, std::nullptr_t> = nullptr> + result& operator=(result other) + { + this->cleanup(); + if(other.is_ok()) + { + auto tmp = ::new(std::addressof(this->succ_)) success_type(std::move(other.as_ok())); + assert(tmp == std::addressof(this->succ_)); + (void)tmp; + } + else + { + auto tmp = ::new(std::addressof(this->fail_)) failure_type(std::move(other.as_err())); + assert(tmp == std::addressof(this->fail_)); + (void)tmp; + } + is_ok_ = other.is_ok(); + return *this; + } + + bool is_ok() const noexcept {return is_ok_;} + bool is_err() const noexcept {return !is_ok_;} + + explicit operator bool() const noexcept {return is_ok_;} + + value_type& unwrap(cxx::source_location loc = cxx::source_location::current()) + { + if(this->is_err()) + { + throw bad_result_access("toml::result: bad unwrap" + cxx::to_string(loc)); + } + return this->succ_.get(); + } + value_type const& unwrap(cxx::source_location loc = cxx::source_location::current()) const + { + if(this->is_err()) + { + throw bad_result_access("toml::result: bad unwrap" + cxx::to_string(loc)); + } + return this->succ_.get(); + } + + value_type& unwrap_or(value_type& opt) noexcept + { + if(this->is_err()) {return opt;} + return this->succ_.get(); + } + value_type const& unwrap_or(value_type const& opt) const noexcept + { + if(this->is_err()) {return opt;} + return this->succ_.get(); + } + + error_type& unwrap_err(cxx::source_location loc = cxx::source_location::current()) + { + if(this->is_ok()) + { + throw bad_result_access("toml::result: bad unwrap_err" + cxx::to_string(loc)); + } + return this->fail_.get(); + } + error_type const& unwrap_err(cxx::source_location loc = cxx::source_location::current()) const + { + if(this->is_ok()) + { + throw bad_result_access("toml::result: bad unwrap_err" + cxx::to_string(loc)); + } + return this->fail_.get(); + } + + value_type& as_ok() noexcept + { + assert(this->is_ok()); + return this->succ_.get(); + } + value_type const& as_ok() const noexcept + { + assert(this->is_ok()); + return this->succ_.get(); + } + + error_type& as_err() noexcept + { + assert(this->is_err()); + return this->fail_.get(); + } + error_type const& as_err() const noexcept + { + assert(this->is_err()); + return this->fail_.get(); + } + + private: + + void cleanup() noexcept + { +#if defined(__GNUC__) && ! defined(__clang__) +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wduplicated-branches" +#endif + + if(this->is_ok_) {this->succ_.~success_type();} + else {this->fail_.~failure_type();} + +#if defined(__GNUC__) && ! defined(__clang__) +#pragma GCC diagnostic pop +#endif + return; + } + + private: + + bool is_ok_; + union + { + success_type succ_; + failure_type fail_; + }; +}; + +// ---------------------------------------------------------------------------- + +namespace detail +{ +struct none_t {}; +inline bool operator==(const none_t&, const none_t&) noexcept {return true;} +inline bool operator!=(const none_t&, const none_t&) noexcept {return false;} +inline bool operator< (const none_t&, const none_t&) noexcept {return false;} +inline bool operator<=(const none_t&, const none_t&) noexcept {return true;} +inline bool operator> (const none_t&, const none_t&) noexcept {return false;} +inline bool operator>=(const none_t&, const none_t&) noexcept {return true;} +inline std::ostream& operator<<(std::ostream& os, const none_t&) +{ + os << "none"; + return os; +} +} // detail + +inline success ok() noexcept +{ + return success(detail::none_t{}); +} +inline failure err() noexcept +{ + return failure(detail::none_t{}); +} + +} // toml +#endif // TOML11_RESULT_HPP +#ifndef TOML11_UTILITY_HPP +#define TOML11_UTILITY_HPP + + +#include +#include + +#include +#include +#include + +namespace toml +{ +namespace detail +{ + +// to output character in an error message. +inline std::string show_char(const int c) +{ + using char_type = unsigned char; + if(std::isgraph(c)) + { + return std::string(1, static_cast(c)); + } + else + { + std::array buf; + buf.fill('\0'); + const auto r = std::snprintf(buf.data(), buf.size(), "0x%02x", c & 0xFF); + assert(r == static_cast(buf.size()) - 1); + (void) r; // Unused variable warning + auto in_hex = std::string(buf.data()); + switch(c) + { + case char_type('\0'): {in_hex += "(NUL)"; break;} + case char_type(' ') : {in_hex += "(SPACE)"; break;} + case char_type('\n'): {in_hex += "(LINE FEED)"; break;} + case char_type('\r'): {in_hex += "(CARRIAGE RETURN)"; break;} + case char_type('\t'): {in_hex += "(TAB)"; break;} + case char_type('\v'): {in_hex += "(VERTICAL TAB)"; break;} + case char_type('\f'): {in_hex += "(FORM FEED)"; break;} + case char_type('\x1B'): {in_hex += "(ESCAPE)"; break;} + default: break; + } + return in_hex; + } +} + +// --------------------------------------------------------------------------- + +template +void try_reserve_impl(Container& container, std::size_t N, std::true_type) +{ + container.reserve(N); + return; +} +template +void try_reserve_impl(Container&, std::size_t, std::false_type) noexcept +{ + return; +} + +template +void try_reserve(Container& container, std::size_t N) +{ + try_reserve_impl(container, N, has_reserve_method{}); + return; +} + +// --------------------------------------------------------------------------- + +template +result from_string(const std::string& str) +{ + T v; + std::istringstream iss(str); + iss >> v; + if(iss.fail()) + { + return err(); + } + return ok(v); +} + +// --------------------------------------------------------------------------- + +// helper function to avoid std::string(0, 'c') or std::string(iter, iter) +template +std::string make_string(Iterator first, Iterator last) +{ + if(first == last) {return "";} + return std::string(first, last); +} +inline std::string make_string(std::size_t len, char c) +{ + if(len == 0) {return "";} + return std::string(len, c); +} + +// --------------------------------------------------------------------------- + +template +struct string_conv_impl +{ + static_assert(sizeof(Char) == sizeof(char), ""); + static_assert(sizeof(Char2) == sizeof(char), ""); + + static std::basic_string invoke(std::basic_string s) + { + std::basic_string retval; + std::transform(s.begin(), s.end(), std::back_inserter(retval), + [](const Char2 c) {return static_cast(c);}); + return retval; + } + template + static std::basic_string invoke(const Char2 (&s)[N]) + { + std::basic_string retval; + // "string literal" has null-char at the end. to skip it, we use prev. + std::transform(std::begin(s), std::prev(std::end(s)), std::back_inserter(retval), + [](const Char2 c) {return static_cast(c);}); + return retval; + } +}; + +template +struct string_conv_impl +{ + static_assert(sizeof(Char) == sizeof(char), ""); + + static std::basic_string invoke(std::basic_string s) + { + return s; + } + template + static std::basic_string invoke(const Char (&s)[N]) + { + return std::basic_string(s); + } +}; + +template +cxx::enable_if_t::value, S> +string_conv(std::basic_string s) +{ + using C = typename S::value_type; + using T = typename S::traits_type; + using A = typename S::allocator_type; + return string_conv_impl::invoke(std::move(s)); +} +template +cxx::enable_if_t::value, S> +string_conv(const char (&s)[N]) +{ + using C = typename S::value_type; + using T = typename S::traits_type; + using A = typename S::allocator_type; + using C2 = char; + using T2 = std::char_traits; + using A2 = std::allocator; + + return string_conv_impl::template invoke(s); +} + +} // namespace detail +} // namespace toml +#endif // TOML11_UTILITY_HPP +#ifndef TOML11_LOCATION_HPP +#define TOML11_LOCATION_HPP + +#ifndef TOML11_LOCATION_FWD_HPP +#define TOML11_LOCATION_FWD_HPP + + +#include +#include +#include + +namespace toml +{ +namespace detail +{ + +class region; // fwd decl + +// +// To represent where we are reading in the parse functions. +// Since it "points" somewhere in the input stream, the length is always 1. +// +class location +{ + public: + + using char_type = unsigned char; // must be unsigned + using container_type = std::vector; + using difference_type = typename container_type::difference_type; // to suppress sign-conversion warning + using source_ptr = std::shared_ptr; + + public: + + location(source_ptr src, std::string src_name) + : source_(std::move(src)), source_name_(std::move(src_name)), + location_(0), line_number_(1) + {} + + location(const location&) = default; + location(location&&) = default; + location& operator=(const location&) = default; + location& operator=(location&&) = default; + ~location() = default; + + void advance(std::size_t n = 1) noexcept; + void retrace(std::size_t n = 1) noexcept; + + bool is_ok() const noexcept { return static_cast(this->source_); } + + bool eof() const noexcept; + char_type current() const; + + char_type peek(); + + std::size_t get_location() const noexcept + { + return this->location_; + } + void set_location(const std::size_t loc) noexcept; + + std::size_t line_number() const noexcept + { + return this->line_number_; + } + std::string get_line() const; + std::size_t column_number() const noexcept; + + source_ptr const& source() const noexcept {return this->source_;} + std::string const& source_name() const noexcept {return this->source_name_;} + + private: + + void advance_line_number(const std::size_t n); + void retrace_line_number(const std::size_t n); + + private: + + friend region; + + private: + + source_ptr source_; + std::string source_name_; + std::size_t location_; // std::vector<>::difference_type is signed + std::size_t line_number_; +}; + +bool operator==(const location& lhs, const location& rhs) noexcept; +bool operator!=(const location& lhs, const location& rhs); + +location prev(const location& loc); +location next(const location& loc); +location make_temporary_location(const std::string& str) noexcept; + +template +result +find_if(const location& first, const location& last, const F& func) noexcept +{ + if(first.source() != last.source()) { return err(); } + if(first.get_location() >= last.get_location()) { return err(); } + + auto loc = first; + while(loc.get_location() != last.get_location()) + { + if(func(loc.current())) + { + return ok(loc); + } + loc.advance(); + } + return err(); +} + +template +result +rfind_if(location first, const location& last, const F& func) +{ + if(first.source() != last.source()) { return err(); } + if(first.get_location() >= last.get_location()) { return err(); } + + auto loc = last; + while(loc.get_location() != first.get_location()) + { + if(func(loc.current())) + { + return ok(loc); + } + loc.retrace(); + } + if(func(first.current())) + { + return ok(first); + } + return err(); +} + +result find(const location& first, const location& last, + const location::char_type val); +result rfind(const location& first, const location& last, + const location::char_type val); + +std::size_t count(const location& first, const location& last, + const location::char_type& c); + +} // detail +} // toml +#endif // TOML11_LOCATION_FWD_HPP + +#if ! defined(TOML11_COMPILE_SOURCES) +#ifndef TOML11_LOCATION_IMPL_HPP +#define TOML11_LOCATION_IMPL_HPP + + +namespace toml +{ +namespace detail +{ + +TOML11_INLINE void location::advance(std::size_t n) noexcept +{ + assert(this->is_ok()); + if(this->location_ + n < this->source_->size()) + { + this->advance_line_number(n); + this->location_ += n; + } + else + { + this->advance_line_number(this->source_->size() - this->location_); + this->location_ = this->source_->size(); + } +} +TOML11_INLINE void location::retrace(std::size_t n) noexcept +{ + assert(this->is_ok()); + if(this->location_ < n) + { + this->location_ = 0; + this->line_number_ = 1; + } + else + { + this->retrace_line_number(n); + this->location_ -= n; + } +} + +TOML11_INLINE bool location::eof() const noexcept +{ + assert(this->is_ok()); + return this->location_ >= this->source_->size(); +} +TOML11_INLINE location::char_type location::current() const +{ + assert(this->is_ok()); + if(this->eof()) {return '\0';} + + assert(this->location_ < this->source_->size()); + return this->source_->at(this->location_); +} + +TOML11_INLINE location::char_type location::peek() +{ + assert(this->is_ok()); + if(this->location_ >= this->source_->size()) + { + return '\0'; + } + else + { + return this->source_->at(this->location_ + 1); + } +} + +TOML11_INLINE void location::set_location(const std::size_t loc) noexcept +{ + if(this->location_ == loc) + { + return ; + } + + if(loc == 0) + { + this->line_number_ = 1; + } + else if(this->location_ < loc) + { + const auto d = loc - this->location_; + this->advance_line_number(d); + } + else + { + const auto d = this->location_ - loc; + this->retrace_line_number(d); + } + this->location_ = loc; +} + +TOML11_INLINE std::string location::get_line() const +{ + assert(this->is_ok()); + const auto iter = std::next(this->source_->cbegin(), static_cast(this->location_)); + const auto riter = cxx::make_reverse_iterator(iter); + + const auto prev = std::find(riter, this->source_->crend(), char_type('\n')); + const auto next = std::find(iter, this->source_->cend(), char_type('\n')); + + return make_string(std::next(prev.base()), next); +} +TOML11_INLINE std::size_t location::column_number() const noexcept +{ + assert(this->is_ok()); + const auto iter = std::next(this->source_->cbegin(), static_cast(this->location_)); + const auto riter = cxx::make_reverse_iterator(iter); + const auto prev = std::find(riter, this->source_->crend(), char_type('\n')); + + assert(prev.base() <= iter); + return static_cast(std::distance(prev.base(), iter) + 1); // 1-origin +} + + +TOML11_INLINE void location::advance_line_number(const std::size_t n) +{ + assert(this->is_ok()); + assert(this->location_ + n <= this->source_->size()); + + const auto iter = this->source_->cbegin(); + this->line_number_ += static_cast(std::count( + std::next(iter, static_cast(this->location_)), + std::next(iter, static_cast(this->location_ + n)), + char_type('\n'))); + + return; +} +TOML11_INLINE void location::retrace_line_number(const std::size_t n) +{ + assert(this->is_ok()); + assert(n <= this->location_); // loc - n >= 0 + + const auto iter = this->source_->cbegin(); + const auto dline_num = static_cast(std::count( + std::next(iter, static_cast(this->location_ - n)), + std::next(iter, static_cast(this->location_)), + char_type('\n'))); + + if(this->line_number_ <= dline_num) + { + this->line_number_ = 1; + } + else + { + this->line_number_ -= dline_num; + } + return; +} + +TOML11_INLINE bool operator==(const location& lhs, const location& rhs) noexcept +{ + if( ! lhs.is_ok() || ! rhs.is_ok()) + { + return (!lhs.is_ok()) && (!rhs.is_ok()); + } + return lhs.source() == rhs.source() && + lhs.source_name() == rhs.source_name() && + lhs.get_location() == rhs.get_location(); +} +TOML11_INLINE bool operator!=(const location& lhs, const location& rhs) +{ + return !(lhs == rhs); +} + +TOML11_INLINE location prev(const location& loc) +{ + location p(loc); + p.retrace(1); + return p; +} +TOML11_INLINE location next(const location& loc) +{ + location p(loc); + p.advance(1); + return p; +} + +TOML11_INLINE location make_temporary_location(const std::string& str) noexcept +{ + location::container_type cont(str.size()); + std::transform(str.begin(), str.end(), cont.begin(), + [](const std::string::value_type& c) { + return cxx::bit_cast(c); + }); + return location(std::make_shared( + std::move(cont)), "internal temporary"); +} + +TOML11_INLINE result +find(const location& first, const location& last, const location::char_type val) +{ + return find_if(first, last, [val](const location::char_type c) { + return c == val; + }); +} +TOML11_INLINE result +rfind(const location& first, const location& last, const location::char_type val) +{ + return rfind_if(first, last, [val](const location::char_type c) { + return c == val; + }); +} + +TOML11_INLINE std::size_t +count(const location& first, const location& last, const location::char_type& c) +{ + if(first.source() != last.source()) { return 0; } + if(first.get_location() >= last.get_location()) { return 0; } + + auto loc = first; + std::size_t num = 0; + while(loc.get_location() != last.get_location()) + { + if(loc.current() == c) + { + num += 1; + } + loc.advance(); + } + return num; +} + +} // detail +} // toml +#endif // TOML11_LOCATION_HPP +#endif + +#endif // TOML11_LOCATION_HPP +#ifndef TOML11_REGION_HPP +#define TOML11_REGION_HPP + +#ifndef TOML11_REGION_FWD_HPP +#define TOML11_REGION_FWD_HPP + + +#include +#include + +#include + +namespace toml +{ +namespace detail +{ + +// +// To represent where is a toml::value defined, or where does an error occur. +// Stored in toml::value. source_location will be constructed based on this. +// +class region +{ + public: + + using char_type = location::char_type; + using container_type = location::container_type; + using difference_type = location::difference_type; + using source_ptr = location::source_ptr; + + using iterator = typename container_type::iterator; + using const_iterator = typename container_type::const_iterator; + + public: + + // a value that is constructed manually does not have input stream info + region() + : source_(nullptr), source_name_(""), length_(0), + first_(0), first_line_(0), first_column_(0), last_(0), last_line_(0), + last_column_(0) + {} + + // a value defined in [first, last). + // Those source must be the same. Instread, `region` does not make sense. + region(const location& first, const location& last); + + // shorthand of [loc, loc+1) + explicit region(const location& loc); + + ~region() = default; + region(const region&) = default; + region(region&&) = default; + region& operator=(const region&) = default; + region& operator=(region&&) = default; + + bool is_ok() const noexcept { return static_cast(this->source_); } + + operator bool() const noexcept { return this->is_ok(); } + + std::size_t length() const noexcept {return this->length_;} + + std::size_t first_line_number() const noexcept + { + return this->first_line_; + } + std::size_t first_column_number() const noexcept + { + return this->first_column_; + } + std::size_t last_line_number() const noexcept + { + return this->last_line_; + } + std::size_t last_column_number() const noexcept + { + return this->last_column_; + } + + char_type at(std::size_t i) const; + + const_iterator begin() const noexcept; + const_iterator end() const noexcept; + const_iterator cbegin() const noexcept; + const_iterator cend() const noexcept; + + std::string as_string() const; + std::vector as_lines() const; + + source_ptr const& source() const noexcept {return this->source_;} + std::string const& source_name() const noexcept {return this->source_name_;} + + private: + + source_ptr source_; + std::string source_name_; + std::size_t length_; + std::size_t first_; + std::size_t first_line_; + std::size_t first_column_; + std::size_t last_; + std::size_t last_line_; + std::size_t last_column_; +}; + +} // namespace detail +} // namespace toml +#endif // TOML11_REGION_FWD_HPP + +#if ! defined(TOML11_COMPILE_SOURCES) +#ifndef TOML11_REGION_IMPL_HPP +#define TOML11_REGION_IMPL_HPP + + +#include +#include +#include +#include +#include +#include + +namespace toml +{ +namespace detail +{ + +// a value defined in [first, last). +// Those source must be the same. Instread, `region` does not make sense. +TOML11_INLINE region::region(const location& first, const location& last) + : source_(first.source()), source_name_(first.source_name()), + length_(last.get_location() - first.get_location()), + first_(first.get_location()), + first_line_(first.line_number()), + first_column_(first.column_number()), + last_(last.get_location()), + last_line_(last.line_number()), + last_column_(last.column_number()) +{ + assert(first.source() == last.source()); + assert(first.source_name() == last.source_name()); +} + + // shorthand of [loc, loc+1) +TOML11_INLINE region::region(const location& loc) + : source_(loc.source()), source_name_(loc.source_name()), length_(0), + first_line_(0), first_column_(0), last_line_(0), last_column_(0) +{ + // if the file ends with LF, the resulting region points no char. + if(loc.eof()) + { + if(loc.get_location() == 0) + { + this->length_ = 0; + this->first_ = 0; + this->first_line_ = 0; + this->first_column_ = 0; + this->last_ = 0; + this->last_line_ = 0; + this->last_column_ = 0; + } + else + { + const auto first = prev(loc); + this->first_ = first.get_location(); + this->first_line_ = first.line_number(); + this->first_column_ = first.column_number(); + this->last_ = loc.get_location(); + this->last_line_ = loc.line_number(); + this->last_column_ = loc.column_number(); + this->length_ = 1; + } + } + else + { + this->first_ = loc.get_location(); + this->first_line_ = loc.line_number(); + this->first_column_ = loc.column_number(); + this->last_ = loc.get_location() + 1; + this->last_line_ = loc.line_number(); + this->last_column_ = loc.column_number() + 1; + this->length_ = 1; + } +} + +TOML11_INLINE region::char_type region::at(std::size_t i) const +{ + if(this->last_ <= this->first_ + i) + { + throw std::out_of_range("range::at: index " + std::to_string(i) + + " exceeds length " + std::to_string(this->length_)); + } + const auto iter = std::next(this->source_->cbegin(), + static_cast(this->first_ + i)); + return *iter; +} + +TOML11_INLINE region::const_iterator region::begin() const noexcept +{ + return std::next(this->source_->cbegin(), + static_cast(this->first_)); +} +TOML11_INLINE region::const_iterator region::end() const noexcept +{ + return std::next(this->source_->cbegin(), + static_cast(this->last_)); +} +TOML11_INLINE region::const_iterator region::cbegin() const noexcept +{ + return std::next(this->source_->cbegin(), + static_cast(this->first_)); +} +TOML11_INLINE region::const_iterator region::cend() const noexcept +{ + return std::next(this->source_->cbegin(), + static_cast(this->last_)); +} + +TOML11_INLINE std::string region::as_string() const +{ + if(this->is_ok()) + { + const auto begin = std::next(this->source_->cbegin(), static_cast(this->first_)); + const auto end = std::next(this->source_->cbegin(), static_cast(this->last_ )); + return ::toml::detail::make_string(begin, end); + } + else + { + return std::string(""); + } +} + +TOML11_INLINE std::vector region::as_lines() const +{ + assert(this->is_ok()); + if(this->length_ == 0) + { + return std::vector{""}; + } + + // Consider the following toml file + // ``` + // array = [ + // ] # comment + // ``` + // and the region represnets + // ``` + // [ + // ] + // ``` + // but we want to show the following. + // ``` + // array = [ + // ] # comment + // ``` + // So we need to find LFs before `begin` and after `end`. + // + // But, if region ends with LF, it should not include the next line. + // ``` + // a = 42 + // ^^^- with the last LF + // ``` + // So we start from `end-1` when looking for LF. + + const auto begin_idx = static_cast(this->first_); + const auto end_idx = static_cast(this->last_) - 1; + + // length_ != 0, so begin < end. then begin <= end-1 + assert(begin_idx <= end_idx); + + const auto begin = std::next(this->source_->cbegin(), begin_idx); + const auto end = std::next(this->source_->cbegin(), end_idx); + + const auto line_begin = std::find(cxx::make_reverse_iterator(begin), this->source_->crend(), char_type('\n')).base(); + const auto line_end = std::find(end, this->source_->cend(), char_type('\n')); + + const auto reg_lines = make_string(line_begin, line_end); + + if(reg_lines == "") // the region is an empty line that only contains LF + { + return std::vector{""}; + } + + std::istringstream iss(reg_lines); + + std::vector lines; + std::string line; + while(std::getline(iss, line)) + { + lines.push_back(line); + } + return lines; +} + +} // namespace detail +} // namespace toml +#endif // TOML11_REGION_IMPL_HPP +#endif + +#endif // TOML11_REGION_HPP +#ifndef TOML11_SOURCE_LOCATION_HPP +#define TOML11_SOURCE_LOCATION_HPP + +#ifndef TOML11_SOURCE_LOCATION_FWD_HPP +#define TOML11_SOURCE_LOCATION_FWD_HPP + + +#include +#include +#include + +namespace toml +{ + +// A struct to contain location in a toml file. +struct source_location +{ + public: + + explicit source_location(const detail::region& r); + ~source_location() = default; + source_location(source_location const&) = default; + source_location(source_location &&) = default; + source_location& operator=(source_location const&) = default; + source_location& operator=(source_location &&) = default; + + bool is_ok() const noexcept {return this->is_ok_;} + std::size_t length() const noexcept {return this->length_;} + + std::size_t first_line_number() const noexcept {return this->first_line_;} + std::size_t first_column_number() const noexcept {return this->first_column_;} + std::size_t last_line_number() const noexcept {return this->last_line_;} + std::size_t last_column_number() const noexcept {return this->last_column_;} + + std::string const& file_name() const noexcept {return this->file_name_;} + + std::size_t num_lines() const noexcept {return this->line_str_.size();} + + std::string const& first_line() const; + std::string const& last_line() const; + + std::vector const& lines() const noexcept {return line_str_;} + + private: + + bool is_ok_; + std::size_t first_line_; + std::size_t first_column_; + std::size_t last_line_; + std::size_t last_column_; + std::size_t length_; + std::string file_name_; + std::vector line_str_; +}; + +namespace detail +{ + +std::size_t integer_width_base10(std::size_t i) noexcept; + +inline std::size_t line_width() noexcept {return 0;} + +template +std::size_t line_width(const source_location& loc, const std::string& /*msg*/, + const Ts& ... tail) noexcept +{ + return (std::max)( + integer_width_base10(loc.last_line_number()), line_width(tail...)); +} + +std::ostringstream& +format_filename(std::ostringstream& oss, const source_location& loc); + +std::ostringstream& +format_empty_line(std::ostringstream& oss, const std::size_t lnw); + +std::ostringstream& format_line(std::ostringstream& oss, + const std::size_t lnw, const std::size_t linenum, const std::string& line); + +std::ostringstream& format_underline(std::ostringstream& oss, + const std::size_t lnw, const std::size_t col, const std::size_t len, + const std::string& msg); + +std::string format_location_impl(const std::size_t lnw, + const std::string& prev_fname, + const source_location& loc, const std::string& msg); + +inline std::string format_location_rec(const std::size_t, const std::string&) +{ + return ""; +} + +template +std::string format_location_rec(const std::size_t lnw, + const std::string& prev_fname, + const source_location& loc, const std::string& msg, + const Ts& ... tail) +{ + return format_location_impl(lnw, prev_fname, loc, msg) + + format_location_rec(lnw, loc.file_name(), tail...); +} + +} // namespace detail + +// format a location info without title +template +std::string format_location( + const source_location& loc, const std::string& msg, const Ts& ... tail) +{ + const auto lnw = detail::line_width(loc, msg, tail...); + + const std::string f(""); // at the 1st iteration, no prev_filename is given + return detail::format_location_rec(lnw, f, loc, msg, tail...); +} + +} // toml +#endif // TOML11_SOURCE_LOCATION_FWD_HPP + +#if ! defined(TOML11_COMPILE_SOURCES) +#ifndef TOML11_SOURCE_LOCATION_IMPL_HPP +#define TOML11_SOURCE_LOCATION_IMPL_HPP + + + +#include +#include +#include +#include + +#include + +namespace toml +{ + +TOML11_INLINE source_location::source_location(const detail::region& r) + : is_ok_(false), + first_line_(1), + first_column_(1), + last_line_(1), + last_column_(1), + length_(0), + file_name_("unknown file") +{ + if(r.is_ok()) + { + this->is_ok_ = true; + this->file_name_ = r.source_name(); + this->first_line_ = r.first_line_number(); + this->first_column_ = r.first_column_number(); + this->last_line_ = r.last_line_number(); + this->last_column_ = r.last_column_number(); + this->length_ = r.length(); + this->line_str_ = r.as_lines(); + } +} + +TOML11_INLINE std::string const& source_location::first_line() const +{ + if(this->line_str_.size() == 0) + { + throw std::out_of_range("toml::source_location::first_line: `lines` is empty"); + } + return this->line_str_.front(); +} +TOML11_INLINE std::string const& source_location::last_line() const +{ + if(this->line_str_.size() == 0) + { + throw std::out_of_range("toml::source_location::first_line: `lines` is empty"); + } + return this->line_str_.back(); +} + +namespace detail +{ + +TOML11_INLINE std::size_t integer_width_base10(std::size_t i) noexcept +{ + std::size_t width = 0; + while(i != 0) + { + i /= 10; + width += 1; + } + return width; +} + +TOML11_INLINE std::ostringstream& +format_filename(std::ostringstream& oss, const source_location& loc) +{ + // --> example.toml + oss << color::bold << color::blue << " --> " << color::reset + << color::bold << loc.file_name() << '\n' << color::reset; + return oss; +} + +TOML11_INLINE std::ostringstream& format_empty_line(std::ostringstream& oss, + const std::size_t lnw) +{ + // | + oss << detail::make_string(lnw + 1, ' ') + << color::bold << color::blue << " |\n" << color::reset; + return oss; +} + +TOML11_INLINE std::ostringstream& format_line(std::ostringstream& oss, + const std::size_t lnw, const std::size_t linenum, const std::string& line) +{ + // 10 | key = "value" + oss << ' ' << color::bold << color::blue + << std::setw(static_cast(lnw)) + << std::right << linenum << " | " << color::reset; + for(const char c : line) + { + if(std::isgraph(c) || c == ' ') + { + oss << c; + } + else + { + oss << show_char(c); + } + } + oss << '\n'; + return oss; +} +TOML11_INLINE std::ostringstream& format_underline(std::ostringstream& oss, + const std::size_t lnw, const std::size_t col, const std::size_t len, + const std::string& msg) +{ + // | ^^^^^^^-- this part + oss << make_string(lnw + 1, ' ') + << color::bold << color::blue << " | " << color::reset; + + oss << make_string(col-1 /*1-origin*/, ' ') + << color::bold << color::red + << make_string(len, '^') << "-- " + << color::reset << msg << '\n'; + + return oss; +} + +TOML11_INLINE std::string format_location_impl(const std::size_t lnw, + const std::string& prev_fname, + const source_location& loc, const std::string& msg) +{ + std::ostringstream oss; + + if(loc.file_name() != prev_fname) + { + format_filename(oss, loc); + if( ! loc.lines().empty()) + { + format_empty_line(oss, lnw); + } + } + + if(loc.lines().size() == 1) + { + // when column points LF, it exceeds the size of the first line. + std::size_t underline_limit = 1; + if(loc.first_line().size() < loc.first_column_number()) + { + underline_limit = 1; + } + else + { + underline_limit = loc.first_line().size() - loc.first_column_number() + 1; + } + const auto underline_len = (std::min)(underline_limit, loc.length()); + + format_line(oss, lnw, loc.first_line_number(), loc.first_line()); + format_underline(oss, lnw, loc.first_column_number(), underline_len, msg); + } + else if(loc.lines().size() == 2) + { + const auto first_underline_len = + loc.first_line().size() - loc.first_column_number() + 1; + format_line(oss, lnw, loc.first_line_number(), loc.first_line()); + format_underline(oss, lnw, loc.first_column_number(), + first_underline_len, ""); + + format_line(oss, lnw, loc.last_line_number(), loc.last_line()); + format_underline(oss, lnw, 1, loc.last_column_number(), msg); + } + else if(loc.lines().size() > 2) + { + const auto first_underline_len = + loc.first_line().size() - loc.first_column_number() + 1; + format_line(oss, lnw, loc.first_line_number(), loc.first_line()); + format_underline(oss, lnw, loc.first_column_number(), + first_underline_len, "and"); + + if(loc.lines().size() == 3) + { + format_line(oss, lnw, loc.first_line_number()+1, loc.lines().at(1)); + format_underline(oss, lnw, 1, loc.lines().at(1).size(), "and"); + } + else + { + format_line(oss, lnw, loc.first_line_number()+1, " ..."); + format_empty_line(oss, lnw); + } + format_line(oss, lnw, loc.last_line_number(), loc.last_line()); + format_underline(oss, lnw, 1, loc.last_column_number(), msg); + } + // if loc is empty, do nothing. + return oss.str(); +} + +} // namespace detail +} // toml +#endif // TOML11_SOURCE_LOCATION_IMPL_HPP +#endif + +#endif // TOML11_SOURCE_LOCATION_HPP +#ifndef TOML11_ERROR_INFO_HPP +#define TOML11_ERROR_INFO_HPP + +#ifndef TOML11_ERROR_INFO_FWD_HPP +#define TOML11_ERROR_INFO_FWD_HPP + + +namespace toml +{ + +// error info returned from parser. +struct error_info +{ + error_info(std::string t, source_location l, std::string m, std::string s = "") + : title_(std::move(t)), locations_{std::make_pair(std::move(l), std::move(m))}, + suffix_(std::move(s)) + {} + + error_info(std::string t, std::vector> l, + std::string s = "") + : title_(std::move(t)), locations_(std::move(l)), suffix_(std::move(s)) + {} + + std::string const& title() const noexcept {return title_;} + std::string & title() noexcept {return title_;} + + std::vector> const& + locations() const noexcept {return locations_;} + + void add_locations(source_location loc, std::string msg) noexcept + { + locations_.emplace_back(std::move(loc), std::move(msg)); + } + + std::string const& suffix() const noexcept {return suffix_;} + std::string & suffix() noexcept {return suffix_;} + + private: + + std::string title_; + std::vector> locations_; + std::string suffix_; // hint or something like that +}; + +// forward decl +template +class basic_value; + +namespace detail +{ +inline error_info make_error_info_rec(error_info e) +{ + return e; +} +inline error_info make_error_info_rec(error_info e, std::string s) +{ + e.suffix() = s; + return e; +} + +template +error_info make_error_info_rec(error_info e, + const basic_value& v, std::string msg, Ts&& ... tail); + +template +error_info make_error_info_rec(error_info e, + source_location loc, std::string msg, Ts&& ... tail) +{ + e.add_locations(std::move(loc), std::move(msg)); + return make_error_info_rec(std::move(e), std::forward(tail)...); +} + +} // detail + +template +error_info make_error_info( + std::string title, source_location loc, std::string msg, Ts&& ... tail) +{ + error_info ei(std::move(title), std::move(loc), std::move(msg)); + return detail::make_error_info_rec(ei, std::forward(tail) ... ); +} + +std::string format_error(const std::string& errkind, const error_info& err); +std::string format_error(const error_info& err); + +// for custom error message +template +std::string format_error(std::string title, + source_location loc, std::string msg, Ts&& ... tail) +{ + return format_error("", make_error_info(std::move(title), + std::move(loc), std::move(msg), std::forward(tail)...)); +} + +std::ostream& operator<<(std::ostream& os, const error_info& e); + +} // toml +#endif // TOML11_ERROR_INFO_FWD_HPP + +#if ! defined(TOML11_COMPILE_SOURCES) +#ifndef TOML11_ERROR_INFO_IMPL_HPP +#define TOML11_ERROR_INFO_IMPL_HPP + + +#include + +namespace toml +{ + +TOML11_INLINE std::string format_error(const std::string& errkind, const error_info& err) +{ + std::string errmsg; + if( ! errkind.empty()) + { + errmsg = errkind; + errmsg += ' '; + } + errmsg += err.title(); + errmsg += '\n'; + + const auto lnw = [&err]() { + std::size_t width = 0; + for(const auto& l : err.locations()) + { + width = (std::max)(detail::integer_width_base10(l.first.last_line_number()), width); + } + return width; + }(); + + bool first = true; + std::string prev_fname; + for(const auto& lm : err.locations()) + { + if( ! first) + { + std::ostringstream oss; + oss << detail::make_string(lnw + 1, ' ') + << color::bold << color::blue << " |" << color::reset + << color::bold << " ...\n" << color::reset; + oss << detail::make_string(lnw + 1, ' ') + << color::bold << color::blue << " |\n" << color::reset; + errmsg += oss.str(); + } + + const auto& l = lm.first; + const auto& m = lm.second; + + errmsg += detail::format_location_impl(lnw, prev_fname, l, m); + + prev_fname = l.file_name(); + first = false; + } + + errmsg += err.suffix(); + + return errmsg; +} + +TOML11_INLINE std::string format_error(const error_info& err) +{ + std::ostringstream oss; + oss << color::red << color::bold << "[error]" << color::reset; + return format_error(oss.str(), err); +} + +TOML11_INLINE std::ostream& operator<<(std::ostream& os, const error_info& e) +{ + os << format_error(e); + return os; +} + +} // toml +#endif // TOML11_ERROR_INFO_IMPL_HPP +#endif + +#endif // TOML11_ERROR_INFO_HPP +#ifndef TOML11_VALUE_HPP +#define TOML11_VALUE_HPP + + +#ifdef TOML11_HAS_STRING_VIEW +#include +#endif + +#include + +namespace toml +{ +template +class basic_value; + +struct type_error final : public ::toml::exception +{ + public: + type_error(std::string what_arg, source_location loc) + : what_(std::move(what_arg)), loc_(std::move(loc)) + {} + ~type_error() noexcept override = default; + + const char* what() const noexcept override {return what_.c_str();} + + source_location const& location() const noexcept {return loc_;} + + private: + std::string what_; + source_location loc_; +}; + +// only for internal use +namespace detail +{ +template +error_info make_type_error(const basic_value&, const std::string&, const value_t); + +template +error_info make_not_found_error(const basic_value&, const std::string&, const typename basic_value::key_type&); + +template +void change_region_of_value(basic_value&, const basic_value&); + +template +struct getter; +} // detail + +template +class basic_value +{ + public: + + using config_type = TypeConfig; + using key_type = typename config_type::string_type; + using value_type = basic_value; + using boolean_type = typename config_type::boolean_type; + using integer_type = typename config_type::integer_type; + using floating_type = typename config_type::floating_type; + using string_type = typename config_type::string_type; + using local_time_type = ::toml::local_time; + using local_date_type = ::toml::local_date; + using local_datetime_type = ::toml::local_datetime; + using offset_datetime_type = ::toml::offset_datetime; + using array_type = typename config_type::template array_type; + using table_type = typename config_type::template table_type; + using comment_type = typename config_type::comment_type; + using char_type = typename string_type::value_type; + + private: + + using region_type = detail::region; + + public: + + basic_value() noexcept + : type_(value_t::empty), empty_('\0'), region_{}, comments_{} + {} + ~basic_value() noexcept {this->cleanup();} + + // copy/move constructor/assigner ===================================== {{{ + + basic_value(const basic_value& v) + : type_(v.type_), region_(v.region_), comments_(v.comments_) + { + switch(this->type_) + { + case value_t::boolean : assigner(boolean_ , v.boolean_ ); break; + case value_t::integer : assigner(integer_ , v.integer_ ); break; + case value_t::floating : assigner(floating_ , v.floating_ ); break; + case value_t::string : assigner(string_ , v.string_ ); break; + case value_t::offset_datetime: assigner(offset_datetime_, v.offset_datetime_); break; + case value_t::local_datetime : assigner(local_datetime_ , v.local_datetime_ ); break; + case value_t::local_date : assigner(local_date_ , v.local_date_ ); break; + case value_t::local_time : assigner(local_time_ , v.local_time_ ); break; + case value_t::array : assigner(array_ , v.array_ ); break; + case value_t::table : assigner(table_ , v.table_ ); break; + default : assigner(empty_ , '\0' ); break; + } + } + basic_value(basic_value&& v) + : type_(v.type()), region_(std::move(v.region_)), + comments_(std::move(v.comments_)) + { + switch(this->type_) + { + case value_t::boolean : assigner(boolean_ , std::move(v.boolean_ )); break; + case value_t::integer : assigner(integer_ , std::move(v.integer_ )); break; + case value_t::floating : assigner(floating_ , std::move(v.floating_ )); break; + case value_t::string : assigner(string_ , std::move(v.string_ )); break; + case value_t::offset_datetime: assigner(offset_datetime_, std::move(v.offset_datetime_)); break; + case value_t::local_datetime : assigner(local_datetime_ , std::move(v.local_datetime_ )); break; + case value_t::local_date : assigner(local_date_ , std::move(v.local_date_ )); break; + case value_t::local_time : assigner(local_time_ , std::move(v.local_time_ )); break; + case value_t::array : assigner(array_ , std::move(v.array_ )); break; + case value_t::table : assigner(table_ , std::move(v.table_ )); break; + default : assigner(empty_ , '\0' ); break; + } + } + + basic_value& operator=(const basic_value& v) + { + if(this == std::addressof(v)) {return *this;} + + this->cleanup(); + this->type_ = v.type_; + this->region_ = v.region_; + this->comments_ = v.comments_; + switch(this->type_) + { + case value_t::boolean : assigner(boolean_ , v.boolean_ ); break; + case value_t::integer : assigner(integer_ , v.integer_ ); break; + case value_t::floating : assigner(floating_ , v.floating_ ); break; + case value_t::string : assigner(string_ , v.string_ ); break; + case value_t::offset_datetime: assigner(offset_datetime_, v.offset_datetime_); break; + case value_t::local_datetime : assigner(local_datetime_ , v.local_datetime_ ); break; + case value_t::local_date : assigner(local_date_ , v.local_date_ ); break; + case value_t::local_time : assigner(local_time_ , v.local_time_ ); break; + case value_t::array : assigner(array_ , v.array_ ); break; + case value_t::table : assigner(table_ , v.table_ ); break; + default : assigner(empty_ , '\0' ); break; + } + return *this; + } + basic_value& operator=(basic_value&& v) + { + if(this == std::addressof(v)) {return *this;} + + this->cleanup(); + this->type_ = v.type_; + this->region_ = std::move(v.region_); + this->comments_ = std::move(v.comments_); + switch(this->type_) + { + case value_t::boolean : assigner(boolean_ , std::move(v.boolean_ )); break; + case value_t::integer : assigner(integer_ , std::move(v.integer_ )); break; + case value_t::floating : assigner(floating_ , std::move(v.floating_ )); break; + case value_t::string : assigner(string_ , std::move(v.string_ )); break; + case value_t::offset_datetime: assigner(offset_datetime_, std::move(v.offset_datetime_)); break; + case value_t::local_datetime : assigner(local_datetime_ , std::move(v.local_datetime_ )); break; + case value_t::local_date : assigner(local_date_ , std::move(v.local_date_ )); break; + case value_t::local_time : assigner(local_time_ , std::move(v.local_time_ )); break; + case value_t::array : assigner(array_ , std::move(v.array_ )); break; + case value_t::table : assigner(table_ , std::move(v.table_ )); break; + default : assigner(empty_ , '\0' ); break; + } + return *this; + } + // }}} + + // constructor to overwrite commnets ================================== {{{ + + basic_value(basic_value v, std::vector com) + : type_(v.type()), region_(std::move(v.region_)), + comments_(std::move(com)) + { + switch(this->type_) + { + case value_t::boolean : assigner(boolean_ , std::move(v.boolean_ )); break; + case value_t::integer : assigner(integer_ , std::move(v.integer_ )); break; + case value_t::floating : assigner(floating_ , std::move(v.floating_ )); break; + case value_t::string : assigner(string_ , std::move(v.string_ )); break; + case value_t::offset_datetime: assigner(offset_datetime_, std::move(v.offset_datetime_)); break; + case value_t::local_datetime : assigner(local_datetime_ , std::move(v.local_datetime_ )); break; + case value_t::local_date : assigner(local_date_ , std::move(v.local_date_ )); break; + case value_t::local_time : assigner(local_time_ , std::move(v.local_time_ )); break; + case value_t::array : assigner(array_ , std::move(v.array_ )); break; + case value_t::table : assigner(table_ , std::move(v.table_ )); break; + default : assigner(empty_ , '\0' ); break; + } + } + // }}} + + // conversion between different basic_values ========================== {{{ + + template + basic_value(basic_value other) + : type_(other.type_), + region_(std::move(other.region_)), + comments_(std::move(other.comments_)) + { + switch(other.type_) + { + // use auto-convert in constructor + case value_t::boolean : assigner(boolean_ , std::move(other.boolean_ )); break; + case value_t::integer : assigner(integer_ , std::move(other.integer_ )); break; + case value_t::floating : assigner(floating_ , std::move(other.floating_ )); break; + case value_t::string : assigner(string_ , std::move(other.string_ )); break; + case value_t::offset_datetime: assigner(offset_datetime_, std::move(other.offset_datetime_)); break; + case value_t::local_datetime : assigner(local_datetime_ , std::move(other.local_datetime_ )); break; + case value_t::local_date : assigner(local_date_ , std::move(other.local_date_ )); break; + case value_t::local_time : assigner(local_time_ , std::move(other.local_time_ )); break; + + // may have different container type + case value_t::array : + { + array_type tmp( + std::make_move_iterator(other.array_.value.get().begin()), + std::make_move_iterator(other.array_.value.get().end())); + assigner(array_, array_storage( + detail::storage(std::move(tmp)), + other.array_.format + )); + break; + } + case value_t::table : + { + table_type tmp( + std::make_move_iterator(other.table_.value.get().begin()), + std::make_move_iterator(other.table_.value.get().end())); + assigner(table_, table_storage( + detail::storage(std::move(tmp)), + other.table_.format + )); + break; + } + default: break; + } + } + + template + basic_value(basic_value other, std::vector com) + : type_(other.type_), + region_(std::move(other.region_)), + comments_(std::move(com)) + { + switch(other.type_) + { + // use auto-convert in constructor + case value_t::boolean : assigner(boolean_ , std::move(other.boolean_ )); break; + case value_t::integer : assigner(integer_ , std::move(other.integer_ )); break; + case value_t::floating : assigner(floating_ , std::move(other.floating_ )); break; + case value_t::string : assigner(string_ , std::move(other.string_ )); break; + case value_t::offset_datetime: assigner(offset_datetime_, std::move(other.offset_datetime_)); break; + case value_t::local_datetime : assigner(local_datetime_ , std::move(other.local_datetime_ )); break; + case value_t::local_date : assigner(local_date_ , std::move(other.local_date_ )); break; + case value_t::local_time : assigner(local_time_ , std::move(other.local_time_ )); break; + + // may have different container type + case value_t::array : + { + array_type tmp( + std::make_move_iterator(other.array_.value.get().begin()), + std::make_move_iterator(other.array_.value.get().end())); + assigner(array_, array_storage( + detail::storage(std::move(tmp)), + other.array_.format + )); + break; + } + case value_t::table : + { + table_type tmp( + std::make_move_iterator(other.table_.value.get().begin()), + std::make_move_iterator(other.table_.value.get().end())); + assigner(table_, table_storage( + detail::storage(std::move(tmp)), + other.table_.format + )); + break; + } + default: break; + } + } + template + basic_value& operator=(basic_value other) + { + this->cleanup(); + this->region_ = other.region_; + this->comments_ = comment_type(other.comments_); + this->type_ = other.type_; + switch(other.type_) + { + // use auto-convert in constructor + case value_t::boolean : assigner(boolean_ , std::move(other.boolean_ )); break; + case value_t::integer : assigner(integer_ , std::move(other.integer_ )); break; + case value_t::floating : assigner(floating_ , std::move(other.floating_ )); break; + case value_t::string : assigner(string_ , std::move(other.string_ )); break; + case value_t::offset_datetime: assigner(offset_datetime_, std::move(other.offset_datetime_)); break; + case value_t::local_datetime : assigner(local_datetime_ , std::move(other.local_datetime_ )); break; + case value_t::local_date : assigner(local_date_ , std::move(other.local_date_ )); break; + case value_t::local_time : assigner(local_time_ , std::move(other.local_time_ )); break; + + // may have different container type + case value_t::array : + { + array_type tmp( + std::make_move_iterator(other.array_.value.get().begin()), + std::make_move_iterator(other.array_.value.get().end())); + assigner(array_, array_storage( + detail::storage(std::move(tmp)), + other.array_.format + )); + break; + } + case value_t::table : + { + table_type tmp( + std::make_move_iterator(other.table_.value.get().begin()), + std::make_move_iterator(other.table_.value.get().end())); + assigner(table_, table_storage( + detail::storage(std::move(tmp)), + other.table_.format + )); + break; + } + default: break; + } + return *this; + } + // }}} + + // constructor (boolean) ============================================== {{{ + + basic_value(boolean_type x) + : basic_value(x, boolean_format_info{}, std::vector{}, region_type{}) + {} + basic_value(boolean_type x, boolean_format_info fmt) + : basic_value(x, fmt, std::vector{}, region_type{}) + {} + basic_value(boolean_type x, std::vector com) + : basic_value(x, boolean_format_info{}, std::move(com), region_type{}) + {} + basic_value(boolean_type x, boolean_format_info fmt, std::vector com) + : basic_value(x, fmt, std::move(com), region_type{}) + {} + basic_value(boolean_type x, boolean_format_info fmt, + std::vector com, region_type reg) + : type_(value_t::boolean), boolean_(boolean_storage(x, fmt)), + region_(std::move(reg)), comments_(std::move(com)) + {} + basic_value& operator=(boolean_type x) + { + boolean_format_info fmt; + if(this->is_boolean()) + { + fmt = this->as_boolean_fmt(); + } + this->cleanup(); + this->type_ = value_t::boolean; + this->region_ = region_type{}; + assigner(this->boolean_, boolean_storage(x, fmt)); + return *this; + } + + // }}} + + // constructor (integer) ============================================== {{{ + + basic_value(integer_type x) + : basic_value(std::move(x), integer_format_info{}, std::vector{}, region_type{}) + {} + basic_value(integer_type x, integer_format_info fmt) + : basic_value(std::move(x), std::move(fmt), std::vector{}, region_type{}) + {} + basic_value(integer_type x, std::vector com) + : basic_value(std::move(x), integer_format_info{}, std::move(com), region_type{}) + {} + basic_value(integer_type x, integer_format_info fmt, std::vector com) + : basic_value(std::move(x), std::move(fmt), std::move(com), region_type{}) + {} + basic_value(integer_type x, integer_format_info fmt, std::vector com, region_type reg) + : type_(value_t::integer), integer_(integer_storage(std::move(x), std::move(fmt))), + region_(std::move(reg)), comments_(std::move(com)) + {} + basic_value& operator=(integer_type x) + { + integer_format_info fmt; + if(this->is_integer()) + { + fmt = this->as_integer_fmt(); + } + this->cleanup(); + this->type_ = value_t::integer; + this->region_ = region_type{}; + assigner(this->integer_, integer_storage(std::move(x), std::move(fmt))); + return *this; + } + + private: + + template + using enable_if_integer_like_t = cxx::enable_if_t, boolean_type>>, + cxx::negation, integer_type>>, + std::is_integral> + >::value, std::nullptr_t>; + + public: + + template = nullptr> + basic_value(T x) + : basic_value(std::move(x), integer_format_info{}, std::vector{}, region_type{}) + {} + template = nullptr> + basic_value(T x, integer_format_info fmt) + : basic_value(std::move(x), std::move(fmt), std::vector{}, region_type{}) + {} + template = nullptr> + basic_value(T x, std::vector com) + : basic_value(std::move(x), integer_format_info{}, std::move(com), region_type{}) + {} + template = nullptr> + basic_value(T x, integer_format_info fmt, std::vector com) + : basic_value(std::move(x), std::move(fmt), std::move(com), region_type{}) + {} + template = nullptr> + basic_value(T x, integer_format_info fmt, std::vector com, region_type reg) + : type_(value_t::integer), integer_(integer_storage(std::move(x), std::move(fmt))), + region_(std::move(reg)), comments_(std::move(com)) + {} + template = nullptr> + basic_value& operator=(T x) + { + integer_format_info fmt; + if(this->is_integer()) + { + fmt = this->as_integer_fmt(); + } + this->cleanup(); + this->type_ = value_t::integer; + this->region_ = region_type{}; + assigner(this->integer_, integer_storage(x, std::move(fmt))); + return *this; + } + + // }}} + + // constructor (floating) ============================================= {{{ + + basic_value(floating_type x) + : basic_value(std::move(x), floating_format_info{}, std::vector{}, region_type{}) + {} + basic_value(floating_type x, floating_format_info fmt) + : basic_value(std::move(x), std::move(fmt), std::vector{}, region_type{}) + {} + basic_value(floating_type x, std::vector com) + : basic_value(std::move(x), floating_format_info{}, std::move(com), region_type{}) + {} + basic_value(floating_type x, floating_format_info fmt, std::vector com) + : basic_value(std::move(x), std::move(fmt), std::move(com), region_type{}) + {} + basic_value(floating_type x, floating_format_info fmt, std::vector com, region_type reg) + : type_(value_t::floating), floating_(floating_storage(std::move(x), std::move(fmt))), + region_(std::move(reg)), comments_(std::move(com)) + {} + basic_value& operator=(floating_type x) + { + floating_format_info fmt; + if(this->is_floating()) + { + fmt = this->as_floating_fmt(); + } + this->cleanup(); + this->type_ = value_t::floating; + this->region_ = region_type{}; + assigner(this->floating_, floating_storage(std::move(x), std::move(fmt))); + return *this; + } + + private: + + template + using enable_if_floating_like_t = cxx::enable_if_t, floating_type>>, + std::is_floating_point> + >::value, std::nullptr_t>; + + public: + + template = nullptr> + basic_value(T x) + : basic_value(x, floating_format_info{}, std::vector{}, region_type{}) + {} + + template = nullptr> + basic_value(T x, floating_format_info fmt) + : basic_value(x, std::move(fmt), std::vector{}, region_type{}) + {} + + template = nullptr> + basic_value(T x, std::vector com) + : basic_value(x, floating_format_info{}, std::move(com), region_type{}) + {} + + template = nullptr> + basic_value(T x, floating_format_info fmt, std::vector com) + : basic_value(x, std::move(fmt), std::move(com), region_type{}) + {} + + template = nullptr> + basic_value(T x, floating_format_info fmt, std::vector com, region_type reg) + : type_(value_t::floating), floating_(floating_storage(x, std::move(fmt))), + region_(std::move(reg)), comments_(std::move(com)) + {} + + template = nullptr> + basic_value& operator=(T x) + { + floating_format_info fmt; + if(this->is_floating()) + { + fmt = this->as_floating_fmt(); + } + this->cleanup(); + this->type_ = value_t::floating; + this->region_ = region_type{}; + assigner(this->floating_, floating_storage(x, std::move(fmt))); + return *this; + } + + // }}} + + // constructor (string) =============================================== {{{ + + basic_value(string_type x) + : basic_value(std::move(x), string_format_info{}, std::vector{}, region_type{}) + {} + basic_value(string_type x, string_format_info fmt) + : basic_value(std::move(x), std::move(fmt), std::vector{}, region_type{}) + {} + basic_value(string_type x, std::vector com) + : basic_value(std::move(x), string_format_info{}, std::move(com), region_type{}) + {} + basic_value(string_type x, string_format_info fmt, std::vector com) + : basic_value(std::move(x), std::move(fmt), std::move(com), region_type{}) + {} + basic_value(string_type x, string_format_info fmt, + std::vector com, region_type reg) + : type_(value_t::string), string_(string_storage(std::move(x), std::move(fmt))), + region_(std::move(reg)), comments_(std::move(com)) + {} + basic_value& operator=(string_type x) + { + string_format_info fmt; + if(this->is_string()) + { + fmt = this->as_string_fmt(); + } + this->cleanup(); + this->type_ = value_t::string; + this->region_ = region_type{}; + assigner(this->string_, string_storage(x, std::move(fmt))); + return *this; + } + + // "string literal" + + basic_value(const typename string_type::value_type* x) + : basic_value(x, string_format_info{}, std::vector{}, region_type{}) + {} + basic_value(const typename string_type::value_type* x, string_format_info fmt) + : basic_value(x, std::move(fmt), std::vector{}, region_type{}) + {} + basic_value(const typename string_type::value_type* x, std::vector com) + : basic_value(x, string_format_info{}, std::move(com), region_type{}) + {} + basic_value(const typename string_type::value_type* x, string_format_info fmt, std::vector com) + : basic_value(x, std::move(fmt), std::move(com), region_type{}) + {} + basic_value(const typename string_type::value_type* x, string_format_info fmt, + std::vector com, region_type reg) + : type_(value_t::string), string_(string_storage(string_type(x), std::move(fmt))), + region_(std::move(reg)), comments_(std::move(com)) + {} + basic_value& operator=(const typename string_type::value_type* x) + { + string_format_info fmt; + if(this->is_string()) + { + fmt = this->as_string_fmt(); + } + this->cleanup(); + this->type_ = value_t::string; + this->region_ = region_type{}; + assigner(this->string_, string_storage(string_type(x), std::move(fmt))); + return *this; + } + +#if defined(TOML11_HAS_STRING_VIEW) + using string_view_type = std::basic_string_view< + typename string_type::value_type, typename string_type::traits_type>; + + basic_value(string_view_type x) + : basic_value(x, string_format_info{}, std::vector{}, region_type{}) + {} + basic_value(string_view_type x, string_format_info fmt) + : basic_value(x, std::move(fmt), std::vector{}, region_type{}) + {} + basic_value(string_view_type x, std::vector com) + : basic_value(x, string_format_info{}, std::move(com), region_type{}) + {} + basic_value(string_view_type x, string_format_info fmt, std::vector com) + : basic_value(x, std::move(fmt), std::move(com), region_type{}) + {} + basic_value(string_view_type x, string_format_info fmt, + std::vector com, region_type reg) + : type_(value_t::string), string_(string_storage(string_type(x), std::move(fmt))), + region_(std::move(reg)), comments_(std::move(com)) + {} + basic_value& operator=(string_view_type x) + { + string_format_info fmt; + if(this->is_string()) + { + fmt = this->as_string_fmt(); + } + this->cleanup(); + this->type_ = value_t::string; + this->region_ = region_type{}; + assigner(this->string_, string_storage(string_type(x), std::move(fmt))); + return *this; + } + +#endif // TOML11_HAS_STRING_VIEW + + template, string_type>>, + detail::is_1byte_std_basic_string + >::value, std::nullptr_t> = nullptr> + basic_value(const T& x) + : basic_value(x, string_format_info{}, std::vector{}, region_type{}) + {} + template, string_type>>, + detail::is_1byte_std_basic_string + >::value, std::nullptr_t> = nullptr> + basic_value(const T& x, string_format_info fmt) + : basic_value(x, std::move(fmt), std::vector{}, region_type{}) + {} + template, string_type>>, + detail::is_1byte_std_basic_string + >::value, std::nullptr_t> = nullptr> + basic_value(const T& x, std::vector com) + : basic_value(x, string_format_info{}, std::move(com), region_type{}) + {} + template, string_type>>, + detail::is_1byte_std_basic_string + >::value, std::nullptr_t> = nullptr> + basic_value(const T& x, string_format_info fmt, std::vector com) + : basic_value(x, std::move(fmt), std::move(com), region_type{}) + {} + template, string_type>>, + detail::is_1byte_std_basic_string + >::value, std::nullptr_t> = nullptr> + basic_value(const T& x, string_format_info fmt, + std::vector com, region_type reg) + : type_(value_t::string), + string_(string_storage(detail::string_conv(x), std::move(fmt))), + region_(std::move(reg)), comments_(std::move(com)) + {} + template, string_type>>, + detail::is_1byte_std_basic_string + >::value, std::nullptr_t> = nullptr> + basic_value& operator=(const T& x) + { + string_format_info fmt; + if(this->is_string()) + { + fmt = this->as_string_fmt(); + } + this->cleanup(); + this->type_ = value_t::string; + this->region_ = region_type{}; + assigner(this->string_, string_storage(detail::string_conv(x), std::move(fmt))); + return *this; + } + + // }}} + + // constructor (local_date) =========================================== {{{ + + basic_value(local_date_type x) + : basic_value(x, local_date_format_info{}, std::vector{}, region_type{}) + {} + basic_value(local_date_type x, local_date_format_info fmt) + : basic_value(x, fmt, std::vector{}, region_type{}) + {} + basic_value(local_date_type x, std::vector com) + : basic_value(x, local_date_format_info{}, std::move(com), region_type{}) + {} + basic_value(local_date_type x, local_date_format_info fmt, std::vector com) + : basic_value(x, fmt, std::move(com), region_type{}) + {} + basic_value(local_date_type x, local_date_format_info fmt, + std::vector com, region_type reg) + : type_(value_t::local_date), local_date_(local_date_storage(x, fmt)), + region_(std::move(reg)), comments_(std::move(com)) + {} + basic_value& operator=(local_date_type x) + { + local_date_format_info fmt; + if(this->is_local_date()) + { + fmt = this->as_local_date_fmt(); + } + this->cleanup(); + this->type_ = value_t::local_date; + this->region_ = region_type{}; + assigner(this->local_date_, local_date_storage(x, fmt)); + return *this; + } + + // }}} + + // constructor (local_time) =========================================== {{{ + + basic_value(local_time_type x) + : basic_value(x, local_time_format_info{}, std::vector{}, region_type{}) + {} + basic_value(local_time_type x, local_time_format_info fmt) + : basic_value(x, fmt, std::vector{}, region_type{}) + {} + basic_value(local_time_type x, std::vector com) + : basic_value(x, local_time_format_info{}, std::move(com), region_type{}) + {} + basic_value(local_time_type x, local_time_format_info fmt, std::vector com) + : basic_value(x, fmt, std::move(com), region_type{}) + {} + basic_value(local_time_type x, local_time_format_info fmt, + std::vector com, region_type reg) + : type_(value_t::local_time), local_time_(local_time_storage(x, fmt)), + region_(std::move(reg)), comments_(std::move(com)) + {} + basic_value& operator=(local_time_type x) + { + local_time_format_info fmt; + if(this->is_local_time()) + { + fmt = this->as_local_time_fmt(); + } + this->cleanup(); + this->type_ = value_t::local_time; + this->region_ = region_type{}; + assigner(this->local_time_, local_time_storage(x, fmt)); + return *this; + } + + template + basic_value(const std::chrono::duration& x) + : basic_value(local_time_type(x), local_time_format_info{}, std::vector{}, region_type{}) + {} + template + basic_value(const std::chrono::duration& x, local_time_format_info fmt) + : basic_value(local_time_type(x), std::move(fmt), std::vector{}, region_type{}) + {} + template + basic_value(const std::chrono::duration& x, std::vector com) + : basic_value(local_time_type(x), local_time_format_info{}, std::move(com), region_type{}) + {} + template + basic_value(const std::chrono::duration& x, local_time_format_info fmt, std::vector com) + : basic_value(local_time_type(x), std::move(fmt), std::move(com), region_type{}) + {} + template + basic_value(const std::chrono::duration& x, + local_time_format_info fmt, + std::vector com, region_type reg) + : basic_value(local_time_type(x), std::move(fmt), std::move(com), std::move(reg)) + {} + template + basic_value& operator=(const std::chrono::duration& x) + { + local_time_format_info fmt; + if(this->is_local_time()) + { + fmt = this->as_local_time_fmt(); + } + this->cleanup(); + this->type_ = value_t::local_time; + this->region_ = region_type{}; + assigner(this->local_time_, local_time_storage(local_time_type(x), std::move(fmt))); + return *this; + } + + // }}} + + // constructor (local_datetime) =========================================== {{{ + + basic_value(local_datetime_type x) + : basic_value(x, local_datetime_format_info{}, std::vector{}, region_type{}) + {} + basic_value(local_datetime_type x, local_datetime_format_info fmt) + : basic_value(x, fmt, std::vector{}, region_type{}) + {} + basic_value(local_datetime_type x, std::vector com) + : basic_value(x, local_datetime_format_info{}, std::move(com), region_type{}) + {} + basic_value(local_datetime_type x, local_datetime_format_info fmt, std::vector com) + : basic_value(x, fmt, std::move(com), region_type{}) + {} + basic_value(local_datetime_type x, local_datetime_format_info fmt, + std::vector com, region_type reg) + : type_(value_t::local_datetime), local_datetime_(local_datetime_storage(x, fmt)), + region_(std::move(reg)), comments_(std::move(com)) + {} + basic_value& operator=(local_datetime_type x) + { + local_datetime_format_info fmt; + if(this->is_local_datetime()) + { + fmt = this->as_local_datetime_fmt(); + } + this->cleanup(); + this->type_ = value_t::local_datetime; + this->region_ = region_type{}; + assigner(this->local_datetime_, local_datetime_storage(x, fmt)); + return *this; + } + + // }}} + + // constructor (offset_datetime) =========================================== {{{ + + basic_value(offset_datetime_type x) + : basic_value(x, offset_datetime_format_info{}, std::vector{}, region_type{}) + {} + basic_value(offset_datetime_type x, offset_datetime_format_info fmt) + : basic_value(x, fmt, std::vector{}, region_type{}) + {} + basic_value(offset_datetime_type x, std::vector com) + : basic_value(x, offset_datetime_format_info{}, std::move(com), region_type{}) + {} + basic_value(offset_datetime_type x, offset_datetime_format_info fmt, std::vector com) + : basic_value(x, fmt, std::move(com), region_type{}) + {} + basic_value(offset_datetime_type x, offset_datetime_format_info fmt, + std::vector com, region_type reg) + : type_(value_t::offset_datetime), offset_datetime_(offset_datetime_storage(x, fmt)), + region_(std::move(reg)), comments_(std::move(com)) + {} + basic_value& operator=(offset_datetime_type x) + { + offset_datetime_format_info fmt; + if(this->is_offset_datetime()) + { + fmt = this->as_offset_datetime_fmt(); + } + this->cleanup(); + this->type_ = value_t::offset_datetime; + this->region_ = region_type{}; + assigner(this->offset_datetime_, offset_datetime_storage(x, fmt)); + return *this; + } + + // system_clock::time_point + + basic_value(std::chrono::system_clock::time_point x) + : basic_value(offset_datetime_type(x), offset_datetime_format_info{}, std::vector{}, region_type{}) + {} + basic_value(std::chrono::system_clock::time_point x, offset_datetime_format_info fmt) + : basic_value(offset_datetime_type(x), fmt, std::vector{}, region_type{}) + {} + basic_value(std::chrono::system_clock::time_point x, std::vector com) + : basic_value(offset_datetime_type(x), offset_datetime_format_info{}, std::move(com), region_type{}) + {} + basic_value(std::chrono::system_clock::time_point x, offset_datetime_format_info fmt, std::vector com) + : basic_value(offset_datetime_type(x), fmt, std::move(com), region_type{}) + {} + basic_value(std::chrono::system_clock::time_point x, offset_datetime_format_info fmt, + std::vector com, region_type reg) + : basic_value(offset_datetime_type(x), std::move(fmt), std::move(com), std::move(reg)) + {} + basic_value& operator=(std::chrono::system_clock::time_point x) + { + offset_datetime_format_info fmt; + if(this->is_offset_datetime()) + { + fmt = this->as_offset_datetime_fmt(); + } + this->cleanup(); + this->type_ = value_t::offset_datetime; + this->region_ = region_type{}; + assigner(this->offset_datetime_, offset_datetime_storage(offset_datetime_type(x), fmt)); + return *this; + } + + // }}} + + // constructor (array) ================================================ {{{ + + basic_value(array_type x) + : basic_value(std::move(x), array_format_info{}, std::vector{}, region_type{}) + {} + basic_value(array_type x, array_format_info fmt) + : basic_value(std::move(x), std::move(fmt), std::vector{}, region_type{}) + {} + basic_value(array_type x, std::vector com) + : basic_value(std::move(x), array_format_info{}, std::move(com), region_type{}) + {} + basic_value(array_type x, array_format_info fmt, std::vector com) + : basic_value(std::move(x), fmt, std::move(com), region_type{}) + {} + basic_value(array_type x, array_format_info fmt, + std::vector com, region_type reg) + : type_(value_t::array), array_(array_storage( + detail::storage(std::move(x)), std::move(fmt) + )), region_(std::move(reg)), comments_(std::move(com)) + {} + basic_value& operator=(array_type x) + { + array_format_info fmt; + if(this->is_array()) + { + fmt = this->as_array_fmt(); + } + this->cleanup(); + this->type_ = value_t::array; + this->region_ = region_type{}; + assigner(this->array_, array_storage( + detail::storage(std::move(x)), std::move(fmt))); + return *this; + } + + private: + + template + using enable_if_array_like_t = cxx::enable_if_t, + cxx::negation>, + cxx::negation>, +#if defined(TOML11_HAS_STRING_VIEW) + cxx::negation>, +#endif + cxx::negation>, + cxx::negation> + >::value, std::nullptr_t>; + + public: + + template = nullptr> + basic_value(T x) + : basic_value(std::move(x), array_format_info{}, std::vector{}, region_type{}) + {} + template = nullptr> + basic_value(T x, array_format_info fmt) + : basic_value(std::move(x), std::move(fmt), std::vector{}, region_type{}) + {} + template = nullptr> + basic_value(T x, std::vector com) + : basic_value(std::move(x), array_format_info{}, std::move(com), region_type{}) + {} + template = nullptr> + basic_value(T x, array_format_info fmt, std::vector com) + : basic_value(std::move(x), fmt, std::move(com), region_type{}) + {} + template = nullptr> + basic_value(T x, array_format_info fmt, + std::vector com, region_type reg) + : type_(value_t::array), array_(array_storage( + detail::storage(array_type( + std::make_move_iterator(x.begin()), + std::make_move_iterator(x.end())) + ), std::move(fmt) + )), region_(std::move(reg)), comments_(std::move(com)) + {} + template = nullptr> + basic_value& operator=(T x) + { + array_format_info fmt; + if(this->is_array()) + { + fmt = this->as_array_fmt(); + } + this->cleanup(); + this->type_ = value_t::array; + this->region_ = region_type{}; + + array_type a(std::make_move_iterator(x.begin()), + std::make_move_iterator(x.end())); + assigner(this->array_, array_storage( + detail::storage(std::move(a)), std::move(fmt))); + return *this; + } + + // }}} + + // constructor (table) ================================================ {{{ + + basic_value(table_type x) + : basic_value(std::move(x), table_format_info{}, std::vector{}, region_type{}) + {} + basic_value(table_type x, table_format_info fmt) + : basic_value(std::move(x), std::move(fmt), std::vector{}, region_type{}) + {} + basic_value(table_type x, std::vector com) + : basic_value(std::move(x), table_format_info{}, std::move(com), region_type{}) + {} + basic_value(table_type x, table_format_info fmt, std::vector com) + : basic_value(std::move(x), fmt, std::move(com), region_type{}) + {} + basic_value(table_type x, table_format_info fmt, + std::vector com, region_type reg) + : type_(value_t::table), table_(table_storage( + detail::storage(std::move(x)), std::move(fmt) + )), region_(std::move(reg)), comments_(std::move(com)) + {} + basic_value& operator=(table_type x) + { + table_format_info fmt; + if(this->is_table()) + { + fmt = this->as_table_fmt(); + } + this->cleanup(); + this->type_ = value_t::table; + this->region_ = region_type{}; + assigner(this->table_, table_storage( + detail::storage(std::move(x)), std::move(fmt))); + return *this; + } + + // table-like + + private: + + template + using enable_if_table_like_t = cxx::enable_if_t>, + detail::is_map, + cxx::negation>, + cxx::negation> + >::value, std::nullptr_t>; + + public: + + template = nullptr> + basic_value(T x) + : basic_value(std::move(x), table_format_info{}, std::vector{}, region_type{}) + {} + template = nullptr> + basic_value(T x, table_format_info fmt) + : basic_value(std::move(x), std::move(fmt), std::vector{}, region_type{}) + {} + template = nullptr> + basic_value(T x, std::vector com) + : basic_value(std::move(x), table_format_info{}, std::move(com), region_type{}) + {} + template = nullptr> + basic_value(T x, table_format_info fmt, std::vector com) + : basic_value(std::move(x), fmt, std::move(com), region_type{}) + {} + template = nullptr> + basic_value(T x, table_format_info fmt, + std::vector com, region_type reg) + : type_(value_t::table), table_(table_storage( + detail::storage(table_type( + std::make_move_iterator(x.begin()), + std::make_move_iterator(x.end()) + )), std::move(fmt) + )), region_(std::move(reg)), comments_(std::move(com)) + {} + template = nullptr> + basic_value& operator=(T x) + { + table_format_info fmt; + if(this->is_table()) + { + fmt = this->as_table_fmt(); + } + this->cleanup(); + this->type_ = value_t::table; + this->region_ = region_type{}; + + table_type t(std::make_move_iterator(x.begin()), + std::make_move_iterator(x.end())); + assigner(this->table_, table_storage( + detail::storage(std::move(t)), std::move(fmt))); + return *this; + } + + // }}} + + // constructor (user_defined) ========================================= {{{ + + template::value, std::nullptr_t> = nullptr> + basic_value(const T& ud) + : basic_value( + into>::template into_toml(ud)) + {} + template::value, std::nullptr_t> = nullptr> + basic_value(const T& ud, std::vector com) + : basic_value( + into>::template into_toml(ud), + std::move(com)) + {} + template::value, std::nullptr_t> = nullptr> + basic_value& operator=(const T& ud) + { + *this = into>::template into_toml(ud); + return *this; + } + + template, + cxx::negation> + >::value, std::nullptr_t> = nullptr> + basic_value(const T& ud): basic_value(ud.into_toml()) {} + + template, + cxx::negation> + >::value, std::nullptr_t> = nullptr> + basic_value(const T& ud, std::vector com) + : basic_value(ud.into_toml(), std::move(com)) + {} + template, + cxx::negation> + >::value, std::nullptr_t> = nullptr> + basic_value& operator=(const T& ud) + { + *this = ud.into_toml(); + return *this; + } + + template, + cxx::negation> + >::value, std::nullptr_t> = nullptr> + basic_value(const T& ud): basic_value(ud.template into_toml()) {} + + template, + cxx::negation> + >::value, std::nullptr_t> = nullptr> + basic_value(const T& ud, std::vector com) + : basic_value(ud.template into_toml(), std::move(com)) + {} + template, + cxx::negation> + >::value, std::nullptr_t> = nullptr> + basic_value& operator=(const T& ud) + { + *this = ud.template into_toml(); + return *this; + } + // }}} + + // empty value with region info ======================================= {{{ + + // mainly for `null` extension + basic_value(detail::none_t, region_type reg) noexcept + : type_(value_t::empty), empty_('\0'), region_(std::move(reg)), comments_{} + {} + + // }}} + + // type checking ====================================================== {{{ + + template, value_type>::value, + std::nullptr_t> = nullptr> + bool is() const noexcept + { + return detail::type_to_enum::value == this->type_; + } + bool is(value_t t) const noexcept {return t == this->type_;} + + bool is_empty() const noexcept {return this->is(value_t::empty );} + bool is_boolean() const noexcept {return this->is(value_t::boolean );} + bool is_integer() const noexcept {return this->is(value_t::integer );} + bool is_floating() const noexcept {return this->is(value_t::floating );} + bool is_string() const noexcept {return this->is(value_t::string );} + bool is_offset_datetime() const noexcept {return this->is(value_t::offset_datetime);} + bool is_local_datetime() const noexcept {return this->is(value_t::local_datetime );} + bool is_local_date() const noexcept {return this->is(value_t::local_date );} + bool is_local_time() const noexcept {return this->is(value_t::local_time );} + bool is_array() const noexcept {return this->is(value_t::array );} + bool is_table() const noexcept {return this->is(value_t::table );} + + bool is_array_of_tables() const noexcept + { + if( ! this->is_array()) {return false;} + const auto& a = this->as_array(std::nothrow); // already checked. + + // when you define [[array.of.tables]], at least one empty table will be + // assigned. In case of array of inline tables, `array_of_tables = []`, + // there is no reason to consider this as an array of *tables*. + // So empty array is not an array-of-tables. + if(a.empty()) {return false;} + + // since toml v1.0.0 allows array of heterogeneous types, we need to + // check all the elements. if any of the elements is not a table, it + // is a heterogeneous array and cannot be expressed by `[[aot]]` form. + for(const auto& e : a) + { + if( ! e.is_table()) {return false;} + } + return true; + } + + value_t type() const noexcept {return type_;} + + // }}} + + // as_xxx (noexcept) version ========================================== {{{ + + template + detail::enum_to_type_t> const& + as(const std::nothrow_t&) const noexcept + { + return detail::getter::get_nothrow(*this); + } + template + detail::enum_to_type_t>& + as(const std::nothrow_t&) noexcept + { + return detail::getter::get_nothrow(*this); + } + + boolean_type const& as_boolean (const std::nothrow_t&) const noexcept {return this->boolean_.value;} + integer_type const& as_integer (const std::nothrow_t&) const noexcept {return this->integer_.value;} + floating_type const& as_floating (const std::nothrow_t&) const noexcept {return this->floating_.value;} + string_type const& as_string (const std::nothrow_t&) const noexcept {return this->string_.value;} + offset_datetime_type const& as_offset_datetime(const std::nothrow_t&) const noexcept {return this->offset_datetime_.value;} + local_datetime_type const& as_local_datetime (const std::nothrow_t&) const noexcept {return this->local_datetime_.value;} + local_date_type const& as_local_date (const std::nothrow_t&) const noexcept {return this->local_date_.value;} + local_time_type const& as_local_time (const std::nothrow_t&) const noexcept {return this->local_time_.value;} + array_type const& as_array (const std::nothrow_t&) const noexcept {return this->array_.value.get();} + table_type const& as_table (const std::nothrow_t&) const noexcept {return this->table_.value.get();} + + boolean_type & as_boolean (const std::nothrow_t&) noexcept {return this->boolean_.value;} + integer_type & as_integer (const std::nothrow_t&) noexcept {return this->integer_.value;} + floating_type & as_floating (const std::nothrow_t&) noexcept {return this->floating_.value;} + string_type & as_string (const std::nothrow_t&) noexcept {return this->string_.value;} + offset_datetime_type& as_offset_datetime(const std::nothrow_t&) noexcept {return this->offset_datetime_.value;} + local_datetime_type & as_local_datetime (const std::nothrow_t&) noexcept {return this->local_datetime_.value;} + local_date_type & as_local_date (const std::nothrow_t&) noexcept {return this->local_date_.value;} + local_time_type & as_local_time (const std::nothrow_t&) noexcept {return this->local_time_.value;} + array_type & as_array (const std::nothrow_t&) noexcept {return this->array_.value.get();} + table_type & as_table (const std::nothrow_t&) noexcept {return this->table_.value.get();} + + // }}} + + // as_xxx (throw) ===================================================== {{{ + + template + detail::enum_to_type_t> const& as() const + { + return detail::getter::get(*this); + } + template + detail::enum_to_type_t>& as() + { + return detail::getter::get(*this); + } + + boolean_type const& as_boolean() const + { + if(this->type_ != value_t::boolean) + { + this->throw_bad_cast("toml::value::as_boolean()", value_t::boolean); + } + return this->boolean_.value; + } + integer_type const& as_integer() const + { + if(this->type_ != value_t::integer) + { + this->throw_bad_cast("toml::value::as_integer()", value_t::integer); + } + return this->integer_.value; + } + floating_type const& as_floating() const + { + if(this->type_ != value_t::floating) + { + this->throw_bad_cast("toml::value::as_floating()", value_t::floating); + } + return this->floating_.value; + } + string_type const& as_string() const + { + if(this->type_ != value_t::string) + { + this->throw_bad_cast("toml::value::as_string()", value_t::string); + } + return this->string_.value; + } + offset_datetime_type const& as_offset_datetime() const + { + if(this->type_ != value_t::offset_datetime) + { + this->throw_bad_cast("toml::value::as_offset_datetime()", value_t::offset_datetime); + } + return this->offset_datetime_.value; + } + local_datetime_type const& as_local_datetime() const + { + if(this->type_ != value_t::local_datetime) + { + this->throw_bad_cast("toml::value::as_local_datetime()", value_t::local_datetime); + } + return this->local_datetime_.value; + } + local_date_type const& as_local_date() const + { + if(this->type_ != value_t::local_date) + { + this->throw_bad_cast("toml::value::as_local_date()", value_t::local_date); + } + return this->local_date_.value; + } + local_time_type const& as_local_time() const + { + if(this->type_ != value_t::local_time) + { + this->throw_bad_cast("toml::value::as_local_time()", value_t::local_time); + } + return this->local_time_.value; + } + array_type const& as_array() const + { + if(this->type_ != value_t::array) + { + this->throw_bad_cast("toml::value::as_array()", value_t::array); + } + return this->array_.value.get(); + } + table_type const& as_table() const + { + if(this->type_ != value_t::table) + { + this->throw_bad_cast("toml::value::as_table()", value_t::table); + } + return this->table_.value.get(); + } + + // ------------------------------------------------------------------------ + // nonconst reference + + boolean_type& as_boolean() + { + if(this->type_ != value_t::boolean) + { + this->throw_bad_cast("toml::value::as_boolean()", value_t::boolean); + } + return this->boolean_.value; + } + integer_type& as_integer() + { + if(this->type_ != value_t::integer) + { + this->throw_bad_cast("toml::value::as_integer()", value_t::integer); + } + return this->integer_.value; + } + floating_type& as_floating() + { + if(this->type_ != value_t::floating) + { + this->throw_bad_cast("toml::value::as_floating()", value_t::floating); + } + return this->floating_.value; + } + string_type& as_string() + { + if(this->type_ != value_t::string) + { + this->throw_bad_cast("toml::value::as_string()", value_t::string); + } + return this->string_.value; + } + offset_datetime_type& as_offset_datetime() + { + if(this->type_ != value_t::offset_datetime) + { + this->throw_bad_cast("toml::value::as_offset_datetime()", value_t::offset_datetime); + } + return this->offset_datetime_.value; + } + local_datetime_type& as_local_datetime() + { + if(this->type_ != value_t::local_datetime) + { + this->throw_bad_cast("toml::value::as_local_datetime()", value_t::local_datetime); + } + return this->local_datetime_.value; + } + local_date_type& as_local_date() + { + if(this->type_ != value_t::local_date) + { + this->throw_bad_cast("toml::value::as_local_date()", value_t::local_date); + } + return this->local_date_.value; + } + local_time_type& as_local_time() + { + if(this->type_ != value_t::local_time) + { + this->throw_bad_cast("toml::value::as_local_time()", value_t::local_time); + } + return this->local_time_.value; + } + array_type& as_array() + { + if(this->type_ != value_t::array) + { + this->throw_bad_cast("toml::value::as_array()", value_t::array); + } + return this->array_.value.get(); + } + table_type& as_table() + { + if(this->type_ != value_t::table) + { + this->throw_bad_cast("toml::value::as_table()", value_t::table); + } + return this->table_.value.get(); + } + + // }}} + + // format accessors (noexcept) ======================================== {{{ + + template + detail::enum_to_fmt_type_t const& + as_fmt(const std::nothrow_t&) const noexcept + { + return detail::getter::get_fmt_nothrow(*this); + } + template + detail::enum_to_fmt_type_t& + as_fmt(const std::nothrow_t&) noexcept + { + return detail::getter::get_fmt_nothrow(*this); + } + + boolean_format_info & as_boolean_fmt (const std::nothrow_t&) noexcept {return this->boolean_.format;} + integer_format_info & as_integer_fmt (const std::nothrow_t&) noexcept {return this->integer_.format;} + floating_format_info & as_floating_fmt (const std::nothrow_t&) noexcept {return this->floating_.format;} + string_format_info & as_string_fmt (const std::nothrow_t&) noexcept {return this->string_.format;} + offset_datetime_format_info& as_offset_datetime_fmt(const std::nothrow_t&) noexcept {return this->offset_datetime_.format;} + local_datetime_format_info & as_local_datetime_fmt (const std::nothrow_t&) noexcept {return this->local_datetime_.format;} + local_date_format_info & as_local_date_fmt (const std::nothrow_t&) noexcept {return this->local_date_.format;} + local_time_format_info & as_local_time_fmt (const std::nothrow_t&) noexcept {return this->local_time_.format;} + array_format_info & as_array_fmt (const std::nothrow_t&) noexcept {return this->array_.format;} + table_format_info & as_table_fmt (const std::nothrow_t&) noexcept {return this->table_.format;} + + boolean_format_info const& as_boolean_fmt (const std::nothrow_t&) const noexcept {return this->boolean_.format;} + integer_format_info const& as_integer_fmt (const std::nothrow_t&) const noexcept {return this->integer_.format;} + floating_format_info const& as_floating_fmt (const std::nothrow_t&) const noexcept {return this->floating_.format;} + string_format_info const& as_string_fmt (const std::nothrow_t&) const noexcept {return this->string_.format;} + offset_datetime_format_info const& as_offset_datetime_fmt(const std::nothrow_t&) const noexcept {return this->offset_datetime_.format;} + local_datetime_format_info const& as_local_datetime_fmt (const std::nothrow_t&) const noexcept {return this->local_datetime_.format;} + local_date_format_info const& as_local_date_fmt (const std::nothrow_t&) const noexcept {return this->local_date_.format;} + local_time_format_info const& as_local_time_fmt (const std::nothrow_t&) const noexcept {return this->local_time_.format;} + array_format_info const& as_array_fmt (const std::nothrow_t&) const noexcept {return this->array_.format;} + table_format_info const& as_table_fmt (const std::nothrow_t&) const noexcept {return this->table_.format;} + + // }}} + + // format accessors (throw) =========================================== {{{ + + template + detail::enum_to_fmt_type_t const& as_fmt() const + { + return detail::getter::get_fmt(*this); + } + template + detail::enum_to_fmt_type_t& as_fmt() + { + return detail::getter::get_fmt(*this); + } + + boolean_format_info const& as_boolean_fmt() const + { + if(this->type_ != value_t::boolean) + { + this->throw_bad_cast("toml::value::as_boolean_fmt()", value_t::boolean); + } + return this->boolean_.format; + } + integer_format_info const& as_integer_fmt() const + { + if(this->type_ != value_t::integer) + { + this->throw_bad_cast("toml::value::as_integer_fmt()", value_t::integer); + } + return this->integer_.format; + } + floating_format_info const& as_floating_fmt() const + { + if(this->type_ != value_t::floating) + { + this->throw_bad_cast("toml::value::as_floating_fmt()", value_t::floating); + } + return this->floating_.format; + } + string_format_info const& as_string_fmt() const + { + if(this->type_ != value_t::string) + { + this->throw_bad_cast("toml::value::as_string_fmt()", value_t::string); + } + return this->string_.format; + } + offset_datetime_format_info const& as_offset_datetime_fmt() const + { + if(this->type_ != value_t::offset_datetime) + { + this->throw_bad_cast("toml::value::as_offset_datetime_fmt()", value_t::offset_datetime); + } + return this->offset_datetime_.format; + } + local_datetime_format_info const& as_local_datetime_fmt() const + { + if(this->type_ != value_t::local_datetime) + { + this->throw_bad_cast("toml::value::as_local_datetime_fmt()", value_t::local_datetime); + } + return this->local_datetime_.format; + } + local_date_format_info const& as_local_date_fmt() const + { + if(this->type_ != value_t::local_date) + { + this->throw_bad_cast("toml::value::as_local_date_fmt()", value_t::local_date); + } + return this->local_date_.format; + } + local_time_format_info const& as_local_time_fmt() const + { + if(this->type_ != value_t::local_time) + { + this->throw_bad_cast("toml::value::as_local_time_fmt()", value_t::local_time); + } + return this->local_time_.format; + } + array_format_info const& as_array_fmt() const + { + if(this->type_ != value_t::array) + { + this->throw_bad_cast("toml::value::as_array_fmt()", value_t::array); + } + return this->array_.format; + } + table_format_info const& as_table_fmt() const + { + if(this->type_ != value_t::table) + { + this->throw_bad_cast("toml::value::as_table_fmt()", value_t::table); + } + return this->table_.format; + } + + // ------------------------------------------------------------------------ + // nonconst reference + + boolean_format_info& as_boolean_fmt() + { + if(this->type_ != value_t::boolean) + { + this->throw_bad_cast("toml::value::as_boolean_fmt()", value_t::boolean); + } + return this->boolean_.format; + } + integer_format_info& as_integer_fmt() + { + if(this->type_ != value_t::integer) + { + this->throw_bad_cast("toml::value::as_integer_fmt()", value_t::integer); + } + return this->integer_.format; + } + floating_format_info& as_floating_fmt() + { + if(this->type_ != value_t::floating) + { + this->throw_bad_cast("toml::value::as_floating_fmt()", value_t::floating); + } + return this->floating_.format; + } + string_format_info& as_string_fmt() + { + if(this->type_ != value_t::string) + { + this->throw_bad_cast("toml::value::as_string_fmt()", value_t::string); + } + return this->string_.format; + } + offset_datetime_format_info& as_offset_datetime_fmt() + { + if(this->type_ != value_t::offset_datetime) + { + this->throw_bad_cast("toml::value::as_offset_datetime_fmt()", value_t::offset_datetime); + } + return this->offset_datetime_.format; + } + local_datetime_format_info& as_local_datetime_fmt() + { + if(this->type_ != value_t::local_datetime) + { + this->throw_bad_cast("toml::value::as_local_datetime_fmt()", value_t::local_datetime); + } + return this->local_datetime_.format; + } + local_date_format_info& as_local_date_fmt() + { + if(this->type_ != value_t::local_date) + { + this->throw_bad_cast("toml::value::as_local_date_fmt()", value_t::local_date); + } + return this->local_date_.format; + } + local_time_format_info& as_local_time_fmt() + { + if(this->type_ != value_t::local_time) + { + this->throw_bad_cast("toml::value::as_local_time_fmt()", value_t::local_time); + } + return this->local_time_.format; + } + array_format_info& as_array_fmt() + { + if(this->type_ != value_t::array) + { + this->throw_bad_cast("toml::value::as_array_fmt()", value_t::array); + } + return this->array_.format; + } + table_format_info& as_table_fmt() + { + if(this->type_ != value_t::table) + { + this->throw_bad_cast("toml::value::as_table_fmt()", value_t::table); + } + return this->table_.format; + } + // }}} + + // table accessors ==================================================== {{{ + + value_type& at(const key_type& k) + { + if(!this->is_table()) + { + this->throw_bad_cast("toml::value::at(key_type)", value_t::table); + } + auto& table = this->as_table(std::nothrow); + const auto found = table.find(k); + if(found == table.end()) + { + this->throw_key_not_found_error("toml::value::at", k); + } + assert(found->first == k); + return found->second; + } + value_type const& at(const key_type& k) const + { + if(!this->is_table()) + { + this->throw_bad_cast("toml::value::at(key_type)", value_t::table); + } + const auto& table = this->as_table(std::nothrow); + const auto found = table.find(k); + if(found == table.end()) + { + this->throw_key_not_found_error("toml::value::at", k); + } + assert(found->first == k); + return found->second; + } + value_type& operator[](const key_type& k) + { + if(this->is_empty()) + { + (*this) = table_type{}; + } + else if( ! this->is_table()) // initialized, but not a table + { + this->throw_bad_cast("toml::value::operator[](key_type)", value_t::table); + } + return (this->as_table(std::nothrow))[k]; + } + std::size_t count(const key_type& k) const + { + if(!this->is_table()) + { + this->throw_bad_cast("toml::value::count(key_type)", value_t::table); + } + return this->as_table(std::nothrow).count(k); + } + bool contains(const key_type& k) const + { + if(!this->is_table()) + { + this->throw_bad_cast("toml::value::contains(key_type)", value_t::table); + } + const auto& table = this->as_table(std::nothrow); + return table.find(k) != table.end(); + } + // }}} + + // array accessors ==================================================== {{{ + + value_type& at(const std::size_t idx) + { + if(!this->is_array()) + { + this->throw_bad_cast("toml::value::at(idx)", value_t::array); + } + auto& ar = this->as_array(std::nothrow); + + if(ar.size() <= idx) + { + std::ostringstream oss; + oss << "actual length (" << ar.size() + << ") is shorter than the specified index (" << idx << ")."; + throw std::out_of_range(format_error( + "toml::value::at(idx): no element corresponding to the index", + this->location(), oss.str() + )); + } + return ar.at(idx); + } + value_type const& at(const std::size_t idx) const + { + if(!this->is_array()) + { + this->throw_bad_cast("toml::value::at(idx)", value_t::array); + } + const auto& ar = this->as_array(std::nothrow); + + if(ar.size() <= idx) + { + std::ostringstream oss; + oss << "actual length (" << ar.size() + << ") is shorter than the specified index (" << idx << ")."; + + throw std::out_of_range(format_error( + "toml::value::at(idx): no element corresponding to the index", + this->location(), oss.str() + )); + } + return ar.at(idx); + } + + value_type& operator[](const std::size_t idx) noexcept + { + // no check... + return this->as_array(std::nothrow)[idx]; + } + value_type const& operator[](const std::size_t idx) const noexcept + { + // no check... + return this->as_array(std::nothrow)[idx]; + } + + void push_back(const value_type& x) + { + if(!this->is_array()) + { + this->throw_bad_cast("toml::value::push_back(idx)", value_t::array); + } + this->as_array(std::nothrow).push_back(x); + return; + } + void push_back(value_type&& x) + { + if(!this->is_array()) + { + this->throw_bad_cast("toml::value::push_back(idx)", value_t::array); + } + this->as_array(std::nothrow).push_back(std::move(x)); + return; + } + + template + value_type& emplace_back(Ts&& ... args) + { + if(!this->is_array()) + { + this->throw_bad_cast("toml::value::emplace_back(idx)", value_t::array); + } + auto& ar = this->as_array(std::nothrow); + ar.emplace_back(std::forward(args) ...); + return ar.back(); + } + + std::size_t size() const + { + switch(this->type_) + { + case value_t::array: + { + return this->as_array(std::nothrow).size(); + } + case value_t::table: + { + return this->as_table(std::nothrow).size(); + } + case value_t::string: + { + return this->as_string(std::nothrow).size(); + } + default: + { + throw type_error(format_error( + "toml::value::size(): bad_cast to container types", + this->location(), + "the actual type is " + to_string(this->type_) + ), this->location()); + } + } + } + + // }}} + + source_location location() const + { + return source_location(this->region_); + } + + comment_type const& comments() const noexcept {return this->comments_;} + comment_type& comments() noexcept {return this->comments_;} + + private: + + // private helper functions =========================================== {{{ + + void cleanup() noexcept + { + switch(this->type_) + { + case value_t::boolean : { boolean_ .~boolean_storage (); break; } + case value_t::integer : { integer_ .~integer_storage (); break; } + case value_t::floating : { floating_ .~floating_storage (); break; } + case value_t::string : { string_ .~string_storage (); break; } + case value_t::offset_datetime : { offset_datetime_.~offset_datetime_storage (); break; } + case value_t::local_datetime : { local_datetime_ .~local_datetime_storage (); break; } + case value_t::local_date : { local_date_ .~local_date_storage (); break; } + case value_t::local_time : { local_time_ .~local_time_storage (); break; } + case value_t::array : { array_ .~array_storage (); break; } + case value_t::table : { table_ .~table_storage (); break; } + default : { break; } + } + this->type_ = value_t::empty; + return; + } + + template + static void assigner(T& dst, U&& v) + { + const auto tmp = ::new(std::addressof(dst)) T(std::forward(v)); + assert(tmp == std::addressof(dst)); + (void)tmp; + } + + [[noreturn]] + void throw_bad_cast(const std::string& funcname, const value_t ty) const + { + throw type_error(format_error(detail::make_type_error(*this, funcname, ty)), + this->location()); + } + + [[noreturn]] + void throw_key_not_found_error(const std::string& funcname, const key_type& key) const + { + throw std::out_of_range(format_error( + detail::make_not_found_error(*this, funcname, key))); + } + + template + friend void detail::change_region_of_value(basic_value&, const basic_value&); + + template + friend class basic_value; + + // }}} + + private: + + using boolean_storage = detail::value_with_format; + using integer_storage = detail::value_with_format; + using floating_storage = detail::value_with_format; + using string_storage = detail::value_with_format; + using offset_datetime_storage = detail::value_with_format; + using local_datetime_storage = detail::value_with_format; + using local_date_storage = detail::value_with_format; + using local_time_storage = detail::value_with_format; + using array_storage = detail::value_with_format, array_format_info >; + using table_storage = detail::value_with_format, table_format_info >; + + private: + + value_t type_; + union + { + char empty_; // the smallest type + boolean_storage boolean_; + integer_storage integer_; + floating_storage floating_; + string_storage string_; + offset_datetime_storage offset_datetime_; + local_datetime_storage local_datetime_; + local_date_storage local_date_; + local_time_storage local_time_; + array_storage array_; + table_storage table_; + }; + region_type region_; + comment_type comments_; +}; + +template +bool operator==(const basic_value& lhs, const basic_value& rhs) +{ + if(lhs.type() != rhs.type()) {return false;} + if(lhs.comments() != rhs.comments()) {return false;} + + switch(lhs.type()) + { + case value_t::boolean : + { + return lhs.as_boolean() == rhs.as_boolean(); + } + case value_t::integer : + { + return lhs.as_integer() == rhs.as_integer(); + } + case value_t::floating : + { + return lhs.as_floating() == rhs.as_floating(); + } + case value_t::string : + { + return lhs.as_string() == rhs.as_string(); + } + case value_t::offset_datetime: + { + return lhs.as_offset_datetime() == rhs.as_offset_datetime(); + } + case value_t::local_datetime: + { + return lhs.as_local_datetime() == rhs.as_local_datetime(); + } + case value_t::local_date: + { + return lhs.as_local_date() == rhs.as_local_date(); + } + case value_t::local_time: + { + return lhs.as_local_time() == rhs.as_local_time(); + } + case value_t::array : + { + return lhs.as_array() == rhs.as_array(); + } + case value_t::table : + { + return lhs.as_table() == rhs.as_table(); + } + case value_t::empty : {return true; } + default: {return false;} + } +} + +template +bool operator!=(const basic_value& lhs, const basic_value& rhs) +{ + return !(lhs == rhs); +} + +template +cxx::enable_if_t::array_type>, + detail::is_comparable::table_type> + >::value, bool> +operator<(const basic_value& lhs, const basic_value& rhs) +{ + if(lhs.type() != rhs.type()) + { + return (lhs.type() < rhs.type()); + } + switch(lhs.type()) + { + case value_t::boolean : + { + return lhs.as_boolean() < rhs.as_boolean() || + (lhs.as_boolean() == rhs.as_boolean() && + lhs.comments() < rhs.comments()); + } + case value_t::integer : + { + return lhs.as_integer() < rhs.as_integer() || + (lhs.as_integer() == rhs.as_integer() && + lhs.comments() < rhs.comments()); + } + case value_t::floating : + { + return lhs.as_floating() < rhs.as_floating() || + (lhs.as_floating() == rhs.as_floating() && + lhs.comments() < rhs.comments()); + } + case value_t::string : + { + return lhs.as_string() < rhs.as_string() || + (lhs.as_string() == rhs.as_string() && + lhs.comments() < rhs.comments()); + } + case value_t::offset_datetime: + { + return lhs.as_offset_datetime() < rhs.as_offset_datetime() || + (lhs.as_offset_datetime() == rhs.as_offset_datetime() && + lhs.comments() < rhs.comments()); + } + case value_t::local_datetime: + { + return lhs.as_local_datetime() < rhs.as_local_datetime() || + (lhs.as_local_datetime() == rhs.as_local_datetime() && + lhs.comments() < rhs.comments()); + } + case value_t::local_date: + { + return lhs.as_local_date() < rhs.as_local_date() || + (lhs.as_local_date() == rhs.as_local_date() && + lhs.comments() < rhs.comments()); + } + case value_t::local_time: + { + return lhs.as_local_time() < rhs.as_local_time() || + (lhs.as_local_time() == rhs.as_local_time() && + lhs.comments() < rhs.comments()); + } + case value_t::array : + { + return lhs.as_array() < rhs.as_array() || + (lhs.as_array() == rhs.as_array() && + lhs.comments() < rhs.comments()); + } + case value_t::table : + { + return lhs.as_table() < rhs.as_table() || + (lhs.as_table() == rhs.as_table() && + lhs.comments() < rhs.comments()); + } + case value_t::empty : + { + return lhs.comments() < rhs.comments(); + } + default: + { + return lhs.comments() < rhs.comments(); + } + } +} + +template +cxx::enable_if_t::array_type>, + detail::is_comparable::table_type> + >::value, bool> +operator<=(const basic_value& lhs, const basic_value& rhs) +{ + return (lhs < rhs) || (lhs == rhs); +} +template +cxx::enable_if_t::array_type>, + detail::is_comparable::table_type> + >::value, bool> +operator>(const basic_value& lhs, const basic_value& rhs) +{ + return !(lhs <= rhs); +} +template +cxx::enable_if_t::array_type>, + detail::is_comparable::table_type> + >::value, bool> +operator>=(const basic_value& lhs, const basic_value& rhs) +{ + return !(lhs < rhs); +} + +// error_info helper +namespace detail +{ +template +error_info make_error_info_rec(error_info e, + const basic_value& v, std::string msg, Ts&& ... tail) +{ + return make_error_info_rec(std::move(e), v.location(), std::move(msg), std::forward(tail)...); +} +} // detail + +template +error_info make_error_info( + std::string title, const basic_value& v, std::string msg, Ts&& ... tail) +{ + return make_error_info(std::move(title), + v.location(), std::move(msg), std::forward(tail)...); +} +template +std::string format_error(std::string title, + const basic_value& v, std::string msg, Ts&& ... tail) +{ + return format_error(std::move(title), + v.location(), std::move(msg), std::forward(tail)...); +} + +namespace detail +{ + +template +error_info make_type_error(const basic_value& v, const std::string& fname, const value_t ty) +{ + return make_error_info(fname + ": bad_cast to " + to_string(ty), + v.location(), "the actual type is " + to_string(v.type())); +} +template +error_info make_not_found_error(const basic_value& v, const std::string& fname, const typename basic_value::key_type& key) +{ + const auto loc = v.location(); + const std::string title = fname + ": key \"" + string_conv(key) + "\" not found"; + + std::vector> locs; + if( ! loc.is_ok()) + { + return error_info(title, locs); + } + + if(loc.first_line_number() == 1 && loc.first_column_number() == 1 && loc.length() == 1) + { + // The top-level table has its region at the 0th character of the file. + // That means that, in the case when a key is not found in the top-level + // table, the error message points to the first character. If the file has + // the first table at the first line, the error message would be like this. + // ```console + // [error] key "a" not found + // --> example.toml + // | + // 1 | [table] + // | ^------ in this table + // ``` + // It actually points to the top-level table at the first character, not + // `[table]`. But it is too confusing. To avoid the confusion, the error + // message should explicitly say "key not found in the top-level table". + locs.emplace_back(v.location(), "at the top-level table"); + } + else + { + locs.emplace_back(v.location(), "in this table"); + } + return error_info(title, locs); +} + +#define TOML11_DETAIL_GENERATE_COMPTIME_GETTER(ty) \ + template \ + struct getter \ + { \ + using value_type = basic_value; \ + using result_type = enum_to_type_t; \ + using format_type = enum_to_fmt_type_t; \ + \ + static result_type& get(value_type& v) \ + { \ + return v.as_ ## ty(); \ + } \ + static result_type const& get(const value_type& v) \ + { \ + return v.as_ ## ty(); \ + } \ + \ + static result_type& get_nothrow(value_type& v) noexcept \ + { \ + return v.as_ ## ty(std::nothrow); \ + } \ + static result_type const& get_nothrow(const value_type& v) noexcept \ + { \ + return v.as_ ## ty(std::nothrow); \ + } \ + \ + static format_type& get_fmt(value_type& v) \ + { \ + return v.as_ ## ty ## _fmt(); \ + } \ + static format_type const& get_fmt(const value_type& v) \ + { \ + return v.as_ ## ty ## _fmt(); \ + } \ + \ + static format_type& get_fmt_nothrow(value_type& v) noexcept \ + { \ + return v.as_ ## ty ## _fmt(std::nothrow); \ + } \ + static format_type const& get_fmt_nothrow(const value_type& v) noexcept \ + { \ + return v.as_ ## ty ## _fmt(std::nothrow); \ + } \ + }; + +TOML11_DETAIL_GENERATE_COMPTIME_GETTER(boolean ) +TOML11_DETAIL_GENERATE_COMPTIME_GETTER(integer ) +TOML11_DETAIL_GENERATE_COMPTIME_GETTER(floating ) +TOML11_DETAIL_GENERATE_COMPTIME_GETTER(string ) +TOML11_DETAIL_GENERATE_COMPTIME_GETTER(offset_datetime) +TOML11_DETAIL_GENERATE_COMPTIME_GETTER(local_datetime ) +TOML11_DETAIL_GENERATE_COMPTIME_GETTER(local_date ) +TOML11_DETAIL_GENERATE_COMPTIME_GETTER(local_time ) +TOML11_DETAIL_GENERATE_COMPTIME_GETTER(array ) +TOML11_DETAIL_GENERATE_COMPTIME_GETTER(table ) + +#undef TOML11_DETAIL_GENERATE_COMPTIME_GETTER + +template +void change_region_of_value(basic_value& dst, const basic_value& src) +{ + dst.region_ = std::move(src.region_); + return; +} + +} // namespace detail +} // namespace toml +#endif // TOML11_VALUE_HPP +#ifndef TOML11_VISIT_HPP +#define TOML11_VISIT_HPP + + +namespace toml +{ + +template +cxx::return_type_of_t::boolean_type&> +visit(Visitor&& visitor, const basic_value& v) +{ + switch(v.type()) + { + case value_t::boolean : {return visitor(v.as_boolean ());} + case value_t::integer : {return visitor(v.as_integer ());} + case value_t::floating : {return visitor(v.as_floating ());} + case value_t::string : {return visitor(v.as_string ());} + case value_t::offset_datetime: {return visitor(v.as_offset_datetime());} + case value_t::local_datetime : {return visitor(v.as_local_datetime ());} + case value_t::local_date : {return visitor(v.as_local_date ());} + case value_t::local_time : {return visitor(v.as_local_time ());} + case value_t::array : {return visitor(v.as_array ());} + case value_t::table : {return visitor(v.as_table ());} + case value_t::empty : break; + default: break; + } + throw type_error(format_error("[error] toml::visit: toml::basic_value " + "does not have any valid type.", v.location(), "here"), v.location()); +} + +template +cxx::return_type_of_t::boolean_type&> +visit(Visitor&& visitor, basic_value& v) +{ + switch(v.type()) + { + case value_t::boolean : {return visitor(v.as_boolean ());} + case value_t::integer : {return visitor(v.as_integer ());} + case value_t::floating : {return visitor(v.as_floating ());} + case value_t::string : {return visitor(v.as_string ());} + case value_t::offset_datetime: {return visitor(v.as_offset_datetime());} + case value_t::local_datetime : {return visitor(v.as_local_datetime ());} + case value_t::local_date : {return visitor(v.as_local_date ());} + case value_t::local_time : {return visitor(v.as_local_time ());} + case value_t::array : {return visitor(v.as_array ());} + case value_t::table : {return visitor(v.as_table ());} + case value_t::empty : break; + default: break; + } + throw type_error(format_error("[error] toml::visit: toml::basic_value " + "does not have any valid type.", v.location(), "here"), v.location()); +} + +template +cxx::return_type_of_t::boolean_type&&> +visit(Visitor&& visitor, basic_value&& v) +{ + switch(v.type()) + { + case value_t::boolean : {return visitor(std::move(v.as_boolean ()));} + case value_t::integer : {return visitor(std::move(v.as_integer ()));} + case value_t::floating : {return visitor(std::move(v.as_floating ()));} + case value_t::string : {return visitor(std::move(v.as_string ()));} + case value_t::offset_datetime: {return visitor(std::move(v.as_offset_datetime()));} + case value_t::local_datetime : {return visitor(std::move(v.as_local_datetime ()));} + case value_t::local_date : {return visitor(std::move(v.as_local_date ()));} + case value_t::local_time : {return visitor(std::move(v.as_local_time ()));} + case value_t::array : {return visitor(std::move(v.as_array ()));} + case value_t::table : {return visitor(std::move(v.as_table ()));} + case value_t::empty : break; + default: break; + } + throw type_error(format_error("[error] toml::visit: toml::basic_value " + "does not have any valid type.", v.location(), "here"), v.location()); +} + +} // toml +#endif // TOML11_VISIT_HPP +#ifndef TOML11_TYPES_HPP +#define TOML11_TYPES_HPP + + +#include +#include +#include +#include +#include +#include + +#include +#include + +namespace toml +{ + +// forward decl +template +class basic_value; + +// when you use a special integer type as toml::value::integer_type, parse must +// be able to read it. So, type_config has static member functions that read the +// integer_type as {dec, hex, oct, bin}-integer. But, in most cases, operator<< +// is enough. To make config easy, we provide the default read functions. +// +// Before this functions is called, syntax is checked and prefix(`0x` etc) and +// spacer(`_`) are removed. + +template +result +read_dec_int(const std::string& str, const source_location src) +{ + constexpr auto max_digits = std::numeric_limits::digits; + assert( ! str.empty()); + + T val{0}; + std::istringstream iss(str); + iss >> val; + if(iss.fail()) + { + return err(make_error_info("toml::parse_dec_integer: " + "too large integer: current max digits = 2^" + std::to_string(max_digits), + std::move(src), "must be < 2^" + std::to_string(max_digits))); + } + return ok(val); +} + +template +result +read_hex_int(const std::string& str, const source_location src) +{ + constexpr auto max_digits = std::numeric_limits::digits; + assert( ! str.empty()); + + T val{0}; + std::istringstream iss(str); + iss >> std::hex >> val; + if(iss.fail()) + { + return err(make_error_info("toml::parse_hex_integer: " + "too large integer: current max value = 2^" + std::to_string(max_digits), + std::move(src), "must be < 2^" + std::to_string(max_digits))); + } + return ok(val); +} + +template +result +read_oct_int(const std::string& str, const source_location src) +{ + constexpr auto max_digits = std::numeric_limits::digits; + assert( ! str.empty()); + + T val{0}; + std::istringstream iss(str); + iss >> std::oct >> val; + if(iss.fail()) + { + return err(make_error_info("toml::parse_oct_integer: " + "too large integer: current max value = 2^" + std::to_string(max_digits), + std::move(src), "must be < 2^" + std::to_string(max_digits))); + } + return ok(val); +} + +template +result +read_bin_int(const std::string& str, const source_location src) +{ + constexpr auto is_bounded = std::numeric_limits::is_bounded; + constexpr auto max_digits = std::numeric_limits::digits; + const auto max_value = (std::numeric_limits::max)(); + + T val{0}; + T base{1}; + for(auto i = str.rbegin(); i != str.rend(); ++i) + { + const auto c = *i; + if(c == '1') + { + val += base; + // prevent `base` from overflow + if(is_bounded && max_value / 2 < base && std::next(i) != str.rend()) + { + base = 0; + } + else + { + base *= 2; + } + } + else + { + assert(c == '0'); + + if(is_bounded && max_value / 2 < base && std::next(i) != str.rend()) + { + base = 0; + } + else + { + base *= 2; + } + } + } + if(base == 0) + { + return err(make_error_info("toml::parse_bin_integer: " + "too large integer: current max value = 2^" + std::to_string(max_digits), + std::move(src), "must be < 2^" + std::to_string(max_digits))); + } + return ok(val); +} + +template +result +read_int(const std::string& str, const source_location src, const std::uint8_t base) +{ + assert(base == 10 || base == 16 || base == 8 || base == 2); + switch(base) + { + case 2: { return read_bin_int(str, src); } + case 8: { return read_oct_int(str, src); } + case 16: { return read_hex_int(str, src); } + default: + { + assert(base == 10); + return read_dec_int(str, src); + } + } +} + +inline result +read_hex_float(const std::string& str, const source_location src, float val) +{ +#if defined(_MSC_VER) && ! defined(__clang__) + const auto res = ::sscanf_s(str.c_str(), "%a", std::addressof(val)); +#else + const auto res = std::sscanf(str.c_str(), "%a", std::addressof(val)); +#endif + if(res != 1) + { + return err(make_error_info("toml::parse_floating: " + "failed to read hexadecimal floating point value ", + std::move(src), "here")); + } + return ok(val); +} +inline result +read_hex_float(const std::string& str, const source_location src, double val) +{ +#if defined(_MSC_VER) && ! defined(__clang__) + const auto res = ::sscanf_s(str.c_str(), "%la", std::addressof(val)); +#else + const auto res = std::sscanf(str.c_str(), "%la", std::addressof(val)); +#endif + if(res != 1) + { + return err(make_error_info("toml::parse_floating: " + "failed to read hexadecimal floating point value ", + std::move(src), "here")); + } + return ok(val); +} +template +cxx::enable_if_t, double>>, + cxx::negation, float>> + >::value, result> +read_hex_float(const std::string&, const source_location src, T) +{ + return err(make_error_info("toml::parse_floating: failed to read " + "floating point value because of unknown type in type_config", + std::move(src), "here")); +} + +template +result +read_dec_float(const std::string& str, const source_location src) +{ + T val; + std::istringstream iss(str); + iss >> val; + if(iss.fail()) + { + return err(make_error_info("toml::parse_floating: " + "failed to read floating point value from stream", + std::move(src), "here")); + } + return ok(val); +} + +template +result +read_float(const std::string& str, const source_location src, const bool is_hex) +{ + if(is_hex) + { + return read_hex_float(str, src, T{}); + } + else + { + return read_dec_float(str, src); + } +} + +struct type_config +{ + using comment_type = preserve_comments; + + using boolean_type = bool; + using integer_type = std::int64_t; + using floating_type = double; + using string_type = std::string; + + template + using array_type = std::vector; + template + using table_type = std::unordered_map; + + static result + parse_int(const std::string& str, const source_location src, const std::uint8_t base) + { + return read_int(str, src, base); + } + static result + parse_float(const std::string& str, const source_location src, const bool is_hex) + { + return read_float(str, src, is_hex); + } +}; + +using value = basic_value; +using table = typename value::table_type; +using array = typename value::array_type; + +struct ordered_type_config +{ + using comment_type = preserve_comments; + + using boolean_type = bool; + using integer_type = std::int64_t; + using floating_type = double; + using string_type = std::string; + + template + using array_type = std::vector; + template + using table_type = ordered_map; + + static result + parse_int(const std::string& str, const source_location src, const std::uint8_t base) + { + return read_int(str, src, base); + } + static result + parse_float(const std::string& str, const source_location src, const bool is_hex) + { + return read_float(str, src, is_hex); + } +}; + +using ordered_value = basic_value; +using ordered_table = typename ordered_value::table_type; +using ordered_array = typename ordered_value::array_type; + +// ---------------------------------------------------------------------------- +// meta functions for internal use + +namespace detail +{ + +// ---------------------------------------------------------------------------- +// check if type T has all the needed member types + +struct has_comment_type_impl +{ + template static std::true_type check(typename T::comment_type*); + template static std::false_type check(...); +}; +template +using has_comment_type = decltype(has_comment_type_impl::check(nullptr)); + +struct has_integer_type_impl +{ + template static std::true_type check(typename T::integer_type*); + template static std::false_type check(...); +}; +template +using has_integer_type = decltype(has_integer_type_impl::check(nullptr)); + +struct has_floating_type_impl +{ + template static std::true_type check(typename T::floating_type*); + template static std::false_type check(...); +}; +template +using has_floating_type = decltype(has_floating_type_impl::check(nullptr)); + +struct has_string_type_impl +{ + template static std::true_type check(typename T::string_type*); + template static std::false_type check(...); +}; +template +using has_string_type = decltype(has_string_type_impl::check(nullptr)); + +struct has_array_type_impl +{ + template static std::true_type check(typename T::template array_type*); + template static std::false_type check(...); +}; +template +using has_array_type = decltype(has_array_type_impl::check(nullptr)); + +struct has_table_type_impl +{ + template static std::true_type check(typename T::template table_type*); + template static std::false_type check(...); +}; +template +using has_table_type = decltype(has_table_type_impl::check(nullptr)); + +struct has_parse_int_impl +{ + template static std::true_type check(decltype(std::declval().parse_int( + std::declval(), + std::declval(), + std::declval() + ))*); + template static std::false_type check(...); +}; +template +using has_parse_int = decltype(has_parse_int_impl::check(nullptr)); + +struct has_parse_float_impl +{ + template static std::true_type check(decltype(std::declval().parse_float( + std::declval(), + std::declval(), + std::declval() + ))*); + template static std::false_type check(...); +}; +template +using has_parse_float = decltype(has_parse_float_impl::check(nullptr)); + +template +using is_type_config = cxx::conjunction< + has_comment_type, + has_integer_type, + has_floating_type, + has_string_type, + has_array_type, + has_table_type, + has_parse_int, + has_parse_float + >; + +} // namespace detail +} // namespace toml + +#if defined(TOML11_COMPILE_SOURCES) +namespace toml +{ +extern template class basic_value; +extern template class basic_value; +} // toml +#endif // TOML11_COMPILE_SOURCES + +#endif // TOML11_TYPES_HPP +#ifndef TOML11_GET_HPP +#define TOML11_GET_HPP + +#include + + +#if defined(TOML11_HAS_STRING_VIEW) +#include +#endif // string_view + +namespace toml +{ + +// ============================================================================ +// T is toml::value; identity transformation. + +template +cxx::enable_if_t>::value, T>& +get(basic_value& v) +{ + return v; +} + +template +cxx::enable_if_t>::value, T> const& +get(const basic_value& v) +{ + return v; +} + +template +cxx::enable_if_t>::value, T> +get(basic_value&& v) +{ + return basic_value(std::move(v)); +} + +// ============================================================================ +// exact toml::* type + +template +cxx::enable_if_t>::value, T> & +get(basic_value& v) +{ + constexpr auto ty = detail::type_to_enum>::value; + return detail::getter::get(v); +} + +template +cxx::enable_if_t>::value, T> const& +get(const basic_value& v) +{ + constexpr auto ty = detail::type_to_enum>::value; + return detail::getter::get(v); +} + +template +cxx::enable_if_t>::value, T> +get(basic_value&& v) +{ + constexpr auto ty = detail::type_to_enum>::value; + return detail::getter::get(std::move(v)); +} + +// ============================================================================ +// T is toml::basic_value + +template +cxx::enable_if_t, + cxx::negation>> + >::value, T> +get(basic_value v) +{ + return T(std::move(v)); +} + +// ============================================================================ +// integer convertible from toml::value::integer_type + +template +cxx::enable_if_t, + cxx::negation>, + detail::is_not_toml_type>, + cxx::negation>, + cxx::negation> + >::value, T> +get(const basic_value& v) +{ + return static_cast(v.as_integer()); +} + +// ============================================================================ +// floating point convertible from toml::value::floating_type + +template +cxx::enable_if_t, + detail::is_not_toml_type>, + cxx::negation>, + cxx::negation> + >::value, T> +get(const basic_value& v) +{ + return static_cast(v.as_floating()); +} + +// ============================================================================ +// std::string with different char/trait/allocator + +template +cxx::enable_if_t>, + detail::is_1byte_std_basic_string + >::value, T> +get(const basic_value& v) +{ + return detail::string_conv>(v.as_string()); +} + +// ============================================================================ +// std::string_view + +#if defined(TOML11_HAS_STRING_VIEW) + +template +cxx::enable_if_t::string_type>::value, T> +get(const basic_value& v) +{ + return T(v.as_string()); +} + +#endif // string_view + +// ============================================================================ +// std::chrono::duration from toml::local_time + +template +cxx::enable_if_t::value, T> +get(const basic_value& v) +{ + return std::chrono::duration_cast( + std::chrono::nanoseconds(v.as_local_time())); +} + +// ============================================================================ +// std::chrono::system_clock::time_point from toml::datetime variants + +template +cxx::enable_if_t< + std::is_same::value, T> +get(const basic_value& v) +{ + switch(v.type()) + { + case value_t::local_date: + { + return std::chrono::system_clock::time_point(v.as_local_date()); + } + case value_t::local_datetime: + { + return std::chrono::system_clock::time_point(v.as_local_datetime()); + } + case value_t::offset_datetime: + { + return std::chrono::system_clock::time_point(v.as_offset_datetime()); + } + default: + { + const auto loc = v.location(); + throw type_error(format_error("toml::get: " + "bad_cast to std::chrono::system_clock::time_point", loc, + "the actual type is " + to_string(v.type())), loc); + } + } +} + +// ============================================================================ +// forward declaration to use this recursively. ignore this and go ahead. + +// array-like (w/ push_back) +template +cxx::enable_if_t, // T is a container + detail::has_push_back_method, // .push_back() works + detail::is_not_toml_type>, // but not toml::array + cxx::negation>, // but not std::basic_string +#if defined(TOML11_HAS_STRING_VIEW) + cxx::negation>, // but not std::basic_string_view +#endif + cxx::negation>, // no T.from_toml() + cxx::negation>, // no toml::from + cxx::negation&>> + >::value, T> +get(const basic_value&); + +// std::array +template +cxx::enable_if_t::value, T> +get(const basic_value&); + +// std::forward_list +template +cxx::enable_if_t::value, T> +get(const basic_value&); + +// std::pair +template +cxx::enable_if_t::value, T> +get(const basic_value&); + +// std::tuple +template +cxx::enable_if_t::value, T> +get(const basic_value&); + +// std::map (key is convertible from toml::value::key_type) +template +cxx::enable_if_t, // T is map + detail::is_not_toml_type>, // but not toml::table + std::is_convertible::key_type, + typename T::key_type>, // keys are convertible + cxx::negation>, // no T.from_toml() + cxx::negation>, // no toml::from + cxx::negation&>> + >::value, T> +get(const basic_value& v); + +// std::map (key is not convertible from toml::value::key_type, but +// is a std::basic_string) +template +cxx::enable_if_t, // T is map + detail::is_not_toml_type>, // but not toml::table + cxx::negation::key_type, + typename T::key_type>>, // keys are NOT convertible + detail::is_1byte_std_basic_string, // is std::basic_string + cxx::negation>, // no T.from_toml() + cxx::negation>, // no toml::from + cxx::negation&>> + >::value, T> +get(const basic_value& v); + +// toml::from::from_toml(v) +template +cxx::enable_if_t::value, T> +get(const basic_value&); + +// has T.from_toml(v) but no from +template +cxx::enable_if_t, // has T.from_toml() + cxx::negation>, // no toml::from + std::is_default_constructible // T{} works + >::value, T> +get(const basic_value&); + +// T(const toml::value&) and T is not toml::basic_value, +// and it does not have `from` nor `from_toml`. +template +cxx::enable_if_t&>, // has T(const basic_value&) + cxx::negation>, // but not basic_value itself + cxx::negation>, // no .from_toml() + cxx::negation> // no toml::from + >::value, T> +get(const basic_value&); + +// ============================================================================ +// array-like types; most likely STL container, like std::vector, etc. + +template +cxx::enable_if_t, // T is a container + detail::has_push_back_method, // .push_back() works + detail::is_not_toml_type>, // but not toml::array + cxx::negation>, // but not std::basic_string +#if defined(TOML11_HAS_STRING_VIEW) + cxx::negation>, // but not std::basic_string_view +#endif + cxx::negation>, // no T.from_toml() + cxx::negation>, // no toml::from + cxx::negation&>> + >::value, T> +get(const basic_value& v) +{ + using value_type = typename T::value_type; + const auto& a = v.as_array(); + + T container; + detail::try_reserve(container, a.size()); // if T has .reserve(), call it + + for(const auto& elem : a) + { + container.push_back(get(elem)); + } + return container; +} + +// ============================================================================ +// std::array + +template +cxx::enable_if_t::value, T> +get(const basic_value& v) +{ + using value_type = typename T::value_type; + const auto& a = v.as_array(); + + T container; + if(a.size() != container.size()) + { + const auto loc = v.location(); + throw std::out_of_range(format_error("toml::get: while converting to an array: " + " array size is " + std::to_string(container.size()) + + " but there are " + std::to_string(a.size()) + " elements in toml array.", + loc, "here")); + } + for(std::size_t i=0; i(a.at(i)); + } + return container; +} + +// ============================================================================ +// std::forward_list + +template +cxx::enable_if_t::value, T> +get(const basic_value& v) +{ + using value_type = typename T::value_type; + + T container; + for(const auto& elem : v.as_array()) + { + container.push_front(get(elem)); + } + container.reverse(); + return container; +} + +// ============================================================================ +// std::pair + +template +cxx::enable_if_t::value, T> +get(const basic_value& v) +{ + using first_type = typename T::first_type; + using second_type = typename T::second_type; + + const auto& ar = v.as_array(); + if(ar.size() != 2) + { + const auto loc = v.location(); + throw std::out_of_range(format_error("toml::get: while converting std::pair: " + " but there are " + std::to_string(ar.size()) + " > 2 elements in toml array.", + loc, "here")); + } + return std::make_pair(::toml::get(ar.at(0)), + ::toml::get(ar.at(1))); +} + +// ============================================================================ +// std::tuple. + +namespace detail +{ +template +T get_tuple_impl(const Array& a, cxx::index_sequence) +{ + return std::make_tuple( + ::toml::get::type>(a.at(I))...); +} +} // detail + +template +cxx::enable_if_t::value, T> +get(const basic_value& v) +{ + const auto& ar = v.as_array(); + if(ar.size() != std::tuple_size::value) + { + const auto loc = v.location(); + throw std::out_of_range(format_error("toml::get: while converting std::tuple: " + " there are " + std::to_string(ar.size()) + " > " + + std::to_string(std::tuple_size::value) + " elements in toml array.", + loc, "here")); + } + return detail::get_tuple_impl(ar, + cxx::make_index_sequence::value>{}); +} + +// ============================================================================ +// map-like types; most likely STL map, like std::map or std::unordered_map. + +// key is convertible from toml::value::key_type +template +cxx::enable_if_t, // T is map + detail::is_not_toml_type>, // but not toml::table + std::is_convertible::key_type, + typename T::key_type>, // keys are convertible + cxx::negation>, // no T.from_toml() + cxx::negation>, // no toml::from + cxx::negation&>> + >::value, T> +get(const basic_value& v) +{ + using key_type = typename T::key_type; + using mapped_type = typename T::mapped_type; + static_assert( + std::is_convertible::key_type, key_type>::value, + "toml::get only supports map type of which key_type is " + "convertible from toml::basic_value::key_type."); + + T m; + for(const auto& kv : v.as_table()) + { + m.emplace(key_type(kv.first), get(kv.second)); + } + return m; +} + +// key is NOT convertible from toml::value::key_type but std::basic_string +template +cxx::enable_if_t, // T is map + detail::is_not_toml_type>, // but not toml::table + cxx::negation::key_type, + typename T::key_type>>, // keys are NOT convertible + detail::is_1byte_std_basic_string, // is std::basic_string + cxx::negation>, // no T.from_toml() + cxx::negation>, // no toml::from + cxx::negation&>> + >::value, T> +get(const basic_value& v) +{ + using key_type = typename T::key_type; + using mapped_type = typename T::mapped_type; + + T m; + for(const auto& kv : v.as_table()) + { + m.emplace(detail::string_conv(kv.first), get(kv.second)); + } + return m; +} + +// ============================================================================ +// user-defined, but convertible types. + +// toml::from +template +cxx::enable_if_t::value, T> +get(const basic_value& v) +{ + return ::toml::from::from_toml(v); +} + +// has T.from_toml(v) but no from +template +cxx::enable_if_t, // has T.from_toml() + cxx::negation>, // no toml::from + std::is_default_constructible // T{} works + >::value, T> +get(const basic_value& v) +{ + T ud; + ud.from_toml(v); + return ud; +} + +// T(const toml::value&) and T is not toml::basic_value, +// and it does not have `from` nor `from_toml`. +template +cxx::enable_if_t&>, // has T(const basic_value&) + cxx::negation>, // but not basic_value itself + cxx::negation>, // no .from_toml() + cxx::negation> // no toml::from + >::value, T> +get(const basic_value& v) +{ + return T(v); +} + +// ============================================================================ +// get_or(value, fallback) + +template +cxx::enable_if_t::value, basic_value> const& +get_or(const basic_value& v, const basic_value&) +{ + return v; +} + +template +cxx::enable_if_t::value, basic_value>& +get_or(basic_value& v, basic_value&) +{ + return v; +} + +template +cxx::enable_if_t::value, basic_value> +get_or(basic_value&& v, basic_value&&) +{ + return v; +} + +// ---------------------------------------------------------------------------- +// specialization for the exact toml types (return type becomes lvalue ref) + +template +cxx::enable_if_t< + detail::is_exact_toml_type>::value, T> const& +get_or(const basic_value& v, const T& opt) noexcept +{ + try + { + return get>(v); + } + catch(...) + { + return opt; + } +} +template +cxx::enable_if_t>, + detail::is_exact_toml_type> + >::value, T>& +get_or(basic_value& v, T& opt) noexcept +{ + try + { + return get>(v); + } + catch(...) + { + return opt; + } +} +template +cxx::enable_if_t, + basic_value>::value, cxx::remove_cvref_t> +get_or(basic_value&& v, T&& opt) noexcept +{ + try + { + return get>(std::move(v)); + } + catch(...) + { + return cxx::remove_cvref_t(std::forward(opt)); + } +} + +// ---------------------------------------------------------------------------- +// specialization for string literal + +// template +// typename basic_value::string_type +// get_or(const basic_value& v, +// const typename basic_value::string_type::value_type (&opt)[N]) +// { +// try +// { +// return v.as_string(); +// } +// catch(...) +// { +// return typename basic_value::string_type(opt); +// } +// } +// +// The above only matches to the literal, like `get_or(v, "foo");` but not +// ```cpp +// const auto opt = "foo"; +// const auto str = get_or(v, opt); +// ``` +// . And the latter causes an error. +// To match to both `"foo"` and `const auto opt = "foo"`, we take a pointer to +// a character here. + +template +typename basic_value::string_type +get_or(const basic_value& v, + const typename basic_value::string_type::value_type* opt) +{ + try + { + return v.as_string(); + } + catch(...) + { + return typename basic_value::string_type(opt); + } +} + +// ---------------------------------------------------------------------------- +// others (require type conversion and return type cannot be lvalue reference) + +template +cxx::enable_if_t>, + cxx::negation>>, + cxx::negation, typename basic_value::string_type::value_type const*>> + >::value, cxx::remove_cvref_t> +get_or(const basic_value& v, T&& opt) +{ + try + { + return get>(v); + } + catch(...) + { + return cxx::remove_cvref_t(std::forward(opt)); + } +} + +} // toml +#endif // TOML11_GET_HPP +#ifndef TOML11_FIND_HPP +#define TOML11_FIND_HPP + +#include + + +#if defined(TOML11_HAS_STRING_VIEW) +#include +#endif + +namespace toml +{ + +// ---------------------------------------------------------------------------- +// find(value, key); + +template +decltype(::toml::get(std::declval const&>())) +find(const basic_value& v, const typename basic_value::key_type& ky) +{ + return ::toml::get(v.at(ky)); +} + +template +decltype(::toml::get(std::declval&>())) +find(basic_value& v, const typename basic_value::key_type& ky) +{ + return ::toml::get(v.at(ky)); +} + +template +decltype(::toml::get(std::declval&&>())) +find(basic_value&& v, const typename basic_value::key_type& ky) +{ + return ::toml::get(std::move(v.at(ky))); +} + +// ---------------------------------------------------------------------------- +// find(value, idx) + +template +decltype(::toml::get(std::declval const&>())) +find(const basic_value& v, const std::size_t idx) +{ + return ::toml::get(v.at(idx)); +} +template +decltype(::toml::get(std::declval&>())) +find(basic_value& v, const std::size_t idx) +{ + return ::toml::get(v.at(idx)); +} +template +decltype(::toml::get(std::declval&&>())) +find(basic_value&& v, const std::size_t idx) +{ + return ::toml::get(std::move(v.at(idx))); +} + +// ---------------------------------------------------------------------------- +// find(value, key/idx), w/o conversion + +template +cxx::enable_if_t::value, basic_value>& +find(basic_value& v, const typename basic_value::key_type& ky) +{ + return v.at(ky); +} +template +cxx::enable_if_t::value, basic_value> const& +find(basic_value const& v, const typename basic_value::key_type& ky) +{ + return v.at(ky); +} +template +cxx::enable_if_t::value, basic_value> +find(basic_value&& v, const typename basic_value::key_type& ky) +{ + return basic_value(std::move(v.at(ky))); +} + +template +cxx::enable_if_t::value, basic_value>& +find(basic_value& v, const std::size_t idx) +{ + return v.at(idx); +} +template +cxx::enable_if_t::value, basic_value> const& +find(basic_value const& v, const std::size_t idx) +{ + return v.at(idx); +} +template +cxx::enable_if_t::value, basic_value> +find(basic_value&& v, const std::size_t idx) +{ + return basic_value(std::move(v.at(idx))); +} + +// -------------------------------------------------------------------------- +// toml::find(toml::value, toml::key, Ts&& ... keys) + +namespace detail +{ + +// It suppresses warnings by -Wsign-conversion when we pass integer literal +// to toml::find. integer literal `0` is deduced as an int, and will be +// converted to std::size_t. This causes sign-conversion. + +template +std::size_t key_cast(const std::size_t& v) noexcept +{ + return v; +} +template +cxx::enable_if_t>::value, std::size_t> +key_cast(const T& v) noexcept +{ + return static_cast(v); +} + +// for string-like (string, string literal, string_view) + +template +typename basic_value::key_type const& +key_cast(const typename basic_value::key_type& v) noexcept +{ + return v; +} +template +typename basic_value::key_type +key_cast(const typename basic_value::key_type::value_type* v) +{ + return typename basic_value::key_type(v); +} +#if defined(TOML11_HAS_STRING_VIEW) +template +typename basic_value::key_type +key_cast(const std::string_view v) +{ + return typename basic_value::key_type(v); +} +#endif // string_view + +} // detail + +// ---------------------------------------------------------------------------- +// find(v, keys...) + +template +cxx::enable_if_t::value, basic_value> const& +find(const basic_value& v, const K1& k1, const K2& k2, const Ks& ... ks) +{ + return find(v.at(detail::key_cast(k1)), detail::key_cast(k2), ks...); +} +template +cxx::enable_if_t::value, basic_value>& +find(basic_value& v, const K1& k1, const K2& k2, const Ks& ... ks) +{ + return find(v.at(detail::key_cast(k1)), detail::key_cast(k2), ks...); +} +template +cxx::enable_if_t::value, basic_value> +find(basic_value&& v, const K1& k1, const K2& k2, const Ks& ... ks) +{ + return find(std::move(v.at(detail::key_cast(k1))), detail::key_cast(k2), ks...); +} + +// ---------------------------------------------------------------------------- +// find(v, keys...) + +template +decltype(::toml::get(std::declval&>())) +find(const basic_value& v, const K1& k1, const K2& k2, const Ks& ... ks) +{ + return find(v.at(detail::key_cast(k1)), detail::key_cast(k2), ks...); +} +template +decltype(::toml::get(std::declval&>())) +find(basic_value& v, const K1& k1, const K2& k2, const Ks& ... ks) +{ + return find(v.at(detail::key_cast(k1)), detail::key_cast(k2), ks...); +} +template +decltype(::toml::get(std::declval&&>())) +find(basic_value&& v, const K1& k1, const K2& k2, const Ks& ... ks) +{ + return find(std::move(v.at(detail::key_cast(k1))), detail::key_cast(k2), ks...); +} + +// =========================================================================== +// find_or(value, key, fallback) + +// --------------------------------------------------------------------------- +// find_or(v, key, other_v) + +template +cxx::enable_if_t::value, basic_value>& +find_or(basic_value& v, const K& k, basic_value& opt) noexcept +{ + try + { + return ::toml::find(v, detail::key_cast(k)); + } + catch(...) + { + return opt; + } +} +template +cxx::enable_if_t::value, basic_value> const& +find_or(const basic_value& v, const K& k, const basic_value& opt) noexcept +{ + try + { + return ::toml::find(v, detail::key_cast(k)); + } + catch(...) + { + return opt; + } +} +template +cxx::enable_if_t::value, basic_value> +find_or(basic_value&& v, const K& k, basic_value&& opt) noexcept +{ + try + { + return ::toml::find(v, detail::key_cast(k)); + } + catch(...) + { + return opt; + } +} + +// --------------------------------------------------------------------------- +// toml types (return type can be a reference) + +template +cxx::enable_if_t>::value, + cxx::remove_cvref_t const&> +find_or(const basic_value& v, const K& k, const T& opt) +{ + try + { + return ::toml::get(v.at(detail::key_cast(k))); + } + catch(...) + { + return opt; + } +} + +template +cxx::enable_if_t>, + detail::is_exact_toml_type> + >::value, cxx::remove_cvref_t&> +find_or(basic_value& v, const K& k, T& opt) +{ + try + { + return ::toml::get(v.at(detail::key_cast(k))); + } + catch(...) + { + return opt; + } +} + +template +cxx::enable_if_t>::value, + cxx::remove_cvref_t> +find_or(basic_value&& v, const K& k, T opt) +{ + try + { + return ::toml::get(std::move(v.at(detail::key_cast(k)))); + } + catch(...) + { + return T(std::move(opt)); + } +} + +// --------------------------------------------------------------------------- +// string literal (deduced as std::string) + +// XXX to avoid confusion when T is explicitly specified in find_or(), +// we restrict the string type as std::string. +template +cxx::enable_if_t::value, std::string> +find_or(const basic_value& v, const K& k, const char* opt) +{ + try + { + return ::toml::get(v.at(detail::key_cast(k))); + } + catch(...) + { + return std::string(opt); + } +} + +// --------------------------------------------------------------------------- +// other types (requires type conversion and return type cannot be a reference) + +template +cxx::enable_if_t>>, + detail::is_not_toml_type, basic_value>, + cxx::negation, + const typename basic_value::string_type::value_type*>> + >::value, cxx::remove_cvref_t> +find_or(const basic_value& v, const K& ky, T opt) +{ + try + { + return ::toml::get>(v.at(detail::key_cast(ky))); + } + catch(...) + { + return cxx::remove_cvref_t(std::move(opt)); + } +} + +// ---------------------------------------------------------------------------- +// recursive + +namespace detail +{ + +template +auto last_one(Ts&&... args) + -> decltype(std::get(std::forward_as_tuple(std::forward(args)...))) +{ + return std::get(std::forward_as_tuple(std::forward(args)...)); +} + +} // detail + +template +auto find_or(Value&& v, const K1& k1, const K2& k2, K3&& k3, Ks&& ... keys) noexcept + -> cxx::enable_if_t< + detail::is_basic_value>::value, + decltype(find_or(v, k2, std::forward(k3), std::forward(keys)...)) + > +{ + try + { + return find_or(v.at(k1), k2, std::forward(k3), std::forward(keys)...); + } + catch(...) + { + return detail::last_one(k3, keys...); + } +} + +template +T find_or(const basic_value& v, const K1& k1, const K2& k2, const K3& k3, const Ks& ... keys) noexcept +{ + try + { + return find_or(v.at(k1), k2, k3, keys...); + } + catch(...) + { + return static_cast(detail::last_one(k3, keys...)); + } +} + +} // toml +#endif // TOML11_FIND_HPP +#ifndef TOML11_CONVERSION_HPP +#define TOML11_CONVERSION_HPP + + +#if defined(TOML11_HAS_OPTIONAL) + +#include + +namespace toml +{ +namespace detail +{ + +template +inline constexpr bool is_optional_v = false; + +template +inline constexpr bool is_optional_v> = true; + +template +void find_member_variable_from_value(T& obj, const basic_value& v, const char* var_name) +{ + if constexpr(is_optional_v) + { + if(v.contains(var_name)) + { + obj = toml::find(v, var_name); + } + else + { + obj = std::nullopt; + } + } + else + { + obj = toml::find(v, var_name); + } +} + +template +void assign_member_variable_to_value(const T& obj, basic_value& v, const char* var_name) +{ + if constexpr(is_optional_v) + { + if(obj.has_value()) + { + v[var_name] = obj.value(); + } + } + else + { + v[var_name] = obj; + } +} + +} // detail +} // toml + +#else + +namespace toml +{ +namespace detail +{ + +template +void find_member_variable_from_value(T& obj, const basic_value& v, const char* var_name) +{ + obj = toml::find(v, var_name); +} + +template +void assign_member_variable_to_value(const T& obj, basic_value& v, const char* var_name) +{ + v[var_name] = obj; +} + +} // detail +} // toml + +#endif // optional + +// use it in the following way. +// ```cpp +// namespace foo +// { +// struct Foo +// { +// std::string s; +// double d; +// int i; +// }; +// } // foo +// +// TOML11_DEFINE_CONVERSION_NON_INTRUSIVE(foo::Foo, s, d, i) +// ``` +// +// And then you can use `toml::get(v)` and `toml::find(file, "foo");` +// + +#define TOML11_STRINGIZE_AUX(x) #x +#define TOML11_STRINGIZE(x) TOML11_STRINGIZE_AUX(x) + +#define TOML11_CONCATENATE_AUX(x, y) x##y +#define TOML11_CONCATENATE(x, y) TOML11_CONCATENATE_AUX(x, y) + +// ============================================================================ +// TOML11_DEFINE_CONVERSION_NON_INTRUSIVE + +#ifndef TOML11_WITHOUT_DEFINE_NON_INTRUSIVE + +// ---------------------------------------------------------------------------- +// TOML11_ARGS_SIZE + +#define TOML11_INDEX_RSEQ() \ + 32, 31, 30, 29, 28, 27, 26, 25, 24, 23, 22, 21, 20, 19, 18, 17, \ + 16, 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0 +#define TOML11_ARGS_SIZE_IMPL(\ + ARG1, ARG2, ARG3, ARG4, ARG5, ARG6, ARG7, ARG8, ARG9, ARG10, \ + ARG11, ARG12, ARG13, ARG14, ARG15, ARG16, ARG17, ARG18, ARG19, ARG20, \ + ARG21, ARG22, ARG23, ARG24, ARG25, ARG26, ARG27, ARG28, ARG29, ARG30, \ + ARG31, ARG32, N, ...) N +#define TOML11_ARGS_SIZE_AUX(...) TOML11_ARGS_SIZE_IMPL(__VA_ARGS__) +#define TOML11_ARGS_SIZE(...) TOML11_ARGS_SIZE_AUX(__VA_ARGS__, TOML11_INDEX_RSEQ()) + +// ---------------------------------------------------------------------------- +// TOML11_FOR_EACH_VA_ARGS + +#define TOML11_FOR_EACH_VA_ARGS_AUX_1( FUNCTOR, ARG1 ) FUNCTOR(ARG1) +#define TOML11_FOR_EACH_VA_ARGS_AUX_2( FUNCTOR, ARG1, ...) FUNCTOR(ARG1) TOML11_FOR_EACH_VA_ARGS_AUX_1( FUNCTOR, __VA_ARGS__) +#define TOML11_FOR_EACH_VA_ARGS_AUX_3( FUNCTOR, ARG1, ...) FUNCTOR(ARG1) TOML11_FOR_EACH_VA_ARGS_AUX_2( FUNCTOR, __VA_ARGS__) +#define TOML11_FOR_EACH_VA_ARGS_AUX_4( FUNCTOR, ARG1, ...) FUNCTOR(ARG1) TOML11_FOR_EACH_VA_ARGS_AUX_3( FUNCTOR, __VA_ARGS__) +#define TOML11_FOR_EACH_VA_ARGS_AUX_5( FUNCTOR, ARG1, ...) FUNCTOR(ARG1) TOML11_FOR_EACH_VA_ARGS_AUX_4( FUNCTOR, __VA_ARGS__) +#define TOML11_FOR_EACH_VA_ARGS_AUX_6( FUNCTOR, ARG1, ...) FUNCTOR(ARG1) TOML11_FOR_EACH_VA_ARGS_AUX_5( FUNCTOR, __VA_ARGS__) +#define TOML11_FOR_EACH_VA_ARGS_AUX_7( FUNCTOR, ARG1, ...) FUNCTOR(ARG1) TOML11_FOR_EACH_VA_ARGS_AUX_6( FUNCTOR, __VA_ARGS__) +#define TOML11_FOR_EACH_VA_ARGS_AUX_8( FUNCTOR, ARG1, ...) FUNCTOR(ARG1) TOML11_FOR_EACH_VA_ARGS_AUX_7( FUNCTOR, __VA_ARGS__) +#define TOML11_FOR_EACH_VA_ARGS_AUX_9( FUNCTOR, ARG1, ...) FUNCTOR(ARG1) TOML11_FOR_EACH_VA_ARGS_AUX_8( FUNCTOR, __VA_ARGS__) +#define TOML11_FOR_EACH_VA_ARGS_AUX_10(FUNCTOR, ARG1, ...) FUNCTOR(ARG1) TOML11_FOR_EACH_VA_ARGS_AUX_9( FUNCTOR, __VA_ARGS__) +#define TOML11_FOR_EACH_VA_ARGS_AUX_11(FUNCTOR, ARG1, ...) FUNCTOR(ARG1) TOML11_FOR_EACH_VA_ARGS_AUX_10(FUNCTOR, __VA_ARGS__) +#define TOML11_FOR_EACH_VA_ARGS_AUX_12(FUNCTOR, ARG1, ...) FUNCTOR(ARG1) TOML11_FOR_EACH_VA_ARGS_AUX_11(FUNCTOR, __VA_ARGS__) +#define TOML11_FOR_EACH_VA_ARGS_AUX_13(FUNCTOR, ARG1, ...) FUNCTOR(ARG1) TOML11_FOR_EACH_VA_ARGS_AUX_12(FUNCTOR, __VA_ARGS__) +#define TOML11_FOR_EACH_VA_ARGS_AUX_14(FUNCTOR, ARG1, ...) FUNCTOR(ARG1) TOML11_FOR_EACH_VA_ARGS_AUX_13(FUNCTOR, __VA_ARGS__) +#define TOML11_FOR_EACH_VA_ARGS_AUX_15(FUNCTOR, ARG1, ...) FUNCTOR(ARG1) TOML11_FOR_EACH_VA_ARGS_AUX_14(FUNCTOR, __VA_ARGS__) +#define TOML11_FOR_EACH_VA_ARGS_AUX_16(FUNCTOR, ARG1, ...) FUNCTOR(ARG1) TOML11_FOR_EACH_VA_ARGS_AUX_15(FUNCTOR, __VA_ARGS__) +#define TOML11_FOR_EACH_VA_ARGS_AUX_17(FUNCTOR, ARG1, ...) FUNCTOR(ARG1) TOML11_FOR_EACH_VA_ARGS_AUX_16(FUNCTOR, __VA_ARGS__) +#define TOML11_FOR_EACH_VA_ARGS_AUX_18(FUNCTOR, ARG1, ...) FUNCTOR(ARG1) TOML11_FOR_EACH_VA_ARGS_AUX_17(FUNCTOR, __VA_ARGS__) +#define TOML11_FOR_EACH_VA_ARGS_AUX_19(FUNCTOR, ARG1, ...) FUNCTOR(ARG1) TOML11_FOR_EACH_VA_ARGS_AUX_18(FUNCTOR, __VA_ARGS__) +#define TOML11_FOR_EACH_VA_ARGS_AUX_20(FUNCTOR, ARG1, ...) FUNCTOR(ARG1) TOML11_FOR_EACH_VA_ARGS_AUX_19(FUNCTOR, __VA_ARGS__) +#define TOML11_FOR_EACH_VA_ARGS_AUX_21(FUNCTOR, ARG1, ...) FUNCTOR(ARG1) TOML11_FOR_EACH_VA_ARGS_AUX_20(FUNCTOR, __VA_ARGS__) +#define TOML11_FOR_EACH_VA_ARGS_AUX_22(FUNCTOR, ARG1, ...) FUNCTOR(ARG1) TOML11_FOR_EACH_VA_ARGS_AUX_21(FUNCTOR, __VA_ARGS__) +#define TOML11_FOR_EACH_VA_ARGS_AUX_23(FUNCTOR, ARG1, ...) FUNCTOR(ARG1) TOML11_FOR_EACH_VA_ARGS_AUX_22(FUNCTOR, __VA_ARGS__) +#define TOML11_FOR_EACH_VA_ARGS_AUX_24(FUNCTOR, ARG1, ...) FUNCTOR(ARG1) TOML11_FOR_EACH_VA_ARGS_AUX_23(FUNCTOR, __VA_ARGS__) +#define TOML11_FOR_EACH_VA_ARGS_AUX_25(FUNCTOR, ARG1, ...) FUNCTOR(ARG1) TOML11_FOR_EACH_VA_ARGS_AUX_24(FUNCTOR, __VA_ARGS__) +#define TOML11_FOR_EACH_VA_ARGS_AUX_26(FUNCTOR, ARG1, ...) FUNCTOR(ARG1) TOML11_FOR_EACH_VA_ARGS_AUX_25(FUNCTOR, __VA_ARGS__) +#define TOML11_FOR_EACH_VA_ARGS_AUX_27(FUNCTOR, ARG1, ...) FUNCTOR(ARG1) TOML11_FOR_EACH_VA_ARGS_AUX_26(FUNCTOR, __VA_ARGS__) +#define TOML11_FOR_EACH_VA_ARGS_AUX_28(FUNCTOR, ARG1, ...) FUNCTOR(ARG1) TOML11_FOR_EACH_VA_ARGS_AUX_27(FUNCTOR, __VA_ARGS__) +#define TOML11_FOR_EACH_VA_ARGS_AUX_29(FUNCTOR, ARG1, ...) FUNCTOR(ARG1) TOML11_FOR_EACH_VA_ARGS_AUX_28(FUNCTOR, __VA_ARGS__) +#define TOML11_FOR_EACH_VA_ARGS_AUX_30(FUNCTOR, ARG1, ...) FUNCTOR(ARG1) TOML11_FOR_EACH_VA_ARGS_AUX_29(FUNCTOR, __VA_ARGS__) +#define TOML11_FOR_EACH_VA_ARGS_AUX_31(FUNCTOR, ARG1, ...) FUNCTOR(ARG1) TOML11_FOR_EACH_VA_ARGS_AUX_30(FUNCTOR, __VA_ARGS__) +#define TOML11_FOR_EACH_VA_ARGS_AUX_32(FUNCTOR, ARG1, ...) FUNCTOR(ARG1) TOML11_FOR_EACH_VA_ARGS_AUX_31(FUNCTOR, __VA_ARGS__) + +#define TOML11_FOR_EACH_VA_ARGS(FUNCTOR, ...)\ + TOML11_CONCATENATE(TOML11_FOR_EACH_VA_ARGS_AUX_, TOML11_ARGS_SIZE(__VA_ARGS__))(FUNCTOR, __VA_ARGS__) + + +#define TOML11_FIND_MEMBER_VARIABLE_FROM_VALUE(VAR_NAME)\ + toml::detail::find_member_variable_from_value(obj.VAR_NAME, v, TOML11_STRINGIZE(VAR_NAME)); + +#define TOML11_ASSIGN_MEMBER_VARIABLE_TO_VALUE(VAR_NAME)\ + toml::detail::assign_member_variable_to_value(obj.VAR_NAME, v, TOML11_STRINGIZE(VAR_NAME)); + +#define TOML11_DEFINE_CONVERSION_NON_INTRUSIVE(NAME, ...)\ + namespace toml { \ + template<> \ + struct from \ + { \ + template \ + static NAME from_toml(const basic_value& v) \ + { \ + NAME obj; \ + TOML11_FOR_EACH_VA_ARGS(TOML11_FIND_MEMBER_VARIABLE_FROM_VALUE, __VA_ARGS__) \ + return obj; \ + } \ + }; \ + template<> \ + struct into \ + { \ + template \ + static basic_value into_toml(const NAME& obj) \ + { \ + ::toml::basic_value v = typename ::toml::basic_value::table_type{}; \ + TOML11_FOR_EACH_VA_ARGS(TOML11_ASSIGN_MEMBER_VARIABLE_TO_VALUE, __VA_ARGS__) \ + return v; \ + } \ + }; \ + } /* toml */ + +#endif// TOML11_WITHOUT_DEFINE_NON_INTRUSIVE + +#endif // TOML11_CONVERSION_HPP +#ifndef TOML11_CONTEXT_HPP +#define TOML11_CONTEXT_HPP + + +#include + +namespace toml +{ +namespace detail +{ + +template +class context +{ + public: + + explicit context(const spec& toml_spec) + : toml_spec_(toml_spec), errors_{} + {} + + bool has_error() const noexcept {return !errors_.empty();} + + std::vector const& errors() const noexcept {return errors_;} + + semantic_version& toml_version() noexcept {return toml_spec_.version;} + semantic_version const& toml_version() const noexcept {return toml_spec_.version;} + + spec& toml_spec() noexcept {return toml_spec_;} + spec const& toml_spec() const noexcept {return toml_spec_;} + + void report_error(error_info err) + { + this->errors_.push_back(std::move(err)); + } + + error_info pop_last_error() + { + assert( ! errors_.empty()); + auto e = std::move(errors_.back()); + errors_.pop_back(); + return e; + } + + private: + + spec toml_spec_; + std::vector errors_; +}; + +} // detail +} // toml + +#if defined(TOML11_COMPILE_SOURCES) +namespace toml +{ +struct type_config; +struct ordered_type_config; +namespace detail +{ +extern template class context<::toml::type_config>; +extern template class context<::toml::ordered_type_config>; +} // detail +} // toml +#endif // TOML11_COMPILE_SOURCES + +#endif // TOML11_CONTEXT_HPP +#ifndef TOML11_SCANNER_HPP +#define TOML11_SCANNER_HPP + +#ifndef TOML11_SCANNER_FWD_HPP +#define TOML11_SCANNER_FWD_HPP + + +#include +#include +#include +#include + +#include +#include +#include + +namespace toml +{ +namespace detail +{ + +class scanner_base +{ + public: + virtual ~scanner_base() = default; + virtual region scan(location& loc) const = 0; + virtual scanner_base* clone() const = 0; + + // returns expected character or set of characters or literal. + // to show the error location, it changes loc (in `sequence`, especially). + virtual std::string expected_chars(location& loc) const = 0; + virtual std::string name() const = 0; +}; + +// make `scanner*` copyable +struct scanner_storage +{ + template>::value, + std::nullptr_t> = nullptr> + explicit scanner_storage(Scanner&& s) + : scanner_(cxx::make_unique>(std::forward(s))) + {} + ~scanner_storage() = default; + + scanner_storage(const scanner_storage& other); + scanner_storage& operator=(const scanner_storage& other); + scanner_storage(scanner_storage&&) = default; + scanner_storage& operator=(scanner_storage&&) = default; + + bool is_ok() const noexcept {return static_cast(scanner_);} + + region scan(location& loc) const; + + std::string expected_chars(location& loc) const; + + scanner_base& get() const noexcept; + + std::string name() const; + + private: + + std::unique_ptr scanner_; +}; + +// ---------------------------------------------------------------------------- + +class character final : public scanner_base +{ + public: + + using char_type = location::char_type; + + public: + + explicit character(const char_type c) noexcept + : value_(c) + {} + ~character() override = default; + + region scan(location& loc) const override; + + std::string expected_chars(location&) const override; + + scanner_base* clone() const override; + + std::string name() const override; + + private: + char_type value_; +}; + +// ---------------------------------------------------------------------------- + +class character_either final : public scanner_base +{ + public: + + using char_type = location::char_type; + + public: + + explicit character_either(std::initializer_list cs) noexcept + : chars_(std::move(cs)) + { + assert(! this->chars_.empty()); + } + + template + explicit character_either(const char (&cs)[N]) noexcept + : chars_(N-1, '\0') + { + static_assert(N >= 1, ""); + for(std::size_t i=0; i+1 chars_; +}; + +// ---------------------------------------------------------------------------- + +class character_in_range final : public scanner_base +{ + public: + + using char_type = location::char_type; + + public: + + explicit character_in_range(const char_type from, const char_type to) noexcept + : from_(from), to_(to) + {} + ~character_in_range() override = default; + + region scan(location& loc) const override; + + std::string expected_chars(location&) const override; + + scanner_base* clone() const override; + + std::string name() const override; + + private: + char_type from_; + char_type to_; +}; + +// ---------------------------------------------------------------------------- + +class literal final : public scanner_base +{ + public: + + using char_type = location::char_type; + + public: + + template + explicit literal(const char (&cs)[N]) noexcept + : value_(cs), size_(N-1) // remove null character at the end + {} + ~literal() override = default; + + region scan(location& loc) const override; + + std::string expected_chars(location&) const override; + + scanner_base* clone() const override; + + std::string name() const override; + + private: + const char* value_; + std::size_t size_; +}; + +// ---------------------------------------------------------------------------- + +class sequence final: public scanner_base +{ + public: + using char_type = location::char_type; + + public: + + template + explicit sequence(Ts&& ... args) + { + push_back_all(std::forward(args)...); + } + sequence(const sequence&) = default; + sequence(sequence&&) = default; + sequence& operator=(const sequence&) = default; + sequence& operator=(sequence&&) = default; + ~sequence() override = default; + + region scan(location& loc) const override; + + std::string expected_chars(location& loc) const override; + + scanner_base* clone() const override; + + template + void push_back(Scanner&& other_scanner) + { + this->others_.emplace_back(std::forward(other_scanner)); + } + + std::string name() const override; + + private: + + void push_back_all() + { + return; + } + template + void push_back_all(T&& head, Ts&& ... args) + { + others_.emplace_back(std::forward(head)); + push_back_all(std::forward(args)...); + return; + } + + private: + std::vector others_; +}; + +// ---------------------------------------------------------------------------- + +class either final: public scanner_base +{ + public: + using char_type = location::char_type; + + public: + + template + explicit either(Ts&& ... args) + { + push_back_all(std::forward(args)...); + } + either(const either&) = default; + either(either&&) = default; + either& operator=(const either&) = default; + either& operator=(either&&) = default; + ~either() override = default; + + region scan(location& loc) const override; + + std::string expected_chars(location& loc) const override; + + scanner_base* clone() const override; + + template + void push_back(Scanner&& other_scanner) + { + this->others_.emplace_back(std::forward(other_scanner)); + } + + std::string name() const override; + + private: + + void push_back_all() + { + return; + } + template + void push_back_all(T&& head, Ts&& ... args) + { + others_.emplace_back(std::forward(head)); + push_back_all(std::forward(args)...); + return; + } + + private: + std::vector others_; +}; + +// ---------------------------------------------------------------------------- + +class repeat_exact final: public scanner_base +{ + public: + using char_type = location::char_type; + + public: + + template + repeat_exact(const std::size_t length, Scanner&& other) + : length_(length), other_(std::forward(other)) + {} + repeat_exact(const repeat_exact&) = default; + repeat_exact(repeat_exact&&) = default; + repeat_exact& operator=(const repeat_exact&) = default; + repeat_exact& operator=(repeat_exact&&) = default; + ~repeat_exact() override = default; + + region scan(location& loc) const override; + + std::string expected_chars(location& loc) const override; + + scanner_base* clone() const override; + + std::string name() const override; + + private: + std::size_t length_; + scanner_storage other_; +}; + +// ---------------------------------------------------------------------------- + +class repeat_at_least final: public scanner_base +{ + public: + using char_type = location::char_type; + + public: + + template + repeat_at_least(const std::size_t length, Scanner&& s) + : length_(length), other_(std::forward(s)) + {} + repeat_at_least(const repeat_at_least&) = default; + repeat_at_least(repeat_at_least&&) = default; + repeat_at_least& operator=(const repeat_at_least&) = default; + repeat_at_least& operator=(repeat_at_least&&) = default; + ~repeat_at_least() override = default; + + region scan(location& loc) const override; + + std::string expected_chars(location& loc) const override; + + scanner_base* clone() const override; + + std::string name() const override; + + private: + std::size_t length_; + scanner_storage other_; +}; + +// ---------------------------------------------------------------------------- + +class maybe final: public scanner_base +{ + public: + using char_type = location::char_type; + + public: + + template + explicit maybe(Scanner&& s) + : other_(std::forward(s)) + {} + maybe(const maybe&) = default; + maybe(maybe&&) = default; + maybe& operator=(const maybe&) = default; + maybe& operator=(maybe&&) = default; + ~maybe() override = default; + + region scan(location& loc) const override; + + std::string expected_chars(location&) const override; + + scanner_base* clone() const override; + + std::string name() const override; + + private: + scanner_storage other_; +}; + +} // detail +} // toml +#endif // TOML11_SCANNER_FWD_HPP + +#if ! defined(TOML11_COMPILE_SOURCES) +#ifndef TOML11_SCANNER_IMPL_HPP +#define TOML11_SCANNER_IMPL_HPP + + +namespace toml +{ +namespace detail +{ + +TOML11_INLINE scanner_storage::scanner_storage(const scanner_storage& other) + : scanner_(nullptr) +{ + if(other.is_ok()) + { + scanner_.reset(other.get().clone()); + } +} +TOML11_INLINE scanner_storage& scanner_storage::operator=(const scanner_storage& other) +{ + if(this == std::addressof(other)) {return *this;} + if(other.is_ok()) + { + scanner_.reset(other.get().clone()); + } + return *this; +} + +TOML11_INLINE region scanner_storage::scan(location& loc) const +{ + assert(this->is_ok()); + return this->scanner_->scan(loc); +} + +TOML11_INLINE std::string scanner_storage::expected_chars(location& loc) const +{ + assert(this->is_ok()); + return this->scanner_->expected_chars(loc); +} + +TOML11_INLINE scanner_base& scanner_storage::get() const noexcept +{ + assert(this->is_ok()); + return *scanner_; +} + +TOML11_INLINE std::string scanner_storage::name() const +{ + assert(this->is_ok()); + return this->scanner_->name(); +} + +// ---------------------------------------------------------------------------- + +TOML11_INLINE region character::scan(location& loc) const +{ + if(loc.eof()) {return region{};} + + if(loc.current() == this->value_) + { + const auto first = loc; + loc.advance(1); + return region(first, loc); + } + return region{}; +} + +TOML11_INLINE std::string character::expected_chars(location&) const +{ + return show_char(value_); +} + +TOML11_INLINE scanner_base* character::clone() const +{ + return new character(*this); +} + +TOML11_INLINE std::string character::name() const +{ + return "character{" + show_char(value_) + "}"; +} + +// ---------------------------------------------------------------------------- + +TOML11_INLINE region character_either::scan(location& loc) const +{ + if(loc.eof()) {return region{};} + + for(const auto c : this->chars_) + { + if(loc.current() == c) + { + const auto first = loc; + loc.advance(1); + return region(first, loc); + } + } + return region{}; +} + +TOML11_INLINE std::string character_either::expected_chars(location&) const +{ + assert( ! chars_.empty()); + + std::string expected; + if(chars_.size() == 1) + { + expected += show_char(chars_.at(0)); + } + else if(chars_.size() == 2) + { + expected += show_char(chars_.at(0)) + " or " + show_char(chars_.at(1)); + } + else + { + for(std::size_t i=0; ichars_) + { + n += show_char(c); + n += ", "; + } + if( ! this->chars_.empty()) + { + n.pop_back(); + n.pop_back(); + } + n += "}"; + return n; +} + +// ---------------------------------------------------------------------------- +// character_in_range + +TOML11_INLINE region character_in_range::scan(location& loc) const +{ + if(loc.eof()) {return region{};} + + const auto curr = loc.current(); + if(this->from_ <= curr && curr <= this->to_) + { + const auto first = loc; + loc.advance(1); + return region(first, loc); + } + return region{}; +} + +TOML11_INLINE std::string character_in_range::expected_chars(location&) const +{ + std::string expected("from `"); + expected += show_char(from_); + expected += "` to `"; + expected += show_char(to_); + expected += "`"; + return expected; +} + +TOML11_INLINE scanner_base* character_in_range::clone() const +{ + return new character_in_range(*this); +} + +TOML11_INLINE std::string character_in_range::name() const +{ + return "character_in_range{" + show_char(from_) + "," + show_char(to_) + "}"; +} + +// ---------------------------------------------------------------------------- +// literal + +TOML11_INLINE region literal::scan(location& loc) const +{ + const auto first = loc; + for(std::size_t i=0; iothers_.empty()) + { + n.pop_back(); + n.pop_back(); + } + n += "}"; + return n; +} + +// ---------------------------------------------------------------------------- +// either + +TOML11_INLINE region either::scan(location& loc) const +{ + for(const auto& other : others_) + { + const auto reg = other.scan(loc); + if(reg.is_ok()) + { + return reg; + } + } + return region{}; +} + +TOML11_INLINE std::string either::expected_chars(location& loc) const +{ + assert( ! others_.empty()); + + std::string expected = others_.at(0).expected_chars(loc); + if(others_.size() == 2) + { + expected += " or "; + expected += others_.at(1).expected_chars(loc); + } + else + { + for(std::size_t i=1; iothers_.empty()) + { + n.pop_back(); + n.pop_back(); + } + n += "}"; + return n; +} + +// ---------------------------------------------------------------------------- +// repeat_exact + +TOML11_INLINE region repeat_exact::scan(location& loc) const +{ + const auto first = loc; + for(std::size_t i=0; i(b1); + } + else if((b1 >> 5) == 6) // 0b110 == 6 + { + const auto b2 = loc.current(); loc.advance(1); + + const std::uint32_t c1 = b1 & ((1 << 5) - 1); + const std::uint32_t c2 = b2 & ((1 << 6) - 1); + const std::uint32_t codep = (c1 << 6) + c2; + + if(codep < 0x80) + { + return 0xFFFFFFFF; + } + return codep; + } + else if((b1 >> 4) == 14) // 0b1110 == 14 + { + const auto b2 = loc.current(); loc.advance(1); if(loc.eof()) {return 0xFFFFFFFF;} + const auto b3 = loc.current(); loc.advance(1); + + const std::uint32_t c1 = b1 & ((1 << 4) - 1); + const std::uint32_t c2 = b2 & ((1 << 6) - 1); + const std::uint32_t c3 = b3 & ((1 << 6) - 1); + + const std::uint32_t codep = (c1 << 12) + (c2 << 6) + c3; + if(codep < 0x800) + { + return 0xFFFFFFFF; + } + return codep; + } + else if((b1 >> 3) == 30) // 0b11110 == 30 + { + const auto b2 = loc.current(); loc.advance(1); if(loc.eof()) {return 0xFFFFFFFF;} + const auto b3 = loc.current(); loc.advance(1); if(loc.eof()) {return 0xFFFFFFFF;} + const auto b4 = loc.current(); loc.advance(1); + + const std::uint32_t c1 = b1 & ((1 << 3) - 1); + const std::uint32_t c2 = b2 & ((1 << 6) - 1); + const std::uint32_t c3 = b3 & ((1 << 6) - 1); + const std::uint32_t c4 = b4 & ((1 << 6) - 1); + const std::uint32_t codep = (c1 << 18) + (c2 << 12) + (c3 << 6) + c4; + + if(codep < 0x10000) + { + return 0xFFFFFFFF; + } + return codep; + } + else // not a Unicode codepoint in UTF-8 + { + return 0xFFFFFFFF; + } +} + +TOML11_INLINE region non_ascii_key_char::scan(location& loc) const +{ + if(loc.eof()) {return region{};} + + const auto first = loc; + + const auto cp = read_utf8(loc); + + if(cp == 0xFFFFFFFF) + { + return region{}; + } + + // ALPHA / DIGIT / %x2D / %x5F ; a-z A-Z 0-9 - _ + // / %xB2 / %xB3 / %xB9 / %xBC-BE ; superscript digits, fractions + // / %xC0-D6 / %xD8-F6 / %xF8-37D ; non-symbol chars in Latin block + // / %x37F-1FFF ; exclude GREEK QUESTION MARK, which is basically a semi-colon + // / %x200C-200D / %x203F-2040 ; from General Punctuation Block, include the two tie symbols and ZWNJ, ZWJ + // / %x2070-218F / %x2460-24FF ; include super-/subscripts, letterlike/numberlike forms, enclosed alphanumerics + // / %x2C00-2FEF / %x3001-D7FF ; skip arrows, math, box drawing etc, skip 2FF0-3000 ideographic up/down markers and spaces + // / %xF900-FDCF / %xFDF0-FFFD ; skip D800-DFFF surrogate block, E000-F8FF Private Use area, FDD0-FDEF intended for process-internal use (unicode) + // / %x10000-EFFFF ; all chars outside BMP range, excluding Private Use planes (F0000-10FFFF) + + if(cp == 0xB2 || cp == 0xB3 || cp == 0xB9 || (0xBC <= cp && cp <= 0xBE) || + (0xC0 <= cp && cp <= 0xD6 ) || (0xD8 <= cp && cp <= 0xF6) || (0xF8 <= cp && cp <= 0x37D) || + (0x37F <= cp && cp <= 0x1FFF) || + (0x200C <= cp && cp <= 0x200D) || (0x203F <= cp && cp <= 0x2040) || + (0x2070 <= cp && cp <= 0x218F) || (0x2460 <= cp && cp <= 0x24FF) || + (0x2C00 <= cp && cp <= 0x2FEF) || (0x3001 <= cp && cp <= 0xD7FF) || + (0xF900 <= cp && cp <= 0xFDCF) || (0xFDF0 <= cp && cp <= 0xFFFD) || + (0x10000 <= cp && cp <= 0xEFFFF) ) + { + return region(first, loc); + } + loc = first; + return region{}; +} + +TOML11_INLINE repeat_at_least unquoted_key(const spec& s) +{ + auto keychar = either( + alpha(s), digit(s), character{0x2D}, character{0x5F} + ); + + if(s.v1_1_0_allow_non_english_in_bare_keys) + { + keychar.push_back(non_ascii_key_char(s)); + } + + return repeat_at_least(1, std::move(keychar)); +} + +TOML11_INLINE either quoted_key(const spec& s) +{ + return either(basic_string(s), literal_string(s)); +} + +TOML11_INLINE either simple_key(const spec& s) +{ + return either(unquoted_key(s), quoted_key(s)); +} + +TOML11_INLINE sequence dot_sep(const spec& s) +{ + return sequence(ws(s), character('.'), ws(s)); +} + +TOML11_INLINE sequence dotted_key(const spec& s) +{ + return sequence( + simple_key(s), + repeat_at_least(1, sequence(dot_sep(s), simple_key(s))) + ); +} + +TOML11_INLINE key::key(const spec& s) noexcept + : scanner_(dotted_key(s), simple_key(s)) +{} + +TOML11_INLINE sequence keyval_sep(const spec& s) +{ + return sequence(ws(s), character('='), ws(s)); +} + +// =========================================================================== +// Table key + +TOML11_INLINE sequence std_table(const spec& s) +{ + return sequence(character('['), ws(s), key(s), ws(s), character(']')); +} + +TOML11_INLINE sequence array_table(const spec& s) +{ + return sequence(literal("[["), ws(s), key(s), ws(s), literal("]]")); +} + +// =========================================================================== +// extension: null + +TOML11_INLINE literal null_value(const spec&) +{ + return literal("null"); +} + +} // namespace syntax +} // namespace detail +} // namespace toml +#endif // TOML11_SYNTAX_IMPL_HPP +#endif + +#endif// TOML11_SYNTAX_HPP +#ifndef TOML11_SKIP_HPP +#define TOML11_SKIP_HPP + + +#include + +namespace toml +{ +namespace detail +{ + +template +bool skip_whitespace(location& loc, const context& ctx) +{ + return syntax::ws(ctx.toml_spec()).scan(loc).is_ok(); +} + +template +bool skip_empty_lines(location& loc, const context& ctx) +{ + return repeat_at_least(1, sequence( + syntax::ws(ctx.toml_spec()), + syntax::newline(ctx.toml_spec()) + )).scan(loc).is_ok(); +} + +// For error recovery. +// +// In case if a comment line contains an invalid character, we need to skip it +// to advance parsing. +template +void skip_comment_block(location& loc, const context& ctx) +{ + while( ! loc.eof()) + { + skip_whitespace(loc, ctx); + if(loc.current() == '#') + { + while( ! loc.eof()) + { + // both CRLF and LF ends with LF. + if(loc.current() == '\n') + { + loc.advance(); + break; + } + } + } + else if(syntax::newline(ctx.toml_spec()).scan(loc).is_ok()) + { + ; // an empty line. skip this also + } + else + { + // the next token is neither a comment nor empty line. + return ; + } + } + return ; +} + +template +void skip_empty_or_comment_lines(location& loc, const context& ctx) +{ + const auto& spec = ctx.toml_spec(); + repeat_at_least(0, sequence( + syntax::ws(spec), + maybe(syntax::comment(spec)), + syntax::newline(spec)) + ).scan(loc); + return ; +} + +// For error recovery. +// +// Sometimes we need to skip a value and find a delimiter, like `,`, `]`, or `}`. +// To find delimiter, we need to skip delimiters in a string. +// Since we are skipping invalid value while error recovery, we don't need +// to check the syntax. Here we just skip string-like region until closing quote +// is found. +template +void skip_string_like(location& loc, const context&) +{ + // if """ is found, skip until the closing """ is found. + if(literal("\"\"\"").scan(loc).is_ok()) + { + while( ! loc.eof()) + { + if(literal("\"\"\"").scan(loc).is_ok()) + { + return; + } + loc.advance(); + } + } + else if(literal("'''").scan(loc).is_ok()) + { + while( ! loc.eof()) + { + if(literal("'''").scan(loc).is_ok()) + { + return; + } + loc.advance(); + } + } + // if " is found, skip until the closing " or newline is found. + else if(loc.current() == '"') + { + while( ! loc.eof()) + { + loc.advance(); + if(loc.current() == '"' || loc.current() == '\n') + { + loc.advance(); + return; + } + } + } + else if(loc.current() == '\'') + { + while( ! loc.eof()) + { + loc.advance(); + if(loc.current() == '\'' || loc.current() == '\n') + { + loc.advance(); + return ; + } + } + } + return; +} + +template +void skip_value(location& loc, const context& ctx); +template +void skip_array_like(location& loc, const context& ctx); +template +void skip_inline_table_like(location& loc, const context& ctx); +template +void skip_key_value_pair(location& loc, const context& ctx); + +template +result +guess_value_type(const location& loc, const context& ctx); + +template +void skip_array_like(location& loc, const context& ctx) +{ + const auto& spec = ctx.toml_spec(); + assert(loc.current() == '['); + loc.advance(); + + while( ! loc.eof()) + { + if(loc.current() == '\"' || loc.current() == '\'') + { + skip_string_like(loc, ctx); + } + else if(loc.current() == '#') + { + skip_comment_block(loc, ctx); + } + else if(loc.current() == '{') + { + skip_inline_table_like(loc, ctx); + } + else if(loc.current() == '[') + { + const auto checkpoint = loc; + if(syntax::std_table(spec).scan(loc).is_ok() || + syntax::array_table(spec).scan(loc).is_ok()) + { + loc = checkpoint; + break; + } + // if it is not a table-definition, then it is an array. + skip_array_like(loc, ctx); + } + else if(loc.current() == '=') + { + // key-value pair cannot be inside the array. + // guessing the error is "missing closing bracket `]`". + // find the previous key just before `=`. + while(loc.get_location() != 0) + { + loc.retrace(); + if(loc.current() == '\n') + { + loc.advance(); + break; + } + } + break; + } + else if(loc.current() == ']') + { + break; // found closing bracket + } + else + { + loc.advance(); + } + } + return ; +} + +template +void skip_inline_table_like(location& loc, const context& ctx) +{ + assert(loc.current() == '{'); + loc.advance(); + + const auto& spec = ctx.toml_spec(); + + while( ! loc.eof()) + { + if(loc.current() == '\n' && ! spec.v1_1_0_allow_newlines_in_inline_tables) + { + break; // missing closing `}`. + } + else if(loc.current() == '\"' || loc.current() == '\'') + { + skip_string_like(loc, ctx); + } + else if(loc.current() == '#') + { + skip_comment_block(loc, ctx); + if( ! spec.v1_1_0_allow_newlines_in_inline_tables) + { + // comment must end with newline. + break; // missing closing `}`. + } + } + else if(loc.current() == '[') + { + const auto checkpoint = loc; + if(syntax::std_table(spec).scan(loc).is_ok() || + syntax::array_table(spec).scan(loc).is_ok()) + { + loc = checkpoint; + break; // missing closing `}`. + } + // if it is not a table-definition, then it is an array. + skip_array_like(loc, ctx); + } + else if(loc.current() == '{') + { + skip_inline_table_like(loc, ctx); + } + else if(loc.current() == '}') + { + // closing brace found. guessing the error is inside the table. + break; + } + else + { + // skip otherwise. + loc.advance(); + } + } + return ; +} + +template +void skip_value(location& loc, const context& ctx) +{ + value_t ty = guess_value_type(loc, ctx).unwrap_or(value_t::empty); + if(ty == value_t::string) + { + skip_string_like(loc, ctx); + } + else if(ty == value_t::array) + { + skip_array_like(loc, ctx); + } + else if(ty == value_t::table) + { + // In case of multiline tables, it may skip key-value pair but not the + // whole table. + skip_inline_table_like(loc, ctx); + } + else // others are an "in-line" values. skip until the next line + { + while( ! loc.eof()) + { + if(loc.current() == '\n') + { + break; + } + else if(loc.current() == ',' || loc.current() == ']' || loc.current() == '}') + { + break; + } + loc.advance(); + } + } + return; +} + +template +void skip_key_value_pair(location& loc, const context& ctx) +{ + while( ! loc.eof()) + { + if(loc.current() == '=') + { + skip_whitespace(loc, ctx); + skip_value(loc, ctx); + return; + } + else if(loc.current() == '\n') + { + // newline is found before finding `=`. assuming "missing `=`". + return; + } + loc.advance(); + } + return ; +} + +template +void skip_until_next_table(location& loc, const context& ctx) +{ + const auto& spec = ctx.toml_spec(); + while( ! loc.eof()) + { + if(loc.current() == '\n') + { + loc.advance(); + const auto line_begin = loc; + + skip_whitespace(loc, ctx); + if(syntax::std_table(spec).scan(loc).is_ok()) + { + loc = line_begin; + return ; + } + if(syntax::array_table(spec).scan(loc).is_ok()) + { + loc = line_begin; + return ; + } + } + loc.advance(); + } +} + +} // namespace detail +} // namespace toml + +#if defined(TOML11_COMPILE_SOURCES) +namespace toml +{ +struct type_config; +struct ordered_type_config; + +namespace detail +{ +extern template bool skip_whitespace (location& loc, const context&); +extern template bool skip_empty_lines (location& loc, const context&); +extern template void skip_comment_block (location& loc, const context&); +extern template void skip_empty_or_comment_lines(location& loc, const context&); +extern template void skip_string_like (location& loc, const context&); +extern template void skip_array_like (location& loc, const context&); +extern template void skip_inline_table_like (location& loc, const context&); +extern template void skip_value (location& loc, const context&); +extern template void skip_key_value_pair (location& loc, const context&); +extern template void skip_until_next_table (location& loc, const context&); + +extern template bool skip_whitespace (location& loc, const context&); +extern template bool skip_empty_lines (location& loc, const context&); +extern template void skip_comment_block (location& loc, const context&); +extern template void skip_empty_or_comment_lines(location& loc, const context&); +extern template void skip_string_like (location& loc, const context&); +extern template void skip_array_like (location& loc, const context&); +extern template void skip_inline_table_like (location& loc, const context&); +extern template void skip_value (location& loc, const context&); +extern template void skip_key_value_pair (location& loc, const context&); +extern template void skip_until_next_table (location& loc, const context&); + +} // detail +} // toml +#endif // TOML11_COMPILE_SOURCES + +#endif // TOML11_SKIP_HPP +#ifndef TOML11_PARSER_HPP +#define TOML11_PARSER_HPP + + +#include +#include + +#include +#include + +#if defined(TOML11_HAS_FILESYSTEM) && TOML11_HAS_FILESYSTEM +#include +#endif + +namespace toml +{ + +struct syntax_error final : public ::toml::exception +{ + public: + syntax_error(std::string what_arg, std::vector err) + : what_(std::move(what_arg)), err_(std::move(err)) + {} + ~syntax_error() noexcept override = default; + + const char* what() const noexcept override {return what_.c_str();} + + std::vector const& errors() const noexcept + { + return err_; + } + + private: + std::string what_; + std::vector err_; +}; + +struct file_io_error final : public ::toml::exception +{ + public: + + file_io_error(const std::string& msg, const std::string& fname) + : errno_(cxx::make_nullopt()), + what_(msg + " \"" + fname + "\"") + {} + file_io_error(int errnum, const std::string& msg, const std::string& fname) + : errno_(errnum), + what_(msg + " \"" + fname + "\": errno=" + std::to_string(errnum)) + {} + ~file_io_error() noexcept override = default; + + const char* what() const noexcept override {return what_.c_str();} + + bool has_errno() const noexcept {return errno_.has_value();} + int get_errno() const noexcept {return errno_.value_or(0);} + + private: + + cxx::optional errno_; + std::string what_; +}; + +namespace detail +{ + +/* ============================================================================ + * __ ___ _ __ _ __ ___ _ _ + * / _/ _ \ ' \| ' \/ _ \ ' \ + * \__\___/_|_|_|_|_|_\___/_||_| + */ + +template +error_info make_syntax_error(std::string title, + const S& scanner, location loc, std::string suffix = "") +{ + auto msg = std::string("expected ") + scanner.expected_chars(loc); + auto src = source_location(region(loc)); + return make_error_info( + std::move(title), std::move(src), std::move(msg), std::move(suffix)); +} + + +/* ============================================================================ + * _ + * __ ___ _ __ _ __ ___ _ _| |_ + * / _/ _ \ ' \| ' \/ -_) ' \ _| + * \__\___/_|_|_|_|_|_\___|_||_\__| + */ + +template +result, error_info> +parse_comment_line(location& loc, context& ctx) +{ + const auto& spec = ctx.toml_spec(); + const auto first = loc; + + skip_whitespace(loc, ctx); + + const auto com_reg = syntax::comment(spec).scan(loc); + if(com_reg.is_ok()) + { + // once comment started, newline must follow (or reach EOF). + if( ! loc.eof() && ! syntax::newline(spec).scan(loc).is_ok()) + { + while( ! loc.eof()) // skip until newline to continue parsing + { + loc.advance(); + if(loc.current() == '\n') { /*skip LF*/ loc.advance(); break; } + } + return err(make_error_info("toml::parse_comment_line: " + "newline (LF / CRLF) or EOF is expected", + source_location(region(loc)), "but got this", + "Hint: most of the control characters are not allowed in comments")); + } + return ok(cxx::optional(com_reg.as_string())); + } + else + { + loc = first; // rollback whitespace to parse indent + return ok(cxx::optional(cxx::make_nullopt())); + } +} + +/* ============================================================================ + * ___ _ + * | _ ) ___ ___| |___ __ _ _ _ + * | _ \/ _ \/ _ \ / -_) _` | ' \ + * |___/\___/\___/_\___\__,_|_||_| + */ + +template +result, error_info> +parse_boolean(location& loc, const context& ctx) +{ + const auto& spec = ctx.toml_spec(); + + // ---------------------------------------------------------------------- + // check syntax + auto reg = syntax::boolean(spec).scan(loc); + if( ! reg.is_ok()) + { + return err(make_syntax_error("toml::parse_boolean: " + "invalid boolean: boolean must be `true` or `false`, in lowercase. " + "string must be surrounded by `\"`", syntax::boolean(spec), loc)); + } + + // ---------------------------------------------------------------------- + // it matches. gen value + const auto str = reg.as_string(); + const auto val = [&str]() { + if(str == "true") + { + return true; + } + else + { + assert(str == "false"); + return false; + } + }(); + + // ---------------------------------------------------------------------- + // no format info for boolean + boolean_format_info fmt; + + return ok(basic_value(val, std::move(fmt), {}, std::move(reg))); +} + +/* ============================================================================ + * ___ _ + * |_ _|_ _| |_ ___ __ _ ___ _ _ + * | || ' \ _/ -_) _` / -_) '_| + * |___|_||_\__\___\__, \___|_| + * |___/ + */ + +template +result, error_info> +parse_bin_integer(location& loc, const context& ctx) +{ + const auto first = loc; + const auto& spec = ctx.toml_spec(); + auto reg = syntax::bin_int(spec).scan(loc); + if( ! reg.is_ok()) + { + return err(make_syntax_error("toml::parse_bin_integer: " + "invalid integer: bin_int must be like: 0b0101, 0b1111_0000", + syntax::bin_int(spec), loc)); + } + + auto str = reg.as_string(); + + integer_format_info fmt; + fmt.fmt = integer_format::bin; + fmt.width = str.size() - 2 - static_cast(std::count(str.begin(), str.end(), '_')); + + const auto first_underscore = std::find(str.rbegin(), str.rend(), '_'); + if(first_underscore != str.rend()) + { + fmt.spacer = static_cast(std::distance(str.rbegin(), first_underscore)); + } + + // skip prefix `0b` and zeros and underscores at the MSB + str.erase(str.begin(), std::find(std::next(str.begin(), 2), str.end(), '1')); + + // remove all `_` before calling TC::parse_int + str.erase(std::remove(str.begin(), str.end(), '_'), str.end()); + + // 0b0000_0000 becomes empty. + if(str.empty()) { str = "0"; } + + const auto val = TC::parse_int(str, source_location(region(loc)), 2); + if(val.is_ok()) + { + return ok(basic_value(val.as_ok(), std::move(fmt), {}, std::move(reg))); + } + else + { + loc = first; + return err(val.as_err()); + } +} + +// ---------------------------------------------------------------------------- + +template +result, error_info> +parse_oct_integer(location& loc, const context& ctx) +{ + const auto first = loc; + const auto& spec = ctx.toml_spec(); + auto reg = syntax::oct_int(spec).scan(loc); + if( ! reg.is_ok()) + { + return err(make_syntax_error("toml::parse_oct_integer: " + "invalid integer: oct_int must be like: 0o775, 0o04_44", + syntax::oct_int(spec), loc)); + } + + auto str = reg.as_string(); + + integer_format_info fmt; + fmt.fmt = integer_format::oct; + fmt.width = str.size() - 2 - static_cast(std::count(str.begin(), str.end(), '_')); + + const auto first_underscore = std::find(str.rbegin(), str.rend(), '_'); + if(first_underscore != str.rend()) + { + fmt.spacer = static_cast(std::distance(str.rbegin(), first_underscore)); + } + + // skip prefix `0o` and zeros and underscores at the MSB + str.erase(str.begin(), std::find_if( + std::next(str.begin(), 2), str.end(), [](const char c) { + return c != '0' && c != '_'; + })); + + // remove all `_` before calling TC::parse_int + str.erase(std::remove(str.begin(), str.end(), '_'), str.end()); + + // 0o0000_0000 becomes empty. + if(str.empty()) { str = "0"; } + + const auto val = TC::parse_int(str, source_location(region(loc)), 8); + if(val.is_ok()) + { + return ok(basic_value(val.as_ok(), std::move(fmt), {}, std::move(reg))); + } + else + { + loc = first; + return err(val.as_err()); + } +} + +template +result, error_info> +parse_hex_integer(location& loc, const context& ctx) +{ + const auto first = loc; + const auto& spec = ctx.toml_spec(); + auto reg = syntax::hex_int(spec).scan(loc); + if( ! reg.is_ok()) + { + return err(make_syntax_error("toml::parse_hex_integer: " + "invalid integer: hex_int must be like: 0xC0FFEE, 0xdead_beef", + syntax::hex_int(spec), loc)); + } + + auto str = reg.as_string(); + + integer_format_info fmt; + fmt.fmt = integer_format::hex; + fmt.width = str.size() - 2 - static_cast(std::count(str.begin(), str.end(), '_')); + + const auto first_underscore = std::find(str.rbegin(), str.rend(), '_'); + if(first_underscore != str.rend()) + { + fmt.spacer = static_cast(std::distance(str.rbegin(), first_underscore)); + } + + // skip prefix `0x` and zeros and underscores at the MSB + str.erase(str.begin(), std::find_if( + std::next(str.begin(), 2), str.end(), [](const char c) { + return c != '0' && c != '_'; + })); + + // remove all `_` before calling TC::parse_int + str.erase(std::remove(str.begin(), str.end(), '_'), str.end()); + + // 0x0000_0000 becomes empty. + if(str.empty()) { str = "0"; } + + // prefix zero and _ is removed. check if it uses upper/lower case. + // if both upper and lower case letters are found, set upper=true. + const auto lower_not_found = std::find_if(str.begin(), str.end(), + [](const char c) { return std::islower(static_cast(c)) != 0; }) == str.end(); + const auto upper_found = std::find_if(str.begin(), str.end(), + [](const char c) { return std::isupper(static_cast(c)) != 0; }) != str.end(); + fmt.uppercase = lower_not_found || upper_found; + + const auto val = TC::parse_int(str, source_location(region(loc)), 16); + if(val.is_ok()) + { + return ok(basic_value(val.as_ok(), std::move(fmt), {}, std::move(reg))); + } + else + { + loc = first; + return err(val.as_err()); + } +} + +template +result, error_info> +parse_dec_integer(location& loc, const context& ctx) +{ + const auto first = loc; + const auto& spec = ctx.toml_spec(); + + // ---------------------------------------------------------------------- + // check syntax + auto reg = syntax::dec_int(spec).scan(loc); + if( ! reg.is_ok()) + { + return err(make_syntax_error("toml::parse_dec_integer: " + "invalid integer: dec_int must be like: 42, 123_456_789", + syntax::dec_int(spec), loc)); + } + + // ---------------------------------------------------------------------- + // it matches. gen value + auto str = reg.as_string(); + + integer_format_info fmt; + fmt.fmt = integer_format::dec; + fmt.width = str.size() - static_cast(std::count(str.begin(), str.end(), '_')); + + const auto first_underscore = std::find(str.rbegin(), str.rend(), '_'); + if(first_underscore != str.rend()) + { + fmt.spacer = static_cast(std::distance(str.rbegin(), first_underscore)); + } + + // remove all `_` before calling TC::parse_int + str.erase(std::remove(str.begin(), str.end(), '_'), str.end()); + + auto src = source_location(region(loc)); + const auto val = TC::parse_int(str, src, 10); + if(val.is_err()) + { + loc = first; + return err(val.as_err()); + } + + // ---------------------------------------------------------------------- + // parse suffix (extension) + + if(spec.ext_num_suffix && loc.current() == '_') + { + const auto sfx_reg = syntax::num_suffix(spec).scan(loc); + if( ! sfx_reg.is_ok()) + { + loc = first; + return err(make_error_info("toml::parse_dec_integer: " + "invalid suffix: should be `_ non-digit-graph (graph | _graph)`", + source_location(region(loc)), "here")); + } + auto sfx = sfx_reg.as_string(); + assert( ! sfx.empty() && sfx.front() == '_'); + sfx.erase(sfx.begin()); // remove the first `_` + + fmt.suffix = sfx; + } + + return ok(basic_value(val.as_ok(), std::move(fmt), {}, std::move(reg))); +} + +template +result, error_info> +parse_integer(location& loc, const context& ctx) +{ + const auto first = loc; + + if( ! loc.eof() && (loc.current() == '+' || loc.current() == '-')) + { + // skip +/- to diagnose +0xDEADBEEF or -0b0011 (invalid). + // without this, +0xDEAD_BEEF will be parsed as a decimal int and + // unexpected "xDEAD_BEEF" will appear after integer "+0". + loc.advance(); + } + + if( ! loc.eof() && loc.current() == '0') + { + loc.advance(); + if(loc.eof()) + { + // `[+-]?0`. parse as an decimal integer. + loc = first; + return parse_dec_integer(loc, ctx); + } + + const auto prefix = loc.current(); + auto prefix_src = source_location(region(loc)); + + loc = first; + + if(prefix == 'b') {return parse_bin_integer(loc, ctx);} + if(prefix == 'o') {return parse_oct_integer(loc, ctx);} + if(prefix == 'x') {return parse_hex_integer(loc, ctx);} + + if(std::isdigit(prefix)) + { + auto src = source_location(region(loc)); + return err(make_error_info("toml::parse_integer: " + "leading zero in an decimal integer is not allowed", + std::move(src), "leading zero")); + } + } + + loc = first; + return parse_dec_integer(loc, ctx); +} + +/* ============================================================================ + * ___ _ _ _ + * | __| |___ __ _| |_(_)_ _ __ _ + * | _|| / _ \/ _` | _| | ' \/ _` | + * |_| |_\___/\__,_|\__|_|_||_\__, | + * |___/ + */ + +template +result, error_info> +parse_floating(location& loc, const context& ctx) +{ + using floating_type = typename basic_value::floating_type; + + const auto first = loc; + const auto& spec = ctx.toml_spec(); + + // ---------------------------------------------------------------------- + // check syntax + bool is_hex = false; + std::string str; + region reg; + if(spec.ext_hex_float && sequence(character('0'), character('x')).scan(loc).is_ok()) + { + loc = first; + is_hex = true; + + reg = syntax::hex_floating(spec).scan(loc); + if( ! reg.is_ok()) + { + return err(make_syntax_error("toml::parse_floating: " + "invalid hex floating: float must be like: 0xABCp-3f", + syntax::floating(spec), loc)); + } + str = reg.as_string(); + } + else + { + reg = syntax::floating(spec).scan(loc); + if( ! reg.is_ok()) + { + return err(make_syntax_error("toml::parse_floating: " + "invalid floating: float must be like: -3.14159_26535, 6.022e+23, " + "inf, or nan (lowercase).", syntax::floating(spec), loc)); + } + str = reg.as_string(); + } + + // ---------------------------------------------------------------------- + // it matches. gen value + + floating_format_info fmt; + + if(is_hex) + { + fmt.fmt = floating_format::hex; + } + else + { + // since we already checked that the string conforms the TOML standard. + if(std::find(str.begin(), str.end(), 'e') != str.end() || + std::find(str.begin(), str.end(), 'E') != str.end()) + { + fmt.fmt = floating_format::scientific; // use exponent part + } + else + { + fmt.fmt = floating_format::fixed; // do not use exponent part + } + } + + str.erase(std::remove(str.begin(), str.end(), '_'), str.end()); + + floating_type val{0}; + + if(str == "inf" || str == "+inf") + { + TOML11_CONSTEXPR_IF(std::numeric_limits::has_infinity) + { + val = std::numeric_limits::infinity(); + } + else + { + return err(make_error_info("toml::parse_floating: inf value found" + " but the current environment does not support inf. Please" + " make sure that the floating-point implementation conforms" + " IEEE 754/ISO 60559 international standard.", + source_location(region(loc)), + "floating_type: inf is not supported")); + } + } + else if(str == "-inf") + { + TOML11_CONSTEXPR_IF(std::numeric_limits::has_infinity) + { + val = -std::numeric_limits::infinity(); + } + else + { + return err(make_error_info("toml::parse_floating: inf value found" + " but the current environment does not support inf. Please" + " make sure that the floating-point implementation conforms" + " IEEE 754/ISO 60559 international standard.", + source_location(region(loc)), + "floating_type: inf is not supported")); + } + } + else if(str == "nan" || str == "+nan") + { + TOML11_CONSTEXPR_IF(std::numeric_limits::has_quiet_NaN) + { + val = std::numeric_limits::quiet_NaN(); + } + else TOML11_CONSTEXPR_IF(std::numeric_limits::has_signaling_NaN) + { + val = std::numeric_limits::signaling_NaN(); + } + else + { + return err(make_error_info("toml::parse_floating: NaN value found" + " but the current environment does not support NaN. Please" + " make sure that the floating-point implementation conforms" + " IEEE 754/ISO 60559 international standard.", + source_location(region(loc)), + "floating_type: NaN is not supported")); + } + } + else if(str == "-nan") + { + using std::copysign; + TOML11_CONSTEXPR_IF(std::numeric_limits::has_quiet_NaN) + { + val = copysign(std::numeric_limits::quiet_NaN(), floating_type(-1)); + } + else TOML11_CONSTEXPR_IF(std::numeric_limits::has_signaling_NaN) + { + val = copysign(std::numeric_limits::signaling_NaN(), floating_type(-1)); + } + else + { + return err(make_error_info("toml::parse_floating: NaN value found" + " but the current environment does not support NaN. Please" + " make sure that the floating-point implementation conforms" + " IEEE 754/ISO 60559 international standard.", + source_location(region(loc)), + "floating_type: NaN is not supported")); + } + } + else + { + // set precision + const auto has_sign = ! str.empty() && (str.front() == '+' || str.front() == '-'); + const auto decpoint = std::find(str.begin(), str.end(), '.'); + const auto exponent = std::find_if(str.begin(), str.end(), + [](const char c) { return c == 'e' || c == 'E'; }); + if(decpoint != str.end() && exponent != str.end()) + { + assert(decpoint < exponent); + } + + if(fmt.fmt == floating_format::scientific) + { + // total width + fmt.prec = static_cast(std::distance(str.begin(), exponent)); + if(has_sign) + { + fmt.prec -= 1; + } + if(decpoint != str.end()) + { + fmt.prec -= 1; + } + } + else if(fmt.fmt == floating_format::hex) + { + fmt.prec = std::numeric_limits::max_digits10; + } + else + { + // width after decimal point + fmt.prec = static_cast(std::distance(std::next(decpoint), exponent)); + } + + auto src = source_location(region(loc)); + const auto res = TC::parse_float(str, src, is_hex); + if(res.is_ok()) + { + val = res.as_ok(); + } + else + { + return err(res.as_err()); + } + } + + // ---------------------------------------------------------------------- + // parse suffix (extension) + + if(spec.ext_num_suffix && loc.current() == '_') + { + const auto sfx_reg = syntax::num_suffix(spec).scan(loc); + if( ! sfx_reg.is_ok()) + { + auto src = source_location(region(loc)); + loc = first; + return err(make_error_info("toml::parse_floating: " + "invalid suffix: should be `_ non-digit-graph (graph | _graph)`", + std::move(src), "here")); + } + auto sfx = sfx_reg.as_string(); + assert( ! sfx.empty() && sfx.front() == '_'); + sfx.erase(sfx.begin()); // remove the first `_` + + fmt.suffix = sfx; + } + + return ok(basic_value(val, std::move(fmt), {}, std::move(reg))); +} + +/* ============================================================================ + * ___ _ _ _ + * | \ __ _| |_ ___| |_(_)_ __ ___ + * | |) / _` | _/ -_) _| | ' \/ -_) + * |___/\__,_|\__\___|\__|_|_|_|_\___| + */ + +// all the offset_datetime, local_datetime, local_date parses date part. +template +result, error_info> +parse_local_date_only(location& loc, const context& ctx) +{ + const auto first = loc; + const auto& spec = ctx.toml_spec(); + + local_date_format_info fmt; + + // ---------------------------------------------------------------------- + // check syntax + auto reg = syntax::local_date(spec).scan(loc); + if( ! reg.is_ok()) + { + return err(make_syntax_error("toml::parse_local_date: " + "invalid date: date must be like: 1234-05-06, yyyy-mm-dd.", + syntax::local_date(spec), loc)); + } + + // ---------------------------------------------------------------------- + // it matches. gen value + const auto str = reg.as_string(); + + // 0123456789 + // yyyy-mm-dd + const auto year_r = from_string(str.substr(0, 4)); + const auto month_r = from_string(str.substr(5, 2)); + const auto day_r = from_string(str.substr(8, 2)); + + if(year_r.is_err()) + { + auto src = source_location(region(first)); + return err(make_error_info("toml::parse_local_date: " + "failed to read year `" + str.substr(0, 4) + "`", + std::move(src), "here")); + } + if(month_r.is_err()) + { + auto src = source_location(region(first)); + return err(make_error_info("toml::parse_local_date: " + "failed to read month `" + str.substr(5, 2) + "`", + std::move(src), "here")); + } + if(day_r.is_err()) + { + auto src = source_location(region(first)); + return err(make_error_info("toml::parse_local_date: " + "failed to read day `" + str.substr(8, 2) + "`", + std::move(src), "here")); + } + + const auto year = year_r.unwrap(); + const auto month = month_r.unwrap(); + const auto day = day_r.unwrap(); + + { + // We briefly check whether the input date is valid or not. + // Actually, because of the historical reasons, there are several + // edge cases, such as 1582/10/5-1582/10/14 (only in several countries). + // But here, we do not care about it. + // It makes the code complicated and there is only low probability + // that such a specific date is needed in practice. If someone need to + // validate date accurately, that means that the one need a specialized + // library for their purpose in another layer. + + const bool is_leap = (year % 4 == 0) && ((year % 100 != 0) || (year % 400 == 0)); + const auto max_day = [month, is_leap]() { + if(month == 2) + { + return is_leap ? 29 : 28; + } + if(month == 4 || month == 6 || month == 9 || month == 11) + { + return 30; + } + return 31; + }(); + + if((month < 1 || 12 < month) || (day < 1 || max_day < day)) + { + auto src = source_location(region(first)); + return err(make_error_info("toml::parse_local_date: invalid date.", + std::move(src), "month must be 01-12, day must be any of " + "01-28,29,30,31 depending on the month/year.")); + } + } + + return ok(std::make_tuple( + local_date(year, static_cast(month - 1), day), + std::move(fmt), std::move(reg) + )); +} + +template +result, error_info> +parse_local_date(location& loc, const context& ctx) +{ + auto val_fmt_reg = parse_local_date_only(loc, ctx); + if(val_fmt_reg.is_err()) + { + return err(val_fmt_reg.unwrap_err()); + } + + auto val = std::move(std::get<0>(val_fmt_reg.unwrap())); + auto fmt = std::move(std::get<1>(val_fmt_reg.unwrap())); + auto reg = std::move(std::get<2>(val_fmt_reg.unwrap())); + + return ok(basic_value(std::move(val), std::move(fmt), {}, std::move(reg))); +} + +// all the offset_datetime, local_datetime, local_time parses date part. +template +result, error_info> +parse_local_time_only(location& loc, const context& ctx) +{ + const auto first = loc; + const auto& spec = ctx.toml_spec(); + + local_time_format_info fmt; + + // ---------------------------------------------------------------------- + // check syntax + auto reg = syntax::local_time(spec).scan(loc); + if( ! reg.is_ok()) + { + if(spec.v1_1_0_make_seconds_optional) + { + return err(make_syntax_error("toml::parse_local_time: " + "invalid time: time must be HH:MM(:SS.sss) (seconds are optional)", + syntax::local_time(spec), loc)); + } + else + { + return err(make_syntax_error("toml::parse_local_time: " + "invalid time: time must be HH:MM:SS(.sss) (subseconds are optional)", + syntax::local_time(spec), loc)); + } + } + + // ---------------------------------------------------------------------- + // it matches. gen value + const auto str = reg.as_string(); + + // at least we have HH:MM. + // 01234 + // HH:MM + const auto hour_r = from_string(str.substr(0, 2)); + const auto minute_r = from_string(str.substr(3, 2)); + + if(hour_r.is_err()) + { + auto src = source_location(region(first)); + return err(make_error_info("toml::parse_local_time: " + "failed to read hour `" + str.substr(0, 2) + "`", + std::move(src), "here")); + } + if(minute_r.is_err()) + { + auto src = source_location(region(first)); + return err(make_error_info("toml::parse_local_time: " + "failed to read minute `" + str.substr(3, 2) + "`", + std::move(src), "here")); + } + + const auto hour = hour_r.unwrap(); + const auto minute = minute_r.unwrap(); + + if((hour < 0 || 24 <= hour) || (minute < 0 || 60 <= minute)) + { + auto src = source_location(region(first)); + return err(make_error_info("toml::parse_local_time: invalid time.", + std::move(src), "hour must be 00-23, minute must be 00-59.")); + } + + // ----------------------------------------------------------------------- + // we have hour and minute. + // Since toml v1.1.0, second and subsecond part becomes optional. + // Check the version and return if second does not exist. + + if(str.size() == 5 && spec.v1_1_0_make_seconds_optional) + { + fmt.has_seconds = false; + fmt.subsecond_precision = 0; + return ok(std::make_tuple(local_time(hour, minute, 0), std::move(fmt), std::move(reg))); + } + assert(str.at(5) == ':'); + + // we have at least `:SS` part. `.subseconds` are optional. + + // 0 1 + // 012345678901234 + // HH:MM:SS.subsec + const auto sec_r = from_string(str.substr(6, 2)); + if(sec_r.is_err()) + { + auto src = source_location(region(first)); + return err(make_error_info("toml::parse_local_time: " + "failed to read second `" + str.substr(6, 2) + "`", + std::move(src), "here")); + } + const auto sec = sec_r.unwrap(); + + if(sec < 0 || 60 < sec) // :60 is allowed + { + auto src = source_location(region(first)); + return err(make_error_info("toml::parse_local_time: invalid time.", + std::move(src), "second must be 00-60.")); + } + + if(str.size() == 8) + { + fmt.has_seconds = true; + fmt.subsecond_precision = 0; + return ok(std::make_tuple(local_time(hour, minute, sec), std::move(fmt), std::move(reg))); + } + + assert(str.at(8) == '.'); + + auto secfrac = str.substr(9, str.size() - 9); + + fmt.has_seconds = true; + fmt.subsecond_precision = secfrac.size(); + + while(secfrac.size() < 9) + { + secfrac += '0'; + } + assert(9 <= secfrac.size()); + const auto ms_r = from_string(secfrac.substr(0, 3)); + const auto us_r = from_string(secfrac.substr(3, 3)); + const auto ns_r = from_string(secfrac.substr(6, 3)); + + if(ms_r.is_err()) + { + auto src = source_location(region(first)); + return err(make_error_info("toml::parse_local_time: " + "failed to read milliseconds `" + secfrac.substr(0, 3) + "`", + std::move(src), "here")); + } + if(us_r.is_err()) + { + auto src = source_location(region(first)); + return err(make_error_info("toml::parse_local_time: " + "failed to read microseconds`" + str.substr(3, 3) + "`", + std::move(src), "here")); + } + if(ns_r.is_err()) + { + auto src = source_location(region(first)); + return err(make_error_info("toml::parse_local_time: " + "failed to read nanoseconds`" + str.substr(6, 3) + "`", + std::move(src), "here")); + } + const auto ms = ms_r.unwrap(); + const auto us = us_r.unwrap(); + const auto ns = ns_r.unwrap(); + + return ok(std::make_tuple(local_time(hour, minute, sec, ms, us, ns), std::move(fmt), std::move(reg))); +} + +template +result, error_info> +parse_local_time(location& loc, const context& ctx) +{ + const auto first = loc; + + auto val_fmt_reg = parse_local_time_only(loc, ctx); + if(val_fmt_reg.is_err()) + { + return err(val_fmt_reg.unwrap_err()); + } + + auto val = std::move(std::get<0>(val_fmt_reg.unwrap())); + auto fmt = std::move(std::get<1>(val_fmt_reg.unwrap())); + auto reg = std::move(std::get<2>(val_fmt_reg.unwrap())); + + return ok(basic_value(std::move(val), std::move(fmt), {}, std::move(reg))); +} + +template +result, error_info> +parse_local_datetime(location& loc, const context& ctx) +{ + using char_type = location::char_type; + + const auto first = loc; + + local_datetime_format_info fmt; + + // ---------------------------------------------------------------------- + + auto date_fmt_reg = parse_local_date_only(loc, ctx); + if(date_fmt_reg.is_err()) + { + return err(date_fmt_reg.unwrap_err()); + } + + if(loc.current() == char_type('T')) + { + loc.advance(); + fmt.delimiter = datetime_delimiter_kind::upper_T; + } + else if(loc.current() == char_type('t')) + { + loc.advance(); + fmt.delimiter = datetime_delimiter_kind::lower_t; + } + else if(loc.current() == char_type(' ')) + { + loc.advance(); + fmt.delimiter = datetime_delimiter_kind::space; + } + else + { + auto src = source_location(region(loc)); + return err(make_error_info("toml::parse_local_datetime: " + "expect date-time delimiter `T`, `t` or ` `(space).", + std::move(src), "here")); + } + + auto time_fmt_reg = parse_local_time_only(loc, ctx); + if(time_fmt_reg.is_err()) + { + return err(time_fmt_reg.unwrap_err()); + } + + fmt.has_seconds = std::get<1>(time_fmt_reg.unwrap()).has_seconds; + fmt.subsecond_precision = std::get<1>(time_fmt_reg.unwrap()).subsecond_precision; + + // ---------------------------------------------------------------------- + + region reg(first, loc); + local_datetime val(std::get<0>(date_fmt_reg.unwrap()), + std::get<0>(time_fmt_reg.unwrap())); + + return ok(basic_value(val, std::move(fmt), {}, std::move(reg))); +} + +template +result, error_info> +parse_offset_datetime(location& loc, const context& ctx) +{ + using char_type = location::char_type; + + const auto first = loc; + const auto& spec = ctx.toml_spec(); + + offset_datetime_format_info fmt; + + // ---------------------------------------------------------------------- + // date part + + auto date_fmt_reg = parse_local_date_only(loc, ctx); + if(date_fmt_reg.is_err()) + { + return err(date_fmt_reg.unwrap_err()); + } + + // ---------------------------------------------------------------------- + // delimiter + + if(loc.current() == char_type('T')) + { + loc.advance(); + fmt.delimiter = datetime_delimiter_kind::upper_T; + } + else if(loc.current() == char_type('t')) + { + loc.advance(); + fmt.delimiter = datetime_delimiter_kind::lower_t; + } + else if(loc.current() == char_type(' ')) + { + loc.advance(); + fmt.delimiter = datetime_delimiter_kind::space; + } + else + { + auto src = source_location(region(loc)); + return err(make_error_info("toml::parse_offset_datetime: " + "expect date-time delimiter `T` or ` `(space).", std::move(src), "here" + )); + } + + // ---------------------------------------------------------------------- + // time part + + auto time_fmt_reg = parse_local_time_only(loc, ctx); + if(time_fmt_reg.is_err()) + { + return err(time_fmt_reg.unwrap_err()); + } + + fmt.has_seconds = std::get<1>(time_fmt_reg.unwrap()).has_seconds; + fmt.subsecond_precision = std::get<1>(time_fmt_reg.unwrap()).subsecond_precision; + + // ---------------------------------------------------------------------- + // offset part + + const auto ofs_reg = syntax::time_offset(spec).scan(loc); + if( ! ofs_reg.is_ok()) + { + return err(make_syntax_error("toml::parse_offset_datetime: " + "invalid offset: offset must be like: Z, +01:00, or -10:00.", + syntax::time_offset(spec), loc)); + } + + const auto ofs_str = ofs_reg.as_string(); + + time_offset offset(0, 0); + + assert(ofs_str.size() != 0); + + if(ofs_str.at(0) == char_type('+') || ofs_str.at(0) == char_type('-')) + { + const auto hour_r = from_string(ofs_str.substr(1, 2)); + const auto minute_r = from_string(ofs_str.substr(4, 2)); + if(hour_r.is_err()) + { + auto src = source_location(region(loc)); + return err(make_error_info("toml::parse_offset_datetime: " + "Failed to read offset hour part", std::move(src), "here")); + } + if(minute_r.is_err()) + { + auto src = source_location(region(loc)); + return err(make_error_info("toml::parse_offset_datetime: " + "Failed to read offset minute part", std::move(src), "here")); + } + const auto hour = hour_r.unwrap(); + const auto minute = minute_r.unwrap(); + + if(ofs_str.at(0) == '+') + { + offset = time_offset(hour, minute); + } + else + { + offset = time_offset(-hour, -minute); + } + } + else + { + assert(ofs_str.at(0) == char_type('Z') || ofs_str.at(0) == char_type('z')); + } + + if (offset.hour < -24 || 24 < offset.hour || + offset.minute < -60 || 60 < offset.minute) + { + return err(make_error_info("toml::parse_offset_datetime: " + "too large offset: |hour| <= 24, |minute| <= 60", + source_location(region(first, loc)), "here")); + } + + + // ---------------------------------------------------------------------- + + region reg(first, loc); + offset_datetime val(local_datetime(std::get<0>(date_fmt_reg.unwrap()), + std::get<0>(time_fmt_reg.unwrap())), + offset); + + return ok(basic_value(val, std::move(fmt), {}, std::move(reg))); +} + +/* ============================================================================ + * ___ _ _ + * / __| |_ _ _(_)_ _ __ _ + * \__ \ _| '_| | ' \/ _` | + * |___/\__|_| |_|_||_\__, | + * |___/ + */ + +template +result::string_type, error_info> +parse_utf8_codepoint(const region& reg) +{ + using string_type = typename basic_value::string_type; + using char_type = typename string_type::value_type; + + // assert(reg.as_lines().size() == 1); // XXX heavy check + + const auto str = reg.as_string(); + assert( ! str.empty()); + assert(str.front() == 'u' || str.front() == 'U' || str.front() == 'x'); + + std::uint_least32_t codepoint; + std::istringstream iss(str.substr(1)); + iss >> std::hex >> codepoint; + + const auto to_char = [](const std::uint_least32_t i) noexcept -> char_type { + const auto uc = static_cast(i & 0xFF); + return cxx::bit_cast(uc); + }; + + string_type character; + if(codepoint < 0x80) // U+0000 ... U+0079 ; just an ASCII. + { + character += static_cast(codepoint); + } + else if(codepoint < 0x800) //U+0080 ... U+07FF + { + // 110yyyyx 10xxxxxx; 0x3f == 0b0011'1111 + character += to_char(0xC0|(codepoint >> 6 )); + character += to_char(0x80|(codepoint & 0x3F)); + } + else if(codepoint < 0x10000) // U+0800...U+FFFF + { + if(0xD800 <= codepoint && codepoint <= 0xDFFF) + { + auto src = source_location(reg); + return err(make_error_info("toml::parse_utf8_codepoint: " + "[0xD800, 0xDFFF] is not a valid UTF-8", + std::move(src), "here")); + } + assert(codepoint < 0xD800 || 0xDFFF < codepoint); + // 1110yyyy 10yxxxxx 10xxxxxx + character += to_char(0xE0| (codepoint >> 12)); + character += to_char(0x80|((codepoint >> 6 ) & 0x3F)); + character += to_char(0x80|((codepoint ) & 0x3F)); + } + else if(codepoint < 0x110000) // U+010000 ... U+10FFFF + { + // 11110yyy 10yyxxxx 10xxxxxx 10xxxxxx + character += to_char(0xF0| (codepoint >> 18)); + character += to_char(0x80|((codepoint >> 12) & 0x3F)); + character += to_char(0x80|((codepoint >> 6 ) & 0x3F)); + character += to_char(0x80|((codepoint ) & 0x3F)); + } + else // out of UTF-8 region + { + auto src = source_location(reg); + return err(make_error_info("toml::parse_utf8_codepoint: " + "input codepoint is too large.", + std::move(src), "must be in range [0x00, 0x10FFFF]")); + } + return ok(character); +} + +template +result::string_type, error_info> +parse_escape_sequence(location& loc, const context& ctx) +{ + using string_type = typename basic_value::string_type; + using char_type = typename string_type::value_type; + + const auto& spec = ctx.toml_spec(); + + assert( ! loc.eof()); + assert(loc.current() == '\\'); + loc.advance(); // consume the first backslash + + string_type retval; + + if (loc.current() == '\\') { retval += char_type('\\'); loc.advance(); } + else if(loc.current() == '"') { retval += char_type('\"'); loc.advance(); } + else if(loc.current() == 'b') { retval += char_type('\b'); loc.advance(); } + else if(loc.current() == 'f') { retval += char_type('\f'); loc.advance(); } + else if(loc.current() == 'n') { retval += char_type('\n'); loc.advance(); } + else if(loc.current() == 'r') { retval += char_type('\r'); loc.advance(); } + else if(loc.current() == 't') { retval += char_type('\t'); loc.advance(); } + else if(spec.v1_1_0_add_escape_sequence_e && loc.current() == 'e') + { + retval += char_type('\x1b'); + loc.advance(); + } + else if(spec.v1_1_0_add_escape_sequence_x && loc.current() == 'x') + { + auto scanner = sequence(character('x'), repeat_exact(2, syntax::hexdig(spec))); + const auto reg = scanner.scan(loc); + if( ! reg.is_ok()) + { + auto src = source_location(region(loc)); + return err(make_error_info("toml::parse_escape_sequence: " + "invalid token found in UTF-8 codepoint \\xhh", + std::move(src), "here")); + } + const auto utf8 = parse_utf8_codepoint(reg); + if(utf8.is_err()) + { + return err(utf8.as_err()); + } + retval += utf8.unwrap(); + } + else if(loc.current() == 'u') + { + auto scanner = sequence(character('u'), repeat_exact(4, syntax::hexdig(spec))); + const auto reg = scanner.scan(loc); + if( ! reg.is_ok()) + { + auto src = source_location(region(loc)); + return err(make_error_info("toml::parse_escape_sequence: " + "invalid token found in UTF-8 codepoint \\uhhhh", + std::move(src), "here")); + } + const auto utf8 = parse_utf8_codepoint(reg); + if(utf8.is_err()) + { + return err(utf8.as_err()); + } + retval += utf8.unwrap(); + } + else if(loc.current() == 'U') + { + auto scanner = sequence(character('U'), repeat_exact(8, syntax::hexdig(spec))); + const auto reg = scanner.scan(loc); + if( ! reg.is_ok()) + { + auto src = source_location(region(loc)); + return err(make_error_info("toml::parse_escape_sequence: " + "invalid token found in UTF-8 codepoint \\Uhhhhhhhh", + std::move(src), "here")); + } + const auto utf8 = parse_utf8_codepoint(reg); + if(utf8.is_err()) + { + return err(utf8.as_err()); + } + retval += utf8.unwrap(); + } + else + { + auto src = source_location(region(loc)); + std::string escape_seqs = "allowed escape seqs: \\\\, \\\", \\b, \\f, \\n, \\r, \\t"; + if(spec.v1_1_0_add_escape_sequence_e) + { + escape_seqs += ", \\e"; + } + if(spec.v1_1_0_add_escape_sequence_x) + { + escape_seqs += ", \\xhh"; + } + escape_seqs += ", \\uhhhh, or \\Uhhhhhhhh"; + + return err(make_error_info("toml::parse_escape_sequence: " + "unknown escape sequence.", std::move(src), escape_seqs)); + } + return ok(retval); +} + +template +result, error_info> +parse_ml_basic_string(location& loc, const context& ctx) +{ + const auto first = loc; + const auto& spec = ctx.toml_spec(); + + string_format_info fmt; + fmt.fmt = string_format::multiline_basic; + + auto reg = syntax::ml_basic_string(spec).scan(loc); + if( ! reg.is_ok()) + { + return err(make_syntax_error("toml::parse_ml_basic_string: " + "invalid string format", + syntax::ml_basic_string(spec), loc)); + } + + // ---------------------------------------------------------------------- + // it matches. gen value + + auto str = reg.as_string(); + + // we already checked that it starts with """ and ends with """. + assert(str.substr(0, 3) == "\"\"\""); + str.erase(0, 3); + + assert(str.size() >= 3); + assert(str.substr(str.size()-3, 3) == "\"\"\""); + str.erase(str.size()-3, 3); + + // the first newline just after """ is trimmed + if(str.size() >= 1 && str.at(0) == '\n') + { + str.erase(0, 1); + fmt.start_with_newline = true; + } + else if(str.size() >= 2 && str.at(0) == '\r' && str.at(1) == '\n') + { + str.erase(0, 2); + fmt.start_with_newline = true; + } + + using string_type = typename basic_value::string_type; + string_type val; + { + auto iter = str.cbegin(); + while(iter != str.cend()) + { + if(*iter == '\\') // remove whitespaces around escaped-newline + { + // we assume that the string is not too long to copy + auto loc2 = make_temporary_location(make_string(iter, str.cend())); + if(syntax::escaped_newline(spec).scan(loc2).is_ok()) + { + std::advance(iter, loc2.get_location()); // skip escaped newline and indent + // now iter points non-WS char + assert(iter == str.end() || (*iter != ' ' && *iter != '\t')); + } + else // normal escape seq. + { + auto esc = parse_escape_sequence(loc2, ctx); + + // syntax does not check its value. the unicode codepoint may be + // invalid, e.g. out-of-bound, [0xD800, 0xDFFF] + if(esc.is_err()) + { + return err(esc.unwrap_err()); + } + + val += esc.unwrap(); + std::advance(iter, loc2.get_location()); + } + } + else // we already checked the syntax. we don't need to check it again. + { + val += static_cast(*iter); + ++iter; + } + } + } + + return ok(basic_value( + std::move(val), std::move(fmt), {}, std::move(reg) + )); +} + +template +result::string_type, region>, error_info> +parse_basic_string_only(location& loc, const context& ctx) +{ + const auto first = loc; + const auto& spec = ctx.toml_spec(); + + auto reg = syntax::basic_string(spec).scan(loc); + if( ! reg.is_ok()) + { + return err(make_syntax_error("toml::parse_basic_string: " + "invalid string format", + syntax::basic_string(spec), loc)); + } + + // ---------------------------------------------------------------------- + // it matches. gen value + + auto str = reg.as_string(); + + assert(str.back() == '\"'); + str.pop_back(); + assert(str.at(0) == '\"'); + str.erase(0, 1); + + using string_type = typename basic_value::string_type; + using char_type = typename string_type::value_type; + string_type val; + + { + auto iter = str.begin(); + while(iter != str.end()) + { + if(*iter == '\\') + { + auto loc2 = make_temporary_location(make_string(iter, str.end())); + + auto esc = parse_escape_sequence(loc2, ctx); + + // syntax does not check its value. the unicode codepoint may be + // invalid, e.g. out-of-bound, [0xD800, 0xDFFF] + if(esc.is_err()) + { + return err(esc.unwrap_err()); + } + + val += esc.unwrap(); + std::advance(iter, loc2.get_location()); + } + else + { + val += char_type(*iter); // we already checked the syntax. + ++iter; + } + } + } + return ok(std::make_pair(val, reg)); +} + +template +result, error_info> +parse_basic_string(location& loc, const context& ctx) +{ + const auto first = loc; + + string_format_info fmt; + fmt.fmt = string_format::basic; + + auto val_res = parse_basic_string_only(loc, ctx); + if(val_res.is_err()) + { + return err(std::move(val_res.unwrap_err())); + } + auto val = std::move(val_res.unwrap().first ); + auto reg = std::move(val_res.unwrap().second); + + return ok(basic_value(std::move(val), std::move(fmt), {}, std::move(reg))); +} + +template +result, error_info> +parse_ml_literal_string(location& loc, const context& ctx) +{ + const auto first = loc; + const auto& spec = ctx.toml_spec(); + + string_format_info fmt; + fmt.fmt = string_format::multiline_literal; + + auto reg = syntax::ml_literal_string(spec).scan(loc); + if( ! reg.is_ok()) + { + return err(make_syntax_error("toml::parse_ml_literal_string: " + "invalid string format", + syntax::ml_literal_string(spec), loc)); + } + + // ---------------------------------------------------------------------- + // it matches. gen value + + auto str = reg.as_string(); + + assert(str.substr(0, 3) == "'''"); + assert(str.substr(str.size()-3, 3) == "'''"); + str.erase(0, 3); + str.erase(str.size()-3, 3); + + // the first newline just after """ is trimmed + if(str.size() >= 1 && str.at(0) == '\n') + { + str.erase(0, 1); + fmt.start_with_newline = true; + } + else if(str.size() >= 2 && str.at(0) == '\r' && str.at(1) == '\n') + { + str.erase(0, 2); + fmt.start_with_newline = true; + } + + using string_type = typename basic_value::string_type; + string_type val(str.begin(), str.end()); + + return ok(basic_value( + std::move(val), std::move(fmt), {}, std::move(reg) + )); +} + +template +result::string_type, region>, error_info> +parse_literal_string_only(location& loc, const context& ctx) +{ + const auto first = loc; + const auto& spec = ctx.toml_spec(); + + auto reg = syntax::literal_string(spec).scan(loc); + if( ! reg.is_ok()) + { + return err(make_syntax_error("toml::parse_literal_string: " + "invalid string format", + syntax::literal_string(spec), loc)); + } + + // ---------------------------------------------------------------------- + // it matches. gen value + + auto str = reg.as_string(); + + assert(str.back() == '\''); + str.pop_back(); + assert(str.at(0) == '\''); + str.erase(0, 1); + + using string_type = typename basic_value::string_type; + string_type val(str.begin(), str.end()); + + return ok(std::make_pair(std::move(val), std::move(reg))); +} + +template +result, error_info> +parse_literal_string(location& loc, const context& ctx) +{ + const auto first = loc; + + string_format_info fmt; + fmt.fmt = string_format::literal; + + auto val_res = parse_literal_string_only(loc, ctx); + if(val_res.is_err()) + { + return err(std::move(val_res.unwrap_err())); + } + auto val = std::move(val_res.unwrap().first ); + auto reg = std::move(val_res.unwrap().second); + + return ok(basic_value( + std::move(val), std::move(fmt), {}, std::move(reg) + )); +} + +template +result, error_info> +parse_string(location& loc, const context& ctx) +{ + const auto first = loc; + + if( ! loc.eof() && loc.current() == '"') + { + if(literal("\"\"\"").scan(loc).is_ok()) + { + loc = first; + return parse_ml_basic_string(loc, ctx); + } + else + { + loc = first; + return parse_basic_string(loc, ctx); + } + } + else if( ! loc.eof() && loc.current() == '\'') + { + if(literal("'''").scan(loc).is_ok()) + { + loc = first; + return parse_ml_literal_string(loc, ctx); + } + else + { + loc = first; + return parse_literal_string(loc, ctx); + } + } + else + { + auto src = source_location(region(loc)); + return err(make_error_info("toml::parse_string: " + "not a string", std::move(src), "here")); + } +} + +template +result, error_info> +parse_null(location& loc, const context& ctx) +{ + const auto& spec = ctx.toml_spec(); + if( ! spec.ext_null_value) + { + return err(make_error_info("toml::parse_null: " + "invalid spec: spec.ext_null_value must be true.", + source_location(region(loc)), "here")); + } + + // ---------------------------------------------------------------------- + // check syntax + auto reg = syntax::null_value(spec).scan(loc); + if( ! reg.is_ok()) + { + return err(make_syntax_error("toml::parse_null: " + "invalid null: null must be lowercase. ", + syntax::null_value(spec), loc)); + } + + // ---------------------------------------------------------------------- + // it matches. gen value + + // ---------------------------------------------------------------------- + // no format info for boolean + + return ok(basic_value(detail::none_t{}, std::move(reg))); +} + +/* ============================================================================ + * _ __ + * | |/ /___ _ _ + * | ' +result::key_type, error_info> +parse_simple_key(location& loc, const context& ctx) +{ + using key_type = typename basic_value::key_type; + const auto& spec = ctx.toml_spec(); + + if(loc.current() == '\"') + { + auto str_res = parse_basic_string_only(loc, ctx); + if(str_res.is_ok()) + { + return ok(std::move(str_res.unwrap().first)); + } + else + { + return err(std::move(str_res.unwrap_err())); + } + } + else if(loc.current() == '\'') + { + auto str_res = parse_literal_string_only(loc, ctx); + if(str_res.is_ok()) + { + return ok(std::move(str_res.unwrap().first)); + } + else + { + return err(std::move(str_res.unwrap_err())); + } + } + + // bare key. + + if(const auto bare = syntax::unquoted_key(spec).scan(loc)) + { + return ok(string_conv(bare.as_string())); + } + else + { + std::string postfix; + if(spec.v1_1_0_allow_non_english_in_bare_keys) + { + postfix = "Hint: Not all Unicode characters are allowed as bare key.\n"; + } + else + { + postfix = "Hint: non-ASCII scripts are allowed in toml v1.1.0, but not in v1.0.0.\n"; + } + return err(make_syntax_error("toml::parse_simple_key: " + "invalid key: key must be \"quoted\", 'quoted-literal', or bare key.", + syntax::unquoted_key(spec), loc, postfix)); + } +} + +// dotted key become vector of keys +template +result::key_type>, region>, error_info> +parse_key(location& loc, const context& ctx) +{ + const auto first = loc; + const auto& spec = ctx.toml_spec(); + + using key_type = typename basic_value::key_type; + std::vector keys; + while( ! loc.eof()) + { + auto key = parse_simple_key(loc, ctx); + if( ! key.is_ok()) + { + return err(key.unwrap_err()); + } + keys.push_back(std::move(key.unwrap())); + + auto reg = syntax::dot_sep(spec).scan(loc); + if( ! reg.is_ok()) + { + break; + } + } + if(keys.empty()) + { + auto src = source_location(region(first)); + return err(make_error_info("toml::parse_key: expected a new key, " + "but got nothing", std::move(src), "reached EOF")); + } + + return ok(std::make_pair(std::move(keys), region(first, loc))); +} + +// ============================================================================ + +// forward-decl to implement parse_array and parse_table +template +result, error_info> +parse_value(location&, context& ctx); + +template +result::key_type>, region>, + basic_value + >, error_info> +parse_key_value_pair(location& loc, context& ctx) +{ + const auto first = loc; + const auto& spec = ctx.toml_spec(); + + auto key_res = parse_key(loc, ctx); + if(key_res.is_err()) + { + loc = first; + return err(key_res.unwrap_err()); + } + + if( ! syntax::keyval_sep(spec).scan(loc).is_ok()) + { + auto e = make_syntax_error("toml::parse_key_value_pair: " + "invalid key value separator `=`", syntax::keyval_sep(spec), loc); + loc = first; + return err(std::move(e)); + } + + auto v_res = parse_value(loc, ctx); + if(v_res.is_err()) + { + // loc = first; + return err(v_res.unwrap_err()); + } + return ok(std::make_pair(std::move(key_res.unwrap()), std::move(v_res.unwrap()))); +} + +/* ============================================================================ + * __ _ _ _ _ _ __ _ _ _ + * / _` | '_| '_/ _` | || | + * \__,_|_| |_| \__,_|\_, | + * |__/ + */ + +// array(and multiline inline table with `{` and `}`) has the following format. +// `[` +// (ws|newline|comment-line)? (value) (ws|newline|comment-line)? `,` +// (ws|newline|comment-line)? (value) (ws|newline|comment-line)? `,` +// ... +// (ws|newline|comment-line)? (value) (ws|newline|comment-line)? (`,`)? +// (ws|newline|comment-line)? `]` +// it skips (ws|newline|comment-line) and returns the token. +template +struct multiline_spacer +{ + using comment_type = typename TC::comment_type; + bool newline_found; + indent_char indent_type; + std::int32_t indent; + comment_type comments; +}; +template +std::ostream& operator<<(std::ostream& os, const multiline_spacer& sp) +{ + os << "{newline=" << sp.newline_found << ", "; + os << "indent_type=" << sp.indent_type << ", "; + os << "indent=" << sp.indent << ", "; + os << "comments=" << sp.comments.size() << "}"; + return os; +} + +template +cxx::optional> +skip_multiline_spacer(location& loc, context& ctx, const bool newline_found = false) +{ + const auto& spec = ctx.toml_spec(); + + multiline_spacer spacer; + spacer.newline_found = newline_found; + spacer.indent_type = indent_char::none; + spacer.indent = 0; + spacer.comments.clear(); + + bool spacer_found = false; + while( ! loc.eof()) + { + if(auto comm = sequence(syntax::comment(spec), syntax::newline(spec)).scan(loc)) + { + spacer.newline_found = true; + auto comment = comm.as_string(); + if( ! comment.empty() && comment.back() == '\n') + { + comment.pop_back(); + if (!comment.empty() && comment.back() == '\r') + { + comment.pop_back(); + } + } + + spacer.comments.push_back(std::move(comment)); + spacer.indent_type = indent_char::none; + spacer.indent = 0; + spacer_found = true; + } + else if(auto nl = syntax::newline(spec).scan(loc)) + { + spacer.newline_found = true; + spacer.comments.clear(); + spacer.indent_type = indent_char::none; + spacer.indent = 0; + spacer_found = true; + } + else if(auto sp = repeat_at_least(1, character(cxx::bit_cast(' '))).scan(loc)) + { + spacer.indent_type = indent_char::space; + spacer.indent = static_cast(sp.length()); + spacer_found = true; + } + else if(auto tabs = repeat_at_least(1, character(cxx::bit_cast('\t'))).scan(loc)) + { + spacer.indent_type = indent_char::tab; + spacer.indent = static_cast(tabs.length()); + spacer_found = true; + } + else + { + break; // done + } + } + if( ! spacer_found) + { + return cxx::make_nullopt(); + } + return spacer; +} + +// not an [[array.of.tables]]. It parses ["this", "type"] +template +result, error_info> +parse_array(location& loc, context& ctx) +{ + const auto num_errors = ctx.errors().size(); + + const auto first = loc; + + if(loc.eof() || loc.current() != '[') + { + auto src = source_location(region(loc)); + return err(make_error_info("toml::parse_array: " + "The next token is not an array", std::move(src), "here")); + } + loc.advance(); + + typename basic_value::array_type val; + + array_format_info fmt; + fmt.fmt = array_format::oneline; + fmt.indent_type = indent_char::none; + + auto spacer = skip_multiline_spacer(loc, ctx); + if(spacer.has_value() && spacer.value().newline_found) + { + fmt.fmt = array_format::multiline; + } + + bool comma_found = true; + while( ! loc.eof()) + { + if(loc.current() == location::char_type(']')) + { + if(spacer.has_value() && spacer.value().newline_found && + spacer.value().indent_type != indent_char::none) + { + fmt.indent_type = spacer.value().indent_type; + fmt.closing_indent = spacer.value().indent; + } + break; + } + + if( ! comma_found) + { + auto src = source_location(region(loc)); + return err(make_error_info("toml::parse_array: " + "expected value-separator `,` or closing `]`", + std::move(src), "here")); + } + + if(spacer.has_value() && spacer.value().newline_found && + spacer.value().indent_type != indent_char::none) + { + fmt.indent_type = spacer.value().indent_type; + fmt.body_indent = spacer.value().indent; + } + + if(auto elem_res = parse_value(loc, ctx)) + { + auto elem = std::move(elem_res.unwrap()); + + if(spacer.has_value()) // copy previous comments to value + { + elem.comments() = std::move(spacer.value().comments); + } + + // parse spaces between a value and a comma + // array = [ + // 42 , # the answer + // ^^^^ + // 3.14 # pi + // , 2.71 ^^^^ + // ^^ + spacer = skip_multiline_spacer(loc, ctx); + if(spacer.has_value()) + { + for(std::size_t i=0; i( + std::move(val), std::move(fmt), {}, region(first, loc) + )); +} + +/* ============================================================================ + * _ _ _ _ _ _ + * (_)_ _ | (_)_ _ ___ | |_ __ _| |__| |___ + * | | ' \| | | ' \/ -_) | _/ _` | '_ \ / -_) + * |_|_||_|_|_|_||_\___| \__\__,_|_.__/_\___| + */ + +// ---------------------------------------------------------------------------- +// insert_value is the most complicated part of the toml spec. +// +// To parse a toml file correctly, we sometimes need to check an exising value +// is appendable or not. +// +// For example while parsing an inline array of tables, +// +// ```toml +// aot = [ +// {a = "foo"}, +// {a = "bar", b = "baz"}, +// ] +// ``` +// +// this `aot` is appendable until parser reaches to `]`. After that, it becomes +// non-appendable. +// +// On the other hand, a normal array of tables, such as +// +// ```toml +// [[aot]] +// a = "foo" +// +// [[aot]] +// a = "bar" +// b = "baz" +// ``` +// This `[[aot]]` is appendable until the parser reaches to the EOF. +// +// +// It becomes a bit more difficult in case of dotted keys. +// In TOML, it is allowed to append a key-value pair to a table that is +// *implicitly* defined by a subtable definitino. +// +// ```toml +// [x.y.z] +// w = 123 +// +// [x] +// a = "foo" # OK. x is defined implicitly by `[x.y.z]`. +// ``` +// +// But if the table is defined by a dotted keys, it is not appendable. +// +// ```toml +// [x] +// y.z.w = 123 +// +// [x.y] +// # ERROR. x.y is already defined by a dotted table in the previous table. +// ``` +// +// Also, reopening a table using dotted keys is invalid. +// +// ```toml +// [x.y.z] +// w = 123 +// +// [x] +// y.z.v = 42 # ERROR. [x.y.z] is already defined. +// ``` +// +// +// ```toml +// [a] +// b.c = "foo" +// b.d = "bar" +// ``` +// +// +// ```toml +// a.b = "foo" +// [a] +// c = "bar" # ERROR +// ``` +// +// In summary, +// - a table must be defined only once. +// - assignment to an exising table is possible only when: +// - defining a subtable [x.y] to an existing table [x]. +// - defining supertable [x] explicitly after [x.y]. +// - adding dotted keys in the same table. + +enum class inserting_value_kind : std::uint8_t +{ + std_table, // insert [standard.table] + array_table, // insert [[array.of.tables]] + dotted_keys // insert a.b.c = "this" +}; + +template +result*, error_info> +insert_value(const inserting_value_kind kind, + typename basic_value::table_type* current_table_ptr, + const std::vector::key_type>& keys, region key_reg, + basic_value val) +{ + using value_type = basic_value; + using array_type = typename basic_value::array_type; + using table_type = typename basic_value::table_type; + + auto key_loc = source_location(key_reg); + + assert( ! keys.empty()); + + // dotted key can insert to dotted key tables defined at the same level. + // dotted key can NOT reopen a table even if it is implcitly-defined one. + // + // [x.y.z] # define x and x.y implicitly. + // a = 42 + // + // [x] # reopening implcitly defined table + // r.s.t = 3.14 # VALID r and r.s are new tables. + // r.s.u = 2.71 # VALID r and r.s are dotted-key tables. valid. + // + // y.z.b = "foo" # INVALID x.y.z are multiline table, not a dotted key. + // y.c = "bar" # INVALID x.y is implicit multiline table, not a dotted key. + + // a table cannot reopen dotted-key tables. + // + // [t1] + // t2.t3.v = 0 + // [t1.t2] # INVALID t1.t2 is defined as a dotted-key table. + + for(std::size_t i=0; i{}, key_reg)); + + assert(current_table.at(key).is_table()); + current_table_ptr = std::addressof(current_table.at(key).as_table()); + } + else if (found->second.is_table()) + { + const auto fmt = found->second.as_table_fmt().fmt; + if(fmt == table_format::oneline || fmt == table_format::multiline_oneline) + { + // foo = {bar = "baz"} or foo = { \n bar = "baz" \n } + return err(make_error_info("toml::insert_value: " + "failed to insert a value: inline table is immutable", + key_loc, "inserting this", + found->second.location(), "to this table")); + } + // dotted key cannot reopen a table. + if(kind ==inserting_value_kind::dotted_keys && fmt != table_format::dotted) + { + return err(make_error_info("toml::insert_value: " + "reopening a table using dotted keys", + key_loc, "dotted key cannot reopen a table", + found->second.location(), "this table is already closed")); + } + assert(found->second.is_table()); + current_table_ptr = std::addressof(found->second.as_table()); + } + else if(found->second.is_array_of_tables()) + { + // aot = [{this = "type", of = "aot"}] # cannot be reopened + if(found->second.as_array_fmt().fmt != array_format::array_of_tables) + { + return err(make_error_info("toml::insert_value:" + "inline array of tables are immutable", + key_loc, "inserting this", + found->second.location(), "inline array of tables")); + } + // appending to [[aot]] + + if(kind == inserting_value_kind::dotted_keys) + { + // [[array.of.tables]] + // [array.of] # reopening supertable is okay + // tables.x = "foo" # appending `x` to the first table + return err(make_error_info("toml::insert_value:" + "dotted key cannot reopen an array-of-tables", + key_loc, "inserting this", + found->second.location(), "to this array-of-tables.")); + } + + // insert_value_by_dotkeys::std_table + // [[array.of.tables]] + // [array.of.tables.subtable] # appending to the last aot + // + // insert_value_by_dotkeys::array_table + // [[array.of.tables]] + // [[array.of.tables.subtable]] # appending to the last aot + auto& current_array_table = found->second.as_array().back(); + + assert(current_array_table.is_table()); + current_table_ptr = std::addressof(current_array_table.as_table()); + } + else + { + return err(make_error_info("toml::insert_value: " + "failed to insert a value, value already exists", + key_loc, "while inserting this", + found->second.location(), "non-table value already exists")); + } + } + else // this is the last key. insert a new value. + { + switch(kind) + { + case inserting_value_kind::dotted_keys: + { + if(current_table.find(key) != current_table.end()) + { + return err(make_error_info("toml::insert_value: " + "failed to insert a value, value already exists", + key_loc, "inserting this", + current_table.at(key).location(), "but value already exists")); + } + current_table.emplace(key, std::move(val)); + return ok(std::addressof(current_table.at(key))); + } + case inserting_value_kind::std_table: + { + // defining a new table or reopening supertable + auto found = current_table.find(key); + if(found == current_table.end()) // define a new aot + { + current_table.emplace(key, std::move(val)); + return ok(std::addressof(current_table.at(key))); + } + else // the table is already defined, reopen it + { + // assigning a [std.table]. it must be an implicit table. + auto& target = found->second; + if( ! target.is_table() || // could be an array-of-tables + target.as_table_fmt().fmt != table_format::implicit) + { + return err(make_error_info("toml::insert_value: " + "failed to insert a table, table already defined", + key_loc, "inserting this", + target.location(), "this table is explicitly defined")); + } + + // merge table + for(const auto& kv : val.as_table()) + { + if(target.contains(kv.first)) + { + // [x.y.z] + // w = "foo" + // [x] + // y = "bar" + return err(make_error_info("toml::insert_value: " + "failed to insert a table, table keys conflict to each other", + key_loc, "inserting this table", + kv.second.location(), "having this value", + target.at(kv.first).location(), "already defined here")); + } + else + { + target[kv.first] = kv.second; + } + } + // change implicit -> explicit + target.as_table_fmt().fmt = table_format::multiline; + // change definition region + change_region_of_value(target, val); + + return ok(std::addressof(current_table.at(key))); + } + } + case inserting_value_kind::array_table: + { + auto found = current_table.find(key); + if(found == current_table.end()) // define a new aot + { + array_format_info fmt; + fmt.fmt = array_format::array_of_tables; + fmt.indent_type = indent_char::none; + + current_table.emplace(key, value_type( + array_type{ std::move(val) }, std::move(fmt), + std::vector{}, std::move(key_reg) + )); + + assert( ! current_table.at(key).as_array().empty()); + return ok(std::addressof(current_table.at(key).as_array().back())); + } + else // the array is already defined, append to it + { + if( ! found->second.is_array_of_tables()) + { + return err(make_error_info("toml::insert_value: " + "failed to insert an array of tables, value already exists", + key_loc, "while inserting this", + found->second.location(), "non-table value already exists")); + } + if(found->second.as_array_fmt().fmt != array_format::array_of_tables) + { + return err(make_error_info("toml::insert_value: " + "failed to insert a table, inline array of tables is immutable", + key_loc, "while inserting this", + found->second.location(), "this is inline array-of-tables")); + } + found->second.as_array().push_back(std::move(val)); + assert( ! current_table.at(key).as_array().empty()); + return ok(std::addressof(current_table.at(key).as_array().back())); + } + } + default: {assert(false);} + } + } + } + return err(make_error_info("toml::insert_key: no keys found", + std::move(key_loc), "here")); +} + +// ---------------------------------------------------------------------------- + +template +result, error_info> +parse_inline_table(location& loc, context& ctx) +{ + using table_type = typename basic_value::table_type; + + const auto num_errors = ctx.errors().size(); + + const auto first = loc; + const auto& spec = ctx.toml_spec(); + + if(loc.eof() || loc.current() != '{') + { + auto src = source_location(region(loc)); + return err(make_error_info("toml::parse_inline_table: " + "The next token is not an inline table", std::move(src), "here")); + } + loc.advance(); + + table_type table; + table_format_info fmt; + fmt.fmt = table_format::oneline; + fmt.indent_type = indent_char::none; + + cxx::optional> spacer(cxx::make_nullopt()); + + if(spec.v1_1_0_allow_newlines_in_inline_tables) + { + spacer = skip_multiline_spacer(loc, ctx); + if(spacer.has_value() && spacer.value().newline_found) + { + fmt.fmt = table_format::multiline_oneline; + } + } + else + { + skip_whitespace(loc, ctx); + } + + bool still_empty = true; + bool comma_found = false; + while( ! loc.eof()) + { + // closing! + if(loc.current() == '}') + { + if(comma_found && ! spec.v1_1_0_allow_trailing_comma_in_inline_tables) + { + auto src = source_location(region(loc)); + return err(make_error_info("toml::parse_inline_table: trailing " + "comma is not allowed in TOML-v1.0.0)", std::move(src), "here")); + } + + if(spec.v1_1_0_allow_newlines_in_inline_tables) + { + if(spacer.has_value() && spacer.value().newline_found && + spacer.value().indent_type != indent_char::none) + { + fmt.indent_type = spacer.value().indent_type; + fmt.closing_indent = spacer.value().indent; + } + } + break; + } + + // if we already found a value and didn't found `,` nor `}`, error. + if( ! comma_found && ! still_empty) + { + auto src = source_location(region(loc)); + return err(make_error_info("toml::parse_inline_table: " + "expected value-separator `,` or closing `}`", + std::move(src), "here")); + } + + // parse indent. + if(spacer.has_value() && spacer.value().newline_found && + spacer.value().indent_type != indent_char::none) + { + fmt.indent_type = spacer.value().indent_type; + fmt.body_indent = spacer.value().indent; + } + + still_empty = false; // parsing a value... + if(auto kv_res = parse_key_value_pair(loc, ctx)) + { + auto keys = std::move(kv_res.unwrap().first.first); + auto key_reg = std::move(kv_res.unwrap().first.second); + auto val = std::move(kv_res.unwrap().second); + + auto ins_res = insert_value(inserting_value_kind::dotted_keys, + std::addressof(table), keys, std::move(key_reg), std::move(val)); + if(ins_res.is_err()) + { + ctx.report_error(std::move(ins_res.unwrap_err())); + // we need to skip until the next value (or end of the table) + // because we don't have valid kv pair. + while( ! loc.eof()) + { + const auto c = loc.current(); + if(c == ',' || c == '\n' || c == '}') + { + comma_found = (c == ','); + break; + } + loc.advance(); + } + continue; + } + + // if comment line follows immediately(without newline) after `,`, then + // the comment is for the elem. we need to check if comment follows `,`. + // + // (key) = (val) (ws|newline|comment-line)? `,` (ws)? (comment)? + + if(spec.v1_1_0_allow_newlines_in_inline_tables) + { + if(spacer.has_value()) // copy previous comments to value + { + for(std::size_t i=0; icomments().push_back(spacer.value().comments.at(i)); + } + } + spacer = skip_multiline_spacer(loc, ctx); + if(spacer.has_value()) + { + for(std::size_t i=0; icomments().push_back(spacer.value().comments.at(i)); + } + if(spacer.value().newline_found) + { + fmt.fmt = table_format::multiline_oneline; + if(spacer.value().indent_type != indent_char::none) + { + fmt.indent_type = spacer.value().indent_type; + fmt.body_indent = spacer.value().indent; + } + } + } + } + else + { + skip_whitespace(loc, ctx); + } + + comma_found = character(',').scan(loc).is_ok(); + + if(spec.v1_1_0_allow_newlines_in_inline_tables) + { + auto com_res = parse_comment_line(loc, ctx); + if(com_res.is_err()) + { + ctx.report_error(com_res.unwrap_err()); + } + const bool comment_found = com_res.is_ok() && com_res.unwrap().has_value(); + if(comment_found) + { + fmt.fmt = table_format::multiline_oneline; + ins_res.unwrap()->comments().push_back(com_res.unwrap().value()); + } + if(comma_found) + { + spacer = skip_multiline_spacer(loc, ctx, comment_found); + if(spacer.has_value() && spacer.value().newline_found) + { + fmt.fmt = table_format::multiline_oneline; + } + } + } + else + { + skip_whitespace(loc, ctx); + } + } + else + { + ctx.report_error(std::move(kv_res.unwrap_err())); + while( ! loc.eof()) + { + if(loc.current() == '}') + { + break; + } + if( ! spec.v1_1_0_allow_newlines_in_inline_tables && loc.current() == '\n') + { + break; + } + loc.advance(); + } + break; + } + } + + if(loc.current() != '}') + { + auto src = source_location(region(loc)); + return err(make_error_info("toml::parse_inline_table: " + "missing closing bracket `}`", + std::move(src), "expected `}`, reached line end")); + } + else + { + loc.advance(); // skip } + } + + // any error reported from this function + if(num_errors < ctx.errors().size()) + { + assert(ctx.has_error()); // already reported + return err(ctx.pop_last_error()); + } + + basic_value retval( + std::move(table), std::move(fmt), {}, region(first, loc)); + + return ok(std::move(retval)); +} + +/* ============================================================================ + * _ + * __ ____ _| |_ _ ___ + * \ V / _` | | || / -_) + * \_/\__,_|_|\_,_\___| + */ + +template +result +guess_number_type(const location& first, const context& ctx) +{ + const auto& spec = ctx.toml_spec(); + location loc = first; + + if(syntax::offset_datetime(spec).scan(loc).is_ok()) + { + return ok(value_t::offset_datetime); + } + loc = first; + + if(syntax::local_datetime(spec).scan(loc).is_ok()) + { + const auto curr = loc.current(); + // if offset_datetime contains bad offset, it syntax::offset_datetime + // fails to scan it. + if(curr == '+' || curr == '-') + { + return err(make_syntax_error("bad offset: must be [+-]HH:MM or Z", + syntax::time_offset(spec), loc, std::string( + "Hint: valid : +09:00, -05:30\n" + "Hint: invalid: +9:00, -5:30\n"))); + } + return ok(value_t::local_datetime); + } + loc = first; + + if(syntax::local_date(spec).scan(loc).is_ok()) + { + // bad time may appear after this. + + if( ! loc.eof()) + { + const auto c = loc.current(); + if(c == 'T' || c == 't') + { + loc.advance(); + + return err(make_syntax_error("bad time: must be HH:MM:SS.subsec", + syntax::local_time(spec), loc, std::string( + "Hint: valid : 1979-05-27T07:32:00, 1979-05-27 07:32:00.999999\n" + "Hint: invalid: 1979-05-27T7:32:00, 1979-05-27 17:32\n"))); + } + if(c == ' ') + { + // A space is allowed as a delimiter between local time. + // But there is a case where bad time follows a space. + // - invalid: 2019-06-16 7:00:00 + // - valid : 2019-06-16 07:00:00 + loc.advance(); + if( ! loc.eof() && ('0' <= loc.current() && loc.current() <= '9')) + { + return err(make_syntax_error("bad time: must be HH:MM:SS.subsec", + syntax::local_time(spec), loc, std::string( + "Hint: valid : 1979-05-27T07:32:00, 1979-05-27 07:32:00.999999\n" + "Hint: invalid: 1979-05-27T7:32:00, 1979-05-27 17:32\n"))); + } + } + if('0' <= c && c <= '9') + { + return err(make_syntax_error("bad datetime: missing T or space", + character_either{'T', 't', ' '}, loc, std::string( + "Hint: valid : 1979-05-27T07:32:00, 1979-05-27 07:32:00.999999\n" + "Hint: invalid: 1979-05-27T7:32:00, 1979-05-27 17:32\n"))); + } + } + return ok(value_t::local_date); + } + loc = first; + + if(syntax::local_time(spec).scan(loc).is_ok()) + { + return ok(value_t::local_time); + } + loc = first; + + if(syntax::floating(spec).scan(loc).is_ok()) + { + if( ! loc.eof() && loc.current() == '_') + { + if(spec.ext_num_suffix && syntax::num_suffix(spec).scan(loc).is_ok()) + { + return ok(value_t::floating); + } + auto src = source_location(region(loc)); + return err(make_error_info( + "bad float: `_` must be surrounded by digits", + std::move(src), "invalid underscore", + "Hint: valid : +1.0, -2e-2, 3.141_592_653_589, inf, nan\n" + "Hint: invalid: .0, 1., _1.0, 1.0_, 1_.0, 1.0__0\n")); + } + return ok(value_t::floating); + } + loc = first; + + if(spec.ext_hex_float) + { + if(syntax::hex_floating(spec).scan(loc).is_ok()) + { + if( ! loc.eof() && loc.current() == '_') + { + if(spec.ext_num_suffix && syntax::num_suffix(spec).scan(loc).is_ok()) + { + return ok(value_t::floating); + } + auto src = source_location(region(loc)); + return err(make_error_info( + "bad float: `_` must be surrounded by digits", + std::move(src), "invalid underscore", + "Hint: valid : +1.0, -2e-2, 3.141_592_653_589, inf, nan\n" + "Hint: invalid: .0, 1., _1.0, 1.0_, 1_.0, 1.0__0\n")); + } + return ok(value_t::floating); + } + loc = first; + } + + if(auto int_reg = syntax::integer(spec).scan(loc)) + { + if( ! loc.eof()) + { + const auto c = loc.current(); + if(c == '_') + { + if(spec.ext_num_suffix && syntax::num_suffix(spec).scan(loc).is_ok()) + { + return ok(value_t::integer); + } + + if(int_reg.length() <= 2 && (int_reg.as_string() == "0" || + int_reg.as_string() == "-0" || int_reg.as_string() == "+0")) + { + auto src = source_location(region(loc)); + return err(make_error_info( + "bad integer: leading zero is not allowed in decimal int", + std::move(src), "leading zero", + "Hint: valid : -42, 1_000, 1_2_3_4_5, 0xC0FFEE, 0b0010, 0o755\n" + "Hint: invalid: _42, 1__000, 0123\n")); + } + else + { + auto src = source_location(region(loc)); + return err(make_error_info( + "bad integer: `_` must be surrounded by digits", + std::move(src), "invalid underscore", + "Hint: valid : -42, 1_000, 1_2_3_4_5, 0xC0FFEE, 0b0010, 0o755\n" + "Hint: invalid: _42, 1__000, 0123\n")); + } + } + if('0' <= c && c <= '9') + { + if(loc.current() == '0') + { + loc.retrace(); + return err(make_error_info( + "bad integer: leading zero", + source_location(region(loc)), "leading zero is not allowed", + std::string("Hint: valid : -42, 1_000, 1_2_3_4_5, 0xC0FFEE, 0b0010, 0o755\n" + "Hint: invalid: _42, 1__000, 0123\n") + )); + } + else // invalid digits, especially in oct/bin ints. + { + return err(make_error_info( + "bad integer: invalid digit after an integer", + source_location(region(loc)), "this digit is not allowed", + std::string("Hint: valid : -42, 1_000, 1_2_3_4_5, 0xC0FFEE, 0b0010, 0o755\n" + "Hint: invalid: _42, 1__000, 0123\n") + )); + } + } + if(c == ':' || c == '-') + { + auto src = source_location(region(loc)); + return err(make_error_info("bad datetime: invalid format", + std::move(src), "here", + std::string("Hint: valid : 1979-05-27T07:32:00-07:00, 1979-05-27 07:32:00.999999Z\n" + "Hint: invalid: 1979-05-27T7:32:00-7:00, 1979-05-27 7:32-00:30") + )); + } + if(c == '.' || c == 'e' || c == 'E') + { + auto src = source_location(region(loc)); + return err(make_error_info("bad float: invalid format", + std::move(src), "here", std::string( + "Hint: valid : +1.0, -2e-2, 3.141_592_653_589, inf, nan\n" + "Hint: invalid: .0, 1., _1.0, 1.0_, 1_.0, 1.0__0\n"))); + } + } + return ok(value_t::integer); + } + if( ! loc.eof() && loc.current() == '.') + { + auto src = source_location(region(loc)); + return err(make_error_info("bad float: integer part is required before decimal point", + std::move(src), "missing integer part", std::string( + "Hint: valid : +1.0, -2e-2, 3.141_592_653_589, inf, nan\n" + "Hint: invalid: .0, 1., _1.0, 1.0_, 1_.0, 1.0__0\n") + )); + } + if( ! loc.eof() && loc.current() == '_') + { + auto src = source_location(region(loc)); + return err(make_error_info("bad number: `_` must be surrounded by digits", + std::move(src), "digits required before `_`", std::string( + "Hint: valid : -42, 1_000, 1_2_3_4_5, 0xC0FFEE, 0b0010, 0o755\n" + "Hint: invalid: _42, 1__000, 0123\n") + )); + } + + auto src = source_location(region(loc)); + return err(make_error_info("bad format: unknown value appeared", + std::move(src), "here")); +} + +template +result +guess_value_type(const location& loc, const context& ctx) +{ + const auto& sp = ctx.toml_spec(); + location inner(loc); + + switch(loc.current()) + { + case '"' : {return ok(value_t::string); } + case '\'': {return ok(value_t::string); } + case '[' : {return ok(value_t::array); } + case '{' : {return ok(value_t::table); } + case 't' : + { + return ok(value_t::boolean); + } + case 'f' : + { + return ok(value_t::boolean); + } + case 'T' : // invalid boolean. + { + return err(make_syntax_error("toml::parse_value: " + "`true` must be in lowercase. " + "A string must be surrounded by quotes.", + syntax::boolean(sp), inner)); + } + case 'F' : + { + return err(make_syntax_error("toml::parse_value: " + "`false` must be in lowercase. " + "A string must be surrounded by quotes.", + syntax::boolean(sp), inner)); + } + case 'i' : // inf or string without quotes(syntax error). + { + if(literal("inf").scan(inner).is_ok()) + { + return ok(value_t::floating); + } + else + { + return err(make_syntax_error("toml::parse_value: " + "`inf` must be in lowercase. " + "A string must be surrounded by quotes.", + syntax::floating(sp), inner)); + } + } + case 'I' : // Inf or string without quotes(syntax error). + { + return err(make_syntax_error("toml::parse_value: " + "`inf` must be in lowercase. " + "A string must be surrounded by quotes.", + syntax::floating(sp), inner)); + } + case 'n' : // nan or null-extension + { + if(sp.ext_null_value) + { + if(literal("nan").scan(inner).is_ok()) + { + return ok(value_t::floating); + } + else if(literal("null").scan(inner).is_ok()) + { + return ok(value_t::empty); + } + else + { + return err(make_syntax_error("toml::parse_value: " + "Both `nan` and `null` must be in lowercase. " + "A string must be surrounded by quotes.", + syntax::floating(sp), inner)); + } + } + else // must be nan. + { + if(literal("nan").scan(inner).is_ok()) + { + return ok(value_t::floating); + } + else + { + return err(make_syntax_error("toml::parse_value: " + "`nan` must be in lowercase. " + "A string must be surrounded by quotes.", + syntax::floating(sp), inner)); + } + } + } + case 'N' : // nan or null-extension + { + if(sp.ext_null_value) + { + return err(make_syntax_error("toml::parse_value: " + "Both `nan` and `null` must be in lowercase. " + "A string must be surrounded by quotes.", + syntax::floating(sp), inner)); + } + else + { + return err(make_syntax_error("toml::parse_value: " + "`nan` must be in lowercase. " + "A string must be surrounded by quotes.", + syntax::floating(sp), inner)); + } + } + default : + { + return guess_number_type(loc, ctx); + } + } +} + +template +result, error_info> +parse_value(location& loc, context& ctx) +{ + const auto ty_res = guess_value_type(loc, ctx); + if(ty_res.is_err()) + { + return err(ty_res.unwrap_err()); + } + + switch(ty_res.unwrap()) + { + case value_t::empty: + { + if(ctx.toml_spec().ext_null_value) + { + return parse_null(loc, ctx); + } + else + { + auto src = source_location(region(loc)); + return err(make_error_info("toml::parse_value: unknown value appeared", + std::move(src), "here")); + } + } + case value_t::boolean : {return parse_boolean (loc, ctx);} + case value_t::integer : {return parse_integer (loc, ctx);} + case value_t::floating : {return parse_floating (loc, ctx);} + case value_t::string : {return parse_string (loc, ctx);} + case value_t::offset_datetime: {return parse_offset_datetime(loc, ctx);} + case value_t::local_datetime : {return parse_local_datetime (loc, ctx);} + case value_t::local_date : {return parse_local_date (loc, ctx);} + case value_t::local_time : {return parse_local_time (loc, ctx);} + case value_t::array : {return parse_array (loc, ctx);} + case value_t::table : {return parse_inline_table (loc, ctx);} + default: + { + auto src = source_location(region(loc)); + return err(make_error_info("toml::parse_value: unknown value appeared", + std::move(src), "here")); + } + } +} + +/* ============================================================================ + * _____ _ _ + * |_ _|_ _| |__| |___ + * | |/ _` | '_ \ / -_) + * |_|\__,_|_.__/_\___| + */ + +template +result::key_type>, region>, error_info> +parse_table_key(location& loc, context& ctx) +{ + const auto first = loc; + const auto& spec = ctx.toml_spec(); + + auto reg = syntax::std_table(spec).scan(loc); + if(!reg.is_ok()) + { + return err(make_syntax_error("toml::parse_table_key: invalid table key", + syntax::std_table(spec), loc)); + } + + loc = first; + loc.advance(); // skip [ + skip_whitespace(loc, ctx); + + auto keys_res = parse_key(loc, ctx); + if(keys_res.is_err()) + { + return err(std::move(keys_res.unwrap_err())); + } + + skip_whitespace(loc, ctx); + loc.advance(); // ] + + return ok(std::make_pair(std::move(keys_res.unwrap().first), std::move(reg))); +} + +template +result::key_type>, region>, error_info> +parse_array_table_key(location& loc, context& ctx) +{ + const auto first = loc; + const auto& spec = ctx.toml_spec(); + + auto reg = syntax::array_table(spec).scan(loc); + if(!reg.is_ok()) + { + return err(make_syntax_error("toml::parse_array_table_key: invalid array-of-tables key", + syntax::array_table(spec), loc)); + } + + loc = first; + loc.advance(); // [ + loc.advance(); // [ + skip_whitespace(loc, ctx); + + auto keys_res = parse_key(loc, ctx); + if(keys_res.is_err()) + { + return err(std::move(keys_res.unwrap_err())); + } + + skip_whitespace(loc, ctx); + loc.advance(); // ] + loc.advance(); // ] + + return ok(std::make_pair(std::move(keys_res.unwrap().first), std::move(reg))); +} + +// called after reading [table.keys] and comments around it. +// Since table may already contain a subtable ([x.y.z] can be defined before [x]), +// the table that is being parsed is passed as an argument. +template +result +parse_table(location& loc, context& ctx, basic_value& table) +{ + assert(table.is_table()); + + const auto num_errors = ctx.errors().size(); + const auto& spec = ctx.toml_spec(); + + // clear indent info + table.as_table_fmt().indent_type = indent_char::none; + + bool newline_found = true; + while( ! loc.eof()) + { + const auto start = loc; + + auto sp = skip_multiline_spacer(loc, ctx, newline_found); + + // if reached to EOF, the table ends here. return. + if(loc.eof()) + { + break; + } + // if next table is comming, return. + if(sequence(syntax::ws(spec), character('[')).scan(loc).is_ok()) + { + loc = start; + break; + } + // otherwise, it should be a key-value pair. + newline_found = newline_found || (sp.has_value() && sp.value().newline_found); + if( ! newline_found) + { + return err(make_error_info("toml::parse_table: " + "newline (LF / CRLF) or EOF is expected", + source_location(region(loc)), "here")); + } + if(sp.has_value() && sp.value().indent_type != indent_char::none) + { + table.as_table_fmt().indent_type = sp.value().indent_type; + table.as_table_fmt().body_indent = sp.value().indent; + } + + newline_found = false; // reset + if(auto kv_res = parse_key_value_pair(loc, ctx)) + { + auto keys = std::move(kv_res.unwrap().first.first); + auto key_reg = std::move(kv_res.unwrap().first.second); + auto val = std::move(kv_res.unwrap().second); + + if(sp.has_value()) + { + for(const auto& com : sp.value().comments) + { + val.comments().push_back(com); + } + } + + if(auto com_res = parse_comment_line(loc, ctx)) + { + if(auto com_opt = com_res.unwrap()) + { + val.comments().push_back(com_opt.value()); + newline_found = true; // comment includes newline at the end + } + } + else + { + ctx.report_error(std::move(com_res.unwrap_err())); + } + + auto ins_res = insert_value(inserting_value_kind::dotted_keys, + std::addressof(table.as_table()), + keys, std::move(key_reg), std::move(val)); + if(ins_res.is_err()) + { + ctx.report_error(std::move(ins_res.unwrap_err())); + } + } + else + { + ctx.report_error(std::move(kv_res.unwrap_err())); + skip_key_value_pair(loc, ctx); + } + } + + if(num_errors < ctx.errors().size()) + { + assert(ctx.has_error()); // already reported + return err(ctx.pop_last_error()); + } + return ok(); +} + +template +result, std::vector> +parse_file(location& loc, context& ctx) +{ + using value_type = basic_value; + using table_type = typename value_type::table_type; + + const auto first = loc; + const auto& spec = ctx.toml_spec(); + + if(loc.eof()) + { + return ok(value_type(table_type(), table_format_info{}, {}, region(loc))); + } + + value_type root(table_type(), table_format_info{}, {}, region(loc)); + root.as_table_fmt().fmt = table_format::multiline; + root.as_table_fmt().indent_type = indent_char::none; + + // parse top comment. + // + // ```toml + // # this is a comment for the top-level table. + // + // key = "the first value" + // ``` + // + // ```toml + // # this is a comment for "the first value". + // key = "the first value" + // ``` + while( ! loc.eof()) + { + if(auto com_res = parse_comment_line(loc, ctx)) + { + if(auto com_opt = com_res.unwrap()) + { + root.comments().push_back(std::move(com_opt.value())); + } + else // no comment found. + { + // if it is not an empty line, clear the root comment. + if( ! sequence(syntax::ws(spec), syntax::newline(spec)).scan(loc).is_ok()) + { + loc = first; + root.comments().clear(); + } + break; + } + } + else + { + ctx.report_error(std::move(com_res.unwrap_err())); + skip_comment_block(loc, ctx); + } + } + + // parse root table + { + const auto res = parse_table(loc, ctx, root); + if(res.is_err()) + { + ctx.report_error(std::move(res.unwrap_err())); + skip_until_next_table(loc, ctx); + } + } + + // parse tables + + while( ! loc.eof()) + { + auto sp = skip_multiline_spacer(loc, ctx, /*newline_found=*/true); + + if(auto key_res = parse_array_table_key(loc, ctx)) + { + auto key = std::move(std::get<0>(key_res.unwrap())); + auto reg = std::move(std::get<1>(key_res.unwrap())); + + std::vector com; + if(sp.has_value()) + { + for(std::size_t i=0; i(table_type()); + auto res = parse_table(loc, ctx, tmp); + if(res.is_err()) + { + ctx.report_error(res.unwrap_err()); + skip_until_next_table(loc, ctx); + } + continue; + } + + auto tab_ptr = inserted.unwrap(); + assert(tab_ptr); + + const auto tab_res = parse_table(loc, ctx, *tab_ptr); + if(tab_res.is_err()) + { + ctx.report_error(tab_res.unwrap_err()); + skip_until_next_table(loc, ctx); + } + + // parse_table first clears `indent_type`. + // to keep header indent info, we must store it later. + if(sp.has_value() && sp.value().indent_type != indent_char::none) + { + tab_ptr->as_table_fmt().indent_type = sp.value().indent_type; + tab_ptr->as_table_fmt().name_indent = sp.value().indent; + } + continue; + } + if(auto key_res = parse_table_key(loc, ctx)) + { + auto key = std::move(std::get<0>(key_res.unwrap())); + auto reg = std::move(std::get<1>(key_res.unwrap())); + + std::vector com; + if(sp.has_value()) + { + for(std::size_t i=0; i(table_type()); + auto res = parse_table(loc, ctx, tmp); + if(res.is_err()) + { + ctx.report_error(res.unwrap_err()); + skip_until_next_table(loc, ctx); + } + continue; + } + + auto tab_ptr = inserted.unwrap(); + assert(tab_ptr); + + const auto tab_res = parse_table(loc, ctx, *tab_ptr); + if(tab_res.is_err()) + { + ctx.report_error(tab_res.unwrap_err()); + skip_until_next_table(loc, ctx); + } + if(sp.has_value() && sp.value().indent_type != indent_char::none) + { + tab_ptr->as_table_fmt().indent_type = sp.value().indent_type; + tab_ptr->as_table_fmt().name_indent = sp.value().indent; + } + continue; + } + + // does not match array_table nor std_table. report an error. + const auto keytop = loc; + const auto maybe_array_of_tables = literal("[[").scan(loc).is_ok(); + loc = keytop; + + if(maybe_array_of_tables) + { + ctx.report_error(make_syntax_error("toml::parse_file: invalid array-table key", + syntax::array_table(spec), loc)); + } + else + { + ctx.report_error(make_syntax_error("toml::parse_file: invalid table key", + syntax::std_table(spec), loc)); + } + skip_until_next_table(loc, ctx); + } + + if( ! ctx.errors().empty()) + { + return err(std::move(ctx.errors())); + } + return ok(std::move(root)); +} + +template +result, std::vector> +parse_impl(std::vector cs, std::string fname, const spec& s) +{ + using value_type = basic_value; + using table_type = typename value_type::table_type; + + // an empty file is a valid toml file. + if(cs.empty()) + { + auto src = std::make_shared>(std::move(cs)); + location loc(std::move(src), std::move(fname)); + return ok(value_type(table_type(), table_format_info{}, std::vector{}, region(loc))); + } + + // to simplify parser, add newline at the end if there is no LF. + // But, if it has raw CR, the file is invalid (in TOML, CR is not a valid + // newline char). if it ends with CR, do not add LF and report it. + if(cs.back() != '\n' && cs.back() != '\r') + { + cs.push_back('\n'); + } + + auto src = std::make_shared>(std::move(cs)); + + location loc(std::move(src), std::move(fname)); + + // skip BOM if found + if(loc.source()->size() >= 3) + { + auto first = loc.get_location(); + + const auto c0 = loc.current(); loc.advance(); + const auto c1 = loc.current(); loc.advance(); + const auto c2 = loc.current(); loc.advance(); + + const auto bom_found = (c0 == 0xEF) && (c1 == 0xBB) && (c2 == 0xBF); + if( ! bom_found) + { + loc.set_location(first); + } + } + + context ctx(s); + + return parse_file(loc, ctx); +} + +} // detail + +// ----------------------------------------------------------------------------- +// parse(byte array) + +template +result, std::vector> +try_parse(std::vector content, std::string filename, + spec s = spec::default_version()) +{ + return detail::parse_impl(std::move(content), std::move(filename), std::move(s)); +} +template +basic_value +parse(std::vector content, std::string filename, + spec s = spec::default_version()) +{ + auto res = try_parse(std::move(content), std::move(filename), std::move(s)); + if(res.is_ok()) + { + return res.unwrap(); + } + else + { + std::string msg; + for(const auto& err : res.unwrap_err()) + { + msg += format_error(err); + } + throw syntax_error(std::move(msg), std::move(res.unwrap_err())); + } +} + +// ----------------------------------------------------------------------------- +// parse(istream) + +template +result, std::vector> +try_parse(std::istream& is, std::string fname = "unknown file", spec s = spec::default_version()) +{ + const auto beg = is.tellg(); + is.seekg(0, std::ios::end); + const auto end = is.tellg(); + const auto fsize = end - beg; + is.seekg(beg); + + // read whole file as a sequence of char + assert(fsize >= 0); + std::vector letters(static_cast(fsize), '\0'); + is.read(reinterpret_cast(letters.data()), static_cast(fsize)); + + return detail::parse_impl(std::move(letters), std::move(fname), std::move(s)); +} + +template +basic_value parse(std::istream& is, std::string fname = "unknown file", spec s = spec::default_version()) +{ + auto res = try_parse(is, std::move(fname), std::move(s)); + if(res.is_ok()) + { + return res.unwrap(); + } + else + { + std::string msg; + for(const auto& err : res.unwrap_err()) + { + msg += format_error(err); + } + throw syntax_error(std::move(msg), std::move(res.unwrap_err())); + } +} + +// ----------------------------------------------------------------------------- +// parse(filename) + +template +result, std::vector> +try_parse(std::string fname, spec s = spec::default_version()) +{ + std::ifstream ifs(fname, std::ios_base::binary); + if(!ifs.good()) + { + std::vector e; + e.push_back(error_info("toml::parse: Error opening file \"" + fname + "\"", {})); + return err(std::move(e)); + } + ifs.exceptions(std::ifstream::failbit | std::ifstream::badbit); + + return try_parse(ifs, std::move(fname), std::move(s)); +} + +template +basic_value parse(std::string fname, spec s = spec::default_version()) +{ + std::ifstream ifs(fname, std::ios_base::binary); + if(!ifs.good()) + { + throw file_io_error("toml::parse: error opening file", fname); + } + ifs.exceptions(std::ifstream::failbit | std::ifstream::badbit); + + return parse(ifs, std::move(fname), std::move(s)); +} + +template +result, std::vector> +try_parse(const char (&fname)[N], spec s = spec::default_version()) +{ + return try_parse(std::string(fname), std::move(s)); +} + +template +basic_value parse(const char (&fname)[N], spec s = spec::default_version()) +{ + return parse(std::string(fname), std::move(s)); +} + +// ---------------------------------------------------------------------------- +// parse_str + +template +result, std::vector> +try_parse_str(std::string content, spec s = spec::default_version(), + cxx::source_location loc = cxx::source_location::current()) +{ + std::istringstream iss(std::move(content)); + std::string name("internal string" + cxx::to_string(loc)); + return try_parse(iss, std::move(name), std::move(s)); +} + +template +basic_value parse_str(std::string content, spec s = spec::default_version(), + cxx::source_location loc = cxx::source_location::current()) +{ + auto res = try_parse_str(std::move(content), std::move(s), std::move(loc)); + if(res.is_ok()) + { + return res.unwrap(); + } + else + { + std::string msg; + for(const auto& err : res.unwrap_err()) + { + msg += format_error(err); + } + throw syntax_error(std::move(msg), std::move(res.unwrap_err())); + } +} + +// ---------------------------------------------------------------------------- +// filesystem + +#if defined(TOML11_HAS_FILESYSTEM) + +template +cxx::enable_if_t::value, + result, std::vector>> +try_parse(const FSPATH& fpath, spec s = spec::default_version()) +{ + std::ifstream ifs(fpath, std::ios_base::binary); + if(!ifs.good()) + { + std::vector e; + e.push_back(error_info("toml::parse: Error opening file \"" + fpath.string() + "\"", {})); + return err(std::move(e)); + } + ifs.exceptions(std::ifstream::failbit | std::ifstream::badbit); + + return try_parse(ifs, fpath.string(), std::move(s)); +} + +template +cxx::enable_if_t::value, + basic_value> +parse(const FSPATH& fpath, spec s = spec::default_version()) +{ + std::ifstream ifs(fpath, std::ios_base::binary); + if(!ifs.good()) + { + throw file_io_error("toml::parse: error opening file", fpath.string()); + } + ifs.exceptions(std::ifstream::failbit | std::ifstream::badbit); + + return parse(ifs, fpath.string(), std::move(s)); +} +#endif + +// ----------------------------------------------------------------------------- +// FILE* + +template +result, std::vector> +try_parse(FILE* fp, std::string filename, spec s = spec::default_version()) +{ + const long beg = std::ftell(fp); + if (beg == -1L) + { + return err(std::vector{error_info( + std::string("Failed to access: \"") + filename + + "\", errno = " + std::to_string(errno), {} + )}); + } + + const int res_seekend = std::fseek(fp, 0, SEEK_END); + if (res_seekend != 0) + { + return err(std::vector{error_info( + std::string("Failed to seek: \"") + filename + + "\", errno = " + std::to_string(errno), {} + )}); + } + + const long end = std::ftell(fp); + if (end == -1L) + { + return err(std::vector{error_info( + std::string("Failed to access: \"") + filename + + "\", errno = " + std::to_string(errno), {} + )}); + } + + const auto fsize = end - beg; + + const auto res_seekbeg = std::fseek(fp, beg, SEEK_SET); + if (res_seekbeg != 0) + { + return err(std::vector{error_info( + std::string("Failed to seek: \"") + filename + + "\", errno = " + std::to_string(errno), {} + )}); + + } + + // read whole file as a sequence of char + assert(fsize >= 0); + std::vector letters(static_cast(fsize)); + const auto actual = std::fread(letters.data(), sizeof(char), static_cast(fsize), fp); + if(actual != static_cast(fsize)) + { + return err(std::vector{error_info( + std::string("File size changed: \"") + filename + + std::string("\" make sure that FILE* is in binary mode " + "to avoid LF <-> CRLF conversion"), {} + )}); + } + + return detail::parse_impl(std::move(letters), std::move(filename), std::move(s)); +} + +template +basic_value +parse(FILE* fp, std::string filename, spec s = spec::default_version()) +{ + const long beg = std::ftell(fp); + if (beg == -1L) + { + throw file_io_error(errno, "Failed to access", filename); + } + + const int res_seekend = std::fseek(fp, 0, SEEK_END); + if (res_seekend != 0) + { + throw file_io_error(errno, "Failed to seek", filename); + } + + const long end = std::ftell(fp); + if (end == -1L) + { + throw file_io_error(errno, "Failed to access", filename); + } + + const auto fsize = end - beg; + + const auto res_seekbeg = std::fseek(fp, beg, SEEK_SET); + if (res_seekbeg != 0) + { + throw file_io_error(errno, "Failed to seek", filename); + } + + // read whole file as a sequence of char + assert(fsize >= 0); + std::vector letters(static_cast(fsize)); + const auto actual = std::fread(letters.data(), sizeof(char), static_cast(fsize), fp); + if(actual != static_cast(fsize)) + { + throw file_io_error(errno, "File size changed; make sure that " + "FILE* is in binary mode to avoid LF <-> CRLF conversion", filename); + } + + auto res = detail::parse_impl(std::move(letters), std::move(filename), std::move(s)); + if(res.is_ok()) + { + return res.unwrap(); + } + else + { + std::string msg; + for(const auto& err : res.unwrap_err()) + { + msg += format_error(err); + } + throw syntax_error(std::move(msg), std::move(res.unwrap_err())); + } +} + +} // namespace toml + +#if defined(TOML11_COMPILE_SOURCES) +namespace toml +{ +struct type_config; +struct ordered_type_config; + +extern template result, std::vector> try_parse(std::vector, std::string, spec); +extern template result, std::vector> try_parse(std::istream&, std::string, spec); +extern template result, std::vector> try_parse(std::string, spec); +extern template result, std::vector> try_parse(FILE*, std::string, spec); +extern template result, std::vector> try_parse_str(std::string, spec, cxx::source_location); + +extern template basic_value parse(std::vector, std::string, spec); +extern template basic_value parse(std::istream&, std::string, spec); +extern template basic_value parse(std::string, spec); +extern template basic_value parse(FILE*, std::string, spec); +extern template basic_value parse_str(std::string, spec, cxx::source_location); + +extern template result, std::vector> try_parse(std::vector, std::string, spec); +extern template result, std::vector> try_parse(std::istream&, std::string, spec); +extern template result, std::vector> try_parse(std::string, spec); +extern template result, std::vector> try_parse(FILE*, std::string, spec); +extern template result, std::vector> try_parse_str(std::string, spec, cxx::source_location); + +extern template basic_value parse(std::vector, std::string, spec); +extern template basic_value parse(std::istream&, std::string, spec); +extern template basic_value parse(std::string, spec); +extern template basic_value parse(FILE*, std::string, spec); +extern template basic_value parse_str(std::string, spec, cxx::source_location); + +#if defined(TOML11_HAS_FILESYSTEM) +extern template cxx::enable_if_t::value, result, std::vector>> try_parse(const std::filesystem::path&, spec); +extern template cxx::enable_if_t::value, result, std::vector>> try_parse(const std::filesystem::path&, spec); +extern template cxx::enable_if_t::value, basic_value > parse (const std::filesystem::path&, spec); +extern template cxx::enable_if_t::value, basic_value > parse (const std::filesystem::path&, spec); +#endif // filesystem + +} // toml +#endif // TOML11_COMPILE_SOURCES + +#endif // TOML11_PARSER_HPP +#ifndef TOML11_LITERAL_HPP +#define TOML11_LITERAL_HPP + +#ifndef TOML11_LITERAL_FWD_HPP +#define TOML11_LITERAL_FWD_HPP + + +namespace toml +{ + +namespace detail +{ +// implementation +::toml::value literal_internal_impl(location loc); +} // detail + +inline namespace literals +{ +inline namespace toml_literals +{ + +::toml::value operator"" _toml(const char* str, std::size_t len); + +#if defined(TOML11_HAS_CHAR8_T) +// value of u8"" literal has been changed from char to char8_t and char8_t is +// NOT compatible to char +::toml::value operator"" _toml(const char8_t* str, std::size_t len); +#endif + +} // toml_literals +} // literals +} // toml +#endif // TOML11_LITERAL_FWD_HPP + +#if ! defined(TOML11_COMPILE_SOURCES) +#ifndef TOML11_LITERAL_IMPL_HPP +#define TOML11_LITERAL_IMPL_HPP + + +namespace toml +{ + +namespace detail +{ +// implementation +TOML11_INLINE ::toml::value literal_internal_impl(location loc) +{ + const auto s = ::toml::spec::default_version(); + context ctx(s); + + const auto front = loc; + + // ------------------------------------------------------------------------ + // check if it is a raw value. + + // skip empty lines and comment lines + auto sp = skip_multiline_spacer(loc, ctx); + if(loc.eof()) + { + ::toml::value val; + if(sp.has_value()) + { + for(std::size_t i=0; i(str), + reinterpret_cast(str + len), + c.begin()); + if( ! c.empty() && c.back()) + { + c.push_back('\n'); // to make it easy to parse comment, we add newline + } + + return literal_internal_impl(::toml::detail::location( + std::make_shared(std::move(c)), + "TOML literal encoded in a C++ code")); +} + +#if defined(__cpp_char8_t) +# if __cpp_char8_t >= 201811L +# define TOML11_HAS_CHAR8_T 1 +# endif +#endif + +#if defined(TOML11_HAS_CHAR8_T) +// value of u8"" literal has been changed from char to char8_t and char8_t is +// NOT compatible to char +TOML11_INLINE ::toml::value +operator"" _toml(const char8_t* str, std::size_t len) +{ + if(len == 0) + { + return ::toml::value{}; + } + + ::toml::detail::location::container_type c(len); + std::copy(reinterpret_cast(str), + reinterpret_cast(str + len), + c.begin()); + if( ! c.empty() && c.back()) + { + c.push_back('\n'); // to make it easy to parse comment, we add newline + } + + return literal_internal_impl(::toml::detail::location( + std::make_shared(std::move(c)), + "TOML literal encoded in a C++ code")); +} +#endif + +} // toml_literals +} // literals +} // toml +#endif // TOML11_LITERAL_IMPL_HPP +#endif + +#endif // TOML11_LITERAL_HPP +#ifndef TOML11_SERIALIZER_HPP +#define TOML11_SERIALIZER_HPP + + +#include +#include +#include + +#include +#include + +namespace toml +{ + +struct serialization_error final : public ::toml::exception +{ + public: + explicit serialization_error(std::string what_arg, source_location loc) + : what_(std::move(what_arg)), loc_(std::move(loc)) + {} + ~serialization_error() noexcept override = default; + + const char* what() const noexcept override {return what_.c_str();} + source_location const& location() const noexcept {return loc_;} + + private: + std::string what_; + source_location loc_; +}; + +namespace detail +{ +template +class serializer +{ + public: + + using value_type = basic_value; + + using key_type = typename value_type::key_type ; + using comment_type = typename value_type::comment_type ; + using boolean_type = typename value_type::boolean_type ; + using integer_type = typename value_type::integer_type ; + using floating_type = typename value_type::floating_type ; + using string_type = typename value_type::string_type ; + using local_time_type = typename value_type::local_time_type ; + using local_date_type = typename value_type::local_date_type ; + using local_datetime_type = typename value_type::local_datetime_type ; + using offset_datetime_type = typename value_type::offset_datetime_type; + using array_type = typename value_type::array_type ; + using table_type = typename value_type::table_type ; + + using char_type = typename string_type::value_type; + + public: + + explicit serializer(const spec& sp) + : spec_(sp), force_inline_(false), current_indent_(0) + {} + + string_type operator()(const std::vector& ks, const value_type& v) + { + for(const auto& k : ks) + { + this->keys_.push_back(k); + } + return (*this)(v); + } + + string_type operator()(const key_type& k, const value_type& v) + { + this->keys_.push_back(k); + return (*this)(v); + } + + string_type operator()(const value_type& v) + { + switch(v.type()) + { + case value_t::boolean : {return (*this)(v.as_boolean (), v.as_boolean_fmt (), v.location());} + case value_t::integer : {return (*this)(v.as_integer (), v.as_integer_fmt (), v.location());} + case value_t::floating : {return (*this)(v.as_floating (), v.as_floating_fmt (), v.location());} + case value_t::string : {return (*this)(v.as_string (), v.as_string_fmt (), v.location());} + case value_t::offset_datetime: {return (*this)(v.as_offset_datetime(), v.as_offset_datetime_fmt(), v.location());} + case value_t::local_datetime : {return (*this)(v.as_local_datetime (), v.as_local_datetime_fmt (), v.location());} + case value_t::local_date : {return (*this)(v.as_local_date (), v.as_local_date_fmt (), v.location());} + case value_t::local_time : {return (*this)(v.as_local_time (), v.as_local_time_fmt (), v.location());} + case value_t::array : + { + return (*this)(v.as_array(), v.as_array_fmt(), v.comments(), v.location()); + } + case value_t::table : + { + string_type retval; + if(this->keys_.empty()) // it might be the root table. emit comments here. + { + retval += format_comments(v.comments(), v.as_table_fmt().indent_type); + } + if( ! retval.empty()) // we have comment. + { + retval += char_type('\n'); + } + + retval += (*this)(v.as_table(), v.as_table_fmt(), v.comments(), v.location()); + return retval; + } + case value_t::empty: + { + if(this->spec_.ext_null_value) + { + return string_conv("null"); + } + break; + } + default: + { + break; + } + } + throw serialization_error(format_error( + "[error] toml::serializer: toml::basic_value " + "does not have any valid type.", v.location(), "here"), v.location()); + } + + private: + + string_type operator()(const boolean_type& b, const boolean_format_info&, const source_location&) // {{{ + { + if(b) + { + return string_conv("true"); + } + else + { + return string_conv("false"); + } + } // }}} + + string_type operator()(const integer_type i, const integer_format_info& fmt, const source_location& loc) // {{{ + { + std::ostringstream oss; + this->set_locale(oss); + + const auto insert_spacer = [&fmt](std::string s) -> std::string { + if(fmt.spacer == 0) {return s;} + + std::string sign; + if( ! s.empty() && (s.at(0) == '+' || s.at(0) == '-')) + { + sign += s.at(0); + s.erase(s.begin()); + } + + std::string spaced; + std::size_t counter = 0; + for(auto iter = s.rbegin(); iter != s.rend(); ++iter) + { + if(counter != 0 && counter % fmt.spacer == 0) + { + spaced += '_'; + } + spaced += *iter; + counter += 1; + } + if(!spaced.empty() && spaced.back() == '_') {spaced.pop_back();} + + s.clear(); + std::copy(spaced.rbegin(), spaced.rend(), std::back_inserter(s)); + return sign + s; + }; + + std::string retval; + if(fmt.fmt == integer_format::dec) + { + oss << std::setw(static_cast(fmt.width)) << std::dec << i; + retval = insert_spacer(oss.str()); + + if(this->spec_.ext_num_suffix && ! fmt.suffix.empty()) + { + retval += '_'; + retval += fmt.suffix; + } + } + else + { + if(i < 0) + { + throw serialization_error(format_error("binary, octal, hexadecimal " + "integer does not allow negative value", loc, "here"), loc); + } + switch(fmt.fmt) + { + case integer_format::hex: + { + oss << std::noshowbase + << std::setw(static_cast(fmt.width)) + << std::setfill('0') + << std::hex; + if(fmt.uppercase) + { + oss << std::uppercase; + } + else + { + oss << std::nouppercase; + } + oss << i; + retval = std::string("0x") + insert_spacer(oss.str()); + break; + } + case integer_format::oct: + { + oss << std::setw(static_cast(fmt.width)) << std::setfill('0') << std::oct << i; + retval = std::string("0o") + insert_spacer(oss.str()); + break; + } + case integer_format::bin: + { + integer_type x{i}; + std::string tmp; + std::size_t bits(0); + while(x != 0) + { + if(fmt.spacer != 0) + { + if(bits != 0 && (bits % fmt.spacer) == 0) {tmp += '_';} + } + if(x % 2 == 1) { tmp += '1'; } else { tmp += '0'; } + x >>= 1; + bits += 1; + } + for(; bits < fmt.width; ++bits) + { + if(fmt.spacer != 0) + { + if(bits != 0 && (bits % fmt.spacer) == 0) {tmp += '_';} + } + tmp += '0'; + } + for(auto iter = tmp.rbegin(); iter != tmp.rend(); ++iter) + { + oss << *iter; + } + retval = std::string("0b") + oss.str(); + break; + } + default: + { + throw serialization_error(format_error( + "none of dec, hex, oct, bin: " + to_string(fmt.fmt), + loc, "here"), loc); + } + } + } + return string_conv(retval); + } // }}} + + string_type operator()(const floating_type f, const floating_format_info& fmt, const source_location&) // {{{ + { + using std::isnan; + using std::isinf; + using std::signbit; + + std::ostringstream oss; + this->set_locale(oss); + + if(isnan(f)) + { + if(signbit(f)) + { + oss << '-'; + } + oss << "nan"; + if(this->spec_.ext_num_suffix && ! fmt.suffix.empty()) + { + oss << '_'; + oss << fmt.suffix; + } + return string_conv(oss.str()); + } + + if(isinf(f)) + { + if(signbit(f)) + { + oss << '-'; + } + oss << "inf"; + if(this->spec_.ext_num_suffix && ! fmt.suffix.empty()) + { + oss << '_'; + oss << fmt.suffix; + } + return string_conv(oss.str()); + } + + switch(fmt.fmt) + { + case floating_format::defaultfloat: + { + if(fmt.prec != 0) + { + oss << std::setprecision(static_cast(fmt.prec)); + } + oss << f; + // since defaultfloat may omit point, we need to add it + std::string s = oss.str(); + if (s.find('.') == std::string::npos && + s.find('e') == std::string::npos && + s.find('E') == std::string::npos ) + { + s += ".0"; + } + if(this->spec_.ext_num_suffix && ! fmt.suffix.empty()) + { + s += '_'; + s += fmt.suffix; + } + return string_conv(s); + } + case floating_format::fixed: + { + if(fmt.prec != 0) + { + oss << std::setprecision(static_cast(fmt.prec)); + } + oss << std::fixed << f; + if(this->spec_.ext_num_suffix && ! fmt.suffix.empty()) + { + oss << '_' << fmt.suffix; + } + return string_conv(oss.str()); + } + case floating_format::scientific: + { + if(fmt.prec != 0) + { + oss << std::setprecision(static_cast(fmt.prec)); + } + oss << std::scientific << f; + if(this->spec_.ext_num_suffix && ! fmt.suffix.empty()) + { + oss << '_' << fmt.suffix; + } + return string_conv(oss.str()); + } + case floating_format::hex: + { + if(this->spec_.ext_hex_float) + { + oss << std::hexfloat << f; + // suffix is only for decimal numbers. + return string_conv(oss.str()); + } + else // no hex allowed. output with max precision. + { + oss << std::setprecision(std::numeric_limits::max_digits10) + << std::scientific << f; + // suffix is only for decimal numbers. + return string_conv(oss.str()); + } + } + default: + { + if(this->spec_.ext_num_suffix && ! fmt.suffix.empty()) + { + oss << '_' << fmt.suffix; + } + return string_conv(oss.str()); + } + } + } // }}} + + string_type operator()(string_type s, const string_format_info& fmt, const source_location& loc) // {{{ + { + string_type retval; + switch(fmt.fmt) + { + case string_format::basic: + { + retval += char_type('"'); + retval += this->escape_basic_string(s); + retval += char_type('"'); + return retval; + } + case string_format::literal: + { + if(std::find(s.begin(), s.end(), char_type('\n')) != s.end()) + { + throw serialization_error(format_error("toml::serializer: " + "(non-multiline) literal string cannot have a newline", + loc, "here"), loc); + } + retval += char_type('\''); + retval += s; + retval += char_type('\''); + return retval; + } + case string_format::multiline_basic: + { + retval += string_conv("\"\"\""); + if(fmt.start_with_newline) + { + retval += char_type('\n'); + } + + retval += this->escape_ml_basic_string(s); + + retval += string_conv("\"\"\""); + return retval; + } + case string_format::multiline_literal: + { + retval += string_conv("'''"); + if(fmt.start_with_newline) + { + retval += char_type('\n'); + } + retval += s; + retval += string_conv("'''"); + return retval; + } + default: + { + throw serialization_error(format_error( + "[error] toml::serializer::operator()(string): " + "invalid string_format value", loc, "here"), loc); + } + } + } // }}} + + string_type operator()(const local_date_type& d, const local_date_format_info&, const source_location&) // {{{ + { + std::ostringstream oss; + oss << d; + return string_conv(oss.str()); + } // }}} + + string_type operator()(const local_time_type& t, const local_time_format_info& fmt, const source_location&) // {{{ + { + return this->format_local_time(t, fmt.has_seconds, fmt.subsecond_precision); + } // }}} + + string_type operator()(const local_datetime_type& dt, const local_datetime_format_info& fmt, const source_location&) // {{{ + { + std::ostringstream oss; + oss << dt.date; + switch(fmt.delimiter) + { + case datetime_delimiter_kind::upper_T: { oss << 'T'; break; } + case datetime_delimiter_kind::lower_t: { oss << 't'; break; } + case datetime_delimiter_kind::space: { oss << ' '; break; } + default: { oss << 'T'; break; } + } + return string_conv(oss.str()) + + this->format_local_time(dt.time, fmt.has_seconds, fmt.subsecond_precision); + } // }}} + + string_type operator()(const offset_datetime_type& odt, const offset_datetime_format_info& fmt, const source_location&) // {{{ + { + std::ostringstream oss; + oss << odt.date; + switch(fmt.delimiter) + { + case datetime_delimiter_kind::upper_T: { oss << 'T'; break; } + case datetime_delimiter_kind::lower_t: { oss << 't'; break; } + case datetime_delimiter_kind::space: { oss << ' '; break; } + default: { oss << 'T'; break; } + } + oss << string_conv(this->format_local_time(odt.time, fmt.has_seconds, fmt.subsecond_precision)); + oss << odt.offset; + return string_conv(oss.str()); + } // }}} + + string_type operator()(const array_type& a, const array_format_info& fmt, const comment_type& com, const source_location& loc) // {{{ + { + array_format f = fmt.fmt; + if(fmt.fmt == array_format::default_format) + { + // [[in.this.form]], you cannot add a comment to the array itself + // (but you can add a comment to each table). + // To keep comments, we need to avoid multiline array-of-tables + // if array itself has a comment. + if( ! this->keys_.empty() && + ! a.empty() && + com.empty() && + std::all_of(a.begin(), a.end(), [](const value_type& e) {return e.is_table();})) + { + f = array_format::array_of_tables; + } + else + { + f = array_format::oneline; + + // check if it becomes long + std::size_t approx_len = 0; + for(const auto& e : a) + { + // have a comment. cannot be inlined + if( ! e.comments().empty()) + { + f = array_format::multiline; + break; + } + // possibly long types ... + if(e.is_array() || e.is_table() || e.is_offset_datetime() || e.is_local_datetime()) + { + f = array_format::multiline; + break; + } + else if(e.is_boolean()) + { + approx_len += (*this)(e.as_boolean(), e.as_boolean_fmt(), e.location()).size(); + } + else if(e.is_integer()) + { + approx_len += (*this)(e.as_integer(), e.as_integer_fmt(), e.location()).size(); + } + else if(e.is_floating()) + { + approx_len += (*this)(e.as_floating(), e.as_floating_fmt(), e.location()).size(); + } + else if(e.is_string()) + { + if(e.as_string_fmt().fmt == string_format::multiline_basic || + e.as_string_fmt().fmt == string_format::multiline_literal) + { + f = array_format::multiline; + break; + } + approx_len += 2 + (*this)(e.as_string(), e.as_string_fmt(), e.location()).size(); + } + else if(e.is_local_date()) + { + approx_len += 10; // 1234-56-78 + } + else if(e.is_local_time()) + { + approx_len += 15; // 12:34:56.789012 + } + + if(approx_len > 60) // key, ` = `, `[...]` < 80 + { + f = array_format::multiline; + break; + } + approx_len += 2; // `, ` + } + } + } + if(this->force_inline_ && f == array_format::array_of_tables) + { + f = array_format::multiline; + } + if(a.empty() && f == array_format::array_of_tables) + { + f = array_format::oneline; + } + + // -------------------------------------------------------------------- + + if(f == array_format::array_of_tables) + { + if(this->keys_.empty()) + { + throw serialization_error("array of table must have its key. " + "use format(key, v)", loc); + } + string_type retval; + for(const auto& e : a) + { + assert(e.is_table()); + + this->current_indent_ += e.as_table_fmt().name_indent; + retval += this->format_comments(e.comments(), e.as_table_fmt().indent_type); + retval += this->format_indent(e.as_table_fmt().indent_type); + this->current_indent_ -= e.as_table_fmt().name_indent; + + retval += string_conv("[["); + retval += this->format_keys(this->keys_).value(); + retval += string_conv("]]\n"); + + retval += this->format_ml_table(e.as_table(), e.as_table_fmt()); + } + return retval; + } + else if(f == array_format::oneline) + { + // ignore comments. we cannot emit comments + string_type retval; + retval += char_type('['); + for(const auto& e : a) + { + this->force_inline_ = true; + retval += (*this)(e); + retval += string_conv(", "); + } + if( ! a.empty()) + { + retval.pop_back(); // ` ` + retval.pop_back(); // `,` + } + retval += char_type(']'); + this->force_inline_ = false; + return retval; + } + else + { + assert(f == array_format::multiline); + + string_type retval; + retval += string_conv("[\n"); + + for(const auto& e : a) + { + this->current_indent_ += fmt.body_indent; + retval += this->format_comments(e.comments(), fmt.indent_type); + retval += this->format_indent(fmt.indent_type); + this->current_indent_ -= fmt.body_indent; + + this->force_inline_ = true; + retval += (*this)(e); + retval += string_conv(",\n"); + } + this->force_inline_ = false; + + this->current_indent_ += fmt.closing_indent; + retval += this->format_indent(fmt.indent_type); + this->current_indent_ -= fmt.closing_indent; + + retval += char_type(']'); + return retval; + } + } // }}} + + string_type operator()(const table_type& t, const table_format_info& fmt, const comment_type& com, const source_location& loc) // {{{ + { + if(this->force_inline_) + { + if(fmt.fmt == table_format::multiline_oneline) + { + return this->format_ml_inline_table(t, fmt); + } + else + { + return this->format_inline_table(t, fmt); + } + } + else + { + if(fmt.fmt == table_format::multiline) + { + string_type retval; + // comment is emitted inside format_ml_table + if(auto k = this->format_keys(this->keys_)) + { + this->current_indent_ += fmt.name_indent; + retval += this->format_comments(com, fmt.indent_type); + retval += this->format_indent(fmt.indent_type); + this->current_indent_ -= fmt.name_indent; + retval += char_type('['); + retval += k.value(); + retval += string_conv("]\n"); + } + // otherwise, its the root. + + retval += this->format_ml_table(t, fmt); + return retval; + } + else if(fmt.fmt == table_format::oneline) + { + return this->format_inline_table(t, fmt); + } + else if(fmt.fmt == table_format::multiline_oneline) + { + return this->format_ml_inline_table(t, fmt); + } + else if(fmt.fmt == table_format::dotted) + { + std::vector keys; + if(this->keys_.empty()) + { + throw serialization_error(format_error("toml::serializer: " + "dotted table must have its key. use format(key, v)", + loc, "here"), loc); + } + keys.push_back(this->keys_.back()); + + const auto retval = this->format_dotted_table(t, fmt, loc, keys); + keys.pop_back(); + return retval; + } + else + { + assert(fmt.fmt == table_format::implicit); + + string_type retval; + for(const auto& kv : t) + { + const auto& k = kv.first; + const auto& v = kv.second; + + if( ! v.is_table() && ! v.is_array_of_tables()) + { + throw serialization_error(format_error("toml::serializer: " + "an implicit table cannot have non-table value.", + v.location(), "here"), v.location()); + } + if(v.is_table()) + { + if(v.as_table_fmt().fmt != table_format::multiline && + v.as_table_fmt().fmt != table_format::implicit) + { + throw serialization_error(format_error("toml::serializer: " + "an implicit table cannot have non-multiline table", + v.location(), "here"), v.location()); + } + } + else + { + assert(v.is_array()); + for(const auto& e : v.as_array()) + { + if(e.as_table_fmt().fmt != table_format::multiline && + v.as_table_fmt().fmt != table_format::implicit) + { + throw serialization_error(format_error("toml::serializer: " + "an implicit table cannot have non-multiline table", + e.location(), "here"), e.location()); + } + } + } + + keys_.push_back(k); + retval += (*this)(v); + keys_.pop_back(); + } + return retval; + } + } + } // }}} + + private: + + string_type escape_basic_string(const string_type& s) const // {{{ + { + string_type retval; + for(const char_type c : s) + { + switch(c) + { + case char_type('\\'): {retval += string_conv("\\\\"); break;} + case char_type('\"'): {retval += string_conv("\\\""); break;} + case char_type('\b'): {retval += string_conv("\\b" ); break;} + case char_type('\t'): {retval += string_conv("\\t" ); break;} + case char_type('\f'): {retval += string_conv("\\f" ); break;} + case char_type('\n'): {retval += string_conv("\\n" ); break;} + case char_type('\r'): {retval += string_conv("\\r" ); break;} + default : + { + if(c == char_type(0x1B) && spec_.v1_1_0_add_escape_sequence_e) + { + retval += string_conv("\\e"); + } + else if((char_type(0x00) <= c && c <= char_type(0x08)) || + (char_type(0x0A) <= c && c <= char_type(0x1F)) || + c == char_type(0x7F)) + { + if(spec_.v1_1_0_add_escape_sequence_x) + { + retval += string_conv("\\x"); + } + else + { + retval += string_conv("\\u00"); + } + const auto c1 = c / 16; + const auto c2 = c % 16; + retval += static_cast('0' + c1); + if(c2 < 10) + { + retval += static_cast('0' + c2); + } + else // 10 <= c2 + { + retval += static_cast('A' + (c2 - 10)); + } + } + else + { + retval += c; + } + } + } + } + return retval; + } // }}} + + string_type escape_ml_basic_string(const string_type& s) // {{{ + { + string_type retval; + for(const char_type c : s) + { + switch(c) + { + case char_type('\\'): {retval += string_conv("\\\\"); break;} + case char_type('\b'): {retval += string_conv("\\b" ); break;} + case char_type('\t'): {retval += string_conv("\\t" ); break;} + case char_type('\f'): {retval += string_conv("\\f" ); break;} + case char_type('\n'): {retval += string_conv("\n" ); break;} + case char_type('\r'): {retval += string_conv("\\r" ); break;} + default : + { + if(c == char_type(0x1B) && spec_.v1_1_0_add_escape_sequence_e) + { + retval += string_conv("\\e"); + } + else if((char_type(0x00) <= c && c <= char_type(0x08)) || + (char_type(0x0A) <= c && c <= char_type(0x1F)) || + c == char_type(0x7F)) + { + if(spec_.v1_1_0_add_escape_sequence_x) + { + retval += string_conv("\\x"); + } + else + { + retval += string_conv("\\u00"); + } + const auto c1 = c / 16; + const auto c2 = c % 16; + retval += static_cast('0' + c1); + if(c2 < 10) + { + retval += static_cast('0' + c2); + } + else // 10 <= c2 + { + retval += static_cast('A' + (c2 - 10)); + } + } + else + { + retval += c; + } + } + } + } + // Only 1 or 2 consecutive `"`s are allowed in multiline basic string. + // 3 consecutive `"`s are considered as a closing delimiter. + // We need to check if there are 3 or more consecutive `"`s and insert + // backslash to break them down into several short `"`s like the `str6` + // in the following example. + // ```toml + // str4 = """Here are two quotation marks: "". Simple enough.""" + // # str5 = """Here are three quotation marks: """.""" # INVALID + // str5 = """Here are three quotation marks: ""\".""" + // str6 = """Here are fifteen quotation marks: ""\"""\"""\"""\"""\".""" + // ``` + auto found_3_quotes = retval.find(string_conv("\"\"\"")); + while(found_3_quotes != string_type::npos) + { + retval.replace(found_3_quotes, 3, string_conv("\"\"\\\"")); + found_3_quotes = retval.find(string_conv("\"\"\"")); + } + return retval; + } // }}} + + string_type format_local_time(const local_time_type& t, const bool has_seconds, const std::size_t subsec_prec) // {{{ + { + std::ostringstream oss; + oss << std::setfill('0') << std::setw(2) << static_cast(t.hour); + oss << ':'; + oss << std::setfill('0') << std::setw(2) << static_cast(t.minute); + if(has_seconds) + { + oss << ':'; + oss << std::setfill('0') << std::setw(2) << static_cast(t.second); + if(subsec_prec != 0) + { + std::ostringstream subsec; + subsec << std::setfill('0') << std::setw(3) << static_cast(t.millisecond); + subsec << std::setfill('0') << std::setw(3) << static_cast(t.microsecond); + subsec << std::setfill('0') << std::setw(3) << static_cast(t.nanosecond); + std::string subsec_str = subsec.str(); + oss << '.' << subsec_str.substr(0, subsec_prec); + } + } + return string_conv(oss.str()); + } // }}} + + string_type format_ml_table(const table_type& t, const table_format_info& fmt) // {{{ + { + const auto format_later = [](const value_type& v) -> bool { + + const bool is_ml_table = v.is_table() && + v.as_table_fmt().fmt != table_format::oneline && + v.as_table_fmt().fmt != table_format::multiline_oneline && + v.as_table_fmt().fmt != table_format::dotted ; + + const bool is_ml_array_table = v.is_array_of_tables() && + v.as_array_fmt().fmt != array_format::oneline && + v.as_array_fmt().fmt != array_format::multiline; + + return is_ml_table || is_ml_array_table; + }; + + string_type retval; + this->current_indent_ += fmt.body_indent; + for(const auto& kv : t) + { + const auto& key = kv.first; + const auto& val = kv.second; + if(format_later(val)) + { + continue; + } + this->keys_.push_back(key); + + retval += format_comments(val.comments(), fmt.indent_type); + retval += format_indent(fmt.indent_type); + if(val.is_table() && val.as_table_fmt().fmt == table_format::dotted) + { + retval += (*this)(val); + } + else + { + retval += format_key(key); + retval += string_conv(" = "); + retval += (*this)(val); + retval += char_type('\n'); + } + this->keys_.pop_back(); + } + this->current_indent_ -= fmt.body_indent; + + if( ! retval.empty()) + { + retval += char_type('\n'); // for readability, add empty line between tables + } + for(const auto& kv : t) + { + if( ! format_later(kv.second)) + { + continue; + } + // must be a [multiline.table] or [[multiline.array.of.tables]]. + // comments will be generated inside it. + this->keys_.push_back(kv.first); + retval += (*this)(kv.second); + this->keys_.pop_back(); + } + return retval; + } // }}} + + string_type format_inline_table(const table_type& t, const table_format_info&) // {{{ + { + // comments are ignored because we cannot write without newline + string_type retval; + retval += char_type('{'); + for(const auto& kv : t) + { + this->force_inline_ = true; + retval += this->format_key(kv.first); + retval += string_conv(" = "); + retval += (*this)(kv.second); + retval += string_conv(", "); + } + if( ! t.empty()) + { + retval.pop_back(); // ' ' + retval.pop_back(); // ',' + } + retval += char_type('}'); + this->force_inline_ = false; + return retval; + } // }}} + + string_type format_ml_inline_table(const table_type& t, const table_format_info& fmt) // {{{ + { + string_type retval; + retval += string_conv("{\n"); + this->current_indent_ += fmt.body_indent; + for(const auto& kv : t) + { + this->force_inline_ = true; + retval += format_comments(kv.second.comments(), fmt.indent_type); + retval += format_indent(fmt.indent_type); + retval += kv.first; + retval += string_conv(" = "); + + this->force_inline_ = true; + retval += (*this)(kv.second); + + retval += string_conv(",\n"); + } + if( ! t.empty()) + { + retval.pop_back(); // '\n' + retval.pop_back(); // ',' + } + this->current_indent_ -= fmt.body_indent; + this->force_inline_ = false; + + this->current_indent_ += fmt.closing_indent; + retval += format_indent(fmt.indent_type); + this->current_indent_ -= fmt.closing_indent; + + retval += char_type('}'); + return retval; + } // }}} + + string_type format_dotted_table(const table_type& t, const table_format_info& fmt, // {{{ + const source_location&, std::vector& keys) + { + // lets say we have: `{"a": {"b": {"c": {"d": "foo", "e": "bar"} } }` + // and `a` and `b` are `dotted`. + // + // - in case if `c` is `oneline`: + // ```toml + // a.b.c = {d = "foo", e = "bar"} + // ``` + // + // - in case if and `c` is `dotted`: + // ```toml + // a.b.c.d = "foo" + // a.b.c.e = "bar" + // ``` + + string_type retval; + + for(const auto& kv : t) + { + const auto& key = kv.first; + const auto& val = kv.second; + + keys.push_back(key); + + // format recursive dotted table? + if (val.is_table() && + val.as_table_fmt().fmt != table_format::oneline && + val.as_table_fmt().fmt != table_format::multiline_oneline) + { + retval += this->format_dotted_table(val.as_table(), val.as_table_fmt(), val.location(), keys); + } + else // non-table or inline tables. format normally + { + retval += format_comments(val.comments(), fmt.indent_type); + retval += format_indent(fmt.indent_type); + retval += format_keys(keys).value(); + retval += string_conv(" = "); + this->force_inline_ = true; // sub-table must be inlined + retval += (*this)(val); + retval += char_type('\n'); + this->force_inline_ = false; + } + keys.pop_back(); + } + return retval; + } // }}} + + string_type format_key(const key_type& key) // {{{ + { + if(key.empty()) + { + return string_conv("\"\""); + } + + // check the key can be a bare (unquoted) key + auto loc = detail::make_temporary_location(string_conv(key)); + auto reg = detail::syntax::unquoted_key(this->spec_).scan(loc); + if(reg.is_ok() && loc.eof()) + { + return key; + } + + //if it includes special characters, then format it in a "quoted" key. + string_type formatted = string_conv("\""); + for(const char_type c : key) + { + switch(c) + { + case char_type('\\'): {formatted += string_conv("\\\\"); break;} + case char_type('\"'): {formatted += string_conv("\\\""); break;} + case char_type('\b'): {formatted += string_conv("\\b" ); break;} + case char_type('\t'): {formatted += string_conv("\\t" ); break;} + case char_type('\f'): {formatted += string_conv("\\f" ); break;} + case char_type('\n'): {formatted += string_conv("\\n" ); break;} + case char_type('\r'): {formatted += string_conv("\\r" ); break;} + default : + { + // ASCII ctrl char + if( (char_type(0x00) <= c && c <= char_type(0x08)) || + (char_type(0x0A) <= c && c <= char_type(0x1F)) || + c == char_type(0x7F)) + { + if(spec_.v1_1_0_add_escape_sequence_x) + { + formatted += string_conv("\\x"); + } + else + { + formatted += string_conv("\\u00"); + } + const auto c1 = c / 16; + const auto c2 = c % 16; + formatted += static_cast('0' + c1); + if(c2 < 10) + { + formatted += static_cast('0' + c2); + } + else // 10 <= c2 + { + formatted += static_cast('A' + (c2 - 10)); + } + } + else + { + formatted += c; + } + break; + } + } + } + formatted += string_conv("\""); + return formatted; + } // }}} + cxx::optional format_keys(const std::vector& keys) // {{{ + { + if(keys.empty()) + { + return cxx::make_nullopt(); + } + + string_type formatted; + for(const auto& ky : keys) + { + formatted += format_key(ky); + formatted += char_type('.'); + } + formatted.pop_back(); // remove the last dot '.' + return formatted; + } // }}} + + string_type format_comments(const discard_comments&, const indent_char) const // {{{ + { + return string_conv(""); + } // }}} + string_type format_comments(const preserve_comments& comments, const indent_char indent_type) const // {{{ + { + string_type retval; + for(const auto& c : comments) + { + if(c.empty()) {continue;} + retval += format_indent(indent_type); + if(c.front() != '#') {retval += char_type('#');} + retval += string_conv(c); + if(c.back() != '\n') {retval += char_type('\n');} + } + return retval; + } // }}} + + string_type format_indent(const indent_char indent_type) const // {{{ + { + const auto indent = static_cast((std::max)(0, this->current_indent_)); + if(indent_type == indent_char::space) + { + return string_conv(make_string(indent, ' ')); + } + else if(indent_type == indent_char::tab) + { + return string_conv(make_string(indent, '\t')); + } + else + { + return string_type{}; + } + } // }}} + + std::locale set_locale(std::ostream& os) const + { + return os.imbue(std::locale::classic()); + } + + private: + + spec spec_; + bool force_inline_; // table inside an array without fmt specification + std::int32_t current_indent_; + std::vector keys_; +}; +} // detail + +template +typename basic_value::string_type +format(const basic_value& v, const spec s = spec::default_version()) +{ + detail::serializer ser(s); + return ser(v); +} +template +typename basic_value::string_type +format(const typename basic_value::key_type& k, + const basic_value& v, + const spec s = spec::default_version()) +{ + detail::serializer ser(s); + return ser(k, v); +} +template +typename basic_value::string_type +format(const std::vector::key_type>& ks, + const basic_value& v, + const spec s = spec::default_version()) +{ + detail::serializer ser(s); + return ser(ks, v); +} + +template +std::ostream& operator<<(std::ostream& os, const basic_value& v) +{ + os << format(v); + return os; +} + +} // toml + +#if defined(TOML11_COMPILE_SOURCES) +namespace toml +{ +struct type_config; +struct ordered_type_config; + +extern template typename basic_value::string_type +format(const basic_value&, const spec); + +extern template typename basic_value::string_type +format(const typename basic_value::key_type& k, + const basic_value& v, const spec); + +extern template typename basic_value::string_type +format(const std::vector::key_type>& ks, + const basic_value& v, const spec s); + +extern template typename basic_value::string_type +format(const basic_value&, const spec); + +extern template typename basic_value::string_type +format(const typename basic_value::key_type& k, + const basic_value& v, const spec); + +extern template typename basic_value::string_type +format(const std::vector::key_type>& ks, + const basic_value& v, const spec s); + +namespace detail +{ +extern template class serializer<::toml::type_config>; +extern template class serializer<::toml::ordered_type_config>; +} // detail +} // toml +#endif // TOML11_COMPILE_SOURCES + + +#endif // TOML11_SERIALIZER_HPP +#ifndef TOML11_TOML_HPP +#define TOML11_TOML_HPP + +// The MIT License (MIT) +// +// Copyright (c) 2017-now Toru Niina +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +// IWYU pragma: begin_exports +// IWYU pragma: end_exports + +#endif// TOML11_TOML_HPP diff --git a/3party/toml11/toml/color.hpp b/3party/toml11/toml/color.hpp deleted file mode 100644 index a3fd890..0000000 --- a/3party/toml11/toml/color.hpp +++ /dev/null @@ -1,109 +0,0 @@ -#ifndef TOML11_COLOR_HPP -#define TOML11_COLOR_HPP -#include -#include - -#ifdef TOML11_COLORIZE_ERROR_MESSAGE -#define TOML11_ERROR_MESSAGE_COLORIZED true -#else -#define TOML11_ERROR_MESSAGE_COLORIZED false -#endif - -namespace toml -{ - -// put ANSI escape sequence to ostream -namespace color_ansi -{ -namespace detail -{ - -inline int colorize_index() -{ - static const int index = std::ios_base::xalloc(); - return index; -} - -// Control color mode globally -class color_mode -{ - public: - inline void enable() - { - should_color_ = true; - } - inline void disable() - { - should_color_ = false; - } - - inline bool should_color() const - { - return should_color_; - } - - static color_mode& status() - { - static color_mode status_; - return status_; - } - - private: - bool should_color_ = false; -}; - -} // detail - -inline std::ostream& colorize(std::ostream& os) -{ - // by default, it is zero. - os.iword(detail::colorize_index()) = 1; - return os; -} -inline std::ostream& nocolorize(std::ostream& os) -{ - os.iword(detail::colorize_index()) = 0; - return os; -} -inline std::ostream& reset (std::ostream& os) -{if(os.iword(detail::colorize_index()) == 1) {os << "\033[00m";} return os;} -inline std::ostream& bold (std::ostream& os) -{if(os.iword(detail::colorize_index()) == 1) {os << "\033[01m";} return os;} -inline std::ostream& grey (std::ostream& os) -{if(os.iword(detail::colorize_index()) == 1) {os << "\033[30m";} return os;} -inline std::ostream& red (std::ostream& os) -{if(os.iword(detail::colorize_index()) == 1) {os << "\033[31m";} return os;} -inline std::ostream& green (std::ostream& os) -{if(os.iword(detail::colorize_index()) == 1) {os << "\033[32m";} return os;} -inline std::ostream& yellow (std::ostream& os) -{if(os.iword(detail::colorize_index()) == 1) {os << "\033[33m";} return os;} -inline std::ostream& blue (std::ostream& os) -{if(os.iword(detail::colorize_index()) == 1) {os << "\033[34m";} return os;} -inline std::ostream& magenta(std::ostream& os) -{if(os.iword(detail::colorize_index()) == 1) {os << "\033[35m";} return os;} -inline std::ostream& cyan (std::ostream& os) -{if(os.iword(detail::colorize_index()) == 1) {os << "\033[36m";} return os;} -inline std::ostream& white (std::ostream& os) -{if(os.iword(detail::colorize_index()) == 1) {os << "\033[37m";} return os;} - -inline void enable() -{ - return detail::color_mode::status().enable(); -} -inline void disable() -{ - return detail::color_mode::status().disable(); -} - -inline bool should_color() -{ - return detail::color_mode::status().should_color(); -} - -} // color_ansi - -// ANSI escape sequence is the only and default colorization method currently -namespace color = color_ansi; - -} // toml -#endif// TOML11_COLOR_HPP diff --git a/3party/toml11/toml/combinator.hpp b/3party/toml11/toml/combinator.hpp deleted file mode 100644 index 33ecca1..0000000 --- a/3party/toml11/toml/combinator.hpp +++ /dev/null @@ -1,306 +0,0 @@ -// Copyright Toru Niina 2017. -// Distributed under the MIT License. -#ifndef TOML11_COMBINATOR_HPP -#define TOML11_COMBINATOR_HPP -#include -#include -#include - -#include -#include -#include -#include -#include - -#include "region.hpp" -#include "result.hpp" -#include "traits.hpp" -#include "utility.hpp" - -// they scans characters and returns region if it matches to the condition. -// when they fail, it does not change the location. -// in lexer.hpp, these are used. - -namespace toml -{ -namespace detail -{ - -// to output character as an error message. -inline std::string show_char(const char c) -{ - // It suppresses an error that occurs only in Debug mode of MSVC++ on Windows. - // I'm not completely sure but they check the value of char to be in the - // range [0, 256) and some of the COMPLETELY VALID utf-8 character sometimes - // has negative value (if char has sign). So here it re-interprets c as - // unsigned char through pointer. In general, converting pointer to a - // pointer that has different type cause UB, but `(signed|unsigned)?char` - // are one of the exceptions. Converting pointer only to char and std::byte - // (c++17) are valid. - if(std::isgraph(*reinterpret_cast(std::addressof(c)))) - { - return std::string(1, c); - } - else - { - std::array buf; - buf.fill('\0'); - const auto r = std::snprintf( - buf.data(), buf.size(), "0x%02x", static_cast(c) & 0xFF); - (void) r; // Unused variable warning - assert(r == static_cast(buf.size()) - 1); - return std::string(buf.data()); - } -} - -template -struct character -{ - static constexpr char target = C; - - static result - invoke(location& loc) - { - if(loc.iter() == loc.end()) {return none();} - const auto first = loc.iter(); - - const char c = *(loc.iter()); - if(c != target) - { - return none(); - } - loc.advance(); // update location - - return ok(region(loc, first, loc.iter())); - } -}; -template -constexpr char character::target; - -// closed interval [Low, Up]. both Low and Up are included. -template -struct in_range -{ - // assuming ascii part of UTF-8... - static_assert(Low <= Up, "lower bound should be less than upper bound."); - - static constexpr char upper = Up; - static constexpr char lower = Low; - - static result - invoke(location& loc) - { - if(loc.iter() == loc.end()) {return none();} - const auto first = loc.iter(); - - const char c = *(loc.iter()); - if(c < lower || upper < c) - { - return none(); - } - - loc.advance(); - return ok(region(loc, first, loc.iter())); - } -}; -template constexpr char in_range::upper; -template constexpr char in_range::lower; - -// keep iterator if `Combinator` matches. otherwise, increment `iter` by 1 char. -// for detecting invalid characters, like control sequences in toml string. -template -struct exclude -{ - static result - invoke(location& loc) - { - if(loc.iter() == loc.end()) {return none();} - auto first = loc.iter(); - - auto rslt = Combinator::invoke(loc); - if(rslt.is_ok()) - { - loc.reset(first); - return none(); - } - loc.reset(std::next(first)); // XXX maybe loc.advance() is okay but... - return ok(region(loc, first, loc.iter())); - } -}; - -// increment `iter`, if matches. otherwise, just return empty string. -template -struct maybe -{ - static result - invoke(location& loc) - { - const auto rslt = Combinator::invoke(loc); - if(rslt.is_ok()) - { - return rslt; - } - return ok(region(loc)); - } -}; - -template -struct sequence; - -template -struct sequence -{ - static result - invoke(location& loc) - { - const auto first = loc.iter(); - auto rslt = Head::invoke(loc); - if(rslt.is_err()) - { - loc.reset(first); - return none(); - } - return sequence::invoke(loc, std::move(rslt.unwrap()), first); - } - - // called from the above function only, recursively. - template - static result - invoke(location& loc, region reg, Iterator first) - { - const auto rslt = Head::invoke(loc); - if(rslt.is_err()) - { - loc.reset(first); - return none(); - } - reg += rslt.unwrap(); // concat regions - return sequence::invoke(loc, std::move(reg), first); - } -}; - -template -struct sequence -{ - // would be called from sequence::invoke only. - template - static result - invoke(location& loc, region reg, Iterator first) - { - const auto rslt = Head::invoke(loc); - if(rslt.is_err()) - { - loc.reset(first); - return none(); - } - reg += rslt.unwrap(); // concat regions - return ok(reg); - } -}; - -template -struct either; - -template -struct either -{ - static result - invoke(location& loc) - { - const auto rslt = Head::invoke(loc); - if(rslt.is_ok()) {return rslt;} - return either::invoke(loc); - } -}; -template -struct either -{ - static result - invoke(location& loc) - { - return Head::invoke(loc); - } -}; - -template -struct repeat; - -template struct exactly{}; -template struct at_least{}; -struct unlimited{}; - -template -struct repeat> -{ - static result - invoke(location& loc) - { - region retval(loc); - const auto first = loc.iter(); - for(std::size_t i=0; i -struct repeat> -{ - static result - invoke(location& loc) - { - region retval(loc); - - const auto first = loc.iter(); - for(std::size_t i=0; i -struct repeat -{ - static result - invoke(location& loc) - { - region retval(loc); - while(true) - { - auto rslt = T::invoke(loc); - if(rslt.is_err()) - { - return ok(std::move(retval)); - } - retval += rslt.unwrap(); - } - } -}; - -} // detail -} // toml -#endif// TOML11_COMBINATOR_HPP diff --git a/3party/toml11/toml/comments.hpp b/3party/toml11/toml/comments.hpp deleted file mode 100644 index c0f66f3..0000000 --- a/3party/toml11/toml/comments.hpp +++ /dev/null @@ -1,484 +0,0 @@ -// Copyright Toru Niina 2019. -// Distributed under the MIT License. -#ifndef TOML11_COMMENTS_HPP -#define TOML11_COMMENTS_HPP -#include -#include -#include -#include -#include -#include -#include - -#ifdef TOML11_PRESERVE_COMMENTS_BY_DEFAULT -# define TOML11_DEFAULT_COMMENT_STRATEGY ::toml::preserve_comments -#else -# define TOML11_DEFAULT_COMMENT_STRATEGY ::toml::discard_comments -#endif - -// This file provides mainly two classes, `preserve_comments` and `discard_comments`. -// Those two are a container that have the same interface as `std::vector` -// but bahaves in the opposite way. `preserve_comments` is just the same as -// `std::vector` and each `std::string` corresponds to a comment line. -// Conversely, `discard_comments` discards all the strings and ignores everything -// assigned in it. `discard_comments` is always empty and you will encounter an -// error whenever you access to the element. -namespace toml -{ -struct discard_comments; // forward decl - -// use it in the following way -// -// const toml::basic_value data = -// toml::parse("example.toml"); -// -// the interface is almost the same as std::vector. -struct preserve_comments -{ - // `container_type` is not provided in discard_comments. - // do not use this inner-type in a generic code. - using container_type = std::vector; - - using size_type = container_type::size_type; - using difference_type = container_type::difference_type; - using value_type = container_type::value_type; - using reference = container_type::reference; - using const_reference = container_type::const_reference; - using pointer = container_type::pointer; - using const_pointer = container_type::const_pointer; - using iterator = container_type::iterator; - using const_iterator = container_type::const_iterator; - using reverse_iterator = container_type::reverse_iterator; - using const_reverse_iterator = container_type::const_reverse_iterator; - - preserve_comments() = default; - ~preserve_comments() = default; - preserve_comments(preserve_comments const&) = default; - preserve_comments(preserve_comments &&) = default; - preserve_comments& operator=(preserve_comments const&) = default; - preserve_comments& operator=(preserve_comments &&) = default; - - explicit preserve_comments(const std::vector& c): comments(c){} - explicit preserve_comments(std::vector&& c) - : comments(std::move(c)) - {} - preserve_comments& operator=(const std::vector& c) - { - comments = c; - return *this; - } - preserve_comments& operator=(std::vector&& c) - { - comments = std::move(c); - return *this; - } - - explicit preserve_comments(const discard_comments&) {} - - explicit preserve_comments(size_type n): comments(n) {} - preserve_comments(size_type n, const std::string& x): comments(n, x) {} - preserve_comments(std::initializer_list x): comments(x) {} - template - preserve_comments(InputIterator first, InputIterator last) - : comments(first, last) - {} - - template - void assign(InputIterator first, InputIterator last) {comments.assign(first, last);} - void assign(std::initializer_list ini) {comments.assign(ini);} - void assign(size_type n, const std::string& val) {comments.assign(n, val);} - - // Related to the issue #97. - // - // It is known that `std::vector::insert` and `std::vector::erase` in - // the standard library implementation included in GCC 4.8.5 takes - // `std::vector::iterator` instead of `std::vector::const_iterator`. - // Because of the const-correctness, we cannot convert a `const_iterator` to - // an `iterator`. It causes compilation error in GCC 4.8.5. -#if defined(__GNUC__) && defined(__GNUC_MINOR__) && defined(__GNUC_PATCHLEVEL__) && !defined(__clang__) -# if (__GNUC__ * 10000 + __GNUC_MINOR__ * 100 + __GNUC_PATCHLEVEL__) <= 40805 -# define TOML11_WORKAROUND_GCC_4_8_X_STANDARD_LIBRARY_IMPLEMENTATION -# endif -#endif - -#ifdef TOML11_WORKAROUND_GCC_4_8_X_STANDARD_LIBRARY_IMPLEMENTATION - iterator insert(iterator p, const std::string& x) - { - return comments.insert(p, x); - } - iterator insert(iterator p, std::string&& x) - { - return comments.insert(p, std::move(x)); - } - void insert(iterator p, size_type n, const std::string& x) - { - return comments.insert(p, n, x); - } - template - void insert(iterator p, InputIterator first, InputIterator last) - { - return comments.insert(p, first, last); - } - void insert(iterator p, std::initializer_list ini) - { - return comments.insert(p, ini); - } - - template - iterator emplace(iterator p, Ts&& ... args) - { - return comments.emplace(p, std::forward(args)...); - } - - iterator erase(iterator pos) {return comments.erase(pos);} - iterator erase(iterator first, iterator last) - { - return comments.erase(first, last); - } -#else - iterator insert(const_iterator p, const std::string& x) - { - return comments.insert(p, x); - } - iterator insert(const_iterator p, std::string&& x) - { - return comments.insert(p, std::move(x)); - } - iterator insert(const_iterator p, size_type n, const std::string& x) - { - return comments.insert(p, n, x); - } - template - iterator insert(const_iterator p, InputIterator first, InputIterator last) - { - return comments.insert(p, first, last); - } - iterator insert(const_iterator p, std::initializer_list ini) - { - return comments.insert(p, ini); - } - - template - iterator emplace(const_iterator p, Ts&& ... args) - { - return comments.emplace(p, std::forward(args)...); - } - - iterator erase(const_iterator pos) {return comments.erase(pos);} - iterator erase(const_iterator first, const_iterator last) - { - return comments.erase(first, last); - } -#endif - - void swap(preserve_comments& other) {comments.swap(other.comments);} - - void push_back(const std::string& v) {comments.push_back(v);} - void push_back(std::string&& v) {comments.push_back(std::move(v));} - void pop_back() {comments.pop_back();} - - template - void emplace_back(Ts&& ... args) {comments.emplace_back(std::forward(args)...);} - - void clear() {comments.clear();} - - size_type size() const noexcept {return comments.size();} - size_type max_size() const noexcept {return comments.max_size();} - size_type capacity() const noexcept {return comments.capacity();} - bool empty() const noexcept {return comments.empty();} - - void reserve(size_type n) {comments.reserve(n);} - void resize(size_type n) {comments.resize(n);} - void resize(size_type n, const std::string& c) {comments.resize(n, c);} - void shrink_to_fit() {comments.shrink_to_fit();} - - reference operator[](const size_type n) noexcept {return comments[n];} - const_reference operator[](const size_type n) const noexcept {return comments[n];} - reference at(const size_type n) {return comments.at(n);} - const_reference at(const size_type n) const {return comments.at(n);} - reference front() noexcept {return comments.front();} - const_reference front() const noexcept {return comments.front();} - reference back() noexcept {return comments.back();} - const_reference back() const noexcept {return comments.back();} - - pointer data() noexcept {return comments.data();} - const_pointer data() const noexcept {return comments.data();} - - iterator begin() noexcept {return comments.begin();} - iterator end() noexcept {return comments.end();} - const_iterator begin() const noexcept {return comments.begin();} - const_iterator end() const noexcept {return comments.end();} - const_iterator cbegin() const noexcept {return comments.cbegin();} - const_iterator cend() const noexcept {return comments.cend();} - - reverse_iterator rbegin() noexcept {return comments.rbegin();} - reverse_iterator rend() noexcept {return comments.rend();} - const_reverse_iterator rbegin() const noexcept {return comments.rbegin();} - const_reverse_iterator rend() const noexcept {return comments.rend();} - const_reverse_iterator crbegin() const noexcept {return comments.crbegin();} - const_reverse_iterator crend() const noexcept {return comments.crend();} - - friend bool operator==(const preserve_comments&, const preserve_comments&); - friend bool operator!=(const preserve_comments&, const preserve_comments&); - friend bool operator< (const preserve_comments&, const preserve_comments&); - friend bool operator<=(const preserve_comments&, const preserve_comments&); - friend bool operator> (const preserve_comments&, const preserve_comments&); - friend bool operator>=(const preserve_comments&, const preserve_comments&); - - friend void swap(preserve_comments&, std::vector&); - friend void swap(std::vector&, preserve_comments&); - - private: - - container_type comments; -}; - -inline bool operator==(const preserve_comments& lhs, const preserve_comments& rhs) {return lhs.comments == rhs.comments;} -inline bool operator!=(const preserve_comments& lhs, const preserve_comments& rhs) {return lhs.comments != rhs.comments;} -inline bool operator< (const preserve_comments& lhs, const preserve_comments& rhs) {return lhs.comments < rhs.comments;} -inline bool operator<=(const preserve_comments& lhs, const preserve_comments& rhs) {return lhs.comments <= rhs.comments;} -inline bool operator> (const preserve_comments& lhs, const preserve_comments& rhs) {return lhs.comments > rhs.comments;} -inline bool operator>=(const preserve_comments& lhs, const preserve_comments& rhs) {return lhs.comments >= rhs.comments;} - -inline void swap(preserve_comments& lhs, preserve_comments& rhs) -{ - lhs.swap(rhs); - return; -} -inline void swap(preserve_comments& lhs, std::vector& rhs) -{ - lhs.comments.swap(rhs); - return; -} -inline void swap(std::vector& lhs, preserve_comments& rhs) -{ - lhs.swap(rhs.comments); - return; -} - -template -std::basic_ostream& -operator<<(std::basic_ostream& os, const preserve_comments& com) -{ - for(const auto& c : com) - { - os << '#' << c << '\n'; - } - return os; -} - -namespace detail -{ - -// To provide the same interface with `preserve_comments`, `discard_comments` -// should have an iterator. But it does not contain anything, so we need to -// add an iterator that points nothing. -// -// It always points null, so DO NOT unwrap this iterator. It always crashes -// your program. -template -struct empty_iterator -{ - using value_type = T; - using reference_type = typename std::conditional::type; - using pointer_type = typename std::conditional::type; - using difference_type = std::ptrdiff_t; - using iterator_category = std::random_access_iterator_tag; - - empty_iterator() = default; - ~empty_iterator() = default; - empty_iterator(empty_iterator const&) = default; - empty_iterator(empty_iterator &&) = default; - empty_iterator& operator=(empty_iterator const&) = default; - empty_iterator& operator=(empty_iterator &&) = default; - - // DO NOT call these operators. - reference_type operator*() const noexcept {std::terminate();} - pointer_type operator->() const noexcept {return nullptr;} - reference_type operator[](difference_type) const noexcept {return this->operator*();} - - // These operators do nothing. - empty_iterator& operator++() noexcept {return *this;} - empty_iterator operator++(int) noexcept {return *this;} - empty_iterator& operator--() noexcept {return *this;} - empty_iterator operator--(int) noexcept {return *this;} - - empty_iterator& operator+=(difference_type) noexcept {return *this;} - empty_iterator& operator-=(difference_type) noexcept {return *this;} - - empty_iterator operator+(difference_type) const noexcept {return *this;} - empty_iterator operator-(difference_type) const noexcept {return *this;} -}; - -template -bool operator==(const empty_iterator&, const empty_iterator&) noexcept {return true;} -template -bool operator!=(const empty_iterator&, const empty_iterator&) noexcept {return false;} -template -bool operator< (const empty_iterator&, const empty_iterator&) noexcept {return false;} -template -bool operator<=(const empty_iterator&, const empty_iterator&) noexcept {return true;} -template -bool operator> (const empty_iterator&, const empty_iterator&) noexcept {return false;} -template -bool operator>=(const empty_iterator&, const empty_iterator&) noexcept {return true;} - -template -typename empty_iterator::difference_type -operator-(const empty_iterator&, const empty_iterator&) noexcept {return 0;} - -template -empty_iterator -operator+(typename empty_iterator::difference_type, const empty_iterator& rhs) noexcept {return rhs;} -template -empty_iterator -operator+(const empty_iterator& lhs, typename empty_iterator::difference_type) noexcept {return lhs;} - -} // detail - -// The default comment type. It discards all the comments. It requires only one -// byte to contain, so the memory footprint is smaller than preserve_comments. -// -// It just ignores `push_back`, `insert`, `erase`, and any other modifications. -// IT always returns size() == 0, the iterator taken by `begin()` is always the -// same as that of `end()`, and accessing through `operator[]` or iterators -// always causes a segmentation fault. DO NOT access to the element of this. -// -// Why this is chose as the default type is because the last version (2.x.y) -// does not contain any comments in a value. To minimize the impact on the -// efficiency, this is chosen as a default. -// -// To reduce the memory footprint, later we can try empty base optimization (EBO). -struct discard_comments -{ - using size_type = std::size_t; - using difference_type = std::ptrdiff_t; - using value_type = std::string; - using reference = std::string&; - using const_reference = std::string const&; - using pointer = std::string*; - using const_pointer = std::string const*; - using iterator = detail::empty_iterator; - using const_iterator = detail::empty_iterator; - using reverse_iterator = detail::empty_iterator; - using const_reverse_iterator = detail::empty_iterator; - - discard_comments() = default; - ~discard_comments() = default; - discard_comments(discard_comments const&) = default; - discard_comments(discard_comments &&) = default; - discard_comments& operator=(discard_comments const&) = default; - discard_comments& operator=(discard_comments &&) = default; - - explicit discard_comments(const std::vector&) noexcept {} - explicit discard_comments(std::vector&&) noexcept {} - discard_comments& operator=(const std::vector&) noexcept {return *this;} - discard_comments& operator=(std::vector&&) noexcept {return *this;} - - explicit discard_comments(const preserve_comments&) noexcept {} - - explicit discard_comments(size_type) noexcept {} - discard_comments(size_type, const std::string&) noexcept {} - discard_comments(std::initializer_list) noexcept {} - template - discard_comments(InputIterator, InputIterator) noexcept {} - - template - void assign(InputIterator, InputIterator) noexcept {} - void assign(std::initializer_list) noexcept {} - void assign(size_type, const std::string&) noexcept {} - - iterator insert(const_iterator, const std::string&) {return iterator{};} - iterator insert(const_iterator, std::string&&) {return iterator{};} - iterator insert(const_iterator, size_type, const std::string&) {return iterator{};} - template - iterator insert(const_iterator, InputIterator, InputIterator) {return iterator{};} - iterator insert(const_iterator, std::initializer_list) {return iterator{};} - - template - iterator emplace(const_iterator, Ts&& ...) {return iterator{};} - iterator erase(const_iterator) {return iterator{};} - iterator erase(const_iterator, const_iterator) {return iterator{};} - - void swap(discard_comments&) {return;} - - void push_back(const std::string&) {return;} - void push_back(std::string&& ) {return;} - void pop_back() {return;} - - template - void emplace_back(Ts&& ...) {return;} - - void clear() {return;} - - size_type size() const noexcept {return 0;} - size_type max_size() const noexcept {return 0;} - size_type capacity() const noexcept {return 0;} - bool empty() const noexcept {return true;} - - void reserve(size_type) {return;} - void resize(size_type) {return;} - void resize(size_type, const std::string&) {return;} - void shrink_to_fit() {return;} - - // DO NOT access to the element of this container. This container is always - // empty, so accessing through operator[], front/back, data causes address - // error. - - reference operator[](const size_type) noexcept {never_call("toml::discard_comment::operator[]");} - const_reference operator[](const size_type) const noexcept {never_call("toml::discard_comment::operator[]");} - reference at(const size_type) {throw std::out_of_range("toml::discard_comment is always empty.");} - const_reference at(const size_type) const {throw std::out_of_range("toml::discard_comment is always empty.");} - reference front() noexcept {never_call("toml::discard_comment::front");} - const_reference front() const noexcept {never_call("toml::discard_comment::front");} - reference back() noexcept {never_call("toml::discard_comment::back");} - const_reference back() const noexcept {never_call("toml::discard_comment::back");} - - pointer data() noexcept {return nullptr;} - const_pointer data() const noexcept {return nullptr;} - - iterator begin() noexcept {return iterator{};} - iterator end() noexcept {return iterator{};} - const_iterator begin() const noexcept {return const_iterator{};} - const_iterator end() const noexcept {return const_iterator{};} - const_iterator cbegin() const noexcept {return const_iterator{};} - const_iterator cend() const noexcept {return const_iterator{};} - - reverse_iterator rbegin() noexcept {return iterator{};} - reverse_iterator rend() noexcept {return iterator{};} - const_reverse_iterator rbegin() const noexcept {return const_iterator{};} - const_reverse_iterator rend() const noexcept {return const_iterator{};} - const_reverse_iterator crbegin() const noexcept {return const_iterator{};} - const_reverse_iterator crend() const noexcept {return const_iterator{};} - - private: - - [[noreturn]] static void never_call(const char *const this_function) - { -#ifdef __has_builtin -# if __has_builtin(__builtin_unreachable) - __builtin_unreachable(); -# endif -#endif - throw std::logic_error{this_function}; - } -}; - -inline bool operator==(const discard_comments&, const discard_comments&) noexcept {return true;} -inline bool operator!=(const discard_comments&, const discard_comments&) noexcept {return false;} -inline bool operator< (const discard_comments&, const discard_comments&) noexcept {return false;} -inline bool operator<=(const discard_comments&, const discard_comments&) noexcept {return true;} -inline bool operator> (const discard_comments&, const discard_comments&) noexcept {return false;} -inline bool operator>=(const discard_comments&, const discard_comments&) noexcept {return true;} - -inline void swap(const discard_comments&, const discard_comments&) noexcept {return;} - -template -std::basic_ostream& -operator<<(std::basic_ostream& os, const discard_comments&) -{ - return os; -} - -} // toml11 -#endif// TOML11_COMMENTS_HPP diff --git a/3party/toml11/toml/datetime.hpp b/3party/toml11/toml/datetime.hpp deleted file mode 100644 index 83d04ba..0000000 --- a/3party/toml11/toml/datetime.hpp +++ /dev/null @@ -1,631 +0,0 @@ -// Copyright Toru Niina 2017. -// Distributed under the MIT License. -#ifndef TOML11_DATETIME_HPP -#define TOML11_DATETIME_HPP -#include -#include -#include - -#include -#include -#include -#include -#include - -namespace toml -{ - -// To avoid non-threadsafe std::localtime. In C11 (not C++11!), localtime_s is -// provided in the absolutely same purpose, but C++11 is actually not compatible -// with C11. We need to dispatch the function depending on the OS. -namespace detail -{ -// TODO: find more sophisticated way to handle this -#if defined(_MSC_VER) -inline std::tm localtime_s(const std::time_t* src) -{ - std::tm dst; - const auto result = ::localtime_s(&dst, src); - if (result) { throw std::runtime_error("localtime_s failed."); } - return dst; -} -inline std::tm gmtime_s(const std::time_t* src) -{ - std::tm dst; - const auto result = ::gmtime_s(&dst, src); - if (result) { throw std::runtime_error("gmtime_s failed."); } - return dst; -} -#elif (defined(_POSIX_C_SOURCE) && _POSIX_C_SOURCE >= 1) || defined(_XOPEN_SOURCE) || defined(_BSD_SOURCE) || defined(_SVID_SOURCE) || defined(_POSIX_SOURCE) -inline std::tm localtime_s(const std::time_t* src) -{ - std::tm dst; - const auto result = ::localtime_r(src, &dst); - if (!result) { throw std::runtime_error("localtime_r failed."); } - return dst; -} -inline std::tm gmtime_s(const std::time_t* src) -{ - std::tm dst; - const auto result = ::gmtime_r(src, &dst); - if (!result) { throw std::runtime_error("gmtime_r failed."); } - return dst; -} -#else // fallback. not threadsafe -inline std::tm localtime_s(const std::time_t* src) -{ - const auto result = std::localtime(src); - if (!result) { throw std::runtime_error("localtime failed."); } - return *result; -} -inline std::tm gmtime_s(const std::time_t* src) -{ - const auto result = std::gmtime(src); - if (!result) { throw std::runtime_error("gmtime failed."); } - return *result; -} -#endif -} // detail - -enum class month_t : std::uint8_t -{ - Jan = 0, - Feb = 1, - Mar = 2, - Apr = 3, - May = 4, - Jun = 5, - Jul = 6, - Aug = 7, - Sep = 8, - Oct = 9, - Nov = 10, - Dec = 11 -}; - -struct local_date -{ - std::int16_t year{}; // A.D. (like, 2018) - std::uint8_t month{}; // [0, 11] - std::uint8_t day{}; // [1, 31] - - local_date(int y, month_t m, int d) - : year (static_cast(y)), - month(static_cast(m)), - day (static_cast(d)) - {} - - explicit local_date(const std::tm& t) - : year (static_cast(t.tm_year + 1900)), - month(static_cast(t.tm_mon)), - day (static_cast(t.tm_mday)) - {} - - explicit local_date(const std::chrono::system_clock::time_point& tp) - { - const auto t = std::chrono::system_clock::to_time_t(tp); - const auto time = detail::localtime_s(&t); - *this = local_date(time); - } - - explicit local_date(const std::time_t t) - : local_date(std::chrono::system_clock::from_time_t(t)) - {} - - operator std::chrono::system_clock::time_point() const - { - // std::mktime returns date as local time zone. no conversion needed - std::tm t; - t.tm_sec = 0; - t.tm_min = 0; - t.tm_hour = 0; - t.tm_mday = static_cast(this->day); - t.tm_mon = static_cast(this->month); - t.tm_year = static_cast(this->year) - 1900; - t.tm_wday = 0; // the value will be ignored - t.tm_yday = 0; // the value will be ignored - t.tm_isdst = -1; - return std::chrono::system_clock::from_time_t(std::mktime(&t)); - } - - operator std::time_t() const - { - return std::chrono::system_clock::to_time_t( - std::chrono::system_clock::time_point(*this)); - } - - local_date() = default; - ~local_date() = default; - local_date(local_date const&) = default; - local_date(local_date&&) = default; - local_date& operator=(local_date const&) = default; - local_date& operator=(local_date&&) = default; -}; - -inline bool operator==(const local_date& lhs, const local_date& rhs) -{ - return std::make_tuple(lhs.year, lhs.month, lhs.day) == - std::make_tuple(rhs.year, rhs.month, rhs.day); -} -inline bool operator!=(const local_date& lhs, const local_date& rhs) -{ - return !(lhs == rhs); -} -inline bool operator< (const local_date& lhs, const local_date& rhs) -{ - return std::make_tuple(lhs.year, lhs.month, lhs.day) < - std::make_tuple(rhs.year, rhs.month, rhs.day); -} -inline bool operator<=(const local_date& lhs, const local_date& rhs) -{ - return (lhs < rhs) || (lhs == rhs); -} -inline bool operator> (const local_date& lhs, const local_date& rhs) -{ - return !(lhs <= rhs); -} -inline bool operator>=(const local_date& lhs, const local_date& rhs) -{ - return !(lhs < rhs); -} - -template -std::basic_ostream& -operator<<(std::basic_ostream& os, const local_date& date) -{ - os << std::setfill('0') << std::setw(4) << static_cast(date.year ) << '-'; - os << std::setfill('0') << std::setw(2) << static_cast(date.month) + 1 << '-'; - os << std::setfill('0') << std::setw(2) << static_cast(date.day ) ; - return os; -} - -struct local_time -{ - std::uint8_t hour{}; // [0, 23] - std::uint8_t minute{}; // [0, 59] - std::uint8_t second{}; // [0, 60] - std::uint16_t millisecond{}; // [0, 999] - std::uint16_t microsecond{}; // [0, 999] - std::uint16_t nanosecond{}; // [0, 999] - - local_time(int h, int m, int s, - int ms = 0, int us = 0, int ns = 0) - : hour (static_cast(h)), - minute(static_cast(m)), - second(static_cast(s)), - millisecond(static_cast(ms)), - microsecond(static_cast(us)), - nanosecond (static_cast(ns)) - {} - - explicit local_time(const std::tm& t) - : hour (static_cast(t.tm_hour)), - minute(static_cast(t.tm_min)), - second(static_cast(t.tm_sec)), - millisecond(0), microsecond(0), nanosecond(0) - {} - - template - explicit local_time(const std::chrono::duration& t) - { - const auto h = std::chrono::duration_cast(t); - this->hour = static_cast(h.count()); - const auto t2 = t - h; - const auto m = std::chrono::duration_cast(t2); - this->minute = static_cast(m.count()); - const auto t3 = t2 - m; - const auto s = std::chrono::duration_cast(t3); - this->second = static_cast(s.count()); - const auto t4 = t3 - s; - const auto ms = std::chrono::duration_cast(t4); - this->millisecond = static_cast(ms.count()); - const auto t5 = t4 - ms; - const auto us = std::chrono::duration_cast(t5); - this->microsecond = static_cast(us.count()); - const auto t6 = t5 - us; - const auto ns = std::chrono::duration_cast(t6); - this->nanosecond = static_cast(ns.count()); - } - - operator std::chrono::nanoseconds() const - { - return std::chrono::nanoseconds (this->nanosecond) + - std::chrono::microseconds(this->microsecond) + - std::chrono::milliseconds(this->millisecond) + - std::chrono::seconds(this->second) + - std::chrono::minutes(this->minute) + - std::chrono::hours(this->hour); - } - - local_time() = default; - ~local_time() = default; - local_time(local_time const&) = default; - local_time(local_time&&) = default; - local_time& operator=(local_time const&) = default; - local_time& operator=(local_time&&) = default; -}; - -inline bool operator==(const local_time& lhs, const local_time& rhs) -{ - return std::make_tuple(lhs.hour, lhs.minute, lhs.second, lhs.millisecond, lhs.microsecond, lhs.nanosecond) == - std::make_tuple(rhs.hour, rhs.minute, rhs.second, rhs.millisecond, rhs.microsecond, rhs.nanosecond); -} -inline bool operator!=(const local_time& lhs, const local_time& rhs) -{ - return !(lhs == rhs); -} -inline bool operator< (const local_time& lhs, const local_time& rhs) -{ - return std::make_tuple(lhs.hour, lhs.minute, lhs.second, lhs.millisecond, lhs.microsecond, lhs.nanosecond) < - std::make_tuple(rhs.hour, rhs.minute, rhs.second, rhs.millisecond, rhs.microsecond, rhs.nanosecond); -} -inline bool operator<=(const local_time& lhs, const local_time& rhs) -{ - return (lhs < rhs) || (lhs == rhs); -} -inline bool operator> (const local_time& lhs, const local_time& rhs) -{ - return !(lhs <= rhs); -} -inline bool operator>=(const local_time& lhs, const local_time& rhs) -{ - return !(lhs < rhs); -} - -template -std::basic_ostream& -operator<<(std::basic_ostream& os, const local_time& time) -{ - os << std::setfill('0') << std::setw(2) << static_cast(time.hour ) << ':'; - os << std::setfill('0') << std::setw(2) << static_cast(time.minute) << ':'; - os << std::setfill('0') << std::setw(2) << static_cast(time.second); - if(time.millisecond != 0 || time.microsecond != 0 || time.nanosecond != 0) - { - os << '.'; - os << std::setfill('0') << std::setw(3) << static_cast(time.millisecond); - if(time.microsecond != 0 || time.nanosecond != 0) - { - os << std::setfill('0') << std::setw(3) << static_cast(time.microsecond); - if(time.nanosecond != 0) - { - os << std::setfill('0') << std::setw(3) << static_cast(time.nanosecond); - } - } - } - return os; -} - -struct time_offset -{ - std::int8_t hour{}; // [-12, 12] - std::int8_t minute{}; // [-59, 59] - - time_offset(int h, int m) - : hour (static_cast(h)), - minute(static_cast(m)) - {} - - operator std::chrono::minutes() const - { - return std::chrono::minutes(this->minute) + - std::chrono::hours(this->hour); - } - - time_offset() = default; - ~time_offset() = default; - time_offset(time_offset const&) = default; - time_offset(time_offset&&) = default; - time_offset& operator=(time_offset const&) = default; - time_offset& operator=(time_offset&&) = default; -}; - -inline bool operator==(const time_offset& lhs, const time_offset& rhs) -{ - return std::make_tuple(lhs.hour, lhs.minute) == - std::make_tuple(rhs.hour, rhs.minute); -} -inline bool operator!=(const time_offset& lhs, const time_offset& rhs) -{ - return !(lhs == rhs); -} -inline bool operator< (const time_offset& lhs, const time_offset& rhs) -{ - return std::make_tuple(lhs.hour, lhs.minute) < - std::make_tuple(rhs.hour, rhs.minute); -} -inline bool operator<=(const time_offset& lhs, const time_offset& rhs) -{ - return (lhs < rhs) || (lhs == rhs); -} -inline bool operator> (const time_offset& lhs, const time_offset& rhs) -{ - return !(lhs <= rhs); -} -inline bool operator>=(const time_offset& lhs, const time_offset& rhs) -{ - return !(lhs < rhs); -} - -template -std::basic_ostream& -operator<<(std::basic_ostream& os, const time_offset& offset) -{ - if(offset.hour == 0 && offset.minute == 0) - { - os << 'Z'; - return os; - } - int minute = static_cast(offset.hour) * 60 + offset.minute; - if(minute < 0){os << '-'; minute = std::abs(minute);} else {os << '+';} - os << std::setfill('0') << std::setw(2) << minute / 60 << ':'; - os << std::setfill('0') << std::setw(2) << minute % 60; - return os; -} - -struct local_datetime -{ - local_date date{}; - local_time time{}; - - local_datetime(local_date d, local_time t): date(d), time(t) {} - - explicit local_datetime(const std::tm& t): date(t), time(t){} - - explicit local_datetime(const std::chrono::system_clock::time_point& tp) - { - const auto t = std::chrono::system_clock::to_time_t(tp); - std::tm ltime = detail::localtime_s(&t); - - this->date = local_date(ltime); - this->time = local_time(ltime); - - // std::tm lacks subsecond information, so diff between tp and tm - // can be used to get millisecond & microsecond information. - const auto t_diff = tp - - std::chrono::system_clock::from_time_t(std::mktime(<ime)); - this->time.millisecond = static_cast( - std::chrono::duration_cast(t_diff).count()); - this->time.microsecond = static_cast( - std::chrono::duration_cast(t_diff).count()); - this->time.nanosecond = static_cast( - std::chrono::duration_cast(t_diff).count()); - } - - explicit local_datetime(const std::time_t t) - : local_datetime(std::chrono::system_clock::from_time_t(t)) - {} - - operator std::chrono::system_clock::time_point() const - { - using internal_duration = - typename std::chrono::system_clock::time_point::duration; - - // Normally DST begins at A.M. 3 or 4. If we re-use conversion operator - // of local_date and local_time independently, the conversion fails if - // it is the day when DST begins or ends. Since local_date considers the - // time is 00:00 A.M. and local_time does not consider DST because it - // does not have any date information. We need to consider both date and - // time information at the same time to convert it correctly. - - std::tm t; - t.tm_sec = static_cast(this->time.second); - t.tm_min = static_cast(this->time.minute); - t.tm_hour = static_cast(this->time.hour); - t.tm_mday = static_cast(this->date.day); - t.tm_mon = static_cast(this->date.month); - t.tm_year = static_cast(this->date.year) - 1900; - t.tm_wday = 0; // the value will be ignored - t.tm_yday = 0; // the value will be ignored - t.tm_isdst = -1; - - // std::mktime returns date as local time zone. no conversion needed - auto dt = std::chrono::system_clock::from_time_t(std::mktime(&t)); - dt += std::chrono::duration_cast( - std::chrono::milliseconds(this->time.millisecond) + - std::chrono::microseconds(this->time.microsecond) + - std::chrono::nanoseconds (this->time.nanosecond)); - return dt; - } - - operator std::time_t() const - { - return std::chrono::system_clock::to_time_t( - std::chrono::system_clock::time_point(*this)); - } - - local_datetime() = default; - ~local_datetime() = default; - local_datetime(local_datetime const&) = default; - local_datetime(local_datetime&&) = default; - local_datetime& operator=(local_datetime const&) = default; - local_datetime& operator=(local_datetime&&) = default; -}; - -inline bool operator==(const local_datetime& lhs, const local_datetime& rhs) -{ - return std::make_tuple(lhs.date, lhs.time) == - std::make_tuple(rhs.date, rhs.time); -} -inline bool operator!=(const local_datetime& lhs, const local_datetime& rhs) -{ - return !(lhs == rhs); -} -inline bool operator< (const local_datetime& lhs, const local_datetime& rhs) -{ - return std::make_tuple(lhs.date, lhs.time) < - std::make_tuple(rhs.date, rhs.time); -} -inline bool operator<=(const local_datetime& lhs, const local_datetime& rhs) -{ - return (lhs < rhs) || (lhs == rhs); -} -inline bool operator> (const local_datetime& lhs, const local_datetime& rhs) -{ - return !(lhs <= rhs); -} -inline bool operator>=(const local_datetime& lhs, const local_datetime& rhs) -{ - return !(lhs < rhs); -} - -template -std::basic_ostream& -operator<<(std::basic_ostream& os, const local_datetime& dt) -{ - os << dt.date << 'T' << dt.time; - return os; -} - -struct offset_datetime -{ - local_date date{}; - local_time time{}; - time_offset offset{}; - - offset_datetime(local_date d, local_time t, time_offset o) - : date(d), time(t), offset(o) - {} - offset_datetime(const local_datetime& dt, time_offset o) - : date(dt.date), time(dt.time), offset(o) - {} - explicit offset_datetime(const local_datetime& ld) - : date(ld.date), time(ld.time), offset(get_local_offset(nullptr)) - // use the current local timezone offset - {} - explicit offset_datetime(const std::chrono::system_clock::time_point& tp) - : offset(0, 0) // use gmtime - { - const auto timet = std::chrono::system_clock::to_time_t(tp); - const auto tm = detail::gmtime_s(&timet); - this->date = local_date(tm); - this->time = local_time(tm); - } - explicit offset_datetime(const std::time_t& t) - : offset(0, 0) // use gmtime - { - const auto tm = detail::gmtime_s(&t); - this->date = local_date(tm); - this->time = local_time(tm); - } - explicit offset_datetime(const std::tm& t) - : offset(0, 0) // assume gmtime - { - this->date = local_date(t); - this->time = local_time(t); - } - - operator std::chrono::system_clock::time_point() const - { - // get date-time - using internal_duration = - typename std::chrono::system_clock::time_point::duration; - - // first, convert it to local date-time information in the same way as - // local_datetime does. later we will use time_t to adjust time offset. - std::tm t; - t.tm_sec = static_cast(this->time.second); - t.tm_min = static_cast(this->time.minute); - t.tm_hour = static_cast(this->time.hour); - t.tm_mday = static_cast(this->date.day); - t.tm_mon = static_cast(this->date.month); - t.tm_year = static_cast(this->date.year) - 1900; - t.tm_wday = 0; // the value will be ignored - t.tm_yday = 0; // the value will be ignored - t.tm_isdst = -1; - const std::time_t tp_loc = std::mktime(std::addressof(t)); - - auto tp = std::chrono::system_clock::from_time_t(tp_loc); - tp += std::chrono::duration_cast( - std::chrono::milliseconds(this->time.millisecond) + - std::chrono::microseconds(this->time.microsecond) + - std::chrono::nanoseconds (this->time.nanosecond)); - - // Since mktime uses local time zone, it should be corrected. - // `12:00:00+09:00` means `03:00:00Z`. So mktime returns `03:00:00Z` if - // we are in `+09:00` timezone. To represent `12:00:00Z` there, we need - // to add `+09:00` to `03:00:00Z`. - // Here, it uses the time_t converted from date-time info to handle - // daylight saving time. - const auto ofs = get_local_offset(std::addressof(tp_loc)); - tp += std::chrono::hours (ofs.hour); - tp += std::chrono::minutes(ofs.minute); - - // We got `12:00:00Z` by correcting local timezone applied by mktime. - // Then we will apply the offset. Let's say `12:00:00-08:00` is given. - // And now, we have `12:00:00Z`. `12:00:00-08:00` means `20:00:00Z`. - // So we need to subtract the offset. - tp -= std::chrono::minutes(this->offset); - return tp; - } - - operator std::time_t() const - { - return std::chrono::system_clock::to_time_t( - std::chrono::system_clock::time_point(*this)); - } - - offset_datetime() = default; - ~offset_datetime() = default; - offset_datetime(offset_datetime const&) = default; - offset_datetime(offset_datetime&&) = default; - offset_datetime& operator=(offset_datetime const&) = default; - offset_datetime& operator=(offset_datetime&&) = default; - - private: - - static time_offset get_local_offset(const std::time_t* tp) - { - // get local timezone with the same date-time information as mktime - const auto t = detail::localtime_s(tp); - - std::array buf; - const auto result = std::strftime(buf.data(), 6, "%z", &t); // +hhmm\0 - if(result != 5) - { - throw std::runtime_error("toml::offset_datetime: cannot obtain " - "timezone information of current env"); - } - const int ofs = std::atoi(buf.data()); - const int ofs_h = ofs / 100; - const int ofs_m = ofs - (ofs_h * 100); - return time_offset(ofs_h, ofs_m); - } -}; - -inline bool operator==(const offset_datetime& lhs, const offset_datetime& rhs) -{ - return std::make_tuple(lhs.date, lhs.time, lhs.offset) == - std::make_tuple(rhs.date, rhs.time, rhs.offset); -} -inline bool operator!=(const offset_datetime& lhs, const offset_datetime& rhs) -{ - return !(lhs == rhs); -} -inline bool operator< (const offset_datetime& lhs, const offset_datetime& rhs) -{ - return std::make_tuple(lhs.date, lhs.time, lhs.offset) < - std::make_tuple(rhs.date, rhs.time, rhs.offset); -} -inline bool operator<=(const offset_datetime& lhs, const offset_datetime& rhs) -{ - return (lhs < rhs) || (lhs == rhs); -} -inline bool operator> (const offset_datetime& lhs, const offset_datetime& rhs) -{ - return !(lhs <= rhs); -} -inline bool operator>=(const offset_datetime& lhs, const offset_datetime& rhs) -{ - return !(lhs < rhs); -} - -template -std::basic_ostream& -operator<<(std::basic_ostream& os, const offset_datetime& dt) -{ - os << dt.date << 'T' << dt.time << dt.offset; - return os; -} - -}//toml -#endif// TOML11_DATETIME diff --git a/3party/toml11/toml/exception.hpp b/3party/toml11/toml/exception.hpp deleted file mode 100644 index 06bfe6e..0000000 --- a/3party/toml11/toml/exception.hpp +++ /dev/null @@ -1,83 +0,0 @@ -// Copyright Toru Niina 2017. -// Distributed under the MIT License. -#ifndef TOML11_EXCEPTION_HPP -#define TOML11_EXCEPTION_HPP - -#include -#include -#include - -#include - -#include "source_location.hpp" - -namespace toml -{ - -struct file_io_error : public std::runtime_error -{ - public: - file_io_error(int errnum, const std::string& msg, const std::string& fname) - : std::runtime_error(msg + " \"" + fname + "\": errno = " + std::to_string(errnum)), - errno_(errnum) - {} - - int get_errno() const noexcept {return errno_;} - - private: - int errno_; -}; - -struct exception : public std::exception -{ - public: - explicit exception(const source_location& loc): loc_(loc) {} - virtual ~exception() noexcept override = default; - virtual const char* what() const noexcept override {return "";} - virtual source_location const& location() const noexcept {return loc_;} - - protected: - source_location loc_; -}; - -struct syntax_error : public toml::exception -{ - public: - explicit syntax_error(const std::string& what_arg, const source_location& loc) - : exception(loc), what_(what_arg) - {} - virtual ~syntax_error() noexcept override = default; - virtual const char* what() const noexcept override {return what_.c_str();} - - protected: - std::string what_; -}; - -struct type_error : public toml::exception -{ - public: - explicit type_error(const std::string& what_arg, const source_location& loc) - : exception(loc), what_(what_arg) - {} - virtual ~type_error() noexcept override = default; - virtual const char* what() const noexcept override {return what_.c_str();} - - protected: - std::string what_; -}; - -struct internal_error : public toml::exception -{ - public: - explicit internal_error(const std::string& what_arg, const source_location& loc) - : exception(loc), what_(what_arg) - {} - virtual ~internal_error() noexcept override = default; - virtual const char* what() const noexcept override {return what_.c_str();} - - protected: - std::string what_; -}; - -} // toml -#endif // TOML_EXCEPTION diff --git a/3party/toml11/toml/from.hpp b/3party/toml11/toml/from.hpp deleted file mode 100644 index 10815ca..0000000 --- a/3party/toml11/toml/from.hpp +++ /dev/null @@ -1,19 +0,0 @@ -// Copyright Toru Niina 2019. -// Distributed under the MIT License. -#ifndef TOML11_FROM_HPP -#define TOML11_FROM_HPP - -namespace toml -{ - -template -struct from; -// { -// static T from_toml(const toml::value& v) -// { -// // User-defined conversions ... -// } -// }; - -} // toml -#endif // TOML11_FROM_HPP diff --git a/3party/toml11/toml/get.hpp b/3party/toml11/toml/get.hpp deleted file mode 100644 index ac1b354..0000000 --- a/3party/toml11/toml/get.hpp +++ /dev/null @@ -1,1154 +0,0 @@ -// Copyright Toru Niina 2017. -// Distributed under the MIT License. -#ifndef TOML11_GET_HPP -#define TOML11_GET_HPP -#include - -#include "from.hpp" -#include "result.hpp" -#include "value.hpp" - -namespace toml -{ - -// ============================================================================ -// exact toml::* type - -template class M, template class V> -detail::enable_if_t>::value, T> & -get(basic_value& v) -{ - return v.template cast>::value>(); -} - -template class M, template class V> -detail::enable_if_t>::value, T> const& -get(const basic_value& v) -{ - return v.template cast>::value>(); -} - -template class M, template class V> -detail::enable_if_t>::value, T> -get(basic_value&& v) -{ - return T(std::move(v).template cast>::value>()); -} - -// ============================================================================ -// T == toml::value; identity transformation. - -template class M, template class V> -inline detail::enable_if_t>::value, T>& -get(basic_value& v) -{ - return v; -} - -template class M, template class V> -inline detail::enable_if_t>::value, T> const& -get(const basic_value& v) -{ - return v; -} - -template class M, template class V> -inline detail::enable_if_t>::value, T> -get(basic_value&& v) -{ - return basic_value(std::move(v)); -} - -// ============================================================================ -// T == toml::basic_value; basic_value -> basic_value - -template class M, template class V> -inline detail::enable_if_t, - detail::negation>> - >::value, T> -get(const basic_value& v) -{ - return T(v); -} - -// ============================================================================ -// integer convertible from toml::Integer - -template class M, template class V> -inline detail::enable_if_t, // T is integral - detail::negation>, // but not bool - detail::negation< // but not toml::integer - detail::is_exact_toml_type>> - >::value, T> -get(const basic_value& v) -{ - return static_cast(v.as_integer()); -} - -// ============================================================================ -// floating point convertible from toml::Float - -template class M, template class V> -inline detail::enable_if_t, // T is floating_point - detail::negation< // but not toml::floating - detail::is_exact_toml_type>> - >::value, T> -get(const basic_value& v) -{ - return static_cast(v.as_floating()); -} - -// ============================================================================ -// std::string; toml uses its own toml::string, but it should be convertible to -// std::string seamlessly - -template class M, template class V> -inline detail::enable_if_t::value, std::string>& -get(basic_value& v) -{ - return v.as_string().str; -} - -template class M, template class V> -inline detail::enable_if_t::value, std::string> const& -get(const basic_value& v) -{ - return v.as_string().str; -} - -template class M, template class V> -inline detail::enable_if_t::value, std::string> -get(basic_value&& v) -{ - return std::string(std::move(v.as_string().str)); -} - -// ============================================================================ -// std::string_view - -#if defined(TOML11_USING_STRING_VIEW) && TOML11_USING_STRING_VIEW>0 -template class M, template class V> -inline detail::enable_if_t::value, std::string_view> -get(const basic_value& v) -{ - return std::string_view(v.as_string().str); -} -#endif - -// ============================================================================ -// std::chrono::duration from toml::local_time. - -template class M, template class V> -inline detail::enable_if_t::value, T> -get(const basic_value& v) -{ - return std::chrono::duration_cast( - std::chrono::nanoseconds(v.as_local_time())); -} - -// ============================================================================ -// std::chrono::system_clock::time_point from toml::datetime variants - -template class M, template class V> -inline detail::enable_if_t< - std::is_same::value, T> -get(const basic_value& v) -{ - switch(v.type()) - { - case value_t::local_date: - { - return std::chrono::system_clock::time_point(v.as_local_date()); - } - case value_t::local_datetime: - { - return std::chrono::system_clock::time_point(v.as_local_datetime()); - } - case value_t::offset_datetime: - { - return std::chrono::system_clock::time_point(v.as_offset_datetime()); - } - default: - { - throw type_error(detail::format_underline("toml::value: " - "bad_cast to std::chrono::system_clock::time_point", { - {v.location(), concat_to_string("the actual type is ", v.type())} - }), v.location()); - } - } -} - -// ============================================================================ -// forward declaration to use this recursively. ignore this and go ahead. - -// array-like type with push_back(value) method -template class M, template class V> -detail::enable_if_t, // T is a container - detail::has_push_back_method, // T::push_back(value) works - detail::negation< // but not toml::array - detail::is_exact_toml_type>> - >::value, T> -get(const basic_value&); - -// array-like type without push_back(value) method -template class M, template class V> -detail::enable_if_t, // T is a container - detail::negation>, // w/o push_back(...) - detail::negation>, // T does not have special conversion - detail::negation< // not toml::array - detail::is_exact_toml_type>> - >::value, T> -get(const basic_value&); - -// std::pair -template class M, template class V> -detail::enable_if_t::value, T> -get(const basic_value&); - -// std::tuple -template class M, template class V> -detail::enable_if_t::value, T> -get(const basic_value&); - -// map-like classes -template class M, template class V> -detail::enable_if_t, // T is map - detail::negation< // but not toml::table - detail::is_exact_toml_type>> - >::value, T> -get(const basic_value&); - -// T.from_toml(v) -template class M, template class V> -detail::enable_if_t>>, - detail::has_from_toml_method, // but has from_toml(toml::value) - std::is_default_constructible // and default constructible - >::value, T> -get(const basic_value&); - -// toml::from::from_toml(v) -template class M, template class V> -detail::enable_if_t::value, T> -get(const basic_value&); - -template class M, template class V> -detail::enable_if_t::value, T> -get(basic_value&); - -// T(const toml::value&) and T is not toml::basic_value, -// and it does not have `from` nor `from_toml`. -template class M, template class V> -detail::enable_if_t>, - std::is_constructible&>, - detail::negation>, - detail::negation> - >::value, T> -get(const basic_value&); - -template class M, template class V> -detail::enable_if_t>, - std::is_constructible&>, - detail::negation>, - detail::negation> - >::value, T> -get(basic_value&); - -// ============================================================================ -// array-like types; most likely STL container, like std::vector, etc. - -template class M, template class V> -detail::enable_if_t, // T is a container - detail::has_push_back_method, // container.push_back(elem) works - detail::negation< // but not toml::array - detail::is_exact_toml_type>> - >::value, T> -get(const basic_value& v) -{ - using value_type = typename T::value_type; - const auto& ary = v.as_array(); - - T container; - try_reserve(container, ary.size()); - - for(const auto& elem : ary) - { - container.push_back(get(elem)); - } - return container; -} - -// ============================================================================ -// std::forward_list does not have push_back, insert, or emplace. -// It has insert_after, emplace_after, push_front. - -template class M, template class V> -detail::enable_if_t::value, T> -get(const basic_value& v) -{ - using value_type = typename T::value_type; - T container; - for(const auto& elem : v.as_array()) - { - container.push_front(get(elem)); - } - container.reverse(); - return container; -} - -// ============================================================================ -// array-like types, without push_back(). most likely [std|boost]::array. - -template class M, template class V> -detail::enable_if_t, // T is a container - detail::negation>, // w/o push_back - detail::negation>, // T does not have special conversion - detail::negation< // T is not toml::array - detail::is_exact_toml_type>> - >::value, T> -get(const basic_value& v) -{ - using value_type = typename T::value_type; - const auto& ar = v.as_array(); - - T container; - if(ar.size() != container.size()) - { - throw std::out_of_range(detail::format_underline(concat_to_string( - "toml::get: specified container size is ", container.size(), - " but there are ", ar.size(), " elements in toml array."), { - {v.location(), "here"} - })); - } - for(std::size_t i=0; i(ar[i]); - } - return container; -} - -// ============================================================================ -// std::pair. - -template class M, template class V> -detail::enable_if_t::value, T> -get(const basic_value& v) -{ - using first_type = typename T::first_type; - using second_type = typename T::second_type; - - const auto& ar = v.as_array(); - if(ar.size() != 2) - { - throw std::out_of_range(detail::format_underline(concat_to_string( - "toml::get: specified std::pair but there are ", ar.size(), - " elements in toml array."), {{v.location(), "here"}})); - } - return std::make_pair(::toml::get(ar.at(0)), - ::toml::get(ar.at(1))); -} - -// ============================================================================ -// std::tuple. - -namespace detail -{ -template -T get_tuple_impl(const Array& a, index_sequence) -{ - return std::make_tuple( - ::toml::get::type>(a.at(I))...); -} -} // detail - -template class M, template class V> -detail::enable_if_t::value, T> -get(const basic_value& v) -{ - const auto& ar = v.as_array(); - if(ar.size() != std::tuple_size::value) - { - throw std::out_of_range(detail::format_underline(concat_to_string( - "toml::get: specified std::tuple with ", - std::tuple_size::value, " elements, but there are ", ar.size(), - " elements in toml array."), {{v.location(), "here"}})); - } - return detail::get_tuple_impl(ar, - detail::make_index_sequence::value>{}); -} - -// ============================================================================ -// map-like types; most likely STL map, like std::map or std::unordered_map. - -template class M, template class V> -detail::enable_if_t, // T is map - detail::negation< // but not toml::array - detail::is_exact_toml_type>> - >::value, T> -get(const basic_value& v) -{ - using key_type = typename T::key_type; - using mapped_type = typename T::mapped_type; - static_assert(std::is_convertible::value, - "toml::get only supports map type of which key_type is " - "convertible from std::string."); - T map; - for(const auto& kv : v.as_table()) - { - map.emplace(key_type(kv.first), get(kv.second)); - } - return map; -} - -// ============================================================================ -// user-defined, but compatible types. - -template class M, template class V> -detail::enable_if_t>>, - detail::has_from_toml_method, // but has from_toml(toml::value) memfn - std::is_default_constructible // and default constructible - >::value, T> -get(const basic_value& v) -{ - T ud; - ud.from_toml(v); - return ud; -} -template class M, template class V> -detail::enable_if_t::value, T> -get(const basic_value& v) -{ - return ::toml::from::from_toml(v); -} -template class M, template class V> -detail::enable_if_t::value, T> -get(basic_value& v) -{ - return ::toml::from::from_toml(v); -} - -template class M, template class V> -detail::enable_if_t>, // T is not a toml::value - std::is_constructible&>, // T is constructible from toml::value - detail::negation>, // and T does not have T.from_toml(v); - detail::negation> // and T does not have toml::from{}; - >::value, T> -get(const basic_value& v) -{ - return T(v); -} - -template class M, template class V> -detail::enable_if_t>, // T is not a toml::value - std::is_constructible&>, // T is constructible from toml::value - detail::negation>, // and T does not have T.from_toml(v); - detail::negation> // and T does not have toml::from{}; - >::value, T> -get(basic_value& v) -{ - return T(v); -} - -// ============================================================================ -// find - -// ---------------------------------------------------------------------------- -// these overloads do not require to set T. and returns value itself. -template class M, template class V> -basic_value const& find(const basic_value& v, const key& ky) -{ - const auto& tab = v.as_table(); - if(tab.count(ky) == 0) - { - detail::throw_key_not_found_error(v, ky); - } - return tab.at(ky); -} -template class M, template class V> -basic_value& find(basic_value& v, const key& ky) -{ - auto& tab = v.as_table(); - if(tab.count(ky) == 0) - { - detail::throw_key_not_found_error(v, ky); - } - return tab.at(ky); -} -template class M, template class V> -basic_value find(basic_value&& v, const key& ky) -{ - typename basic_value::table_type tab = std::move(v).as_table(); - if(tab.count(ky) == 0) - { - detail::throw_key_not_found_error(v, ky); - } - return basic_value(std::move(tab.at(ky))); -} - -// ---------------------------------------------------------------------------- -// find(value, idx) -template class M, template class V> -basic_value const& -find(const basic_value& v, const std::size_t idx) -{ - const auto& ary = v.as_array(); - if(ary.size() <= idx) - { - throw std::out_of_range(detail::format_underline(concat_to_string( - "index ", idx, " is out of range"), {{v.location(), "in this array"}})); - } - return ary.at(idx); -} -template class M, template class V> -basic_value& find(basic_value& v, const std::size_t idx) -{ - auto& ary = v.as_array(); - if(ary.size() <= idx) - { - throw std::out_of_range(detail::format_underline(concat_to_string( - "index ", idx, " is out of range"), {{v.location(), "in this array"}})); - } - return ary.at(idx); -} -template class M, template class V> -basic_value find(basic_value&& v, const std::size_t idx) -{ - auto& ary = v.as_array(); - if(ary.size() <= idx) - { - throw std::out_of_range(detail::format_underline(concat_to_string( - "index ", idx, " is out of range"), {{v.location(), "in this array"}})); - } - return basic_value(std::move(ary.at(idx))); -} - -// ---------------------------------------------------------------------------- -// find(value, key); - -template class M, template class V> -decltype(::toml::get(std::declval const&>())) -find(const basic_value& v, const key& ky) -{ - const auto& tab = v.as_table(); - if(tab.count(ky) == 0) - { - detail::throw_key_not_found_error(v, ky); - } - return ::toml::get(tab.at(ky)); -} - -template class M, template class V> -decltype(::toml::get(std::declval&>())) -find(basic_value& v, const key& ky) -{ - auto& tab = v.as_table(); - if(tab.count(ky) == 0) - { - detail::throw_key_not_found_error(v, ky); - } - return ::toml::get(tab.at(ky)); -} - -template class M, template class V> -decltype(::toml::get(std::declval&&>())) -find(basic_value&& v, const key& ky) -{ - typename basic_value::table_type tab = std::move(v).as_table(); - if(tab.count(ky) == 0) - { - detail::throw_key_not_found_error(v, ky); - } - return ::toml::get(std::move(tab.at(ky))); -} - -// ---------------------------------------------------------------------------- -// find(value, idx) -template class M, template class V> -decltype(::toml::get(std::declval const&>())) -find(const basic_value& v, const std::size_t idx) -{ - const auto& ary = v.as_array(); - if(ary.size() <= idx) - { - throw std::out_of_range(detail::format_underline(concat_to_string( - "index ", idx, " is out of range"), {{v.location(), "in this array"}})); - } - return ::toml::get(ary.at(idx)); -} -template class M, template class V> -decltype(::toml::get(std::declval&>())) -find(basic_value& v, const std::size_t idx) -{ - auto& ary = v.as_array(); - if(ary.size() <= idx) - { - throw std::out_of_range(detail::format_underline(concat_to_string( - "index ", idx, " is out of range"), {{v.location(), "in this array"}})); - } - return ::toml::get(ary.at(idx)); -} -template class M, template class V> -decltype(::toml::get(std::declval&&>())) -find(basic_value&& v, const std::size_t idx) -{ - typename basic_value::array_type ary = std::move(v).as_array(); - if(ary.size() <= idx) - { - throw std::out_of_range(detail::format_underline(concat_to_string( - "index ", idx, " is out of range"), {{v.location(), "in this array"}})); - } - return ::toml::get(std::move(ary.at(idx))); -} - -// -------------------------------------------------------------------------- -// toml::find(toml::value, toml::key, Ts&& ... keys) - -namespace detail -{ -// It suppresses warnings by -Wsign-conversion. Let's say we have the following -// code. -// ```cpp -// const auto x = toml::find(data, "array", 0); -// ``` -// Here, the type of literal number `0` is `int`. `int` is a signed integer. -// `toml::find` takes `std::size_t` as an index. So it causes implicit sign -// conversion and `-Wsign-conversion` warns about it. Using `0u` instead of `0` -// suppresses the warning, but it makes user code messy. -// To suppress this warning, we need to be aware of type conversion caused -// by `toml::find(v, key1, key2, ... keys)`. But the thing is that the types of -// keys can be any combination of {string-like, size_t-like}. Of course we can't -// write down all the combinations. Thus we need to use some function that -// recognize the type of argument and cast it into `std::string` or -// `std::size_t` depending on the context. -// `key_cast` does the job. It has 2 overloads. One is invoked when the -// argument type is an integer and cast the argument into `std::size_t`. The -// other is invoked when the argument type is not an integer, possibly one of -// std::string, const char[N] or const char*, and construct std::string from -// the argument. -// `toml::find(v, k1, k2, ... ks)` uses `key_cast` before passing `ks` to -// `toml::find(v, k)` to suppress -Wsign-conversion. - -template -enable_if_t>, - negation, bool>>>::value, std::size_t> -key_cast(T&& v) noexcept -{ - return std::size_t(v); -} -template -enable_if_t>, - negation, bool>>>>::value, std::string> -key_cast(T&& v) noexcept -{ - return std::string(std::forward(v)); -} -} // detail - -template class M, template class V, - typename Key1, typename Key2, typename ... Keys> -const basic_value& -find(const basic_value& v, Key1&& k1, Key2&& k2, Keys&& ... keys) -{ - return ::toml::find(::toml::find(v, detail::key_cast(k1)), - detail::key_cast(k2), std::forward(keys)...); -} -template class M, template class V, - typename Key1, typename Key2, typename ... Keys> -basic_value& -find(basic_value& v, Key1&& k1, Key2&& k2, Keys&& ... keys) -{ - return ::toml::find(::toml::find(v, detail::key_cast(k1)), - detail::key_cast(k2), std::forward(keys)...); -} -template class M, template class V, - typename Key1, typename Key2, typename ... Keys> -basic_value -find(basic_value&& v, Key1&& k1, Key2&& k2, Keys&& ... keys) -{ - return ::toml::find(::toml::find(std::move(v), std::forward(k1)), - detail::key_cast(k2), std::forward(keys)...); -} - -template class M, template class V, - typename Key1, typename Key2, typename ... Keys> -decltype(::toml::get(std::declval&>())) -find(const basic_value& v, Key1&& k1, Key2&& k2, Keys&& ... keys) -{ - return ::toml::find(::toml::find(v, detail::key_cast(k1)), - detail::key_cast(k2), std::forward(keys)...); -} -template class M, template class V, - typename Key1, typename Key2, typename ... Keys> -decltype(::toml::get(std::declval&>())) -find(basic_value& v, Key1&& k1, Key2&& k2, Keys&& ... keys) -{ - return ::toml::find(::toml::find(v, detail::key_cast(k1)), - detail::key_cast(k2), std::forward(keys)...); -} -template class M, template class V, - typename Key1, typename Key2, typename ... Keys> -decltype(::toml::get(std::declval&&>())) -find(basic_value&& v, Key1&& k1, Key2&& k2, Keys&& ... keys) -{ - return ::toml::find(::toml::find(std::move(v), detail::key_cast(k1)), - detail::key_cast(k2), std::forward(keys)...); -} - -// ============================================================================ -// get_or(value, fallback) - -template class M, template class V> -basic_value const& -get_or(const basic_value& v, const basic_value&) -{ - return v; -} -template class M, template class V> -basic_value& -get_or(basic_value& v, basic_value&) -{ - return v; -} -template class M, template class V> -basic_value -get_or(basic_value&& v, basic_value&&) -{ - return v; -} - -// ---------------------------------------------------------------------------- -// specialization for the exact toml types (return type becomes lvalue ref) - -template class M, template class V> -detail::enable_if_t< - detail::is_exact_toml_type>::value, T> const& -get_or(const basic_value& v, const T& opt) -{ - try - { - return get>(v); - } - catch(...) - { - return opt; - } -} -template class M, template class V> -detail::enable_if_t< - detail::is_exact_toml_type>::value, T>& -get_or(basic_value& v, T& opt) -{ - try - { - return get>(v); - } - catch(...) - { - return opt; - } -} -template class M, template class V> -detail::enable_if_t, - basic_value>::value, detail::remove_cvref_t> -get_or(basic_value&& v, T&& opt) -{ - try - { - return get>(std::move(v)); - } - catch(...) - { - return detail::remove_cvref_t(std::forward(opt)); - } -} - -// ---------------------------------------------------------------------------- -// specialization for std::string (return type becomes lvalue ref) - -template class M, template class V> -detail::enable_if_t, std::string>::value, - std::string> const& -get_or(const basic_value& v, const T& opt) -{ - try - { - return v.as_string().str; - } - catch(...) - { - return opt; - } -} -template class M, template class V> -detail::enable_if_t::value, std::string>& -get_or(basic_value& v, T& opt) -{ - try - { - return v.as_string().str; - } - catch(...) - { - return opt; - } -} -template class M, template class V> -detail::enable_if_t< - std::is_same, std::string>::value, std::string> -get_or(basic_value&& v, T&& opt) -{ - try - { - return std::move(v.as_string().str); - } - catch(...) - { - return std::string(std::forward(opt)); - } -} - -// ---------------------------------------------------------------------------- -// specialization for string literal - -template class M, template class V> -detail::enable_if_t::type>::value, std::string> -get_or(const basic_value& v, T&& opt) -{ - try - { - return std::move(v.as_string().str); - } - catch(...) - { - return std::string(std::forward(opt)); - } -} - -// ---------------------------------------------------------------------------- -// others (require type conversion and return type cannot be lvalue reference) - -template class M, template class V> -detail::enable_if_t, - basic_value>>, - detail::negation>>, - detail::negation::type>> - >::value, detail::remove_cvref_t> -get_or(const basic_value& v, T&& opt) -{ - try - { - return get>(v); - } - catch(...) - { - return detail::remove_cvref_t(std::forward(opt)); - } -} - -// =========================================================================== -// find_or(value, key, fallback) - -template class M, template class V> -basic_value const& -find_or(const basic_value& v, const key& ky, - const basic_value& opt) -{ - if(!v.is_table()) {return opt;} - const auto& tab = v.as_table(); - if(tab.count(ky) == 0) {return opt;} - return tab.at(ky); -} - -template class M, template class V> -basic_value& -find_or(basic_value& v, const toml::key& ky, basic_value& opt) -{ - if(!v.is_table()) {return opt;} - auto& tab = v.as_table(); - if(tab.count(ky) == 0) {return opt;} - return tab.at(ky); -} - -template class M, template class V> -basic_value -find_or(basic_value&& v, const toml::key& ky, basic_value&& opt) -{ - if(!v.is_table()) {return opt;} - auto tab = std::move(v).as_table(); - if(tab.count(ky) == 0) {return opt;} - return basic_value(std::move(tab.at(ky))); -} - -// --------------------------------------------------------------------------- -// exact types (return type can be a reference) -template class M, template class V> -detail::enable_if_t< - detail::is_exact_toml_type>::value, T> const& -find_or(const basic_value& v, const key& ky, const T& opt) -{ - if(!v.is_table()) {return opt;} - const auto& tab = v.as_table(); - if(tab.count(ky) == 0) {return opt;} - return get_or(tab.at(ky), opt); -} - -template class M, template class V> -detail::enable_if_t< - detail::is_exact_toml_type>::value, T>& -find_or(basic_value& v, const toml::key& ky, T& opt) -{ - if(!v.is_table()) {return opt;} - auto& tab = v.as_table(); - if(tab.count(ky) == 0) {return opt;} - return get_or(tab.at(ky), opt); -} - -template class M, template class V> -detail::enable_if_t< - detail::is_exact_toml_type>::value, - detail::remove_cvref_t> -find_or(basic_value&& v, const toml::key& ky, T&& opt) -{ - if(!v.is_table()) {return std::forward(opt);} - auto tab = std::move(v).as_table(); - if(tab.count(ky) == 0) {return std::forward(opt);} - return get_or(std::move(tab.at(ky)), std::forward(opt)); -} - -// --------------------------------------------------------------------------- -// std::string (return type can be a reference) - -template class M, template class V> -detail::enable_if_t::value, std::string> const& -find_or(const basic_value& v, const key& ky, const T& opt) -{ - if(!v.is_table()) {return opt;} - const auto& tab = v.as_table(); - if(tab.count(ky) == 0) {return opt;} - return get_or(tab.at(ky), opt); -} -template class M, template class V> -detail::enable_if_t::value, std::string>& -find_or(basic_value& v, const toml::key& ky, T& opt) -{ - if(!v.is_table()) {return opt;} - auto& tab = v.as_table(); - if(tab.count(ky) == 0) {return opt;} - return get_or(tab.at(ky), opt); -} -template class M, template class V> -detail::enable_if_t::value, std::string> -find_or(basic_value&& v, const toml::key& ky, T&& opt) -{ - if(!v.is_table()) {return std::forward(opt);} - auto tab = std::move(v).as_table(); - if(tab.count(ky) == 0) {return std::forward(opt);} - return get_or(std::move(tab.at(ky)), std::forward(opt)); -} - -// --------------------------------------------------------------------------- -// string literal (deduced as std::string) -template class M, template class V> -detail::enable_if_t< - detail::is_string_literal::type>::value, - std::string> -find_or(const basic_value& v, const toml::key& ky, T&& opt) -{ - if(!v.is_table()) {return std::string(opt);} - const auto& tab = v.as_table(); - if(tab.count(ky) == 0) {return std::string(opt);} - return get_or(tab.at(ky), std::forward(opt)); -} - -// --------------------------------------------------------------------------- -// others (require type conversion and return type cannot be lvalue reference) -template class M, template class V> -detail::enable_if_t, basic_value>>, - // T is not std::string - detail::negation>>, - // T is not a string literal - detail::negation::type>> - >::value, detail::remove_cvref_t> -find_or(const basic_value& v, const toml::key& ky, T&& opt) -{ - if(!v.is_table()) {return std::forward(opt);} - const auto& tab = v.as_table(); - if(tab.count(ky) == 0) {return std::forward(opt);} - return get_or(tab.at(ky), std::forward(opt)); -} - -// --------------------------------------------------------------------------- -// recursive find-or with type deduction (find_or(value, keys, opt)) - -template 1), std::nullptr_t> = nullptr> - // here we need to add SFINAE in the template parameter to avoid - // infinite recursion in type deduction on gcc -auto find_or(Value&& v, const toml::key& ky, Ks&& ... keys) - -> decltype(find_or(std::forward(v), ky, detail::last_one(std::forward(keys)...))) -{ - if(!v.is_table()) - { - return detail::last_one(std::forward(keys)...); - } - auto&& tab = std::forward(v).as_table(); - if(tab.count(ky) == 0) - { - return detail::last_one(std::forward(keys)...); - } - return find_or(std::forward(tab).at(ky), std::forward(keys)...); -} - -// --------------------------------------------------------------------------- -// recursive find_or with explicit type specialization, find_or(value, keys...) - -template 1), std::nullptr_t> = nullptr> - // here we need to add SFINAE in the template parameter to avoid - // infinite recursion in type deduction on gcc -auto find_or(Value&& v, const toml::key& ky, Ks&& ... keys) - -> decltype(find_or(std::forward(v), ky, detail::last_one(std::forward(keys)...))) -{ - if(!v.is_table()) - { - return detail::last_one(std::forward(keys)...); - } - auto&& tab = std::forward(v).as_table(); - if(tab.count(ky) == 0) - { - return detail::last_one(std::forward(keys)...); - } - return find_or(std::forward(tab).at(ky), std::forward(keys)...); -} - -// ============================================================================ -// expect - -template class M, template class V> -result expect(const basic_value& v) noexcept -{ - try - { - return ok(get(v)); - } - catch(const std::exception& e) - { - return err(e.what()); - } -} -template class M, template class V> -result -expect(const basic_value& v, const toml::key& k) noexcept -{ - try - { - return ok(find(v, k)); - } - catch(const std::exception& e) - { - return err(e.what()); - } -} - -} // toml -#endif// TOML11_GET diff --git a/3party/toml11/toml/into.hpp b/3party/toml11/toml/into.hpp deleted file mode 100644 index 7449556..0000000 --- a/3party/toml11/toml/into.hpp +++ /dev/null @@ -1,19 +0,0 @@ -// Copyright Toru Niina 2019. -// Distributed under the MIT License. -#ifndef TOML11_INTO_HPP -#define TOML11_INTO_HPP - -namespace toml -{ - -template -struct into; -// { -// static toml::value into_toml(const T& user_defined_type) -// { -// // User-defined conversions ... -// } -// }; - -} // toml -#endif // TOML11_INTO_HPP diff --git a/3party/toml11/toml/lexer.hpp b/3party/toml11/toml/lexer.hpp deleted file mode 100644 index 2a1ff2d..0000000 --- a/3party/toml11/toml/lexer.hpp +++ /dev/null @@ -1,294 +0,0 @@ -// Copyright Toru Niina 2017. -// Distributed under the MIT License. -#ifndef TOML11_LEXER_HPP -#define TOML11_LEXER_HPP -#include -#include -#include - -#include "combinator.hpp" - -namespace toml -{ -namespace detail -{ - -// these scans contents from current location in a container of char -// and extract a region that matches their own pattern. -// to see the implementation of each component, see combinator.hpp. - -using lex_wschar = either, character<'\t'>>; -using lex_ws = repeat>; -using lex_newline = either, - sequence, character<'\n'>>>; -using lex_lower = in_range<'a', 'z'>; -using lex_upper = in_range<'A', 'Z'>; -using lex_alpha = either; -using lex_digit = in_range<'0', '9'>; -using lex_nonzero = in_range<'1', '9'>; -using lex_oct_dig = in_range<'0', '7'>; -using lex_bin_dig = in_range<'0', '1'>; -using lex_hex_dig = either, in_range<'a', 'f'>>; - -using lex_hex_prefix = sequence, character<'x'>>; -using lex_oct_prefix = sequence, character<'o'>>; -using lex_bin_prefix = sequence, character<'b'>>; -using lex_underscore = character<'_'>; -using lex_plus = character<'+'>; -using lex_minus = character<'-'>; -using lex_sign = either; - -// digit | nonzero 1*(digit | _ digit) -using lex_unsigned_dec_int = either>, at_least<1>>>, - lex_digit>; -// (+|-)? unsigned_dec_int -using lex_dec_int = sequence, lex_unsigned_dec_int>; - -// hex_prefix hex_dig *(hex_dig | _ hex_dig) -using lex_hex_int = sequence>, unlimited>>>; -// oct_prefix oct_dig *(oct_dig | _ oct_dig) -using lex_oct_int = sequence>, unlimited>>>; -// bin_prefix bin_dig *(bin_dig | _ bin_dig) -using lex_bin_int = sequence>, unlimited>>>; - -// (dec_int | hex_int | oct_int | bin_int) -using lex_integer = either; - -// =========================================================================== - -using lex_inf = sequence, character<'n'>, character<'f'>>; -using lex_nan = sequence, character<'a'>, character<'n'>>; -using lex_special_float = sequence, either>; - -using lex_zero_prefixable_int = sequence>, unlimited>>; - -using lex_fractional_part = sequence, lex_zero_prefixable_int>; - -using lex_exponent_part = sequence, character<'E'>>, - maybe, lex_zero_prefixable_int>; - -using lex_float = either>>>>; - -// =========================================================================== - -using lex_true = sequence, character<'r'>, - character<'u'>, character<'e'>>; -using lex_false = sequence, character<'a'>, character<'l'>, - character<'s'>, character<'e'>>; -using lex_boolean = either; - -// =========================================================================== - -using lex_date_fullyear = repeat>; -using lex_date_month = repeat>; -using lex_date_mday = repeat>; -using lex_time_delim = either, character<'t'>, character<' '>>; -using lex_time_hour = repeat>; -using lex_time_minute = repeat>; -using lex_time_second = repeat>; -using lex_time_secfrac = sequence, - repeat>>; - -using lex_time_numoffset = sequence, character<'-'>>, - sequence, - lex_time_minute>>; -using lex_time_offset = either, character<'z'>, - lex_time_numoffset>; - -using lex_partial_time = sequence, - lex_time_minute, character<':'>, - lex_time_second, maybe>; -using lex_full_date = sequence, - lex_date_month, character<'-'>, - lex_date_mday>; -using lex_full_time = sequence; - -using lex_offset_date_time = sequence; -using lex_local_date_time = sequence; -using lex_local_date = lex_full_date; -using lex_local_time = lex_partial_time; - -// =========================================================================== - -using lex_quotation_mark = character<'"'>; -using lex_basic_unescaped = exclude, // 0x09 (tab) is allowed - in_range<0x0A, 0x1F>, - character<0x22>, character<0x5C>, - character<0x7F>>>; - -using lex_escape = character<'\\'>; -using lex_escape_unicode_short = sequence, - repeat>>; -using lex_escape_unicode_long = sequence, - repeat>>; -using lex_escape_seq_char = either, character<'\\'>, - character<'b'>, character<'f'>, - character<'n'>, character<'r'>, - character<'t'>, -#ifdef TOML11_USE_UNRELEASED_TOML_FEATURES - character<'e'>, // ESC (0x1B) -#endif - lex_escape_unicode_short, - lex_escape_unicode_long - >; -using lex_escaped = sequence; -using lex_basic_char = either; -using lex_basic_string = sequence, - lex_quotation_mark>; - -// After toml post-v0.5.0, it is explicitly clarified how quotes in ml-strings -// are allowed to be used. -// After this, the following strings are *explicitly* allowed. -// - One or two `"`s in a multi-line basic string is allowed wherever it is. -// - Three consecutive `"`s in a multi-line basic string is considered as a delimiter. -// - One or two `"`s can appear just before or after the delimiter. -// ```toml -// str4 = """Here are two quotation marks: "". Simple enough.""" -// str5 = """Here are three quotation marks: ""\".""" -// str6 = """Here are fifteen quotation marks: ""\"""\"""\"""\"""\".""" -// str7 = """"This," she said, "is just a pointless statement."""" -// ``` -// In the current implementation (v3.3.0), it is difficult to parse `str7` in -// the above example. It is difficult to recognize `"` at the end of string body -// collectly. It will be misunderstood as a `"""` delimiter and an additional, -// invalid `"`. Like this: -// ```console -// what(): [error] toml::parse_table: invalid line format -// --> hoge.toml -// | -// 13 | str7 = """"This," she said, "is just a pointless statement."""" -// | ^- expected newline, but got '"'. -// ``` -// As a quick workaround for this problem, `lex_ml_basic_string_delim` was -// split into two, `lex_ml_basic_string_open` and `lex_ml_basic_string_close`. -// `lex_ml_basic_string_open` allows only `"""`. `_close` allows 3-5 `"`s. -// In parse_ml_basic_string() function, the trailing `"`s will be attached to -// the string body. -// -using lex_ml_basic_string_delim = repeat>; -using lex_ml_basic_string_open = lex_ml_basic_string_delim; -using lex_ml_basic_string_close = sequence< - repeat>, - maybe, maybe - >; - -using lex_ml_basic_unescaped = exclude, // 0x09 is tab - in_range<0x0A, 0x1F>, - character<0x5C>, // backslash - character<0x7F>, // DEL - lex_ml_basic_string_delim>>; - -using lex_ml_basic_escaped_newline = sequence< - lex_escape, maybe, lex_newline, - repeat, unlimited>>; - -using lex_ml_basic_char = either; -using lex_ml_basic_body = repeat, - unlimited>; -using lex_ml_basic_string = sequence; - -using lex_literal_char = exclude, in_range<0x0A, 0x1F>, - character<0x7F>, character<0x27>>>; -using lex_apostrophe = character<'\''>; -using lex_literal_string = sequence, - lex_apostrophe>; - -// the same reason as above. -using lex_ml_literal_string_delim = repeat>; -using lex_ml_literal_string_open = lex_ml_literal_string_delim; -using lex_ml_literal_string_close = sequence< - repeat>, - maybe, maybe - >; - -using lex_ml_literal_char = exclude, - in_range<0x0A, 0x1F>, - character<0x7F>, - lex_ml_literal_string_delim>>; -using lex_ml_literal_body = repeat, - unlimited>; -using lex_ml_literal_string = sequence; - -using lex_string = either; - -// =========================================================================== -using lex_dot_sep = sequence, character<'.'>, maybe>; - -using lex_unquoted_key = repeat, character<'_'>>, - at_least<1>>; -using lex_quoted_key = either; -using lex_simple_key = either; -using lex_dotted_key = sequence, - at_least<1> - > - >; -using lex_key = either; - -using lex_keyval_sep = sequence, - character<'='>, - maybe>; - -using lex_std_table_open = character<'['>; -using lex_std_table_close = character<']'>; -using lex_std_table = sequence, - lex_key, - maybe, - lex_std_table_close>; - -using lex_array_table_open = sequence; -using lex_array_table_close = sequence; -using lex_array_table = sequence, - lex_key, - maybe, - lex_array_table_close>; - -using lex_utf8_1byte = in_range<0x00, 0x7F>; -using lex_utf8_2byte = sequence< - in_range<'\xC2', '\xDF'>, - in_range<'\x80', '\xBF'> - >; -using lex_utf8_3byte = sequence, in_range<'\xA0', '\xBF'>>, - sequence, in_range<'\x80', '\xBF'>>, - sequence, in_range<'\x80', '\x9F'>>, - sequence, in_range<'\x80', '\xBF'>> - >, in_range<'\x80', '\xBF'>>; -using lex_utf8_4byte = sequence, in_range<'\x90', '\xBF'>>, - sequence, in_range<'\x80', '\xBF'>>, - sequence, in_range<'\x80', '\x8F'>> - >, in_range<'\x80', '\xBF'>, in_range<'\x80', '\xBF'>>; -using lex_utf8_code = either< - lex_utf8_1byte, - lex_utf8_2byte, - lex_utf8_3byte, - lex_utf8_4byte - >; - -using lex_comment_start_symbol = character<'#'>; -using lex_non_eol_ascii = either, in_range<0x20, 0x7E>>; -using lex_comment = sequence, unlimited>>; - -} // detail -} // toml -#endif // TOML_LEXER_HPP diff --git a/3party/toml11/toml/literal.hpp b/3party/toml11/toml/literal.hpp deleted file mode 100644 index 5086a76..0000000 --- a/3party/toml11/toml/literal.hpp +++ /dev/null @@ -1,113 +0,0 @@ -// Copyright Toru Niina 2019. -// Distributed under the MIT License. -#ifndef TOML11_LITERAL_HPP -#define TOML11_LITERAL_HPP -#include "parser.hpp" - -namespace toml -{ -inline namespace literals -{ -inline namespace toml_literals -{ - -// implementation -inline ::toml::basic_value -literal_internal_impl(::toml::detail::location loc) -{ - using value_type = ::toml::basic_value< - TOML11_DEFAULT_COMMENT_STRATEGY, std::unordered_map, std::vector>; - // if there are some comments or empty lines, skip them. - using skip_line = ::toml::detail::repeat, - ::toml::detail::maybe<::toml::detail::lex_comment>, - ::toml::detail::lex_newline - >, ::toml::detail::at_least<1>>; - skip_line::invoke(loc); - - // if there are some whitespaces before a value, skip them. - using skip_ws = ::toml::detail::repeat< - ::toml::detail::lex_ws, ::toml::detail::at_least<1>>; - skip_ws::invoke(loc); - - // to distinguish arrays and tables, first check it is a table or not. - // - // "[1,2,3]"_toml; // this is an array - // "[table]"_toml; // a table that has an empty table named "table" inside. - // "[[1,2,3]]"_toml; // this is an array of arrays - // "[[table]]"_toml; // this is a table that has an array of tables inside. - // - // "[[1]]"_toml; // this can be both... (currently it becomes a table) - // "1 = [{}]"_toml; // this is a table that has an array of table named 1. - // "[[1,]]"_toml; // this is an array of arrays. - // "[[1],]"_toml; // this also. - - const auto the_front = loc.iter(); - - const bool is_table_key = ::toml::detail::lex_std_table::invoke(loc); - loc.reset(the_front); - - const bool is_aots_key = ::toml::detail::lex_array_table::invoke(loc); - loc.reset(the_front); - - // If it is neither a table-key or a array-of-table-key, it may be a value. - if(!is_table_key && !is_aots_key) - { - if(auto data = ::toml::detail::parse_value(loc, 0)) - { - return data.unwrap(); - } - } - - // Note that still it can be a table, because the literal might be something - // like the following. - // ```cpp - // R"( // c++11 raw string literals - // key = "value" - // int = 42 - // )"_toml; - // ``` - // It is a valid toml file. - // It should be parsed as if we parse a file with this content. - - if(auto data = ::toml::detail::parse_toml_file(loc)) - { - return data.unwrap(); - } - else // none of them. - { - throw ::toml::syntax_error(data.unwrap_err(), source_location(loc)); - } - -} - -inline ::toml::basic_value -operator"" _toml(const char* str, std::size_t len) -{ - ::toml::detail::location loc( - std::string("TOML literal encoded in a C++ code"), - std::vector(str, str + len)); - // literal length does not include the null character at the end. - return literal_internal_impl(std::move(loc)); -} - -// value of __cplusplus in C++2a/20 mode is not fixed yet along compilers. -// So here we use the feature test macro for `char8_t` itself. -#if defined(__cpp_char8_t) && __cpp_char8_t >= 201811L -// value of u8"" literal has been changed from char to char8_t and char8_t is -// NOT compatible to char -inline ::toml::basic_value -operator"" _toml(const char8_t* str, std::size_t len) -{ - ::toml::detail::location loc( - std::string("TOML literal encoded in a C++ code"), - std::vector(reinterpret_cast(str), - reinterpret_cast(str) + len)); - return literal_internal_impl(std::move(loc)); -} -#endif - -} // toml_literals -} // literals -} // toml -#endif//TOML11_LITERAL_HPP diff --git a/3party/toml11/toml/macros.hpp b/3party/toml11/toml/macros.hpp deleted file mode 100644 index e8f91ae..0000000 --- a/3party/toml11/toml/macros.hpp +++ /dev/null @@ -1,121 +0,0 @@ -#ifndef TOML11_MACROS_HPP -#define TOML11_MACROS_HPP - -#define TOML11_STRINGIZE_AUX(x) #x -#define TOML11_STRINGIZE(x) TOML11_STRINGIZE_AUX(x) - -#define TOML11_CONCATENATE_AUX(x, y) x##y -#define TOML11_CONCATENATE(x, y) TOML11_CONCATENATE_AUX(x, y) - -// ============================================================================ -// TOML11_DEFINE_CONVERSION_NON_INTRUSIVE - -#ifndef TOML11_WITHOUT_DEFINE_NON_INTRUSIVE - -// ---------------------------------------------------------------------------- -// TOML11_ARGS_SIZE - -#define TOML11_INDEX_RSEQ() \ - 32, 31, 30, 29, 28, 27, 26, 25, 24, 23, 22, 21, 20, 19, 18, 17, \ - 16, 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0 -#define TOML11_ARGS_SIZE_IMPL(\ - ARG1, ARG2, ARG3, ARG4, ARG5, ARG6, ARG7, ARG8, ARG9, ARG10, \ - ARG11, ARG12, ARG13, ARG14, ARG15, ARG16, ARG17, ARG18, ARG19, ARG20, \ - ARG21, ARG22, ARG23, ARG24, ARG25, ARG26, ARG27, ARG28, ARG29, ARG30, \ - ARG31, ARG32, N, ...) N -#define TOML11_ARGS_SIZE_AUX(...) TOML11_ARGS_SIZE_IMPL(__VA_ARGS__) -#define TOML11_ARGS_SIZE(...) TOML11_ARGS_SIZE_AUX(__VA_ARGS__, TOML11_INDEX_RSEQ()) - -// ---------------------------------------------------------------------------- -// TOML11_FOR_EACH_VA_ARGS - -#define TOML11_FOR_EACH_VA_ARGS_AUX_1( FUNCTOR, ARG1 ) FUNCTOR(ARG1) -#define TOML11_FOR_EACH_VA_ARGS_AUX_2( FUNCTOR, ARG1, ...) FUNCTOR(ARG1) TOML11_FOR_EACH_VA_ARGS_AUX_1( FUNCTOR, __VA_ARGS__) -#define TOML11_FOR_EACH_VA_ARGS_AUX_3( FUNCTOR, ARG1, ...) FUNCTOR(ARG1) TOML11_FOR_EACH_VA_ARGS_AUX_2( FUNCTOR, __VA_ARGS__) -#define TOML11_FOR_EACH_VA_ARGS_AUX_4( FUNCTOR, ARG1, ...) FUNCTOR(ARG1) TOML11_FOR_EACH_VA_ARGS_AUX_3( FUNCTOR, __VA_ARGS__) -#define TOML11_FOR_EACH_VA_ARGS_AUX_5( FUNCTOR, ARG1, ...) FUNCTOR(ARG1) TOML11_FOR_EACH_VA_ARGS_AUX_4( FUNCTOR, __VA_ARGS__) -#define TOML11_FOR_EACH_VA_ARGS_AUX_6( FUNCTOR, ARG1, ...) FUNCTOR(ARG1) TOML11_FOR_EACH_VA_ARGS_AUX_5( FUNCTOR, __VA_ARGS__) -#define TOML11_FOR_EACH_VA_ARGS_AUX_7( FUNCTOR, ARG1, ...) FUNCTOR(ARG1) TOML11_FOR_EACH_VA_ARGS_AUX_6( FUNCTOR, __VA_ARGS__) -#define TOML11_FOR_EACH_VA_ARGS_AUX_8( FUNCTOR, ARG1, ...) FUNCTOR(ARG1) TOML11_FOR_EACH_VA_ARGS_AUX_7( FUNCTOR, __VA_ARGS__) -#define TOML11_FOR_EACH_VA_ARGS_AUX_9( FUNCTOR, ARG1, ...) FUNCTOR(ARG1) TOML11_FOR_EACH_VA_ARGS_AUX_8( FUNCTOR, __VA_ARGS__) -#define TOML11_FOR_EACH_VA_ARGS_AUX_10(FUNCTOR, ARG1, ...) FUNCTOR(ARG1) TOML11_FOR_EACH_VA_ARGS_AUX_9( FUNCTOR, __VA_ARGS__) -#define TOML11_FOR_EACH_VA_ARGS_AUX_11(FUNCTOR, ARG1, ...) FUNCTOR(ARG1) TOML11_FOR_EACH_VA_ARGS_AUX_10(FUNCTOR, __VA_ARGS__) -#define TOML11_FOR_EACH_VA_ARGS_AUX_12(FUNCTOR, ARG1, ...) FUNCTOR(ARG1) TOML11_FOR_EACH_VA_ARGS_AUX_11(FUNCTOR, __VA_ARGS__) -#define TOML11_FOR_EACH_VA_ARGS_AUX_13(FUNCTOR, ARG1, ...) FUNCTOR(ARG1) TOML11_FOR_EACH_VA_ARGS_AUX_12(FUNCTOR, __VA_ARGS__) -#define TOML11_FOR_EACH_VA_ARGS_AUX_14(FUNCTOR, ARG1, ...) FUNCTOR(ARG1) TOML11_FOR_EACH_VA_ARGS_AUX_13(FUNCTOR, __VA_ARGS__) -#define TOML11_FOR_EACH_VA_ARGS_AUX_15(FUNCTOR, ARG1, ...) FUNCTOR(ARG1) TOML11_FOR_EACH_VA_ARGS_AUX_14(FUNCTOR, __VA_ARGS__) -#define TOML11_FOR_EACH_VA_ARGS_AUX_16(FUNCTOR, ARG1, ...) FUNCTOR(ARG1) TOML11_FOR_EACH_VA_ARGS_AUX_15(FUNCTOR, __VA_ARGS__) -#define TOML11_FOR_EACH_VA_ARGS_AUX_17(FUNCTOR, ARG1, ...) FUNCTOR(ARG1) TOML11_FOR_EACH_VA_ARGS_AUX_16(FUNCTOR, __VA_ARGS__) -#define TOML11_FOR_EACH_VA_ARGS_AUX_18(FUNCTOR, ARG1, ...) FUNCTOR(ARG1) TOML11_FOR_EACH_VA_ARGS_AUX_17(FUNCTOR, __VA_ARGS__) -#define TOML11_FOR_EACH_VA_ARGS_AUX_19(FUNCTOR, ARG1, ...) FUNCTOR(ARG1) TOML11_FOR_EACH_VA_ARGS_AUX_18(FUNCTOR, __VA_ARGS__) -#define TOML11_FOR_EACH_VA_ARGS_AUX_20(FUNCTOR, ARG1, ...) FUNCTOR(ARG1) TOML11_FOR_EACH_VA_ARGS_AUX_19(FUNCTOR, __VA_ARGS__) -#define TOML11_FOR_EACH_VA_ARGS_AUX_21(FUNCTOR, ARG1, ...) FUNCTOR(ARG1) TOML11_FOR_EACH_VA_ARGS_AUX_20(FUNCTOR, __VA_ARGS__) -#define TOML11_FOR_EACH_VA_ARGS_AUX_22(FUNCTOR, ARG1, ...) FUNCTOR(ARG1) TOML11_FOR_EACH_VA_ARGS_AUX_21(FUNCTOR, __VA_ARGS__) -#define TOML11_FOR_EACH_VA_ARGS_AUX_23(FUNCTOR, ARG1, ...) FUNCTOR(ARG1) TOML11_FOR_EACH_VA_ARGS_AUX_22(FUNCTOR, __VA_ARGS__) -#define TOML11_FOR_EACH_VA_ARGS_AUX_24(FUNCTOR, ARG1, ...) FUNCTOR(ARG1) TOML11_FOR_EACH_VA_ARGS_AUX_23(FUNCTOR, __VA_ARGS__) -#define TOML11_FOR_EACH_VA_ARGS_AUX_25(FUNCTOR, ARG1, ...) FUNCTOR(ARG1) TOML11_FOR_EACH_VA_ARGS_AUX_24(FUNCTOR, __VA_ARGS__) -#define TOML11_FOR_EACH_VA_ARGS_AUX_26(FUNCTOR, ARG1, ...) FUNCTOR(ARG1) TOML11_FOR_EACH_VA_ARGS_AUX_25(FUNCTOR, __VA_ARGS__) -#define TOML11_FOR_EACH_VA_ARGS_AUX_27(FUNCTOR, ARG1, ...) FUNCTOR(ARG1) TOML11_FOR_EACH_VA_ARGS_AUX_26(FUNCTOR, __VA_ARGS__) -#define TOML11_FOR_EACH_VA_ARGS_AUX_28(FUNCTOR, ARG1, ...) FUNCTOR(ARG1) TOML11_FOR_EACH_VA_ARGS_AUX_27(FUNCTOR, __VA_ARGS__) -#define TOML11_FOR_EACH_VA_ARGS_AUX_29(FUNCTOR, ARG1, ...) FUNCTOR(ARG1) TOML11_FOR_EACH_VA_ARGS_AUX_28(FUNCTOR, __VA_ARGS__) -#define TOML11_FOR_EACH_VA_ARGS_AUX_30(FUNCTOR, ARG1, ...) FUNCTOR(ARG1) TOML11_FOR_EACH_VA_ARGS_AUX_29(FUNCTOR, __VA_ARGS__) -#define TOML11_FOR_EACH_VA_ARGS_AUX_31(FUNCTOR, ARG1, ...) FUNCTOR(ARG1) TOML11_FOR_EACH_VA_ARGS_AUX_30(FUNCTOR, __VA_ARGS__) -#define TOML11_FOR_EACH_VA_ARGS_AUX_32(FUNCTOR, ARG1, ...) FUNCTOR(ARG1) TOML11_FOR_EACH_VA_ARGS_AUX_31(FUNCTOR, __VA_ARGS__) - -#define TOML11_FOR_EACH_VA_ARGS(FUNCTOR, ...)\ - TOML11_CONCATENATE(TOML11_FOR_EACH_VA_ARGS_AUX_, TOML11_ARGS_SIZE(__VA_ARGS__))(FUNCTOR, __VA_ARGS__) - -// ---------------------------------------------------------------------------- -// TOML11_DEFINE_CONVERSION_NON_INTRUSIVE - -// use it in the following way. -// ```cpp -// namespace foo -// { -// struct Foo -// { -// std::string s; -// double d; -// int i; -// }; -// } // foo -// -// TOML11_DEFINE_CONVERSION_NON_INTRUSIVE(foo::Foo, s, d, i) -// ``` -// And then you can use `toml::find(file, "foo");` -// -#define TOML11_FIND_MEMBER_VARIABLE_FROM_VALUE(VAR_NAME)\ - obj.VAR_NAME = toml::find(v, TOML11_STRINGIZE(VAR_NAME)); - -#define TOML11_ASSIGN_MEMBER_VARIABLE_TO_VALUE(VAR_NAME)\ - v[TOML11_STRINGIZE(VAR_NAME)] = obj.VAR_NAME; - -#define TOML11_DEFINE_CONVERSION_NON_INTRUSIVE(NAME, ...)\ - namespace toml { \ - template<> \ - struct from \ - { \ - template class T, \ - template class A> \ - static NAME from_toml(const basic_value& v) \ - { \ - NAME obj; \ - TOML11_FOR_EACH_VA_ARGS(TOML11_FIND_MEMBER_VARIABLE_FROM_VALUE, __VA_ARGS__) \ - return obj; \ - } \ - }; \ - template<> \ - struct into \ - { \ - static value into_toml(const NAME& obj) \ - { \ - ::toml::value v = ::toml::table{}; \ - TOML11_FOR_EACH_VA_ARGS(TOML11_ASSIGN_MEMBER_VARIABLE_TO_VALUE, __VA_ARGS__) \ - return v; \ - } \ - }; \ - } /* toml */ - -#endif// TOML11_WITHOUT_DEFINE_NON_INTRUSIVE - -#endif// TOML11_MACROS_HPP diff --git a/3party/toml11/toml/parser.hpp b/3party/toml11/toml/parser.hpp deleted file mode 100644 index c8ec1d3..0000000 --- a/3party/toml11/toml/parser.hpp +++ /dev/null @@ -1,2611 +0,0 @@ -// Copyright Toru Niina 2017. -// Distributed under the MIT License. -#ifndef TOML11_PARSER_HPP -#define TOML11_PARSER_HPP -#include -#include -#include - -#include "combinator.hpp" -#include "lexer.hpp" -#include "macros.hpp" -#include "region.hpp" -#include "result.hpp" -#include "types.hpp" -#include "value.hpp" - -#ifndef TOML11_DISABLE_STD_FILESYSTEM -#ifdef __cpp_lib_filesystem -#if __has_include() -#define TOML11_HAS_STD_FILESYSTEM -#include -#endif // has_include() -#endif // __cpp_lib_filesystem -#endif // TOML11_DISABLE_STD_FILESYSTEM - -// the previous commit works with 500+ recursions. so it may be too small. -// but in most cases, i think we don't need such a deep recursion of -// arrays or inline-tables. -#define TOML11_VALUE_RECURSION_LIMIT 64 - -namespace toml -{ -namespace detail -{ - -inline result, std::string> -parse_boolean(location& loc) -{ - const auto first = loc.iter(); - if(const auto token = lex_boolean::invoke(loc)) - { - const auto reg = token.unwrap(); - if (reg.str() == "true") {return ok(std::make_pair(true, reg));} - else if(reg.str() == "false") {return ok(std::make_pair(false, reg));} - else // internal error. - { - throw internal_error(format_underline( - "toml::parse_boolean: internal error", - {{source_location(reg), "invalid token"}}), - source_location(reg)); - } - } - loc.reset(first); //rollback - return err(format_underline("toml::parse_boolean: ", - {{source_location(loc), "the next token is not a boolean"}})); -} - -inline result, std::string> -parse_binary_integer(location& loc) -{ - const auto first = loc.iter(); - if(const auto token = lex_bin_int::invoke(loc)) - { - auto str = token.unwrap().str(); - assert(str.size() > 2); // minimum -> 0b1 - assert(str.at(0) == '0' && str.at(1) == 'b'); - - // skip all the zeros and `_` locating at the MSB - str.erase(str.begin(), std::find_if( - str.begin() + 2, // to skip prefix `0b` - str.end(), - [](const char c) { return c == '1'; }) - ); - assert(str.empty() || str.front() == '1'); - - // since toml11 uses int64_t, 64bit (unsigned) input cannot be read. - const auto max_length = 63 + std::count(str.begin(), str.end(), '_'); - if(static_cast(max_length) < str.size()) - { - loc.reset(first); - return err(format_underline("toml::parse_binary_integer: " - "only signed 64bit integer is available", - {{source_location(loc), "too large input (> int64_t)"}})); - } - - integer retval(0), base(1); - for(auto i(str.rbegin()), e(str.rend()); i!=e; ++i) - { - assert(base != 0); // means overflow, checked in the above code - if(*i == '1') - { - retval += base; - if( (std::numeric_limits::max)() / 2 < base ) - { - base = 0; - } - base *= 2; - } - else if(*i == '0') - { - if( (std::numeric_limits::max)() / 2 < base ) - { - base = 0; - } - base *= 2; - } - else if(*i == '_') - { - // do nothing. - } - else // should be detected by lex_bin_int. [[unlikely]] - { - throw internal_error(format_underline( - "toml::parse_binary_integer: internal error", - {{source_location(token.unwrap()), "invalid token"}}), - source_location(loc)); - } - } - return ok(std::make_pair(retval, token.unwrap())); - } - loc.reset(first); - return err(format_underline("toml::parse_binary_integer:", - {{source_location(loc), "the next token is not an integer"}})); -} - -inline result, std::string> -parse_octal_integer(location& loc) -{ - const auto first = loc.iter(); - if(const auto token = lex_oct_int::invoke(loc)) - { - auto str = token.unwrap().str(); - str.erase(std::remove(str.begin(), str.end(), '_'), str.end()); - str.erase(str.begin()); str.erase(str.begin()); // remove `0o` prefix - - std::istringstream iss(str); - integer retval(0); - iss >> std::oct >> retval; - if(iss.fail()) - { - // `istream` sets `failbit` if internally-called `std::num_get::get` - // fails. - // `std::num_get::get` calls `std::strtoll` if the argument type is - // signed. - // `std::strtoll` fails if - // - the value is out_of_range or - // - no conversion is possible. - // since we already checked that the string is valid octal integer, - // so the error reason is out_of_range. - loc.reset(first); - return err(format_underline("toml::parse_octal_integer:", - {{source_location(loc), "out of range"}})); - } - return ok(std::make_pair(retval, token.unwrap())); - } - loc.reset(first); - return err(format_underline("toml::parse_octal_integer:", - {{source_location(loc), "the next token is not an integer"}})); -} - -inline result, std::string> -parse_hexadecimal_integer(location& loc) -{ - const auto first = loc.iter(); - if(const auto token = lex_hex_int::invoke(loc)) - { - auto str = token.unwrap().str(); - str.erase(std::remove(str.begin(), str.end(), '_'), str.end()); - str.erase(str.begin()); str.erase(str.begin()); // remove `0x` prefix - - std::istringstream iss(str); - integer retval(0); - iss >> std::hex >> retval; - if(iss.fail()) - { - // see parse_octal_integer for detail of this error message. - loc.reset(first); - return err(format_underline("toml::parse_hexadecimal_integer:", - {{source_location(loc), "out of range"}})); - } - return ok(std::make_pair(retval, token.unwrap())); - } - loc.reset(first); - return err(format_underline("toml::parse_hexadecimal_integer", - {{source_location(loc), "the next token is not an integer"}})); -} - -inline result, std::string> -parse_integer(location& loc) -{ - const auto first = loc.iter(); - if(first != loc.end() && *first == '0') - { - const auto second = std::next(first); - if(second == loc.end()) // the token is just zero. - { - loc.advance(); - return ok(std::make_pair(0, region(loc, first, second))); - } - - if(*second == 'b') {return parse_binary_integer (loc);} // 0b1100 - if(*second == 'o') {return parse_octal_integer (loc);} // 0o775 - if(*second == 'x') {return parse_hexadecimal_integer(loc);} // 0xC0FFEE - - if(std::isdigit(*second)) - { - return err(format_underline("toml::parse_integer: " - "leading zero in an Integer is not allowed.", - {{source_location(loc), "leading zero"}})); - } - else if(std::isalpha(*second)) - { - return err(format_underline("toml::parse_integer: " - "unknown integer prefix appeared.", - {{source_location(loc), "none of 0x, 0o, 0b"}})); - } - } - - if(const auto token = lex_dec_int::invoke(loc)) - { - auto str = token.unwrap().str(); - str.erase(std::remove(str.begin(), str.end(), '_'), str.end()); - - std::istringstream iss(str); - integer retval(0); - iss >> retval; - if(iss.fail()) - { - // see parse_octal_integer for detail of this error message. - loc.reset(first); - return err(format_underline("toml::parse_integer:", - {{source_location(loc), "out of range"}})); - } - return ok(std::make_pair(retval, token.unwrap())); - } - loc.reset(first); - return err(format_underline("toml::parse_integer: ", - {{source_location(loc), "the next token is not an integer"}})); -} - -inline result, std::string> -parse_floating(location& loc) -{ - const auto first = loc.iter(); - if(const auto token = lex_float::invoke(loc)) - { - auto str = token.unwrap().str(); - if(str == "inf" || str == "+inf") - { - if(std::numeric_limits::has_infinity) - { - return ok(std::make_pair( - std::numeric_limits::infinity(), token.unwrap())); - } - else - { - throw std::domain_error("toml::parse_floating: inf value found" - " but the current environment does not support inf. Please" - " make sure that the floating-point implementation conforms" - " IEEE 754/ISO 60559 international standard."); - } - } - else if(str == "-inf") - { - if(std::numeric_limits::has_infinity) - { - return ok(std::make_pair( - -std::numeric_limits::infinity(), token.unwrap())); - } - else - { - throw std::domain_error("toml::parse_floating: inf value found" - " but the current environment does not support inf. Please" - " make sure that the floating-point implementation conforms" - " IEEE 754/ISO 60559 international standard."); - } - } - else if(str == "nan" || str == "+nan") - { - if(std::numeric_limits::has_quiet_NaN) - { - return ok(std::make_pair( - std::numeric_limits::quiet_NaN(), token.unwrap())); - } - else if(std::numeric_limits::has_signaling_NaN) - { - return ok(std::make_pair( - std::numeric_limits::signaling_NaN(), token.unwrap())); - } - else - { - throw std::domain_error("toml::parse_floating: NaN value found" - " but the current environment does not support NaN. Please" - " make sure that the floating-point implementation conforms" - " IEEE 754/ISO 60559 international standard."); - } - } - else if(str == "-nan") - { - if(std::numeric_limits::has_quiet_NaN) - { - return ok(std::make_pair( - -std::numeric_limits::quiet_NaN(), token.unwrap())); - } - else if(std::numeric_limits::has_signaling_NaN) - { - return ok(std::make_pair( - -std::numeric_limits::signaling_NaN(), token.unwrap())); - } - else - { - throw std::domain_error("toml::parse_floating: NaN value found" - " but the current environment does not support NaN. Please" - " make sure that the floating-point implementation conforms" - " IEEE 754/ISO 60559 international standard."); - } - } - str.erase(std::remove(str.begin(), str.end(), '_'), str.end()); - std::istringstream iss(str); - floating v(0.0); - iss >> v; - if(iss.fail()) - { - // see parse_octal_integer for detail of this error message. - loc.reset(first); - return err(format_underline("toml::parse_floating:", - {{source_location(loc), "out of range"}})); - } - return ok(std::make_pair(v, token.unwrap())); - } - loc.reset(first); - return err(format_underline("toml::parse_floating: ", - {{source_location(loc), "the next token is not a float"}})); -} - -inline std::string read_utf8_codepoint(const region& reg, const location& loc) -{ - const auto str = reg.str().substr(1); - std::uint_least32_t codepoint; - std::istringstream iss(str); - iss >> std::hex >> codepoint; - - const auto to_char = [](const std::uint_least32_t i) noexcept -> char { - const auto uc = static_cast(i); - return *reinterpret_cast(std::addressof(uc)); - }; - - std::string character; - if(codepoint < 0x80) // U+0000 ... U+0079 ; just an ASCII. - { - character += static_cast(codepoint); - } - else if(codepoint < 0x800) //U+0080 ... U+07FF - { - // 110yyyyx 10xxxxxx; 0x3f == 0b0011'1111 - character += to_char(0xC0| codepoint >> 6); - character += to_char(0x80|(codepoint & 0x3F)); - } - else if(codepoint < 0x10000) // U+0800...U+FFFF - { - if(0xD800 <= codepoint && codepoint <= 0xDFFF) - { - throw syntax_error(format_underline( - "toml::read_utf8_codepoint: codepoints in the range " - "[0xD800, 0xDFFF] are not valid UTF-8.", {{ - source_location(loc), "not a valid UTF-8 codepoint" - }}), source_location(loc)); - } - assert(codepoint < 0xD800 || 0xDFFF < codepoint); - // 1110yyyy 10yxxxxx 10xxxxxx - character += to_char(0xE0| codepoint >> 12); - character += to_char(0x80|(codepoint >> 6 & 0x3F)); - character += to_char(0x80|(codepoint & 0x3F)); - } - else if(codepoint < 0x110000) // U+010000 ... U+10FFFF - { - // 11110yyy 10yyxxxx 10xxxxxx 10xxxxxx - character += to_char(0xF0| codepoint >> 18); - character += to_char(0x80|(codepoint >> 12 & 0x3F)); - character += to_char(0x80|(codepoint >> 6 & 0x3F)); - character += to_char(0x80|(codepoint & 0x3F)); - } - else // out of UTF-8 region - { - throw syntax_error(format_underline("toml::read_utf8_codepoint:" - " input codepoint is too large.", - {{source_location(loc), "should be in [0x00..0x10FFFF]"}}), - source_location(loc)); - } - return character; -} - -inline result parse_escape_sequence(location& loc) -{ - const auto first = loc.iter(); - if(first == loc.end() || *first != '\\') - { - return err(format_underline("toml::parse_escape_sequence: ", {{ - source_location(loc), "the next token is not a backslash \"\\\""}})); - } - loc.advance(); - switch(*loc.iter()) - { - case '\\':{loc.advance(); return ok(std::string("\\"));} - case '"' :{loc.advance(); return ok(std::string("\""));} - case 'b' :{loc.advance(); return ok(std::string("\b"));} - case 't' :{loc.advance(); return ok(std::string("\t"));} - case 'n' :{loc.advance(); return ok(std::string("\n"));} - case 'f' :{loc.advance(); return ok(std::string("\f"));} - case 'r' :{loc.advance(); return ok(std::string("\r"));} -#ifdef TOML11_USE_UNRELEASED_TOML_FEATURES - case 'e' :{loc.advance(); return ok(std::string("\x1b"));} // ESC -#endif - case 'u' : - { - if(const auto token = lex_escape_unicode_short::invoke(loc)) - { - return ok(read_utf8_codepoint(token.unwrap(), loc)); - } - else - { - return err(format_underline("parse_escape_sequence: " - "invalid token found in UTF-8 codepoint uXXXX.", - {{source_location(loc), "here"}})); - } - } - case 'U': - { - if(const auto token = lex_escape_unicode_long::invoke(loc)) - { - return ok(read_utf8_codepoint(token.unwrap(), loc)); - } - else - { - return err(format_underline("parse_escape_sequence: " - "invalid token found in UTF-8 codepoint Uxxxxxxxx", - {{source_location(loc), "here"}})); - } - } - } - - const auto msg = format_underline("parse_escape_sequence: " - "unknown escape sequence appeared.", {{source_location(loc), - "escape sequence is one of \\, \", b, t, n, f, r, uxxxx, Uxxxxxxxx"}}, - /* Hints = */{"if you want to write backslash as just one backslash, " - "use literal string like: regex = '<\\i\\c*\\s*>'"}); - loc.reset(first); - return err(msg); -} - -inline std::ptrdiff_t check_utf8_validity(const std::string& reg) -{ - location loc("tmp", reg); - const auto u8 = repeat::invoke(loc); - if(!u8 || loc.iter() != loc.end()) - { - const auto error_location = std::distance(loc.begin(), loc.iter()); - assert(0 <= error_location); - return error_location; - } - return -1; -} - -inline result, std::string> -parse_ml_basic_string(location& loc) -{ - const auto first = loc.iter(); - if(const auto token = lex_ml_basic_string::invoke(loc)) - { - auto inner_loc = loc; - inner_loc.reset(first); - - std::string retval; - retval.reserve(token.unwrap().size()); - - auto delim = lex_ml_basic_string_open::invoke(inner_loc); - if(!delim) - { - throw internal_error(format_underline( - "parse_ml_basic_string: invalid token", - {{source_location(inner_loc), "should be \"\"\""}}), - source_location(inner_loc)); - } - // immediate newline is ignored (if exists) - /* discard return value */ lex_newline::invoke(inner_loc); - - delim = none(); - while(!delim) - { - using lex_unescaped_seq = repeat< - either, unlimited>; - if(auto unescaped = lex_unescaped_seq::invoke(inner_loc)) - { - retval += unescaped.unwrap().str(); - } - if(auto escaped = parse_escape_sequence(inner_loc)) - { - retval += escaped.unwrap(); - } - if(auto esc_nl = lex_ml_basic_escaped_newline::invoke(inner_loc)) - { - // ignore newline after escape until next non-ws char - } - if(inner_loc.iter() == inner_loc.end()) - { - throw internal_error(format_underline( - "parse_ml_basic_string: unexpected end of region", - {{source_location(inner_loc), "not sufficient token"}}), - source_location(inner_loc)); - } - delim = lex_ml_basic_string_close::invoke(inner_loc); - } - // `lex_ml_basic_string_close` allows 3 to 5 `"`s to allow 1 or 2 `"`s - // at just before the delimiter. Here, we need to attach `"`s at the - // end of the string body, if it exists. - // For detail, see the definition of `lex_ml_basic_string_close`. - assert(std::all_of(delim.unwrap().first(), delim.unwrap().last(), - [](const char c) noexcept {return c == '\"';})); - switch(delim.unwrap().size()) - { - case 3: {break;} - case 4: {retval += "\""; break;} - case 5: {retval += "\"\""; break;} - default: - { - throw internal_error(format_underline( - "parse_ml_basic_string: closing delimiter has invalid length", - {{source_location(inner_loc), "end of this"}}), - source_location(inner_loc)); - } - } - - const auto err_loc = check_utf8_validity(token.unwrap().str()); - if(err_loc == -1) - { - return ok(std::make_pair(toml::string(retval), token.unwrap())); - } - else - { - inner_loc.reset(first); - inner_loc.advance(err_loc); - throw syntax_error(format_underline( - "parse_ml_basic_string: invalid utf8 sequence found", - {{source_location(inner_loc), "here"}}), - source_location(inner_loc)); - } - } - else - { - loc.reset(first); - return err(format_underline("toml::parse_ml_basic_string: " - "the next token is not a valid multiline string", - {{source_location(loc), "here"}})); - } -} - -inline result, std::string> -parse_basic_string(location& loc) -{ - const auto first = loc.iter(); - if(const auto token = lex_basic_string::invoke(loc)) - { - auto inner_loc = loc; - inner_loc.reset(first); - - auto quot = lex_quotation_mark::invoke(inner_loc); - if(!quot) - { - throw internal_error(format_underline("parse_basic_string: " - "invalid token", {{source_location(inner_loc), "should be \""}}), - source_location(inner_loc)); - } - - std::string retval; - retval.reserve(token.unwrap().size()); - - quot = none(); - while(!quot) - { - using lex_unescaped_seq = repeat; - if(auto unescaped = lex_unescaped_seq::invoke(inner_loc)) - { - retval += unescaped.unwrap().str(); - } - if(auto escaped = parse_escape_sequence(inner_loc)) - { - retval += escaped.unwrap(); - } - if(inner_loc.iter() == inner_loc.end()) - { - throw internal_error(format_underline( - "parse_basic_string: unexpected end of region", - {{source_location(inner_loc), "not sufficient token"}}), - source_location(inner_loc)); - } - quot = lex_quotation_mark::invoke(inner_loc); - } - - const auto err_loc = check_utf8_validity(token.unwrap().str()); - if(err_loc == -1) - { - return ok(std::make_pair(toml::string(retval), token.unwrap())); - } - else - { - inner_loc.reset(first); - inner_loc.advance(err_loc); - throw syntax_error(format_underline( - "parse_basic_string: invalid utf8 sequence found", - {{source_location(inner_loc), "here"}}), - source_location(inner_loc)); - } - } - else - { - loc.reset(first); // rollback - return err(format_underline("toml::parse_basic_string: " - "the next token is not a valid string", - {{source_location(loc), "here"}})); - } -} - -inline result, std::string> -parse_ml_literal_string(location& loc) -{ - const auto first = loc.iter(); - if(const auto token = lex_ml_literal_string::invoke(loc)) - { - auto inner_loc = loc; - inner_loc.reset(first); - - const auto open = lex_ml_literal_string_open::invoke(inner_loc); - if(!open) - { - throw internal_error(format_underline( - "parse_ml_literal_string: invalid token", - {{source_location(inner_loc), "should be '''"}}), - source_location(inner_loc)); - } - // immediate newline is ignored (if exists) - /* discard return value */ lex_newline::invoke(inner_loc); - - const auto body = lex_ml_literal_body::invoke(inner_loc); - - const auto close = lex_ml_literal_string_close::invoke(inner_loc); - if(!close) - { - throw internal_error(format_underline( - "parse_ml_literal_string: invalid token", - {{source_location(inner_loc), "should be '''"}}), - source_location(inner_loc)); - } - // `lex_ml_literal_string_close` allows 3 to 5 `'`s to allow 1 or 2 `'`s - // at just before the delimiter. Here, we need to attach `'`s at the - // end of the string body, if it exists. - // For detail, see the definition of `lex_ml_basic_string_close`. - - std::string retval = body.unwrap().str(); - assert(std::all_of(close.unwrap().first(), close.unwrap().last(), - [](const char c) noexcept {return c == '\'';})); - switch(close.unwrap().size()) - { - case 3: {break;} - case 4: {retval += "'"; break;} - case 5: {retval += "''"; break;} - default: - { - throw internal_error(format_underline( - "parse_ml_literal_string: closing delimiter has invalid length", - {{source_location(inner_loc), "end of this"}}), - source_location(inner_loc)); - } - } - - const auto err_loc = check_utf8_validity(token.unwrap().str()); - if(err_loc == -1) - { - return ok(std::make_pair(toml::string(retval, toml::string_t::literal), - token.unwrap())); - } - else - { - inner_loc.reset(first); - inner_loc.advance(err_loc); - throw syntax_error(format_underline( - "parse_ml_literal_string: invalid utf8 sequence found", - {{source_location(inner_loc), "here"}}), - source_location(inner_loc)); - } - } - else - { - loc.reset(first); // rollback - return err(format_underline("toml::parse_ml_literal_string: " - "the next token is not a valid multiline literal string", - {{source_location(loc), "here"}})); - } -} - -inline result, std::string> -parse_literal_string(location& loc) -{ - const auto first = loc.iter(); - if(const auto token = lex_literal_string::invoke(loc)) - { - auto inner_loc = loc; - inner_loc.reset(first); - - const auto open = lex_apostrophe::invoke(inner_loc); - if(!open) - { - throw internal_error(format_underline( - "parse_literal_string: invalid token", - {{source_location(inner_loc), "should be '"}}), - source_location(inner_loc)); - } - - const auto body = repeat::invoke(inner_loc); - - const auto close = lex_apostrophe::invoke(inner_loc); - if(!close) - { - throw internal_error(format_underline( - "parse_literal_string: invalid token", - {{source_location(inner_loc), "should be '"}}), - source_location(inner_loc)); - } - - const auto err_loc = check_utf8_validity(token.unwrap().str()); - if(err_loc == -1) - { - return ok(std::make_pair( - toml::string(body.unwrap().str(), toml::string_t::literal), - token.unwrap())); - } - else - { - inner_loc.reset(first); - inner_loc.advance(err_loc); - throw syntax_error(format_underline( - "parse_literal_string: invalid utf8 sequence found", - {{source_location(inner_loc), "here"}}), - source_location(inner_loc)); - } - } - else - { - loc.reset(first); // rollback - return err(format_underline("toml::parse_literal_string: " - "the next token is not a valid literal string", - {{source_location(loc), "here"}})); - } -} - -inline result, std::string> -parse_string(location& loc) -{ - if(loc.iter() != loc.end() && *(loc.iter()) == '"') - { - if(loc.iter() + 1 != loc.end() && *(loc.iter() + 1) == '"' && - loc.iter() + 2 != loc.end() && *(loc.iter() + 2) == '"') - { - return parse_ml_basic_string(loc); - } - else - { - return parse_basic_string(loc); - } - } - else if(loc.iter() != loc.end() && *(loc.iter()) == '\'') - { - if(loc.iter() + 1 != loc.end() && *(loc.iter() + 1) == '\'' && - loc.iter() + 2 != loc.end() && *(loc.iter() + 2) == '\'') - { - return parse_ml_literal_string(loc); - } - else - { - return parse_literal_string(loc); - } - } - return err(format_underline("toml::parse_string: ", - {{source_location(loc), "the next token is not a string"}})); -} - -inline result, std::string> -parse_local_date(location& loc) -{ - const auto first = loc.iter(); - if(const auto token = lex_local_date::invoke(loc)) - { - location inner_loc(loc.name(), token.unwrap().str()); - - const auto y = lex_date_fullyear::invoke(inner_loc); - if(!y || inner_loc.iter() == inner_loc.end() || *inner_loc.iter() != '-') - { - throw internal_error(format_underline( - "toml::parse_local_date: invalid year format", - {{source_location(inner_loc), "should be `-`"}}), - source_location(inner_loc)); - } - inner_loc.advance(); - const auto m = lex_date_month::invoke(inner_loc); - if(!m || inner_loc.iter() == inner_loc.end() || *inner_loc.iter() != '-') - { - throw internal_error(format_underline( - "toml::parse_local_date: invalid month format", - {{source_location(inner_loc), "should be `-`"}}), - source_location(inner_loc)); - } - inner_loc.advance(); - const auto d = lex_date_mday::invoke(inner_loc); - if(!d) - { - throw internal_error(format_underline( - "toml::parse_local_date: invalid day format", - {{source_location(inner_loc), "here"}}), - source_location(inner_loc)); - } - - const auto year = static_cast(from_string(y.unwrap().str(), 0)); - const auto month = static_cast(from_string(m.unwrap().str(), 0)); - const auto day = static_cast(from_string(d.unwrap().str(), 0)); - - // We briefly check whether the input date is valid or not. But here, we - // only check if the RFC3339 compliance. - // Actually there are several special date that does not exist, - // because of historical reasons, such as 1582/10/5-1582/10/14 (only in - // several countries). But here, we do not care about such a complicated - // rule. It makes the code complicated and there is only low probability - // that such a specific date is needed in practice. If someone need to - // validate date accurately, that means that the one need a specialized - // library for their purpose in a different layer. - { - const bool is_leap = (year % 4 == 0) && ((year % 100 != 0) || (year % 400 == 0)); - const auto max_day = (month == 2) ? (is_leap ? 29 : 28) : - ((month == 4 || month == 6 || month == 9 || month == 11) ? 30 : 31); - - if((month < 1 || 12 < month) || (day < 1 || max_day < day)) - { - throw syntax_error(format_underline("toml::parse_date: " - "invalid date: it does not conform RFC3339.", {{ - source_location(loc), "month should be 01-12, day should be" - " 01-28,29,30,31, depending on month/year." - }}), source_location(inner_loc)); - } - } - return ok(std::make_pair(local_date(year, static_cast(month - 1), day), - token.unwrap())); - } - else - { - loc.reset(first); - return err(format_underline("toml::parse_local_date: ", - {{source_location(loc), "the next token is not a local_date"}})); - } -} - -inline result, std::string> -parse_local_time(location& loc) -{ - const auto first = loc.iter(); - if(const auto token = lex_local_time::invoke(loc)) - { - location inner_loc(loc.name(), token.unwrap().str()); - - const auto h = lex_time_hour::invoke(inner_loc); - if(!h || inner_loc.iter() == inner_loc.end() || *inner_loc.iter() != ':') - { - throw internal_error(format_underline( - "toml::parse_local_time: invalid year format", - {{source_location(inner_loc), "should be `:`"}}), - source_location(inner_loc)); - } - inner_loc.advance(); - const auto m = lex_time_minute::invoke(inner_loc); - if(!m || inner_loc.iter() == inner_loc.end() || *inner_loc.iter() != ':') - { - throw internal_error(format_underline( - "toml::parse_local_time: invalid month format", - {{source_location(inner_loc), "should be `:`"}}), - source_location(inner_loc)); - } - inner_loc.advance(); - const auto s = lex_time_second::invoke(inner_loc); - if(!s) - { - throw internal_error(format_underline( - "toml::parse_local_time: invalid second format", - {{source_location(inner_loc), "here"}}), - source_location(inner_loc)); - } - - const int hour = from_string(h.unwrap().str(), 0); - const int minute = from_string(m.unwrap().str(), 0); - const int second = from_string(s.unwrap().str(), 0); - - if((hour < 0 || 23 < hour) || (minute < 0 || 59 < minute) || - (second < 0 || 60 < second)) // it may be leap second - { - throw syntax_error(format_underline("toml::parse_local_time: " - "invalid time: it does not conform RFC3339.", {{ - source_location(loc), "hour should be 00-23, minute should be" - " 00-59, second should be 00-60 (depending on the leap" - " second rules.)"}}), source_location(inner_loc)); - } - - local_time time(hour, minute, second, 0, 0); - - const auto before_secfrac = inner_loc.iter(); - if(const auto secfrac = lex_time_secfrac::invoke(inner_loc)) - { - auto sf = secfrac.unwrap().str(); - sf.erase(sf.begin()); // sf.front() == '.' - switch(sf.size() % 3) - { - case 2: sf += '0'; break; - case 1: sf += "00"; break; - case 0: break; - default: break; - } - if(sf.size() >= 9) - { - time.millisecond = from_string(sf.substr(0, 3), 0u); - time.microsecond = from_string(sf.substr(3, 3), 0u); - time.nanosecond = from_string(sf.substr(6, 3), 0u); - } - else if(sf.size() >= 6) - { - time.millisecond = from_string(sf.substr(0, 3), 0u); - time.microsecond = from_string(sf.substr(3, 3), 0u); - } - else if(sf.size() >= 3) - { - time.millisecond = from_string(sf, 0u); - time.microsecond = 0u; - } - } - else - { - if(before_secfrac != inner_loc.iter()) - { - throw internal_error(format_underline( - "toml::parse_local_time: invalid subsecond format", - {{source_location(inner_loc), "here"}}), - source_location(inner_loc)); - } - } - return ok(std::make_pair(time, token.unwrap())); - } - else - { - loc.reset(first); - return err(format_underline("toml::parse_local_time: ", - {{source_location(loc), "the next token is not a local_time"}})); - } -} - -inline result, std::string> -parse_local_datetime(location& loc) -{ - const auto first = loc.iter(); - if(const auto token = lex_local_date_time::invoke(loc)) - { - location inner_loc(loc.name(), token.unwrap().str()); - const auto date = parse_local_date(inner_loc); - if(!date || inner_loc.iter() == inner_loc.end()) - { - throw internal_error(format_underline( - "toml::parse_local_datetime: invalid datetime format", - {{source_location(inner_loc), "date, not datetime"}}), - source_location(inner_loc)); - } - const char delim = *(inner_loc.iter()); - if(delim != 'T' && delim != 't' && delim != ' ') - { - throw internal_error(format_underline( - "toml::parse_local_datetime: invalid datetime format", - {{source_location(inner_loc), "should be `T` or ` ` (space)"}}), - source_location(inner_loc)); - } - inner_loc.advance(); - const auto time = parse_local_time(inner_loc); - if(!time) - { - throw internal_error(format_underline( - "toml::parse_local_datetime: invalid datetime format", - {{source_location(inner_loc), "invalid time format"}}), - source_location(inner_loc)); - } - return ok(std::make_pair( - local_datetime(date.unwrap().first, time.unwrap().first), - token.unwrap())); - } - else - { - loc.reset(first); - return err(format_underline("toml::parse_local_datetime: ", - {{source_location(loc), "the next token is not a local_datetime"}})); - } -} - -inline result, std::string> -parse_offset_datetime(location& loc) -{ - const auto first = loc.iter(); - if(const auto token = lex_offset_date_time::invoke(loc)) - { - location inner_loc(loc.name(), token.unwrap().str()); - const auto datetime = parse_local_datetime(inner_loc); - if(!datetime || inner_loc.iter() == inner_loc.end()) - { - throw internal_error(format_underline( - "toml::parse_offset_datetime: invalid datetime format", - {{source_location(inner_loc), "date, not datetime"}}), - source_location(inner_loc)); - } - time_offset offset(0, 0); - if(const auto ofs = lex_time_numoffset::invoke(inner_loc)) - { - const auto str = ofs.unwrap().str(); - - const auto hour = from_string(str.substr(1,2), 0); - const auto minute = from_string(str.substr(4,2), 0); - - if((hour < 0 || 23 < hour) || (minute < 0 || 59 < minute)) - { - throw syntax_error(format_underline("toml::parse_offset_datetime: " - "invalid offset: it does not conform RFC3339.", {{ - source_location(loc), "month should be 01-12, day should be" - " 01-28,29,30,31, depending on month/year." - }}), source_location(inner_loc)); - } - - if(str.front() == '+') - { - offset = time_offset(hour, minute); - } - else - { - offset = time_offset(-hour, -minute); - } - } - else if(*inner_loc.iter() != 'Z' && *inner_loc.iter() != 'z') - { - throw internal_error(format_underline( - "toml::parse_offset_datetime: invalid datetime format", - {{source_location(inner_loc), "should be `Z` or `+HH:MM`"}}), - source_location(inner_loc)); - } - return ok(std::make_pair(offset_datetime(datetime.unwrap().first, offset), - token.unwrap())); - } - else - { - loc.reset(first); - return err(format_underline("toml::parse_offset_datetime: ", - {{source_location(loc), "the next token is not a offset_datetime"}})); - } -} - -inline result, std::string> -parse_simple_key(location& loc) -{ - if(const auto bstr = parse_basic_string(loc)) - { - return ok(std::make_pair(bstr.unwrap().first.str, bstr.unwrap().second)); - } - if(const auto lstr = parse_literal_string(loc)) - { - return ok(std::make_pair(lstr.unwrap().first.str, lstr.unwrap().second)); - } - if(const auto bare = lex_unquoted_key::invoke(loc)) - { - const auto reg = bare.unwrap(); - return ok(std::make_pair(reg.str(), reg)); - } - return err(format_underline("toml::parse_simple_key: ", - {{source_location(loc), "the next token is not a simple key"}})); -} - -// dotted key become vector of keys -inline result, region>, std::string> -parse_key(location& loc) -{ - const auto first = loc.iter(); - // dotted key -> `foo.bar.baz` where several single keys are chained by - // dots. Whitespaces between keys and dots are allowed. - if(const auto token = lex_dotted_key::invoke(loc)) - { - const auto reg = token.unwrap(); - location inner_loc(loc.name(), reg.str()); - std::vector keys; - - while(inner_loc.iter() != inner_loc.end()) - { - lex_ws::invoke(inner_loc); - if(const auto k = parse_simple_key(inner_loc)) - { - keys.push_back(k.unwrap().first); - } - else - { - throw internal_error(format_underline( - "toml::parse_key: dotted key contains invalid key", - {{source_location(inner_loc), k.unwrap_err()}}), - source_location(inner_loc)); - } - - lex_ws::invoke(inner_loc); - if(inner_loc.iter() == inner_loc.end()) - { - break; - } - else if(*inner_loc.iter() == '.') - { - inner_loc.advance(); // to skip `.` - } - else - { - throw internal_error(format_underline("toml::parse_key: " - "dotted key contains invalid key ", - {{source_location(inner_loc), "should be `.`"}}), - source_location(inner_loc)); - } - } - return ok(std::make_pair(keys, reg)); - } - loc.reset(first); - - // simple_key: a single (basic_string|literal_string|bare key) - if(const auto smpl = parse_simple_key(loc)) - { - return ok(std::make_pair(std::vector(1, smpl.unwrap().first), - smpl.unwrap().second)); - } - return err(format_underline("toml::parse_key: an invalid key appeared.", - {{source_location(loc), "is not a valid key"}}, { - "bare keys : non-empty strings composed only of [A-Za-z0-9_-].", - "quoted keys: same as \"basic strings\" or 'literal strings'.", - "dotted keys: sequence of bare or quoted keys joined with a dot." - })); -} - -// forward-decl to implement parse_array and parse_table -template -result parse_value(location&, const std::size_t n_rec); - -template -result, std::string> -parse_array(location& loc, const std::size_t n_rec) -{ - using value_type = Value; - using array_type = typename value_type::array_type; - - if(n_rec > TOML11_VALUE_RECURSION_LIMIT) - { - // parse_array does not have any way to handle recursive error currently... - throw syntax_error(std::string("toml::parse_array: recursion limit (" - TOML11_STRINGIZE(TOML11_VALUE_RECURSION_LIMIT) ") exceeded"), - source_location(loc)); - } - - const auto first = loc.iter(); - if(loc.iter() == loc.end()) - { - return err("toml::parse_array: input is empty"); - } - if(*loc.iter() != '[') - { - return err("toml::parse_array: token is not an array"); - } - loc.advance(); - - using lex_ws_comment_newline = repeat< - either, unlimited>; - - array_type retval; - while(loc.iter() != loc.end()) - { - lex_ws_comment_newline::invoke(loc); // skip - - if(loc.iter() != loc.end() && *loc.iter() == ']') - { - loc.advance(); // skip ']' - return ok(std::make_pair(retval, - region(loc, first, loc.iter()))); - } - - if(auto val = parse_value(loc, n_rec+1)) - { - // After TOML v1.0.0-rc.1, array becomes to be able to have values - // with different types. So here we will omit this by default. - // - // But some of the test-suite checks if the parser accepts a hetero- - // geneous arrays, so we keep this for a while. -#ifdef TOML11_DISALLOW_HETEROGENEOUS_ARRAYS - if(!retval.empty() && retval.front().type() != val.as_ok().type()) - { - auto array_start_loc = loc; - array_start_loc.reset(first); - - throw syntax_error(format_underline("toml::parse_array: " - "type of elements should be the same each other.", { - {source_location(array_start_loc), "array starts here"}, - { - retval.front().location(), - "value has type " + stringize(retval.front().type()) - }, - { - val.unwrap().location(), - "value has different type, " + stringize(val.unwrap().type()) - } - }), source_location(loc)); - } -#endif - retval.push_back(std::move(val.unwrap())); - } - else - { - auto array_start_loc = loc; - array_start_loc.reset(first); - - throw syntax_error(format_underline("toml::parse_array: " - "value having invalid format appeared in an array", { - {source_location(array_start_loc), "array starts here"}, - {source_location(loc), "it is not a valid value."} - }), source_location(loc)); - } - - using lex_array_separator = sequence, character<','>>; - const auto sp = lex_array_separator::invoke(loc); - if(!sp) - { - lex_ws_comment_newline::invoke(loc); - if(loc.iter() != loc.end() && *loc.iter() == ']') - { - loc.advance(); // skip ']' - return ok(std::make_pair(retval, - region(loc, first, loc.iter()))); - } - else - { - auto array_start_loc = loc; - array_start_loc.reset(first); - - throw syntax_error(format_underline("toml::parse_array:" - " missing array separator `,` after a value", { - {source_location(array_start_loc), "array starts here"}, - {source_location(loc), "should be `,`"} - }), source_location(loc)); - } - } - } - loc.reset(first); - throw syntax_error(format_underline("toml::parse_array: " - "array did not closed by `]`", - {{source_location(loc), "should be closed"}}), - source_location(loc)); -} - -template -result, region>, Value>, std::string> -parse_key_value_pair(location& loc, const std::size_t n_rec) -{ - using value_type = Value; - - const auto first = loc.iter(); - auto key_reg = parse_key(loc); - if(!key_reg) - { - std::string msg = std::move(key_reg.unwrap_err()); - // if the next token is keyvalue-separator, it means that there are no - // key. then we need to show error as "empty key is not allowed". - if(const auto keyval_sep = lex_keyval_sep::invoke(loc)) - { - loc.reset(first); - msg = format_underline("toml::parse_key_value_pair: " - "empty key is not allowed.", - {{source_location(loc), "key expected before '='"}}); - } - return err(std::move(msg)); - } - - const auto kvsp = lex_keyval_sep::invoke(loc); - if(!kvsp) - { - std::string msg; - // if the line contains '=' after the invalid sequence, possibly the - // error is in the key (like, invalid character in bare key). - const auto line_end = std::find(loc.iter(), loc.end(), '\n'); - if(std::find(loc.iter(), line_end, '=') != line_end) - { - msg = format_underline("toml::parse_key_value_pair: " - "invalid format for key", - {{source_location(loc), "invalid character in key"}}, - {"Did you forget '.' to separate dotted-key?", - "Allowed characters for bare key are [0-9a-zA-Z_-]."}); - } - else // if not, the error is lack of key-value separator. - { - msg = format_underline("toml::parse_key_value_pair: " - "missing key-value separator `=`", - {{source_location(loc), "should be `=`"}}); - } - loc.reset(first); - return err(std::move(msg)); - } - - const auto after_kvsp = loc.iter(); // err msg - auto val = parse_value(loc, n_rec); - if(!val) - { - std::string msg; - loc.reset(after_kvsp); - // check there is something not a comment/whitespace after `=` - if(sequence, maybe, lex_newline>::invoke(loc)) - { - loc.reset(after_kvsp); - msg = format_underline("toml::parse_key_value_pair: " - "missing value after key-value separator '='", - {{source_location(loc), "expected value, but got nothing"}}); - } - else // there is something not a comment/whitespace, so invalid format. - { - msg = std::move(val.unwrap_err()); - } - loc.reset(first); - return err(msg); - } - return ok(std::make_pair(std::move(key_reg.unwrap()), - std::move(val.unwrap()))); -} - -// for error messages. -template -std::string format_dotted_keys(InputIterator first, const InputIterator last) -{ - static_assert(std::is_same::value_type>::value,""); - - std::string retval(*first++); - for(; first != last; ++first) - { - retval += '.'; - retval += *first; - } - return retval; -} - -// forward decl for is_valid_forward_table_definition -result, region>, std::string> -parse_table_key(location& loc); -result, region>, std::string> -parse_array_table_key(location& loc); -template -result, std::string> -parse_inline_table(location& loc, const std::size_t n_rec); - -// The following toml file is allowed. -// ```toml -// [a.b.c] # here, table `a` has element `b`. -// foo = "bar" -// [a] # merge a = {baz = "qux"} to a = {b = {...}} -// baz = "qux" -// ``` -// But the following is not allowed. -// ```toml -// [a] -// b.c.foo = "bar" -// [a] # error! the same table [a] defined! -// baz = "qux" -// ``` -// The following is neither allowed. -// ```toml -// a = { b.c.foo = "bar"} -// [a] # error! the same table [a] defined! -// baz = "qux" -// ``` -// Here, it parses region of `tab->at(k)` as a table key and check the depth -// of the key. If the key region points deeper node, it would be allowed. -// Otherwise, the key points the same node. It would be rejected. -template -bool is_valid_forward_table_definition(const Value& fwd, const Value& inserting, - Iterator key_first, Iterator key_curr, Iterator key_last) -{ - // ------------------------------------------------------------------------ - // check type of the value to be inserted/merged - - std::string inserting_reg = ""; - if(const auto ptr = detail::get_region(inserting)) - { - inserting_reg = ptr->str(); - } - location inserting_def("internal", std::move(inserting_reg)); - if(const auto inlinetable = parse_inline_table(inserting_def, 0)) - { - // check if we are overwriting existing table. - // ```toml - // # NG - // a.b = 42 - // a = {d = 3.14} - // ``` - // Inserting an inline table to a existing super-table is not allowed in - // any case. If we found it, we can reject it without further checking. - return false; - } - - // Valid and invalid cases when inserting to the [a.b] table: - // - // ## Invalid - // - // ```toml - // # invalid - // [a] - // b.c.d = "foo" - // [a.b] # a.b is already defined and closed - // d = "bar" - // ``` - // ```toml - // # invalid - // a = {b.c.d = "foo"} - // [a.b] # a is already defined and inline table is closed - // d = "bar" - // ``` - // ```toml - // # invalid - // a.b.c.d = "foo" - // [a.b] # a.b is already defined and dotted-key table is closed - // d = "bar" - // ``` - // - // ## Valid - // - // ```toml - // # OK. a.b is defined, but is *overwritable* - // [a.b.c] - // d = "foo" - // [a.b] - // d = "bar" - // ``` - // ```toml - // # OK. a.b is defined, but is *overwritable* - // [a] - // b.c.d = "foo" - // b.e = "bar" - // ``` - - // ------------------------------------------------------------------------ - // check table defined before - - std::string internal = ""; - if(const auto ptr = detail::get_region(fwd)) - { - internal = ptr->str(); - } - location def("internal", std::move(internal)); - if(const auto tabkeys = parse_table_key(def)) // [table.key] - { - // table keys always contains all the nodes from the root. - const auto& tks = tabkeys.unwrap().first; - if(std::size_t(std::distance(key_first, key_last)) == tks.size() && - std::equal(tks.begin(), tks.end(), key_first)) - { - // the keys are equivalent. it is not allowed. - return false; - } - // the keys are not equivalent. it is allowed. - return true; - } - // nested array-of-table definition implicitly defines tables. - // those tables can be reopened. - if(const auto atabkeys = parse_array_table_key(def)) - { - // table keys always contains all the nodes from the root. - const auto& tks = atabkeys.unwrap().first; - if(std::size_t(std::distance(key_first, key_last)) == tks.size() && - std::equal(tks.begin(), tks.end(), key_first)) - { - // the keys are equivalent. it is not allowed. - return false; - } - // the keys are not equivalent. it is allowed. - return true; - } - if(const auto dotkeys = parse_key(def)) // a.b.c = "foo" - { - // consider the following case. - // [a] - // b.c = {d = 42} - // [a.b.c] - // e = 2.71 - // this defines the table [a.b.c] twice. no? - if(const auto reopening_dotkey_by_table = parse_table_key(inserting_def)) - { - // re-opening a dotkey-defined table by a table is invalid. - // only dotkey can append a key-val. Like: - // ```toml - // a.b.c = "foo" - // a.b.d = "bar" # OK. reopen `a.b` by dotkey - // [a.b] - // e = "bar" # Invalid. re-opening `a.b` by [a.b] is not allowed. - // ``` - return false; - } - - // a dotted key starts from the node representing a table in which the - // dotted key belongs to. - const auto& dks = dotkeys.unwrap().first; - if(std::size_t(std::distance(key_curr, key_last)) == dks.size() && - std::equal(dks.begin(), dks.end(), key_curr)) - { - // the keys are equivalent. it is not allowed. - return false; - } - // the keys are not equivalent. it is allowed. - return true; - } - return false; -} - -template -result -insert_nested_key(typename Value::table_type& root, const Value& v, - InputIterator iter, const InputIterator last, - region key_reg, - const bool is_array_of_table = false) -{ - static_assert(std::is_same::value_type>::value,""); - - using value_type = Value; - using table_type = typename value_type::table_type; - using array_type = typename value_type::array_type; - - const auto first = iter; - assert(iter != last); - - table_type* tab = std::addressof(root); - for(; iter != last; ++iter) // search recursively - { - const key& k = *iter; - if(std::next(iter) == last) // k is the last key - { - // XXX if the value is array-of-tables, there can be several - // tables that are in the same array. in that case, we need to - // find the last element and insert it to there. - if(is_array_of_table) - { - if(tab->count(k) == 1) // there is already an array of table - { - if(tab->at(k).is_table()) - { - // show special err msg for conflicting table - throw syntax_error(format_underline(concat_to_string( - "toml::insert_value: array of table (\"", - format_dotted_keys(first, last), - "\") cannot be defined"), { - {tab->at(k).location(), "table already defined"}, - {v.location(), "this conflicts with the previous table"} - }), v.location()); - } - else if(!(tab->at(k).is_array())) - { - throw syntax_error(format_underline(concat_to_string( - "toml::insert_value: array of table (\"", - format_dotted_keys(first, last), "\") collides with" - " existing value"), { - {tab->at(k).location(), - concat_to_string("this ", tab->at(k).type(), - " value already exists")}, - {v.location(), - "while inserting this array-of-tables"} - }), v.location()); - } - // the above if-else-if checks tab->at(k) is an array - auto& a = tab->at(k).as_array(); - // If table element is defined as [[array_of_tables]], it - // cannot be an empty array. If an array of tables is - // defined as `aot = []`, it cannot be appended. - if(a.empty() || !(a.front().is_table())) - { - throw syntax_error(format_underline(concat_to_string( - "toml::insert_value: array of table (\"", - format_dotted_keys(first, last), "\") collides with" - " existing value"), { - {tab->at(k).location(), - concat_to_string("this ", tab->at(k).type(), - " value already exists")}, - {v.location(), - "while inserting this array-of-tables"} - }), v.location()); - } - // avoid conflicting array of table like the following. - // ```toml - // a = [{b = 42}] # define a as an array of *inline* tables - // [[a]] # a is an array of *multi-line* tables - // b = 54 - // ``` - // Here, from the type information, these cannot be detected - // because inline table is also a table. - // But toml v0.5.0 explicitly says it is invalid. The above - // array-of-tables has a static size and appending to the - // array is invalid. - // In this library, multi-line table value has a region - // that points to the key of the table (e.g. [[a]]). By - // comparing the first two letters in key, we can detect - // the array-of-table is inline or multiline. - if(const auto ptr = detail::get_region(a.front())) - { - if(ptr->str().substr(0,2) != "[[") - { - throw syntax_error(format_underline(concat_to_string( - "toml::insert_value: array of table (\"", - format_dotted_keys(first, last), "\") collides " - "with existing array-of-tables"), { - {tab->at(k).location(), - concat_to_string("this ", tab->at(k).type(), - " value has static size")}, - {v.location(), - "appending it to the statically sized array"} - }), v.location()); - } - } - a.push_back(v); - return ok(true); - } - else // if not, we need to create the array of table - { - // XXX: Consider the following array of tables. - // ```toml - // # This is a comment. - // [[aot]] - // foo = "bar" - // ``` - // Here, the comment is for `aot`. But here, actually two - // values are defined. An array that contains tables, named - // `aot`, and the 0th element of the `aot`, `{foo = "bar"}`. - // Those two are different from each other. But both of them - // points to the same portion of the TOML file, `[[aot]]`, - // so `key_reg.comments()` returns `# This is a comment`. - // If it is assigned as a comment of `aot` defined here, the - // comment will be duplicated. Both the `aot` itself and - // the 0-th element will have the same comment. This causes - // "duplication of the same comments" bug when the data is - // serialized. - // Next, consider the following. - // ```toml - // # comment 1 - // aot = [ - // # comment 2 - // {foo = "bar"}, - // ] - // ``` - // In this case, we can distinguish those two comments. So - // here we need to add "comment 1" to the `aot` and - // "comment 2" to the 0th element of that. - // To distinguish those two, we check the key region. - std::vector comments{/* empty by default */}; - if(key_reg.str().substr(0, 2) != "[[") - { - comments = key_reg.comments(); - } - value_type aot(array_type(1, v), key_reg, std::move(comments)); - tab->insert(std::make_pair(k, aot)); - return ok(true); - } - } // end if(array of table) - - if(tab->count(k) == 1) - { - if(tab->at(k).is_table() && v.is_table()) - { - if(!is_valid_forward_table_definition( - tab->at(k), v, first, iter, last)) - { - throw syntax_error(format_underline(concat_to_string( - "toml::insert_value: table (\"", - format_dotted_keys(first, last), - "\") already exists."), { - {tab->at(k).location(), "table already exists here"}, - {v.location(), "table defined twice"} - }), v.location()); - } - // to allow the following toml file. - // [a.b.c] - // d = 42 - // [a] - // e = 2.71 - auto& t = tab->at(k).as_table(); - for(const auto& kv : v.as_table()) - { - if(tab->at(k).contains(kv.first)) - { - throw syntax_error(format_underline(concat_to_string( - "toml::insert_value: value (\"", - format_dotted_keys(first, last), - "\") already exists."), { - {t.at(kv.first).location(), "already exists here"}, - {v.location(), "this defined twice"} - }), v.location()); - } - t[kv.first] = kv.second; - } - detail::change_region(tab->at(k), key_reg); - return ok(true); - } - else if(v.is_table() && - tab->at(k).is_array() && - tab->at(k).as_array().size() > 0 && - tab->at(k).as_array().front().is_table()) - { - throw syntax_error(format_underline(concat_to_string( - "toml::insert_value: array of tables (\"", - format_dotted_keys(first, last), "\") already exists."), { - {tab->at(k).location(), "array of tables defined here"}, - {v.location(), "table conflicts with the previous array of table"} - }), v.location()); - } - else - { - throw syntax_error(format_underline(concat_to_string( - "toml::insert_value: value (\"", - format_dotted_keys(first, last), "\") already exists."), { - {tab->at(k).location(), "value already exists here"}, - {v.location(), "value defined twice"} - }), v.location()); - } - } - tab->insert(std::make_pair(k, v)); - return ok(true); - } - else // k is not the last one, we should insert recursively - { - // if there is no corresponding value, insert it first. - // related: you don't need to write - // # [x] - // # [x.y] - // to write - // [x.y.z] - if(tab->count(k) == 0) - { - // a table that is defined implicitly doesn't have any comments. - (*tab)[k] = value_type(table_type{}, key_reg, {/*no comment*/}); - } - - // type checking... - if(tab->at(k).is_table()) - { - // According to toml-lang/toml:36d3091b3 "Clarify that inline - // tables are immutable", check if it adds key-value pair to an - // inline table. - if(const auto* ptr = get_region(tab->at(k))) - { - // here, if the value is a (multi-line) table, the region - // should be something like `[table-name]`. - if(ptr->front() == '{') - { - throw syntax_error(format_underline(concat_to_string( - "toml::insert_value: inserting to an inline table (", - format_dotted_keys(first, std::next(iter)), - ") but inline tables are immutable"), { - {tab->at(k).location(), "inline tables are immutable"}, - {v.location(), "inserting this"} - }), v.location()); - } - } - tab = std::addressof((*tab)[k].as_table()); - } - else if(tab->at(k).is_array()) // inserting to array-of-tables? - { - auto& a = (*tab)[k].as_array(); - if(!a.back().is_table()) - { - throw syntax_error(format_underline(concat_to_string( - "toml::insert_value: target (", - format_dotted_keys(first, std::next(iter)), - ") is neither table nor an array of tables"), { - {a.back().location(), concat_to_string( - "actual type is ", a.back().type())}, - {v.location(), "inserting this"} - }), v.location()); - } - if(a.empty()) - { - throw syntax_error(format_underline(concat_to_string( - "toml::insert_value: table (\"", - format_dotted_keys(first, last), "\") conflicts with" - " existing value"), { - {tab->at(k).location(), std::string("this array is not insertable")}, - {v.location(), std::string("appending it to the statically sized array")} - }), v.location()); - } - if(const auto ptr = detail::get_region(a.at(0))) - { - if(ptr->str().substr(0,2) != "[[") - { - throw syntax_error(format_underline(concat_to_string( - "toml::insert_value: a table (\"", - format_dotted_keys(first, last), "\") cannot be " - "inserted to an existing inline array-of-tables"), { - {tab->at(k).location(), std::string("this array of table has a static size")}, - {v.location(), std::string("appending it to the statically sized array")} - }), v.location()); - } - } - tab = std::addressof(a.back().as_table()); - } - else - { - throw syntax_error(format_underline(concat_to_string( - "toml::insert_value: target (", - format_dotted_keys(first, std::next(iter)), - ") is neither table nor an array of tables"), { - {tab->at(k).location(), concat_to_string( - "actual type is ", tab->at(k).type())}, - {v.location(), "inserting this"} - }), v.location()); - } - } - } - return err(std::string("toml::detail::insert_nested_key: never reach here")); -} - -template -result, std::string> -parse_inline_table(location& loc, const std::size_t n_rec) -{ - using value_type = Value; - using table_type = typename value_type::table_type; - - if(n_rec > TOML11_VALUE_RECURSION_LIMIT) - { - throw syntax_error(std::string("toml::parse_inline_table: recursion limit (" - TOML11_STRINGIZE(TOML11_VALUE_RECURSION_LIMIT) ") exceeded"), - source_location(loc)); - } - - const auto first = loc.iter(); - table_type retval; - if(!(loc.iter() != loc.end() && *loc.iter() == '{')) - { - return err(format_underline("toml::parse_inline_table: ", - {{source_location(loc), "the next token is not an inline table"}})); - } - loc.advance(); - - // check if the inline table is an empty table = { } - maybe::invoke(loc); - if(loc.iter() != loc.end() && *loc.iter() == '}') - { - loc.advance(); // skip `}` - return ok(std::make_pair(retval, region(loc, first, loc.iter()))); - } - - // it starts from "{". it should be formatted as inline-table - while(loc.iter() != loc.end()) - { - const auto kv_r = parse_key_value_pair(loc, n_rec+1); - if(!kv_r) - { - return err(kv_r.unwrap_err()); - } - - const auto& kvpair = kv_r.unwrap(); - const std::vector& keys = kvpair.first.first; - const auto& key_reg = kvpair.first.second; - const value_type& val = kvpair.second; - - const auto inserted = - insert_nested_key(retval, val, keys.begin(), keys.end(), key_reg); - if(!inserted) - { - throw internal_error("toml::parse_inline_table: " - "failed to insert value into table: " + inserted.unwrap_err(), - source_location(loc)); - } - - using lex_table_separator = sequence, character<','>>; - const auto sp = lex_table_separator::invoke(loc); - - if(!sp) - { - maybe::invoke(loc); - - if(loc.iter() == loc.end()) - { - throw syntax_error(format_underline( - "toml::parse_inline_table: missing table separator `}` ", - {{source_location(loc), "should be `}`"}}), - source_location(loc)); - } - else if(*loc.iter() == '}') - { - loc.advance(); // skip `}` - return ok(std::make_pair( - retval, region(loc, first, loc.iter()))); - } - else if(*loc.iter() == '#' || *loc.iter() == '\r' || *loc.iter() == '\n') - { - throw syntax_error(format_underline( - "toml::parse_inline_table: missing curly brace `}`", - {{source_location(loc), "should be `}`"}}), - source_location(loc)); - } - else - { - throw syntax_error(format_underline( - "toml::parse_inline_table: missing table separator `,` ", - {{source_location(loc), "should be `,`"}}), - source_location(loc)); - } - } - else // `,` is found - { - maybe::invoke(loc); - if(loc.iter() != loc.end() && *loc.iter() == '}') - { - throw syntax_error(format_underline( - "toml::parse_inline_table: trailing comma is not allowed in" - " an inline table", - {{source_location(loc), "should be `}`"}}), - source_location(loc)); - } - } - } - loc.reset(first); - throw syntax_error(format_underline("toml::parse_inline_table: " - "inline table did not closed by `}`", - {{source_location(loc), "should be closed"}}), - source_location(loc)); -} - -inline result guess_number_type(const location& l) -{ - // This function tries to find some (common) mistakes by checking characters - // that follows the last character of a value. But it is often difficult - // because some non-newline characters can appear after a value. E.g. - // spaces, tabs, commas (in an array or inline table), closing brackets - // (of an array or inline table), comment-sign (#). Since this function - // does not parse further, those characters are always allowed to be there. - location loc = l; - - if(lex_offset_date_time::invoke(loc)) {return ok(value_t::offset_datetime);} - loc.reset(l.iter()); - - if(lex_local_date_time::invoke(loc)) - { - // bad offset may appear after this. - if(loc.iter() != loc.end() && (*loc.iter() == '+' || *loc.iter() == '-' - || *loc.iter() == 'Z' || *loc.iter() == 'z')) - { - return err(format_underline("bad offset: should be [+-]HH:MM or Z", - {{source_location(loc), "[+-]HH:MM or Z"}}, - {"pass: +09:00, -05:30", "fail: +9:00, -5:30"})); - } - return ok(value_t::local_datetime); - } - loc.reset(l.iter()); - - if(lex_local_date::invoke(loc)) - { - // bad time may appear after this. - // A space is allowed as a delimiter between local time. But there are - // both cases in which a space becomes valid or invalid. - // - invalid: 2019-06-16 7:00:00 - // - valid : 2019-06-16 07:00:00 - if(loc.iter() != loc.end()) - { - const auto c = *loc.iter(); - if(c == 'T' || c == 't') - { - return err(format_underline("bad time: should be HH:MM:SS.subsec", - {{source_location(loc), "HH:MM:SS.subsec"}}, - {"pass: 1979-05-27T07:32:00, 1979-05-27 07:32:00.999999", - "fail: 1979-05-27T7:32:00, 1979-05-27 17:32"})); - } - if('0' <= c && c <= '9') - { - return err(format_underline("bad time: missing T", - {{source_location(loc), "T or space required here"}}, - {"pass: 1979-05-27T07:32:00, 1979-05-27 07:32:00.999999", - "fail: 1979-05-27T7:32:00, 1979-05-27 7:32"})); - } - if(c == ' ' && std::next(loc.iter()) != loc.end() && - ('0' <= *std::next(loc.iter()) && *std::next(loc.iter())<= '9')) - { - loc.advance(); - return err(format_underline("bad time: should be HH:MM:SS.subsec", - {{source_location(loc), "HH:MM:SS.subsec"}}, - {"pass: 1979-05-27T07:32:00, 1979-05-27 07:32:00.999999", - "fail: 1979-05-27T7:32:00, 1979-05-27 7:32"})); - } - } - return ok(value_t::local_date); - } - loc.reset(l.iter()); - - if(lex_local_time::invoke(loc)) {return ok(value_t::local_time);} - loc.reset(l.iter()); - - if(lex_float::invoke(loc)) - { - if(loc.iter() != loc.end() && *loc.iter() == '_') - { - return err(format_underline("bad float: `_` should be surrounded by digits", - {{source_location(loc), "here"}}, - {"pass: +1.0, -2e-2, 3.141_592_653_589, inf, nan", - "fail: .0, 1., _1.0, 1.0_, 1_.0, 1.0__0"})); - } - return ok(value_t::floating); - } - loc.reset(l.iter()); - - if(lex_integer::invoke(loc)) - { - if(loc.iter() != loc.end()) - { - const auto c = *loc.iter(); - if(c == '_') - { - return err(format_underline("bad integer: `_` should be surrounded by digits", - {{source_location(loc), "here"}}, - {"pass: -42, 1_000, 1_2_3_4_5, 0xC0FFEE, 0b0010, 0o755", - "fail: 1__000, 0123"})); - } - if('0' <= c && c <= '9') - { - // leading zero. point '0' - loc.retrace(); - return err(format_underline("bad integer: leading zero", - {{source_location(loc), "here"}}, - {"pass: -42, 1_000, 1_2_3_4_5, 0xC0FFEE, 0b0010, 0o755", - "fail: 1__000, 0123"})); - } - if(c == ':' || c == '-') - { - return err(format_underline("bad datetime: invalid format", - {{source_location(loc), "here"}}, - {"pass: 1979-05-27T07:32:00-07:00, 1979-05-27 07:32:00.999999Z", - "fail: 1979-05-27T7:32:00-7:00, 1979-05-27 7:32-00:30"})); - } - if(c == '.' || c == 'e' || c == 'E') - { - return err(format_underline("bad float: invalid format", - {{source_location(loc), "here"}}, - {"pass: +1.0, -2e-2, 3.141_592_653_589, inf, nan", - "fail: .0, 1., _1.0, 1.0_, 1_.0, 1.0__0"})); - } - } - return ok(value_t::integer); - } - if(loc.iter() != loc.end() && *loc.iter() == '.') - { - return err(format_underline("bad float: invalid format", - {{source_location(loc), "integer part required before this"}}, - {"pass: +1.0, -2e-2, 3.141_592_653_589, inf, nan", - "fail: .0, 1., _1.0, 1.0_, 1_.0, 1.0__0"})); - } - if(loc.iter() != loc.end() && *loc.iter() == '_') - { - return err(format_underline("bad number: `_` should be surrounded by digits", - {{source_location(loc), "`_` is not surrounded by digits"}}, - {"pass: -42, 1_000, 1_2_3_4_5, 0xC0FFEE, 0b0010, 0o755", - "fail: 1__000, 0123"})); - } - return err(format_underline("bad format: unknown value appeared", - {{source_location(loc), "here"}})); -} - -inline result guess_value_type(const location& loc) -{ - switch(*loc.iter()) - { - case '"' : {return ok(value_t::string); } - case '\'': {return ok(value_t::string); } - case 't' : {return ok(value_t::boolean); } - case 'f' : {return ok(value_t::boolean); } - case '[' : {return ok(value_t::array); } - case '{' : {return ok(value_t::table); } - case 'i' : {return ok(value_t::floating);} // inf. - case 'n' : {return ok(value_t::floating);} // nan. - default : {return guess_number_type(loc);} - } -} - -template -result -parse_value_helper(result, std::string> rslt) -{ - if(rslt.is_ok()) - { - auto comments = rslt.as_ok().second.comments(); - return ok(Value(std::move(rslt.as_ok()), std::move(comments))); - } - else - { - return err(std::move(rslt.as_err())); - } -} - -template -result parse_value(location& loc, const std::size_t n_rec) -{ - const auto first = loc.iter(); - if(first == loc.end()) - { - return err(format_underline("toml::parse_value: input is empty", - {{source_location(loc), ""}})); - } - - const auto type = guess_value_type(loc); - if(!type) - { - return err(type.unwrap_err()); - } - - switch(type.unwrap()) - { - case value_t::boolean : {return parse_value_helper(parse_boolean(loc) );} - case value_t::integer : {return parse_value_helper(parse_integer(loc) );} - case value_t::floating : {return parse_value_helper(parse_floating(loc) );} - case value_t::string : {return parse_value_helper(parse_string(loc) );} - case value_t::offset_datetime: {return parse_value_helper(parse_offset_datetime(loc) );} - case value_t::local_datetime : {return parse_value_helper(parse_local_datetime(loc) );} - case value_t::local_date : {return parse_value_helper(parse_local_date(loc) );} - case value_t::local_time : {return parse_value_helper(parse_local_time(loc) );} - case value_t::array : {return parse_value_helper(parse_array(loc, n_rec));} - case value_t::table : {return parse_value_helper(parse_inline_table(loc, n_rec));} - default: - { - const auto msg = format_underline("toml::parse_value: " - "unknown token appeared", {{source_location(loc), "unknown"}}); - loc.reset(first); - return err(msg); - } - } -} - -inline result, region>, std::string> -parse_table_key(location& loc) -{ - if(auto token = lex_std_table::invoke(loc)) - { - location inner_loc(loc.name(), token.unwrap().str()); - - const auto open = lex_std_table_open::invoke(inner_loc); - if(!open || inner_loc.iter() == inner_loc.end()) - { - throw internal_error(format_underline( - "toml::parse_table_key: no `[`", - {{source_location(inner_loc), "should be `[`"}}), - source_location(inner_loc)); - } - // to skip [ a . b . c ] - // ^----------- this whitespace - lex_ws::invoke(inner_loc); - const auto keys = parse_key(inner_loc); - if(!keys) - { - throw internal_error(format_underline( - "toml::parse_table_key: invalid key", - {{source_location(inner_loc), "not key"}}), - source_location(inner_loc)); - } - // to skip [ a . b . c ] - // ^-- this whitespace - lex_ws::invoke(inner_loc); - const auto close = lex_std_table_close::invoke(inner_loc); - if(!close) - { - throw internal_error(format_underline( - "toml::parse_table_key: no `]`", - {{source_location(inner_loc), "should be `]`"}}), - source_location(inner_loc)); - } - - // after [table.key], newline or EOF(empty table) required. - if(loc.iter() != loc.end()) - { - using lex_newline_after_table_key = - sequence, maybe, lex_newline>; - const auto nl = lex_newline_after_table_key::invoke(loc); - if(!nl) - { - throw syntax_error(format_underline( - "toml::parse_table_key: newline required after [table.key]", - {{source_location(loc), "expected newline"}}), - source_location(loc)); - } - } - return ok(std::make_pair(keys.unwrap().first, token.unwrap())); - } - else - { - return err(format_underline("toml::parse_table_key: " - "not a valid table key", {{source_location(loc), "here"}})); - } -} - -inline result, region>, std::string> -parse_array_table_key(location& loc) -{ - if(auto token = lex_array_table::invoke(loc)) - { - location inner_loc(loc.name(), token.unwrap().str()); - - const auto open = lex_array_table_open::invoke(inner_loc); - if(!open || inner_loc.iter() == inner_loc.end()) - { - throw internal_error(format_underline( - "toml::parse_array_table_key: no `[[`", - {{source_location(inner_loc), "should be `[[`"}}), - source_location(inner_loc)); - } - lex_ws::invoke(inner_loc); - const auto keys = parse_key(inner_loc); - if(!keys) - { - throw internal_error(format_underline( - "toml::parse_array_table_key: invalid key", - {{source_location(inner_loc), "not a key"}}), - source_location(inner_loc)); - } - lex_ws::invoke(inner_loc); - const auto close = lex_array_table_close::invoke(inner_loc); - if(!close) - { - throw internal_error(format_underline( - "toml::parse_array_table_key: no `]]`", - {{source_location(inner_loc), "should be `]]`"}}), - source_location(inner_loc)); - } - - // after [[table.key]], newline or EOF(empty table) required. - if(loc.iter() != loc.end()) - { - using lex_newline_after_table_key = - sequence, maybe, lex_newline>; - const auto nl = lex_newline_after_table_key::invoke(loc); - if(!nl) - { - throw syntax_error(format_underline("toml::" - "parse_array_table_key: newline required after [[table.key]]", - {{source_location(loc), "expected newline"}}), - source_location(loc)); - } - } - return ok(std::make_pair(keys.unwrap().first, token.unwrap())); - } - else - { - return err(format_underline("toml::parse_array_table_key: " - "not a valid table key", {{source_location(loc), "here"}})); - } -} - -// parse table body (key-value pairs until the iter hits the next [tablekey]) -template -result -parse_ml_table(location& loc) -{ - using value_type = Value; - using table_type = typename value_type::table_type; - - const auto first = loc.iter(); - if(first == loc.end()) - { - return ok(table_type{}); - } - - // XXX at lest one newline is needed. - using skip_line = repeat< - sequence, maybe, lex_newline>, at_least<1>>; - skip_line::invoke(loc); - lex_ws::invoke(loc); - - table_type tab; - while(loc.iter() != loc.end()) - { - lex_ws::invoke(loc); - const auto before = loc.iter(); - if(const auto tmp = parse_array_table_key(loc)) // next table found - { - loc.reset(before); - return ok(tab); - } - if(const auto tmp = parse_table_key(loc)) // next table found - { - loc.reset(before); - return ok(tab); - } - - if(const auto kv = parse_key_value_pair(loc, 0)) - { - const auto& kvpair = kv.unwrap(); - const std::vector& keys = kvpair.first.first; - const auto& key_reg = kvpair.first.second; - const value_type& val = kvpair.second; - const auto inserted = - insert_nested_key(tab, val, keys.begin(), keys.end(), key_reg); - if(!inserted) - { - return err(inserted.unwrap_err()); - } - } - else - { - return err(kv.unwrap_err()); - } - - // comment lines are skipped by the above function call. - // However, since the `skip_line` requires at least 1 newline, it fails - // if the file ends with ws and/or comment without newline. - // `skip_line` matches `ws? + comment? + newline`, not `ws` or `comment` - // itself. To skip the last ws and/or comment, call lexers. - // It does not matter if these fails, so the return value is discarded. - lex_ws::invoke(loc); - lex_comment::invoke(loc); - - // skip_line is (whitespace? comment? newline)_{1,}. multiple empty lines - // and comments after the last key-value pairs are allowed. - const auto newline = skip_line::invoke(loc); - if(!newline && loc.iter() != loc.end()) - { - const auto before2 = loc.iter(); - lex_ws::invoke(loc); // skip whitespace - const auto msg = format_underline("toml::parse_table: " - "invalid line format", {{source_location(loc), concat_to_string( - "expected newline, but got '", show_char(*loc.iter()), "'.")}}); - loc.reset(before2); - return err(msg); - } - - // the skip_lines only matches with lines that includes newline. - // to skip the last line that includes comment and/or whitespace - // but no newline, call them one more time. - lex_ws::invoke(loc); - lex_comment::invoke(loc); - } - return ok(tab); -} - -template -result parse_toml_file(location& loc) -{ - using value_type = Value; - using table_type = typename value_type::table_type; - - const auto first = loc.iter(); - if(first == loc.end()) - { - // For empty files, return an empty table with an empty region (zero-length). - // Without the region, error messages would miss the filename. - return ok(value_type(table_type{}, region(loc, first, first), {})); - } - - // put the first line as a region of a file - // Here first != loc.end(), so taking std::next is okay - const region file(loc, first, std::next(loc.iter())); - - // The first successive comments that are separated from the first value - // by an empty line are for a file itself. - // ```toml - // # this is a comment for a file. - // - // key = "the first value" - // ``` - // ```toml - // # this is a comment for "the first value". - // key = "the first value" - // ``` - std::vector comments; - using lex_first_comments = sequence< - repeat, lex_comment, lex_newline>, at_least<1>>, - sequence, lex_newline> - >; - if(const auto token = lex_first_comments::invoke(loc)) - { - location inner_loc(loc.name(), token.unwrap().str()); - while(inner_loc.iter() != inner_loc.end()) - { - maybe::invoke(inner_loc); // remove ws if exists - if(lex_newline::invoke(inner_loc)) - { - assert(inner_loc.iter() == inner_loc.end()); - break; // empty line found. - } - auto com = lex_comment::invoke(inner_loc).unwrap().str(); - com.erase(com.begin()); // remove # sign - comments.push_back(std::move(com)); - lex_newline::invoke(inner_loc); - } - } - - table_type data; - // root object is also a table, but without [tablename] - if(const auto tab = parse_ml_table(loc)) - { - data = std::move(tab.unwrap()); - } - else // failed (empty table is regarded as success in parse_ml_table) - { - return err(tab.unwrap_err()); - } - while(loc.iter() != loc.end()) - { - // here, the region of [table] is regarded as the table-key because - // the table body is normally too big and it is not so informative - // if the first key-value pair of the table is shown in the error - // message. - if(const auto tabkey = parse_array_table_key(loc)) - { - const auto tab = parse_ml_table(loc); - if(!tab){return err(tab.unwrap_err());} - - const auto& tk = tabkey.unwrap(); - const auto& keys = tk.first; - const auto& reg = tk.second; - - const auto inserted = insert_nested_key(data, - value_type(tab.unwrap(), reg, reg.comments()), - keys.begin(), keys.end(), reg, - /*is_array_of_table=*/ true); - if(!inserted) {return err(inserted.unwrap_err());} - - continue; - } - if(const auto tabkey = parse_table_key(loc)) - { - const auto tab = parse_ml_table(loc); - if(!tab){return err(tab.unwrap_err());} - - const auto& tk = tabkey.unwrap(); - const auto& keys = tk.first; - const auto& reg = tk.second; - - const auto inserted = insert_nested_key(data, - value_type(tab.unwrap(), reg, reg.comments()), - keys.begin(), keys.end(), reg); - if(!inserted) {return err(inserted.unwrap_err());} - - continue; - } - return err(format_underline("toml::parse_toml_file: " - "unknown line appeared", {{source_location(loc), "unknown format"}})); - } - - return ok(Value(std::move(data), file, comments)); -} - -template class Table = std::unordered_map, - template class Array = std::vector> -basic_value -parse(std::vector letters, const std::string& fname) -{ - using value_type = basic_value; - - // append LF. - // Although TOML does not require LF at the EOF, to make parsing logic - // simpler, we "normalize" the content by adding LF if it does not exist. - // It also checks if the last char is CR, to avoid changing the meaning. - // This is not the *best* way to deal with the last character, but is a - // simple and quick fix. - if(!letters.empty() && letters.back() != '\n' && letters.back() != '\r') - { - letters.push_back('\n'); - } - - detail::location loc(fname, std::move(letters)); - - // skip BOM if exists. - // XXX component of BOM (like 0xEF) exceeds the representable range of - // signed char, so on some (actually, most) of the environment, these cannot - // be compared to char. However, since we are always out of luck, we need to - // check our chars are equivalent to BOM. To do this, first we need to - // convert char to unsigned char to guarantee the comparability. - if(loc.source()->size() >= 3) - { - std::array BOM; - std::memcpy(BOM.data(), loc.source()->data(), 3); - if(BOM[0] == 0xEF && BOM[1] == 0xBB && BOM[2] == 0xBF) - { - loc.advance(3); // BOM found. skip. - } - } - - if (auto data = detail::parse_toml_file(loc)) - { - return std::move(data).unwrap(); - } - else - { - throw syntax_error(std::move(data).unwrap_err(), source_location(loc)); - } -} - -} // detail - -template class Table = std::unordered_map, - template class Array = std::vector> -basic_value -parse(FILE * file, const std::string& fname) -{ - const long beg = std::ftell(file); - if (beg == -1l) - { - throw file_io_error(errno, "Failed to access", fname); - } - - const int res_seekend = std::fseek(file, 0, SEEK_END); - if (res_seekend != 0) - { - throw file_io_error(errno, "Failed to seek", fname); - } - - const long end = std::ftell(file); - if (end == -1l) - { - throw file_io_error(errno, "Failed to access", fname); - } - - const auto fsize = end - beg; - - const auto res_seekbeg = std::fseek(file, beg, SEEK_SET); - if (res_seekbeg != 0) - { - throw file_io_error(errno, "Failed to seek", fname); - } - - // read whole file as a sequence of char - assert(fsize >= 0); - std::vector letters(static_cast(fsize)); - std::fread(letters.data(), sizeof(char), static_cast(fsize), file); - - return detail::parse(letters, fname); -} - -template class Table = std::unordered_map, - template class Array = std::vector> -basic_value -parse(std::istream& is, std::string fname = "unknown file") -{ - const auto beg = is.tellg(); - is.seekg(0, std::ios::end); - const auto end = is.tellg(); - const auto fsize = end - beg; - is.seekg(beg); - - // read whole file as a sequence of char - assert(fsize >= 0); - std::vector letters(static_cast(fsize)); - is.read(letters.data(), fsize); - - return detail::parse(letters, fname); -} - -template class Table = std::unordered_map, - template class Array = std::vector> -basic_value parse(std::string fname) -{ - std::ifstream ifs(fname, std::ios_base::binary); - if(!ifs.good()) - { - throw std::ios_base::failure( - "toml::parse: Error opening file \"" + fname + "\""); - } - ifs.exceptions(std::ifstream::failbit | std::ifstream::badbit); - return parse(ifs, std::move(fname)); -} - -#ifdef TOML11_HAS_STD_FILESYSTEM -// This function just forwards `parse("filename.toml")` to std::string version -// to avoid the ambiguity in overload resolution. -// -// Both std::string and std::filesystem::path are convertible from const char*. -// Without this, both parse(std::string) and parse(std::filesystem::path) -// matches to parse("filename.toml"). This breaks the existing code. -// -// This function exactly matches to the invocation with c-string. -// So this function is preferred than others and the ambiguity disappears. -template class Table = std::unordered_map, - template class Array = std::vector> -basic_value parse(const char* fname) -{ - return parse(std::string(fname)); -} - -template class Table = std::unordered_map, - template class Array = std::vector> -basic_value parse(const std::filesystem::path& fpath) -{ - std::ifstream ifs(fpath, std::ios_base::binary); - if(!ifs.good()) - { - throw std::ios_base::failure( - "toml::parse: Error opening file \"" + fpath.string() + "\""); - } - ifs.exceptions(std::ifstream::failbit | std::ifstream::badbit); - return parse(ifs, fpath.string()); -} -#endif // TOML11_HAS_STD_FILESYSTEM - -} // toml -#endif// TOML11_PARSER_HPP diff --git a/3party/toml11/toml/region.hpp b/3party/toml11/toml/region.hpp deleted file mode 100644 index 72a6f11..0000000 --- a/3party/toml11/toml/region.hpp +++ /dev/null @@ -1,416 +0,0 @@ -// Copyright Toru Niina 2017. -// Distributed under the MIT License. -#ifndef TOML11_REGION_HPP -#define TOML11_REGION_HPP -#include -#include -#include -#include -#include -#include -#include -#include "color.hpp" - -namespace toml -{ -namespace detail -{ - -// helper function to avoid std::string(0, 'c') or std::string(iter, iter) -template -std::string make_string(Iterator first, Iterator last) -{ - if(first == last) {return "";} - return std::string(first, last); -} -inline std::string make_string(std::size_t len, char c) -{ - if(len == 0) {return "";} - return std::string(len, c); -} - -// region_base is a base class of location and region that are defined below. -// it will be used to generate better error messages. -struct region_base -{ - region_base() = default; - virtual ~region_base() = default; - region_base(const region_base&) = default; - region_base(region_base&& ) = default; - region_base& operator=(const region_base&) = default; - region_base& operator=(region_base&& ) = default; - - virtual bool is_ok() const noexcept {return false;} - virtual char front() const noexcept {return '\0';} - - virtual std::string str() const {return std::string("unknown region");} - virtual std::string name() const {return std::string("unknown file");} - virtual std::string line() const {return std::string("unknown line");} - virtual std::string line_num() const {return std::string("?");} - - // length of the region - virtual std::size_t size() const noexcept {return 0;} - // number of characters in the line before the region - virtual std::size_t before() const noexcept {return 0;} - // number of characters in the line after the region - virtual std::size_t after() const noexcept {return 0;} - - virtual std::vector comments() const {return {};} - // ```toml - // # comment_before - // key = "value" # comment_inline - // ``` -}; - -// location represents a position in a container, which contains a file content. -// it can be considered as a region that contains only one character. -// -// it contains pointer to the file content and iterator that points the current -// location. -struct location final : public region_base -{ - using const_iterator = typename std::vector::const_iterator; - using difference_type = typename std::iterator_traits::difference_type; - using source_ptr = std::shared_ptr>; - - location(std::string source_name, std::vector cont) - : source_(std::make_shared>(std::move(cont))), - line_number_(1), source_name_(std::move(source_name)), iter_(source_->cbegin()) - {} - location(std::string source_name, const std::string& cont) - : source_(std::make_shared>(cont.begin(), cont.end())), - line_number_(1), source_name_(std::move(source_name)), iter_(source_->cbegin()) - {} - - location(const location&) = default; - location(location&&) = default; - location& operator=(const location&) = default; - location& operator=(location&&) = default; - ~location() = default; - - bool is_ok() const noexcept override {return static_cast(source_);} - char front() const noexcept override {return *iter_;} - - // this const prohibits codes like `++(loc.iter())`. - std::add_const::type iter() const noexcept {return iter_;} - - const_iterator begin() const noexcept {return source_->cbegin();} - const_iterator end() const noexcept {return source_->cend();} - - // XXX `location::line_num()` used to be implemented using `std::count` to - // count a number of '\n'. But with a long toml file (typically, 10k lines), - // it becomes intolerably slow because each time it generates error messages, - // it counts '\n' from thousands of characters. To workaround it, I decided - // to introduce `location::line_number_` member variable and synchronize it - // to the location changes the point to look. So an overload of `iter()` - // which returns mutable reference is removed and `advance()`, `retrace()` - // and `reset()` is added. - void advance(difference_type n = 1) noexcept - { - this->line_number_ += static_cast( - std::count(this->iter_, std::next(this->iter_, n), '\n')); - this->iter_ += n; - return; - } - void retrace(difference_type n = 1) noexcept - { - this->line_number_ -= static_cast( - std::count(std::prev(this->iter_, n), this->iter_, '\n')); - this->iter_ -= n; - return; - } - void reset(const_iterator rollback) noexcept - { - // since c++11, std::distance works in both ways for random-access - // iterators and returns a negative value if `first > last`. - if(0 <= std::distance(rollback, this->iter_)) // rollback < iter - { - this->line_number_ -= static_cast( - std::count(rollback, this->iter_, '\n')); - } - else // iter < rollback [[unlikely]] - { - this->line_number_ += static_cast( - std::count(this->iter_, rollback, '\n')); - } - this->iter_ = rollback; - return; - } - - std::string str() const override {return make_string(1, *this->iter());} - std::string name() const override {return source_name_;} - - std::string line_num() const override - { - return std::to_string(this->line_number_); - } - - std::string line() const override - { - return make_string(this->line_begin(), this->line_end()); - } - - const_iterator line_begin() const noexcept - { - using reverse_iterator = std::reverse_iterator; - return std::find(reverse_iterator(this->iter()), - reverse_iterator(this->begin()), '\n').base(); - } - const_iterator line_end() const noexcept - { - return std::find(this->iter(), this->end(), '\n'); - } - - // location is always points a character. so the size is 1. - std::size_t size() const noexcept override - { - return 1u; - } - std::size_t before() const noexcept override - { - const auto sz = std::distance(this->line_begin(), this->iter()); - assert(sz >= 0); - return static_cast(sz); - } - std::size_t after() const noexcept override - { - const auto sz = std::distance(this->iter(), this->line_end()); - assert(sz >= 0); - return static_cast(sz); - } - - source_ptr const& source() const& noexcept {return source_;} - source_ptr&& source() && noexcept {return std::move(source_);} - - private: - - source_ptr source_; - std::size_t line_number_; - std::string source_name_; - const_iterator iter_; -}; - -// region represents a range in a container, which contains a file content. -// -// it contains pointer to the file content and iterator that points the first -// and last location. -struct region final : public region_base -{ - using const_iterator = typename std::vector::const_iterator; - using source_ptr = std::shared_ptr>; - - // delete default constructor. source_ never be null. - region() = delete; - - explicit region(const location& loc) - : source_(loc.source()), source_name_(loc.name()), - first_(loc.iter()), last_(loc.iter()) - {} - explicit region(location&& loc) - : source_(loc.source()), source_name_(loc.name()), - first_(loc.iter()), last_(loc.iter()) - {} - - region(const location& loc, const_iterator f, const_iterator l) - : source_(loc.source()), source_name_(loc.name()), first_(f), last_(l) - {} - region(location&& loc, const_iterator f, const_iterator l) - : source_(loc.source()), source_name_(loc.name()), first_(f), last_(l) - {} - - region(const region&) = default; - region(region&&) = default; - region& operator=(const region&) = default; - region& operator=(region&&) = default; - ~region() = default; - - region& operator+=(const region& other) - { - // different regions cannot be concatenated - assert(this->source_ == other.source_ && this->last_ == other.first_); - - this->last_ = other.last_; - return *this; - } - - bool is_ok() const noexcept override {return static_cast(source_);} - char front() const noexcept override {return *first_;} - - std::string str() const override {return make_string(first_, last_);} - std::string line() const override - { - if(this->contain_newline()) - { - return make_string(this->line_begin(), - std::find(this->line_begin(), this->last(), '\n')); - } - return make_string(this->line_begin(), this->line_end()); - } - std::string line_num() const override - { - return std::to_string(1 + std::count(this->begin(), this->first(), '\n')); - } - - std::size_t size() const noexcept override - { - const auto sz = std::distance(first_, last_); - assert(sz >= 0); - return static_cast(sz); - } - std::size_t before() const noexcept override - { - const auto sz = std::distance(this->line_begin(), this->first()); - assert(sz >= 0); - return static_cast(sz); - } - std::size_t after() const noexcept override - { - const auto sz = std::distance(this->last(), this->line_end()); - assert(sz >= 0); - return static_cast(sz); - } - - bool contain_newline() const noexcept - { - return std::find(this->first(), this->last(), '\n') != this->last(); - } - - const_iterator line_begin() const noexcept - { - using reverse_iterator = std::reverse_iterator; - return std::find(reverse_iterator(this->first()), - reverse_iterator(this->begin()), '\n').base(); - } - const_iterator line_end() const noexcept - { - return std::find(this->last(), this->end(), '\n'); - } - - const_iterator begin() const noexcept {return source_->cbegin();} - const_iterator end() const noexcept {return source_->cend();} - const_iterator first() const noexcept {return first_;} - const_iterator last() const noexcept {return last_;} - - source_ptr const& source() const& noexcept {return source_;} - source_ptr&& source() && noexcept {return std::move(source_);} - - std::string name() const override {return source_name_;} - - std::vector comments() const override - { - // assuming the current region (`*this`) points a value. - // ```toml - // a = "value" - // ^^^^^^^- this region - // ``` - using rev_iter = std::reverse_iterator; - - std::vector com{}; - { - // find comments just before the current region. - // ```toml - // # this should be collected. - // # this also. - // a = value # not this. - // ``` - - // # this is a comment for `a`, not array elements. - // a = [1, 2, 3, 4, 5] - if(this->first() == std::find_if(this->line_begin(), this->first(), - [](const char c) noexcept -> bool {return c == '[' || c == '{';})) - { - auto iter = this->line_begin(); // points the first character - while(iter != this->begin()) - { - iter = std::prev(iter); - - // range [line_start, iter) represents the previous line - const auto line_start = std::find( - rev_iter(iter), rev_iter(this->begin()), '\n').base(); - const auto comment_found = std::find(line_start, iter, '#'); - if(comment_found == iter) - { - break; // comment not found. - } - - // exclude the following case. - // > a = "foo" # comment // <-- this is not a comment for b but a. - // > b = "current value" - if(std::all_of(line_start, comment_found, - [](const char c) noexcept -> bool { - return c == ' ' || c == '\t'; - })) - { - // unwrap the first '#' by std::next. - auto s = make_string(std::next(comment_found), iter); - if(!s.empty() && s.back() == '\r') {s.pop_back();} - com.push_back(std::move(s)); - } - else - { - break; - } - iter = line_start; - } - } - } - - if(com.size() > 1) - { - std::reverse(com.begin(), com.end()); - } - - { - // find comments just after the current region. - // ```toml - // # not this. - // a = value # this one. - // a = [ # not this (technically difficult) - // - // ] # and this. - // ``` - // The reason why it's difficult is that it requires parsing in the - // following case. - // ```toml - // a = [ 10 # this comment is for `10`. not for `a` but `a[0]`. - // # ... - // ] # this is apparently a comment for a. - // - // b = [ - // 3.14 ] # there is no way to add a comment to `3.14` currently. - // - // c = [ - // 3.14 # do this if you need a comment here. - // ] - // ``` - const auto comment_found = - std::find(this->last(), this->line_end(), '#'); - if(comment_found != this->line_end()) // '#' found - { - // table = {key = "value"} # what is this for? - // the above comment is not for "value", but {key="value"}. - if(comment_found == std::find_if(this->last(), comment_found, - [](const char c) noexcept -> bool { - return !(c == ' ' || c == '\t' || c == ','); - })) - { - // unwrap the first '#' by std::next. - auto s = make_string(std::next(comment_found), this->line_end()); - if(!s.empty() && s.back() == '\r') {s.pop_back();} - com.push_back(std::move(s)); - } - } - } - return com; - } - - private: - - source_ptr source_; - std::string source_name_; - const_iterator first_, last_; -}; - -} // detail -} // toml -#endif// TOML11_REGION_H diff --git a/3party/toml11/toml/result.hpp b/3party/toml11/toml/result.hpp deleted file mode 100644 index 77cd46c..0000000 --- a/3party/toml11/toml/result.hpp +++ /dev/null @@ -1,717 +0,0 @@ -// Copyright Toru Niina 2017. -// Distributed under the MIT License. -#ifndef TOML11_RESULT_HPP -#define TOML11_RESULT_HPP -#include "traits.hpp" -#include -#include -#include -#include -#include -#include -#include - -namespace toml -{ - -template -struct success -{ - using value_type = T; - value_type value; - - explicit success(const value_type& v) - noexcept(std::is_nothrow_copy_constructible::value) - : value(v) - {} - explicit success(value_type&& v) - noexcept(std::is_nothrow_move_constructible::value) - : value(std::move(v)) - {} - - template - explicit success(U&& v): value(std::forward(v)) {} - - template - explicit success(const success& v): value(v.value) {} - template - explicit success(success&& v): value(std::move(v.value)) {} - - ~success() = default; - success(const success&) = default; - success(success&&) = default; - success& operator=(const success&) = default; - success& operator=(success&&) = default; -}; - -template -struct failure -{ - using value_type = T; - value_type value; - - explicit failure(const value_type& v) - noexcept(std::is_nothrow_copy_constructible::value) - : value(v) - {} - explicit failure(value_type&& v) - noexcept(std::is_nothrow_move_constructible::value) - : value(std::move(v)) - {} - - template - explicit failure(U&& v): value(std::forward(v)) {} - - template - explicit failure(const failure& v): value(v.value) {} - template - explicit failure(failure&& v): value(std::move(v.value)) {} - - ~failure() = default; - failure(const failure&) = default; - failure(failure&&) = default; - failure& operator=(const failure&) = default; - failure& operator=(failure&&) = default; -}; - -template -success::type>::type> -ok(T&& v) -{ - return success< - typename std::remove_cv::type>::type - >(std::forward(v)); -} -template -failure::type>::type> -err(T&& v) -{ - return failure< - typename std::remove_cv::type>::type - >(std::forward(v)); -} - -inline success ok(const char* literal) -{ - return success(std::string(literal)); -} -inline failure err(const char* literal) -{ - return failure(std::string(literal)); -} - - -template -struct result -{ - using value_type = T; - using error_type = E; - using success_type = success; - using failure_type = failure; - - result(const success_type& s): is_ok_(true) - { - auto tmp = ::new(std::addressof(this->succ)) success_type(s); - assert(tmp == std::addressof(this->succ)); - (void)tmp; - } - result(const failure_type& f): is_ok_(false) - { - auto tmp = ::new(std::addressof(this->fail)) failure_type(f); - assert(tmp == std::addressof(this->fail)); - (void)tmp; - } - result(success_type&& s): is_ok_(true) - { - auto tmp = ::new(std::addressof(this->succ)) success_type(std::move(s)); - assert(tmp == std::addressof(this->succ)); - (void)tmp; - } - result(failure_type&& f): is_ok_(false) - { - auto tmp = ::new(std::addressof(this->fail)) failure_type(std::move(f)); - assert(tmp == std::addressof(this->fail)); - (void)tmp; - } - - template - result(const success& s): is_ok_(true) - { - auto tmp = ::new(std::addressof(this->succ)) success_type(s.value); - assert(tmp == std::addressof(this->succ)); - (void)tmp; - } - template - result(const failure& f): is_ok_(false) - { - auto tmp = ::new(std::addressof(this->fail)) failure_type(f.value); - assert(tmp == std::addressof(this->fail)); - (void)tmp; - } - template - result(success&& s): is_ok_(true) - { - auto tmp = ::new(std::addressof(this->succ)) success_type(std::move(s.value)); - assert(tmp == std::addressof(this->succ)); - (void)tmp; - } - template - result(failure&& f): is_ok_(false) - { - auto tmp = ::new(std::addressof(this->fail)) failure_type(std::move(f.value)); - assert(tmp == std::addressof(this->fail)); - (void)tmp; - } - - result& operator=(const success_type& s) - { - this->cleanup(); - this->is_ok_ = true; - auto tmp = ::new(std::addressof(this->succ)) success_type(s); - assert(tmp == std::addressof(this->succ)); - (void)tmp; - return *this; - } - result& operator=(const failure_type& f) - { - this->cleanup(); - this->is_ok_ = false; - auto tmp = ::new(std::addressof(this->fail)) failure_type(f); - assert(tmp == std::addressof(this->fail)); - (void)tmp; - return *this; - } - result& operator=(success_type&& s) - { - this->cleanup(); - this->is_ok_ = true; - auto tmp = ::new(std::addressof(this->succ)) success_type(std::move(s)); - assert(tmp == std::addressof(this->succ)); - (void)tmp; - return *this; - } - result& operator=(failure_type&& f) - { - this->cleanup(); - this->is_ok_ = false; - auto tmp = ::new(std::addressof(this->fail)) failure_type(std::move(f)); - assert(tmp == std::addressof(this->fail)); - (void)tmp; - return *this; - } - - template - result& operator=(const success& s) - { - this->cleanup(); - this->is_ok_ = true; - auto tmp = ::new(std::addressof(this->succ)) success_type(s.value); - assert(tmp == std::addressof(this->succ)); - (void)tmp; - return *this; - } - template - result& operator=(const failure& f) - { - this->cleanup(); - this->is_ok_ = false; - auto tmp = ::new(std::addressof(this->fail)) failure_type(f.value); - assert(tmp == std::addressof(this->fail)); - (void)tmp; - return *this; - } - template - result& operator=(success&& s) - { - this->cleanup(); - this->is_ok_ = true; - auto tmp = ::new(std::addressof(this->succ)) success_type(std::move(s.value)); - assert(tmp == std::addressof(this->succ)); - (void)tmp; - return *this; - } - template - result& operator=(failure&& f) - { - this->cleanup(); - this->is_ok_ = false; - auto tmp = ::new(std::addressof(this->fail)) failure_type(std::move(f.value)); - assert(tmp == std::addressof(this->fail)); - (void)tmp; - return *this; - } - - ~result() noexcept {this->cleanup();} - - result(const result& other): is_ok_(other.is_ok()) - { - if(other.is_ok()) - { - auto tmp = ::new(std::addressof(this->succ)) success_type(other.as_ok()); - assert(tmp == std::addressof(this->succ)); - (void)tmp; - } - else - { - auto tmp = ::new(std::addressof(this->fail)) failure_type(other.as_err()); - assert(tmp == std::addressof(this->fail)); - (void)tmp; - } - } - result(result&& other): is_ok_(other.is_ok()) - { - if(other.is_ok()) - { - auto tmp = ::new(std::addressof(this->succ)) success_type(std::move(other.as_ok())); - assert(tmp == std::addressof(this->succ)); - (void)tmp; - } - else - { - auto tmp = ::new(std::addressof(this->fail)) failure_type(std::move(other.as_err())); - assert(tmp == std::addressof(this->fail)); - (void)tmp; - } - } - - template - result(const result& other): is_ok_(other.is_ok()) - { - if(other.is_ok()) - { - auto tmp = ::new(std::addressof(this->succ)) success_type(other.as_ok()); - assert(tmp == std::addressof(this->succ)); - (void)tmp; - } - else - { - auto tmp = ::new(std::addressof(this->fail)) failure_type(other.as_err()); - assert(tmp == std::addressof(this->fail)); - (void)tmp; - } - } - template - result(result&& other): is_ok_(other.is_ok()) - { - if(other.is_ok()) - { - auto tmp = ::new(std::addressof(this->succ)) success_type(std::move(other.as_ok())); - assert(tmp == std::addressof(this->succ)); - (void)tmp; - } - else - { - auto tmp = ::new(std::addressof(this->fail)) failure_type(std::move(other.as_err())); - assert(tmp == std::addressof(this->fail)); - (void)tmp; - } - } - - result& operator=(const result& other) - { - this->cleanup(); - if(other.is_ok()) - { - auto tmp = ::new(std::addressof(this->succ)) success_type(other.as_ok()); - assert(tmp == std::addressof(this->succ)); - (void)tmp; - } - else - { - auto tmp = ::new(std::addressof(this->fail)) failure_type(other.as_err()); - assert(tmp == std::addressof(this->fail)); - (void)tmp; - } - is_ok_ = other.is_ok(); - return *this; - } - result& operator=(result&& other) - { - this->cleanup(); - if(other.is_ok()) - { - auto tmp = ::new(std::addressof(this->succ)) success_type(std::move(other.as_ok())); - assert(tmp == std::addressof(this->succ)); - (void)tmp; - } - else - { - auto tmp = ::new(std::addressof(this->fail)) failure_type(std::move(other.as_err())); - assert(tmp == std::addressof(this->fail)); - (void)tmp; - } - is_ok_ = other.is_ok(); - return *this; - } - - template - result& operator=(const result& other) - { - this->cleanup(); - if(other.is_ok()) - { - auto tmp = ::new(std::addressof(this->succ)) success_type(other.as_ok()); - assert(tmp == std::addressof(this->succ)); - (void)tmp; - } - else - { - auto tmp = ::new(std::addressof(this->fail)) failure_type(other.as_err()); - assert(tmp == std::addressof(this->fail)); - (void)tmp; - } - is_ok_ = other.is_ok(); - return *this; - } - template - result& operator=(result&& other) - { - this->cleanup(); - if(other.is_ok()) - { - auto tmp = ::new(std::addressof(this->succ)) success_type(std::move(other.as_ok())); - assert(tmp == std::addressof(this->succ)); - (void)tmp; - } - else - { - auto tmp = ::new(std::addressof(this->fail)) failure_type(std::move(other.as_err())); - assert(tmp == std::addressof(this->fail)); - (void)tmp; - } - is_ok_ = other.is_ok(); - return *this; - } - - bool is_ok() const noexcept {return is_ok_;} - bool is_err() const noexcept {return !is_ok_;} - - operator bool() const noexcept {return is_ok_;} - - value_type& unwrap() & - { - if(is_err()) - { - throw std::runtime_error("toml::result: bad unwrap: " + - format_error(this->as_err())); - } - return this->succ.value; - } - value_type const& unwrap() const& - { - if(is_err()) - { - throw std::runtime_error("toml::result: bad unwrap: " + - format_error(this->as_err())); - } - return this->succ.value; - } - value_type&& unwrap() && - { - if(is_err()) - { - throw std::runtime_error("toml::result: bad unwrap: " + - format_error(this->as_err())); - } - return std::move(this->succ.value); - } - - value_type& unwrap_or(value_type& opt) & - { - if(is_err()) {return opt;} - return this->succ.value; - } - value_type const& unwrap_or(value_type const& opt) const& - { - if(is_err()) {return opt;} - return this->succ.value; - } - value_type unwrap_or(value_type opt) && - { - if(is_err()) {return opt;} - return this->succ.value; - } - - error_type& unwrap_err() & - { - if(is_ok()) {throw std::runtime_error("toml::result: bad unwrap_err");} - return this->fail.value; - } - error_type const& unwrap_err() const& - { - if(is_ok()) {throw std::runtime_error("toml::result: bad unwrap_err");} - return this->fail.value; - } - error_type&& unwrap_err() && - { - if(is_ok()) {throw std::runtime_error("toml::result: bad unwrap_err");} - return std::move(this->fail.value); - } - - value_type& as_ok() & noexcept {return this->succ.value;} - value_type const& as_ok() const& noexcept {return this->succ.value;} - value_type&& as_ok() && noexcept {return std::move(this->succ.value);} - - error_type& as_err() & noexcept {return this->fail.value;} - error_type const& as_err() const& noexcept {return this->fail.value;} - error_type&& as_err() && noexcept {return std::move(this->fail.value);} - - - // prerequisities - // F: T -> U - // retval: result - template - result, error_type> - map(F&& f) & - { - if(this->is_ok()){return ok(f(this->as_ok()));} - return err(this->as_err()); - } - template - result, error_type> - map(F&& f) const& - { - if(this->is_ok()){return ok(f(this->as_ok()));} - return err(this->as_err()); - } - template - result, error_type> - map(F&& f) && - { - if(this->is_ok()){return ok(f(std::move(this->as_ok())));} - return err(std::move(this->as_err())); - } - - // prerequisities - // F: E -> F - // retval: result - template - result> - map_err(F&& f) & - { - if(this->is_err()){return err(f(this->as_err()));} - return ok(this->as_ok()); - } - template - result> - map_err(F&& f) const& - { - if(this->is_err()){return err(f(this->as_err()));} - return ok(this->as_ok()); - } - template - result> - map_err(F&& f) && - { - if(this->is_err()){return err(f(std::move(this->as_err())));} - return ok(std::move(this->as_ok())); - } - - // prerequisities - // F: T -> U - // retval: U - template - detail::return_type_of_t - map_or_else(F&& f, U&& opt) & - { - if(this->is_err()){return std::forward(opt);} - return f(this->as_ok()); - } - template - detail::return_type_of_t - map_or_else(F&& f, U&& opt) const& - { - if(this->is_err()){return std::forward(opt);} - return f(this->as_ok()); - } - template - detail::return_type_of_t - map_or_else(F&& f, U&& opt) && - { - if(this->is_err()){return std::forward(opt);} - return f(std::move(this->as_ok())); - } - - // prerequisities - // F: E -> U - // retval: U - template - detail::return_type_of_t - map_err_or_else(F&& f, U&& opt) & - { - if(this->is_ok()){return std::forward(opt);} - return f(this->as_err()); - } - template - detail::return_type_of_t - map_err_or_else(F&& f, U&& opt) const& - { - if(this->is_ok()){return std::forward(opt);} - return f(this->as_err()); - } - template - detail::return_type_of_t - map_err_or_else(F&& f, U&& opt) && - { - if(this->is_ok()){return std::forward(opt);} - return f(std::move(this->as_err())); - } - - // prerequisities: - // F: func T -> U - // toml::err(error_type) should be convertible to U. - // normally, type U is another result and E is convertible to F - template - detail::return_type_of_t - and_then(F&& f) & - { - if(this->is_ok()){return f(this->as_ok());} - return err(this->as_err()); - } - template - detail::return_type_of_t - and_then(F&& f) const& - { - if(this->is_ok()){return f(this->as_ok());} - return err(this->as_err()); - } - template - detail::return_type_of_t - and_then(F&& f) && - { - if(this->is_ok()){return f(std::move(this->as_ok()));} - return err(std::move(this->as_err())); - } - - // prerequisities: - // F: func E -> U - // toml::ok(value_type) should be convertible to U. - // normally, type U is another result and T is convertible to S - template - detail::return_type_of_t - or_else(F&& f) & - { - if(this->is_err()){return f(this->as_err());} - return ok(this->as_ok()); - } - template - detail::return_type_of_t - or_else(F&& f) const& - { - if(this->is_err()){return f(this->as_err());} - return ok(this->as_ok()); - } - template - detail::return_type_of_t - or_else(F&& f) && - { - if(this->is_err()){return f(std::move(this->as_err()));} - return ok(std::move(this->as_ok())); - } - - // if *this is error, returns *this. otherwise, returns other. - result and_other(const result& other) const& - { - return this->is_err() ? *this : other; - } - result and_other(result&& other) && - { - return this->is_err() ? std::move(*this) : std::move(other); - } - - // if *this is okay, returns *this. otherwise, returns other. - result or_other(const result& other) const& - { - return this->is_ok() ? *this : other; - } - result or_other(result&& other) && - { - return this->is_ok() ? std::move(*this) : std::move(other); - } - - void swap(result& other) - { - result tmp(std::move(*this)); - *this = std::move(other); - other = std::move(tmp); - return ; - } - - private: - - static std::string format_error(std::exception const& excpt) - { - return std::string(excpt.what()); - } - template::value, std::nullptr_t>::type = nullptr> - static std::string format_error(U const& others) - { - std::ostringstream oss; oss << others; - return oss.str(); - } - - void cleanup() noexcept - { - if(this->is_ok_) {this->succ.~success_type();} - else {this->fail.~failure_type();} - return; - } - - private: - - bool is_ok_; - union - { - success_type succ; - failure_type fail; - }; -}; - -template -void swap(result& lhs, result& rhs) -{ - lhs.swap(rhs); - return; -} - -// this might be confusing because it eagerly evaluated, while in the other -// cases operator && and || are short-circuited. -// -// template -// inline result -// operator&&(const result& lhs, const result& rhs) noexcept -// { -// return lhs.is_ok() ? rhs : lhs; -// } -// -// template -// inline result -// operator||(const result& lhs, const result& rhs) noexcept -// { -// return lhs.is_ok() ? lhs : rhs; -// } - -// ---------------------------------------------------------------------------- -// re-use result as a optional with none_t - -namespace detail -{ -struct none_t {}; -inline bool operator==(const none_t&, const none_t&) noexcept {return true;} -inline bool operator!=(const none_t&, const none_t&) noexcept {return false;} -inline bool operator< (const none_t&, const none_t&) noexcept {return false;} -inline bool operator<=(const none_t&, const none_t&) noexcept {return true;} -inline bool operator> (const none_t&, const none_t&) noexcept {return false;} -inline bool operator>=(const none_t&, const none_t&) noexcept {return true;} -template -std::basic_ostream& -operator<<(std::basic_ostream& os, const none_t&) -{ - os << "none"; - return os; -} -inline failure none() noexcept {return failure{none_t{}};} -} // detail -} // toml11 -#endif// TOML11_RESULT_H diff --git a/3party/toml11/toml/serializer.hpp b/3party/toml11/toml/serializer.hpp deleted file mode 100644 index 23fb89a..0000000 --- a/3party/toml11/toml/serializer.hpp +++ /dev/null @@ -1,994 +0,0 @@ -// Copyright Toru Niina 2019. -// Distributed under the MIT License. -#ifndef TOML11_SERIALIZER_HPP -#define TOML11_SERIALIZER_HPP -#include -#include - -#include - -#if defined(_WIN32) -#include -#elif defined(__APPLE__) || defined(__FreeBSD__) -#include -#elif defined(__linux__) -#include -#endif - -#include "lexer.hpp" -#include "value.hpp" - -namespace toml -{ - -// This function serialize a key. It checks a string is a bare key and -// escapes special characters if the string is not compatible to a bare key. -// ```cpp -// std::string k("non.bare.key"); // the key itself includes `.`s. -// std::string formatted = toml::format_key(k); -// assert(formatted == "\"non.bare.key\""); -// ``` -// -// This function is exposed to make it easy to write a user-defined serializer. -// Since toml restricts characters available in a bare key, generally a string -// should be escaped. But checking whether a string needs to be surrounded by -// a `"` and escaping some special character is boring. -template -std::basic_string -format_key(const std::basic_string& k) -{ - if(k.empty()) - { - return std::string("\"\""); - } - - // check the key can be a bare (unquoted) key - detail::location loc(k, std::vector(k.begin(), k.end())); - detail::lex_unquoted_key::invoke(loc); - if(loc.iter() == loc.end()) - { - return k; // all the tokens are consumed. the key is unquoted-key. - } - - //if it includes special characters, then format it in a "quoted" key. - std::basic_string serialized("\""); - for(const char c : k) - { - switch(c) - { - case '\\': {serialized += "\\\\"; break;} - case '\"': {serialized += "\\\""; break;} - case '\b': {serialized += "\\b"; break;} - case '\t': {serialized += "\\t"; break;} - case '\f': {serialized += "\\f"; break;} - case '\n': {serialized += "\\n"; break;} - case '\r': {serialized += "\\r"; break;} - default: { - if (c >= 0x00 && c < 0x20) - { - std::array buf; - std::snprintf(buf.data(), buf.size(), "\\u00%02x", static_cast(c)); - serialized += buf.data(); - } - else - { - serialized += c; - } - break; - } - } - } - serialized += "\""; - return serialized; -} - -template -std::basic_string -format_keys(const std::vector>& keys) -{ - if(keys.empty()) - { - return std::string("\"\""); - } - - std::basic_string serialized; - for(const auto& ky : keys) - { - serialized += format_key(ky); - serialized += charT('.'); - } - serialized.pop_back(); // remove the last dot '.' - return serialized; -} - -template -struct serializer -{ - static_assert(detail::is_basic_value::value, - "toml::serializer is for toml::value and its variants, " - "toml::basic_value<...>."); - - using value_type = Value; - using key_type = typename value_type::key_type ; - using comment_type = typename value_type::comment_type ; - using boolean_type = typename value_type::boolean_type ; - using integer_type = typename value_type::integer_type ; - using floating_type = typename value_type::floating_type ; - using string_type = typename value_type::string_type ; - using local_time_type = typename value_type::local_time_type ; - using local_date_type = typename value_type::local_date_type ; - using local_datetime_type = typename value_type::local_datetime_type ; - using offset_datetime_type = typename value_type::offset_datetime_type; - using array_type = typename value_type::array_type ; - using table_type = typename value_type::table_type ; - - serializer(const std::size_t w = 80u, - const int float_prec = std::numeric_limits::max_digits10, - const bool can_be_inlined = false, - const bool no_comment = false, - std::vector ks = {}, - const bool value_has_comment = false) - : can_be_inlined_(can_be_inlined), no_comment_(no_comment), - value_has_comment_(value_has_comment && !no_comment), - float_prec_(float_prec), width_(w), keys_(std::move(ks)) - {} - ~serializer() = default; - - std::string operator()(const boolean_type& b) const - { - return b ? "true" : "false"; - } - std::string operator()(const integer_type i) const - { -#if defined(_WIN32) - _configthreadlocale(_ENABLE_PER_THREAD_LOCALE); - const std::string original_locale(setlocale(LC_NUMERIC, nullptr)); - setlocale(LC_NUMERIC, "C"); -#elif defined(__APPLE__) || defined(__FreeBSD__) || defined(__linux__) - const auto c_locale = newlocale(LC_NUMERIC_MASK, "C", locale_t(0)); - locale_t original_locale(0); - if(c_locale != locale_t(0)) - { - original_locale = uselocale(c_locale); - } -#endif - - const auto str = std::to_string(i); - -#if defined(_WIN32) - setlocale(LC_NUMERIC, original_locale.c_str()); - _configthreadlocale(_DISABLE_PER_THREAD_LOCALE); -#elif defined(__APPLE__) || defined(__FreeBSD__) || defined(__linux__) - if(original_locale != locale_t(0)) - { - uselocale(original_locale); - } -#endif - return str; - } - std::string operator()(const floating_type f) const - { - if(std::isnan(f)) - { - if(std::signbit(f)) - { - return std::string("-nan"); - } - else - { - return std::string("nan"); - } - } - else if(!std::isfinite(f)) - { - if(std::signbit(f)) - { - return std::string("-inf"); - } - else - { - return std::string("inf"); - } - } - - // set locale to "C". - // To make it thread-local, we use OS-specific features. - // If we set process-global locale, it can break other thread that also - // outputs something simultaneously. -#if defined(_WIN32) - _configthreadlocale(_ENABLE_PER_THREAD_LOCALE); - const std::string original_locale(setlocale(LC_NUMERIC, nullptr)); - setlocale(LC_NUMERIC, "C"); -#elif defined(__APPLE__) || defined(__FreeBSD__) || defined(__linux__) - const auto c_locale = newlocale(LC_NUMERIC_MASK, "C", locale_t(0)); - locale_t original_locale(0); - if(c_locale != locale_t(0)) - { - original_locale = uselocale(c_locale); - } -#endif - - const auto fmt = "%.*g"; - const auto bsz = std::snprintf(nullptr, 0, fmt, this->float_prec_, f); - // +1 for null character(\0) - std::vector buf(static_cast(bsz + 1), '\0'); - std::snprintf(buf.data(), buf.size(), fmt, this->float_prec_, f); - - // restore the original locale -#if defined(_WIN32) - setlocale(LC_NUMERIC, original_locale.c_str()); - _configthreadlocale(_DISABLE_PER_THREAD_LOCALE); -#elif defined(__APPLE__) || defined(__FreeBSD__) || defined(__linux__) - if(original_locale != locale_t(0)) - { - uselocale(original_locale); - } -#endif - - std::string token(buf.begin(), std::prev(buf.end())); - if(!token.empty() && token.back() == '.') // 1. => 1.0 - { - token += '0'; - } - - const auto e = std::find_if( - token.cbegin(), token.cend(), [](const char c) noexcept -> bool { - return c == 'e' || c == 'E'; - }); - const auto has_exponent = (token.cend() != e); - const auto has_fraction = (token.cend() != std::find( - token.cbegin(), token.cend(), '.')); - - if(!has_exponent && !has_fraction) - { - // the resulting value does not have any float specific part! - token += ".0"; - } - return token; - } - std::string operator()(const string_type& s) const - { - if(s.kind == string_t::basic) - { - if((std::find(s.str.cbegin(), s.str.cend(), '\n') != s.str.cend() || - std::find(s.str.cbegin(), s.str.cend(), '\"') != s.str.cend()) && - this->width_ != (std::numeric_limits::max)()) - { - // if linefeed or double-quote is contained, - // make it multiline basic string. - const auto escaped = this->escape_ml_basic_string(s.str); - std::string open("\"\"\""); - std::string close("\"\"\""); - if(escaped.find('\n') != std::string::npos || - this->width_ < escaped.size() + 6) - { - // if the string body contains newline or is enough long, - // add newlines after and before delimiters. - open += "\n"; - close = std::string("\\\n") + close; - } - return open + escaped + close; - } - - // no linefeed. try to make it oneline-string. - std::string oneline = this->escape_basic_string(s.str); - if(oneline.size() + 2 < width_ || width_ < 2) - { - const std::string quote("\""); - return quote + oneline + quote; - } - - // the line is too long compared to the specified width. - // split it into multiple lines. - std::string token("\"\"\"\n"); - while(!oneline.empty()) - { - if(oneline.size() < width_) - { - token += oneline; - oneline.clear(); - } - else if(oneline.at(width_-2) == '\\') - { - token += oneline.substr(0, width_-2); - token += "\\\n"; - oneline.erase(0, width_-2); - } - else - { - token += oneline.substr(0, width_-1); - token += "\\\n"; - oneline.erase(0, width_-1); - } - } - return token + std::string("\\\n\"\"\""); - } - else // the string `s` is literal-string. - { - if(std::find(s.str.cbegin(), s.str.cend(), '\n') != s.str.cend() || - std::find(s.str.cbegin(), s.str.cend(), '\'') != s.str.cend() ) - { - std::string open("'''"); - if(this->width_ + 6 < s.str.size()) - { - open += '\n'; // the first newline is ignored by TOML spec - } - const std::string close("'''"); - return open + s.str + close; - } - else - { - const std::string quote("'"); - return quote + s.str + quote; - } - } - } - - std::string operator()(const local_date_type& d) const - { - std::ostringstream oss; - oss << d; - return oss.str(); - } - std::string operator()(const local_time_type& t) const - { - std::ostringstream oss; - oss << t; - return oss.str(); - } - std::string operator()(const local_datetime_type& dt) const - { - std::ostringstream oss; - oss << dt; - return oss.str(); - } - std::string operator()(const offset_datetime_type& odt) const - { - std::ostringstream oss; - oss << odt; - return oss.str(); - } - - std::string operator()(const array_type& v) const - { - if(v.empty()) - { - return std::string("[]"); - } - if(this->is_array_of_tables(v)) - { - return make_array_of_tables(v); - } - - // not an array of tables. normal array. - // first, try to make it inline if none of the elements have a comment. - if( ! this->has_comment_inside(v)) - { - const auto inl = this->make_inline_array(v); - if(inl.size() < this->width_ && - std::find(inl.cbegin(), inl.cend(), '\n') == inl.cend()) - { - return inl; - } - } - - // if the length exceeds this->width_, print multiline array. - // key = [ - // # ... - // 42, - // ... - // ] - std::string token; - std::string current_line; - token += "[\n"; - for(const auto& item : v) - { - if( ! item.comments().empty() && !no_comment_) - { - // if comment exists, the element must be the only element in the line. - // e.g. the following is not allowed. - // ```toml - // array = [ - // # comment for what? - // 1, 2, 3, 4, 5 - // ] - // ``` - if(!current_line.empty()) - { - if(current_line.back() != '\n') - { - current_line += '\n'; - } - token += current_line; - current_line.clear(); - } - for(const auto& c : item.comments()) - { - token += '#'; - token += c; - token += '\n'; - } - token += toml::visit(*this, item); - if(!token.empty() && token.back() == '\n') {token.pop_back();} - token += ",\n"; - continue; - } - std::string next_elem; - if(item.is_table()) - { - serializer ser(*this); - ser.can_be_inlined_ = true; - ser.width_ = (std::numeric_limits::max)(); - next_elem += toml::visit(ser, item); - } - else - { - next_elem += toml::visit(*this, item); - } - - // comma before newline. - if(!next_elem.empty() && next_elem.back() == '\n') {next_elem.pop_back();} - - // if current line does not exceeds the width limit, continue. - if(current_line.size() + next_elem.size() + 1 < this->width_) - { - current_line += next_elem; - current_line += ','; - } - else if(current_line.empty()) - { - // if current line was empty, force put the next_elem because - // next_elem is not splittable - token += next_elem; - token += ",\n"; - // current_line is kept empty - } - else // reset current_line - { - assert(current_line.back() == ','); - token += current_line; - token += '\n'; - current_line = next_elem; - current_line += ','; - } - } - if(!current_line.empty()) - { - if(!current_line.empty() && current_line.back() != '\n') - { - current_line += '\n'; - } - token += current_line; - } - token += "]\n"; - return token; - } - - // templatize for any table-like container - std::string operator()(const table_type& v) const - { - // if an element has a comment, then it can't be inlined. - // table = {# how can we write a comment for this? key = "value"} - if(this->can_be_inlined_ && !(this->has_comment_inside(v))) - { - std::string token; - if(!this->keys_.empty()) - { - token += format_key(this->keys_.back()); - token += " = "; - } - token += this->make_inline_table(v); - if(token.size() < this->width_ && - token.end() == std::find(token.begin(), token.end(), '\n')) - { - return token; - } - } - - std::string token; - if(!keys_.empty()) - { - token += '['; - token += format_keys(keys_); - token += "]\n"; - } - token += this->make_multiline_table(v); - return token; - } - - private: - - std::string escape_basic_string(const std::string& s) const - { - //XXX assuming `s` is a valid utf-8 sequence. - std::string retval; - for(const char c : s) - { - switch(c) - { - case '\\': {retval += "\\\\"; break;} - case '\"': {retval += "\\\""; break;} - case '\b': {retval += "\\b"; break;} - case '\t': {retval += "\\t"; break;} - case '\f': {retval += "\\f"; break;} - case '\n': {retval += "\\n"; break;} - case '\r': {retval += "\\r"; break;} - default : - { - if((0x00 <= c && c <= 0x08) || (0x0A <= c && c <= 0x1F) || c == 0x7F) - { - retval += "\\u00"; - retval += char(48 + (c / 16)); - retval += char((c % 16 < 10 ? 48 : 55) + (c % 16)); - } - else - { - retval += c; - } - } - } - } - return retval; - } - - std::string escape_ml_basic_string(const std::string& s) const - { - std::string retval; - for(auto i=s.cbegin(), e=s.cend(); i!=e; ++i) - { - switch(*i) - { - case '\\': {retval += "\\\\"; break;} - // One or two consecutive "s are allowed. - // Later we will check there are no three consecutive "s. - // case '\"': {retval += "\\\""; break;} - case '\b': {retval += "\\b"; break;} - case '\t': {retval += "\\t"; break;} - case '\f': {retval += "\\f"; break;} - case '\n': {retval += "\n"; break;} - case '\r': - { - if(std::next(i) != e && *std::next(i) == '\n') - { - retval += "\r\n"; - ++i; - } - else - { - retval += "\\r"; - } - break; - } - default : - { - const auto c = *i; - if((0x00 <= c && c <= 0x08) || (0x0A <= c && c <= 0x1F) || c == 0x7F) - { - retval += "\\u00"; - retval += char(48 + (c / 16)); - retval += char((c % 16 < 10 ? 48 : 55) + (c % 16)); - } - else - { - retval += c; - } - } - - } - } - // Only 1 or 2 consecutive `"`s are allowed in multiline basic string. - // 3 consecutive `"`s are considered as a closing delimiter. - // We need to check if there are 3 or more consecutive `"`s and insert - // backslash to break them down into several short `"`s like the `str6` - // in the following example. - // ```toml - // str4 = """Here are two quotation marks: "". Simple enough.""" - // # str5 = """Here are three quotation marks: """.""" # INVALID - // str5 = """Here are three quotation marks: ""\".""" - // str6 = """Here are fifteen quotation marks: ""\"""\"""\"""\"""\".""" - // ``` - auto found_3_quotes = retval.find("\"\"\""); - while(found_3_quotes != std::string::npos) - { - retval.replace(found_3_quotes, 3, "\"\"\\\""); - found_3_quotes = retval.find("\"\"\""); - } - return retval; - } - - // if an element of a table or an array has a comment, it cannot be inlined. - bool has_comment_inside(const array_type& a) const noexcept - { - // if no_comment is set, comments would not be written. - if(this->no_comment_) {return false;} - - for(const auto& v : a) - { - if(!v.comments().empty()) {return true;} - } - return false; - } - bool has_comment_inside(const table_type& t) const noexcept - { - // if no_comment is set, comments would not be written. - if(this->no_comment_) {return false;} - - for(const auto& kv : t) - { - if(!kv.second.comments().empty()) {return true;} - } - return false; - } - - std::string make_inline_array(const array_type& v) const - { - assert(!has_comment_inside(v)); - std::string token; - token += '['; - bool is_first = true; - for(const auto& item : v) - { - if(is_first) {is_first = false;} else {token += ',';} - token += visit(serializer( - (std::numeric_limits::max)(), this->float_prec_, - /* inlined */ true, /*no comment*/ false, /*keys*/ {}, - /*has_comment*/ !item.comments().empty()), item); - } - token += ']'; - return token; - } - - std::string make_inline_table(const table_type& v) const - { - assert(!has_comment_inside(v)); - assert(this->can_be_inlined_); - std::string token; - token += '{'; - bool is_first = true; - for(const auto& kv : v) - { - // in inline tables, trailing comma is not allowed (toml-lang #569). - if(is_first) {is_first = false;} else {token += ',';} - token += format_key(kv.first); - token += '='; - token += visit(serializer( - (std::numeric_limits::max)(), this->float_prec_, - /* inlined */ true, /*no comment*/ false, /*keys*/ {}, - /*has_comment*/ !kv.second.comments().empty()), kv.second); - } - token += '}'; - return token; - } - - std::string make_multiline_table(const table_type& v) const - { - std::string token; - - // print non-table elements first. - // ```toml - // [foo] # a table we're writing now here - // key = "value" # <- non-table element, "key" - // # ... - // [foo.bar] # <- table element, "bar" - // ``` - // because after printing [foo.bar], the remaining non-table values will - // be assigned into [foo.bar], not [foo]. Those values should be printed - // earlier. - for(const auto& kv : v) - { - if(kv.second.is_table() || is_array_of_tables(kv.second)) - { - continue; - } - - token += write_comments(kv.second); - - const auto key_and_sep = format_key(kv.first) + " = "; - const auto residual_width = (this->width_ > key_and_sep.size()) ? - this->width_ - key_and_sep.size() : 0; - token += key_and_sep; - token += visit(serializer(residual_width, this->float_prec_, - /*can be inlined*/ true, /*no comment*/ false, /*keys*/ {}, - /*has_comment*/ !kv.second.comments().empty()), kv.second); - - if(token.back() != '\n') - { - token += '\n'; - } - } - - // normal tables / array of tables - - // after multiline table appeared, the other tables cannot be inline - // because the table would be assigned into the table. - // [foo] - // ... - // bar = {...} # <- bar will be a member of [foo]. - bool multiline_table_printed = false; - for(const auto& kv : v) - { - if(!kv.second.is_table() && !is_array_of_tables(kv.second)) - { - continue; // other stuff are already serialized. skip them. - } - - std::vector ks(this->keys_); - ks.push_back(kv.first); - - auto tmp = visit(serializer(this->width_, this->float_prec_, - !multiline_table_printed, this->no_comment_, ks, - /*has_comment*/ !kv.second.comments().empty()), kv.second); - - // If it is the first time to print a multi-line table, it would be - // helpful to separate normal key-value pair and subtables by a - // newline. - // (this checks if the current key-value pair contains newlines. - // but it is not perfect because multi-line string can also contain - // a newline. in such a case, an empty line will be written) TODO - if((!multiline_table_printed) && - std::find(tmp.cbegin(), tmp.cend(), '\n') != tmp.cend()) - { - multiline_table_printed = true; - token += '\n'; // separate key-value pairs and subtables - - token += write_comments(kv.second); - token += tmp; - - // care about recursive tables (all tables in each level prints - // newline and there will be a full of newlines) - if(tmp.substr(tmp.size() - 2, 2) != "\n\n" && - tmp.substr(tmp.size() - 4, 4) != "\r\n\r\n" ) - { - token += '\n'; - } - } - else - { - token += write_comments(kv.second); - token += tmp; - token += '\n'; - } - } - return token; - } - - std::string make_array_of_tables(const array_type& v) const - { - // if it's not inlined, we need to add `[[table.key]]`. - // but if it can be inlined, we can format it as the following. - // ``` - // table.key = [ - // {...}, - // # comment - // {...}, - // ] - // ``` - // This function checks if inlinization is possible or not, and then - // format the array-of-tables in a proper way. - // - // Note about comments: - // - // If the array itself has a comment (value_has_comment_ == true), we - // should try to make it inline. - // ```toml - // # comment about array - // array = [ - // # comment about table element - // {of = "table"} - // ] - // ``` - // If it is formatted as a multiline table, the two comments becomes - // indistinguishable. - // ```toml - // # comment about array - // # comment about table element - // [[array]] - // of = "table" - // ``` - // So we need to try to make it inline, and it force-inlines regardless - // of the line width limit. - // It may fail if the element of a table has comment. In that case, - // the array-of-tables will be formatted as a multiline table. - if(this->can_be_inlined_ || this->value_has_comment_) - { - std::string token; - if(!keys_.empty()) - { - token += format_key(keys_.back()); - token += " = "; - } - - bool failed = false; - token += "[\n"; - for(const auto& item : v) - { - // if an element of the table has a comment, the table - // cannot be inlined. - if(this->has_comment_inside(item.as_table())) - { - failed = true; - break; - } - // write comments for the table itself - token += write_comments(item); - - const auto t = this->make_inline_table(item.as_table()); - - if(t.size() + 1 > width_ || // +1 for the last comma {...}, - std::find(t.cbegin(), t.cend(), '\n') != t.cend()) - { - // if the value itself has a comment, ignore the line width limit - if( ! this->value_has_comment_) - { - failed = true; - break; - } - } - token += t; - token += ",\n"; - } - - if( ! failed) - { - token += "]\n"; - return token; - } - // if failed, serialize them as [[array.of.tables]]. - } - - std::string token; - for(const auto& item : v) - { - token += write_comments(item); - token += "[["; - token += format_keys(keys_); - token += "]]\n"; - token += this->make_multiline_table(item.as_table()); - } - return token; - } - - std::string write_comments(const value_type& v) const - { - std::string retval; - if(this->no_comment_) {return retval;} - - for(const auto& c : v.comments()) - { - retval += '#'; - retval += c; - retval += '\n'; - } - return retval; - } - - bool is_array_of_tables(const value_type& v) const - { - if(!v.is_array() || v.as_array().empty()) {return false;} - return is_array_of_tables(v.as_array()); - } - bool is_array_of_tables(const array_type& v) const - { - // Since TOML v0.5.0, heterogeneous arrays are allowed. So we need to - // check all the element in an array to check if the array is an array - // of tables. - return std::all_of(v.begin(), v.end(), [](const value_type& elem) { - return elem.is_table(); - }); - } - - private: - - bool can_be_inlined_; - bool no_comment_; - bool value_has_comment_; - int float_prec_; - std::size_t width_; - std::vector keys_; -}; - -template class M, template class V> -std::string -format(const basic_value& v, std::size_t w = 80u, - int fprec = std::numeric_limits::max_digits10, - bool no_comment = false, bool force_inline = false) -{ - using value_type = basic_value; - // if value is a table, it is considered to be a root object. - // the root object can't be an inline table. - if(v.is_table()) - { - std::ostringstream oss; - if(!v.comments().empty()) - { - oss << v.comments(); - oss << '\n'; // to split the file comment from the first element - } - const auto serialized = visit(serializer(w, fprec, false, no_comment), v); - oss << serialized; - return oss.str(); - } - return visit(serializer(w, fprec, force_inline), v); -} - -namespace detail -{ -template -int comment_index(std::basic_ostream&) -{ - static const int index = std::ios_base::xalloc(); - return index; -} -} // detail - -template -std::basic_ostream& -nocomment(std::basic_ostream& os) -{ - // by default, it is zero. and by default, it shows comments. - os.iword(detail::comment_index(os)) = 1; - return os; -} - -template -std::basic_ostream& -showcomment(std::basic_ostream& os) -{ - // by default, it is zero. and by default, it shows comments. - os.iword(detail::comment_index(os)) = 0; - return os; -} - -template class M, template class V> -std::basic_ostream& -operator<<(std::basic_ostream& os, const basic_value& v) -{ - using value_type = basic_value; - - // get status of std::setw(). - const auto w = static_cast(os.width()); - const int fprec = static_cast(os.precision()); - os.width(0); - - // by default, iword is initialized by 0. And by default, toml11 outputs - // comments. So `0` means showcomment. 1 means nocommnet. - const bool no_comment = (1 == os.iword(detail::comment_index(os))); - - if(!no_comment && v.is_table() && !v.comments().empty()) - { - os << v.comments(); - os << '\n'; // to split the file comment from the first element - } - // the root object can't be an inline table. so pass `false`. - const auto serialized = visit(serializer(w, fprec, no_comment, false), v); - os << serialized; - - // if v is a non-table value, and has only one comment, then - // put a comment just after a value. in the following way. - // - // ```toml - // key = "value" # comment. - // ``` - // - // Since the top-level toml object is a table, one who want to put a - // non-table toml value must use this in a following way. - // - // ```cpp - // toml::value v; - // std::cout << "user-defined-key = " << v << std::endl; - // ``` - // - // In this case, it is impossible to put comments before key-value pair. - // The only way to preserve comments is to put all of them after a value. - if(!no_comment && !v.is_table() && !v.comments().empty()) - { - os << " #"; - for(const auto& c : v.comments()) {os << c;} - } - return os; -} - -} // toml -#endif// TOML11_SERIALIZER_HPP diff --git a/3party/toml11/toml/source_location.hpp b/3party/toml11/toml/source_location.hpp deleted file mode 100644 index 135024f..0000000 --- a/3party/toml11/toml/source_location.hpp +++ /dev/null @@ -1,239 +0,0 @@ -// Copyright Toru Niina 2019. -// Distributed under the MIT License. -#ifndef TOML11_SOURCE_LOCATION_HPP -#define TOML11_SOURCE_LOCATION_HPP -#include -#include - -#include "region.hpp" - -namespace toml -{ - -// A struct to contain location in a toml file. -// The interface imitates std::experimental::source_location, -// but not completely the same. -// -// It would be constructed by toml::value. It can be used to generate -// user-defined error messages. -// -// - std::uint_least32_t line() const noexcept -// - returns the line number where the region is on. -// - std::uint_least32_t column() const noexcept -// - returns the column number where the region starts. -// - std::uint_least32_t region() const noexcept -// - returns the size of the region. -// -// +-- line() +-- region of interest (region() == 9) -// v .---+---. -// 12 | value = "foo bar" -// ^ -// +-- column() -// -// - std::string const& file_name() const noexcept; -// - name of the file. -// - std::string const& line_str() const noexcept; -// - the whole line that contains the region of interest. -// -struct source_location -{ - public: - - source_location() - : line_num_(1), column_num_(1), region_size_(1), - file_name_("unknown file"), line_str_("") - {} - - explicit source_location(const detail::region_base* reg) - : line_num_(1), column_num_(1), region_size_(1), - file_name_("unknown file"), line_str_("") - { - if(reg) - { - if(reg->line_num() != detail::region_base().line_num()) - { - line_num_ = static_cast( - std::stoul(reg->line_num())); - } - column_num_ = static_cast(reg->before() + 1); - region_size_ = static_cast(reg->size()); - file_name_ = reg->name(); - line_str_ = reg->line(); - } - } - - explicit source_location(const detail::region& reg) - : line_num_(static_cast(std::stoul(reg.line_num()))), - column_num_(static_cast(reg.before() + 1)), - region_size_(static_cast(reg.size())), - file_name_(reg.name()), - line_str_ (reg.line()) - {} - explicit source_location(const detail::location& loc) - : line_num_(static_cast(std::stoul(loc.line_num()))), - column_num_(static_cast(loc.before() + 1)), - region_size_(static_cast(loc.size())), - file_name_(loc.name()), - line_str_ (loc.line()) - {} - - ~source_location() = default; - source_location(source_location const&) = default; - source_location(source_location &&) = default; - source_location& operator=(source_location const&) = default; - source_location& operator=(source_location &&) = default; - - std::uint_least32_t line() const noexcept {return line_num_;} - std::uint_least32_t column() const noexcept {return column_num_;} - std::uint_least32_t region() const noexcept {return region_size_;} - - std::string const& file_name() const noexcept {return file_name_;} - std::string const& line_str() const noexcept {return line_str_;} - - private: - - std::uint_least32_t line_num_; - std::uint_least32_t column_num_; - std::uint_least32_t region_size_; - std::string file_name_; - std::string line_str_; -}; - -namespace detail -{ - -// internal error message generation. -inline std::string format_underline(const std::string& message, - const std::vector>& loc_com, - const std::vector& helps = {}, - const bool colorize = TOML11_ERROR_MESSAGE_COLORIZED) -{ - std::size_t line_num_width = 0; - for(const auto& lc : loc_com) - { - std::uint_least32_t line = lc.first.line(); - std::size_t digit = 0; - while(line != 0) - { - line /= 10; - digit += 1; - } - line_num_width = (std::max)(line_num_width, digit); - } - // 1 is the minimum width - line_num_width = std::max(line_num_width, 1); - - std::ostringstream retval; - - if(color::should_color() || colorize) - { - retval << color::colorize; // turn on ANSI color - } - - // XXX - // Here, before `colorize` support, it does not output `[error]` prefix - // automatically. So some user may output it manually and this change may - // duplicate the prefix. To avoid it, check the first 7 characters and - // if it is "[error]", it removes that part from the message shown. - if(message.size() > 7 && message.substr(0, 7) == "[error]") - { - retval -#ifndef TOML11_NO_ERROR_PREFIX - << color::bold << color::red << "[error]" << color::reset -#endif - << color::bold << message.substr(7) << color::reset << '\n'; - } - else - { - retval -#ifndef TOML11_NO_ERROR_PREFIX - << color::bold << color::red << "[error] " << color::reset -#endif - << color::bold << message << color::reset << '\n'; - } - - const auto format_one_location = [line_num_width] - (std::ostringstream& oss, - const source_location& loc, const std::string& comment) -> void - { - oss << ' ' << color::bold << color::blue - << std::setw(static_cast(line_num_width)) - << std::right << loc.line() << " | " << color::reset - << loc.line_str() << '\n'; - - oss << make_string(line_num_width + 1, ' ') - << color::bold << color::blue << " | " << color::reset - << make_string(loc.column()-1 /*1-origin*/, ' '); - - if(loc.region() == 1) - { - // invalid - // ^------ - oss << color::bold << color::red << "^---" << color::reset; - } - else - { - // invalid - // ~~~~~~~ - const auto underline_len = (std::min)( - static_cast(loc.region()), loc.line_str().size()); - oss << color::bold << color::red - << make_string(underline_len, '~') << color::reset; - } - oss << ' '; - oss << comment; - return; - }; - - assert(!loc_com.empty()); - - // --> example.toml - // | - retval << color::bold << color::blue << " --> " << color::reset - << loc_com.front().first.file_name() << '\n'; - retval << make_string(line_num_width + 1, ' ') - << color::bold << color::blue << " |\n" << color::reset; - // 1 | key value - // | ^--- missing = - format_one_location(retval, loc_com.front().first, loc_com.front().second); - - // process the rest of the locations - for(std::size_t i=1; i filename.toml" again - { - retval << color::bold << color::blue << " --> " << color::reset - << curr.first.file_name() << '\n'; - retval << make_string(line_num_width + 1, ' ') - << color::bold << color::blue << " |\n" << color::reset; - } - - format_one_location(retval, curr.first, curr.second); - } - - if(!helps.empty()) - { - retval << '\n'; - retval << make_string(line_num_width + 1, ' '); - retval << color::bold << color::blue << " |" << color::reset; - for(const auto& help : helps) - { - retval << color::bold << "\nHint: " << color::reset; - retval << help; - } - } - return retval.str(); -} - -} // detail -} // toml -#endif// TOML11_SOURCE_LOCATION_HPP diff --git a/3party/toml11/toml/storage.hpp b/3party/toml11/toml/storage.hpp deleted file mode 100644 index 202f903..0000000 --- a/3party/toml11/toml/storage.hpp +++ /dev/null @@ -1,43 +0,0 @@ -// Copyright Toru Niina 2017. -// Distributed under the MIT License. -#ifndef TOML11_STORAGE_HPP -#define TOML11_STORAGE_HPP -#include "utility.hpp" - -namespace toml -{ -namespace detail -{ - -// this contains pointer and deep-copy the content if copied. -// to avoid recursive pointer. -template -struct storage -{ - using value_type = T; - - explicit storage(value_type const& v): ptr(toml::make_unique(v)) {} - explicit storage(value_type&& v): ptr(toml::make_unique(std::move(v))) {} - ~storage() = default; - storage(const storage& rhs): ptr(toml::make_unique(*rhs.ptr)) {} - storage& operator=(const storage& rhs) - { - this->ptr = toml::make_unique(*rhs.ptr); - return *this; - } - storage(storage&&) = default; - storage& operator=(storage&&) = default; - - bool is_ok() const noexcept {return static_cast(ptr);} - - value_type& value() & noexcept {return *ptr;} - value_type const& value() const& noexcept {return *ptr;} - value_type&& value() && noexcept {return std::move(*ptr);} - - private: - std::unique_ptr ptr; -}; - -} // detail -} // toml -#endif// TOML11_STORAGE_HPP diff --git a/3party/toml11/toml/string.hpp b/3party/toml11/toml/string.hpp deleted file mode 100644 index def3e57..0000000 --- a/3party/toml11/toml/string.hpp +++ /dev/null @@ -1,228 +0,0 @@ -// Copyright Toru Niina 2017. -// Distributed under the MIT License. -#ifndef TOML11_STRING_HPP -#define TOML11_STRING_HPP - -#include "version.hpp" - -#include - -#include -#include - -#if TOML11_CPLUSPLUS_STANDARD_VERSION >= 201703L -#if __has_include() -#define TOML11_USING_STRING_VIEW 1 -#include -#endif -#endif - -namespace toml -{ - -enum class string_t : std::uint8_t -{ - basic = 0, - literal = 1, -}; - -struct string -{ - string() = default; - ~string() = default; - string(const string& s) = default; - string(string&& s) = default; - string& operator=(const string& s) = default; - string& operator=(string&& s) = default; - - string(const std::string& s): kind(string_t::basic), str(s){} - string(const std::string& s, string_t k): kind(k), str(s){} - string(const char* s): kind(string_t::basic), str(s){} - string(const char* s, string_t k): kind(k), str(s){} - - string(std::string&& s): kind(string_t::basic), str(std::move(s)){} - string(std::string&& s, string_t k): kind(k), str(std::move(s)){} - - string& operator=(const std::string& s) - {kind = string_t::basic; str = s; return *this;} - string& operator=(std::string&& s) - {kind = string_t::basic; str = std::move(s); return *this;} - - operator std::string& () & noexcept {return str;} - operator std::string const& () const& noexcept {return str;} - operator std::string&& () && noexcept {return std::move(str);} - - string& operator+=(const char* rhs) {str += rhs; return *this;} - string& operator+=(const char rhs) {str += rhs; return *this;} - string& operator+=(const std::string& rhs) {str += rhs; return *this;} - string& operator+=(const string& rhs) {str += rhs.str; return *this;} - -#if defined(TOML11_USING_STRING_VIEW) && TOML11_USING_STRING_VIEW>0 - explicit string(std::string_view s): kind(string_t::basic), str(s){} - string(std::string_view s, string_t k): kind(k), str(s){} - - string& operator=(std::string_view s) - {kind = string_t::basic; str = s; return *this;} - - explicit operator std::string_view() const noexcept - {return std::string_view(str);} - - string& operator+=(const std::string_view& rhs) {str += rhs; return *this;} -#endif - - string_t kind; - std::string str; -}; - -inline bool operator==(const string& lhs, const string& rhs) -{ - return lhs.kind == rhs.kind && lhs.str == rhs.str; -} -inline bool operator!=(const string& lhs, const string& rhs) -{ - return !(lhs == rhs); -} -inline bool operator<(const string& lhs, const string& rhs) -{ - return (lhs.kind == rhs.kind) ? (lhs.str < rhs.str) : (lhs.kind < rhs.kind); -} -inline bool operator>(const string& lhs, const string& rhs) -{ - return rhs < lhs; -} -inline bool operator<=(const string& lhs, const string& rhs) -{ - return !(rhs < lhs); -} -inline bool operator>=(const string& lhs, const string& rhs) -{ - return !(lhs < rhs); -} - -inline bool -operator==(const string& lhs, const std::string& rhs) {return lhs.str == rhs;} -inline bool -operator!=(const string& lhs, const std::string& rhs) {return lhs.str != rhs;} -inline bool -operator< (const string& lhs, const std::string& rhs) {return lhs.str < rhs;} -inline bool -operator> (const string& lhs, const std::string& rhs) {return lhs.str > rhs;} -inline bool -operator<=(const string& lhs, const std::string& rhs) {return lhs.str <= rhs;} -inline bool -operator>=(const string& lhs, const std::string& rhs) {return lhs.str >= rhs;} - -inline bool -operator==(const std::string& lhs, const string& rhs) {return lhs == rhs.str;} -inline bool -operator!=(const std::string& lhs, const string& rhs) {return lhs != rhs.str;} -inline bool -operator< (const std::string& lhs, const string& rhs) {return lhs < rhs.str;} -inline bool -operator> (const std::string& lhs, const string& rhs) {return lhs > rhs.str;} -inline bool -operator<=(const std::string& lhs, const string& rhs) {return lhs <= rhs.str;} -inline bool -operator>=(const std::string& lhs, const string& rhs) {return lhs >= rhs.str;} - -inline bool -operator==(const string& lhs, const char* rhs) {return lhs.str == std::string(rhs);} -inline bool -operator!=(const string& lhs, const char* rhs) {return lhs.str != std::string(rhs);} -inline bool -operator< (const string& lhs, const char* rhs) {return lhs.str < std::string(rhs);} -inline bool -operator> (const string& lhs, const char* rhs) {return lhs.str > std::string(rhs);} -inline bool -operator<=(const string& lhs, const char* rhs) {return lhs.str <= std::string(rhs);} -inline bool -operator>=(const string& lhs, const char* rhs) {return lhs.str >= std::string(rhs);} - -inline bool -operator==(const char* lhs, const string& rhs) {return std::string(lhs) == rhs.str;} -inline bool -operator!=(const char* lhs, const string& rhs) {return std::string(lhs) != rhs.str;} -inline bool -operator< (const char* lhs, const string& rhs) {return std::string(lhs) < rhs.str;} -inline bool -operator> (const char* lhs, const string& rhs) {return std::string(lhs) > rhs.str;} -inline bool -operator<=(const char* lhs, const string& rhs) {return std::string(lhs) <= rhs.str;} -inline bool -operator>=(const char* lhs, const string& rhs) {return std::string(lhs) >= rhs.str;} - -template -std::basic_ostream& -operator<<(std::basic_ostream& os, const string& s) -{ - if(s.kind == string_t::basic) - { - if(std::find(s.str.cbegin(), s.str.cend(), '\n') != s.str.cend()) - { - // it contains newline. make it multiline string. - os << "\"\"\"\n"; - for(auto i=s.str.cbegin(), e=s.str.cend(); i!=e; ++i) - { - switch(*i) - { - case '\\': {os << "\\\\"; break;} - case '\"': {os << "\\\""; break;} - case '\b': {os << "\\b"; break;} - case '\t': {os << "\\t"; break;} - case '\f': {os << "\\f"; break;} - case '\n': {os << '\n'; break;} - case '\r': - { - // since it is a multiline string, - // CRLF is not needed to be escaped. - if(std::next(i) != e && *std::next(i) == '\n') - { - os << "\r\n"; - ++i; - } - else - { - os << "\\r"; - } - break; - } - default: {os << *i; break;} - } - } - os << "\\\n\"\"\""; - return os; - } - // no newline. make it inline. - os << "\""; - for(const auto c : s.str) - { - switch(c) - { - case '\\': {os << "\\\\"; break;} - case '\"': {os << "\\\""; break;} - case '\b': {os << "\\b"; break;} - case '\t': {os << "\\t"; break;} - case '\f': {os << "\\f"; break;} - case '\n': {os << "\\n"; break;} - case '\r': {os << "\\r"; break;} - default : {os << c; break;} - } - } - os << "\""; - return os; - } - // the string `s` is literal-string. - if(std::find(s.str.cbegin(), s.str.cend(), '\n') != s.str.cend() || - std::find(s.str.cbegin(), s.str.cend(), '\'') != s.str.cend() ) - { - // contains newline or single quote. make it multiline. - os << "'''\n" << s.str << "'''"; - return os; - } - // normal literal string - os << '\'' << s.str << '\''; - return os; -} - -} // toml -#endif// TOML11_STRING_H diff --git a/3party/toml11/toml/traits.hpp b/3party/toml11/toml/traits.hpp deleted file mode 100644 index 255d9e8..0000000 --- a/3party/toml11/toml/traits.hpp +++ /dev/null @@ -1,328 +0,0 @@ -// Copyright Toru Niina 2017. -// Distributed under the MIT License. -#ifndef TOML11_TRAITS_HPP -#define TOML11_TRAITS_HPP - -#include "from.hpp" -#include "into.hpp" -#include "version.hpp" - -#include -#include -#include -#include -#include -#include - -#if TOML11_CPLUSPLUS_STANDARD_VERSION >= 201703L -#if __has_include() -#include -#endif // has_include() -#endif // cplusplus >= C++17 - -namespace toml -{ -template class T, template class A> -class basic_value; - -namespace detail -{ -// --------------------------------------------------------------------------- -// check whether type T is a kind of container/map class - -struct has_iterator_impl -{ - template static std::true_type check(typename T::iterator*); - template static std::false_type check(...); -}; -struct has_value_type_impl -{ - template static std::true_type check(typename T::value_type*); - template static std::false_type check(...); -}; -struct has_key_type_impl -{ - template static std::true_type check(typename T::key_type*); - template static std::false_type check(...); -}; -struct has_mapped_type_impl -{ - template static std::true_type check(typename T::mapped_type*); - template static std::false_type check(...); -}; -struct has_reserve_method_impl -{ - template static std::false_type check(...); - template static std::true_type check( - decltype(std::declval().reserve(std::declval()))*); -}; -struct has_push_back_method_impl -{ - template static std::false_type check(...); - template static std::true_type check( - decltype(std::declval().push_back(std::declval()))*); -}; -struct is_comparable_impl -{ - template static std::false_type check(...); - template static std::true_type check( - decltype(std::declval() < std::declval())*); -}; - -struct has_from_toml_method_impl -{ - template class Tb, template class A> - static std::true_type check( - decltype(std::declval().from_toml( - std::declval<::toml::basic_value>()))*); - - template class Tb, template class A> - static std::false_type check(...); -}; -struct has_into_toml_method_impl -{ - template - static std::true_type check(decltype(std::declval().into_toml())*); - template - static std::false_type check(...); -}; - -struct has_specialized_from_impl -{ - template - static std::false_type check(...); - template)> - static std::true_type check(::toml::from*); -}; -struct has_specialized_into_impl -{ - template - static std::false_type check(...); - template)> - static std::true_type check(::toml::from*); -}; - - -/// Intel C++ compiler can not use decltype in parent class declaration, here -/// is a hack to work around it. https://stackoverflow.com/a/23953090/4692076 -#ifdef __INTEL_COMPILER -#define decltype(...) std::enable_if::type -#endif - -template -struct has_iterator : decltype(has_iterator_impl::check(nullptr)){}; -template -struct has_value_type : decltype(has_value_type_impl::check(nullptr)){}; -template -struct has_key_type : decltype(has_key_type_impl::check(nullptr)){}; -template -struct has_mapped_type : decltype(has_mapped_type_impl::check(nullptr)){}; -template -struct has_reserve_method : decltype(has_reserve_method_impl::check(nullptr)){}; -template -struct has_push_back_method : decltype(has_push_back_method_impl::check(nullptr)){}; -template -struct is_comparable : decltype(is_comparable_impl::check(nullptr)){}; - -template class Tb, template class A> -struct has_from_toml_method -: decltype(has_from_toml_method_impl::check(nullptr)){}; - -template -struct has_into_toml_method -: decltype(has_into_toml_method_impl::check(nullptr)){}; - -template -struct has_specialized_from : decltype(has_specialized_from_impl::check(nullptr)){}; -template -struct has_specialized_into : decltype(has_specialized_into_impl::check(nullptr)){}; - -#ifdef __INTEL_COMPILER -#undef decltype -#endif - -// --------------------------------------------------------------------------- -// C++17 and/or/not - -#if TOML11_CPLUSPLUS_STANDARD_VERSION >= 201703L - -using std::conjunction; -using std::disjunction; -using std::negation; - -#else - -template struct conjunction : std::true_type{}; -template struct conjunction : T{}; -template -struct conjunction : - std::conditional(T::value), conjunction, T>::type -{}; - -template struct disjunction : std::false_type{}; -template struct disjunction : T {}; -template -struct disjunction : - std::conditional(T::value), T, disjunction>::type -{}; - -template -struct negation : std::integral_constant(T::value)>{}; - -#endif - -// --------------------------------------------------------------------------- -// type checkers - -template struct is_std_pair : std::false_type{}; -template -struct is_std_pair> : std::true_type{}; - -template struct is_std_tuple : std::false_type{}; -template -struct is_std_tuple> : std::true_type{}; - -template struct is_std_forward_list : std::false_type{}; -template -struct is_std_forward_list> : std::true_type{}; - -template struct is_chrono_duration: std::false_type{}; -template -struct is_chrono_duration>: std::true_type{}; - -template -struct is_map : conjunction< // map satisfies all the following conditions - has_iterator, // has T::iterator - has_value_type, // has T::value_type - has_key_type, // has T::key_type - has_mapped_type // has T::mapped_type - >{}; -template struct is_map : is_map{}; -template struct is_map : is_map{}; -template struct is_map : is_map{}; -template struct is_map : is_map{}; - -template -struct is_container : conjunction< - negation>, // not a map - negation>, // not a std::string -#if TOML11_CPLUSPLUS_STANDARD_VERSION >= 201703L -#if __has_include() - negation>, // not a std::string_view -#endif // has_include() -#endif - has_iterator, // has T::iterator - has_value_type // has T::value_type - >{}; -template struct is_container : is_container{}; -template struct is_container : is_container{}; -template struct is_container : is_container{}; -template struct is_container : is_container{}; - -template -struct is_basic_value: std::false_type{}; -template struct is_basic_value : is_basic_value{}; -template struct is_basic_value : is_basic_value{}; -template struct is_basic_value : is_basic_value{}; -template struct is_basic_value : is_basic_value{}; -template class M, template class V> -struct is_basic_value<::toml::basic_value>: std::true_type{}; - -// --------------------------------------------------------------------------- -// C++14 index_sequence - -#if TOML11_CPLUSPLUS_STANDARD_VERSION >= 201402L - -using std::index_sequence; -using std::make_index_sequence; - -#else - -template struct index_sequence{}; - -template struct push_back_index_sequence{}; -template -struct push_back_index_sequence, N> -{ - typedef index_sequence type; -}; - -template -struct index_sequence_maker -{ - typedef typename push_back_index_sequence< - typename index_sequence_maker::type, N>::type type; -}; -template<> -struct index_sequence_maker<0> -{ - typedef index_sequence<0> type; -}; -template -using make_index_sequence = typename index_sequence_maker::type; - -#endif // cplusplus >= 2014 - -// --------------------------------------------------------------------------- -// C++14 enable_if_t - -#if TOML11_CPLUSPLUS_STANDARD_VERSION >= 201402L - -using std::enable_if_t; - -#else - -template -using enable_if_t = typename std::enable_if::type; - -#endif // cplusplus >= 2014 - -// --------------------------------------------------------------------------- -// return_type_of_t - -#if TOML11_CPLUSPLUS_STANDARD_VERSION >= 201703L && defined(__cpp_lib_is_invocable) && __cpp_lib_is_invocable>=201703 - -template -using return_type_of_t = std::invoke_result_t; - -#else -// result_of is deprecated after C++17 -template -using return_type_of_t = typename std::result_of::type; - -#endif - -// --------------------------------------------------------------------------- -// is_string_literal -// -// to use this, pass `typename remove_reference::type` to T. - -template -struct is_string_literal: -disjunction< - std::is_same, - conjunction< - std::is_array, - std::is_same::type> - > - >{}; - -// --------------------------------------------------------------------------- -// C++20 remove_cvref_t - -template -struct remove_cvref -{ - using type = typename std::remove_cv< - typename std::remove_reference::type>::type; -}; - -template -using remove_cvref_t = typename remove_cvref::type; - -}// detail -}//toml -#endif // TOML_TRAITS diff --git a/3party/toml11/toml/types.hpp b/3party/toml11/toml/types.hpp deleted file mode 100644 index 1e420e7..0000000 --- a/3party/toml11/toml/types.hpp +++ /dev/null @@ -1,173 +0,0 @@ -// Copyright Toru Niina 2017. -// Distributed under the MIT License. -#ifndef TOML11_TYPES_HPP -#define TOML11_TYPES_HPP -#include -#include - -#include "comments.hpp" -#include "datetime.hpp" -#include "string.hpp" -#include "traits.hpp" - -namespace toml -{ - -template class Table, // map-like class - template class Array> // vector-like class -class basic_value; - -using character = char; -using key = std::string; - -#if !defined(__clang__) && defined(__GNUC__) && __GNUC__ <= 4 -# pragma GCC diagnostic push -# pragma GCC diagnostic ignored "-Wshadow" -#endif - -using boolean = bool; -using integer = std::int64_t; -using floating = double; // "float" is a keyword, cannot use it here. -// the following stuffs are structs defined here, so aliases are not needed. -// - string -// - offset_datetime -// - offset_datetime -// - local_datetime -// - local_date -// - local_time - -#if defined(__GNUC__) && !defined(__clang__) -# pragma GCC diagnostic pop -#endif - -// default toml::value and default array/table. these are defined after defining -// basic_value itself. -// using value = basic_value; -// using array = typename value::array_type; -// using table = typename value::table_type; - -// to avoid warnings about `value_t::integer` is "shadowing" toml::integer in -// GCC -Wshadow=global. -#if defined(__GNUC__) && !defined(__clang__) -# pragma GCC diagnostic push -# if 7 <= __GNUC__ -# pragma GCC diagnostic ignored "-Wshadow=global" -# else // gcc-6 or older -# pragma GCC diagnostic ignored "-Wshadow" -# endif -#endif -enum class value_t : std::uint8_t -{ - empty = 0, - boolean = 1, - integer = 2, - floating = 3, - string = 4, - offset_datetime = 5, - local_datetime = 6, - local_date = 7, - local_time = 8, - array = 9, - table = 10, -}; -#if defined(__GNUC__) && !defined(__clang__) -# pragma GCC diagnostic pop -#endif - -template -inline std::basic_ostream& -operator<<(std::basic_ostream& os, value_t t) -{ - switch(t) - { - case value_t::boolean : os << "boolean"; return os; - case value_t::integer : os << "integer"; return os; - case value_t::floating : os << "floating"; return os; - case value_t::string : os << "string"; return os; - case value_t::offset_datetime : os << "offset_datetime"; return os; - case value_t::local_datetime : os << "local_datetime"; return os; - case value_t::local_date : os << "local_date"; return os; - case value_t::local_time : os << "local_time"; return os; - case value_t::array : os << "array"; return os; - case value_t::table : os << "table"; return os; - case value_t::empty : os << "empty"; return os; - default : os << "unknown"; return os; - } -} - -template, - typename alloc = std::allocator> -inline std::basic_string stringize(value_t t) -{ - std::basic_ostringstream oss; - oss << t; - return oss.str(); -} - -namespace detail -{ - -// helper to define a type that represents a value_t value. -template -using value_t_constant = std::integral_constant; - -// meta-function that convertes from value_t to the exact toml type that corresponds to. -// It takes toml::basic_value type because array and table types depend on it. -template struct enum_to_type {using type = void ;}; -template struct enum_to_type{using type = void ;}; -template struct enum_to_type{using type = boolean ;}; -template struct enum_to_type{using type = integer ;}; -template struct enum_to_type{using type = floating ;}; -template struct enum_to_type{using type = string ;}; -template struct enum_to_type{using type = offset_datetime ;}; -template struct enum_to_type{using type = local_datetime ;}; -template struct enum_to_type{using type = local_date ;}; -template struct enum_to_type{using type = local_time ;}; -template struct enum_to_type{using type = typename Value::array_type;}; -template struct enum_to_type{using type = typename Value::table_type;}; - -// meta-function that converts from an exact toml type to the enum that corresponds to. -template -struct type_to_enum : std::conditional< - std::is_same::value, // if T == array_type, - value_t_constant, // then value_t::array - typename std::conditional< // else... - std::is_same::value, // if T == table_type - value_t_constant, // then value_t::table - value_t_constant // else value_t::empty - >::type - >::type {}; -template struct type_to_enum: value_t_constant {}; -template struct type_to_enum: value_t_constant {}; -template struct type_to_enum: value_t_constant {}; -template struct type_to_enum: value_t_constant {}; -template struct type_to_enum: value_t_constant {}; -template struct type_to_enum: value_t_constant {}; -template struct type_to_enum: value_t_constant {}; -template struct type_to_enum: value_t_constant {}; - -// meta-function that checks the type T is the same as one of the toml::* types. -template -struct is_exact_toml_type : disjunction< - std::is_same, - std::is_same, - std::is_same, - std::is_same, - std::is_same, - std::is_same, - std::is_same, - std::is_same, - std::is_same, - std::is_same - >{}; -template struct is_exact_toml_type : is_exact_toml_type{}; -template struct is_exact_toml_type : is_exact_toml_type{}; -template struct is_exact_toml_type : is_exact_toml_type{}; -template struct is_exact_toml_type: is_exact_toml_type{}; - -} // detail -} // toml - -#endif// TOML11_TYPES_H diff --git a/3party/toml11/toml/utility.hpp b/3party/toml11/toml/utility.hpp deleted file mode 100644 index 53a18b9..0000000 --- a/3party/toml11/toml/utility.hpp +++ /dev/null @@ -1,150 +0,0 @@ -// Copyright Toru Niina 2017. -// Distributed under the MIT License. -#ifndef TOML11_UTILITY_HPP -#define TOML11_UTILITY_HPP -#include -#include -#include - -#include "traits.hpp" -#include "version.hpp" - -#if TOML11_CPLUSPLUS_STANDARD_VERSION >= 201402L -# define TOML11_MARK_AS_DEPRECATED(msg) [[deprecated(msg)]] -#elif defined(__GNUC__) -# define TOML11_MARK_AS_DEPRECATED(msg) __attribute__((deprecated(msg))) -#elif defined(_MSC_VER) -# define TOML11_MARK_AS_DEPRECATED(msg) __declspec(deprecated(msg)) -#else -# define TOML11_MARK_AS_DEPRECATED -#endif - -namespace toml -{ - -#if TOML11_CPLUSPLUS_STANDARD_VERSION >= 201402L - -using std::make_unique; - -#else - -template -inline std::unique_ptr make_unique(Ts&& ... args) -{ - return std::unique_ptr(new T(std::forward(args)...)); -} - -#endif // TOML11_CPLUSPLUS_STANDARD_VERSION >= 2014 - -namespace detail -{ -template -void try_reserve_impl(Container& container, std::size_t N, std::true_type) -{ - container.reserve(N); - return; -} -template -void try_reserve_impl(Container&, std::size_t, std::false_type) noexcept -{ - return; -} -} // detail - -template -void try_reserve(Container& container, std::size_t N) -{ - if(N <= container.size()) {return;} - detail::try_reserve_impl(container, N, detail::has_reserve_method{}); - return; -} - -namespace detail -{ -inline std::string concat_to_string_impl(std::ostringstream& oss) -{ - return oss.str(); -} -template -std::string concat_to_string_impl(std::ostringstream& oss, T&& head, Ts&& ... tail) -{ - oss << std::forward(head); - return concat_to_string_impl(oss, std::forward(tail) ... ); -} -} // detail - -template -std::string concat_to_string(Ts&& ... args) -{ - std::ostringstream oss; - oss << std::boolalpha << std::fixed; - return detail::concat_to_string_impl(oss, std::forward(args) ...); -} - -template -T from_string(const std::string& str, T opt) -{ - T v(opt); - std::istringstream iss(str); - iss >> v; - return v; -} - -namespace detail -{ -#if TOML11_CPLUSPLUS_STANDARD_VERSION >= 201402L -template -decltype(auto) last_one(T&& tail) noexcept -{ - return std::forward(tail); -} - -template -decltype(auto) last_one(T&& /*head*/, Ts&& ... tail) noexcept -{ - return last_one(std::forward(tail)...); -} -#else // C++11 -// The following code -// ```cpp -// 1 | template -// 2 | auto last_one(T&& /*head*/, Ts&& ... tail) -// 3 | -> decltype(last_one(std::forward(tail)...)) -// 4 | { -// 5 | return last_one(std::forward(tail)...); -// 6 | } -// ``` -// does not work because the function `last_one(...)` is not yet defined at -// line #3, so `decltype()` cannot deduce the type returned from `last_one`. -// So we need to determine return type in a different way, like a meta func. - -template -struct last_one_in_pack -{ - using type = typename last_one_in_pack::type; -}; -template -struct last_one_in_pack -{ - using type = T; -}; -template -using last_one_in_pack_t = typename last_one_in_pack::type; - -template -T&& last_one(T&& tail) noexcept -{ - return std::forward(tail); -} -template -enable_if_t<(sizeof...(Ts) > 0), last_one_in_pack_t> -last_one(T&& /*head*/, Ts&& ... tail) -{ - return last_one(std::forward(tail)...); -} - -#endif -} // detail - -}// toml -#endif // TOML11_UTILITY diff --git a/3party/toml11/toml/value.hpp b/3party/toml11/toml/value.hpp deleted file mode 100644 index d57ab80..0000000 --- a/3party/toml11/toml/value.hpp +++ /dev/null @@ -1,2037 +0,0 @@ -// Copyright Toru Niina 2017. -// Distributed under the MIT License. -#ifndef TOML11_VALUE_HPP -#define TOML11_VALUE_HPP -#include - -#include "comments.hpp" -#include "exception.hpp" -#include "into.hpp" -#include "region.hpp" -#include "source_location.hpp" -#include "storage.hpp" -#include "traits.hpp" -#include "types.hpp" -#include "utility.hpp" - -namespace toml -{ - -namespace detail -{ - -// to show error messages. not recommended for users. -template -inline region_base const* get_region(const Value& v) -{ - return v.region_info_.get(); -} - -template -void change_region(Value& v, region reg) -{ - v.region_info_ = std::make_shared(std::move(reg)); - return; -} - -template -[[noreturn]] inline void -throw_bad_cast(const std::string& funcname, value_t actual, const Value& v) -{ - throw type_error(detail::format_underline( - concat_to_string(funcname, "bad_cast to ", Expected), { - {v.location(), concat_to_string("the actual type is ", actual)} - }), v.location()); -} - -// Throw `out_of_range` from `toml::value::at()` and `toml::find()` -// after generating an error message. -// -// The implementation is a bit complicated and there are many edge-cases. -// If you are not interested in the error message generation, just skip this. -template -[[noreturn]] void -throw_key_not_found_error(const Value& v, const key& ky) -{ - // The top-level table has its region at the first character of the file. - // That means that, in the case when a key is not found in the top-level - // table, the error message points to the first character. If the file has - // its first table at the first line, the error message would be like this. - // ```console - // [error] key "a" not found - // --> example.toml - // | - // 1 | [table] - // | ^------ in this table - // ``` - // It actually points to the top-level table at the first character, - // not `[table]`. But it is too confusing. To avoid the confusion, the error - // message should explicitly say "key not found in the top-level table", - // or "the parsed file is empty" if there is no content at all (0 bytes in file). - const auto loc = v.location(); - if(loc.line() == 1 && loc.region() == 0) - { - // First line with a zero-length region means "empty file". - // The region will be generated at `parse_toml_file` function - // if the file contains no bytes. - throw std::out_of_range(format_underline(concat_to_string( - "key \"", ky, "\" not found in the top-level table"), { - {loc, "the parsed file is empty"} - })); - } - else if(loc.line() == 1 && loc.region() == 1) - { - // Here it assumes that top-level table starts at the first character. - // The region corresponds to the top-level table will be generated at - // `parse_toml_file` function. - // It also assumes that the top-level table size is just one and - // the line number is `1`. It is always satisfied. And those conditions - // are satisfied only if the table is the top-level table. - // - // 1. one-character dot-key at the first line - // ```toml - // a.b = "c" - // ``` - // toml11 counts whole key as the table key. Here, `a.b` is the region - // of the table "a". It could be counter intuitive, but it works. - // The size of the region is 3, not 1. The above example is the shortest - // dot-key example. The size cannot be 1. - // - // 2. one-character inline-table at the first line - // ```toml - // a = {b = "c"} - // ``` - // toml11 considers the inline table body as the table region. Here, - // `{b = "c"}` is the region of the table "a". The size of the region - // is 9, not 1. The shotest inline table still has two characters, `{` - // and `}`. The size cannot be 1. - // - // 3. one-character table declaration at the first line - // ```toml - // [a] - // ``` - // toml11 considers the whole table key as the table region. Here, - // `[a]` is the table region. The size is 3, not 1. - // - throw std::out_of_range(format_underline(concat_to_string( - "key \"", ky, "\" not found in the top-level table"), { - {loc, "the top-level table starts here"} - })); - } - else - { - // normal table. - throw std::out_of_range(format_underline(concat_to_string( - "key \"", ky, "\" not found"), { {loc, "in this table"} })); - } -} - -// switch by `value_t` at the compile time. -template -struct switch_cast {}; -#define TOML11_GENERATE_SWITCH_CASTER(TYPE) \ - template<> \ - struct switch_cast \ - { \ - template \ - static typename Value::TYPE##_type& invoke(Value& v) \ - { \ - return v.as_##TYPE(); \ - } \ - template \ - static typename Value::TYPE##_type const& invoke(const Value& v) \ - { \ - return v.as_##TYPE(); \ - } \ - template \ - static typename Value::TYPE##_type&& invoke(Value&& v) \ - { \ - return std::move(v).as_##TYPE(); \ - } \ - }; \ - /**/ -TOML11_GENERATE_SWITCH_CASTER(boolean) -TOML11_GENERATE_SWITCH_CASTER(integer) -TOML11_GENERATE_SWITCH_CASTER(floating) -TOML11_GENERATE_SWITCH_CASTER(string) -TOML11_GENERATE_SWITCH_CASTER(offset_datetime) -TOML11_GENERATE_SWITCH_CASTER(local_datetime) -TOML11_GENERATE_SWITCH_CASTER(local_date) -TOML11_GENERATE_SWITCH_CASTER(local_time) -TOML11_GENERATE_SWITCH_CASTER(array) -TOML11_GENERATE_SWITCH_CASTER(table) - -#undef TOML11_GENERATE_SWITCH_CASTER - -}// detail - -template class Table = std::unordered_map, - template class Array = std::vector> -class basic_value -{ - template - static void assigner(T& dst, U&& v) - { - const auto tmp = ::new(std::addressof(dst)) T(std::forward(v)); - assert(tmp == std::addressof(dst)); - (void)tmp; - } - - using region_base = detail::region_base; - - template class T, - template class A> - friend class basic_value; - - public: - - using comment_type = Comment; - using key_type = ::toml::key; - using value_type = basic_value; - using boolean_type = ::toml::boolean; - using integer_type = ::toml::integer; - using floating_type = ::toml::floating; - using string_type = ::toml::string; - using local_time_type = ::toml::local_time; - using local_date_type = ::toml::local_date; - using local_datetime_type = ::toml::local_datetime; - using offset_datetime_type = ::toml::offset_datetime; - using array_type = Array; - using table_type = Table; - - public: - - basic_value() noexcept - : type_(value_t::empty), - region_info_(std::make_shared(region_base{})) - {} - ~basic_value() noexcept {this->cleanup();} - - basic_value(const basic_value& v) - : type_(v.type()), region_info_(v.region_info_), comments_(v.comments_) - { - switch(v.type()) - { - case value_t::boolean : assigner(boolean_ , v.boolean_ ); break; - case value_t::integer : assigner(integer_ , v.integer_ ); break; - case value_t::floating : assigner(floating_ , v.floating_ ); break; - case value_t::string : assigner(string_ , v.string_ ); break; - case value_t::offset_datetime: assigner(offset_datetime_, v.offset_datetime_); break; - case value_t::local_datetime : assigner(local_datetime_ , v.local_datetime_ ); break; - case value_t::local_date : assigner(local_date_ , v.local_date_ ); break; - case value_t::local_time : assigner(local_time_ , v.local_time_ ); break; - case value_t::array : assigner(array_ , v.array_ ); break; - case value_t::table : assigner(table_ , v.table_ ); break; - default: break; - } - } - basic_value(basic_value&& v) - : type_(v.type()), region_info_(std::move(v.region_info_)), - comments_(std::move(v.comments_)) - { - switch(this->type_) // here this->type_ is already initialized - { - case value_t::boolean : assigner(boolean_ , std::move(v.boolean_ )); break; - case value_t::integer : assigner(integer_ , std::move(v.integer_ )); break; - case value_t::floating : assigner(floating_ , std::move(v.floating_ )); break; - case value_t::string : assigner(string_ , std::move(v.string_ )); break; - case value_t::offset_datetime: assigner(offset_datetime_, std::move(v.offset_datetime_)); break; - case value_t::local_datetime : assigner(local_datetime_ , std::move(v.local_datetime_ )); break; - case value_t::local_date : assigner(local_date_ , std::move(v.local_date_ )); break; - case value_t::local_time : assigner(local_time_ , std::move(v.local_time_ )); break; - case value_t::array : assigner(array_ , std::move(v.array_ )); break; - case value_t::table : assigner(table_ , std::move(v.table_ )); break; - default: break; - } - } - basic_value& operator=(const basic_value& v) - { - if(this == std::addressof(v)) {return *this;} - this->cleanup(); - this->region_info_ = v.region_info_; - this->comments_ = v.comments_; - this->type_ = v.type(); - switch(this->type_) - { - case value_t::boolean : assigner(boolean_ , v.boolean_ ); break; - case value_t::integer : assigner(integer_ , v.integer_ ); break; - case value_t::floating : assigner(floating_ , v.floating_ ); break; - case value_t::string : assigner(string_ , v.string_ ); break; - case value_t::offset_datetime: assigner(offset_datetime_, v.offset_datetime_); break; - case value_t::local_datetime : assigner(local_datetime_ , v.local_datetime_ ); break; - case value_t::local_date : assigner(local_date_ , v.local_date_ ); break; - case value_t::local_time : assigner(local_time_ , v.local_time_ ); break; - case value_t::array : assigner(array_ , v.array_ ); break; - case value_t::table : assigner(table_ , v.table_ ); break; - default: break; - } - return *this; - } - basic_value& operator=(basic_value&& v) - { - if(this == std::addressof(v)) {return *this;} - this->cleanup(); - this->region_info_ = std::move(v.region_info_); - this->comments_ = std::move(v.comments_); - this->type_ = v.type(); - switch(this->type_) - { - case value_t::boolean : assigner(boolean_ , std::move(v.boolean_ )); break; - case value_t::integer : assigner(integer_ , std::move(v.integer_ )); break; - case value_t::floating : assigner(floating_ , std::move(v.floating_ )); break; - case value_t::string : assigner(string_ , std::move(v.string_ )); break; - case value_t::offset_datetime: assigner(offset_datetime_, std::move(v.offset_datetime_)); break; - case value_t::local_datetime : assigner(local_datetime_ , std::move(v.local_datetime_ )); break; - case value_t::local_date : assigner(local_date_ , std::move(v.local_date_ )); break; - case value_t::local_time : assigner(local_time_ , std::move(v.local_time_ )); break; - case value_t::array : assigner(array_ , std::move(v.array_ )); break; - case value_t::table : assigner(table_ , std::move(v.table_ )); break; - default: break; - } - return *this; - } - - // overwrite comments ---------------------------------------------------- - - basic_value(const basic_value& v, std::vector com) - : type_(v.type()), region_info_(v.region_info_), - comments_(std::move(com)) - { - switch(v.type()) - { - case value_t::boolean : assigner(boolean_ , v.boolean_ ); break; - case value_t::integer : assigner(integer_ , v.integer_ ); break; - case value_t::floating : assigner(floating_ , v.floating_ ); break; - case value_t::string : assigner(string_ , v.string_ ); break; - case value_t::offset_datetime: assigner(offset_datetime_, v.offset_datetime_); break; - case value_t::local_datetime : assigner(local_datetime_ , v.local_datetime_ ); break; - case value_t::local_date : assigner(local_date_ , v.local_date_ ); break; - case value_t::local_time : assigner(local_time_ , v.local_time_ ); break; - case value_t::array : assigner(array_ , v.array_ ); break; - case value_t::table : assigner(table_ , v.table_ ); break; - default: break; - } - } - - basic_value(basic_value&& v, std::vector com) - : type_(v.type()), region_info_(std::move(v.region_info_)), - comments_(std::move(com)) - { - switch(this->type_) // here this->type_ is already initialized - { - case value_t::boolean : assigner(boolean_ , std::move(v.boolean_ )); break; - case value_t::integer : assigner(integer_ , std::move(v.integer_ )); break; - case value_t::floating : assigner(floating_ , std::move(v.floating_ )); break; - case value_t::string : assigner(string_ , std::move(v.string_ )); break; - case value_t::offset_datetime: assigner(offset_datetime_, std::move(v.offset_datetime_)); break; - case value_t::local_datetime : assigner(local_datetime_ , std::move(v.local_datetime_ )); break; - case value_t::local_date : assigner(local_date_ , std::move(v.local_date_ )); break; - case value_t::local_time : assigner(local_time_ , std::move(v.local_time_ )); break; - case value_t::array : assigner(array_ , std::move(v.array_ )); break; - case value_t::table : assigner(table_ , std::move(v.table_ )); break; - default: break; - } - } - - // ----------------------------------------------------------------------- - // conversion between different basic_values. - template class T, - template class A> - basic_value(const basic_value& v) - : type_(v.type()), region_info_(v.region_info_), comments_(v.comments()) - { - switch(v.type()) - { - case value_t::boolean : assigner(boolean_ , v.boolean_ ); break; - case value_t::integer : assigner(integer_ , v.integer_ ); break; - case value_t::floating : assigner(floating_ , v.floating_ ); break; - case value_t::string : assigner(string_ , v.string_ ); break; - case value_t::offset_datetime: assigner(offset_datetime_, v.offset_datetime_); break; - case value_t::local_datetime : assigner(local_datetime_ , v.local_datetime_ ); break; - case value_t::local_date : assigner(local_date_ , v.local_date_ ); break; - case value_t::local_time : assigner(local_time_ , v.local_time_ ); break; - case value_t::array : - { - array_type tmp(v.as_array(std::nothrow).begin(), - v.as_array(std::nothrow).end()); - assigner(array_, std::move(tmp)); - break; - } - case value_t::table : - { - table_type tmp(v.as_table(std::nothrow).begin(), - v.as_table(std::nothrow).end()); - assigner(table_, std::move(tmp)); - break; - } - default: break; - } - } - template class T, - template class A> - basic_value(const basic_value& v, std::vector com) - : type_(v.type()), region_info_(v.region_info_), - comments_(std::move(com)) - { - switch(v.type()) - { - case value_t::boolean : assigner(boolean_ , v.boolean_ ); break; - case value_t::integer : assigner(integer_ , v.integer_ ); break; - case value_t::floating : assigner(floating_ , v.floating_ ); break; - case value_t::string : assigner(string_ , v.string_ ); break; - case value_t::offset_datetime: assigner(offset_datetime_, v.offset_datetime_); break; - case value_t::local_datetime : assigner(local_datetime_ , v.local_datetime_ ); break; - case value_t::local_date : assigner(local_date_ , v.local_date_ ); break; - case value_t::local_time : assigner(local_time_ , v.local_time_ ); break; - case value_t::array : - { - array_type tmp(v.as_array(std::nothrow).begin(), - v.as_array(std::nothrow).end()); - assigner(array_, std::move(tmp)); - break; - } - case value_t::table : - { - table_type tmp(v.as_table(std::nothrow).begin(), - v.as_table(std::nothrow).end()); - assigner(table_, std::move(tmp)); - break; - } - default: break; - } - } - template class T, - template class A> - basic_value& operator=(const basic_value& v) - { - this->region_info_ = v.region_info_; - this->comments_ = comment_type(v.comments()); - this->type_ = v.type(); - switch(v.type()) - { - case value_t::boolean : assigner(boolean_ , v.boolean_ ); break; - case value_t::integer : assigner(integer_ , v.integer_ ); break; - case value_t::floating : assigner(floating_ , v.floating_ ); break; - case value_t::string : assigner(string_ , v.string_ ); break; - case value_t::offset_datetime: assigner(offset_datetime_, v.offset_datetime_); break; - case value_t::local_datetime : assigner(local_datetime_ , v.local_datetime_ ); break; - case value_t::local_date : assigner(local_date_ , v.local_date_ ); break; - case value_t::local_time : assigner(local_time_ , v.local_time_ ); break; - case value_t::array : - { - array_type tmp(v.as_array(std::nothrow).begin(), - v.as_array(std::nothrow).end()); - assigner(array_, std::move(tmp)); - break; - } - case value_t::table : - { - table_type tmp(v.as_table(std::nothrow).begin(), - v.as_table(std::nothrow).end()); - assigner(table_, std::move(tmp)); - break; - } - default: break; - } - return *this; - } - - // boolean ============================================================== - - basic_value(boolean b) - : type_(value_t::boolean), - region_info_(std::make_shared(region_base{})) - { - assigner(this->boolean_, b); - } - basic_value& operator=(boolean b) - { - this->cleanup(); - this->type_ = value_t::boolean; - this->region_info_ = std::make_shared(region_base{}); - assigner(this->boolean_, b); - return *this; - } - basic_value(boolean b, std::vector com) - : type_(value_t::boolean), - region_info_(std::make_shared(region_base{})), - comments_(std::move(com)) - { - assigner(this->boolean_, b); - } - - // integer ============================================================== - - template, detail::negation>>::value, - std::nullptr_t>::type = nullptr> - basic_value(T i) - : type_(value_t::integer), - region_info_(std::make_shared(region_base{})) - { - assigner(this->integer_, static_cast(i)); - } - - template, detail::negation>>::value, - std::nullptr_t>::type = nullptr> - basic_value& operator=(T i) - { - this->cleanup(); - this->type_ = value_t::integer; - this->region_info_ = std::make_shared(region_base{}); - assigner(this->integer_, static_cast(i)); - return *this; - } - - template, detail::negation>>::value, - std::nullptr_t>::type = nullptr> - basic_value(T i, std::vector com) - : type_(value_t::integer), - region_info_(std::make_shared(region_base{})), - comments_(std::move(com)) - { - assigner(this->integer_, static_cast(i)); - } - - // floating ============================================================= - - template::value, std::nullptr_t>::type = nullptr> - basic_value(T f) - : type_(value_t::floating), - region_info_(std::make_shared(region_base{})) - { - assigner(this->floating_, static_cast(f)); - } - - - template::value, std::nullptr_t>::type = nullptr> - basic_value& operator=(T f) - { - this->cleanup(); - this->type_ = value_t::floating; - this->region_info_ = std::make_shared(region_base{}); - assigner(this->floating_, static_cast(f)); - return *this; - } - - template::value, std::nullptr_t>::type = nullptr> - basic_value(T f, std::vector com) - : type_(value_t::floating), - region_info_(std::make_shared(region_base{})), - comments_(std::move(com)) - { - assigner(this->floating_, f); - } - - // string =============================================================== - - basic_value(toml::string s) - : type_(value_t::string), - region_info_(std::make_shared(region_base{})) - { - assigner(this->string_, std::move(s)); - } - basic_value& operator=(toml::string s) - { - this->cleanup(); - this->type_ = value_t::string ; - this->region_info_ = std::make_shared(region_base{}); - assigner(this->string_, s); - return *this; - } - basic_value(toml::string s, std::vector com) - : type_(value_t::string), - region_info_(std::make_shared(region_base{})), - comments_(std::move(com)) - { - assigner(this->string_, std::move(s)); - } - - basic_value(std::string s) - : type_(value_t::string), - region_info_(std::make_shared(region_base{})) - { - assigner(this->string_, toml::string(std::move(s))); - } - basic_value& operator=(std::string s) - { - this->cleanup(); - this->type_ = value_t::string ; - this->region_info_ = std::make_shared(region_base{}); - assigner(this->string_, toml::string(std::move(s))); - return *this; - } - basic_value(std::string s, string_t kind) - : type_(value_t::string), - region_info_(std::make_shared(region_base{})) - { - assigner(this->string_, toml::string(std::move(s), kind)); - } - basic_value(std::string s, std::vector com) - : type_(value_t::string), - region_info_(std::make_shared(region_base{})), - comments_(std::move(com)) - { - assigner(this->string_, toml::string(std::move(s))); - } - basic_value(std::string s, string_t kind, std::vector com) - : type_(value_t::string), - region_info_(std::make_shared(region_base{})), - comments_(std::move(com)) - { - assigner(this->string_, toml::string(std::move(s), kind)); - } - - basic_value(const char* s) - : type_(value_t::string), - region_info_(std::make_shared(region_base{})) - { - assigner(this->string_, toml::string(std::string(s))); - } - basic_value& operator=(const char* s) - { - this->cleanup(); - this->type_ = value_t::string ; - this->region_info_ = std::make_shared(region_base{}); - assigner(this->string_, toml::string(std::string(s))); - return *this; - } - basic_value(const char* s, string_t kind) - : type_(value_t::string), - region_info_(std::make_shared(region_base{})) - { - assigner(this->string_, toml::string(std::string(s), kind)); - } - basic_value(const char* s, std::vector com) - : type_(value_t::string), - region_info_(std::make_shared(region_base{})), - comments_(std::move(com)) - { - assigner(this->string_, toml::string(std::string(s))); - } - basic_value(const char* s, string_t kind, std::vector com) - : type_(value_t::string), - region_info_(std::make_shared(region_base{})), - comments_(std::move(com)) - { - assigner(this->string_, toml::string(std::string(s), kind)); - } - -#if defined(TOML11_USING_STRING_VIEW) && TOML11_USING_STRING_VIEW>0 - basic_value(std::string_view s) - : type_(value_t::string), - region_info_(std::make_shared(region_base{})) - { - assigner(this->string_, toml::string(s)); - } - basic_value& operator=(std::string_view s) - { - this->cleanup(); - this->type_ = value_t::string ; - this->region_info_ = std::make_shared(region_base{}); - assigner(this->string_, toml::string(s)); - return *this; - } - basic_value(std::string_view s, std::vector com) - : type_(value_t::string), - region_info_(std::make_shared(region_base{})), - comments_(std::move(com)) - { - assigner(this->string_, toml::string(s)); - } - basic_value(std::string_view s, string_t kind) - : type_(value_t::string), - region_info_(std::make_shared(region_base{})) - { - assigner(this->string_, toml::string(s, kind)); - } - basic_value(std::string_view s, string_t kind, std::vector com) - : type_(value_t::string), - region_info_(std::make_shared(region_base{})), - comments_(std::move(com)) - { - assigner(this->string_, toml::string(s, kind)); - } -#endif - - // local date =========================================================== - - basic_value(const local_date& ld) - : type_(value_t::local_date), - region_info_(std::make_shared(region_base{})) - { - assigner(this->local_date_, ld); - } - basic_value& operator=(const local_date& ld) - { - this->cleanup(); - this->type_ = value_t::local_date; - this->region_info_ = std::make_shared(region_base{}); - assigner(this->local_date_, ld); - return *this; - } - basic_value(const local_date& ld, std::vector com) - : type_(value_t::local_date), - region_info_(std::make_shared(region_base{})), - comments_(std::move(com)) - { - assigner(this->local_date_, ld); - } - - // local time =========================================================== - - basic_value(const local_time& lt) - : type_(value_t::local_time), - region_info_(std::make_shared(region_base{})) - { - assigner(this->local_time_, lt); - } - basic_value(const local_time& lt, std::vector com) - : type_(value_t::local_time), - region_info_(std::make_shared(region_base{})), - comments_(std::move(com)) - { - assigner(this->local_time_, lt); - } - basic_value& operator=(const local_time& lt) - { - this->cleanup(); - this->type_ = value_t::local_time; - this->region_info_ = std::make_shared(region_base{}); - assigner(this->local_time_, lt); - return *this; - } - - template - basic_value(const std::chrono::duration& dur) - : type_(value_t::local_time), - region_info_(std::make_shared(region_base{})) - { - assigner(this->local_time_, local_time(dur)); - } - template - basic_value(const std::chrono::duration& dur, - std::vector com) - : type_(value_t::local_time), - region_info_(std::make_shared(region_base{})), - comments_(std::move(com)) - { - assigner(this->local_time_, local_time(dur)); - } - template - basic_value& operator=(const std::chrono::duration& dur) - { - this->cleanup(); - this->type_ = value_t::local_time; - this->region_info_ = std::make_shared(region_base{}); - assigner(this->local_time_, local_time(dur)); - return *this; - } - - // local datetime ======================================================= - - basic_value(const local_datetime& ldt) - : type_(value_t::local_datetime), - region_info_(std::make_shared(region_base{})) - { - assigner(this->local_datetime_, ldt); - } - basic_value(const local_datetime& ldt, std::vector com) - : type_(value_t::local_datetime), - region_info_(std::make_shared(region_base{})), - comments_(std::move(com)) - { - assigner(this->local_datetime_, ldt); - } - basic_value& operator=(const local_datetime& ldt) - { - this->cleanup(); - this->type_ = value_t::local_datetime; - this->region_info_ = std::make_shared(region_base{}); - assigner(this->local_datetime_, ldt); - return *this; - } - - // offset datetime ====================================================== - - basic_value(const offset_datetime& odt) - : type_(value_t::offset_datetime), - region_info_(std::make_shared(region_base{})) - { - assigner(this->offset_datetime_, odt); - } - basic_value(const offset_datetime& odt, std::vector com) - : type_(value_t::offset_datetime), - region_info_(std::make_shared(region_base{})), - comments_(std::move(com)) - { - assigner(this->offset_datetime_, odt); - } - basic_value& operator=(const offset_datetime& odt) - { - this->cleanup(); - this->type_ = value_t::offset_datetime; - this->region_info_ = std::make_shared(region_base{}); - assigner(this->offset_datetime_, odt); - return *this; - } - basic_value(const std::chrono::system_clock::time_point& tp) - : type_(value_t::offset_datetime), - region_info_(std::make_shared(region_base{})) - { - assigner(this->offset_datetime_, offset_datetime(tp)); - } - basic_value(const std::chrono::system_clock::time_point& tp, - std::vector com) - : type_(value_t::offset_datetime), - region_info_(std::make_shared(region_base{})), - comments_(std::move(com)) - { - assigner(this->offset_datetime_, offset_datetime(tp)); - } - basic_value& operator=(const std::chrono::system_clock::time_point& tp) - { - this->cleanup(); - this->type_ = value_t::offset_datetime; - this->region_info_ = std::make_shared(region_base{}); - assigner(this->offset_datetime_, offset_datetime(tp)); - return *this; - } - - // array ================================================================ - - basic_value(const array_type& ary) - : type_(value_t::array), - region_info_(std::make_shared(region_base{})) - { - assigner(this->array_, ary); - } - basic_value(const array_type& ary, std::vector com) - : type_(value_t::array), - region_info_(std::make_shared(region_base{})), - comments_(std::move(com)) - { - assigner(this->array_, ary); - } - basic_value& operator=(const array_type& ary) - { - this->cleanup(); - this->type_ = value_t::array ; - this->region_info_ = std::make_shared(region_base{}); - assigner(this->array_, ary); - return *this; - } - - // array (initializer_list) ---------------------------------------------- - - template::value, - std::nullptr_t>::type = nullptr> - basic_value(std::initializer_list list) - : type_(value_t::array), - region_info_(std::make_shared(region_base{})) - { - array_type ary(list.begin(), list.end()); - assigner(this->array_, std::move(ary)); - } - template::value, - std::nullptr_t>::type = nullptr> - basic_value(std::initializer_list list, std::vector com) - : type_(value_t::array), - region_info_(std::make_shared(region_base{})), - comments_(std::move(com)) - { - array_type ary(list.begin(), list.end()); - assigner(this->array_, std::move(ary)); - } - template::value, - std::nullptr_t>::type = nullptr> - basic_value& operator=(std::initializer_list list) - { - this->cleanup(); - this->type_ = value_t::array; - this->region_info_ = std::make_shared(region_base{}); - - array_type ary(list.begin(), list.end()); - assigner(this->array_, std::move(ary)); - return *this; - } - - // array (STL Containers) ------------------------------------------------ - - template>, - detail::is_container - >::value, std::nullptr_t>::type = nullptr> - basic_value(const T& list) - : type_(value_t::array), - region_info_(std::make_shared(region_base{})) - { - static_assert(std::is_convertible::value, - "elements of a container should be convertible to toml::value"); - - array_type ary(list.size()); - std::copy(list.begin(), list.end(), ary.begin()); - assigner(this->array_, std::move(ary)); - } - template>, - detail::is_container - >::value, std::nullptr_t>::type = nullptr> - basic_value(const T& list, std::vector com) - : type_(value_t::array), - region_info_(std::make_shared(region_base{})), - comments_(std::move(com)) - { - static_assert(std::is_convertible::value, - "elements of a container should be convertible to toml::value"); - - array_type ary(list.size()); - std::copy(list.begin(), list.end(), ary.begin()); - assigner(this->array_, std::move(ary)); - } - template>, - detail::is_container - >::value, std::nullptr_t>::type = nullptr> - basic_value& operator=(const T& list) - { - static_assert(std::is_convertible::value, - "elements of a container should be convertible to toml::value"); - - this->cleanup(); - this->type_ = value_t::array; - this->region_info_ = std::make_shared(region_base{}); - - array_type ary(list.size()); - std::copy(list.begin(), list.end(), ary.begin()); - assigner(this->array_, std::move(ary)); - return *this; - } - - // table ================================================================ - - basic_value(const table_type& tab) - : type_(value_t::table), - region_info_(std::make_shared(region_base{})) - { - assigner(this->table_, tab); - } - basic_value(const table_type& tab, std::vector com) - : type_(value_t::table), - region_info_(std::make_shared(region_base{})), - comments_(std::move(com)) - { - assigner(this->table_, tab); - } - basic_value& operator=(const table_type& tab) - { - this->cleanup(); - this->type_ = value_t::table; - this->region_info_ = std::make_shared(region_base{}); - assigner(this->table_, tab); - return *this; - } - - // initializer-list ------------------------------------------------------ - - basic_value(std::initializer_list> list) - : type_(value_t::table), - region_info_(std::make_shared(region_base{})) - { - table_type tab; - for(const auto& elem : list) {tab[elem.first] = elem.second;} - assigner(this->table_, std::move(tab)); - } - - basic_value(std::initializer_list> list, - std::vector com) - : type_(value_t::table), - region_info_(std::make_shared(region_base{})), - comments_(std::move(com)) - { - table_type tab; - for(const auto& elem : list) {tab[elem.first] = elem.second;} - assigner(this->table_, std::move(tab)); - } - basic_value& operator=(std::initializer_list> list) - { - this->cleanup(); - this->type_ = value_t::table; - this->region_info_ = std::make_shared(region_base{}); - - table_type tab; - for(const auto& elem : list) {tab[elem.first] = elem.second;} - assigner(this->table_, std::move(tab)); - return *this; - } - - // other table-like ----------------------------------------------------- - - template>, - detail::is_map - >::value, std::nullptr_t>::type = nullptr> - basic_value(const Map& mp) - : type_(value_t::table), - region_info_(std::make_shared(region_base{})) - { - table_type tab; - for(const auto& elem : mp) {tab[elem.first] = elem.second;} - assigner(this->table_, std::move(tab)); - } - template>, - detail::is_map - >::value, std::nullptr_t>::type = nullptr> - basic_value(const Map& mp, std::vector com) - : type_(value_t::table), - region_info_(std::make_shared(region_base{})), - comments_(std::move(com)) - { - table_type tab; - for(const auto& elem : mp) {tab[elem.first] = elem.second;} - assigner(this->table_, std::move(tab)); - } - template>, - detail::is_map - >::value, std::nullptr_t>::type = nullptr> - basic_value& operator=(const Map& mp) - { - this->cleanup(); - this->type_ = value_t::table; - this->region_info_ = std::make_shared(region_base{}); - - table_type tab; - for(const auto& elem : mp) {tab[elem.first] = elem.second;} - assigner(this->table_, std::move(tab)); - return *this; - } - - // user-defined ========================================================= - - // convert using into_toml() method ------------------------------------- - - template::value, std::nullptr_t>::type = nullptr> - basic_value(const T& ud): basic_value(ud.into_toml()) {} - - template::value, std::nullptr_t>::type = nullptr> - basic_value(const T& ud, std::vector com) - : basic_value(ud.into_toml(), std::move(com)) - {} - template::value, std::nullptr_t>::type = nullptr> - basic_value& operator=(const T& ud) - { - *this = ud.into_toml(); - return *this; - } - - // convert using into struct ----------------------------------------- - - template)> - basic_value(const T& ud): basic_value(::toml::into::into_toml(ud)) {} - template)> - basic_value(const T& ud, std::vector com) - : basic_value(::toml::into::into_toml(ud), std::move(com)) - {} - template)> - basic_value& operator=(const T& ud) - { - *this = ::toml::into::into_toml(ud); - return *this; - } - - // for internal use ------------------------------------------------------ - // - // Those constructors take detail::region that contains parse result. - - basic_value(boolean b, detail::region reg, std::vector cm) - : type_(value_t::boolean), - region_info_(std::make_shared(std::move(reg))), - comments_(std::move(cm)) - { - assigner(this->boolean_, b); - } - template, detail::negation> - >::value, std::nullptr_t>::type = nullptr> - basic_value(T i, detail::region reg, std::vector cm) - : type_(value_t::integer), - region_info_(std::make_shared(std::move(reg))), - comments_(std::move(cm)) - { - assigner(this->integer_, static_cast(i)); - } - template::value, std::nullptr_t>::type = nullptr> - basic_value(T f, detail::region reg, std::vector cm) - : type_(value_t::floating), - region_info_(std::make_shared(std::move(reg))), - comments_(std::move(cm)) - { - assigner(this->floating_, static_cast(f)); - } - basic_value(toml::string s, detail::region reg, - std::vector cm) - : type_(value_t::string), - region_info_(std::make_shared(std::move(reg))), - comments_(std::move(cm)) - { - assigner(this->string_, std::move(s)); - } - basic_value(const local_date& ld, detail::region reg, - std::vector cm) - : type_(value_t::local_date), - region_info_(std::make_shared(std::move(reg))), - comments_(std::move(cm)) - { - assigner(this->local_date_, ld); - } - basic_value(const local_time& lt, detail::region reg, - std::vector cm) - : type_(value_t::local_time), - region_info_(std::make_shared(std::move(reg))), - comments_(std::move(cm)) - { - assigner(this->local_time_, lt); - } - basic_value(const local_datetime& ldt, detail::region reg, - std::vector cm) - : type_(value_t::local_datetime), - region_info_(std::make_shared(std::move(reg))), - comments_(std::move(cm)) - { - assigner(this->local_datetime_, ldt); - } - basic_value(const offset_datetime& odt, detail::region reg, - std::vector cm) - : type_(value_t::offset_datetime), - region_info_(std::make_shared(std::move(reg))), - comments_(std::move(cm)) - { - assigner(this->offset_datetime_, odt); - } - basic_value(const array_type& ary, detail::region reg, - std::vector cm) - : type_(value_t::array), - region_info_(std::make_shared(std::move(reg))), - comments_(std::move(cm)) - { - assigner(this->array_, ary); - } - basic_value(const table_type& tab, detail::region reg, - std::vector cm) - : type_(value_t::table), - region_info_(std::make_shared(std::move(reg))), - comments_(std::move(cm)) - { - assigner(this->table_, tab); - } - - template::value, - std::nullptr_t>::type = nullptr> - basic_value(std::pair parse_result, std::vector com) - : basic_value(std::move(parse_result.first), - std::move(parse_result.second), - std::move(com)) - {} - - // type checking and casting ============================================ - - template::value, - std::nullptr_t>::type = nullptr> - bool is() const noexcept - { - return detail::type_to_enum::value == this->type_; - } - bool is(value_t t) const noexcept {return t == this->type_;} - - bool is_uninitialized() const noexcept {return this->is(value_t::empty );} - bool is_boolean() const noexcept {return this->is(value_t::boolean );} - bool is_integer() const noexcept {return this->is(value_t::integer );} - bool is_floating() const noexcept {return this->is(value_t::floating );} - bool is_string() const noexcept {return this->is(value_t::string );} - bool is_offset_datetime() const noexcept {return this->is(value_t::offset_datetime);} - bool is_local_datetime() const noexcept {return this->is(value_t::local_datetime );} - bool is_local_date() const noexcept {return this->is(value_t::local_date );} - bool is_local_time() const noexcept {return this->is(value_t::local_time );} - bool is_array() const noexcept {return this->is(value_t::array );} - bool is_table() const noexcept {return this->is(value_t::table );} - - value_t type() const noexcept {return type_;} - - template - typename detail::enum_to_type::type& cast() & - { - if(this->type_ != T) - { - detail::throw_bad_cast("toml::value::cast: ", this->type_, *this); - } - return detail::switch_cast::invoke(*this); - } - template - typename detail::enum_to_type::type const& cast() const& - { - if(this->type_ != T) - { - detail::throw_bad_cast("toml::value::cast: ", this->type_, *this); - } - return detail::switch_cast::invoke(*this); - } - template - typename detail::enum_to_type::type&& cast() && - { - if(this->type_ != T) - { - detail::throw_bad_cast("toml::value::cast: ", this->type_, *this); - } - return detail::switch_cast::invoke(std::move(*this)); - } - - // ------------------------------------------------------------------------ - // nothrow version - - boolean const& as_boolean (const std::nothrow_t&) const& noexcept {return this->boolean_;} - integer const& as_integer (const std::nothrow_t&) const& noexcept {return this->integer_;} - floating const& as_floating (const std::nothrow_t&) const& noexcept {return this->floating_;} - string const& as_string (const std::nothrow_t&) const& noexcept {return this->string_;} - offset_datetime const& as_offset_datetime(const std::nothrow_t&) const& noexcept {return this->offset_datetime_;} - local_datetime const& as_local_datetime (const std::nothrow_t&) const& noexcept {return this->local_datetime_;} - local_date const& as_local_date (const std::nothrow_t&) const& noexcept {return this->local_date_;} - local_time const& as_local_time (const std::nothrow_t&) const& noexcept {return this->local_time_;} - array_type const& as_array (const std::nothrow_t&) const& noexcept {return this->array_.value();} - table_type const& as_table (const std::nothrow_t&) const& noexcept {return this->table_.value();} - - boolean & as_boolean (const std::nothrow_t&) & noexcept {return this->boolean_;} - integer & as_integer (const std::nothrow_t&) & noexcept {return this->integer_;} - floating & as_floating (const std::nothrow_t&) & noexcept {return this->floating_;} - string & as_string (const std::nothrow_t&) & noexcept {return this->string_;} - offset_datetime& as_offset_datetime(const std::nothrow_t&) & noexcept {return this->offset_datetime_;} - local_datetime & as_local_datetime (const std::nothrow_t&) & noexcept {return this->local_datetime_;} - local_date & as_local_date (const std::nothrow_t&) & noexcept {return this->local_date_;} - local_time & as_local_time (const std::nothrow_t&) & noexcept {return this->local_time_;} - array_type & as_array (const std::nothrow_t&) & noexcept {return this->array_.value();} - table_type & as_table (const std::nothrow_t&) & noexcept {return this->table_.value();} - - boolean && as_boolean (const std::nothrow_t&) && noexcept {return std::move(this->boolean_);} - integer && as_integer (const std::nothrow_t&) && noexcept {return std::move(this->integer_);} - floating && as_floating (const std::nothrow_t&) && noexcept {return std::move(this->floating_);} - string && as_string (const std::nothrow_t&) && noexcept {return std::move(this->string_);} - offset_datetime&& as_offset_datetime(const std::nothrow_t&) && noexcept {return std::move(this->offset_datetime_);} - local_datetime && as_local_datetime (const std::nothrow_t&) && noexcept {return std::move(this->local_datetime_);} - local_date && as_local_date (const std::nothrow_t&) && noexcept {return std::move(this->local_date_);} - local_time && as_local_time (const std::nothrow_t&) && noexcept {return std::move(this->local_time_);} - array_type && as_array (const std::nothrow_t&) && noexcept {return std::move(this->array_.value());} - table_type && as_table (const std::nothrow_t&) && noexcept {return std::move(this->table_.value());} - - // ======================================================================== - // throw version - // ------------------------------------------------------------------------ - // const reference {{{ - - boolean const& as_boolean() const& - { - if(this->type_ != value_t::boolean) - { - detail::throw_bad_cast( - "toml::value::as_boolean(): ", this->type_, *this); - } - return this->boolean_; - } - integer const& as_integer() const& - { - if(this->type_ != value_t::integer) - { - detail::throw_bad_cast( - "toml::value::as_integer(): ", this->type_, *this); - } - return this->integer_; - } - floating const& as_floating() const& - { - if(this->type_ != value_t::floating) - { - detail::throw_bad_cast( - "toml::value::as_floating(): ", this->type_, *this); - } - return this->floating_; - } - string const& as_string() const& - { - if(this->type_ != value_t::string) - { - detail::throw_bad_cast( - "toml::value::as_string(): ", this->type_, *this); - } - return this->string_; - } - offset_datetime const& as_offset_datetime() const& - { - if(this->type_ != value_t::offset_datetime) - { - detail::throw_bad_cast( - "toml::value::as_offset_datetime(): ", this->type_, *this); - } - return this->offset_datetime_; - } - local_datetime const& as_local_datetime() const& - { - if(this->type_ != value_t::local_datetime) - { - detail::throw_bad_cast( - "toml::value::as_local_datetime(): ", this->type_, *this); - } - return this->local_datetime_; - } - local_date const& as_local_date() const& - { - if(this->type_ != value_t::local_date) - { - detail::throw_bad_cast( - "toml::value::as_local_date(): ", this->type_, *this); - } - return this->local_date_; - } - local_time const& as_local_time() const& - { - if(this->type_ != value_t::local_time) - { - detail::throw_bad_cast( - "toml::value::as_local_time(): ", this->type_, *this); - } - return this->local_time_; - } - array_type const& as_array() const& - { - if(this->type_ != value_t::array) - { - detail::throw_bad_cast( - "toml::value::as_array(): ", this->type_, *this); - } - return this->array_.value(); - } - table_type const& as_table() const& - { - if(this->type_ != value_t::table) - { - detail::throw_bad_cast( - "toml::value::as_table(): ", this->type_, *this); - } - return this->table_.value(); - } - // }}} - // ------------------------------------------------------------------------ - // nonconst reference {{{ - - boolean & as_boolean() & - { - if(this->type_ != value_t::boolean) - { - detail::throw_bad_cast( - "toml::value::as_boolean(): ", this->type_, *this); - } - return this->boolean_; - } - integer & as_integer() & - { - if(this->type_ != value_t::integer) - { - detail::throw_bad_cast( - "toml::value::as_integer(): ", this->type_, *this); - } - return this->integer_; - } - floating & as_floating() & - { - if(this->type_ != value_t::floating) - { - detail::throw_bad_cast( - "toml::value::as_floating(): ", this->type_, *this); - } - return this->floating_; - } - string & as_string() & - { - if(this->type_ != value_t::string) - { - detail::throw_bad_cast( - "toml::value::as_string(): ", this->type_, *this); - } - return this->string_; - } - offset_datetime & as_offset_datetime() & - { - if(this->type_ != value_t::offset_datetime) - { - detail::throw_bad_cast( - "toml::value::as_offset_datetime(): ", this->type_, *this); - } - return this->offset_datetime_; - } - local_datetime & as_local_datetime() & - { - if(this->type_ != value_t::local_datetime) - { - detail::throw_bad_cast( - "toml::value::as_local_datetime(): ", this->type_, *this); - } - return this->local_datetime_; - } - local_date & as_local_date() & - { - if(this->type_ != value_t::local_date) - { - detail::throw_bad_cast( - "toml::value::as_local_date(): ", this->type_, *this); - } - return this->local_date_; - } - local_time & as_local_time() & - { - if(this->type_ != value_t::local_time) - { - detail::throw_bad_cast( - "toml::value::as_local_time(): ", this->type_, *this); - } - return this->local_time_; - } - array_type & as_array() & - { - if(this->type_ != value_t::array) - { - detail::throw_bad_cast( - "toml::value::as_array(): ", this->type_, *this); - } - return this->array_.value(); - } - table_type & as_table() & - { - if(this->type_ != value_t::table) - { - detail::throw_bad_cast( - "toml::value::as_table(): ", this->type_, *this); - } - return this->table_.value(); - } - - // }}} - // ------------------------------------------------------------------------ - // rvalue reference {{{ - - boolean && as_boolean() && - { - if(this->type_ != value_t::boolean) - { - detail::throw_bad_cast( - "toml::value::as_boolean(): ", this->type_, *this); - } - return std::move(this->boolean_); - } - integer && as_integer() && - { - if(this->type_ != value_t::integer) - { - detail::throw_bad_cast( - "toml::value::as_integer(): ", this->type_, *this); - } - return std::move(this->integer_); - } - floating && as_floating() && - { - if(this->type_ != value_t::floating) - { - detail::throw_bad_cast( - "toml::value::as_floating(): ", this->type_, *this); - } - return std::move(this->floating_); - } - string && as_string() && - { - if(this->type_ != value_t::string) - { - detail::throw_bad_cast( - "toml::value::as_string(): ", this->type_, *this); - } - return std::move(this->string_); - } - offset_datetime && as_offset_datetime() && - { - if(this->type_ != value_t::offset_datetime) - { - detail::throw_bad_cast( - "toml::value::as_offset_datetime(): ", this->type_, *this); - } - return std::move(this->offset_datetime_); - } - local_datetime && as_local_datetime() && - { - if(this->type_ != value_t::local_datetime) - { - detail::throw_bad_cast( - "toml::value::as_local_datetime(): ", this->type_, *this); - } - return std::move(this->local_datetime_); - } - local_date && as_local_date() && - { - if(this->type_ != value_t::local_date) - { - detail::throw_bad_cast( - "toml::value::as_local_date(): ", this->type_, *this); - } - return std::move(this->local_date_); - } - local_time && as_local_time() && - { - if(this->type_ != value_t::local_time) - { - detail::throw_bad_cast( - "toml::value::as_local_time(): ", this->type_, *this); - } - return std::move(this->local_time_); - } - array_type && as_array() && - { - if(this->type_ != value_t::array) - { - detail::throw_bad_cast( - "toml::value::as_array(): ", this->type_, *this); - } - return std::move(this->array_.value()); - } - table_type && as_table() && - { - if(this->type_ != value_t::table) - { - detail::throw_bad_cast( - "toml::value::as_table(): ", this->type_, *this); - } - return std::move(this->table_.value()); - } - // }}} - - // accessors ============================================================= - // - // may throw type_error or out_of_range - // - value_type& at(const key& k) - { - if(!this->is_table()) - { - detail::throw_bad_cast( - "toml::value::at(key): ", this->type_, *this); - } - if(this->as_table(std::nothrow).count(k) == 0) - { - detail::throw_key_not_found_error(*this, k); - } - return this->as_table(std::nothrow).at(k); - } - value_type const& at(const key& k) const - { - if(!this->is_table()) - { - detail::throw_bad_cast( - "toml::value::at(key): ", this->type_, *this); - } - if(this->as_table(std::nothrow).count(k) == 0) - { - detail::throw_key_not_found_error(*this, k); - } - return this->as_table(std::nothrow).at(k); - } - value_type& operator[](const key& k) - { - if(this->is_uninitialized()) - { - *this = table_type{}; - } - else if(!this->is_table()) // initialized, but not a table - { - detail::throw_bad_cast( - "toml::value::operator[](key): ", this->type_, *this); - } - return this->as_table(std::nothrow)[k]; - } - - value_type& at(const std::size_t idx) - { - if(!this->is_array()) - { - detail::throw_bad_cast( - "toml::value::at(idx): ", this->type_, *this); - } - if(this->as_array(std::nothrow).size() <= idx) - { - throw std::out_of_range(detail::format_underline( - "toml::value::at(idx): no element corresponding to the index", { - {this->location(), concat_to_string("the length is ", - this->as_array(std::nothrow).size(), - ", and the specified index is ", idx)} - })); - } - return this->as_array().at(idx); - } - value_type const& at(const std::size_t idx) const - { - if(!this->is_array()) - { - detail::throw_bad_cast( - "toml::value::at(idx): ", this->type_, *this); - } - if(this->as_array(std::nothrow).size() <= idx) - { - throw std::out_of_range(detail::format_underline( - "toml::value::at(idx): no element corresponding to the index", { - {this->location(), concat_to_string("the length is ", - this->as_array(std::nothrow).size(), - ", and the specified index is ", idx)} - })); - } - return this->as_array(std::nothrow).at(idx); - } - - value_type& operator[](const std::size_t idx) noexcept - { - // no check... - return this->as_array(std::nothrow)[idx]; - } - value_type const& operator[](const std::size_t idx) const noexcept - { - // no check... - return this->as_array(std::nothrow)[idx]; - } - - void push_back(const value_type& x) - { - if(!this->is_array()) - { - detail::throw_bad_cast( - "toml::value::push_back(value): ", this->type_, *this); - } - this->as_array(std::nothrow).push_back(x); - return; - } - void push_back(value_type&& x) - { - if(!this->is_array()) - { - detail::throw_bad_cast( - "toml::value::push_back(value): ", this->type_, *this); - } - this->as_array(std::nothrow).push_back(std::move(x)); - return; - } - - template - value_type& emplace_back(Ts&& ... args) - { - if(!this->is_array()) - { - detail::throw_bad_cast( - "toml::value::emplace_back(...): ", this->type_, *this); - } - this->as_array(std::nothrow).emplace_back(std::forward(args) ...); - return this->as_array(std::nothrow).back(); - } - - std::size_t size() const - { - switch(this->type_) - { - case value_t::array: - { - return this->as_array(std::nothrow).size(); - } - case value_t::table: - { - return this->as_table(std::nothrow).size(); - } - case value_t::string: - { - return this->as_string(std::nothrow).str.size(); - } - default: - { - throw type_error(detail::format_underline( - "toml::value::size(): bad_cast to container types", { - {this->location(), - concat_to_string("the actual type is ", this->type_)} - }), this->location()); - } - } - } - - std::size_t count(const key_type& k) const - { - if(!this->is_table()) - { - detail::throw_bad_cast( - "toml::value::count(key): ", this->type_, *this); - } - return this->as_table(std::nothrow).count(k); - } - - bool contains(const key_type& k) const - { - if(!this->is_table()) - { - detail::throw_bad_cast( - "toml::value::contains(key): ", this->type_, *this); - } - return (this->as_table(std::nothrow).count(k) != 0); - } - - source_location location() const - { - return source_location(this->region_info_.get()); - } - - comment_type const& comments() const noexcept {return this->comments_;} - comment_type& comments() noexcept {return this->comments_;} - - private: - - void cleanup() noexcept - { - switch(this->type_) - { - case value_t::string : {string_.~string(); return;} - case value_t::array : {array_.~array_storage(); return;} - case value_t::table : {table_.~table_storage(); return;} - default : return; - } - } - - // for error messages - template - friend region_base const* detail::get_region(const Value& v); - - template - friend void detail::change_region(Value& v, detail::region reg); - - private: - - using array_storage = detail::storage; - using table_storage = detail::storage; - - value_t type_; - union - { - boolean boolean_; - integer integer_; - floating floating_; - string string_; - offset_datetime offset_datetime_; - local_datetime local_datetime_; - local_date local_date_; - local_time local_time_; - array_storage array_; - table_storage table_; - }; - std::shared_ptr region_info_; - comment_type comments_; -}; - -// default toml::value and default array/table. -// TOML11_DEFAULT_COMMENT_STRATEGY is defined in comments.hpp -using value = basic_value; -using array = typename value::array_type; -using table = typename value::table_type; - -template class T, template class A> -inline bool -operator==(const basic_value& lhs, const basic_value& rhs) -{ - if(lhs.type() != rhs.type()) {return false;} - if(lhs.comments() != rhs.comments()) {return false;} - - switch(lhs.type()) - { - case value_t::boolean : - { - return lhs.as_boolean() == rhs.as_boolean(); - } - case value_t::integer : - { - return lhs.as_integer() == rhs.as_integer(); - } - case value_t::floating : - { - return lhs.as_floating() == rhs.as_floating(); - } - case value_t::string : - { - return lhs.as_string() == rhs.as_string(); - } - case value_t::offset_datetime: - { - return lhs.as_offset_datetime() == rhs.as_offset_datetime(); - } - case value_t::local_datetime: - { - return lhs.as_local_datetime() == rhs.as_local_datetime(); - } - case value_t::local_date: - { - return lhs.as_local_date() == rhs.as_local_date(); - } - case value_t::local_time: - { - return lhs.as_local_time() == rhs.as_local_time(); - } - case value_t::array : - { - return lhs.as_array() == rhs.as_array(); - } - case value_t::table : - { - return lhs.as_table() == rhs.as_table(); - } - case value_t::empty : {return true; } - default: {return false;} - } -} - -template class T, template class A> -inline bool operator!=(const basic_value& lhs, const basic_value& rhs) -{ - return !(lhs == rhs); -} - -template class T, template class A> -typename std::enable_if::array_type>, - detail::is_comparable::table_type> - >::value, bool>::type -operator<(const basic_value& lhs, const basic_value& rhs) -{ - if(lhs.type() != rhs.type()){return (lhs.type() < rhs.type());} - switch(lhs.type()) - { - case value_t::boolean : - { - return lhs.as_boolean() < rhs.as_boolean() || - (lhs.as_boolean() == rhs.as_boolean() && - lhs.comments() < rhs.comments()); - } - case value_t::integer : - { - return lhs.as_integer() < rhs.as_integer() || - (lhs.as_integer() == rhs.as_integer() && - lhs.comments() < rhs.comments()); - } - case value_t::floating : - { - return lhs.as_floating() < rhs.as_floating() || - (lhs.as_floating() == rhs.as_floating() && - lhs.comments() < rhs.comments()); - } - case value_t::string : - { - return lhs.as_string() < rhs.as_string() || - (lhs.as_string() == rhs.as_string() && - lhs.comments() < rhs.comments()); - } - case value_t::offset_datetime: - { - return lhs.as_offset_datetime() < rhs.as_offset_datetime() || - (lhs.as_offset_datetime() == rhs.as_offset_datetime() && - lhs.comments() < rhs.comments()); - } - case value_t::local_datetime: - { - return lhs.as_local_datetime() < rhs.as_local_datetime() || - (lhs.as_local_datetime() == rhs.as_local_datetime() && - lhs.comments() < rhs.comments()); - } - case value_t::local_date: - { - return lhs.as_local_date() < rhs.as_local_date() || - (lhs.as_local_date() == rhs.as_local_date() && - lhs.comments() < rhs.comments()); - } - case value_t::local_time: - { - return lhs.as_local_time() < rhs.as_local_time() || - (lhs.as_local_time() == rhs.as_local_time() && - lhs.comments() < rhs.comments()); - } - case value_t::array : - { - return lhs.as_array() < rhs.as_array() || - (lhs.as_array() == rhs.as_array() && - lhs.comments() < rhs.comments()); - } - case value_t::table : - { - return lhs.as_table() < rhs.as_table() || - (lhs.as_table() == rhs.as_table() && - lhs.comments() < rhs.comments()); - } - case value_t::empty : - { - return lhs.comments() < rhs.comments(); - } - default: - { - return lhs.comments() < rhs.comments(); - } - } -} - -template class T, template class A> -typename std::enable_if::array_type>, - detail::is_comparable::table_type> - >::value, bool>::type -operator<=(const basic_value& lhs, const basic_value& rhs) -{ - return (lhs < rhs) || (lhs == rhs); -} -template class T, template class A> -typename std::enable_if::array_type>, - detail::is_comparable::table_type> - >::value, bool>::type -operator>(const basic_value& lhs, const basic_value& rhs) -{ - return !(lhs <= rhs); -} -template class T, template class A> -typename std::enable_if::array_type>, - detail::is_comparable::table_type> - >::value, bool>::type -operator>=(const basic_value& lhs, const basic_value& rhs) -{ - return !(lhs < rhs); -} - -template class T, template class A> -inline std::string format_error(const std::string& err_msg, - const basic_value& v, const std::string& comment, - std::vector hints = {}, - const bool colorize = TOML11_ERROR_MESSAGE_COLORIZED) -{ - return detail::format_underline(err_msg, {{v.location(), comment}}, - std::move(hints), colorize); -} - -template class T, template class A> -inline std::string format_error(const std::string& err_msg, - const toml::basic_value& v1, const std::string& comment1, - const toml::basic_value& v2, const std::string& comment2, - std::vector hints = {}, - const bool colorize = TOML11_ERROR_MESSAGE_COLORIZED) -{ - return detail::format_underline(err_msg, { - {v1.location(), comment1}, {v2.location(), comment2} - }, std::move(hints), colorize); -} - -template class T, template class A> -inline std::string format_error(const std::string& err_msg, - const toml::basic_value& v1, const std::string& comment1, - const toml::basic_value& v2, const std::string& comment2, - const toml::basic_value& v3, const std::string& comment3, - std::vector hints = {}, - const bool colorize = TOML11_ERROR_MESSAGE_COLORIZED) -{ - return detail::format_underline(err_msg, {{v1.location(), comment1}, - {v2.location(), comment2}, {v3.location(), comment3} - }, std::move(hints), colorize); -} - -template class T, template class A> -detail::return_type_of_t -visit(Visitor&& visitor, const toml::basic_value& v) -{ - switch(v.type()) - { - case value_t::boolean : {return visitor(v.as_boolean ());} - case value_t::integer : {return visitor(v.as_integer ());} - case value_t::floating : {return visitor(v.as_floating ());} - case value_t::string : {return visitor(v.as_string ());} - case value_t::offset_datetime: {return visitor(v.as_offset_datetime());} - case value_t::local_datetime : {return visitor(v.as_local_datetime ());} - case value_t::local_date : {return visitor(v.as_local_date ());} - case value_t::local_time : {return visitor(v.as_local_time ());} - case value_t::array : {return visitor(v.as_array ());} - case value_t::table : {return visitor(v.as_table ());} - case value_t::empty : break; - default: break; - } - throw std::runtime_error(format_error("[error] toml::visit: toml::basic_value " - "does not have any valid basic_value.", v, "here")); -} - -template class T, template class A> -detail::return_type_of_t -visit(Visitor&& visitor, toml::basic_value& v) -{ - switch(v.type()) - { - case value_t::boolean : {return visitor(v.as_boolean ());} - case value_t::integer : {return visitor(v.as_integer ());} - case value_t::floating : {return visitor(v.as_floating ());} - case value_t::string : {return visitor(v.as_string ());} - case value_t::offset_datetime: {return visitor(v.as_offset_datetime());} - case value_t::local_datetime : {return visitor(v.as_local_datetime ());} - case value_t::local_date : {return visitor(v.as_local_date ());} - case value_t::local_time : {return visitor(v.as_local_time ());} - case value_t::array : {return visitor(v.as_array ());} - case value_t::table : {return visitor(v.as_table ());} - case value_t::empty : break; - default: break; - } - throw std::runtime_error(format_error("[error] toml::visit: toml::basic_value " - "does not have any valid basic_value.", v, "here")); -} - -template class T, template class A> -detail::return_type_of_t -visit(Visitor&& visitor, toml::basic_value&& v) -{ - switch(v.type()) - { - case value_t::boolean : {return visitor(std::move(v.as_boolean ()));} - case value_t::integer : {return visitor(std::move(v.as_integer ()));} - case value_t::floating : {return visitor(std::move(v.as_floating ()));} - case value_t::string : {return visitor(std::move(v.as_string ()));} - case value_t::offset_datetime: {return visitor(std::move(v.as_offset_datetime()));} - case value_t::local_datetime : {return visitor(std::move(v.as_local_datetime ()));} - case value_t::local_date : {return visitor(std::move(v.as_local_date ()));} - case value_t::local_time : {return visitor(std::move(v.as_local_time ()));} - case value_t::array : {return visitor(std::move(v.as_array ()));} - case value_t::table : {return visitor(std::move(v.as_table ()));} - case value_t::empty : break; - default: break; - } - throw std::runtime_error(format_error("[error] toml::visit: toml::basic_value " - "does not have any valid basic_value.", v, "here")); -} - -}// toml -#endif// TOML11_VALUE diff --git a/3party/toml11/toml/version.hpp b/3party/toml11/toml/version.hpp deleted file mode 100644 index 9cbfa39..0000000 --- a/3party/toml11/toml/version.hpp +++ /dev/null @@ -1,42 +0,0 @@ -#ifndef TOML11_VERSION_HPP -#define TOML11_VERSION_HPP - -// This file checks C++ version. - -#ifndef __cplusplus -# error "__cplusplus is not defined" -#endif - -// Since MSVC does not define `__cplusplus` correctly unless you pass -// `/Zc:__cplusplus` when compiling, the workaround macros are added. -// Those enables you to define version manually or to use MSVC specific -// version macro automatically. -// -// The value of `__cplusplus` macro is defined in the C++ standard spec, but -// MSVC ignores the value, maybe because of backward compatibility. Instead, -// MSVC defines _MSVC_LANG that has the same value as __cplusplus defined in -// the C++ standard. First we check the manual version definition, and then -// we check if _MSVC_LANG is defined. If neither, use normal `__cplusplus`. -// -// FYI: https://docs.microsoft.com/en-us/cpp/build/reference/zc-cplusplus?view=msvc-170 -// https://docs.microsoft.com/en-us/cpp/preprocessor/predefined-macros?view=msvc-170 -// -#if defined(TOML11_ENFORCE_CXX11) -# define TOML11_CPLUSPLUS_STANDARD_VERSION 201103L -#elif defined(TOML11_ENFORCE_CXX14) -# define TOML11_CPLUSPLUS_STANDARD_VERSION 201402L -#elif defined(TOML11_ENFORCE_CXX17) -# define TOML11_CPLUSPLUS_STANDARD_VERSION 201703L -#elif defined(TOML11_ENFORCE_CXX20) -# define TOML11_CPLUSPLUS_STANDARD_VERSION 202002L -#elif defined(_MSVC_LANG) && defined(_MSC_VER) && 1910 <= _MSC_VER -# define TOML11_CPLUSPLUS_STANDARD_VERSION _MSVC_LANG -#else -# define TOML11_CPLUSPLUS_STANDARD_VERSION __cplusplus -#endif - -#if TOML11_CPLUSPLUS_STANDARD_VERSION < 201103L && _MSC_VER < 1900 -# error "toml11 requires C++11 or later." -#endif - -#endif// TOML11_VERSION_HPP