dfd5af045f
Some checks failed
linux-aarch64-cpu-gcc / linux-gcc-aarch64 (push) Failing after 51s
linux-arm-gcc / linux-gcc-armhf (push) Failing after 49s
linux-mips64-gcc / linux-gcc-mips64el (Debug) (push) Failing after 55s
linux-mips64-gcc / linux-gcc-mips64el (Release) (push) Failing after 1m5s
linux-x64-gcc / linux-gcc (Debug) (push) Failing after 42s
linux-x64-gcc / linux-gcc (Release) (push) Failing after 54s
17242 lines
552 KiB
C++
17242 lines
552 KiB
C++
#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(<string_view>)
|
|
# define TOML11_HAS_STRING_VIEW 1
|
|
# endif
|
|
#endif
|
|
|
|
#ifndef TOML11_DISABLE_STD_FILESYSTEM
|
|
# if TOML11_CPLUSPLUS_STANDARD_VERSION >= TOML11_CXX17_VALUE
|
|
# if __has_include(<filesystem>)
|
|
# define TOML11_HAS_FILESYSTEM 1
|
|
# endif
|
|
# endif
|
|
#endif
|
|
|
|
#if TOML11_CPLUSPLUS_STANDARD_VERSION >= TOML11_CXX17_VALUE
|
|
# if __has_include(<optional>)
|
|
# 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 <iosfwd>
|
|
#include <string>
|
|
#include <utility>
|
|
|
|
#include <cstddef>
|
|
#include <cstdint>
|
|
|
|
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<typename T, typename F>
|
|
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<typename U>
|
|
value_with_format(value_with_format<U, format_type> 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 <ostream>
|
|
#include <sstream>
|
|
|
|
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<std::uint8_t>(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<std::uint8_t>(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<std::uint8_t>(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<std::uint8_t>(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<std::uint8_t>(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<std::uint8_t>(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<std::uint8_t>(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 <chrono>
|
|
#include <iosfwd>
|
|
#include <string>
|
|
|
|
#include <cstdint>
|
|
#include <cstdlib>
|
|
#include <ctime>
|
|
|
|
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<std::int16_t>(y)},
|
|
month{static_cast<std::uint8_t>(m)},
|
|
day {static_cast<std::uint8_t>(d)}
|
|
{}
|
|
|
|
explicit local_date(const std::tm& t)
|
|
: year {static_cast<std::int16_t>(t.tm_year + 1900)},
|
|
month{static_cast<std::uint8_t>(t.tm_mon)},
|
|
day {static_cast<std::uint8_t>(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<std::uint8_t>(h)},
|
|
minute{static_cast<std::uint8_t>(m)},
|
|
second{static_cast<std::uint8_t>(s)},
|
|
millisecond{static_cast<std::uint16_t>(ms)},
|
|
microsecond{static_cast<std::uint16_t>(us)},
|
|
nanosecond {static_cast<std::uint16_t>(ns)}
|
|
{}
|
|
|
|
explicit local_time(const std::tm& t)
|
|
: hour {static_cast<std::uint8_t>(t.tm_hour)},
|
|
minute{static_cast<std::uint8_t>(t.tm_min )},
|
|
second{static_cast<std::uint8_t>(t.tm_sec )},
|
|
millisecond{0}, microsecond{0}, nanosecond{0}
|
|
{}
|
|
|
|
template<typename Rep, typename Period>
|
|
explicit local_time(const std::chrono::duration<Rep, Period>& t)
|
|
{
|
|
const auto h = std::chrono::duration_cast<std::chrono::hours>(t);
|
|
this->hour = static_cast<std::uint8_t>(h.count());
|
|
const auto t2 = t - h;
|
|
const auto m = std::chrono::duration_cast<std::chrono::minutes>(t2);
|
|
this->minute = static_cast<std::uint8_t>(m.count());
|
|
const auto t3 = t2 - m;
|
|
const auto s = std::chrono::duration_cast<std::chrono::seconds>(t3);
|
|
this->second = static_cast<std::uint8_t>(s.count());
|
|
const auto t4 = t3 - s;
|
|
const auto ms = std::chrono::duration_cast<std::chrono::milliseconds>(t4);
|
|
this->millisecond = static_cast<std::uint16_t>(ms.count());
|
|
const auto t5 = t4 - ms;
|
|
const auto us = std::chrono::duration_cast<std::chrono::microseconds>(t5);
|
|
this->microsecond = static_cast<std::uint16_t>(us.count());
|
|
const auto t6 = t5 - us;
|
|
const auto ns = std::chrono::duration_cast<std::chrono::nanoseconds>(t6);
|
|
this->nanosecond = static_cast<std::uint16_t>(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<std::int8_t>(h)},
|
|
minute{static_cast<std::int8_t>(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 <array>
|
|
#include <iomanip>
|
|
#include <ostream>
|
|
#include <sstream>
|
|
#include <tuple>
|
|
|
|
#include <cstdlib>
|
|
#include <ctime>
|
|
|
|
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<int>(this->day);
|
|
t.tm_mon = static_cast<int>(this->month);
|
|
t.tm_year = static_cast<int>(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<int>(date.year ) << '-';
|
|
os << std::setfill('0') << std::setw(2) << static_cast<int>(date.month) + 1 << '-';
|
|
os << std::setfill('0') << std::setw(2) << static_cast<int>(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<int>(time.hour ) << ':';
|
|
os << std::setfill('0') << std::setw(2) << static_cast<int>(time.minute) << ':';
|
|
os << std::setfill('0') << std::setw(2) << static_cast<int>(time.second);
|
|
if(time.millisecond != 0 || time.microsecond != 0 || time.nanosecond != 0)
|
|
{
|
|
os << '.';
|
|
os << std::setfill('0') << std::setw(3) << static_cast<int>(time.millisecond);
|
|
if(time.microsecond != 0 || time.nanosecond != 0)
|
|
{
|
|
os << std::setfill('0') << std::setw(3) << static_cast<int>(time.microsecond);
|
|
if(time.nanosecond != 0)
|
|
{
|
|
os << std::setfill('0') << std::setw(3) << static_cast<int>(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<int>(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::uint16_t>(
|
|
std::chrono::duration_cast<std::chrono::milliseconds>(t_diff).count());
|
|
this->time.microsecond = static_cast<std::uint16_t>(
|
|
std::chrono::duration_cast<std::chrono::microseconds>(t_diff).count());
|
|
this->time.nanosecond = static_cast<std::uint16_t>(
|
|
std::chrono::duration_cast<std::chrono::nanoseconds >(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<int>(this->time.second);
|
|
t.tm_min = static_cast<int>(this->time.minute);
|
|
t.tm_hour = static_cast<int>(this->time.hour);
|
|
t.tm_mday = static_cast<int>(this->date.day);
|
|
t.tm_mon = static_cast<int>(this->date.month);
|
|
t.tm_year = static_cast<int>(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<internal_duration>(
|
|
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<int>(this->time.second);
|
|
t.tm_min = static_cast<int>(this->time.minute);
|
|
t.tm_hour = static_cast<int>(this->time.hour);
|
|
t.tm_mday = static_cast<int>(this->date.day);
|
|
t.tm_mon = static_cast<int>(this->date.month);
|
|
t.tm_year = static_cast<int>(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<internal_duration>(
|
|
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<char, 6> 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 <algorithm>
|
|
#include <iterator>
|
|
#include <memory>
|
|
#include <string>
|
|
#include <type_traits>
|
|
|
|
#include <cassert>
|
|
|
|
#if TOML11_CPLUSPLUS_STANDARD_VERSION >= TOML11_CXX20_VALUE
|
|
# if __has_include(<bit>)
|
|
# include <bit>
|
|
# endif
|
|
#endif
|
|
|
|
#include <cstring>
|
|
|
|
// ----------------------------------------------------------------------------
|
|
|
|
#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<typename T, typename ... Ts>
|
|
std::unique_ptr<T> make_unique(Ts&& ... args)
|
|
{
|
|
return std::unique_ptr<T>(new T(std::forward<Ts>(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<typename Iterator>
|
|
std::reverse_iterator<Iterator> make_reverse_iterator(Iterator iter)
|
|
{
|
|
return std::reverse_iterator<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<typename T>
|
|
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<typename U, typename T>
|
|
U bit_cast(const T& x) noexcept
|
|
{
|
|
static_assert(sizeof(T) == sizeof(U), "");
|
|
static_assert(std::is_default_constructible<T>::value, "");
|
|
|
|
U z;
|
|
std::memcpy(reinterpret_cast<char*>(std::addressof(z)),
|
|
reinterpret_cast<const char*>(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<typename T>
|
|
struct remove_cvref
|
|
{
|
|
using type = typename std::remove_cv<
|
|
typename std::remove_reference<T>::type>::type;
|
|
};
|
|
|
|
template<typename T>
|
|
using remove_cvref_t = typename remove_cvref<T>::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<typename ...> struct conjunction : std::true_type{};
|
|
template<typename T> struct conjunction<T> : T{};
|
|
template<typename T, typename ... Ts>
|
|
struct conjunction<T, Ts...> :
|
|
std::conditional<static_cast<bool>(T::value), conjunction<Ts...>, T>::type
|
|
{};
|
|
|
|
template<typename ...> struct disjunction : std::false_type{};
|
|
template<typename T> struct disjunction<T> : T {};
|
|
template<typename T, typename ... Ts>
|
|
struct disjunction<T, Ts...> :
|
|
std::conditional<static_cast<bool>(T::value), T, disjunction<Ts...>>::type
|
|
{};
|
|
|
|
template<typename T>
|
|
struct negation : std::integral_constant<bool, !static_cast<bool>(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<std::size_t ... Ns> struct index_sequence{};
|
|
|
|
template<bool B, std::size_t N, typename T>
|
|
struct double_index_sequence;
|
|
|
|
template<std::size_t N, std::size_t ... Is>
|
|
struct double_index_sequence<true, N, index_sequence<Is...>>
|
|
{
|
|
using type = index_sequence<Is..., (Is+N)..., N*2>;
|
|
};
|
|
template<std::size_t N, std::size_t ... Is>
|
|
struct double_index_sequence<false, N, index_sequence<Is...>>
|
|
{
|
|
using type = index_sequence<Is..., (Is+N)...>;
|
|
};
|
|
|
|
template<std::size_t N>
|
|
struct index_sequence_maker
|
|
{
|
|
using type = typename double_index_sequence<
|
|
N % 2 == 1, N/2, typename index_sequence_maker<N/2>::type
|
|
>::type;
|
|
};
|
|
template<>
|
|
struct index_sequence_maker<0>
|
|
{
|
|
using type = index_sequence<>;
|
|
};
|
|
|
|
template<std::size_t N>
|
|
using make_index_sequence = typename index_sequence_maker<N>::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<bool B, typename T>
|
|
using enable_if_t = typename std::enable_if<B, T>::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<typename F, typename ... Args>
|
|
using return_type_of_t = std::invoke_result_t<F, Args...>;
|
|
|
|
#else
|
|
|
|
// result_of is deprecated after C++17
|
|
template<typename F, typename ... Args>
|
|
using return_type_of_t = typename std::result_of<F(Args...)>::type;
|
|
|
|
#endif // TOML11_HAS_STD_INVOKE_RESULT
|
|
|
|
} // cxx
|
|
} // toml
|
|
|
|
// ----------------------------------------------------------------------------
|
|
// (subset of) source_location
|
|
|
|
#if TOML11_CPLUSPLUS_STANDARD_VERSION >= 202002L
|
|
# if __has_include(<source_location>)
|
|
# 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(<experimental/source_location>)
|
|
# 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 <source_location>
|
|
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 <experimental/source_location>
|
|
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(<optional>)
|
|
# include <optional>
|
|
# 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<typename charT, typename traitsT>
|
|
std::basic_ostream<charT, traitsT>&
|
|
operator<<(std::basic_ostream<charT, traitsT>& 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<typename charT, typename traitsT>
|
|
std::basic_ostream<charT, traitsT>&
|
|
operator<<(std::basic_ostream<charT, traitsT>& os, const nullopt_t&)
|
|
{
|
|
os << "nullopt";
|
|
return os;
|
|
}
|
|
|
|
template<typename T>
|
|
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<typename U, enable_if_t<std::is_constructible<T, U>::value, std::nullptr_t> = nullptr>
|
|
explicit optional(U&& x): has_value_(true), value_(std::forward<U>(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<typename U, enable_if_t<conjunction<
|
|
negation<std::is_same<T, U>>, std::is_constructible<T, U>
|
|
>::value, std::nullptr_t> = nullptr>
|
|
explicit optional(const optional<U>& rhs): has_value_(rhs.has_value_), null_('\0')
|
|
{
|
|
if(rhs.has_value_)
|
|
{
|
|
this->assigner(rhs.value_);
|
|
}
|
|
}
|
|
template<typename U, enable_if_t<conjunction<
|
|
negation<std::is_same<T, U>>, std::is_constructible<T, U>
|
|
>::value, std::nullptr_t> = nullptr>
|
|
explicit optional(optional<U>&& rhs): has_value_(rhs.has_value_), null_('\0')
|
|
{
|
|
if(this->has_value_)
|
|
{
|
|
this->assigner(std::move(rhs.value_));
|
|
}
|
|
}
|
|
|
|
template<typename U, enable_if_t<conjunction<
|
|
negation<std::is_same<T, U>>, std::is_constructible<T, U>
|
|
>::value, std::nullptr_t> = nullptr>
|
|
optional& operator=(const optional<U>& 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<typename U, enable_if_t<conjunction<
|
|
negation<std::is_same<T, U>>, std::is_constructible<T, U>
|
|
>::value, std::nullptr_t> = nullptr>
|
|
optional& operator=(optional<U>&& 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<typename U>
|
|
void assigner(U&& x)
|
|
{
|
|
const auto tmp = ::new(std::addressof(this->value_)) value_type(std::forward<U>(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 <iosfwd>
|
|
#include <string>
|
|
#include <type_traits>
|
|
|
|
#include <cstdint>
|
|
|
|
namespace toml
|
|
{
|
|
|
|
// forward decl
|
|
template<typename TypeConfig>
|
|
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<value_t V>
|
|
using value_t_constant = std::integral_constant<value_t, V>;
|
|
|
|
template<typename T, typename Value>
|
|
struct type_to_enum : value_t_constant<value_t::empty> {};
|
|
|
|
template<typename V> struct type_to_enum<typename V::boolean_type , V> : value_t_constant<value_t::boolean > {};
|
|
template<typename V> struct type_to_enum<typename V::integer_type , V> : value_t_constant<value_t::integer > {};
|
|
template<typename V> struct type_to_enum<typename V::floating_type , V> : value_t_constant<value_t::floating > {};
|
|
template<typename V> struct type_to_enum<typename V::string_type , V> : value_t_constant<value_t::string > {};
|
|
template<typename V> struct type_to_enum<typename V::offset_datetime_type, V> : value_t_constant<value_t::offset_datetime> {};
|
|
template<typename V> struct type_to_enum<typename V::local_datetime_type , V> : value_t_constant<value_t::local_datetime > {};
|
|
template<typename V> struct type_to_enum<typename V::local_date_type , V> : value_t_constant<value_t::local_date > {};
|
|
template<typename V> struct type_to_enum<typename V::local_time_type , V> : value_t_constant<value_t::local_time > {};
|
|
template<typename V> struct type_to_enum<typename V::array_type , V> : value_t_constant<value_t::array > {};
|
|
template<typename V> struct type_to_enum<typename V::table_type , V> : value_t_constant<value_t::table > {};
|
|
|
|
template<value_t V, typename Value>
|
|
struct enum_to_type { using type = void; };
|
|
|
|
template<typename V> struct enum_to_type<value_t::boolean , V> { using type = typename V::boolean_type ; };
|
|
template<typename V> struct enum_to_type<value_t::integer , V> { using type = typename V::integer_type ; };
|
|
template<typename V> struct enum_to_type<value_t::floating , V> { using type = typename V::floating_type ; };
|
|
template<typename V> struct enum_to_type<value_t::string , V> { using type = typename V::string_type ; };
|
|
template<typename V> struct enum_to_type<value_t::offset_datetime, V> { using type = typename V::offset_datetime_type; };
|
|
template<typename V> struct enum_to_type<value_t::local_datetime , V> { using type = typename V::local_datetime_type ; };
|
|
template<typename V> struct enum_to_type<value_t::local_date , V> { using type = typename V::local_date_type ; };
|
|
template<typename V> struct enum_to_type<value_t::local_time , V> { using type = typename V::local_time_type ; };
|
|
template<typename V> struct enum_to_type<value_t::array , V> { using type = typename V::array_type ; };
|
|
template<typename V> struct enum_to_type<value_t::table , V> { using type = typename V::table_type ; };
|
|
|
|
template<value_t V, typename Value>
|
|
using enum_to_type_t = typename enum_to_type<V, Value>::type;
|
|
|
|
template<value_t V>
|
|
struct enum_to_fmt_type { using type = void; };
|
|
|
|
template<> struct enum_to_fmt_type<value_t::boolean > { using type = boolean_format_info ; };
|
|
template<> struct enum_to_fmt_type<value_t::integer > { using type = integer_format_info ; };
|
|
template<> struct enum_to_fmt_type<value_t::floating > { using type = floating_format_info ; };
|
|
template<> struct enum_to_fmt_type<value_t::string > { using type = string_format_info ; };
|
|
template<> struct enum_to_fmt_type<value_t::offset_datetime> { using type = offset_datetime_format_info; };
|
|
template<> struct enum_to_fmt_type<value_t::local_datetime > { using type = local_datetime_format_info ; };
|
|
template<> struct enum_to_fmt_type<value_t::local_date > { using type = local_date_format_info ; };
|
|
template<> struct enum_to_fmt_type<value_t::local_time > { using type = local_time_format_info ; };
|
|
template<> struct enum_to_fmt_type<value_t::array > { using type = array_format_info ; };
|
|
template<> struct enum_to_fmt_type<value_t::table > { using type = table_format_info ; };
|
|
|
|
template<value_t V>
|
|
using enum_to_fmt_type_t = typename enum_to_fmt_type<V>::type;
|
|
|
|
template<typename T, typename Value>
|
|
struct is_exact_toml_type0 : cxx::disjunction<
|
|
std::is_same<T, typename Value::boolean_type >,
|
|
std::is_same<T, typename Value::integer_type >,
|
|
std::is_same<T, typename Value::floating_type >,
|
|
std::is_same<T, typename Value::string_type >,
|
|
std::is_same<T, typename Value::offset_datetime_type>,
|
|
std::is_same<T, typename Value::local_datetime_type >,
|
|
std::is_same<T, typename Value::local_date_type >,
|
|
std::is_same<T, typename Value::local_time_type >,
|
|
std::is_same<T, typename Value::array_type >,
|
|
std::is_same<T, typename Value::table_type >
|
|
>{};
|
|
template<typename T, typename V> struct is_exact_toml_type: is_exact_toml_type0<cxx::remove_cvref_t<T>, V> {};
|
|
template<typename T, typename V> struct is_not_toml_type : cxx::negation<is_exact_toml_type<T, V>> {};
|
|
|
|
} // 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 <ostream>
|
|
#include <sstream>
|
|
#include <string>
|
|
|
|
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<toml::value>` 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<std::unique_ptr<toml::value>>`. Although `std::unique_ptr` is
|
|
// noncopyable, we want to make `toml::value` copyable. `storage` is introduced
|
|
// to resolve those problems.
|
|
template<typename T>
|
|
struct storage
|
|
{
|
|
using value_type = T;
|
|
|
|
explicit storage(value_type v): ptr_(cxx::make_unique<T>(std::move(v))) {}
|
|
~storage() = default;
|
|
|
|
storage(const storage& rhs): ptr_(cxx::make_unique<T>(*rhs.ptr_)) {}
|
|
storage& operator=(const storage& rhs)
|
|
{
|
|
this->ptr_ = cxx::make_unique<T>(*rhs.ptr_);
|
|
return *this;
|
|
}
|
|
|
|
storage(storage&&) = default;
|
|
storage& operator=(storage&&) = default;
|
|
|
|
bool is_ok() const noexcept {return static_cast<bool>(ptr_);}
|
|
|
|
value_type& get() const noexcept {return *ptr_;}
|
|
|
|
private:
|
|
std::unique_ptr<value_type> 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 <exception>
|
|
#include <initializer_list>
|
|
#include <iterator>
|
|
#include <stdexcept>
|
|
#include <string>
|
|
#include <type_traits>
|
|
#include <utility>
|
|
#include <vector>
|
|
#include <ostream>
|
|
|
|
// This file provides mainly two classes, `preserve_comments` and `discard_comments`.
|
|
// Those two are a container that have the same interface as `std::vector<std::string>`
|
|
// but bahaves in the opposite way. `preserve_comments` is just the same as
|
|
// `std::vector<std::string>` 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<std::string>;
|
|
|
|
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<std::string>& c): comments(c){}
|
|
explicit preserve_comments(std::vector<std::string>&& c)
|
|
: comments(std::move(c))
|
|
{}
|
|
preserve_comments& operator=(const std::vector<std::string>& c)
|
|
{
|
|
comments = c;
|
|
return *this;
|
|
}
|
|
preserve_comments& operator=(std::vector<std::string>&& 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<std::string> x): comments(x) {}
|
|
template<typename InputIterator>
|
|
preserve_comments(InputIterator first, InputIterator last)
|
|
: comments(first, last)
|
|
{}
|
|
|
|
template<typename InputIterator>
|
|
void assign(InputIterator first, InputIterator last) {comments.assign(first, last);}
|
|
void assign(std::initializer_list<std::string> 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<typename InputIterator>
|
|
void insert(iterator p, InputIterator first, InputIterator last)
|
|
{
|
|
return comments.insert(p, first, last);
|
|
}
|
|
void insert(iterator p, std::initializer_list<std::string> ini)
|
|
{
|
|
return comments.insert(p, ini);
|
|
}
|
|
|
|
template<typename ... Ts>
|
|
iterator emplace(iterator p, Ts&& ... args)
|
|
{
|
|
return comments.emplace(p, std::forward<Ts>(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<typename InputIterator>
|
|
iterator insert(const_iterator p, InputIterator first, InputIterator last)
|
|
{
|
|
return comments.insert(p, first, last);
|
|
}
|
|
iterator insert(const_iterator p, std::initializer_list<std::string> ini)
|
|
{
|
|
return comments.insert(p, ini);
|
|
}
|
|
|
|
template<typename ... Ts>
|
|
iterator emplace(const_iterator p, Ts&& ... args)
|
|
{
|
|
return comments.emplace(p, std::forward<Ts>(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<typename ... Ts>
|
|
void emplace_back(Ts&& ... args) {comments.emplace_back(std::forward<Ts>(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<std::string>&);
|
|
friend void swap(std::vector<std::string>&, 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<std::string>& rhs);
|
|
void swap(std::vector<std::string>& 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<typename T, bool is_const>
|
|
struct empty_iterator
|
|
{
|
|
using value_type = T;
|
|
using reference_type = typename std::conditional<is_const, T const&, T&>::type;
|
|
using pointer_type = typename std::conditional<is_const, T const*, T*>::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<typename T, bool C>
|
|
bool operator==(const empty_iterator<T, C>&, const empty_iterator<T, C>&) noexcept {return true;}
|
|
template<typename T, bool C>
|
|
bool operator!=(const empty_iterator<T, C>&, const empty_iterator<T, C>&) noexcept {return false;}
|
|
template<typename T, bool C>
|
|
bool operator< (const empty_iterator<T, C>&, const empty_iterator<T, C>&) noexcept {return false;}
|
|
template<typename T, bool C>
|
|
bool operator<=(const empty_iterator<T, C>&, const empty_iterator<T, C>&) noexcept {return true;}
|
|
template<typename T, bool C>
|
|
bool operator> (const empty_iterator<T, C>&, const empty_iterator<T, C>&) noexcept {return false;}
|
|
template<typename T, bool C>
|
|
bool operator>=(const empty_iterator<T, C>&, const empty_iterator<T, C>&) noexcept {return true;}
|
|
|
|
template<typename T, bool C>
|
|
typename empty_iterator<T, C>::difference_type
|
|
operator-(const empty_iterator<T, C>&, const empty_iterator<T, C>&) noexcept {return 0;}
|
|
|
|
template<typename T, bool C>
|
|
empty_iterator<T, C>
|
|
operator+(typename empty_iterator<T, C>::difference_type, const empty_iterator<T, C>& rhs) noexcept {return rhs;}
|
|
template<typename T, bool C>
|
|
empty_iterator<T, C>
|
|
operator+(const empty_iterator<T, C>& lhs, typename empty_iterator<T, C>::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<std::string, false>;
|
|
using const_iterator = detail::empty_iterator<std::string, true>;
|
|
using reverse_iterator = detail::empty_iterator<std::string, false>;
|
|
using const_reverse_iterator = detail::empty_iterator<std::string, true>;
|
|
|
|
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<std::string>&) noexcept {}
|
|
explicit discard_comments(std::vector<std::string>&&) noexcept {}
|
|
discard_comments& operator=(const std::vector<std::string>&) noexcept {return *this;}
|
|
discard_comments& operator=(std::vector<std::string>&&) 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<std::string>) noexcept {}
|
|
template<typename InputIterator>
|
|
discard_comments(InputIterator, InputIterator) noexcept {}
|
|
|
|
template<typename InputIterator>
|
|
void assign(InputIterator, InputIterator) noexcept {}
|
|
void assign(std::initializer_list<std::string>) 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<typename InputIterator>
|
|
iterator insert(const_iterator, InputIterator, InputIterator) {return iterator{};}
|
|
iterator insert(const_iterator, std::initializer_list<std::string>) {return iterator{};}
|
|
|
|
template<typename ... Ts>
|
|
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<typename ... Ts>
|
|
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<std::string>& rhs)
|
|
{
|
|
lhs.comments.swap(rhs);
|
|
return;
|
|
}
|
|
TOML11_INLINE void swap(std::vector<std::string>& 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 <iosfwd>
|
|
|
|
#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 <ostream>
|
|
|
|
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 <ostream>
|
|
#include <sstream>
|
|
|
|
#include <cstdint>
|
|
|
|
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 <algorithm>
|
|
#include <stdexcept>
|
|
#include <utility>
|
|
#include <vector>
|
|
|
|
namespace toml
|
|
{
|
|
|
|
namespace detail
|
|
{
|
|
template<typename Cmp>
|
|
struct ordered_map_ebo_container
|
|
{
|
|
Cmp cmp_; // empty base optimization for empty Cmp type
|
|
};
|
|
} // detail
|
|
|
|
template<typename Key, typename Val, typename Cmp = std::equal_to<Key>,
|
|
typename Allocator = std::allocator<std::pair<Key, Val>>>
|
|
class ordered_map : detail::ordered_map_ebo_container<Cmp>
|
|
{
|
|
public:
|
|
using key_type = Key;
|
|
using mapped_type = Val;
|
|
using value_type = std::pair<Key, Val>;
|
|
|
|
using key_compare = Cmp;
|
|
using allocator_type = Allocator;
|
|
|
|
using container_type = std::vector<value_type, Allocator>;
|
|
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<Cmp>;
|
|
|
|
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<typename InputIterator>
|
|
ordered_map(InputIterator first, InputIterator last, const Cmp& cmp = Cmp(), const Allocator& alloc = Allocator())
|
|
: ebo_base{cmp}, container_(first, last, alloc)
|
|
{}
|
|
template<typename InputIterator>
|
|
ordered_map(InputIterator first, InputIterator last, const Allocator& alloc)
|
|
: container_(first, last, alloc)
|
|
{}
|
|
|
|
ordered_map(std::initializer_list<value_type> v, const Cmp& cmp = Cmp(), const Allocator& alloc = Allocator())
|
|
: ebo_base{cmp}, container_(std::move(v), alloc)
|
|
{}
|
|
ordered_map(std::initializer_list<value_type> v, const Allocator& alloc)
|
|
: container_(std::move(v), alloc)
|
|
{}
|
|
ordered_map& operator=(std::initializer_list<value_type> 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<typename K, typename V, typename C, typename A>
|
|
bool operator==(const ordered_map<K,V,C,A>& lhs, const ordered_map<K,V,C,A>& rhs)
|
|
{
|
|
return lhs.size() == rhs.size() && std::equal(lhs.begin(), lhs.end(), rhs.begin());
|
|
}
|
|
template<typename K, typename V, typename C, typename A>
|
|
bool operator!=(const ordered_map<K,V,C,A>& lhs, const ordered_map<K,V,C,A>& rhs)
|
|
{
|
|
return !(lhs == rhs);
|
|
}
|
|
template<typename K, typename V, typename C, typename A>
|
|
bool operator<(const ordered_map<K,V,C,A>& lhs, const ordered_map<K,V,C,A>& rhs)
|
|
{
|
|
return std::lexicographical_compare(lhs.begin(), lhs.end(), rhs.begin(), rhs.end());
|
|
}
|
|
template<typename K, typename V, typename C, typename A>
|
|
bool operator>(const ordered_map<K,V,C,A>& lhs, const ordered_map<K,V,C,A>& rhs)
|
|
{
|
|
return rhs < lhs;
|
|
}
|
|
template<typename K, typename V, typename C, typename A>
|
|
bool operator<=(const ordered_map<K,V,C,A>& lhs, const ordered_map<K,V,C,A>& rhs)
|
|
{
|
|
return !(lhs > rhs);
|
|
}
|
|
template<typename K, typename V, typename C, typename A>
|
|
bool operator>=(const ordered_map<K,V,C,A>& lhs, const ordered_map<K,V,C,A>& rhs)
|
|
{
|
|
return !(lhs < rhs);
|
|
}
|
|
|
|
template<typename K, typename V, typename C, typename A>
|
|
void swap(ordered_map<K,V,C,A>& lhs, ordered_map<K,V,C,A>& rhs)
|
|
{
|
|
lhs.swap(rhs);
|
|
return;
|
|
}
|
|
|
|
|
|
} // toml
|
|
#endif // TOML11_ORDERED_MAP_HPP
|
|
#ifndef TOML11_INTO_HPP
|
|
#define TOML11_INTO_HPP
|
|
|
|
namespace toml
|
|
{
|
|
|
|
template<typename T>
|
|
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<typename T>
|
|
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 <array>
|
|
#include <chrono>
|
|
#include <forward_list>
|
|
#include <string>
|
|
#include <tuple>
|
|
#include <type_traits>
|
|
#include <utility>
|
|
|
|
#if defined(TOML11_HAS_STRING_VIEW)
|
|
#include <string_view>
|
|
#endif
|
|
|
|
namespace toml
|
|
{
|
|
template<typename TypeConcig>
|
|
class basic_value;
|
|
|
|
namespace detail
|
|
{
|
|
// ---------------------------------------------------------------------------
|
|
// check whether type T is a kind of container/map class
|
|
|
|
struct has_iterator_impl
|
|
{
|
|
template<typename T> static std::true_type check(typename T::iterator*);
|
|
template<typename T> static std::false_type check(...);
|
|
};
|
|
struct has_value_type_impl
|
|
{
|
|
template<typename T> static std::true_type check(typename T::value_type*);
|
|
template<typename T> static std::false_type check(...);
|
|
};
|
|
struct has_key_type_impl
|
|
{
|
|
template<typename T> static std::true_type check(typename T::key_type*);
|
|
template<typename T> static std::false_type check(...);
|
|
};
|
|
struct has_mapped_type_impl
|
|
{
|
|
template<typename T> static std::true_type check(typename T::mapped_type*);
|
|
template<typename T> static std::false_type check(...);
|
|
};
|
|
struct has_reserve_method_impl
|
|
{
|
|
template<typename T> static std::false_type check(...);
|
|
template<typename T> static std::true_type check(
|
|
decltype(std::declval<T>().reserve(std::declval<std::size_t>()))*);
|
|
};
|
|
struct has_push_back_method_impl
|
|
{
|
|
template<typename T> static std::false_type check(...);
|
|
template<typename T> static std::true_type check(
|
|
decltype(std::declval<T>().push_back(std::declval<typename T::value_type>()))*);
|
|
};
|
|
struct is_comparable_impl
|
|
{
|
|
template<typename T> static std::false_type check(...);
|
|
template<typename T> static std::true_type check(
|
|
decltype(std::declval<T>() < std::declval<T>())*);
|
|
};
|
|
|
|
struct has_from_toml_method_impl
|
|
{
|
|
template<typename T, typename TC>
|
|
static std::true_type check(
|
|
decltype(std::declval<T>().from_toml(std::declval<::toml::basic_value<TC>>()))*);
|
|
|
|
template<typename T, typename TC>
|
|
static std::false_type check(...);
|
|
};
|
|
struct has_into_toml_method_impl
|
|
{
|
|
template<typename T>
|
|
static std::true_type check(decltype(std::declval<T>().into_toml())*);
|
|
template<typename T>
|
|
static std::false_type check(...);
|
|
};
|
|
|
|
struct has_template_into_toml_method_impl
|
|
{
|
|
template<typename T, typename TypeConfig>
|
|
static std::true_type check(decltype(std::declval<T>().template into_toml<TypeConfig>())*);
|
|
template<typename T, typename TypeConfig>
|
|
static std::false_type check(...);
|
|
};
|
|
|
|
struct has_specialized_from_impl
|
|
{
|
|
template<typename T>
|
|
static std::false_type check(...);
|
|
template<typename T, std::size_t S = sizeof(::toml::from<T>)>
|
|
static std::true_type check(::toml::from<T>*);
|
|
};
|
|
struct has_specialized_into_impl
|
|
{
|
|
template<typename T>
|
|
static std::false_type check(...);
|
|
template<typename T, std::size_t S = sizeof(::toml::into<T>)>
|
|
static std::true_type check(::toml::into<T>*);
|
|
};
|
|
|
|
|
|
/// 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<true, decltype(__VA_ARGS__)>::type
|
|
#endif
|
|
|
|
template<typename T>
|
|
struct has_iterator: decltype(has_iterator_impl::check<T>(nullptr)){};
|
|
template<typename T>
|
|
struct has_value_type: decltype(has_value_type_impl::check<T>(nullptr)){};
|
|
template<typename T>
|
|
struct has_key_type: decltype(has_key_type_impl::check<T>(nullptr)){};
|
|
template<typename T>
|
|
struct has_mapped_type: decltype(has_mapped_type_impl::check<T>(nullptr)){};
|
|
template<typename T>
|
|
struct has_reserve_method: decltype(has_reserve_method_impl::check<T>(nullptr)){};
|
|
template<typename T>
|
|
struct has_push_back_method: decltype(has_push_back_method_impl::check<T>(nullptr)){};
|
|
template<typename T>
|
|
struct is_comparable: decltype(is_comparable_impl::check<T>(nullptr)){};
|
|
|
|
template<typename T, typename TC>
|
|
struct has_from_toml_method: decltype(has_from_toml_method_impl::check<T, TC>(nullptr)){};
|
|
|
|
template<typename T>
|
|
struct has_into_toml_method: decltype(has_into_toml_method_impl::check<T>(nullptr)){};
|
|
|
|
template<typename T, typename TypeConfig>
|
|
struct has_template_into_toml_method: decltype(has_template_into_toml_method_impl::check<T, TypeConfig>(nullptr)){};
|
|
|
|
template<typename T>
|
|
struct has_specialized_from: decltype(has_specialized_from_impl::check<T>(nullptr)){};
|
|
template<typename T>
|
|
struct has_specialized_into: decltype(has_specialized_into_impl::check<T>(nullptr)){};
|
|
|
|
#ifdef __INTEL_COMPILER
|
|
#undef decltype
|
|
#endif
|
|
|
|
// ---------------------------------------------------------------------------
|
|
// type checkers
|
|
|
|
template<typename T> struct is_std_pair_impl : std::false_type{};
|
|
template<typename T1, typename T2>
|
|
struct is_std_pair_impl<std::pair<T1, T2>> : std::true_type{};
|
|
template<typename T>
|
|
using is_std_pair = is_std_pair_impl<cxx::remove_cvref_t<T>>;
|
|
|
|
template<typename T> struct is_std_tuple_impl : std::false_type{};
|
|
template<typename ... Ts>
|
|
struct is_std_tuple_impl<std::tuple<Ts...>> : std::true_type{};
|
|
template<typename T>
|
|
using is_std_tuple = is_std_tuple_impl<cxx::remove_cvref_t<T>>;
|
|
|
|
template<typename T> struct is_std_array_impl : std::false_type{};
|
|
template<typename T, std::size_t N>
|
|
struct is_std_array_impl<std::array<T, N>> : std::true_type{};
|
|
template<typename T>
|
|
using is_std_array = is_std_array_impl<cxx::remove_cvref_t<T>>;
|
|
|
|
template<typename T> struct is_std_forward_list_impl : std::false_type{};
|
|
template<typename T>
|
|
struct is_std_forward_list_impl<std::forward_list<T>> : std::true_type{};
|
|
template<typename T>
|
|
using is_std_forward_list = is_std_forward_list_impl<cxx::remove_cvref_t<T>>;
|
|
|
|
template<typename T> struct is_std_basic_string_impl : std::false_type{};
|
|
template<typename C, typename T, typename A>
|
|
struct is_std_basic_string_impl<std::basic_string<C, T, A>> : std::true_type{};
|
|
template<typename T>
|
|
using is_std_basic_string = is_std_basic_string_impl<cxx::remove_cvref_t<T>>;
|
|
|
|
template<typename T> struct is_1byte_std_basic_string_impl : std::false_type{};
|
|
template<typename C, typename T, typename A>
|
|
struct is_1byte_std_basic_string_impl<std::basic_string<C, T, A>>
|
|
: std::integral_constant<bool, sizeof(C) == sizeof(char)> {};
|
|
template<typename T>
|
|
using is_1byte_std_basic_string = is_std_basic_string_impl<cxx::remove_cvref_t<T>>;
|
|
|
|
#if defined(TOML11_HAS_STRING_VIEW)
|
|
template<typename T> struct is_std_basic_string_view_impl : std::false_type{};
|
|
template<typename C, typename T>
|
|
struct is_std_basic_string_view_impl<std::basic_string_view<C, T>> : std::true_type{};
|
|
template<typename T>
|
|
using is_std_basic_string_view = is_std_basic_string_view_impl<cxx::remove_cvref_t<T>>;
|
|
|
|
template<typename V, typename S>
|
|
struct is_string_view_of : std::false_type {};
|
|
template<typename C, typename T>
|
|
struct is_string_view_of<std::basic_string_view<C, T>, std::basic_string<C, T>> : std::true_type {};
|
|
#endif
|
|
|
|
template<typename T> struct is_chrono_duration_impl: std::false_type{};
|
|
template<typename Rep, typename Period>
|
|
struct is_chrono_duration_impl<std::chrono::duration<Rep, Period>>: std::true_type{};
|
|
template<typename T>
|
|
using is_chrono_duration = is_chrono_duration_impl<cxx::remove_cvref_t<T>>;
|
|
|
|
template<typename T>
|
|
struct is_map_impl : cxx::conjunction< // map satisfies all the following conditions
|
|
has_iterator<T>, // has T::iterator
|
|
has_value_type<T>, // has T::value_type
|
|
has_key_type<T>, // has T::key_type
|
|
has_mapped_type<T> // has T::mapped_type
|
|
>{};
|
|
template<typename T>
|
|
using is_map = is_map_impl<cxx::remove_cvref_t<T>>;
|
|
|
|
template<typename T>
|
|
struct is_container_impl : cxx::conjunction<
|
|
cxx::negation<is_map<T>>, // not a map
|
|
cxx::negation<std::is_same<T, std::string>>, // not a std::string
|
|
#ifdef TOML11_HAS_STRING_VIEW
|
|
cxx::negation<std::is_same<T, std::string_view>>, // not a std::string_view
|
|
#endif
|
|
has_iterator<T>, // has T::iterator
|
|
has_value_type<T> // has T::value_type
|
|
>{};
|
|
template<typename T>
|
|
using is_container = is_container_impl<cxx::remove_cvref_t<T>>;
|
|
|
|
template<typename T>
|
|
struct is_basic_value_impl: std::false_type{};
|
|
template<typename TC>
|
|
struct is_basic_value_impl<::toml::basic_value<TC>>: std::true_type{};
|
|
template<typename T>
|
|
using is_basic_value = is_basic_value_impl<cxx::remove_cvref_t<T>>;
|
|
|
|
}// detail
|
|
}//toml
|
|
#endif // TOML11_TRAITS_HPP
|
|
#ifndef TOML11_EXCEPTION_HPP
|
|
#define TOML11_EXCEPTION_HPP
|
|
|
|
#include <exception>
|
|
|
|
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 <ostream>
|
|
#include <string>
|
|
#include <type_traits>
|
|
#include <utility>
|
|
|
|
#include <cassert>
|
|
|
|
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<typename T>
|
|
struct success
|
|
{
|
|
static_assert( ! std::is_same<T, void>::value, "");
|
|
|
|
using value_type = T;
|
|
|
|
explicit success(value_type v)
|
|
noexcept(std::is_nothrow_move_constructible<value_type>::value)
|
|
: value(std::move(v))
|
|
{}
|
|
|
|
template<typename U, cxx::enable_if_t<
|
|
std::is_convertible<cxx::remove_cvref_t<U>, T>::value,
|
|
std::nullptr_t> = nullptr>
|
|
explicit success(U&& v): value(std::forward<U>(v)) {}
|
|
|
|
template<typename U>
|
|
explicit success(success<U> 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<typename T>
|
|
struct success<std::reference_wrapper<T>>
|
|
{
|
|
static_assert( ! std::is_same<T, void>::value, "");
|
|
|
|
using value_type = T;
|
|
|
|
explicit success(std::reference_wrapper<value_type> 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_type> value;
|
|
};
|
|
|
|
template<typename T>
|
|
success<typename std::decay<T>::type> ok(T&& v)
|
|
{
|
|
return success<typename std::decay<T>::type>(std::forward<T>(v));
|
|
}
|
|
template<std::size_t N>
|
|
success<std::string> ok(const char (&literal)[N])
|
|
{
|
|
return success<std::string>(std::string(literal));
|
|
}
|
|
|
|
// -----------------------------------------------------------------------------
|
|
|
|
template<typename T>
|
|
struct failure
|
|
{
|
|
using value_type = T;
|
|
|
|
explicit failure(value_type v)
|
|
noexcept(std::is_nothrow_move_constructible<value_type>::value)
|
|
: value(std::move(v))
|
|
{}
|
|
|
|
template<typename U, cxx::enable_if_t<
|
|
std::is_convertible<cxx::remove_cvref_t<U>, T>::value,
|
|
std::nullptr_t> = nullptr>
|
|
explicit failure(U&& v): value(std::forward<U>(v)) {}
|
|
|
|
template<typename U>
|
|
explicit failure(failure<U> 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<typename T>
|
|
struct failure<std::reference_wrapper<T>>
|
|
{
|
|
using value_type = T;
|
|
|
|
explicit failure(std::reference_wrapper<value_type> 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_type> value;
|
|
};
|
|
|
|
template<typename T>
|
|
failure<typename std::decay<T>::type> err(T&& v)
|
|
{
|
|
return failure<typename std::decay<T>::type>(std::forward<T>(v));
|
|
}
|
|
|
|
template<std::size_t N>
|
|
failure<std::string> err(const char (&literal)[N])
|
|
{
|
|
return failure<std::string>(std::string(literal));
|
|
}
|
|
|
|
/* ============================================================================
|
|
* _ _
|
|
* _ _ ___ ____ _| | |_
|
|
* | '_/ -_|_-< || | | _|
|
|
* |_| \___/__/\_,_|_|\__|
|
|
*/
|
|
|
|
template<typename T, typename E>
|
|
struct result
|
|
{
|
|
using success_type = success<T>;
|
|
using failure_type = failure<E>;
|
|
using value_type = typename success_type::value_type;
|
|
using error_type = typename failure_type::value_type;
|
|
|
|
result(success_type s): is_ok_(true), succ_(std::move(s)) {}
|
|
result(failure_type f): is_ok_(false), fail_(std::move(f)) {}
|
|
|
|
template<typename U, cxx::enable_if_t<cxx::conjunction<
|
|
cxx::negation<std::is_same<cxx::remove_cvref_t<U>, value_type>>,
|
|
std::is_convertible<cxx::remove_cvref_t<U>, value_type>
|
|
>::value, std::nullptr_t> = nullptr>
|
|
result(success<U> s): is_ok_(true), succ_(std::move(s.value)) {}
|
|
|
|
template<typename U, cxx::enable_if_t<cxx::conjunction<
|
|
cxx::negation<std::is_same<cxx::remove_cvref_t<U>, error_type>>,
|
|
std::is_convertible<cxx::remove_cvref_t<U>, error_type>
|
|
>::value, std::nullptr_t> = nullptr>
|
|
result(failure<U> 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<typename U>
|
|
result& operator=(success<U> 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<typename U>
|
|
result& operator=(failure<U> 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<typename U, typename F, cxx::enable_if_t<cxx::conjunction<
|
|
cxx::negation<std::is_same<cxx::remove_cvref_t<U>, value_type>>,
|
|
cxx::negation<std::is_same<cxx::remove_cvref_t<F>, error_type>>,
|
|
std::is_convertible<cxx::remove_cvref_t<U>, value_type>,
|
|
std::is_convertible<cxx::remove_cvref_t<F>, error_type>
|
|
>::value, std::nullptr_t> = nullptr>
|
|
result(result<U, F> 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<typename U, typename F, cxx::enable_if_t<cxx::conjunction<
|
|
cxx::negation<std::is_same<cxx::remove_cvref_t<U>, value_type>>,
|
|
cxx::negation<std::is_same<cxx::remove_cvref_t<F>, error_type>>,
|
|
std::is_convertible<cxx::remove_cvref_t<U>, value_type>,
|
|
std::is_convertible<cxx::remove_cvref_t<F>, error_type>
|
|
>::value, std::nullptr_t> = nullptr>
|
|
result& operator=(result<U, F> 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<detail::none_t> ok() noexcept
|
|
{
|
|
return success<detail::none_t>(detail::none_t{});
|
|
}
|
|
inline failure<detail::none_t> err() noexcept
|
|
{
|
|
return failure<detail::none_t>(detail::none_t{});
|
|
}
|
|
|
|
} // toml
|
|
#endif // TOML11_RESULT_HPP
|
|
#ifndef TOML11_UTILITY_HPP
|
|
#define TOML11_UTILITY_HPP
|
|
|
|
|
|
#include <array>
|
|
#include <sstream>
|
|
|
|
#include <cassert>
|
|
#include <cctype>
|
|
#include <cstring>
|
|
|
|
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<char>(c));
|
|
}
|
|
else
|
|
{
|
|
std::array<char, 5> buf;
|
|
buf.fill('\0');
|
|
const auto r = std::snprintf(buf.data(), buf.size(), "0x%02x", c & 0xFF);
|
|
assert(r == static_cast<int>(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<typename Container>
|
|
void try_reserve_impl(Container& container, std::size_t N, std::true_type)
|
|
{
|
|
container.reserve(N);
|
|
return;
|
|
}
|
|
template<typename Container>
|
|
void try_reserve_impl(Container&, std::size_t, std::false_type) noexcept
|
|
{
|
|
return;
|
|
}
|
|
|
|
template<typename Container>
|
|
void try_reserve(Container& container, std::size_t N)
|
|
{
|
|
try_reserve_impl(container, N, has_reserve_method<Container>{});
|
|
return;
|
|
}
|
|
|
|
// ---------------------------------------------------------------------------
|
|
|
|
template<typename T>
|
|
result<T, none_t> 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<typename Iterator>
|
|
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<typename Char, typename Traits, typename Alloc,
|
|
typename Char2, typename Traits2, typename Alloc2>
|
|
struct string_conv_impl
|
|
{
|
|
static_assert(sizeof(Char) == sizeof(char), "");
|
|
static_assert(sizeof(Char2) == sizeof(char), "");
|
|
|
|
static std::basic_string<Char, Traits, Alloc> invoke(std::basic_string<Char2, Traits2, Alloc2> s)
|
|
{
|
|
std::basic_string<Char, Traits, Alloc> retval;
|
|
std::transform(s.begin(), s.end(), std::back_inserter(retval),
|
|
[](const Char2 c) {return static_cast<Char>(c);});
|
|
return retval;
|
|
}
|
|
template<std::size_t N>
|
|
static std::basic_string<Char, Traits, Alloc> invoke(const Char2 (&s)[N])
|
|
{
|
|
std::basic_string<Char, Traits, Alloc> 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<Char>(c);});
|
|
return retval;
|
|
}
|
|
};
|
|
|
|
template<typename Char, typename Traits, typename Alloc>
|
|
struct string_conv_impl<Char, Traits, Alloc, Char, Traits, Alloc>
|
|
{
|
|
static_assert(sizeof(Char) == sizeof(char), "");
|
|
|
|
static std::basic_string<Char, Traits, Alloc> invoke(std::basic_string<Char, Traits, Alloc> s)
|
|
{
|
|
return s;
|
|
}
|
|
template<std::size_t N>
|
|
static std::basic_string<Char, Traits, Alloc> invoke(const Char (&s)[N])
|
|
{
|
|
return std::basic_string<Char, Traits, Alloc>(s);
|
|
}
|
|
};
|
|
|
|
template<typename S, typename Char2, typename Traits2, typename Alloc2>
|
|
cxx::enable_if_t<is_std_basic_string<S>::value, S>
|
|
string_conv(std::basic_string<Char2, Traits2, Alloc2> s)
|
|
{
|
|
using C = typename S::value_type;
|
|
using T = typename S::traits_type;
|
|
using A = typename S::allocator_type;
|
|
return string_conv_impl<C, T, A, Char2, Traits2, Alloc2>::invoke(std::move(s));
|
|
}
|
|
template<typename S, std::size_t N>
|
|
cxx::enable_if_t<is_std_basic_string<S>::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<C2>;
|
|
using A2 = std::allocator<C2>;
|
|
|
|
return string_conv_impl<C, T, A, C2, T2, A2>::template invoke<N>(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 <memory>
|
|
#include <string>
|
|
#include <vector>
|
|
|
|
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<char_type>;
|
|
using difference_type = typename container_type::difference_type; // to suppress sign-conversion warning
|
|
using source_ptr = std::shared_ptr<const container_type>;
|
|
|
|
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<bool>(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<typename F>
|
|
result<location, none_t>
|
|
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<typename F>
|
|
result<location, none_t>
|
|
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<location, none_t> find(const location& first, const location& last,
|
|
const location::char_type val);
|
|
result<location, none_t> 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<difference_type>(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<difference_type>(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::size_t>(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::size_t>(std::count(
|
|
std::next(iter, static_cast<difference_type>(this->location_)),
|
|
std::next(iter, static_cast<difference_type>(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::size_t>(std::count(
|
|
std::next(iter, static_cast<difference_type>(this->location_ - n)),
|
|
std::next(iter, static_cast<difference_type>(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<location::char_type>(c);
|
|
});
|
|
return location(std::make_shared<const location::container_type>(
|
|
std::move(cont)), "internal temporary");
|
|
}
|
|
|
|
TOML11_INLINE result<location, none_t>
|
|
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<location, none_t>
|
|
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 <string>
|
|
#include <vector>
|
|
|
|
#include <cassert>
|
|
|
|
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<bool>(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<std::string> 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 <algorithm>
|
|
#include <iterator>
|
|
#include <string>
|
|
#include <sstream>
|
|
#include <vector>
|
|
#include <cassert>
|
|
|
|
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<difference_type>(this->first_ + i));
|
|
return *iter;
|
|
}
|
|
|
|
TOML11_INLINE region::const_iterator region::begin() const noexcept
|
|
{
|
|
return std::next(this->source_->cbegin(),
|
|
static_cast<difference_type>(this->first_));
|
|
}
|
|
TOML11_INLINE region::const_iterator region::end() const noexcept
|
|
{
|
|
return std::next(this->source_->cbegin(),
|
|
static_cast<difference_type>(this->last_));
|
|
}
|
|
TOML11_INLINE region::const_iterator region::cbegin() const noexcept
|
|
{
|
|
return std::next(this->source_->cbegin(),
|
|
static_cast<difference_type>(this->first_));
|
|
}
|
|
TOML11_INLINE region::const_iterator region::cend() const noexcept
|
|
{
|
|
return std::next(this->source_->cbegin(),
|
|
static_cast<difference_type>(this->last_));
|
|
}
|
|
|
|
TOML11_INLINE std::string region::as_string() const
|
|
{
|
|
if(this->is_ok())
|
|
{
|
|
const auto begin = std::next(this->source_->cbegin(), static_cast<difference_type>(this->first_));
|
|
const auto end = std::next(this->source_->cbegin(), static_cast<difference_type>(this->last_ ));
|
|
return ::toml::detail::make_string(begin, end);
|
|
}
|
|
else
|
|
{
|
|
return std::string("");
|
|
}
|
|
}
|
|
|
|
TOML11_INLINE std::vector<std::string> region::as_lines() const
|
|
{
|
|
assert(this->is_ok());
|
|
if(this->length_ == 0)
|
|
{
|
|
return std::vector<std::string>{""};
|
|
}
|
|
|
|
// 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<difference_type>(this->first_);
|
|
const auto end_idx = static_cast<difference_type>(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::string>{""};
|
|
}
|
|
|
|
std::istringstream iss(reg_lines);
|
|
|
|
std::vector<std::string> 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 <sstream>
|
|
#include <string>
|
|
#include <vector>
|
|
|
|
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<std::string> 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<std::string> 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<typename ... Ts>
|
|
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<typename ... Ts>
|
|
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<typename ... Ts>
|
|
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 <iomanip>
|
|
#include <sstream>
|
|
#include <string>
|
|
#include <vector>
|
|
|
|
#include <cctype>
|
|
|
|
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<int>(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<std::pair<source_location, std::string>> 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<std::pair<source_location, std::string>> 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<std::pair<source_location, std::string>> locations_;
|
|
std::string suffix_; // hint or something like that
|
|
};
|
|
|
|
// forward decl
|
|
template<typename TypeConfig>
|
|
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<typename TC, typename ... Ts>
|
|
error_info make_error_info_rec(error_info e,
|
|
const basic_value<TC>& v, std::string msg, Ts&& ... tail);
|
|
|
|
template<typename ... Ts>
|
|
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<Ts>(tail)...);
|
|
}
|
|
|
|
} // detail
|
|
|
|
template<typename ... Ts>
|
|
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<Ts>(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<typename ... Ts>
|
|
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<Ts>(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 <sstream>
|
|
|
|
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 <string_view>
|
|
#endif
|
|
|
|
#include <cassert>
|
|
|
|
namespace toml
|
|
{
|
|
template<typename TypeConfig>
|
|
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<typename TC>
|
|
error_info make_type_error(const basic_value<TC>&, const std::string&, const value_t);
|
|
|
|
template<typename TC>
|
|
error_info make_not_found_error(const basic_value<TC>&, const std::string&, const typename basic_value<TC>::key_type&);
|
|
|
|
template<typename TC>
|
|
void change_region_of_value(basic_value<TC>&, const basic_value<TC>&);
|
|
|
|
template<typename TC, value_t V>
|
|
struct getter;
|
|
} // detail
|
|
|
|
template<typename TypeConfig>
|
|
class basic_value
|
|
{
|
|
public:
|
|
|
|
using config_type = TypeConfig;
|
|
using key_type = typename config_type::string_type;
|
|
using value_type = basic_value<config_type>;
|
|
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<value_type>;
|
|
using table_type = typename config_type::template table_type<key_type, value_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<std::string> 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<typename TI>
|
|
basic_value(basic_value<TI> 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<array_type>(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<table_type>(std::move(tmp)),
|
|
other.table_.format
|
|
));
|
|
break;
|
|
}
|
|
default: break;
|
|
}
|
|
}
|
|
|
|
template<typename TI>
|
|
basic_value(basic_value<TI> other, std::vector<std::string> 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<array_type>(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<table_type>(std::move(tmp)),
|
|
other.table_.format
|
|
));
|
|
break;
|
|
}
|
|
default: break;
|
|
}
|
|
}
|
|
template<typename TI>
|
|
basic_value& operator=(basic_value<TI> 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<array_type>(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<table_type>(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<std::string>{}, region_type{})
|
|
{}
|
|
basic_value(boolean_type x, boolean_format_info fmt)
|
|
: basic_value(x, fmt, std::vector<std::string>{}, region_type{})
|
|
{}
|
|
basic_value(boolean_type x, std::vector<std::string> com)
|
|
: basic_value(x, boolean_format_info{}, std::move(com), region_type{})
|
|
{}
|
|
basic_value(boolean_type x, boolean_format_info fmt, std::vector<std::string> com)
|
|
: basic_value(x, fmt, std::move(com), region_type{})
|
|
{}
|
|
basic_value(boolean_type x, boolean_format_info fmt,
|
|
std::vector<std::string> 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<std::string>{}, region_type{})
|
|
{}
|
|
basic_value(integer_type x, integer_format_info fmt)
|
|
: basic_value(std::move(x), std::move(fmt), std::vector<std::string>{}, region_type{})
|
|
{}
|
|
basic_value(integer_type x, std::vector<std::string> 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<std::string> 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<std::string> 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<typename T>
|
|
using enable_if_integer_like_t = cxx::enable_if_t<cxx::conjunction<
|
|
cxx::negation<std::is_same<cxx::remove_cvref_t<T>, boolean_type>>,
|
|
cxx::negation<std::is_same<cxx::remove_cvref_t<T>, integer_type>>,
|
|
std::is_integral<cxx::remove_cvref_t<T>>
|
|
>::value, std::nullptr_t>;
|
|
|
|
public:
|
|
|
|
template<typename T, enable_if_integer_like_t<T> = nullptr>
|
|
basic_value(T x)
|
|
: basic_value(std::move(x), integer_format_info{}, std::vector<std::string>{}, region_type{})
|
|
{}
|
|
template<typename T, enable_if_integer_like_t<T> = nullptr>
|
|
basic_value(T x, integer_format_info fmt)
|
|
: basic_value(std::move(x), std::move(fmt), std::vector<std::string>{}, region_type{})
|
|
{}
|
|
template<typename T, enable_if_integer_like_t<T> = nullptr>
|
|
basic_value(T x, std::vector<std::string> com)
|
|
: basic_value(std::move(x), integer_format_info{}, std::move(com), region_type{})
|
|
{}
|
|
template<typename T, enable_if_integer_like_t<T> = nullptr>
|
|
basic_value(T x, integer_format_info fmt, std::vector<std::string> com)
|
|
: basic_value(std::move(x), std::move(fmt), std::move(com), region_type{})
|
|
{}
|
|
template<typename T, enable_if_integer_like_t<T> = nullptr>
|
|
basic_value(T x, integer_format_info fmt, std::vector<std::string> 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<typename T, enable_if_integer_like_t<T> = 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<std::string>{}, region_type{})
|
|
{}
|
|
basic_value(floating_type x, floating_format_info fmt)
|
|
: basic_value(std::move(x), std::move(fmt), std::vector<std::string>{}, region_type{})
|
|
{}
|
|
basic_value(floating_type x, std::vector<std::string> 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<std::string> 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<std::string> 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<typename T>
|
|
using enable_if_floating_like_t = cxx::enable_if_t<cxx::conjunction<
|
|
cxx::negation<std::is_same<cxx::remove_cvref_t<T>, floating_type>>,
|
|
std::is_floating_point<cxx::remove_cvref_t<T>>
|
|
>::value, std::nullptr_t>;
|
|
|
|
public:
|
|
|
|
template<typename T, enable_if_floating_like_t<T> = nullptr>
|
|
basic_value(T x)
|
|
: basic_value(x, floating_format_info{}, std::vector<std::string>{}, region_type{})
|
|
{}
|
|
|
|
template<typename T, enable_if_floating_like_t<T> = nullptr>
|
|
basic_value(T x, floating_format_info fmt)
|
|
: basic_value(x, std::move(fmt), std::vector<std::string>{}, region_type{})
|
|
{}
|
|
|
|
template<typename T, enable_if_floating_like_t<T> = nullptr>
|
|
basic_value(T x, std::vector<std::string> com)
|
|
: basic_value(x, floating_format_info{}, std::move(com), region_type{})
|
|
{}
|
|
|
|
template<typename T, enable_if_floating_like_t<T> = nullptr>
|
|
basic_value(T x, floating_format_info fmt, std::vector<std::string> com)
|
|
: basic_value(x, std::move(fmt), std::move(com), region_type{})
|
|
{}
|
|
|
|
template<typename T, enable_if_floating_like_t<T> = nullptr>
|
|
basic_value(T x, floating_format_info fmt, std::vector<std::string> com, region_type reg)
|
|
: type_(value_t::floating), floating_(floating_storage(x, std::move(fmt))),
|
|
region_(std::move(reg)), comments_(std::move(com))
|
|
{}
|
|
|
|
template<typename T, enable_if_floating_like_t<T> = 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<std::string>{}, region_type{})
|
|
{}
|
|
basic_value(string_type x, string_format_info fmt)
|
|
: basic_value(std::move(x), std::move(fmt), std::vector<std::string>{}, region_type{})
|
|
{}
|
|
basic_value(string_type x, std::vector<std::string> 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<std::string> 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<std::string> 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<std::string>{}, region_type{})
|
|
{}
|
|
basic_value(const typename string_type::value_type* x, string_format_info fmt)
|
|
: basic_value(x, std::move(fmt), std::vector<std::string>{}, region_type{})
|
|
{}
|
|
basic_value(const typename string_type::value_type* x, std::vector<std::string> 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<std::string> 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<std::string> 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<std::string>{}, region_type{})
|
|
{}
|
|
basic_value(string_view_type x, string_format_info fmt)
|
|
: basic_value(x, std::move(fmt), std::vector<std::string>{}, region_type{})
|
|
{}
|
|
basic_value(string_view_type x, std::vector<std::string> com)
|
|
: basic_value(x, string_format_info{}, std::move(com), region_type{})
|
|
{}
|
|
basic_value(string_view_type x, string_format_info fmt, std::vector<std::string> com)
|
|
: basic_value(x, std::move(fmt), std::move(com), region_type{})
|
|
{}
|
|
basic_value(string_view_type x, string_format_info fmt,
|
|
std::vector<std::string> 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<typename T, cxx::enable_if_t<cxx::conjunction<
|
|
cxx::negation<std::is_same<cxx::remove_cvref_t<T>, string_type>>,
|
|
detail::is_1byte_std_basic_string<T>
|
|
>::value, std::nullptr_t> = nullptr>
|
|
basic_value(const T& x)
|
|
: basic_value(x, string_format_info{}, std::vector<std::string>{}, region_type{})
|
|
{}
|
|
template<typename T, cxx::enable_if_t<cxx::conjunction<
|
|
cxx::negation<std::is_same<cxx::remove_cvref_t<T>, string_type>>,
|
|
detail::is_1byte_std_basic_string<T>
|
|
>::value, std::nullptr_t> = nullptr>
|
|
basic_value(const T& x, string_format_info fmt)
|
|
: basic_value(x, std::move(fmt), std::vector<std::string>{}, region_type{})
|
|
{}
|
|
template<typename T, cxx::enable_if_t<cxx::conjunction<
|
|
cxx::negation<std::is_same<cxx::remove_cvref_t<T>, string_type>>,
|
|
detail::is_1byte_std_basic_string<T>
|
|
>::value, std::nullptr_t> = nullptr>
|
|
basic_value(const T& x, std::vector<std::string> com)
|
|
: basic_value(x, string_format_info{}, std::move(com), region_type{})
|
|
{}
|
|
template<typename T, cxx::enable_if_t<cxx::conjunction<
|
|
cxx::negation<std::is_same<cxx::remove_cvref_t<T>, string_type>>,
|
|
detail::is_1byte_std_basic_string<T>
|
|
>::value, std::nullptr_t> = nullptr>
|
|
basic_value(const T& x, string_format_info fmt, std::vector<std::string> com)
|
|
: basic_value(x, std::move(fmt), std::move(com), region_type{})
|
|
{}
|
|
template<typename T, cxx::enable_if_t<cxx::conjunction<
|
|
cxx::negation<std::is_same<cxx::remove_cvref_t<T>, string_type>>,
|
|
detail::is_1byte_std_basic_string<T>
|
|
>::value, std::nullptr_t> = nullptr>
|
|
basic_value(const T& x, string_format_info fmt,
|
|
std::vector<std::string> com, region_type reg)
|
|
: type_(value_t::string),
|
|
string_(string_storage(detail::string_conv<string_type>(x), std::move(fmt))),
|
|
region_(std::move(reg)), comments_(std::move(com))
|
|
{}
|
|
template<typename T, cxx::enable_if_t<cxx::conjunction<
|
|
cxx::negation<std::is_same<cxx::remove_cvref_t<T>, string_type>>,
|
|
detail::is_1byte_std_basic_string<T>
|
|
>::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<string_type>(x), std::move(fmt)));
|
|
return *this;
|
|
}
|
|
|
|
// }}}
|
|
|
|
// constructor (local_date) =========================================== {{{
|
|
|
|
basic_value(local_date_type x)
|
|
: basic_value(x, local_date_format_info{}, std::vector<std::string>{}, region_type{})
|
|
{}
|
|
basic_value(local_date_type x, local_date_format_info fmt)
|
|
: basic_value(x, fmt, std::vector<std::string>{}, region_type{})
|
|
{}
|
|
basic_value(local_date_type x, std::vector<std::string> 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<std::string> com)
|
|
: basic_value(x, fmt, std::move(com), region_type{})
|
|
{}
|
|
basic_value(local_date_type x, local_date_format_info fmt,
|
|
std::vector<std::string> 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<std::string>{}, region_type{})
|
|
{}
|
|
basic_value(local_time_type x, local_time_format_info fmt)
|
|
: basic_value(x, fmt, std::vector<std::string>{}, region_type{})
|
|
{}
|
|
basic_value(local_time_type x, std::vector<std::string> 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<std::string> com)
|
|
: basic_value(x, fmt, std::move(com), region_type{})
|
|
{}
|
|
basic_value(local_time_type x, local_time_format_info fmt,
|
|
std::vector<std::string> 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<typename Rep, typename Period>
|
|
basic_value(const std::chrono::duration<Rep, Period>& x)
|
|
: basic_value(local_time_type(x), local_time_format_info{}, std::vector<std::string>{}, region_type{})
|
|
{}
|
|
template<typename Rep, typename Period>
|
|
basic_value(const std::chrono::duration<Rep, Period>& x, local_time_format_info fmt)
|
|
: basic_value(local_time_type(x), std::move(fmt), std::vector<std::string>{}, region_type{})
|
|
{}
|
|
template<typename Rep, typename Period>
|
|
basic_value(const std::chrono::duration<Rep, Period>& x, std::vector<std::string> com)
|
|
: basic_value(local_time_type(x), local_time_format_info{}, std::move(com), region_type{})
|
|
{}
|
|
template<typename Rep, typename Period>
|
|
basic_value(const std::chrono::duration<Rep, Period>& x, local_time_format_info fmt, std::vector<std::string> com)
|
|
: basic_value(local_time_type(x), std::move(fmt), std::move(com), region_type{})
|
|
{}
|
|
template<typename Rep, typename Period>
|
|
basic_value(const std::chrono::duration<Rep, Period>& x,
|
|
local_time_format_info fmt,
|
|
std::vector<std::string> com, region_type reg)
|
|
: basic_value(local_time_type(x), std::move(fmt), std::move(com), std::move(reg))
|
|
{}
|
|
template<typename Rep, typename Period>
|
|
basic_value& operator=(const std::chrono::duration<Rep, Period>& 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<std::string>{}, region_type{})
|
|
{}
|
|
basic_value(local_datetime_type x, local_datetime_format_info fmt)
|
|
: basic_value(x, fmt, std::vector<std::string>{}, region_type{})
|
|
{}
|
|
basic_value(local_datetime_type x, std::vector<std::string> 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<std::string> com)
|
|
: basic_value(x, fmt, std::move(com), region_type{})
|
|
{}
|
|
basic_value(local_datetime_type x, local_datetime_format_info fmt,
|
|
std::vector<std::string> 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<std::string>{}, region_type{})
|
|
{}
|
|
basic_value(offset_datetime_type x, offset_datetime_format_info fmt)
|
|
: basic_value(x, fmt, std::vector<std::string>{}, region_type{})
|
|
{}
|
|
basic_value(offset_datetime_type x, std::vector<std::string> 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<std::string> com)
|
|
: basic_value(x, fmt, std::move(com), region_type{})
|
|
{}
|
|
basic_value(offset_datetime_type x, offset_datetime_format_info fmt,
|
|
std::vector<std::string> 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<std::string>{}, 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<std::string>{}, region_type{})
|
|
{}
|
|
basic_value(std::chrono::system_clock::time_point x, std::vector<std::string> 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<std::string> 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<std::string> 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<std::string>{}, region_type{})
|
|
{}
|
|
basic_value(array_type x, array_format_info fmt)
|
|
: basic_value(std::move(x), std::move(fmt), std::vector<std::string>{}, region_type{})
|
|
{}
|
|
basic_value(array_type x, std::vector<std::string> 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<std::string> com)
|
|
: basic_value(std::move(x), fmt, std::move(com), region_type{})
|
|
{}
|
|
basic_value(array_type x, array_format_info fmt,
|
|
std::vector<std::string> com, region_type reg)
|
|
: type_(value_t::array), array_(array_storage(
|
|
detail::storage<array_type>(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<array_type>(std::move(x)), std::move(fmt)));
|
|
return *this;
|
|
}
|
|
|
|
private:
|
|
|
|
template<typename T>
|
|
using enable_if_array_like_t = cxx::enable_if_t<cxx::conjunction<
|
|
detail::is_container<T>,
|
|
cxx::negation<std::is_same<T, array_type>>,
|
|
cxx::negation<detail::is_std_basic_string<T>>,
|
|
#if defined(TOML11_HAS_STRING_VIEW)
|
|
cxx::negation<detail::is_std_basic_string_view<T>>,
|
|
#endif
|
|
cxx::negation<detail::has_from_toml_method<T, config_type>>,
|
|
cxx::negation<detail::has_specialized_from<T>>
|
|
>::value, std::nullptr_t>;
|
|
|
|
public:
|
|
|
|
template<typename T, enable_if_array_like_t<T> = nullptr>
|
|
basic_value(T x)
|
|
: basic_value(std::move(x), array_format_info{}, std::vector<std::string>{}, region_type{})
|
|
{}
|
|
template<typename T, enable_if_array_like_t<T> = nullptr>
|
|
basic_value(T x, array_format_info fmt)
|
|
: basic_value(std::move(x), std::move(fmt), std::vector<std::string>{}, region_type{})
|
|
{}
|
|
template<typename T, enable_if_array_like_t<T> = nullptr>
|
|
basic_value(T x, std::vector<std::string> com)
|
|
: basic_value(std::move(x), array_format_info{}, std::move(com), region_type{})
|
|
{}
|
|
template<typename T, enable_if_array_like_t<T> = nullptr>
|
|
basic_value(T x, array_format_info fmt, std::vector<std::string> com)
|
|
: basic_value(std::move(x), fmt, std::move(com), region_type{})
|
|
{}
|
|
template<typename T, enable_if_array_like_t<T> = nullptr>
|
|
basic_value(T x, array_format_info fmt,
|
|
std::vector<std::string> com, region_type reg)
|
|
: type_(value_t::array), array_(array_storage(
|
|
detail::storage<array_type>(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<typename T, enable_if_array_like_t<T> = 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<array_type>(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<std::string>{}, region_type{})
|
|
{}
|
|
basic_value(table_type x, table_format_info fmt)
|
|
: basic_value(std::move(x), std::move(fmt), std::vector<std::string>{}, region_type{})
|
|
{}
|
|
basic_value(table_type x, std::vector<std::string> 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<std::string> com)
|
|
: basic_value(std::move(x), fmt, std::move(com), region_type{})
|
|
{}
|
|
basic_value(table_type x, table_format_info fmt,
|
|
std::vector<std::string> com, region_type reg)
|
|
: type_(value_t::table), table_(table_storage(
|
|
detail::storage<table_type>(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<table_type>(std::move(x)), std::move(fmt)));
|
|
return *this;
|
|
}
|
|
|
|
// table-like
|
|
|
|
private:
|
|
|
|
template<typename T>
|
|
using enable_if_table_like_t = cxx::enable_if_t<cxx::conjunction<
|
|
cxx::negation<std::is_same<T, table_type>>,
|
|
detail::is_map<T>,
|
|
cxx::negation<detail::has_from_toml_method<T, config_type>>,
|
|
cxx::negation<detail::has_specialized_from<T>>
|
|
>::value, std::nullptr_t>;
|
|
|
|
public:
|
|
|
|
template<typename T, enable_if_table_like_t<T> = nullptr>
|
|
basic_value(T x)
|
|
: basic_value(std::move(x), table_format_info{}, std::vector<std::string>{}, region_type{})
|
|
{}
|
|
template<typename T, enable_if_table_like_t<T> = nullptr>
|
|
basic_value(T x, table_format_info fmt)
|
|
: basic_value(std::move(x), std::move(fmt), std::vector<std::string>{}, region_type{})
|
|
{}
|
|
template<typename T, enable_if_table_like_t<T> = nullptr>
|
|
basic_value(T x, std::vector<std::string> com)
|
|
: basic_value(std::move(x), table_format_info{}, std::move(com), region_type{})
|
|
{}
|
|
template<typename T, enable_if_table_like_t<T> = nullptr>
|
|
basic_value(T x, table_format_info fmt, std::vector<std::string> com)
|
|
: basic_value(std::move(x), fmt, std::move(com), region_type{})
|
|
{}
|
|
template<typename T, enable_if_table_like_t<T> = nullptr>
|
|
basic_value(T x, table_format_info fmt,
|
|
std::vector<std::string> com, region_type reg)
|
|
: type_(value_t::table), table_(table_storage(
|
|
detail::storage<table_type>(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<typename T, enable_if_table_like_t<T> = 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<table_type>(std::move(t)), std::move(fmt)));
|
|
return *this;
|
|
}
|
|
|
|
// }}}
|
|
|
|
// constructor (user_defined) ========================================= {{{
|
|
|
|
template<typename T, cxx::enable_if_t<
|
|
detail::has_specialized_into<T>::value, std::nullptr_t> = nullptr>
|
|
basic_value(const T& ud)
|
|
: basic_value(
|
|
into<cxx::remove_cvref_t<T>>::template into_toml<config_type>(ud))
|
|
{}
|
|
template<typename T, cxx::enable_if_t<
|
|
detail::has_specialized_into<T>::value, std::nullptr_t> = nullptr>
|
|
basic_value(const T& ud, std::vector<std::string> com)
|
|
: basic_value(
|
|
into<cxx::remove_cvref_t<T>>::template into_toml<config_type>(ud),
|
|
std::move(com))
|
|
{}
|
|
template<typename T, cxx::enable_if_t<
|
|
detail::has_specialized_into<T>::value, std::nullptr_t> = nullptr>
|
|
basic_value& operator=(const T& ud)
|
|
{
|
|
*this = into<cxx::remove_cvref_t<T>>::template into_toml<config_type>(ud);
|
|
return *this;
|
|
}
|
|
|
|
template<typename T, cxx::enable_if_t<cxx::conjunction<
|
|
detail::has_into_toml_method<T>,
|
|
cxx::negation<detail::has_specialized_into<T>>
|
|
>::value, std::nullptr_t> = nullptr>
|
|
basic_value(const T& ud): basic_value(ud.into_toml()) {}
|
|
|
|
template<typename T, cxx::enable_if_t<cxx::conjunction<
|
|
detail::has_into_toml_method<T>,
|
|
cxx::negation<detail::has_specialized_into<T>>
|
|
>::value, std::nullptr_t> = nullptr>
|
|
basic_value(const T& ud, std::vector<std::string> com)
|
|
: basic_value(ud.into_toml(), std::move(com))
|
|
{}
|
|
template<typename T, cxx::enable_if_t<cxx::conjunction<
|
|
detail::has_into_toml_method<T>,
|
|
cxx::negation<detail::has_specialized_into<T>>
|
|
>::value, std::nullptr_t> = nullptr>
|
|
basic_value& operator=(const T& ud)
|
|
{
|
|
*this = ud.into_toml();
|
|
return *this;
|
|
}
|
|
|
|
template<typename T, cxx::enable_if_t<cxx::conjunction<
|
|
detail::has_template_into_toml_method<T, TypeConfig>,
|
|
cxx::negation<detail::has_specialized_into<T>>
|
|
>::value, std::nullptr_t> = nullptr>
|
|
basic_value(const T& ud): basic_value(ud.template into_toml<TypeConfig>()) {}
|
|
|
|
template<typename T, cxx::enable_if_t<cxx::conjunction<
|
|
detail::has_template_into_toml_method<T, TypeConfig>,
|
|
cxx::negation<detail::has_specialized_into<T>>
|
|
>::value, std::nullptr_t> = nullptr>
|
|
basic_value(const T& ud, std::vector<std::string> com)
|
|
: basic_value(ud.template into_toml<TypeConfig>(), std::move(com))
|
|
{}
|
|
template<typename T, cxx::enable_if_t<cxx::conjunction<
|
|
detail::has_template_into_toml_method<T, TypeConfig>,
|
|
cxx::negation<detail::has_specialized_into<T>>
|
|
>::value, std::nullptr_t> = nullptr>
|
|
basic_value& operator=(const T& ud)
|
|
{
|
|
*this = ud.template into_toml<TypeConfig>();
|
|
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<typename T, cxx::enable_if_t<
|
|
detail::is_exact_toml_type<cxx::remove_cvref_t<T>, value_type>::value,
|
|
std::nullptr_t> = nullptr>
|
|
bool is() const noexcept
|
|
{
|
|
return detail::type_to_enum<T, value_type>::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<value_t T>
|
|
detail::enum_to_type_t<T, basic_value<config_type>> const&
|
|
as(const std::nothrow_t&) const noexcept
|
|
{
|
|
return detail::getter<config_type, T>::get_nothrow(*this);
|
|
}
|
|
template<value_t T>
|
|
detail::enum_to_type_t<T, basic_value<config_type>>&
|
|
as(const std::nothrow_t&) noexcept
|
|
{
|
|
return detail::getter<config_type, T>::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<value_t T>
|
|
detail::enum_to_type_t<T, basic_value<config_type>> const& as() const
|
|
{
|
|
return detail::getter<config_type, T>::get(*this);
|
|
}
|
|
template<value_t T>
|
|
detail::enum_to_type_t<T, basic_value<config_type>>& as()
|
|
{
|
|
return detail::getter<config_type, T>::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<value_t T>
|
|
detail::enum_to_fmt_type_t<T> const&
|
|
as_fmt(const std::nothrow_t&) const noexcept
|
|
{
|
|
return detail::getter<config_type, T>::get_fmt_nothrow(*this);
|
|
}
|
|
template<value_t T>
|
|
detail::enum_to_fmt_type_t<T>&
|
|
as_fmt(const std::nothrow_t&) noexcept
|
|
{
|
|
return detail::getter<config_type, T>::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<value_t T>
|
|
detail::enum_to_fmt_type_t<T> const& as_fmt() const
|
|
{
|
|
return detail::getter<config_type, T>::get_fmt(*this);
|
|
}
|
|
template<value_t T>
|
|
detail::enum_to_fmt_type_t<T>& as_fmt()
|
|
{
|
|
return detail::getter<config_type, T>::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<typename ... Ts>
|
|
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<Ts>(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<typename T, typename U>
|
|
static void assigner(T& dst, U&& v)
|
|
{
|
|
const auto tmp = ::new(std::addressof(dst)) T(std::forward<U>(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<typename TC>
|
|
friend void detail::change_region_of_value(basic_value<TC>&, const basic_value<TC>&);
|
|
|
|
template<typename TC>
|
|
friend class basic_value;
|
|
|
|
// }}}
|
|
|
|
private:
|
|
|
|
using boolean_storage = detail::value_with_format<boolean_type, boolean_format_info >;
|
|
using integer_storage = detail::value_with_format<integer_type, integer_format_info >;
|
|
using floating_storage = detail::value_with_format<floating_type, floating_format_info >;
|
|
using string_storage = detail::value_with_format<string_type, string_format_info >;
|
|
using offset_datetime_storage = detail::value_with_format<offset_datetime_type, offset_datetime_format_info>;
|
|
using local_datetime_storage = detail::value_with_format<local_datetime_type, local_datetime_format_info >;
|
|
using local_date_storage = detail::value_with_format<local_date_type, local_date_format_info >;
|
|
using local_time_storage = detail::value_with_format<local_time_type, local_time_format_info >;
|
|
using array_storage = detail::value_with_format<detail::storage<array_type>, array_format_info >;
|
|
using table_storage = detail::value_with_format<detail::storage<table_type>, 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<typename TC>
|
|
bool operator==(const basic_value<TC>& lhs, const basic_value<TC>& 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<typename TC>
|
|
bool operator!=(const basic_value<TC>& lhs, const basic_value<TC>& rhs)
|
|
{
|
|
return !(lhs == rhs);
|
|
}
|
|
|
|
template<typename TC>
|
|
cxx::enable_if_t<cxx::conjunction<
|
|
detail::is_comparable<typename basic_value<TC>::array_type>,
|
|
detail::is_comparable<typename basic_value<TC>::table_type>
|
|
>::value, bool>
|
|
operator<(const basic_value<TC>& lhs, const basic_value<TC>& 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<typename TC>
|
|
cxx::enable_if_t<cxx::conjunction<
|
|
detail::is_comparable<typename basic_value<TC>::array_type>,
|
|
detail::is_comparable<typename basic_value<TC>::table_type>
|
|
>::value, bool>
|
|
operator<=(const basic_value<TC>& lhs, const basic_value<TC>& rhs)
|
|
{
|
|
return (lhs < rhs) || (lhs == rhs);
|
|
}
|
|
template<typename TC>
|
|
cxx::enable_if_t<cxx::conjunction<
|
|
detail::is_comparable<typename basic_value<TC>::array_type>,
|
|
detail::is_comparable<typename basic_value<TC>::table_type>
|
|
>::value, bool>
|
|
operator>(const basic_value<TC>& lhs, const basic_value<TC>& rhs)
|
|
{
|
|
return !(lhs <= rhs);
|
|
}
|
|
template<typename TC>
|
|
cxx::enable_if_t<cxx::conjunction<
|
|
detail::is_comparable<typename basic_value<TC>::array_type>,
|
|
detail::is_comparable<typename basic_value<TC>::table_type>
|
|
>::value, bool>
|
|
operator>=(const basic_value<TC>& lhs, const basic_value<TC>& rhs)
|
|
{
|
|
return !(lhs < rhs);
|
|
}
|
|
|
|
// error_info helper
|
|
namespace detail
|
|
{
|
|
template<typename TC, typename ... Ts>
|
|
error_info make_error_info_rec(error_info e,
|
|
const basic_value<TC>& v, std::string msg, Ts&& ... tail)
|
|
{
|
|
return make_error_info_rec(std::move(e), v.location(), std::move(msg), std::forward<Ts>(tail)...);
|
|
}
|
|
} // detail
|
|
|
|
template<typename TC, typename ... Ts>
|
|
error_info make_error_info(
|
|
std::string title, const basic_value<TC>& v, std::string msg, Ts&& ... tail)
|
|
{
|
|
return make_error_info(std::move(title),
|
|
v.location(), std::move(msg), std::forward<Ts>(tail)...);
|
|
}
|
|
template<typename TC, typename ... Ts>
|
|
std::string format_error(std::string title,
|
|
const basic_value<TC>& v, std::string msg, Ts&& ... tail)
|
|
{
|
|
return format_error(std::move(title),
|
|
v.location(), std::move(msg), std::forward<Ts>(tail)...);
|
|
}
|
|
|
|
namespace detail
|
|
{
|
|
|
|
template<typename TC>
|
|
error_info make_type_error(const basic_value<TC>& 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<typename TC>
|
|
error_info make_not_found_error(const basic_value<TC>& v, const std::string& fname, const typename basic_value<TC>::key_type& key)
|
|
{
|
|
const auto loc = v.location();
|
|
const std::string title = fname + ": key \"" + string_conv<std::string>(key) + "\" not found";
|
|
|
|
std::vector<std::pair<source_location, std::string>> 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<typename TC> \
|
|
struct getter<TC, value_t::ty> \
|
|
{ \
|
|
using value_type = basic_value<TC>; \
|
|
using result_type = enum_to_type_t<value_t::ty, value_type>; \
|
|
using format_type = enum_to_fmt_type_t<value_t::ty>; \
|
|
\
|
|
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<typename TC>
|
|
void change_region_of_value(basic_value<TC>& dst, const basic_value<TC>& 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<typename Visitor, typename TC>
|
|
cxx::return_type_of_t<Visitor, const typename basic_value<TC>::boolean_type&>
|
|
visit(Visitor&& visitor, const basic_value<TC>& 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<typename Visitor, typename TC>
|
|
cxx::return_type_of_t<Visitor, typename basic_value<TC>::boolean_type&>
|
|
visit(Visitor&& visitor, basic_value<TC>& 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<typename Visitor, typename TC>
|
|
cxx::return_type_of_t<Visitor, typename basic_value<TC>::boolean_type&&>
|
|
visit(Visitor&& visitor, basic_value<TC>&& 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 <ostream>
|
|
#include <sstream>
|
|
#include <string>
|
|
#include <type_traits>
|
|
#include <unordered_map>
|
|
#include <vector>
|
|
|
|
#include <cstdint>
|
|
#include <cstdio>
|
|
|
|
namespace toml
|
|
{
|
|
|
|
// forward decl
|
|
template<typename TypeConfig>
|
|
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<typename T>
|
|
result<T, error_info>
|
|
read_dec_int(const std::string& str, const source_location src)
|
|
{
|
|
constexpr auto max_digits = std::numeric_limits<T>::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<typename T>
|
|
result<T, error_info>
|
|
read_hex_int(const std::string& str, const source_location src)
|
|
{
|
|
constexpr auto max_digits = std::numeric_limits<T>::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<typename T>
|
|
result<T, error_info>
|
|
read_oct_int(const std::string& str, const source_location src)
|
|
{
|
|
constexpr auto max_digits = std::numeric_limits<T>::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<typename T>
|
|
result<T, error_info>
|
|
read_bin_int(const std::string& str, const source_location src)
|
|
{
|
|
constexpr auto is_bounded = std::numeric_limits<T>::is_bounded;
|
|
constexpr auto max_digits = std::numeric_limits<T>::digits;
|
|
const auto max_value = (std::numeric_limits<T>::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<typename T>
|
|
result<T, error_info>
|
|
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<T>(str, src); }
|
|
case 8: { return read_oct_int<T>(str, src); }
|
|
case 16: { return read_hex_int<T>(str, src); }
|
|
default:
|
|
{
|
|
assert(base == 10);
|
|
return read_dec_int<T>(str, src);
|
|
}
|
|
}
|
|
}
|
|
|
|
inline result<float, error_info>
|
|
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<double, error_info>
|
|
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<typename T>
|
|
cxx::enable_if_t<cxx::conjunction<
|
|
cxx::negation<std::is_same<cxx::remove_cvref_t<T>, double>>,
|
|
cxx::negation<std::is_same<cxx::remove_cvref_t<T>, float>>
|
|
>::value, result<T, error_info>>
|
|
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<typename T>
|
|
result<T, error_info>
|
|
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<typename T>
|
|
result<T, error_info>
|
|
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<T>(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<typename T>
|
|
using array_type = std::vector<T>;
|
|
template<typename K, typename T>
|
|
using table_type = std::unordered_map<K, T>;
|
|
|
|
static result<integer_type, error_info>
|
|
parse_int(const std::string& str, const source_location src, const std::uint8_t base)
|
|
{
|
|
return read_int<integer_type>(str, src, base);
|
|
}
|
|
static result<floating_type, error_info>
|
|
parse_float(const std::string& str, const source_location src, const bool is_hex)
|
|
{
|
|
return read_float<floating_type>(str, src, is_hex);
|
|
}
|
|
};
|
|
|
|
using value = basic_value<type_config>;
|
|
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<typename T>
|
|
using array_type = std::vector<T>;
|
|
template<typename K, typename T>
|
|
using table_type = ordered_map<K, T>;
|
|
|
|
static result<integer_type, error_info>
|
|
parse_int(const std::string& str, const source_location src, const std::uint8_t base)
|
|
{
|
|
return read_int<integer_type>(str, src, base);
|
|
}
|
|
static result<floating_type, error_info>
|
|
parse_float(const std::string& str, const source_location src, const bool is_hex)
|
|
{
|
|
return read_float<floating_type>(str, src, is_hex);
|
|
}
|
|
};
|
|
|
|
using ordered_value = basic_value<ordered_type_config>;
|
|
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<typename T> static std::true_type check(typename T::comment_type*);
|
|
template<typename T> static std::false_type check(...);
|
|
};
|
|
template<typename T>
|
|
using has_comment_type = decltype(has_comment_type_impl::check<T>(nullptr));
|
|
|
|
struct has_integer_type_impl
|
|
{
|
|
template<typename T> static std::true_type check(typename T::integer_type*);
|
|
template<typename T> static std::false_type check(...);
|
|
};
|
|
template<typename T>
|
|
using has_integer_type = decltype(has_integer_type_impl::check<T>(nullptr));
|
|
|
|
struct has_floating_type_impl
|
|
{
|
|
template<typename T> static std::true_type check(typename T::floating_type*);
|
|
template<typename T> static std::false_type check(...);
|
|
};
|
|
template<typename T>
|
|
using has_floating_type = decltype(has_floating_type_impl::check<T>(nullptr));
|
|
|
|
struct has_string_type_impl
|
|
{
|
|
template<typename T> static std::true_type check(typename T::string_type*);
|
|
template<typename T> static std::false_type check(...);
|
|
};
|
|
template<typename T>
|
|
using has_string_type = decltype(has_string_type_impl::check<T>(nullptr));
|
|
|
|
struct has_array_type_impl
|
|
{
|
|
template<typename T> static std::true_type check(typename T::template array_type<int>*);
|
|
template<typename T> static std::false_type check(...);
|
|
};
|
|
template<typename T>
|
|
using has_array_type = decltype(has_array_type_impl::check<T>(nullptr));
|
|
|
|
struct has_table_type_impl
|
|
{
|
|
template<typename T> static std::true_type check(typename T::template table_type<int, int>*);
|
|
template<typename T> static std::false_type check(...);
|
|
};
|
|
template<typename T>
|
|
using has_table_type = decltype(has_table_type_impl::check<T>(nullptr));
|
|
|
|
struct has_parse_int_impl
|
|
{
|
|
template<typename T> static std::true_type check(decltype(std::declval<T>().parse_int(
|
|
std::declval<const std::string&>(),
|
|
std::declval<const source_location>(),
|
|
std::declval<const std::uint8_t>()
|
|
))*);
|
|
template<typename T> static std::false_type check(...);
|
|
};
|
|
template<typename T>
|
|
using has_parse_int = decltype(has_parse_int_impl::check<T>(nullptr));
|
|
|
|
struct has_parse_float_impl
|
|
{
|
|
template<typename T> static std::true_type check(decltype(std::declval<T>().parse_float(
|
|
std::declval<const std::string&>(),
|
|
std::declval<const source_location>(),
|
|
std::declval<const bool>()
|
|
))*);
|
|
template<typename T> static std::false_type check(...);
|
|
};
|
|
template<typename T>
|
|
using has_parse_float = decltype(has_parse_float_impl::check<T>(nullptr));
|
|
|
|
template<typename T>
|
|
using is_type_config = cxx::conjunction<
|
|
has_comment_type<T>,
|
|
has_integer_type<T>,
|
|
has_floating_type<T>,
|
|
has_string_type<T>,
|
|
has_array_type<T>,
|
|
has_table_type<T>,
|
|
has_parse_int<T>,
|
|
has_parse_float<T>
|
|
>;
|
|
|
|
} // namespace detail
|
|
} // namespace toml
|
|
|
|
#if defined(TOML11_COMPILE_SOURCES)
|
|
namespace toml
|
|
{
|
|
extern template class basic_value<type_config>;
|
|
extern template class basic_value<ordered_type_config>;
|
|
} // toml
|
|
#endif // TOML11_COMPILE_SOURCES
|
|
|
|
#endif // TOML11_TYPES_HPP
|
|
#ifndef TOML11_GET_HPP
|
|
#define TOML11_GET_HPP
|
|
|
|
#include <algorithm>
|
|
|
|
|
|
#if defined(TOML11_HAS_STRING_VIEW)
|
|
#include <string_view>
|
|
#endif // string_view
|
|
|
|
namespace toml
|
|
{
|
|
|
|
// ============================================================================
|
|
// T is toml::value; identity transformation.
|
|
|
|
template<typename T, typename TC>
|
|
cxx::enable_if_t<std::is_same<T, basic_value<TC>>::value, T>&
|
|
get(basic_value<TC>& v)
|
|
{
|
|
return v;
|
|
}
|
|
|
|
template<typename T, typename TC>
|
|
cxx::enable_if_t<std::is_same<T, basic_value<TC>>::value, T> const&
|
|
get(const basic_value<TC>& v)
|
|
{
|
|
return v;
|
|
}
|
|
|
|
template<typename T, typename TC>
|
|
cxx::enable_if_t<std::is_same<T, basic_value<TC>>::value, T>
|
|
get(basic_value<TC>&& v)
|
|
{
|
|
return basic_value<TC>(std::move(v));
|
|
}
|
|
|
|
// ============================================================================
|
|
// exact toml::* type
|
|
|
|
template<typename T, typename TC>
|
|
cxx::enable_if_t<detail::is_exact_toml_type<T, basic_value<TC>>::value, T> &
|
|
get(basic_value<TC>& v)
|
|
{
|
|
constexpr auto ty = detail::type_to_enum<T, basic_value<TC>>::value;
|
|
return detail::getter<TC, ty>::get(v);
|
|
}
|
|
|
|
template<typename T, typename TC>
|
|
cxx::enable_if_t<detail::is_exact_toml_type<T, basic_value<TC>>::value, T> const&
|
|
get(const basic_value<TC>& v)
|
|
{
|
|
constexpr auto ty = detail::type_to_enum<T, basic_value<TC>>::value;
|
|
return detail::getter<TC, ty>::get(v);
|
|
}
|
|
|
|
template<typename T, typename TC>
|
|
cxx::enable_if_t<detail::is_exact_toml_type<T, basic_value<TC>>::value, T>
|
|
get(basic_value<TC>&& v)
|
|
{
|
|
constexpr auto ty = detail::type_to_enum<T, basic_value<TC>>::value;
|
|
return detail::getter<TC, ty>::get(std::move(v));
|
|
}
|
|
|
|
// ============================================================================
|
|
// T is toml::basic_value<U>
|
|
|
|
template<typename T, typename TC>
|
|
cxx::enable_if_t<cxx::conjunction<
|
|
detail::is_basic_value<T>,
|
|
cxx::negation<std::is_same<T, basic_value<TC>>>
|
|
>::value, T>
|
|
get(basic_value<TC> v)
|
|
{
|
|
return T(std::move(v));
|
|
}
|
|
|
|
// ============================================================================
|
|
// integer convertible from toml::value::integer_type
|
|
|
|
template<typename T, typename TC>
|
|
cxx::enable_if_t<cxx::conjunction<
|
|
std::is_integral<T>,
|
|
cxx::negation<std::is_same<T, bool>>,
|
|
detail::is_not_toml_type<T, basic_value<TC>>,
|
|
cxx::negation<detail::has_from_toml_method<T, TC>>,
|
|
cxx::negation<detail::has_specialized_from<T>>
|
|
>::value, T>
|
|
get(const basic_value<TC>& v)
|
|
{
|
|
return static_cast<T>(v.as_integer());
|
|
}
|
|
|
|
// ============================================================================
|
|
// floating point convertible from toml::value::floating_type
|
|
|
|
template<typename T, typename TC>
|
|
cxx::enable_if_t<cxx::conjunction<
|
|
std::is_floating_point<T>,
|
|
detail::is_not_toml_type<T, basic_value<TC>>,
|
|
cxx::negation<detail::has_from_toml_method<T, TC>>,
|
|
cxx::negation<detail::has_specialized_from<T>>
|
|
>::value, T>
|
|
get(const basic_value<TC>& v)
|
|
{
|
|
return static_cast<T>(v.as_floating());
|
|
}
|
|
|
|
// ============================================================================
|
|
// std::string with different char/trait/allocator
|
|
|
|
template<typename T, typename TC>
|
|
cxx::enable_if_t<cxx::conjunction<
|
|
detail::is_not_toml_type<T, basic_value<TC>>,
|
|
detail::is_1byte_std_basic_string<T>
|
|
>::value, T>
|
|
get(const basic_value<TC>& v)
|
|
{
|
|
return detail::string_conv<cxx::remove_cvref_t<T>>(v.as_string());
|
|
}
|
|
|
|
// ============================================================================
|
|
// std::string_view
|
|
|
|
#if defined(TOML11_HAS_STRING_VIEW)
|
|
|
|
template<typename T, typename TC>
|
|
cxx::enable_if_t<detail::is_string_view_of<T, typename basic_value<TC>::string_type>::value, T>
|
|
get(const basic_value<TC>& v)
|
|
{
|
|
return T(v.as_string());
|
|
}
|
|
|
|
#endif // string_view
|
|
|
|
// ============================================================================
|
|
// std::chrono::duration from toml::local_time
|
|
|
|
template<typename T, typename TC>
|
|
cxx::enable_if_t<detail::is_chrono_duration<T>::value, T>
|
|
get(const basic_value<TC>& v)
|
|
{
|
|
return std::chrono::duration_cast<T>(
|
|
std::chrono::nanoseconds(v.as_local_time()));
|
|
}
|
|
|
|
// ============================================================================
|
|
// std::chrono::system_clock::time_point from toml::datetime variants
|
|
|
|
template<typename T, typename TC>
|
|
cxx::enable_if_t<
|
|
std::is_same<std::chrono::system_clock::time_point, T>::value, T>
|
|
get(const basic_value<TC>& 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<typename T, typename TC>
|
|
cxx::enable_if_t<cxx::conjunction<
|
|
detail::is_container<T>, // T is a container
|
|
detail::has_push_back_method<T>, // .push_back() works
|
|
detail::is_not_toml_type<T, basic_value<TC>>, // but not toml::array
|
|
cxx::negation<detail::is_std_basic_string<T>>, // but not std::basic_string<CharT>
|
|
#if defined(TOML11_HAS_STRING_VIEW)
|
|
cxx::negation<detail::is_std_basic_string_view<T>>, // but not std::basic_string_view<CharT>
|
|
#endif
|
|
cxx::negation<detail::has_from_toml_method<T, TC>>, // no T.from_toml()
|
|
cxx::negation<detail::has_specialized_from<T>>, // no toml::from<T>
|
|
cxx::negation<std::is_constructible<T, const basic_value<TC>&>>
|
|
>::value, T>
|
|
get(const basic_value<TC>&);
|
|
|
|
// std::array
|
|
template<typename T, typename TC>
|
|
cxx::enable_if_t<detail::is_std_array<T>::value, T>
|
|
get(const basic_value<TC>&);
|
|
|
|
// std::forward_list
|
|
template<typename T, typename TC>
|
|
cxx::enable_if_t<detail::is_std_forward_list<T>::value, T>
|
|
get(const basic_value<TC>&);
|
|
|
|
// std::pair<T1, T2>
|
|
template<typename T, typename TC>
|
|
cxx::enable_if_t<detail::is_std_pair<T>::value, T>
|
|
get(const basic_value<TC>&);
|
|
|
|
// std::tuple<T1, T2, ...>
|
|
template<typename T, typename TC>
|
|
cxx::enable_if_t<detail::is_std_tuple<T>::value, T>
|
|
get(const basic_value<TC>&);
|
|
|
|
// std::map<key, value> (key is convertible from toml::value::key_type)
|
|
template<typename T, typename TC>
|
|
cxx::enable_if_t<cxx::conjunction<
|
|
detail::is_map<T>, // T is map
|
|
detail::is_not_toml_type<T, basic_value<TC>>, // but not toml::table
|
|
std::is_convertible<typename basic_value<TC>::key_type,
|
|
typename T::key_type>, // keys are convertible
|
|
cxx::negation<detail::has_from_toml_method<T, TC>>, // no T.from_toml()
|
|
cxx::negation<detail::has_specialized_from<T>>, // no toml::from<T>
|
|
cxx::negation<std::is_constructible<T, const basic_value<TC>&>>
|
|
>::value, T>
|
|
get(const basic_value<TC>& v);
|
|
|
|
// std::map<key, value> (key is not convertible from toml::value::key_type, but
|
|
// is a std::basic_string)
|
|
template<typename T, typename TC>
|
|
cxx::enable_if_t<cxx::conjunction<
|
|
detail::is_map<T>, // T is map
|
|
detail::is_not_toml_type<T, basic_value<TC>>, // but not toml::table
|
|
cxx::negation<std::is_convertible<typename basic_value<TC>::key_type,
|
|
typename T::key_type>>, // keys are NOT convertible
|
|
detail::is_1byte_std_basic_string<typename T::key_type>, // is std::basic_string
|
|
cxx::negation<detail::has_from_toml_method<T, TC>>, // no T.from_toml()
|
|
cxx::negation<detail::has_specialized_from<T>>, // no toml::from<T>
|
|
cxx::negation<std::is_constructible<T, const basic_value<TC>&>>
|
|
>::value, T>
|
|
get(const basic_value<TC>& v);
|
|
|
|
// toml::from<T>::from_toml(v)
|
|
template<typename T, typename TC>
|
|
cxx::enable_if_t<detail::has_specialized_from<T>::value, T>
|
|
get(const basic_value<TC>&);
|
|
|
|
// has T.from_toml(v) but no from<T>
|
|
template<typename T, typename TC>
|
|
cxx::enable_if_t<cxx::conjunction<
|
|
detail::has_from_toml_method<T, TC>, // has T.from_toml()
|
|
cxx::negation<detail::has_specialized_from<T>>, // no toml::from<T>
|
|
std::is_default_constructible<T> // T{} works
|
|
>::value, T>
|
|
get(const basic_value<TC>&);
|
|
|
|
// T(const toml::value&) and T is not toml::basic_value,
|
|
// and it does not have `from<T>` nor `from_toml`.
|
|
template<typename T, typename TC>
|
|
cxx::enable_if_t<cxx::conjunction<
|
|
std::is_constructible<T, const basic_value<TC>&>, // has T(const basic_value&)
|
|
cxx::negation<detail::is_basic_value<T>>, // but not basic_value itself
|
|
cxx::negation<detail::has_from_toml_method<T, TC>>, // no .from_toml()
|
|
cxx::negation<detail::has_specialized_from<T>> // no toml::from<T>
|
|
>::value, T>
|
|
get(const basic_value<TC>&);
|
|
|
|
// ============================================================================
|
|
// array-like types; most likely STL container, like std::vector, etc.
|
|
|
|
template<typename T, typename TC>
|
|
cxx::enable_if_t<cxx::conjunction<
|
|
detail::is_container<T>, // T is a container
|
|
detail::has_push_back_method<T>, // .push_back() works
|
|
detail::is_not_toml_type<T, basic_value<TC>>, // but not toml::array
|
|
cxx::negation<detail::is_std_basic_string<T>>, // but not std::basic_string<CharT>
|
|
#if defined(TOML11_HAS_STRING_VIEW)
|
|
cxx::negation<detail::is_std_basic_string_view<T>>, // but not std::basic_string_view<CharT>
|
|
#endif
|
|
cxx::negation<detail::has_from_toml_method<T, TC>>, // no T.from_toml()
|
|
cxx::negation<detail::has_specialized_from<T>>, // no toml::from<T>
|
|
cxx::negation<std::is_constructible<T, const basic_value<TC>&>>
|
|
>::value, T>
|
|
get(const basic_value<TC>& 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<value_type>(elem));
|
|
}
|
|
return container;
|
|
}
|
|
|
|
// ============================================================================
|
|
// std::array
|
|
|
|
template<typename T, typename TC>
|
|
cxx::enable_if_t<detail::is_std_array<T>::value, T>
|
|
get(const basic_value<TC>& 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.size(); ++i)
|
|
{
|
|
container.at(i) = ::toml::get<value_type>(a.at(i));
|
|
}
|
|
return container;
|
|
}
|
|
|
|
// ============================================================================
|
|
// std::forward_list
|
|
|
|
template<typename T, typename TC>
|
|
cxx::enable_if_t<detail::is_std_forward_list<T>::value, T>
|
|
get(const basic_value<TC>& v)
|
|
{
|
|
using value_type = typename T::value_type;
|
|
|
|
T container;
|
|
for(const auto& elem : v.as_array())
|
|
{
|
|
container.push_front(get<value_type>(elem));
|
|
}
|
|
container.reverse();
|
|
return container;
|
|
}
|
|
|
|
// ============================================================================
|
|
// std::pair
|
|
|
|
template<typename T, typename TC>
|
|
cxx::enable_if_t<detail::is_std_pair<T>::value, T>
|
|
get(const basic_value<TC>& 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<first_type >(ar.at(0)),
|
|
::toml::get<second_type>(ar.at(1)));
|
|
}
|
|
|
|
// ============================================================================
|
|
// std::tuple.
|
|
|
|
namespace detail
|
|
{
|
|
template<typename T, typename Array, std::size_t ... I>
|
|
T get_tuple_impl(const Array& a, cxx::index_sequence<I...>)
|
|
{
|
|
return std::make_tuple(
|
|
::toml::get<typename std::tuple_element<I, T>::type>(a.at(I))...);
|
|
}
|
|
} // detail
|
|
|
|
template<typename T, typename TC>
|
|
cxx::enable_if_t<detail::is_std_tuple<T>::value, T>
|
|
get(const basic_value<TC>& v)
|
|
{
|
|
const auto& ar = v.as_array();
|
|
if(ar.size() != std::tuple_size<T>::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<T>::value) + " elements in toml array.",
|
|
loc, "here"));
|
|
}
|
|
return detail::get_tuple_impl<T>(ar,
|
|
cxx::make_index_sequence<std::tuple_size<T>::value>{});
|
|
}
|
|
|
|
// ============================================================================
|
|
// map-like types; most likely STL map, like std::map or std::unordered_map.
|
|
|
|
// key is convertible from toml::value::key_type
|
|
template<typename T, typename TC>
|
|
cxx::enable_if_t<cxx::conjunction<
|
|
detail::is_map<T>, // T is map
|
|
detail::is_not_toml_type<T, basic_value<TC>>, // but not toml::table
|
|
std::is_convertible<typename basic_value<TC>::key_type,
|
|
typename T::key_type>, // keys are convertible
|
|
cxx::negation<detail::has_from_toml_method<T, TC>>, // no T.from_toml()
|
|
cxx::negation<detail::has_specialized_from<T>>, // no toml::from<T>
|
|
cxx::negation<std::is_constructible<T, const basic_value<TC>&>>
|
|
>::value, T>
|
|
get(const basic_value<TC>& v)
|
|
{
|
|
using key_type = typename T::key_type;
|
|
using mapped_type = typename T::mapped_type;
|
|
static_assert(
|
|
std::is_convertible<typename basic_value<TC>::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<mapped_type>(kv.second));
|
|
}
|
|
return m;
|
|
}
|
|
|
|
// key is NOT convertible from toml::value::key_type but std::basic_string
|
|
template<typename T, typename TC>
|
|
cxx::enable_if_t<cxx::conjunction<
|
|
detail::is_map<T>, // T is map
|
|
detail::is_not_toml_type<T, basic_value<TC>>, // but not toml::table
|
|
cxx::negation<std::is_convertible<typename basic_value<TC>::key_type,
|
|
typename T::key_type>>, // keys are NOT convertible
|
|
detail::is_1byte_std_basic_string<typename T::key_type>, // is std::basic_string
|
|
cxx::negation<detail::has_from_toml_method<T, TC>>, // no T.from_toml()
|
|
cxx::negation<detail::has_specialized_from<T>>, // no toml::from<T>
|
|
cxx::negation<std::is_constructible<T, const basic_value<TC>&>>
|
|
>::value, T>
|
|
get(const basic_value<TC>& 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<key_type>(kv.first), get<mapped_type>(kv.second));
|
|
}
|
|
return m;
|
|
}
|
|
|
|
// ============================================================================
|
|
// user-defined, but convertible types.
|
|
|
|
// toml::from<T>
|
|
template<typename T, typename TC>
|
|
cxx::enable_if_t<detail::has_specialized_from<T>::value, T>
|
|
get(const basic_value<TC>& v)
|
|
{
|
|
return ::toml::from<T>::from_toml(v);
|
|
}
|
|
|
|
// has T.from_toml(v) but no from<T>
|
|
template<typename T, typename TC>
|
|
cxx::enable_if_t<cxx::conjunction<
|
|
detail::has_from_toml_method<T, TC>, // has T.from_toml()
|
|
cxx::negation<detail::has_specialized_from<T>>, // no toml::from<T>
|
|
std::is_default_constructible<T> // T{} works
|
|
>::value, T>
|
|
get(const basic_value<TC>& 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<T>` nor `from_toml`.
|
|
template<typename T, typename TC>
|
|
cxx::enable_if_t<cxx::conjunction<
|
|
std::is_constructible<T, const basic_value<TC>&>, // has T(const basic_value&)
|
|
cxx::negation<detail::is_basic_value<T>>, // but not basic_value itself
|
|
cxx::negation<detail::has_from_toml_method<T, TC>>, // no .from_toml()
|
|
cxx::negation<detail::has_specialized_from<T>> // no toml::from<T>
|
|
>::value, T>
|
|
get(const basic_value<TC>& v)
|
|
{
|
|
return T(v);
|
|
}
|
|
|
|
// ============================================================================
|
|
// get_or(value, fallback)
|
|
|
|
template<typename TC>
|
|
cxx::enable_if_t<detail::is_type_config<TC>::value, basic_value<TC>> const&
|
|
get_or(const basic_value<TC>& v, const basic_value<TC>&)
|
|
{
|
|
return v;
|
|
}
|
|
|
|
template<typename TC>
|
|
cxx::enable_if_t<detail::is_type_config<TC>::value, basic_value<TC>>&
|
|
get_or(basic_value<TC>& v, basic_value<TC>&)
|
|
{
|
|
return v;
|
|
}
|
|
|
|
template<typename TC>
|
|
cxx::enable_if_t<detail::is_type_config<TC>::value, basic_value<TC>>
|
|
get_or(basic_value<TC>&& v, basic_value<TC>&&)
|
|
{
|
|
return v;
|
|
}
|
|
|
|
// ----------------------------------------------------------------------------
|
|
// specialization for the exact toml types (return type becomes lvalue ref)
|
|
|
|
template<typename T, typename TC>
|
|
cxx::enable_if_t<
|
|
detail::is_exact_toml_type<T, basic_value<TC>>::value, T> const&
|
|
get_or(const basic_value<TC>& v, const T& opt) noexcept
|
|
{
|
|
try
|
|
{
|
|
return get<cxx::remove_cvref_t<T>>(v);
|
|
}
|
|
catch(...)
|
|
{
|
|
return opt;
|
|
}
|
|
}
|
|
template<typename T, typename TC>
|
|
cxx::enable_if_t<cxx::conjunction<
|
|
cxx::negation<std::is_const<T>>,
|
|
detail::is_exact_toml_type<T, basic_value<TC>>
|
|
>::value, T>&
|
|
get_or(basic_value<TC>& v, T& opt) noexcept
|
|
{
|
|
try
|
|
{
|
|
return get<cxx::remove_cvref_t<T>>(v);
|
|
}
|
|
catch(...)
|
|
{
|
|
return opt;
|
|
}
|
|
}
|
|
template<typename T, typename TC>
|
|
cxx::enable_if_t<detail::is_exact_toml_type<cxx::remove_cvref_t<T>,
|
|
basic_value<TC>>::value, cxx::remove_cvref_t<T>>
|
|
get_or(basic_value<TC>&& v, T&& opt) noexcept
|
|
{
|
|
try
|
|
{
|
|
return get<cxx::remove_cvref_t<T>>(std::move(v));
|
|
}
|
|
catch(...)
|
|
{
|
|
return cxx::remove_cvref_t<T>(std::forward<T>(opt));
|
|
}
|
|
}
|
|
|
|
// ----------------------------------------------------------------------------
|
|
// specialization for string literal
|
|
|
|
// template<std::size_t N, typename TC>
|
|
// typename basic_value<TC>::string_type
|
|
// get_or(const basic_value<TC>& v,
|
|
// const typename basic_value<TC>::string_type::value_type (&opt)[N])
|
|
// {
|
|
// try
|
|
// {
|
|
// return v.as_string();
|
|
// }
|
|
// catch(...)
|
|
// {
|
|
// return typename basic_value<TC>::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 TC>
|
|
typename basic_value<TC>::string_type
|
|
get_or(const basic_value<TC>& v,
|
|
const typename basic_value<TC>::string_type::value_type* opt)
|
|
{
|
|
try
|
|
{
|
|
return v.as_string();
|
|
}
|
|
catch(...)
|
|
{
|
|
return typename basic_value<TC>::string_type(opt);
|
|
}
|
|
}
|
|
|
|
// ----------------------------------------------------------------------------
|
|
// others (require type conversion and return type cannot be lvalue reference)
|
|
|
|
template<typename T, typename TC>
|
|
cxx::enable_if_t<cxx::conjunction<
|
|
cxx::negation<detail::is_basic_value<T>>,
|
|
cxx::negation<detail::is_exact_toml_type<T, basic_value<TC>>>,
|
|
cxx::negation<std::is_same<cxx::remove_cvref_t<T>, typename basic_value<TC>::string_type::value_type const*>>
|
|
>::value, cxx::remove_cvref_t<T>>
|
|
get_or(const basic_value<TC>& v, T&& opt)
|
|
{
|
|
try
|
|
{
|
|
return get<cxx::remove_cvref_t<T>>(v);
|
|
}
|
|
catch(...)
|
|
{
|
|
return cxx::remove_cvref_t<T>(std::forward<T>(opt));
|
|
}
|
|
}
|
|
|
|
} // toml
|
|
#endif // TOML11_GET_HPP
|
|
#ifndef TOML11_FIND_HPP
|
|
#define TOML11_FIND_HPP
|
|
|
|
#include <algorithm>
|
|
|
|
|
|
#if defined(TOML11_HAS_STRING_VIEW)
|
|
#include <string_view>
|
|
#endif
|
|
|
|
namespace toml
|
|
{
|
|
|
|
// ----------------------------------------------------------------------------
|
|
// find<T>(value, key);
|
|
|
|
template<typename T, typename TC>
|
|
decltype(::toml::get<T>(std::declval<basic_value<TC> const&>()))
|
|
find(const basic_value<TC>& v, const typename basic_value<TC>::key_type& ky)
|
|
{
|
|
return ::toml::get<T>(v.at(ky));
|
|
}
|
|
|
|
template<typename T, typename TC>
|
|
decltype(::toml::get<T>(std::declval<basic_value<TC>&>()))
|
|
find(basic_value<TC>& v, const typename basic_value<TC>::key_type& ky)
|
|
{
|
|
return ::toml::get<T>(v.at(ky));
|
|
}
|
|
|
|
template<typename T, typename TC>
|
|
decltype(::toml::get<T>(std::declval<basic_value<TC>&&>()))
|
|
find(basic_value<TC>&& v, const typename basic_value<TC>::key_type& ky)
|
|
{
|
|
return ::toml::get<T>(std::move(v.at(ky)));
|
|
}
|
|
|
|
// ----------------------------------------------------------------------------
|
|
// find<T>(value, idx)
|
|
|
|
template<typename T, typename TC>
|
|
decltype(::toml::get<T>(std::declval<basic_value<TC> const&>()))
|
|
find(const basic_value<TC>& v, const std::size_t idx)
|
|
{
|
|
return ::toml::get<T>(v.at(idx));
|
|
}
|
|
template<typename T, typename TC>
|
|
decltype(::toml::get<T>(std::declval<basic_value<TC>&>()))
|
|
find(basic_value<TC>& v, const std::size_t idx)
|
|
{
|
|
return ::toml::get<T>(v.at(idx));
|
|
}
|
|
template<typename T, typename TC>
|
|
decltype(::toml::get<T>(std::declval<basic_value<TC>&&>()))
|
|
find(basic_value<TC>&& v, const std::size_t idx)
|
|
{
|
|
return ::toml::get<T>(std::move(v.at(idx)));
|
|
}
|
|
|
|
// ----------------------------------------------------------------------------
|
|
// find(value, key/idx), w/o conversion
|
|
|
|
template<typename TC>
|
|
cxx::enable_if_t<detail::is_type_config<TC>::value, basic_value<TC>>&
|
|
find(basic_value<TC>& v, const typename basic_value<TC>::key_type& ky)
|
|
{
|
|
return v.at(ky);
|
|
}
|
|
template<typename TC>
|
|
cxx::enable_if_t<detail::is_type_config<TC>::value, basic_value<TC>> const&
|
|
find(basic_value<TC> const& v, const typename basic_value<TC>::key_type& ky)
|
|
{
|
|
return v.at(ky);
|
|
}
|
|
template<typename TC>
|
|
cxx::enable_if_t<detail::is_type_config<TC>::value, basic_value<TC>>
|
|
find(basic_value<TC>&& v, const typename basic_value<TC>::key_type& ky)
|
|
{
|
|
return basic_value<TC>(std::move(v.at(ky)));
|
|
}
|
|
|
|
template<typename TC>
|
|
cxx::enable_if_t<detail::is_type_config<TC>::value, basic_value<TC>>&
|
|
find(basic_value<TC>& v, const std::size_t idx)
|
|
{
|
|
return v.at(idx);
|
|
}
|
|
template<typename TC>
|
|
cxx::enable_if_t<detail::is_type_config<TC>::value, basic_value<TC>> const&
|
|
find(basic_value<TC> const& v, const std::size_t idx)
|
|
{
|
|
return v.at(idx);
|
|
}
|
|
template<typename TC>
|
|
cxx::enable_if_t<detail::is_type_config<TC>::value, basic_value<TC>>
|
|
find(basic_value<TC>&& v, const std::size_t idx)
|
|
{
|
|
return basic_value<TC>(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<typename TC>
|
|
std::size_t key_cast(const std::size_t& v) noexcept
|
|
{
|
|
return v;
|
|
}
|
|
template<typename TC, typename T>
|
|
cxx::enable_if_t<std::is_integral<cxx::remove_cvref_t<T>>::value, std::size_t>
|
|
key_cast(const T& v) noexcept
|
|
{
|
|
return static_cast<std::size_t>(v);
|
|
}
|
|
|
|
// for string-like (string, string literal, string_view)
|
|
|
|
template<typename TC>
|
|
typename basic_value<TC>::key_type const&
|
|
key_cast(const typename basic_value<TC>::key_type& v) noexcept
|
|
{
|
|
return v;
|
|
}
|
|
template<typename TC>
|
|
typename basic_value<TC>::key_type
|
|
key_cast(const typename basic_value<TC>::key_type::value_type* v)
|
|
{
|
|
return typename basic_value<TC>::key_type(v);
|
|
}
|
|
#if defined(TOML11_HAS_STRING_VIEW)
|
|
template<typename TC>
|
|
typename basic_value<TC>::key_type
|
|
key_cast(const std::string_view v)
|
|
{
|
|
return typename basic_value<TC>::key_type(v);
|
|
}
|
|
#endif // string_view
|
|
|
|
} // detail
|
|
|
|
// ----------------------------------------------------------------------------
|
|
// find(v, keys...)
|
|
|
|
template<typename TC, typename K1, typename K2, typename ... Ks>
|
|
cxx::enable_if_t<detail::is_type_config<TC>::value, basic_value<TC>> const&
|
|
find(const basic_value<TC>& v, const K1& k1, const K2& k2, const Ks& ... ks)
|
|
{
|
|
return find(v.at(detail::key_cast<TC>(k1)), detail::key_cast<TC>(k2), ks...);
|
|
}
|
|
template<typename TC, typename K1, typename K2, typename ... Ks>
|
|
cxx::enable_if_t<detail::is_type_config<TC>::value, basic_value<TC>>&
|
|
find(basic_value<TC>& v, const K1& k1, const K2& k2, const Ks& ... ks)
|
|
{
|
|
return find(v.at(detail::key_cast<TC>(k1)), detail::key_cast<TC>(k2), ks...);
|
|
}
|
|
template<typename TC, typename K1, typename K2, typename ... Ks>
|
|
cxx::enable_if_t<detail::is_type_config<TC>::value, basic_value<TC>>
|
|
find(basic_value<TC>&& v, const K1& k1, const K2& k2, const Ks& ... ks)
|
|
{
|
|
return find(std::move(v.at(detail::key_cast<TC>(k1))), detail::key_cast<TC>(k2), ks...);
|
|
}
|
|
|
|
// ----------------------------------------------------------------------------
|
|
// find<T>(v, keys...)
|
|
|
|
template<typename T, typename TC, typename K1, typename K2, typename ... Ks>
|
|
decltype(::toml::get<T>(std::declval<const basic_value<TC>&>()))
|
|
find(const basic_value<TC>& v, const K1& k1, const K2& k2, const Ks& ... ks)
|
|
{
|
|
return find<T>(v.at(detail::key_cast<TC>(k1)), detail::key_cast<TC>(k2), ks...);
|
|
}
|
|
template<typename T, typename TC, typename K1, typename K2, typename ... Ks>
|
|
decltype(::toml::get<T>(std::declval<basic_value<TC>&>()))
|
|
find(basic_value<TC>& v, const K1& k1, const K2& k2, const Ks& ... ks)
|
|
{
|
|
return find<T>(v.at(detail::key_cast<TC>(k1)), detail::key_cast<TC>(k2), ks...);
|
|
}
|
|
template<typename T, typename TC, typename K1, typename K2, typename ... Ks>
|
|
decltype(::toml::get<T>(std::declval<basic_value<TC>&&>()))
|
|
find(basic_value<TC>&& v, const K1& k1, const K2& k2, const Ks& ... ks)
|
|
{
|
|
return find<T>(std::move(v.at(detail::key_cast<TC>(k1))), detail::key_cast<TC>(k2), ks...);
|
|
}
|
|
|
|
// ===========================================================================
|
|
// find_or<T>(value, key, fallback)
|
|
|
|
// ---------------------------------------------------------------------------
|
|
// find_or(v, key, other_v)
|
|
|
|
template<typename TC, typename K>
|
|
cxx::enable_if_t<detail::is_type_config<TC>::value, basic_value<TC>>&
|
|
find_or(basic_value<TC>& v, const K& k, basic_value<TC>& opt) noexcept
|
|
{
|
|
try
|
|
{
|
|
return ::toml::find(v, detail::key_cast<TC>(k));
|
|
}
|
|
catch(...)
|
|
{
|
|
return opt;
|
|
}
|
|
}
|
|
template<typename TC, typename K>
|
|
cxx::enable_if_t<detail::is_type_config<TC>::value, basic_value<TC>> const&
|
|
find_or(const basic_value<TC>& v, const K& k, const basic_value<TC>& opt) noexcept
|
|
{
|
|
try
|
|
{
|
|
return ::toml::find(v, detail::key_cast<TC>(k));
|
|
}
|
|
catch(...)
|
|
{
|
|
return opt;
|
|
}
|
|
}
|
|
template<typename TC, typename K>
|
|
cxx::enable_if_t<detail::is_type_config<TC>::value, basic_value<TC>>
|
|
find_or(basic_value<TC>&& v, const K& k, basic_value<TC>&& opt) noexcept
|
|
{
|
|
try
|
|
{
|
|
return ::toml::find(v, detail::key_cast<TC>(k));
|
|
}
|
|
catch(...)
|
|
{
|
|
return opt;
|
|
}
|
|
}
|
|
|
|
// ---------------------------------------------------------------------------
|
|
// toml types (return type can be a reference)
|
|
|
|
template<typename T, typename TC, typename K>
|
|
cxx::enable_if_t<detail::is_exact_toml_type<T, basic_value<TC>>::value,
|
|
cxx::remove_cvref_t<T> const&>
|
|
find_or(const basic_value<TC>& v, const K& k, const T& opt)
|
|
{
|
|
try
|
|
{
|
|
return ::toml::get<T>(v.at(detail::key_cast<TC>(k)));
|
|
}
|
|
catch(...)
|
|
{
|
|
return opt;
|
|
}
|
|
}
|
|
|
|
template<typename T, typename TC, typename K>
|
|
cxx::enable_if_t<cxx::conjunction<
|
|
cxx::negation<std::is_const<T>>,
|
|
detail::is_exact_toml_type<T, basic_value<TC>>
|
|
>::value, cxx::remove_cvref_t<T>&>
|
|
find_or(basic_value<TC>& v, const K& k, T& opt)
|
|
{
|
|
try
|
|
{
|
|
return ::toml::get<T>(v.at(detail::key_cast<TC>(k)));
|
|
}
|
|
catch(...)
|
|
{
|
|
return opt;
|
|
}
|
|
}
|
|
|
|
template<typename T, typename TC, typename K>
|
|
cxx::enable_if_t<detail::is_exact_toml_type<T, basic_value<TC>>::value,
|
|
cxx::remove_cvref_t<T>>
|
|
find_or(basic_value<TC>&& v, const K& k, T opt)
|
|
{
|
|
try
|
|
{
|
|
return ::toml::get<T>(std::move(v.at(detail::key_cast<TC>(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<T>(),
|
|
// we restrict the string type as std::string.
|
|
template<typename TC, typename K>
|
|
cxx::enable_if_t<detail::is_type_config<TC>::value, std::string>
|
|
find_or(const basic_value<TC>& v, const K& k, const char* opt)
|
|
{
|
|
try
|
|
{
|
|
return ::toml::get<std::string>(v.at(detail::key_cast<TC>(k)));
|
|
}
|
|
catch(...)
|
|
{
|
|
return std::string(opt);
|
|
}
|
|
}
|
|
|
|
// ---------------------------------------------------------------------------
|
|
// other types (requires type conversion and return type cannot be a reference)
|
|
|
|
template<typename T, typename TC, typename K>
|
|
cxx::enable_if_t<cxx::conjunction<
|
|
cxx::negation<detail::is_basic_value<cxx::remove_cvref_t<T>>>,
|
|
detail::is_not_toml_type<cxx::remove_cvref_t<T>, basic_value<TC>>,
|
|
cxx::negation<std::is_same<cxx::remove_cvref_t<T>,
|
|
const typename basic_value<TC>::string_type::value_type*>>
|
|
>::value, cxx::remove_cvref_t<T>>
|
|
find_or(const basic_value<TC>& v, const K& ky, T opt)
|
|
{
|
|
try
|
|
{
|
|
return ::toml::get<cxx::remove_cvref_t<T>>(v.at(detail::key_cast<TC>(ky)));
|
|
}
|
|
catch(...)
|
|
{
|
|
return cxx::remove_cvref_t<T>(std::move(opt));
|
|
}
|
|
}
|
|
|
|
// ----------------------------------------------------------------------------
|
|
// recursive
|
|
|
|
namespace detail
|
|
{
|
|
|
|
template<typename ...Ts>
|
|
auto last_one(Ts&&... args)
|
|
-> decltype(std::get<sizeof...(Ts)-1>(std::forward_as_tuple(std::forward<Ts>(args)...)))
|
|
{
|
|
return std::get<sizeof...(Ts)-1>(std::forward_as_tuple(std::forward<Ts>(args)...));
|
|
}
|
|
|
|
} // detail
|
|
|
|
template<typename Value, typename K1, typename K2, typename K3, typename ... Ks>
|
|
auto find_or(Value&& v, const K1& k1, const K2& k2, K3&& k3, Ks&& ... keys) noexcept
|
|
-> cxx::enable_if_t<
|
|
detail::is_basic_value<cxx::remove_cvref_t<Value>>::value,
|
|
decltype(find_or(v, k2, std::forward<K3>(k3), std::forward<Ks>(keys)...))
|
|
>
|
|
{
|
|
try
|
|
{
|
|
return find_or(v.at(k1), k2, std::forward<K3>(k3), std::forward<Ks>(keys)...);
|
|
}
|
|
catch(...)
|
|
{
|
|
return detail::last_one(k3, keys...);
|
|
}
|
|
}
|
|
|
|
template<typename T, typename TC, typename K1, typename K2, typename K3, typename ... Ks>
|
|
T find_or(const basic_value<TC>& v, const K1& k1, const K2& k2, const K3& k3, const Ks& ... keys) noexcept
|
|
{
|
|
try
|
|
{
|
|
return find_or<T>(v.at(k1), k2, k3, keys...);
|
|
}
|
|
catch(...)
|
|
{
|
|
return static_cast<T>(detail::last_one(k3, keys...));
|
|
}
|
|
}
|
|
|
|
} // toml
|
|
#endif // TOML11_FIND_HPP
|
|
#ifndef TOML11_CONVERSION_HPP
|
|
#define TOML11_CONVERSION_HPP
|
|
|
|
|
|
#if defined(TOML11_HAS_OPTIONAL)
|
|
|
|
#include <optional>
|
|
|
|
namespace toml
|
|
{
|
|
namespace detail
|
|
{
|
|
|
|
template<typename T>
|
|
inline constexpr bool is_optional_v = false;
|
|
|
|
template<typename T>
|
|
inline constexpr bool is_optional_v<std::optional<T>> = true;
|
|
|
|
template<typename T, typename TC>
|
|
void find_member_variable_from_value(T& obj, const basic_value<TC>& v, const char* var_name)
|
|
{
|
|
if constexpr(is_optional_v<T>)
|
|
{
|
|
if(v.contains(var_name))
|
|
{
|
|
obj = toml::find<typename T::value_type>(v, var_name);
|
|
}
|
|
else
|
|
{
|
|
obj = std::nullopt;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
obj = toml::find<T>(v, var_name);
|
|
}
|
|
}
|
|
|
|
template<typename T, typename TC>
|
|
void assign_member_variable_to_value(const T& obj, basic_value<TC>& v, const char* var_name)
|
|
{
|
|
if constexpr(is_optional_v<T>)
|
|
{
|
|
if(obj.has_value())
|
|
{
|
|
v[var_name] = obj.value();
|
|
}
|
|
}
|
|
else
|
|
{
|
|
v[var_name] = obj;
|
|
}
|
|
}
|
|
|
|
} // detail
|
|
} // toml
|
|
|
|
#else
|
|
|
|
namespace toml
|
|
{
|
|
namespace detail
|
|
{
|
|
|
|
template<typename T, typename TC>
|
|
void find_member_variable_from_value(T& obj, const basic_value<TC>& v, const char* var_name)
|
|
{
|
|
obj = toml::find<T>(v, var_name);
|
|
}
|
|
|
|
template<typename T, typename TC>
|
|
void assign_member_variable_to_value(const T& obj, basic_value<TC>& 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<foo::Foo>(v)` and `toml::find<foo::Foo>(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<NAME> \
|
|
{ \
|
|
template<typename TC> \
|
|
static NAME from_toml(const basic_value<TC>& v) \
|
|
{ \
|
|
NAME obj; \
|
|
TOML11_FOR_EACH_VA_ARGS(TOML11_FIND_MEMBER_VARIABLE_FROM_VALUE, __VA_ARGS__) \
|
|
return obj; \
|
|
} \
|
|
}; \
|
|
template<> \
|
|
struct into<NAME> \
|
|
{ \
|
|
template<typename TC> \
|
|
static basic_value<TC> into_toml(const NAME& obj) \
|
|
{ \
|
|
::toml::basic_value<TC> v = typename ::toml::basic_value<TC>::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 <vector>
|
|
|
|
namespace toml
|
|
{
|
|
namespace detail
|
|
{
|
|
|
|
template<typename TypeConfig>
|
|
class context
|
|
{
|
|
public:
|
|
|
|
explicit context(const spec& toml_spec)
|
|
: toml_spec_(toml_spec), errors_{}
|
|
{}
|
|
|
|
bool has_error() const noexcept {return !errors_.empty();}
|
|
|
|
std::vector<error_info> 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<error_info> 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 <memory>
|
|
#include <string>
|
|
#include <utility>
|
|
#include <vector>
|
|
|
|
#include <cassert>
|
|
#include <cstdio>
|
|
#include <cctype>
|
|
|
|
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<typename Scanner, cxx::enable_if_t<
|
|
std::is_base_of<scanner_base, cxx::remove_cvref_t<Scanner>>::value,
|
|
std::nullptr_t> = nullptr>
|
|
explicit scanner_storage(Scanner&& s)
|
|
: scanner_(cxx::make_unique<cxx::remove_cvref_t<Scanner>>(std::forward<Scanner>(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<bool>(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_base> 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<char_type> cs) noexcept
|
|
: chars_(std::move(cs))
|
|
{
|
|
assert(! this->chars_.empty());
|
|
}
|
|
|
|
template<std::size_t N>
|
|
explicit character_either(const char (&cs)[N]) noexcept
|
|
: chars_(N-1, '\0')
|
|
{
|
|
static_assert(N >= 1, "");
|
|
for(std::size_t i=0; i+1<N; ++i)
|
|
{
|
|
chars_.at(i) = char_type(cs[i]);
|
|
}
|
|
}
|
|
~character_either() override = default;
|
|
|
|
region scan(location& loc) const override;
|
|
|
|
std::string expected_chars(location&) const override;
|
|
|
|
scanner_base* clone() const override;
|
|
|
|
void push_back(const char_type c);
|
|
|
|
std::string name() const override;
|
|
|
|
private:
|
|
std::vector<char_type> 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<std::size_t N>
|
|
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<typename ... Ts>
|
|
explicit sequence(Ts&& ... args)
|
|
{
|
|
push_back_all(std::forward<Ts>(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<typename Scanner>
|
|
void push_back(Scanner&& other_scanner)
|
|
{
|
|
this->others_.emplace_back(std::forward<Scanner>(other_scanner));
|
|
}
|
|
|
|
std::string name() const override;
|
|
|
|
private:
|
|
|
|
void push_back_all()
|
|
{
|
|
return;
|
|
}
|
|
template<typename T, typename ... Ts>
|
|
void push_back_all(T&& head, Ts&& ... args)
|
|
{
|
|
others_.emplace_back(std::forward<T>(head));
|
|
push_back_all(std::forward<Ts>(args)...);
|
|
return;
|
|
}
|
|
|
|
private:
|
|
std::vector<scanner_storage> others_;
|
|
};
|
|
|
|
// ----------------------------------------------------------------------------
|
|
|
|
class either final: public scanner_base
|
|
{
|
|
public:
|
|
using char_type = location::char_type;
|
|
|
|
public:
|
|
|
|
template<typename ... Ts>
|
|
explicit either(Ts&& ... args)
|
|
{
|
|
push_back_all(std::forward<Ts>(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<typename Scanner>
|
|
void push_back(Scanner&& other_scanner)
|
|
{
|
|
this->others_.emplace_back(std::forward<Scanner>(other_scanner));
|
|
}
|
|
|
|
std::string name() const override;
|
|
|
|
private:
|
|
|
|
void push_back_all()
|
|
{
|
|
return;
|
|
}
|
|
template<typename T, typename ... Ts>
|
|
void push_back_all(T&& head, Ts&& ... args)
|
|
{
|
|
others_.emplace_back(std::forward<T>(head));
|
|
push_back_all(std::forward<Ts>(args)...);
|
|
return;
|
|
}
|
|
|
|
private:
|
|
std::vector<scanner_storage> others_;
|
|
};
|
|
|
|
// ----------------------------------------------------------------------------
|
|
|
|
class repeat_exact final: public scanner_base
|
|
{
|
|
public:
|
|
using char_type = location::char_type;
|
|
|
|
public:
|
|
|
|
template<typename Scanner>
|
|
repeat_exact(const std::size_t length, Scanner&& other)
|
|
: length_(length), other_(std::forward<Scanner>(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<typename Scanner>
|
|
repeat_at_least(const std::size_t length, Scanner&& s)
|
|
: length_(length), other_(std::forward<Scanner>(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<typename Scanner>
|
|
explicit maybe(Scanner&& s)
|
|
: other_(std::forward<Scanner>(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; i<chars_.size(); ++i)
|
|
{
|
|
if(i != 0)
|
|
{
|
|
expected += ", ";
|
|
}
|
|
if(i + 1 == chars_.size())
|
|
{
|
|
expected += "or ";
|
|
}
|
|
expected += show_char(chars_.at(i));
|
|
}
|
|
}
|
|
return expected;
|
|
}
|
|
|
|
TOML11_INLINE scanner_base* character_either::clone() const
|
|
{
|
|
return new character_either(*this);
|
|
}
|
|
|
|
TOML11_INLINE void character_either::push_back(const char_type c)
|
|
{
|
|
chars_.push_back(c);
|
|
}
|
|
|
|
TOML11_INLINE std::string character_either::name() const
|
|
{
|
|
std::string n("character_either{");
|
|
for(const auto c : this->chars_)
|
|
{
|
|
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; i<size_; ++i)
|
|
{
|
|
if(loc.eof() || char_type(value_[i]) != loc.current())
|
|
{
|
|
loc = first;
|
|
return region{};
|
|
}
|
|
loc.advance(1);
|
|
}
|
|
return region(first, loc);
|
|
}
|
|
|
|
TOML11_INLINE std::string literal::expected_chars(location&) const
|
|
{
|
|
return std::string(value_);
|
|
}
|
|
|
|
TOML11_INLINE scanner_base* literal::clone() const
|
|
{
|
|
return new literal(*this);
|
|
}
|
|
|
|
TOML11_INLINE std::string literal::name() const
|
|
{
|
|
return std::string("literal{") + std::string(value_, size_) + "}";
|
|
}
|
|
|
|
// ----------------------------------------------------------------------------
|
|
// sequence
|
|
|
|
TOML11_INLINE region sequence::scan(location& loc) const
|
|
{
|
|
const auto first = loc;
|
|
for(const auto& other : others_)
|
|
{
|
|
const auto reg = other.scan(loc);
|
|
if( ! reg.is_ok())
|
|
{
|
|
loc = first;
|
|
return region{};
|
|
}
|
|
}
|
|
return region(first, loc);
|
|
}
|
|
|
|
TOML11_INLINE std::string sequence::expected_chars(location& loc) const
|
|
{
|
|
const auto first = loc;
|
|
for(const auto& other : others_)
|
|
{
|
|
const auto reg = other.scan(loc);
|
|
if( ! reg.is_ok())
|
|
{
|
|
return other.expected_chars(loc);
|
|
}
|
|
}
|
|
assert(false);
|
|
return ""; // XXX
|
|
}
|
|
|
|
TOML11_INLINE scanner_base* sequence::clone() const
|
|
{
|
|
return new sequence(*this);
|
|
}
|
|
|
|
TOML11_INLINE std::string sequence::name() const
|
|
{
|
|
std::string n("sequence{");
|
|
for(const auto& other : others_)
|
|
{
|
|
n += other.name();
|
|
n += ", ";
|
|
}
|
|
if( ! this->others_.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; i<others_.size(); ++i)
|
|
{
|
|
expected += ", ";
|
|
if(i + 1 == others_.size())
|
|
{
|
|
expected += "or ";
|
|
}
|
|
expected += others_.at(i).expected_chars(loc);
|
|
}
|
|
}
|
|
return expected;
|
|
}
|
|
|
|
TOML11_INLINE scanner_base* either::clone() const
|
|
{
|
|
return new either(*this);
|
|
}
|
|
|
|
TOML11_INLINE std::string either::name() const
|
|
{
|
|
std::string n("either{");
|
|
for(const auto& other : others_)
|
|
{
|
|
n += other.name();
|
|
n += ", ";
|
|
}
|
|
if( ! this->others_.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<length_; ++i)
|
|
{
|
|
const auto reg = other_.scan(loc);
|
|
if( ! reg.is_ok())
|
|
{
|
|
loc = first;
|
|
return region{};
|
|
}
|
|
}
|
|
return region(first, loc);
|
|
}
|
|
|
|
TOML11_INLINE std::string repeat_exact::expected_chars(location& loc) const
|
|
{
|
|
for(std::size_t i=0; i<length_; ++i)
|
|
{
|
|
const auto reg = other_.scan(loc);
|
|
if( ! reg.is_ok())
|
|
{
|
|
return other_.expected_chars(loc);
|
|
}
|
|
}
|
|
assert(false);
|
|
return "";
|
|
}
|
|
|
|
TOML11_INLINE scanner_base* repeat_exact::clone() const
|
|
{
|
|
return new repeat_exact(*this);
|
|
}
|
|
|
|
TOML11_INLINE std::string repeat_exact::name() const
|
|
{
|
|
return "repeat_exact{" + std::to_string(length_) + ", " + other_.name() + "}";
|
|
}
|
|
|
|
// ----------------------------------------------------------------------------
|
|
// repeat_at_least
|
|
|
|
TOML11_INLINE region repeat_at_least::scan(location& loc) const
|
|
{
|
|
const auto first = loc;
|
|
for(std::size_t i=0; i<length_; ++i)
|
|
{
|
|
const auto reg = other_.scan(loc);
|
|
if( ! reg.is_ok())
|
|
{
|
|
loc = first;
|
|
return region{};
|
|
}
|
|
}
|
|
while( ! loc.eof())
|
|
{
|
|
const auto checkpoint = loc;
|
|
const auto reg = other_.scan(loc);
|
|
if( ! reg.is_ok())
|
|
{
|
|
loc = checkpoint;
|
|
return region(first, loc);
|
|
}
|
|
}
|
|
return region(first, loc);
|
|
}
|
|
|
|
TOML11_INLINE std::string repeat_at_least::expected_chars(location& loc) const
|
|
{
|
|
for(std::size_t i=0; i<length_; ++i)
|
|
{
|
|
const auto reg = other_.scan(loc);
|
|
if( ! reg.is_ok())
|
|
{
|
|
return other_.expected_chars(loc);
|
|
}
|
|
}
|
|
assert(false);
|
|
return "";
|
|
}
|
|
|
|
TOML11_INLINE scanner_base* repeat_at_least::clone() const
|
|
{
|
|
return new repeat_at_least(*this);
|
|
}
|
|
|
|
TOML11_INLINE std::string repeat_at_least::name() const
|
|
{
|
|
return "repeat_at_least{" + std::to_string(length_) + ", " + other_.name() + "}";
|
|
}
|
|
|
|
// ----------------------------------------------------------------------------
|
|
// maybe
|
|
|
|
TOML11_INLINE region maybe::scan(location& loc) const
|
|
{
|
|
const auto first = loc;
|
|
const auto reg = other_.scan(loc);
|
|
if( ! reg.is_ok())
|
|
{
|
|
loc = first;
|
|
}
|
|
return region(first, loc);
|
|
}
|
|
|
|
TOML11_INLINE std::string maybe::expected_chars(location&) const
|
|
{
|
|
return "";
|
|
}
|
|
|
|
TOML11_INLINE scanner_base* maybe::clone() const
|
|
{
|
|
return new maybe(*this);
|
|
}
|
|
|
|
TOML11_INLINE std::string maybe::name() const
|
|
{
|
|
return "maybe{" + other_.name() + "}";
|
|
}
|
|
|
|
} // detail
|
|
} // toml
|
|
#endif // TOML11_SCANNER_IMPL_HPP
|
|
#endif
|
|
|
|
#endif // TOML11_SCANNER_HPP
|
|
#ifndef TOML11_SYNTAX_HPP
|
|
#define TOML11_SYNTAX_HPP
|
|
|
|
#ifndef TOML11_SYNTAX_FWD_HPP
|
|
#define TOML11_SYNTAX_FWD_HPP
|
|
|
|
|
|
namespace toml
|
|
{
|
|
namespace detail
|
|
{
|
|
namespace syntax
|
|
{
|
|
|
|
using char_type = location::char_type;
|
|
|
|
// ===========================================================================
|
|
// UTF-8
|
|
|
|
// avoid redundant representation and out-of-unicode sequence
|
|
|
|
character_in_range utf8_1byte (const spec&);
|
|
sequence utf8_2bytes(const spec&);
|
|
sequence utf8_3bytes(const spec&);
|
|
sequence utf8_4bytes(const spec&);
|
|
|
|
class non_ascii final : public scanner_base
|
|
{
|
|
public:
|
|
|
|
using char_type = location::char_type;
|
|
|
|
public:
|
|
|
|
explicit non_ascii(const spec& s) noexcept;
|
|
~non_ascii() override = default;
|
|
|
|
region scan(location& loc) const override
|
|
{
|
|
return scanner_.scan(loc);
|
|
}
|
|
|
|
std::string expected_chars(location&) const override
|
|
{
|
|
return "non-ascii utf-8 bytes";
|
|
}
|
|
|
|
scanner_base* clone() const override
|
|
{
|
|
return new non_ascii(*this);
|
|
}
|
|
|
|
std::string name() const override
|
|
{
|
|
return "non_ascii";
|
|
}
|
|
|
|
private:
|
|
|
|
either scanner_;
|
|
};
|
|
|
|
// ===========================================================================
|
|
// Whitespace
|
|
|
|
character_either wschar(const spec&);
|
|
|
|
repeat_at_least ws(const spec& s);
|
|
|
|
// ===========================================================================
|
|
// Newline
|
|
|
|
either newline(const spec&);
|
|
|
|
// ===========================================================================
|
|
// Comments
|
|
|
|
either allowed_comment_char(const spec& s);
|
|
|
|
// XXX Note that it does not take newline
|
|
sequence comment(const spec& s);
|
|
|
|
// ===========================================================================
|
|
// Boolean
|
|
|
|
either boolean(const spec&);
|
|
|
|
// ===========================================================================
|
|
// Integer
|
|
|
|
class digit final : public scanner_base
|
|
{
|
|
public:
|
|
|
|
using char_type = location::char_type;
|
|
|
|
public:
|
|
|
|
explicit digit(const spec&) noexcept;
|
|
~digit() override = default;
|
|
|
|
region scan(location& loc) const override
|
|
{
|
|
return scanner_.scan(loc);
|
|
}
|
|
|
|
std::string expected_chars(location&) const override
|
|
{
|
|
return "digit [0-9]";
|
|
}
|
|
|
|
scanner_base* clone() const override
|
|
{
|
|
return new digit(*this);
|
|
}
|
|
|
|
std::string name() const override
|
|
{
|
|
return "digit";
|
|
}
|
|
|
|
private:
|
|
|
|
character_in_range scanner_;
|
|
};
|
|
|
|
class alpha final : public scanner_base
|
|
{
|
|
public:
|
|
|
|
using char_type = location::char_type;
|
|
|
|
public:
|
|
|
|
explicit alpha(const spec&) noexcept;
|
|
~alpha() override = default;
|
|
|
|
region scan(location& loc) const override
|
|
{
|
|
return scanner_.scan(loc);
|
|
}
|
|
|
|
std::string expected_chars(location&) const override
|
|
{
|
|
return "alpha [a-zA-Z]";
|
|
}
|
|
|
|
scanner_base* clone() const override
|
|
{
|
|
return new alpha(*this);
|
|
}
|
|
|
|
std::string name() const override
|
|
{
|
|
return "alpha";
|
|
}
|
|
|
|
private:
|
|
|
|
either scanner_;
|
|
};
|
|
|
|
class hexdig final : public scanner_base
|
|
{
|
|
public:
|
|
|
|
using char_type = location::char_type;
|
|
|
|
public:
|
|
|
|
explicit hexdig(const spec& s) noexcept;
|
|
~hexdig() override = default;
|
|
|
|
region scan(location& loc) const override
|
|
{
|
|
return scanner_.scan(loc);
|
|
}
|
|
|
|
std::string expected_chars(location&) const override
|
|
{
|
|
return "hex [0-9a-fA-F]";
|
|
}
|
|
|
|
scanner_base* clone() const override
|
|
{
|
|
return new hexdig(*this);
|
|
}
|
|
|
|
std::string name() const override
|
|
{
|
|
return "hexdig";
|
|
}
|
|
|
|
private:
|
|
|
|
either scanner_;
|
|
};
|
|
|
|
sequence num_suffix(const spec& s);
|
|
|
|
sequence dec_int(const spec& s);
|
|
sequence hex_int(const spec& s);
|
|
sequence oct_int(const spec&);
|
|
sequence bin_int(const spec&);
|
|
either integer(const spec& s);
|
|
|
|
// ===========================================================================
|
|
// Floating
|
|
|
|
sequence zero_prefixable_int(const spec& s);
|
|
sequence fractional_part(const spec& s);
|
|
sequence exponent_part(const spec& s);
|
|
sequence hex_floating(const spec& s);
|
|
either floating(const spec& s);
|
|
|
|
// ===========================================================================
|
|
// Datetime
|
|
|
|
sequence local_date(const spec& s);
|
|
sequence local_time(const spec& s);
|
|
either time_offset(const spec& s);
|
|
sequence full_time(const spec& s);
|
|
character_either time_delim(const spec&);
|
|
sequence local_datetime(const spec& s);
|
|
sequence offset_datetime(const spec& s);
|
|
|
|
// ===========================================================================
|
|
// String
|
|
|
|
sequence escaped(const spec& s);
|
|
|
|
either basic_char(const spec& s);
|
|
|
|
sequence basic_string(const spec& s);
|
|
|
|
// ---------------------------------------------------------------------------
|
|
// multiline string
|
|
|
|
sequence escaped_newline(const spec& s);
|
|
sequence ml_basic_string(const spec& s);
|
|
|
|
// ---------------------------------------------------------------------------
|
|
// literal string
|
|
|
|
either literal_char(const spec& s);
|
|
sequence literal_string(const spec& s);
|
|
|
|
sequence ml_literal_string(const spec& s);
|
|
|
|
either string(const spec& s);
|
|
|
|
// ===========================================================================
|
|
// Keys
|
|
|
|
// to keep `expected_chars` simple
|
|
class non_ascii_key_char final : public scanner_base
|
|
{
|
|
public:
|
|
|
|
using char_type = location::char_type;
|
|
|
|
private:
|
|
|
|
using in_range = character_in_range; // make definition short
|
|
|
|
public:
|
|
|
|
explicit non_ascii_key_char(const spec& s) noexcept;
|
|
~non_ascii_key_char() override = default;
|
|
|
|
region scan(location& loc) const override;
|
|
|
|
std::string expected_chars(location&) const override
|
|
{
|
|
return "bare key non-ASCII script";
|
|
}
|
|
|
|
scanner_base* clone() const override
|
|
{
|
|
return new non_ascii_key_char(*this);
|
|
}
|
|
|
|
std::string name() const override
|
|
{
|
|
return "non-ASCII bare key";
|
|
}
|
|
|
|
private:
|
|
|
|
std::uint32_t read_utf8(location& loc) const;
|
|
};
|
|
|
|
|
|
repeat_at_least unquoted_key(const spec& s);
|
|
|
|
either quoted_key(const spec& s);
|
|
|
|
either simple_key(const spec& s);
|
|
|
|
sequence dot_sep(const spec& s);
|
|
|
|
sequence dotted_key(const spec& s);
|
|
|
|
|
|
class key final : public scanner_base
|
|
{
|
|
public:
|
|
|
|
using char_type = location::char_type;
|
|
|
|
public:
|
|
|
|
explicit key(const spec& s) noexcept;
|
|
~key() override = default;
|
|
|
|
region scan(location& loc) const override
|
|
{
|
|
return scanner_.scan(loc);
|
|
}
|
|
|
|
std::string expected_chars(location&) const override
|
|
{
|
|
return "basic key([a-zA-Z0-9_-]) or quoted key(\" or ')";
|
|
}
|
|
|
|
scanner_base* clone() const override
|
|
{
|
|
return new key(*this);
|
|
}
|
|
|
|
std::string name() const override
|
|
{
|
|
return "key";
|
|
}
|
|
|
|
private:
|
|
|
|
either scanner_;
|
|
};
|
|
|
|
sequence keyval_sep(const spec& s);
|
|
|
|
// ===========================================================================
|
|
// Table key
|
|
|
|
sequence std_table(const spec& s);
|
|
|
|
sequence array_table(const spec& s);
|
|
|
|
// ===========================================================================
|
|
// extension: null
|
|
|
|
literal null_value(const spec&);
|
|
|
|
} // namespace syntax
|
|
} // namespace detail
|
|
} // namespace toml
|
|
#endif // TOML11_SYNTAX_FWD_HPP
|
|
|
|
#if ! defined(TOML11_COMPILE_SOURCES)
|
|
#ifndef TOML11_SYNTAX_IMPL_HPP
|
|
#define TOML11_SYNTAX_IMPL_HPP
|
|
|
|
|
|
namespace toml
|
|
{
|
|
namespace detail
|
|
{
|
|
namespace syntax
|
|
{
|
|
|
|
using char_type = location::char_type;
|
|
|
|
// ===========================================================================
|
|
// UTF-8
|
|
|
|
// avoid redundant representation and out-of-unicode sequence
|
|
|
|
TOML11_INLINE character_in_range utf8_1byte(const spec&)
|
|
{
|
|
return character_in_range(0x00, 0x7F);
|
|
}
|
|
|
|
TOML11_INLINE sequence utf8_2bytes(const spec&)
|
|
{
|
|
return sequence(character_in_range(0xC2, 0xDF),
|
|
character_in_range(0x80, 0xBF));
|
|
}
|
|
|
|
TOML11_INLINE sequence utf8_3bytes(const spec&)
|
|
{
|
|
return sequence(/*1~2 bytes = */either(
|
|
sequence(character (0xE0), character_in_range(0xA0, 0xBF)),
|
|
sequence(character_in_range(0xE1, 0xEC), character_in_range(0x80, 0xBF)),
|
|
sequence(character (0xED), character_in_range(0x80, 0x9F)),
|
|
sequence(character_in_range(0xEE, 0xEF), character_in_range(0x80, 0xBF))
|
|
), /*3rd byte = */ character_in_range(0x80, 0xBF));
|
|
}
|
|
|
|
TOML11_INLINE sequence utf8_4bytes(const spec&)
|
|
{
|
|
return sequence(/*1~2 bytes = */either(
|
|
sequence(character (0xF0), character_in_range(0x90, 0xBF)),
|
|
sequence(character_in_range(0xF1, 0xF3), character_in_range(0x80, 0xBF)),
|
|
sequence(character (0xF4), character_in_range(0x80, 0x8F))
|
|
), character_in_range(0x80, 0xBF), character_in_range(0x80, 0xBF));
|
|
}
|
|
|
|
TOML11_INLINE non_ascii::non_ascii(const spec& s) noexcept
|
|
: scanner_(utf8_2bytes(s), utf8_3bytes(s), utf8_4bytes(s))
|
|
{}
|
|
|
|
|
|
// ===========================================================================
|
|
// Whitespace
|
|
|
|
TOML11_INLINE character_either wschar(const spec&)
|
|
{
|
|
return character_either{char_type(' '), char_type('\t')};
|
|
}
|
|
|
|
TOML11_INLINE repeat_at_least ws(const spec& s)
|
|
{
|
|
return repeat_at_least(0, wschar(s));
|
|
}
|
|
|
|
// ===========================================================================
|
|
// Newline
|
|
|
|
TOML11_INLINE either newline(const spec&)
|
|
{
|
|
return either(character(char_type('\n')), literal("\r\n"));
|
|
}
|
|
|
|
// ===========================================================================
|
|
// Comments
|
|
|
|
TOML11_INLINE either allowed_comment_char(const spec& s)
|
|
{
|
|
if(s.v1_1_0_allow_control_characters_in_comments)
|
|
{
|
|
return either(
|
|
character_in_range(0x01, 0x09),
|
|
character_in_range(0x0E, 0x7F),
|
|
non_ascii(s)
|
|
);
|
|
}
|
|
else
|
|
{
|
|
return either(
|
|
character(0x09),
|
|
character_in_range(0x20, 0x7E),
|
|
non_ascii(s)
|
|
);
|
|
}
|
|
}
|
|
|
|
// XXX Note that it does not take newline
|
|
TOML11_INLINE sequence comment(const spec& s)
|
|
{
|
|
return sequence(character(char_type('#')),
|
|
repeat_at_least(0, allowed_comment_char(s)));
|
|
}
|
|
|
|
// ===========================================================================
|
|
// Boolean
|
|
|
|
TOML11_INLINE either boolean(const spec&)
|
|
{
|
|
return either(literal("true"), literal("false"));
|
|
}
|
|
|
|
// ===========================================================================
|
|
// Integer
|
|
|
|
TOML11_INLINE digit::digit(const spec&) noexcept
|
|
: scanner_(char_type('0'), char_type('9'))
|
|
{}
|
|
|
|
TOML11_INLINE alpha::alpha(const spec&) noexcept
|
|
: scanner_(
|
|
character_in_range(char_type('a'), char_type('z')),
|
|
character_in_range(char_type('A'), char_type('Z'))
|
|
)
|
|
{}
|
|
|
|
TOML11_INLINE hexdig::hexdig(const spec& s) noexcept
|
|
: scanner_(
|
|
digit(s),
|
|
character_in_range(char_type('a'), char_type('f')),
|
|
character_in_range(char_type('A'), char_type('F'))
|
|
)
|
|
{}
|
|
|
|
// non-digit-graph = ([a-zA-Z]|unicode mb char)
|
|
// graph = ([a-zA-Z0-9]|unicode mb char)
|
|
// suffix = _ non-digit-graph (graph | _graph)
|
|
TOML11_INLINE sequence num_suffix(const spec& s)
|
|
{
|
|
const auto non_digit_graph = [&s]() {
|
|
return either(
|
|
alpha(s),
|
|
non_ascii(s)
|
|
);
|
|
};
|
|
const auto graph = [&s]() {
|
|
return either(
|
|
alpha(s),
|
|
digit(s),
|
|
non_ascii(s)
|
|
);
|
|
};
|
|
|
|
return sequence(
|
|
character(char_type('_')),
|
|
non_digit_graph(),
|
|
repeat_at_least(0,
|
|
either(
|
|
sequence(character(char_type('_')), graph()),
|
|
graph()
|
|
)
|
|
)
|
|
);
|
|
}
|
|
|
|
TOML11_INLINE sequence dec_int(const spec& s)
|
|
{
|
|
const auto digit19 = []() {
|
|
return character_in_range(char_type('1'), char_type('9'));
|
|
};
|
|
return sequence(
|
|
maybe(character_either{char_type('-'), char_type('+')}),
|
|
either(
|
|
sequence(
|
|
digit19(),
|
|
repeat_at_least(1,
|
|
either(
|
|
digit(s),
|
|
sequence(character(char_type('_')), digit(s))
|
|
)
|
|
)
|
|
),
|
|
digit(s)
|
|
)
|
|
);
|
|
}
|
|
|
|
TOML11_INLINE sequence hex_int(const spec& s)
|
|
{
|
|
return sequence(
|
|
literal("0x"),
|
|
hexdig(s),
|
|
repeat_at_least(0,
|
|
either(
|
|
hexdig(s),
|
|
sequence(character(char_type('_')), hexdig(s))
|
|
)
|
|
)
|
|
);
|
|
}
|
|
|
|
TOML11_INLINE sequence oct_int(const spec&)
|
|
{
|
|
const auto digit07 = []() {
|
|
return character_in_range(char_type('0'), char_type('7'));
|
|
};
|
|
return sequence(
|
|
literal("0o"),
|
|
digit07(),
|
|
repeat_at_least(0,
|
|
either(
|
|
digit07(),
|
|
sequence(character(char_type('_')), digit07())
|
|
)
|
|
)
|
|
);
|
|
}
|
|
|
|
TOML11_INLINE sequence bin_int(const spec&)
|
|
{
|
|
const auto digit01 = []() {
|
|
return character_either{char_type('0'), char_type('1')};
|
|
};
|
|
return sequence(
|
|
literal("0b"),
|
|
digit01(),
|
|
repeat_at_least(0,
|
|
either(
|
|
digit01(),
|
|
sequence(character(char_type('_')), digit01())
|
|
)
|
|
)
|
|
);
|
|
}
|
|
|
|
TOML11_INLINE either integer(const spec& s)
|
|
{
|
|
return either(
|
|
hex_int(s),
|
|
oct_int(s),
|
|
bin_int(s),
|
|
dec_int(s)
|
|
);
|
|
}
|
|
|
|
|
|
// ===========================================================================
|
|
// Floating
|
|
|
|
TOML11_INLINE sequence zero_prefixable_int(const spec& s)
|
|
{
|
|
return sequence(
|
|
digit(s),
|
|
repeat_at_least(0,
|
|
either(
|
|
digit(s),
|
|
sequence(character('_'), digit(s))
|
|
)
|
|
)
|
|
);
|
|
}
|
|
|
|
TOML11_INLINE sequence fractional_part(const spec& s)
|
|
{
|
|
return sequence(
|
|
character('.'),
|
|
zero_prefixable_int(s)
|
|
);
|
|
}
|
|
|
|
TOML11_INLINE sequence exponent_part(const spec& s)
|
|
{
|
|
return sequence(
|
|
character_either{char_type('e'), char_type('E')},
|
|
maybe(character_either{char_type('+'), char_type('-')}),
|
|
zero_prefixable_int(s)
|
|
);
|
|
}
|
|
|
|
TOML11_INLINE sequence hex_floating(const spec& s)
|
|
{
|
|
// C99 hexfloat (%a)
|
|
// [+-]? 0x ( [0-9a-fA-F]*\.[0-9a-fA-F]+ | [0-9a-fA-F]+\.? ) [pP] [+-]? [0-9]+
|
|
|
|
// - 0x(int).(frac)p[+-](int)
|
|
// - 0x(int).p[+-](int)
|
|
// - 0x.(frac)p[+-](int)
|
|
// - 0x(int)p[+-](int)
|
|
|
|
return sequence(
|
|
maybe(character_either{char_type('+'), char_type('-')}),
|
|
character('0'),
|
|
character_either{char_type('x'), char_type('X')},
|
|
either(
|
|
sequence(
|
|
repeat_at_least(0, hexdig(s)),
|
|
character('.'),
|
|
repeat_at_least(1, hexdig(s))
|
|
),
|
|
sequence(
|
|
repeat_at_least(1, hexdig(s)),
|
|
maybe(character('.'))
|
|
)
|
|
),
|
|
character_either{char_type('p'), char_type('P')},
|
|
maybe(character_either{char_type('+'), char_type('-')}),
|
|
repeat_at_least(1, character_in_range('0', '9'))
|
|
);
|
|
}
|
|
|
|
TOML11_INLINE either floating(const spec& s)
|
|
{
|
|
return either(
|
|
sequence(
|
|
dec_int(s),
|
|
either(
|
|
exponent_part(s),
|
|
sequence(fractional_part(s), maybe(exponent_part(s)))
|
|
)
|
|
),
|
|
sequence(
|
|
maybe(character_either{char_type('-'), char_type('+')}),
|
|
either(literal("inf"), literal("nan"))
|
|
)
|
|
);
|
|
}
|
|
|
|
// ===========================================================================
|
|
// Datetime
|
|
|
|
TOML11_INLINE sequence local_date(const spec& s)
|
|
{
|
|
return sequence(
|
|
repeat_exact(4, digit(s)),
|
|
character('-'),
|
|
repeat_exact(2, digit(s)),
|
|
character('-'),
|
|
repeat_exact(2, digit(s))
|
|
);
|
|
}
|
|
TOML11_INLINE sequence local_time(const spec& s)
|
|
{
|
|
auto time = sequence(
|
|
repeat_exact(2, digit(s)),
|
|
character(':'),
|
|
repeat_exact(2, digit(s))
|
|
);
|
|
|
|
if(s.v1_1_0_make_seconds_optional)
|
|
{
|
|
time.push_back(maybe(sequence(
|
|
character(':'),
|
|
repeat_exact(2, digit(s)),
|
|
maybe(sequence(character('.'), repeat_at_least(1, digit(s))))
|
|
)));
|
|
}
|
|
else
|
|
{
|
|
time.push_back(character(':'));
|
|
time.push_back(repeat_exact(2, digit(s)));
|
|
time.push_back(
|
|
maybe(sequence(character('.'), repeat_at_least(1, digit(s))))
|
|
);
|
|
}
|
|
|
|
return time;
|
|
}
|
|
TOML11_INLINE either time_offset(const spec& s)
|
|
{
|
|
return either(
|
|
character_either{'Z', 'z'},
|
|
sequence(character_either{'+', '-'},
|
|
repeat_exact(2, digit(s)),
|
|
character(':'),
|
|
repeat_exact(2, digit(s))
|
|
)
|
|
);
|
|
}
|
|
TOML11_INLINE sequence full_time(const spec& s)
|
|
{
|
|
return sequence(local_time(s), time_offset(s));
|
|
}
|
|
TOML11_INLINE character_either time_delim(const spec&)
|
|
{
|
|
return character_either{'T', 't', ' '};
|
|
}
|
|
TOML11_INLINE sequence local_datetime(const spec& s)
|
|
{
|
|
return sequence(local_date(s), time_delim(s), local_time(s));
|
|
}
|
|
TOML11_INLINE sequence offset_datetime(const spec& s)
|
|
{
|
|
return sequence(local_date(s), time_delim(s), full_time(s));
|
|
}
|
|
|
|
// ===========================================================================
|
|
// String
|
|
|
|
TOML11_INLINE sequence escaped(const spec& s)
|
|
{
|
|
character_either escape_char{
|
|
'\"','\\', 'b', 'f', 'n', 'r', 't'
|
|
};
|
|
if(s.v1_1_0_add_escape_sequence_e)
|
|
{
|
|
escape_char.push_back(char_type('e'));
|
|
}
|
|
|
|
either escape_seq(
|
|
std::move(escape_char),
|
|
sequence(character('u'), repeat_exact(4, hexdig(s))),
|
|
sequence(character('U'), repeat_exact(8, hexdig(s)))
|
|
);
|
|
|
|
if(s.v1_1_0_add_escape_sequence_x)
|
|
{
|
|
escape_seq.push_back(
|
|
sequence(character('x'), repeat_exact(2, hexdig(s)))
|
|
);
|
|
}
|
|
|
|
return sequence(
|
|
character('\\'),
|
|
std::move(escape_seq)
|
|
);
|
|
}
|
|
|
|
TOML11_INLINE either basic_char(const spec& s)
|
|
{
|
|
const auto basic_unescaped = [&s]() {
|
|
return either(
|
|
wschar(s),
|
|
character(0x21), // 22 is "
|
|
character_in_range(0x23, 0x5B), // 5C is backslash
|
|
character_in_range(0x5D, 0x7E), // 7F is DEL
|
|
non_ascii(s)
|
|
);
|
|
};
|
|
return either(basic_unescaped(), escaped(s));
|
|
}
|
|
|
|
TOML11_INLINE sequence basic_string(const spec& s)
|
|
{
|
|
return sequence(
|
|
character('"'),
|
|
repeat_at_least(0, basic_char(s)),
|
|
character('"')
|
|
);
|
|
}
|
|
|
|
// ---------------------------------------------------------------------------
|
|
// multiline string
|
|
|
|
TOML11_INLINE sequence escaped_newline(const spec& s)
|
|
{
|
|
return sequence(
|
|
character('\\'), ws(s), newline(s),
|
|
repeat_at_least(0, either(wschar(s), newline(s)))
|
|
);
|
|
}
|
|
|
|
TOML11_INLINE sequence ml_basic_string(const spec& s)
|
|
{
|
|
const auto mlb_content = [&s]() {
|
|
return either(basic_char(s), newline(s), escaped_newline(s));
|
|
};
|
|
const auto mlb_quotes = []() {
|
|
return either(literal("\"\""), character('\"'));
|
|
};
|
|
|
|
return sequence(
|
|
literal("\"\"\""),
|
|
maybe(newline(s)),
|
|
repeat_at_least(0, mlb_content()),
|
|
repeat_at_least(0,
|
|
sequence(
|
|
mlb_quotes(),
|
|
repeat_at_least(1, mlb_content())
|
|
)
|
|
),
|
|
// XXX """ and mlb_quotes are intentionally reordered to avoid
|
|
// unexpected match of mlb_quotes
|
|
literal("\"\"\""),
|
|
maybe(mlb_quotes())
|
|
);
|
|
}
|
|
|
|
// ---------------------------------------------------------------------------
|
|
// literal string
|
|
|
|
TOML11_INLINE either literal_char(const spec& s)
|
|
{
|
|
return either(
|
|
character (0x09),
|
|
character_in_range(0x20, 0x26),
|
|
character_in_range(0x28, 0x7E),
|
|
non_ascii(s)
|
|
);
|
|
}
|
|
|
|
TOML11_INLINE sequence literal_string(const spec& s)
|
|
{
|
|
return sequence(
|
|
character('\''),
|
|
repeat_at_least(0, literal_char(s)),
|
|
character('\'')
|
|
);
|
|
}
|
|
|
|
TOML11_INLINE sequence ml_literal_string(const spec& s)
|
|
{
|
|
const auto mll_quotes = []() {
|
|
return either(literal("''"), character('\''));
|
|
};
|
|
const auto mll_content = [&s]() {
|
|
return either(literal_char(s), newline(s));
|
|
};
|
|
|
|
return sequence(
|
|
literal("'''"),
|
|
maybe(newline(s)),
|
|
repeat_at_least(0, mll_content()),
|
|
repeat_at_least(0, sequence(
|
|
mll_quotes(),
|
|
repeat_at_least(1, mll_content())
|
|
)
|
|
),
|
|
literal("'''"),
|
|
maybe(mll_quotes())
|
|
// XXX ''' and mll_quotes are intentionally reordered to avoid
|
|
// unexpected match of mll_quotes
|
|
);
|
|
}
|
|
|
|
TOML11_INLINE either string(const spec& s)
|
|
{
|
|
return either(
|
|
ml_basic_string(s),
|
|
ml_literal_string(s),
|
|
basic_string(s),
|
|
literal_string(s)
|
|
);
|
|
}
|
|
|
|
// ===========================================================================
|
|
// Keys
|
|
|
|
// to keep `expected_chars` simple
|
|
TOML11_INLINE non_ascii_key_char::non_ascii_key_char(const spec& s) noexcept
|
|
{
|
|
assert(s.v1_1_0_allow_non_english_in_bare_keys);
|
|
(void)s; // for NDEBUG
|
|
}
|
|
|
|
TOML11_INLINE std::uint32_t non_ascii_key_char::read_utf8(location& loc) const
|
|
{
|
|
// U+0000 ... U+0079 ; 0xxx_xxxx
|
|
// U+0080 ... U+07FF ; 110y_yyyx 10xx_xxxx;
|
|
// U+0800 ... U+FFFF ; 1110_yyyy 10yx_xxxx 10xx_xxxx
|
|
// U+010000 ... U+10FFFF; 1111_0yyy 10yy_xxxx 10xx_xxxx 10xx_xxxx
|
|
|
|
const unsigned char b1 = loc.current(); loc.advance(1);
|
|
if(b1 < 0x80)
|
|
{
|
|
return static_cast<std::uint32_t>(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 <cassert>
|
|
|
|
namespace toml
|
|
{
|
|
namespace detail
|
|
{
|
|
|
|
template<typename TC>
|
|
bool skip_whitespace(location& loc, const context<TC>& ctx)
|
|
{
|
|
return syntax::ws(ctx.toml_spec()).scan(loc).is_ok();
|
|
}
|
|
|
|
template<typename TC>
|
|
bool skip_empty_lines(location& loc, const context<TC>& 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<typename TC>
|
|
void skip_comment_block(location& loc, const context<TC>& 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<typename TC>
|
|
void skip_empty_or_comment_lines(location& loc, const context<TC>& 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<typename TC>
|
|
void skip_string_like(location& loc, const context<TC>&)
|
|
{
|
|
// 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<typename TC>
|
|
void skip_value(location& loc, const context<TC>& ctx);
|
|
template<typename TC>
|
|
void skip_array_like(location& loc, const context<TC>& ctx);
|
|
template<typename TC>
|
|
void skip_inline_table_like(location& loc, const context<TC>& ctx);
|
|
template<typename TC>
|
|
void skip_key_value_pair(location& loc, const context<TC>& ctx);
|
|
|
|
template<typename TC>
|
|
result<value_t, error_info>
|
|
guess_value_type(const location& loc, const context<TC>& ctx);
|
|
|
|
template<typename TC>
|
|
void skip_array_like(location& loc, const context<TC>& 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<typename TC>
|
|
void skip_inline_table_like(location& loc, const context<TC>& 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<typename TC>
|
|
void skip_value(location& loc, const context<TC>& 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<typename TC>
|
|
void skip_key_value_pair(location& loc, const context<TC>& 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<typename TC>
|
|
void skip_until_next_table(location& loc, const context<TC>& 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 <type_config>(location& loc, const context<type_config>&);
|
|
extern template bool skip_empty_lines <type_config>(location& loc, const context<type_config>&);
|
|
extern template void skip_comment_block <type_config>(location& loc, const context<type_config>&);
|
|
extern template void skip_empty_or_comment_lines<type_config>(location& loc, const context<type_config>&);
|
|
extern template void skip_string_like <type_config>(location& loc, const context<type_config>&);
|
|
extern template void skip_array_like <type_config>(location& loc, const context<type_config>&);
|
|
extern template void skip_inline_table_like <type_config>(location& loc, const context<type_config>&);
|
|
extern template void skip_value <type_config>(location& loc, const context<type_config>&);
|
|
extern template void skip_key_value_pair <type_config>(location& loc, const context<type_config>&);
|
|
extern template void skip_until_next_table <type_config>(location& loc, const context<type_config>&);
|
|
|
|
extern template bool skip_whitespace <ordered_type_config>(location& loc, const context<ordered_type_config>&);
|
|
extern template bool skip_empty_lines <ordered_type_config>(location& loc, const context<ordered_type_config>&);
|
|
extern template void skip_comment_block <ordered_type_config>(location& loc, const context<ordered_type_config>&);
|
|
extern template void skip_empty_or_comment_lines<ordered_type_config>(location& loc, const context<ordered_type_config>&);
|
|
extern template void skip_string_like <ordered_type_config>(location& loc, const context<ordered_type_config>&);
|
|
extern template void skip_array_like <ordered_type_config>(location& loc, const context<ordered_type_config>&);
|
|
extern template void skip_inline_table_like <ordered_type_config>(location& loc, const context<ordered_type_config>&);
|
|
extern template void skip_value <ordered_type_config>(location& loc, const context<ordered_type_config>&);
|
|
extern template void skip_key_value_pair <ordered_type_config>(location& loc, const context<ordered_type_config>&);
|
|
extern template void skip_until_next_table <ordered_type_config>(location& loc, const context<ordered_type_config>&);
|
|
|
|
} // detail
|
|
} // toml
|
|
#endif // TOML11_COMPILE_SOURCES
|
|
|
|
#endif // TOML11_SKIP_HPP
|
|
#ifndef TOML11_PARSER_HPP
|
|
#define TOML11_PARSER_HPP
|
|
|
|
|
|
#include <fstream>
|
|
#include <sstream>
|
|
|
|
#include <cassert>
|
|
#include <cmath>
|
|
|
|
#if defined(TOML11_HAS_FILESYSTEM) && TOML11_HAS_FILESYSTEM
|
|
#include <filesystem>
|
|
#endif
|
|
|
|
namespace toml
|
|
{
|
|
|
|
struct syntax_error final : public ::toml::exception
|
|
{
|
|
public:
|
|
syntax_error(std::string what_arg, std::vector<error_info> 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<error_info> const& errors() const noexcept
|
|
{
|
|
return err_;
|
|
}
|
|
|
|
private:
|
|
std::string what_;
|
|
std::vector<error_info> 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<int> errno_;
|
|
std::string what_;
|
|
};
|
|
|
|
namespace detail
|
|
{
|
|
|
|
/* ============================================================================
|
|
* __ ___ _ __ _ __ ___ _ _
|
|
* / _/ _ \ ' \| ' \/ _ \ ' \
|
|
* \__\___/_|_|_|_|_|_\___/_||_|
|
|
*/
|
|
|
|
template<typename S>
|
|
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<typename TC>
|
|
result<cxx::optional<std::string>, error_info>
|
|
parse_comment_line(location& loc, context<TC>& 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<std::string>(com_reg.as_string()));
|
|
}
|
|
else
|
|
{
|
|
loc = first; // rollback whitespace to parse indent
|
|
return ok(cxx::optional<std::string>(cxx::make_nullopt()));
|
|
}
|
|
}
|
|
|
|
/* ============================================================================
|
|
* ___ _
|
|
* | _ ) ___ ___| |___ __ _ _ _
|
|
* | _ \/ _ \/ _ \ / -_) _` | ' \
|
|
* |___/\___/\___/_\___\__,_|_||_|
|
|
*/
|
|
|
|
template<typename TC>
|
|
result<basic_value<TC>, error_info>
|
|
parse_boolean(location& loc, const context<TC>& 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<TC>(val, std::move(fmt), {}, std::move(reg)));
|
|
}
|
|
|
|
/* ============================================================================
|
|
* ___ _
|
|
* |_ _|_ _| |_ ___ __ _ ___ _ _
|
|
* | || ' \ _/ -_) _` / -_) '_|
|
|
* |___|_||_\__\___\__, \___|_|
|
|
* |___/
|
|
*/
|
|
|
|
template<typename TC>
|
|
result<basic_value<TC>, error_info>
|
|
parse_bin_integer(location& loc, const context<TC>& 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::size_t>(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::size_t>(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<TC>(val.as_ok(), std::move(fmt), {}, std::move(reg)));
|
|
}
|
|
else
|
|
{
|
|
loc = first;
|
|
return err(val.as_err());
|
|
}
|
|
}
|
|
|
|
// ----------------------------------------------------------------------------
|
|
|
|
template<typename TC>
|
|
result<basic_value<TC>, error_info>
|
|
parse_oct_integer(location& loc, const context<TC>& 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::size_t>(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::size_t>(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<TC>(val.as_ok(), std::move(fmt), {}, std::move(reg)));
|
|
}
|
|
else
|
|
{
|
|
loc = first;
|
|
return err(val.as_err());
|
|
}
|
|
}
|
|
|
|
template<typename TC>
|
|
result<basic_value<TC>, error_info>
|
|
parse_hex_integer(location& loc, const context<TC>& 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::size_t>(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::size_t>(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<int>(c)) != 0; }) == str.end();
|
|
const auto upper_found = std::find_if(str.begin(), str.end(),
|
|
[](const char c) { return std::isupper(static_cast<int>(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<TC>(val.as_ok(), std::move(fmt), {}, std::move(reg)));
|
|
}
|
|
else
|
|
{
|
|
loc = first;
|
|
return err(val.as_err());
|
|
}
|
|
}
|
|
|
|
template<typename TC>
|
|
result<basic_value<TC>, error_info>
|
|
parse_dec_integer(location& loc, const context<TC>& 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::size_t>(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::size_t>(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<TC>(val.as_ok(), std::move(fmt), {}, std::move(reg)));
|
|
}
|
|
|
|
template<typename TC>
|
|
result<basic_value<TC>, error_info>
|
|
parse_integer(location& loc, const context<TC>& 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<typename TC>
|
|
result<basic_value<TC>, error_info>
|
|
parse_floating(location& loc, const context<TC>& ctx)
|
|
{
|
|
using floating_type = typename basic_value<TC>::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<floating_type>::has_infinity)
|
|
{
|
|
val = std::numeric_limits<floating_type>::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<floating_type>::has_infinity)
|
|
{
|
|
val = -std::numeric_limits<floating_type>::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<floating_type>::has_quiet_NaN)
|
|
{
|
|
val = std::numeric_limits<floating_type>::quiet_NaN();
|
|
}
|
|
else TOML11_CONSTEXPR_IF(std::numeric_limits<floating_type>::has_signaling_NaN)
|
|
{
|
|
val = std::numeric_limits<floating_type>::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<floating_type>::has_quiet_NaN)
|
|
{
|
|
val = copysign(std::numeric_limits<floating_type>::quiet_NaN(), floating_type(-1));
|
|
}
|
|
else TOML11_CONSTEXPR_IF(std::numeric_limits<floating_type>::has_signaling_NaN)
|
|
{
|
|
val = copysign(std::numeric_limits<floating_type>::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::size_t>(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<floating_type>::max_digits10;
|
|
}
|
|
else
|
|
{
|
|
// width after decimal point
|
|
fmt.prec = static_cast<std::size_t>(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<TC>(val, std::move(fmt), {}, std::move(reg)));
|
|
}
|
|
|
|
/* ============================================================================
|
|
* ___ _ _ _
|
|
* | \ __ _| |_ ___| |_(_)_ __ ___
|
|
* | |) / _` | _/ -_) _| | ' \/ -_)
|
|
* |___/\__,_|\__\___|\__|_|_|_|_\___|
|
|
*/
|
|
|
|
// all the offset_datetime, local_datetime, local_date parses date part.
|
|
template<typename TC>
|
|
result<std::tuple<local_date, local_date_format_info, region>, error_info>
|
|
parse_local_date_only(location& loc, const context<TC>& 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<int>(str.substr(0, 4));
|
|
const auto month_r = from_string<int>(str.substr(5, 2));
|
|
const auto day_r = from_string<int>(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_t>(month - 1), day),
|
|
std::move(fmt), std::move(reg)
|
|
));
|
|
}
|
|
|
|
template<typename TC>
|
|
result<basic_value<TC>, error_info>
|
|
parse_local_date(location& loc, const context<TC>& 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<TC>(std::move(val), std::move(fmt), {}, std::move(reg)));
|
|
}
|
|
|
|
// all the offset_datetime, local_datetime, local_time parses date part.
|
|
template<typename TC>
|
|
result<std::tuple<local_time, local_time_format_info, region>, error_info>
|
|
parse_local_time_only(location& loc, const context<TC>& 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<int>(str.substr(0, 2));
|
|
const auto minute_r = from_string<int>(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<int>(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<int>(secfrac.substr(0, 3));
|
|
const auto us_r = from_string<int>(secfrac.substr(3, 3));
|
|
const auto ns_r = from_string<int>(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<typename TC>
|
|
result<basic_value<TC>, error_info>
|
|
parse_local_time(location& loc, const context<TC>& 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<TC>(std::move(val), std::move(fmt), {}, std::move(reg)));
|
|
}
|
|
|
|
template<typename TC>
|
|
result<basic_value<TC>, error_info>
|
|
parse_local_datetime(location& loc, const context<TC>& 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<TC>(val, std::move(fmt), {}, std::move(reg)));
|
|
}
|
|
|
|
template<typename TC>
|
|
result<basic_value<TC>, error_info>
|
|
parse_offset_datetime(location& loc, const context<TC>& 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<int>(ofs_str.substr(1, 2));
|
|
const auto minute_r = from_string<int>(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<TC>(val, std::move(fmt), {}, std::move(reg)));
|
|
}
|
|
|
|
/* ============================================================================
|
|
* ___ _ _
|
|
* / __| |_ _ _(_)_ _ __ _
|
|
* \__ \ _| '_| | ' \/ _` |
|
|
* |___/\__|_| |_|_||_\__, |
|
|
* |___/
|
|
*/
|
|
|
|
template<typename TC>
|
|
result<typename basic_value<TC>::string_type, error_info>
|
|
parse_utf8_codepoint(const region& reg)
|
|
{
|
|
using string_type = typename basic_value<TC>::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<unsigned char>(i & 0xFF);
|
|
return cxx::bit_cast<char_type>(uc);
|
|
};
|
|
|
|
string_type character;
|
|
if(codepoint < 0x80) // U+0000 ... U+0079 ; just an ASCII.
|
|
{
|
|
character += static_cast<char>(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<typename TC>
|
|
result<typename basic_value<TC>::string_type, error_info>
|
|
parse_escape_sequence(location& loc, const context<TC>& ctx)
|
|
{
|
|
using string_type = typename basic_value<TC>::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<TC>(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<TC>(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<TC>(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<typename TC>
|
|
result<basic_value<TC>, error_info>
|
|
parse_ml_basic_string(location& loc, const context<TC>& 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<TC>::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<typename string_type::value_type>(*iter);
|
|
++iter;
|
|
}
|
|
}
|
|
}
|
|
|
|
return ok(basic_value<TC>(
|
|
std::move(val), std::move(fmt), {}, std::move(reg)
|
|
));
|
|
}
|
|
|
|
template<typename TC>
|
|
result<std::pair<typename basic_value<TC>::string_type, region>, error_info>
|
|
parse_basic_string_only(location& loc, const context<TC>& 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<TC>::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<typename TC>
|
|
result<basic_value<TC>, error_info>
|
|
parse_basic_string(location& loc, const context<TC>& 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<TC>(std::move(val), std::move(fmt), {}, std::move(reg)));
|
|
}
|
|
|
|
template<typename TC>
|
|
result<basic_value<TC>, error_info>
|
|
parse_ml_literal_string(location& loc, const context<TC>& 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<TC>::string_type;
|
|
string_type val(str.begin(), str.end());
|
|
|
|
return ok(basic_value<TC>(
|
|
std::move(val), std::move(fmt), {}, std::move(reg)
|
|
));
|
|
}
|
|
|
|
template<typename TC>
|
|
result<std::pair<typename basic_value<TC>::string_type, region>, error_info>
|
|
parse_literal_string_only(location& loc, const context<TC>& 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<TC>::string_type;
|
|
string_type val(str.begin(), str.end());
|
|
|
|
return ok(std::make_pair(std::move(val), std::move(reg)));
|
|
}
|
|
|
|
template<typename TC>
|
|
result<basic_value<TC>, error_info>
|
|
parse_literal_string(location& loc, const context<TC>& 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<TC>(
|
|
std::move(val), std::move(fmt), {}, std::move(reg)
|
|
));
|
|
}
|
|
|
|
template<typename TC>
|
|
result<basic_value<TC>, error_info>
|
|
parse_string(location& loc, const context<TC>& 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<typename TC>
|
|
result<basic_value<TC>, error_info>
|
|
parse_null(location& loc, const context<TC>& 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<TC>(detail::none_t{}, std::move(reg)));
|
|
}
|
|
|
|
/* ============================================================================
|
|
* _ __
|
|
* | |/ /___ _ _
|
|
* | ' </ -_) || |
|
|
* |_|\_\___|\_, |
|
|
* |__/
|
|
*/
|
|
|
|
// non-dotted key.
|
|
template<typename TC>
|
|
result<typename basic_value<TC>::key_type, error_info>
|
|
parse_simple_key(location& loc, const context<TC>& ctx)
|
|
{
|
|
using key_type = typename basic_value<TC>::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<key_type>(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<typename TC>
|
|
result<std::pair<std::vector<typename basic_value<TC>::key_type>, region>, error_info>
|
|
parse_key(location& loc, const context<TC>& ctx)
|
|
{
|
|
const auto first = loc;
|
|
const auto& spec = ctx.toml_spec();
|
|
|
|
using key_type = typename basic_value<TC>::key_type;
|
|
std::vector<key_type> 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<typename TC>
|
|
result<basic_value<TC>, error_info>
|
|
parse_value(location&, context<TC>& ctx);
|
|
|
|
template<typename TC>
|
|
result<std::pair<
|
|
std::pair<std::vector<typename basic_value<TC>::key_type>, region>,
|
|
basic_value<TC>
|
|
>, error_info>
|
|
parse_key_value_pair(location& loc, context<TC>& 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<typename TC>
|
|
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<typename T>
|
|
std::ostream& operator<<(std::ostream& os, const multiline_spacer<T>& sp)
|
|
{
|
|
os << "{newline=" << sp.newline_found << ", ";
|
|
os << "indent_type=" << sp.indent_type << ", ";
|
|
os << "indent=" << sp.indent << ", ";
|
|
os << "comments=" << sp.comments.size() << "}";
|
|
return os;
|
|
}
|
|
|
|
template<typename TC>
|
|
cxx::optional<multiline_spacer<TC>>
|
|
skip_multiline_spacer(location& loc, context<TC>& ctx, const bool newline_found = false)
|
|
{
|
|
const auto& spec = ctx.toml_spec();
|
|
|
|
multiline_spacer<TC> 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<location::char_type>(' '))).scan(loc))
|
|
{
|
|
spacer.indent_type = indent_char::space;
|
|
spacer.indent = static_cast<std::int32_t>(sp.length());
|
|
spacer_found = true;
|
|
}
|
|
else if(auto tabs = repeat_at_least(1, character(cxx::bit_cast<location::char_type>('\t'))).scan(loc))
|
|
{
|
|
spacer.indent_type = indent_char::tab;
|
|
spacer.indent = static_cast<std::int32_t>(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<typename TC>
|
|
result<basic_value<TC>, error_info>
|
|
parse_array(location& loc, context<TC>& 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<TC>::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<spacer.value().comments.size(); ++i)
|
|
{
|
|
elem.comments().push_back(std::move(spacer.value().comments.at(i)));
|
|
}
|
|
if(spacer.value().newline_found)
|
|
{
|
|
fmt.fmt = array_format::multiline;
|
|
}
|
|
}
|
|
|
|
comma_found = character(',').scan(loc).is_ok();
|
|
|
|
// parse comment after a comma
|
|
// array = [
|
|
// 42 , # the answer
|
|
// ^^^^^^^^^^^^
|
|
// 3.14 # pi
|
|
// ^^^^
|
|
// ]
|
|
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 = array_format::multiline;
|
|
elem.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 = array_format::multiline;
|
|
}
|
|
}
|
|
val.push_back(std::move(elem));
|
|
}
|
|
else
|
|
{
|
|
// if err, push error to ctx and try recovery.
|
|
ctx.report_error(std::move(elem_res.unwrap_err()));
|
|
|
|
// if it looks like some value, then skip the value.
|
|
// otherwise, it may be a new key-value pair or a new table and
|
|
// the error is "missing closing ]". stop parsing.
|
|
|
|
const auto before_skip = loc.get_location();
|
|
skip_value(loc, ctx);
|
|
if(before_skip == loc.get_location()) // cannot skip! break...
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
if(loc.current() != ']')
|
|
{
|
|
auto src = source_location(region(loc));
|
|
return err(make_error_info("toml::parse_array: missing closing bracket `]`",
|
|
std::move(src), "expected `]`, reached EOF"));
|
|
}
|
|
else
|
|
{
|
|
loc.advance();
|
|
}
|
|
// any error reported from this function
|
|
if(num_errors != ctx.errors().size())
|
|
{
|
|
assert(ctx.has_error()); // already reported
|
|
return err(ctx.errors().back());
|
|
}
|
|
|
|
return ok(basic_value<TC>(
|
|
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<typename TC>
|
|
result<basic_value<TC>*, error_info>
|
|
insert_value(const inserting_value_kind kind,
|
|
typename basic_value<TC>::table_type* current_table_ptr,
|
|
const std::vector<typename basic_value<TC>::key_type>& keys, region key_reg,
|
|
basic_value<TC> val)
|
|
{
|
|
using value_type = basic_value<TC>;
|
|
using array_type = typename basic_value<TC>::array_type;
|
|
using table_type = typename basic_value<TC>::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<keys.size(); ++i)
|
|
{
|
|
const auto& key = keys.at(i);
|
|
table_type& current_table = *current_table_ptr;
|
|
|
|
if(i+1 < keys.size()) // there are more keys. go down recursively...
|
|
{
|
|
const auto found = current_table.find(key);
|
|
if(found == current_table.end()) // not found. add new table
|
|
{
|
|
table_format_info fmt;
|
|
fmt.indent_type = indent_char::none;
|
|
if(kind == inserting_value_kind::dotted_keys)
|
|
{
|
|
fmt.fmt = table_format::dotted;
|
|
}
|
|
else // table / array of tables
|
|
{
|
|
fmt.fmt = table_format::implicit;
|
|
}
|
|
current_table.emplace(key, value_type(
|
|
table_type{}, fmt, std::vector<std::string>{}, 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::string>{}, 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<typename TC>
|
|
result<basic_value<TC>, error_info>
|
|
parse_inline_table(location& loc, context<TC>& ctx)
|
|
{
|
|
using table_type = typename basic_value<TC>::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<multiline_spacer<TC>> 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<TC>(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; i<spacer.value().comments.size(); ++i)
|
|
{
|
|
ins_res.unwrap()->comments().push_back(spacer.value().comments.at(i));
|
|
}
|
|
}
|
|
spacer = skip_multiline_spacer(loc, ctx);
|
|
if(spacer.has_value())
|
|
{
|
|
for(std::size_t i=0; i<spacer.value().comments.size(); ++i)
|
|
{
|
|
ins_res.unwrap()->comments().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<TC> retval(
|
|
std::move(table), std::move(fmt), {}, region(first, loc));
|
|
|
|
return ok(std::move(retval));
|
|
}
|
|
|
|
/* ============================================================================
|
|
* _
|
|
* __ ____ _| |_ _ ___
|
|
* \ V / _` | | || / -_)
|
|
* \_/\__,_|_|\_,_\___|
|
|
*/
|
|
|
|
template<typename TC>
|
|
result<value_t, error_info>
|
|
guess_number_type(const location& first, const context<TC>& 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<typename TC>
|
|
result<value_t, error_info>
|
|
guess_value_type(const location& loc, const context<TC>& 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<typename TC>
|
|
result<basic_value<TC>, error_info>
|
|
parse_value(location& loc, context<TC>& 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<typename TC>
|
|
result<std::pair<std::vector<typename basic_value<TC>::key_type>, region>, error_info>
|
|
parse_table_key(location& loc, context<TC>& 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<typename TC>
|
|
result<std::pair<std::vector<typename basic_value<TC>::key_type>, region>, error_info>
|
|
parse_array_table_key(location& loc, context<TC>& 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<typename TC>
|
|
result<none_t, error_info>
|
|
parse_table(location& loc, context<TC>& ctx, basic_value<TC>& 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<typename TC>
|
|
result<basic_value<TC>, std::vector<error_info>>
|
|
parse_file(location& loc, context<TC>& ctx)
|
|
{
|
|
using value_type = basic_value<TC>;
|
|
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<std::string> com;
|
|
if(sp.has_value())
|
|
{
|
|
for(std::size_t i=0; i<sp.value().comments.size(); ++i)
|
|
{
|
|
com.push_back(std::move(sp.value().comments.at(i)));
|
|
}
|
|
}
|
|
|
|
// [table.def] must be followed by one of
|
|
// - a comment line
|
|
// - whitespace + newline
|
|
// - EOF
|
|
if(auto com_res = parse_comment_line(loc, ctx))
|
|
{
|
|
if(auto com_opt = com_res.unwrap())
|
|
{
|
|
com.push_back(com_opt.value());
|
|
}
|
|
else // if there is no comment, ws+newline must exist (or EOF)
|
|
{
|
|
skip_whitespace(loc, ctx);
|
|
if( ! loc.eof() && ! syntax::newline(ctx.toml_spec()).scan(loc).is_ok())
|
|
{
|
|
ctx.report_error(make_syntax_error("toml::parse_file: "
|
|
"newline (or EOF) expected",
|
|
syntax::newline(ctx.toml_spec()), loc));
|
|
skip_until_next_table(loc, ctx);
|
|
continue;
|
|
}
|
|
}
|
|
}
|
|
else // comment syntax error (rare)
|
|
{
|
|
ctx.report_error(com_res.unwrap_err());
|
|
skip_until_next_table(loc, ctx);
|
|
continue;
|
|
}
|
|
|
|
table_format_info fmt;
|
|
fmt.fmt = table_format::multiline;
|
|
fmt.indent_type = indent_char::none;
|
|
auto tab = value_type(table_type{}, std::move(fmt), std::move(com), reg);
|
|
|
|
auto inserted = insert_value(inserting_value_kind::array_table,
|
|
std::addressof(root.as_table()),
|
|
key, std::move(reg), std::move(tab));
|
|
|
|
if(inserted.is_err())
|
|
{
|
|
ctx.report_error(inserted.unwrap_err());
|
|
|
|
// check errors in the table
|
|
auto tmp = basic_value<TC>(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<std::string> com;
|
|
if(sp.has_value())
|
|
{
|
|
for(std::size_t i=0; i<sp.value().comments.size(); ++i)
|
|
{
|
|
com.push_back(std::move(sp.value().comments.at(i)));
|
|
}
|
|
}
|
|
|
|
// [table.def] must be followed by one of
|
|
// - a comment line
|
|
// - whitespace + newline
|
|
// - EOF
|
|
if(auto com_res = parse_comment_line(loc, ctx))
|
|
{
|
|
if(auto com_opt = com_res.unwrap())
|
|
{
|
|
com.push_back(com_opt.value());
|
|
}
|
|
else // if there is no comment, ws+newline must exist (or EOF)
|
|
{
|
|
skip_whitespace(loc, ctx);
|
|
if( ! loc.eof() && ! syntax::newline(ctx.toml_spec()).scan(loc).is_ok())
|
|
{
|
|
ctx.report_error(make_syntax_error("toml::parse_file: "
|
|
"newline (or EOF) expected",
|
|
syntax::newline(ctx.toml_spec()), loc));
|
|
skip_until_next_table(loc, ctx);
|
|
continue;
|
|
}
|
|
}
|
|
}
|
|
else // comment syntax error (rare)
|
|
{
|
|
ctx.report_error(com_res.unwrap_err());
|
|
skip_until_next_table(loc, ctx);
|
|
continue;
|
|
}
|
|
|
|
table_format_info fmt;
|
|
fmt.fmt = table_format::multiline;
|
|
fmt.indent_type = indent_char::none;
|
|
auto tab = value_type(table_type{}, std::move(fmt), std::move(com), reg);
|
|
|
|
auto inserted = insert_value(inserting_value_kind::std_table,
|
|
std::addressof(root.as_table()),
|
|
key, std::move(reg), std::move(tab));
|
|
|
|
if(inserted.is_err())
|
|
{
|
|
ctx.report_error(inserted.unwrap_err());
|
|
|
|
// check errors in the table
|
|
auto tmp = basic_value<TC>(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<typename TC>
|
|
result<basic_value<TC>, std::vector<error_info>>
|
|
parse_impl(std::vector<location::char_type> cs, std::string fname, const spec& s)
|
|
{
|
|
using value_type = basic_value<TC>;
|
|
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::vector<location::char_type>>(std::move(cs));
|
|
location loc(std::move(src), std::move(fname));
|
|
return ok(value_type(table_type(), table_format_info{}, std::vector<std::string>{}, 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::vector<location::char_type>>(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<TC> ctx(s);
|
|
|
|
return parse_file(loc, ctx);
|
|
}
|
|
|
|
} // detail
|
|
|
|
// -----------------------------------------------------------------------------
|
|
// parse(byte array)
|
|
|
|
template<typename TC = type_config>
|
|
result<basic_value<TC>, std::vector<error_info>>
|
|
try_parse(std::vector<unsigned char> content, std::string filename,
|
|
spec s = spec::default_version())
|
|
{
|
|
return detail::parse_impl<TC>(std::move(content), std::move(filename), std::move(s));
|
|
}
|
|
template<typename TC = type_config>
|
|
basic_value<TC>
|
|
parse(std::vector<unsigned char> content, std::string filename,
|
|
spec s = spec::default_version())
|
|
{
|
|
auto res = try_parse<TC>(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<typename TC = type_config>
|
|
result<basic_value<TC>, std::vector<error_info>>
|
|
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<detail::location::char_type> letters(static_cast<std::size_t>(fsize), '\0');
|
|
is.read(reinterpret_cast<char*>(letters.data()), static_cast<std::streamsize>(fsize));
|
|
|
|
return detail::parse_impl<TC>(std::move(letters), std::move(fname), std::move(s));
|
|
}
|
|
|
|
template<typename TC = type_config>
|
|
basic_value<TC> parse(std::istream& is, std::string fname = "unknown file", spec s = spec::default_version())
|
|
{
|
|
auto res = try_parse<TC>(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<typename TC = type_config>
|
|
result<basic_value<TC>, std::vector<error_info>>
|
|
try_parse(std::string fname, spec s = spec::default_version())
|
|
{
|
|
std::ifstream ifs(fname, std::ios_base::binary);
|
|
if(!ifs.good())
|
|
{
|
|
std::vector<error_info> 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<TC>(ifs, std::move(fname), std::move(s));
|
|
}
|
|
|
|
template<typename TC = type_config>
|
|
basic_value<TC> 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<TC>(ifs, std::move(fname), std::move(s));
|
|
}
|
|
|
|
template<typename TC = type_config, std::size_t N>
|
|
result<basic_value<TC>, std::vector<error_info>>
|
|
try_parse(const char (&fname)[N], spec s = spec::default_version())
|
|
{
|
|
return try_parse<TC>(std::string(fname), std::move(s));
|
|
}
|
|
|
|
template<typename TC = type_config, std::size_t N>
|
|
basic_value<TC> parse(const char (&fname)[N], spec s = spec::default_version())
|
|
{
|
|
return parse<TC>(std::string(fname), std::move(s));
|
|
}
|
|
|
|
// ----------------------------------------------------------------------------
|
|
// parse_str
|
|
|
|
template<typename TC = type_config>
|
|
result<basic_value<TC>, std::vector<error_info>>
|
|
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<TC>(iss, std::move(name), std::move(s));
|
|
}
|
|
|
|
template<typename TC = type_config>
|
|
basic_value<TC> parse_str(std::string content, spec s = spec::default_version(),
|
|
cxx::source_location loc = cxx::source_location::current())
|
|
{
|
|
auto res = try_parse_str<TC>(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<typename TC = type_config, typename FSPATH>
|
|
cxx::enable_if_t<std::is_same<FSPATH, std::filesystem::path>::value,
|
|
result<basic_value<TC>, std::vector<error_info>>>
|
|
try_parse(const FSPATH& fpath, spec s = spec::default_version())
|
|
{
|
|
std::ifstream ifs(fpath, std::ios_base::binary);
|
|
if(!ifs.good())
|
|
{
|
|
std::vector<error_info> 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<TC>(ifs, fpath.string(), std::move(s));
|
|
}
|
|
|
|
template<typename TC = type_config, typename FSPATH>
|
|
cxx::enable_if_t<std::is_same<FSPATH, std::filesystem::path>::value,
|
|
basic_value<TC>>
|
|
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<TC>(ifs, fpath.string(), std::move(s));
|
|
}
|
|
#endif
|
|
|
|
// -----------------------------------------------------------------------------
|
|
// FILE*
|
|
|
|
template<typename TC = type_config>
|
|
result<basic_value<TC>, std::vector<error_info>>
|
|
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>{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>{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>{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>{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<detail::location::char_type> letters(static_cast<std::size_t>(fsize));
|
|
const auto actual = std::fread(letters.data(), sizeof(char), static_cast<std::size_t>(fsize), fp);
|
|
if(actual != static_cast<std::size_t>(fsize))
|
|
{
|
|
return err(std::vector<error_info>{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<TC>(std::move(letters), std::move(filename), std::move(s));
|
|
}
|
|
|
|
template<typename TC = type_config>
|
|
basic_value<TC>
|
|
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<detail::location::char_type> letters(static_cast<std::size_t>(fsize));
|
|
const auto actual = std::fread(letters.data(), sizeof(char), static_cast<std::size_t>(fsize), fp);
|
|
if(actual != static_cast<std::size_t>(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<TC>(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<basic_value<type_config>, std::vector<error_info>> try_parse<type_config>(std::vector<unsigned char>, std::string, spec);
|
|
extern template result<basic_value<type_config>, std::vector<error_info>> try_parse<type_config>(std::istream&, std::string, spec);
|
|
extern template result<basic_value<type_config>, std::vector<error_info>> try_parse<type_config>(std::string, spec);
|
|
extern template result<basic_value<type_config>, std::vector<error_info>> try_parse<type_config>(FILE*, std::string, spec);
|
|
extern template result<basic_value<type_config>, std::vector<error_info>> try_parse_str<type_config>(std::string, spec, cxx::source_location);
|
|
|
|
extern template basic_value<type_config> parse<type_config>(std::vector<unsigned char>, std::string, spec);
|
|
extern template basic_value<type_config> parse<type_config>(std::istream&, std::string, spec);
|
|
extern template basic_value<type_config> parse<type_config>(std::string, spec);
|
|
extern template basic_value<type_config> parse<type_config>(FILE*, std::string, spec);
|
|
extern template basic_value<type_config> parse_str<type_config>(std::string, spec, cxx::source_location);
|
|
|
|
extern template result<basic_value<ordered_type_config>, std::vector<error_info>> try_parse<ordered_type_config>(std::vector<unsigned char>, std::string, spec);
|
|
extern template result<basic_value<ordered_type_config>, std::vector<error_info>> try_parse<ordered_type_config>(std::istream&, std::string, spec);
|
|
extern template result<basic_value<ordered_type_config>, std::vector<error_info>> try_parse<ordered_type_config>(std::string, spec);
|
|
extern template result<basic_value<ordered_type_config>, std::vector<error_info>> try_parse<ordered_type_config>(FILE*, std::string, spec);
|
|
extern template result<basic_value<ordered_type_config>, std::vector<error_info>> try_parse_str<ordered_type_config>(std::string, spec, cxx::source_location);
|
|
|
|
extern template basic_value<ordered_type_config> parse<ordered_type_config>(std::vector<unsigned char>, std::string, spec);
|
|
extern template basic_value<ordered_type_config> parse<ordered_type_config>(std::istream&, std::string, spec);
|
|
extern template basic_value<ordered_type_config> parse<ordered_type_config>(std::string, spec);
|
|
extern template basic_value<ordered_type_config> parse<ordered_type_config>(FILE*, std::string, spec);
|
|
extern template basic_value<ordered_type_config> parse_str<ordered_type_config>(std::string, spec, cxx::source_location);
|
|
|
|
#if defined(TOML11_HAS_FILESYSTEM)
|
|
extern template cxx::enable_if_t<std::is_same<std::filesystem::path, std::filesystem::path>::value, result<basic_value<type_config>, std::vector<error_info>>> try_parse<type_config, std::filesystem::path>(const std::filesystem::path&, spec);
|
|
extern template cxx::enable_if_t<std::is_same<std::filesystem::path, std::filesystem::path>::value, result<basic_value<ordered_type_config>, std::vector<error_info>>> try_parse<ordered_type_config, std::filesystem::path>(const std::filesystem::path&, spec);
|
|
extern template cxx::enable_if_t<std::is_same<std::filesystem::path, std::filesystem::path>::value, basic_value<type_config> > parse <type_config, std::filesystem::path>(const std::filesystem::path&, spec);
|
|
extern template cxx::enable_if_t<std::is_same<std::filesystem::path, std::filesystem::path>::value, basic_value<ordered_type_config> > parse <ordered_type_config, std::filesystem::path>(const std::filesystem::path&, spec);
|
|
#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<type_config> 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<sp.value().comments.size(); ++i)
|
|
{
|
|
val.comments().push_back(std::move(sp.value().comments.at(i)));
|
|
}
|
|
}
|
|
return val;
|
|
}
|
|
|
|
// to distinguish arrays and tables, first check it is a table or not.
|
|
//
|
|
// "[1,2,3]"_toml; // json: [1, 2, 3]
|
|
// "[table]"_toml; // json: {"table": {}}
|
|
// "[[1,2,3]]"_toml; // json: [[1, 2, 3]]
|
|
// "[[table]]"_toml; // json: {"table": [{}]}
|
|
//
|
|
// "[[1]]"_toml; // json: {"1": [{}]}
|
|
// "1 = [{}]"_toml; // json: {"1": [{}]}
|
|
// "[[1,]]"_toml; // json: [[1]]
|
|
// "[[1],]"_toml; // json: [[1]]
|
|
const auto val_start = loc;
|
|
|
|
const bool is_table_key = syntax::std_table(s).scan(loc).is_ok();
|
|
loc = val_start;
|
|
const bool is_aots_key = syntax::array_table(s).scan(loc).is_ok();
|
|
loc = val_start;
|
|
|
|
// 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)
|
|
{
|
|
auto data = parse_value(loc, ctx);
|
|
if(data.is_ok())
|
|
{
|
|
auto val = std::move(data.unwrap());
|
|
if(sp.has_value())
|
|
{
|
|
for(std::size_t i=0; i<sp.value().comments.size(); ++i)
|
|
{
|
|
val.comments().push_back(std::move(sp.value().comments.at(i)));
|
|
}
|
|
}
|
|
auto com_res = parse_comment_line(loc, ctx);
|
|
if(com_res.is_ok() && com_res.unwrap().has_value())
|
|
{
|
|
val.comments().push_back(com_res.unwrap().value());
|
|
}
|
|
return val;
|
|
}
|
|
}
|
|
|
|
// -------------------------------------------------------------------------
|
|
// Note that still it can be a table, because the literal might be something
|
|
// like the following.
|
|
// ```cpp
|
|
// // c++11 raw-string literal
|
|
// const auto val = R"(
|
|
// key = "value"
|
|
// int = 42
|
|
// )"_toml;
|
|
// ```
|
|
// It is a valid toml file.
|
|
// It should be parsed as if we parse a file with this content.
|
|
|
|
loc = front;
|
|
auto data = parse_file(loc, ctx);
|
|
if(data.is_ok())
|
|
{
|
|
return data.unwrap();
|
|
}
|
|
else // not a value && not a file. error.
|
|
{
|
|
std::string msg;
|
|
for(const auto& err : data.unwrap_err())
|
|
{
|
|
msg += format_error(err);
|
|
}
|
|
throw ::toml::syntax_error(std::move(msg), std::move(data.unwrap_err()));
|
|
}
|
|
}
|
|
|
|
} // detail
|
|
|
|
inline namespace literals
|
|
{
|
|
inline namespace toml_literals
|
|
{
|
|
|
|
TOML11_INLINE ::toml::value
|
|
operator"" _toml(const char* str, std::size_t len)
|
|
{
|
|
if(len == 0)
|
|
{
|
|
return ::toml::value{};
|
|
}
|
|
|
|
::toml::detail::location::container_type c(len);
|
|
std::copy(reinterpret_cast<const ::toml::detail::location::char_type*>(str),
|
|
reinterpret_cast<const ::toml::detail::location::char_type*>(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<const toml::detail::location::container_type>(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<const ::toml::detail::location::char_type*>(str),
|
|
reinterpret_cast<const ::toml::detail::location::char_type*>(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<const toml::detail::location::container_type>(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 <iomanip>
|
|
#include <iterator>
|
|
#include <sstream>
|
|
|
|
#include <cmath>
|
|
#include <cstdio>
|
|
|
|
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<typename TC>
|
|
class serializer
|
|
{
|
|
public:
|
|
|
|
using value_type = basic_value<TC>;
|
|
|
|
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<key_type>& 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<string_type>("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<string_type>("true");
|
|
}
|
|
else
|
|
{
|
|
return string_conv<string_type>("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<int>(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<int>(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<int>(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<string_type>(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<string_type>(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<string_type>(oss.str());
|
|
}
|
|
|
|
switch(fmt.fmt)
|
|
{
|
|
case floating_format::defaultfloat:
|
|
{
|
|
if(fmt.prec != 0)
|
|
{
|
|
oss << std::setprecision(static_cast<int>(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<string_type>(s);
|
|
}
|
|
case floating_format::fixed:
|
|
{
|
|
if(fmt.prec != 0)
|
|
{
|
|
oss << std::setprecision(static_cast<int>(fmt.prec));
|
|
}
|
|
oss << std::fixed << f;
|
|
if(this->spec_.ext_num_suffix && ! fmt.suffix.empty())
|
|
{
|
|
oss << '_' << fmt.suffix;
|
|
}
|
|
return string_conv<string_type>(oss.str());
|
|
}
|
|
case floating_format::scientific:
|
|
{
|
|
if(fmt.prec != 0)
|
|
{
|
|
oss << std::setprecision(static_cast<int>(fmt.prec));
|
|
}
|
|
oss << std::scientific << f;
|
|
if(this->spec_.ext_num_suffix && ! fmt.suffix.empty())
|
|
{
|
|
oss << '_' << fmt.suffix;
|
|
}
|
|
return string_conv<string_type>(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<string_type>(oss.str());
|
|
}
|
|
else // no hex allowed. output with max precision.
|
|
{
|
|
oss << std::setprecision(std::numeric_limits<floating_type>::max_digits10)
|
|
<< std::scientific << f;
|
|
// suffix is only for decimal numbers.
|
|
return string_conv<string_type>(oss.str());
|
|
}
|
|
}
|
|
default:
|
|
{
|
|
if(this->spec_.ext_num_suffix && ! fmt.suffix.empty())
|
|
{
|
|
oss << '_' << fmt.suffix;
|
|
}
|
|
return string_conv<string_type>(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<string_type>("\"\"\"");
|
|
if(fmt.start_with_newline)
|
|
{
|
|
retval += char_type('\n');
|
|
}
|
|
|
|
retval += this->escape_ml_basic_string(s);
|
|
|
|
retval += string_conv<string_type>("\"\"\"");
|
|
return retval;
|
|
}
|
|
case string_format::multiline_literal:
|
|
{
|
|
retval += string_conv<string_type>("'''");
|
|
if(fmt.start_with_newline)
|
|
{
|
|
retval += char_type('\n');
|
|
}
|
|
retval += s;
|
|
retval += string_conv<string_type>("'''");
|
|
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<string_type>(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<string_type>(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<std::string>(this->format_local_time(odt.time, fmt.has_seconds, fmt.subsecond_precision));
|
|
oss << odt.offset;
|
|
return string_conv<string_type>(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<string_type>("[[");
|
|
retval += this->format_keys(this->keys_).value();
|
|
retval += string_conv<string_type>("]]\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<string_type>(", ");
|
|
}
|
|
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<string_type>("[\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<string_type>(",\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<string_type>("]\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<string_type> 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<string_type>("\\\\"); break;}
|
|
case char_type('\"'): {retval += string_conv<string_type>("\\\""); break;}
|
|
case char_type('\b'): {retval += string_conv<string_type>("\\b" ); break;}
|
|
case char_type('\t'): {retval += string_conv<string_type>("\\t" ); break;}
|
|
case char_type('\f'): {retval += string_conv<string_type>("\\f" ); break;}
|
|
case char_type('\n'): {retval += string_conv<string_type>("\\n" ); break;}
|
|
case char_type('\r'): {retval += string_conv<string_type>("\\r" ); break;}
|
|
default :
|
|
{
|
|
if(c == char_type(0x1B) && spec_.v1_1_0_add_escape_sequence_e)
|
|
{
|
|
retval += string_conv<string_type>("\\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<string_type>("\\x");
|
|
}
|
|
else
|
|
{
|
|
retval += string_conv<string_type>("\\u00");
|
|
}
|
|
const auto c1 = c / 16;
|
|
const auto c2 = c % 16;
|
|
retval += static_cast<char_type>('0' + c1);
|
|
if(c2 < 10)
|
|
{
|
|
retval += static_cast<char_type>('0' + c2);
|
|
}
|
|
else // 10 <= c2
|
|
{
|
|
retval += static_cast<char_type>('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<string_type>("\\\\"); break;}
|
|
case char_type('\b'): {retval += string_conv<string_type>("\\b" ); break;}
|
|
case char_type('\t'): {retval += string_conv<string_type>("\\t" ); break;}
|
|
case char_type('\f'): {retval += string_conv<string_type>("\\f" ); break;}
|
|
case char_type('\n'): {retval += string_conv<string_type>("\n" ); break;}
|
|
case char_type('\r'): {retval += string_conv<string_type>("\\r" ); break;}
|
|
default :
|
|
{
|
|
if(c == char_type(0x1B) && spec_.v1_1_0_add_escape_sequence_e)
|
|
{
|
|
retval += string_conv<string_type>("\\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<string_type>("\\x");
|
|
}
|
|
else
|
|
{
|
|
retval += string_conv<string_type>("\\u00");
|
|
}
|
|
const auto c1 = c / 16;
|
|
const auto c2 = c % 16;
|
|
retval += static_cast<char_type>('0' + c1);
|
|
if(c2 < 10)
|
|
{
|
|
retval += static_cast<char_type>('0' + c2);
|
|
}
|
|
else // 10 <= c2
|
|
{
|
|
retval += static_cast<char_type>('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<string_type>("\"\"\""));
|
|
while(found_3_quotes != string_type::npos)
|
|
{
|
|
retval.replace(found_3_quotes, 3, string_conv<string_type>("\"\"\\\""));
|
|
found_3_quotes = retval.find(string_conv<string_type>("\"\"\""));
|
|
}
|
|
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<int>(t.hour);
|
|
oss << ':';
|
|
oss << std::setfill('0') << std::setw(2) << static_cast<int>(t.minute);
|
|
if(has_seconds)
|
|
{
|
|
oss << ':';
|
|
oss << std::setfill('0') << std::setw(2) << static_cast<int>(t.second);
|
|
if(subsec_prec != 0)
|
|
{
|
|
std::ostringstream subsec;
|
|
subsec << std::setfill('0') << std::setw(3) << static_cast<int>(t.millisecond);
|
|
subsec << std::setfill('0') << std::setw(3) << static_cast<int>(t.microsecond);
|
|
subsec << std::setfill('0') << std::setw(3) << static_cast<int>(t.nanosecond);
|
|
std::string subsec_str = subsec.str();
|
|
oss << '.' << subsec_str.substr(0, subsec_prec);
|
|
}
|
|
}
|
|
return string_conv<string_type>(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<string_type>(" = ");
|
|
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<string_type>(" = ");
|
|
retval += (*this)(kv.second);
|
|
retval += string_conv<string_type>(", ");
|
|
}
|
|
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<string_type>("{\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<string_type>(" = ");
|
|
|
|
this->force_inline_ = true;
|
|
retval += (*this)(kv.second);
|
|
|
|
retval += string_conv<string_type>(",\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<string_type>& 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<string_type>(" = ");
|
|
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<string_type>("\"\"");
|
|
}
|
|
|
|
// check the key can be a bare (unquoted) key
|
|
auto loc = detail::make_temporary_location(string_conv<std::string>(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<string_type>("\"");
|
|
for(const char_type c : key)
|
|
{
|
|
switch(c)
|
|
{
|
|
case char_type('\\'): {formatted += string_conv<string_type>("\\\\"); break;}
|
|
case char_type('\"'): {formatted += string_conv<string_type>("\\\""); break;}
|
|
case char_type('\b'): {formatted += string_conv<string_type>("\\b" ); break;}
|
|
case char_type('\t'): {formatted += string_conv<string_type>("\\t" ); break;}
|
|
case char_type('\f'): {formatted += string_conv<string_type>("\\f" ); break;}
|
|
case char_type('\n'): {formatted += string_conv<string_type>("\\n" ); break;}
|
|
case char_type('\r'): {formatted += string_conv<string_type>("\\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<string_type>("\\x");
|
|
}
|
|
else
|
|
{
|
|
formatted += string_conv<string_type>("\\u00");
|
|
}
|
|
const auto c1 = c / 16;
|
|
const auto c2 = c % 16;
|
|
formatted += static_cast<char_type>('0' + c1);
|
|
if(c2 < 10)
|
|
{
|
|
formatted += static_cast<char_type>('0' + c2);
|
|
}
|
|
else // 10 <= c2
|
|
{
|
|
formatted += static_cast<char_type>('A' + (c2 - 10));
|
|
}
|
|
}
|
|
else
|
|
{
|
|
formatted += c;
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
formatted += string_conv<string_type>("\"");
|
|
return formatted;
|
|
} // }}}
|
|
cxx::optional<string_type> format_keys(const std::vector<key_type>& 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>("");
|
|
} // }}}
|
|
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<string_type>(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::size_t>((std::max)(0, this->current_indent_));
|
|
if(indent_type == indent_char::space)
|
|
{
|
|
return string_conv<string_type>(make_string(indent, ' '));
|
|
}
|
|
else if(indent_type == indent_char::tab)
|
|
{
|
|
return string_conv<string_type>(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<key_type> keys_;
|
|
};
|
|
} // detail
|
|
|
|
template<typename TC>
|
|
typename basic_value<TC>::string_type
|
|
format(const basic_value<TC>& v, const spec s = spec::default_version())
|
|
{
|
|
detail::serializer<TC> ser(s);
|
|
return ser(v);
|
|
}
|
|
template<typename TC>
|
|
typename basic_value<TC>::string_type
|
|
format(const typename basic_value<TC>::key_type& k,
|
|
const basic_value<TC>& v,
|
|
const spec s = spec::default_version())
|
|
{
|
|
detail::serializer<TC> ser(s);
|
|
return ser(k, v);
|
|
}
|
|
template<typename TC>
|
|
typename basic_value<TC>::string_type
|
|
format(const std::vector<typename basic_value<TC>::key_type>& ks,
|
|
const basic_value<TC>& v,
|
|
const spec s = spec::default_version())
|
|
{
|
|
detail::serializer<TC> ser(s);
|
|
return ser(ks, v);
|
|
}
|
|
|
|
template<typename TC>
|
|
std::ostream& operator<<(std::ostream& os, const basic_value<TC>& 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<type_config>::string_type
|
|
format<type_config>(const basic_value<type_config>&, const spec);
|
|
|
|
extern template typename basic_value<type_config>::string_type
|
|
format<type_config>(const typename basic_value<type_config>::key_type& k,
|
|
const basic_value<type_config>& v, const spec);
|
|
|
|
extern template typename basic_value<type_config>::string_type
|
|
format<type_config>(const std::vector<typename basic_value<type_config>::key_type>& ks,
|
|
const basic_value<type_config>& v, const spec s);
|
|
|
|
extern template typename basic_value<type_config>::string_type
|
|
format<ordered_type_config>(const basic_value<ordered_type_config>&, const spec);
|
|
|
|
extern template typename basic_value<type_config>::string_type
|
|
format<ordered_type_config>(const typename basic_value<ordered_type_config>::key_type& k,
|
|
const basic_value<ordered_type_config>& v, const spec);
|
|
|
|
extern template typename basic_value<type_config>::string_type
|
|
format<ordered_type_config>(const std::vector<typename basic_value<ordered_type_config>::key_type>& ks,
|
|
const basic_value<ordered_type_config>& 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
|