added iterators for tables and arrays
also: - fixed parsing table headers allowing invalid characters - fixed implicit fallthrough warnings - fixed some issues parsing dates and times - added `table::erase` - added `array::operator[]` - added `value::operator*` - added stream operators for date, time and date_time - added `impl::print_to_stream` - added more parsing diagnostics - added many tests
This commit is contained in:
parent
40ffee43fb
commit
537eb30080
@ -30,6 +30,7 @@ toml++.
|
||||
| `TOML_ASSERT(expr)` | function macro | `assert(expr)`<br>(or undefined) | Sets the assert function used by the library. |
|
||||
| `TOML_CHAR_8_STRINGS` | boolean | `0` | Uses C++20 [char8_t]-based strings as the toml string data type. |
|
||||
| `TOML_CONFIG_HEADER` | string literal | undefined | Includes the given header file before the rest of the library. |
|
||||
| `TOML_LARGE_FILES` | boolean | `0` | Uses 32-bit integers for line and column indices (instead of 16-bit). |
|
||||
| `TOML_UNDEF_MACROS` | boolean | `1` | `#undefs` the library's internal macros at the end of the header. |
|
||||
| `TOML_UNRELEASED_FEATURES` | boolean | `1` | Enables support for [unreleased TOML language features] not yet part of a [numbered version]. |
|
||||
|
||||
|
@ -10,6 +10,7 @@ int2 = 9223372036854775807
|
||||
flt1 = 0.00000000001
|
||||
flt2 = 1e-11
|
||||
flt3 = 11.0
|
||||
flt4 = +1.0
|
||||
|
||||
# hexadecimal with prefix `0x`
|
||||
hex1 = 0xDEADBEEF
|
||||
|
@ -27,7 +27,7 @@ int main(int argc, char** argv)
|
||||
try
|
||||
{
|
||||
const auto table = toml::parse(file, std::move(path));
|
||||
std::cout << table << std::endl;
|
||||
std::cout << toml::json_formatter{ table } << std::endl;
|
||||
}
|
||||
catch (const toml::parse_error & err)
|
||||
{
|
||||
@ -49,7 +49,7 @@ int main(int argc, char** argv)
|
||||
const auto table = toml::parse(std::cin);
|
||||
std::cout << toml::json_formatter{ table } << std::endl;
|
||||
}
|
||||
catch (const toml::parse_error & err)
|
||||
catch (const toml::parse_error& err)
|
||||
{
|
||||
std::cerr
|
||||
<< "Error parsing stdin:\n"sv << err.what()
|
||||
|
@ -5,6 +5,7 @@
|
||||
//# is used as the source for generate_single_header.py.
|
||||
|
||||
#include "toml_common.h"
|
||||
#include "toml_print_to_stream.h"
|
||||
#include "toml_node.h"
|
||||
#include "toml_table.h"
|
||||
#include "toml_array.h"
|
||||
@ -20,10 +21,10 @@
|
||||
#if TOML_UNDEF_MACROS
|
||||
#undef TOML_EXCEPTIONS
|
||||
#undef TOML_USE_STREAMS_FOR_FLOATS
|
||||
#undef TOML_GCC_ATTR
|
||||
#undef TOML_PUSH_WARNINGS
|
||||
#undef TOML_DISABLE_SWITCH_WARNING
|
||||
#undef TOML_DISABLE_FIELD_INIT_WARNING
|
||||
#undef TOML_DISABLE_VAR_INIT_WARNING
|
||||
#undef TOML_DISABLE_SWITCH_WARNINGS
|
||||
#undef TOML_DISABLE_INIT_WARNINGS
|
||||
#undef TOML_DISABLE_ALL_WARNINGS
|
||||
#undef TOML_POP_WARNINGS
|
||||
#undef TOML_ALWAYS_INLINE
|
||||
@ -31,6 +32,7 @@
|
||||
#undef TOML_UNREACHABLE
|
||||
#undef TOML_INTERFACE
|
||||
#undef TOML_EMPTY_BASES
|
||||
#undef TOML_CPP_VERSION
|
||||
#undef TOML_CPP
|
||||
#undef TOML_CONDITIONAL_NOEXCEPT
|
||||
#undef TOML_MAY_THROW
|
||||
|
@ -1,6 +1,82 @@
|
||||
#pragma once
|
||||
#include "toml_node.h"
|
||||
|
||||
namespace toml::impl
|
||||
{
|
||||
template <bool is_const>
|
||||
class array_iterator final
|
||||
{
|
||||
private:
|
||||
friend class toml::array;
|
||||
|
||||
using raw_iterator = std::conditional_t<
|
||||
is_const,
|
||||
std::vector<std::unique_ptr<node>>::const_iterator,
|
||||
std::vector<std::unique_ptr<node>>::iterator
|
||||
>;
|
||||
|
||||
mutable raw_iterator raw_;
|
||||
|
||||
array_iterator(const raw_iterator& raw) noexcept
|
||||
: raw_{ raw }
|
||||
{}
|
||||
|
||||
array_iterator(raw_iterator&& raw) noexcept
|
||||
: raw_{ std::move(raw) }
|
||||
{}
|
||||
|
||||
public:
|
||||
|
||||
array_iterator() noexcept = default;
|
||||
|
||||
using reference = std::conditional_t<is_const, const node&, node&>;
|
||||
|
||||
array_iterator& operator++() noexcept // ++pre
|
||||
{
|
||||
++raw_;
|
||||
return *this;
|
||||
}
|
||||
|
||||
array_iterator operator++(int) noexcept // post++
|
||||
{
|
||||
array_iterator out{ raw_ };
|
||||
++raw_;
|
||||
return out;
|
||||
}
|
||||
|
||||
array_iterator& operator--() noexcept // --pre
|
||||
{
|
||||
--raw_;
|
||||
return *this;
|
||||
}
|
||||
|
||||
array_iterator operator--(int) noexcept // post--
|
||||
{
|
||||
array_iterator out{ raw_ };
|
||||
--raw_;
|
||||
return out;
|
||||
}
|
||||
|
||||
[[nodiscard]]
|
||||
reference operator* () const noexcept
|
||||
{
|
||||
return *raw_->get();
|
||||
}
|
||||
|
||||
[[nodiscard]]
|
||||
friend constexpr bool operator == (const array_iterator& lhs, const array_iterator& rhs) noexcept
|
||||
{
|
||||
return lhs.raw_ == rhs.raw_;
|
||||
}
|
||||
|
||||
[[nodiscard]]
|
||||
friend constexpr bool operator != (const array_iterator& lhs, const array_iterator& rhs) noexcept
|
||||
{
|
||||
return lhs.raw_ != rhs.raw_;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
namespace toml
|
||||
{
|
||||
class array final
|
||||
@ -12,6 +88,9 @@ namespace toml
|
||||
|
||||
public:
|
||||
|
||||
using iterator = impl::array_iterator<false>;
|
||||
using const_iterator = impl::array_iterator<true>;
|
||||
|
||||
TOML_NODISCARD_CTOR
|
||||
array() noexcept = default;
|
||||
|
||||
@ -27,8 +106,12 @@ namespace toml
|
||||
values = std::move(rhs.values);
|
||||
return *this;
|
||||
}
|
||||
|
||||
[[nodiscard]] node_type type() const noexcept override { return node_type::array; }
|
||||
|
||||
[[nodiscard]] bool is_table() const noexcept override { return false; }
|
||||
[[nodiscard]] bool is_array() const noexcept override { return true; }
|
||||
[[nodiscard]] bool is_value() const noexcept override { return false; }
|
||||
|
||||
[[nodiscard]] bool is_array_of_tables() const noexcept override
|
||||
{
|
||||
@ -42,40 +125,56 @@ namespace toml
|
||||
return true;
|
||||
}
|
||||
|
||||
template <typename T = void>
|
||||
[[nodiscard]] bool is_homogeneous() const noexcept
|
||||
{
|
||||
if (values.size() <= 1_sz)
|
||||
return true;
|
||||
if (values.empty())
|
||||
return false;
|
||||
|
||||
const auto type = values[0]->type();
|
||||
for (size_t i = 1; i < values.size(); i++)
|
||||
if (values[i]->type() != type)
|
||||
return false;
|
||||
if constexpr (std::is_same_v<T, void>)
|
||||
{
|
||||
const auto type = values[0]->type();
|
||||
for (size_t i = 1; i < values.size(); i++)
|
||||
if (values[i]->type() != type)
|
||||
return false;
|
||||
}
|
||||
else
|
||||
{
|
||||
for (auto& v : values)
|
||||
if (!v->is<T>())
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
[[nodiscard]] array* as_array() noexcept override { return this; }
|
||||
[[nodiscard]] const array* as_array() const noexcept override { return this; }
|
||||
|
||||
[[nodiscard]] node_type type() const noexcept override { return node_type::array; }
|
||||
|
||||
[[nodiscard]] bool empty() const noexcept { return values.empty(); }
|
||||
|
||||
[[nodiscard]] size_t size() const noexcept { return values.size(); }
|
||||
|
||||
[[nodiscard]] node* get(size_t index) noexcept { return values[index].get(); }
|
||||
[[nodiscard]] const node* get(size_t index) const noexcept { return values[index].get(); }
|
||||
[[nodiscard]] node& operator[] (size_t index) & noexcept { return *values[index].get(); }
|
||||
[[nodiscard]] node&& operator[] (size_t index) && noexcept { return std::move(*values[index].get()); }
|
||||
[[nodiscard]] const node& operator[] (size_t index) const& noexcept { return *values[index].get(); }
|
||||
|
||||
template <typename T>
|
||||
[[nodiscard]] node_of<T>* get_as(size_t index) noexcept
|
||||
{
|
||||
return get(index)->as<T>();
|
||||
return values[index]->as<T>();
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
[[nodiscard]] const node_of<T>* get_as(size_t index) const noexcept
|
||||
{
|
||||
return get(index)->as<T>();
|
||||
return values[index]->as<T>();
|
||||
}
|
||||
|
||||
[[nodiscard]] iterator begin() noexcept { return { values.begin() }; }
|
||||
[[nodiscard]] const_iterator begin() const noexcept { return { values.begin() }; }
|
||||
[[nodiscard]] const_iterator cbegin() const noexcept { return { values.cbegin() }; }
|
||||
|
||||
[[nodiscard]] iterator end() noexcept { return { values.end() }; }
|
||||
[[nodiscard]] const_iterator end() const noexcept { return { values.end() }; }
|
||||
[[nodiscard]] const_iterator cend() const noexcept { return { values.cend() }; }
|
||||
};
|
||||
}
|
||||
|
@ -16,6 +16,10 @@
|
||||
#define TOML_UNRELEASED_FEATURES 1
|
||||
#endif
|
||||
|
||||
#ifndef TOML_LARGE_FILES
|
||||
#define TOML_LARGE_FILES 0
|
||||
#endif
|
||||
|
||||
#ifndef TOML_ASSERT
|
||||
#ifdef assert
|
||||
#define TOML_ASSERT(expr) assert(expr)
|
||||
@ -34,6 +38,12 @@
|
||||
#error toml++ is a C++ library.
|
||||
#endif
|
||||
|
||||
#if defined(__clang__) or defined(__GNUC__)
|
||||
#define TOML_GCC_ATTR(attr) __attribute__((attr))
|
||||
#else
|
||||
#define TOML_GCC_ATTR(attr)
|
||||
#endif
|
||||
|
||||
#ifdef __clang__
|
||||
|
||||
#ifndef __cpp_exceptions
|
||||
@ -41,11 +51,11 @@
|
||||
#endif
|
||||
|
||||
#define TOML_PUSH_WARNINGS _Pragma("clang diagnostic push")
|
||||
#define TOML_DISABLE_SWITCH_WARNING _Pragma("clang diagnostic ignored \"-Wswitch\"")
|
||||
#define TOML_DISABLE_FIELD_INIT_WARNING _Pragma("clang diagnostic ignored \"-Wmissing-field-initializers\"")
|
||||
#define TOML_DISABLE_SWITCH_WARNINGS _Pragma("clang diagnostic ignored \"-Wswitch\"")
|
||||
#define TOML_DISABLE_INIT_WARNINGS _Pragma("clang diagnostic ignored \"-Wmissing-field-initializers\"")
|
||||
#define TOML_DISABLE_ALL_WARNINGS _Pragma("clang diagnostic ignored \"-Weverything\"")
|
||||
#define TOML_POP_WARNINGS _Pragma("clang diagnostic pop")
|
||||
#define TOML_ALWAYS_INLINE __attribute__((__always_inline__)) inline
|
||||
#define TOML_ALWAYS_INLINE TOML_GCC_ATTR(__always_inline__) inline
|
||||
#define TOML_ASSUME(cond) __builtin_assume(cond)
|
||||
#define TOML_UNREACHABLE __builtin_unreachable()
|
||||
|
||||
@ -70,7 +80,7 @@
|
||||
|
||||
#define TOML_CPP_VERSION _MSVC_LANG
|
||||
#define TOML_PUSH_WARNINGS __pragma(warning(push))
|
||||
#define TOML_DISABLE_SWITCH_WARNING __pragma(warning(disable: 4063))
|
||||
#define TOML_DISABLE_SWITCH_WARNINGS __pragma(warning(disable: 4063))
|
||||
#define TOML_DISABLE_ALL_WARNINGS __pragma(warning(pop)) \
|
||||
__pragma(warning(push, 0))
|
||||
#define TOML_POP_WARNINGS __pragma(warning(pop))
|
||||
@ -87,16 +97,16 @@
|
||||
#endif
|
||||
|
||||
#define TOML_PUSH_WARNINGS _Pragma("GCC diagnostic push")
|
||||
#define TOML_DISABLE_SWITCH_WARNING _Pragma("GCC diagnostic ignored \"-Wswitch\"")
|
||||
#define TOML_DISABLE_FIELD_INIT_WARNING _Pragma("GCC diagnostic ignored \"-Wmissing-field-initializers\"")
|
||||
#define TOML_DISABLE_VAR_INIT_WARNING _Pragma("GCC diagnostic ignored \"-Wmaybe-uninitialized\"")
|
||||
#define TOML_DISABLE_ALL_WARNINGS _Pragma("GCC diagnostic ignored \"-Wall\"") \
|
||||
_Pragma("GCC diagnostic ignored \"-Wextra\"") \
|
||||
_Pragma("GCC diagnostic ignored \"-Wchar-subscripts\"") \
|
||||
#define TOML_DISABLE_SWITCH_WARNINGS _Pragma("GCC diagnostic ignored \"-Wswitch\"")
|
||||
#define TOML_DISABLE_INIT_WARNINGS _Pragma("GCC diagnostic ignored \"-Wmissing-field-initializers\"") \
|
||||
_Pragma("GCC diagnostic ignored \"-Wmaybe-uninitialized\"") \
|
||||
_Pragma("GCC diagnostic ignored \"-Wuninitialized\"")
|
||||
#define TOML_DISABLE_ALL_WARNINGS _Pragma("GCC diagnostic ignored \"-Wall\"") \
|
||||
_Pragma("GCC diagnostic ignored \"-Wextra\"") \
|
||||
_Pragma("GCC diagnostic ignored \"-Wchar-subscripts\"") \
|
||||
_Pragma("GCC diagnostic ignored \"-Wtype-limits\"")
|
||||
|
||||
#define TOML_POP_WARNINGS _Pragma("GCC diagnostic pop")
|
||||
#define TOML_ALWAYS_INLINE __attribute__((__always_inline__)) inline
|
||||
#define TOML_ALWAYS_INLINE TOML_GCC_ATTR(__always_inline__) inline
|
||||
#define TOML_UNREACHABLE __builtin_unreachable()
|
||||
|
||||
//floating-point from_chars and to_chars are not implemented in any version of gcc as of 1/1/2020
|
||||
@ -109,7 +119,11 @@
|
||||
#ifndef TOML_CPP_VERSION
|
||||
#define TOML_CPP_VERSION __cplusplus
|
||||
#endif
|
||||
#if TOML_CPP_VERSION >= 202600L
|
||||
#if TOML_CPP_VERSION < 201103L
|
||||
#error toml++ requires C++17 or higher. For a TOML parser supporting pre-C++11 see https://github.com/ToruNiina/Boost.toml
|
||||
#elif TOML_CPP_VERSION < 201703L
|
||||
#error toml++ requires C++17 or higher. For a TOML parser supporting C++11 see https://github.com/skystrife/cpptoml
|
||||
#elif TOML_CPP_VERSION >= 202600L
|
||||
#define TOML_CPP 26
|
||||
#elif TOML_CPP_VERSION >= 202300L
|
||||
#define TOML_CPP 23
|
||||
@ -117,12 +131,7 @@
|
||||
#define TOML_CPP 20
|
||||
#elif TOML_CPP_VERSION >= 201703L
|
||||
#define TOML_CPP 17
|
||||
#elif TOML_CPP_VERSION >= 201100L
|
||||
#error toml++ requires C++17 or higher. For a TOML parser supporting C++11 see https://github.com/skystrife/cpptoml
|
||||
#else
|
||||
#error toml++ requires C++17 or higher. For a TOML parser supporting pre-C++11 see https://github.com/ToruNiina/Boost.toml
|
||||
#endif
|
||||
#undef TOML_CPP_VERSION
|
||||
|
||||
#ifndef TOML_EXCEPTIONS
|
||||
#define TOML_EXCEPTIONS 1
|
||||
@ -135,11 +144,8 @@
|
||||
#define TOML_MAY_THROW noexcept
|
||||
#endif
|
||||
|
||||
#ifndef TOML_DISABLE_FIELD_INIT_WARNING
|
||||
#define TOML_DISABLE_FIELD_INIT_WARNING
|
||||
#endif
|
||||
#ifndef TOML_DISABLE_VAR_INIT_WARNING
|
||||
#define TOML_DISABLE_VAR_INIT_WARNING
|
||||
#ifndef TOML_DISABLE_INIT_WARNINGS
|
||||
#define TOML_DISABLE_INIT_WARNINGS
|
||||
#endif
|
||||
#ifndef TOML_USE_STREAMS_FOR_FLOATS
|
||||
#define TOML_USE_STREAMS_FOR_FLOATS 0
|
||||
@ -282,98 +288,6 @@ namespace toml
|
||||
}
|
||||
}
|
||||
|
||||
struct date final
|
||||
{
|
||||
uint16_t year;
|
||||
uint8_t month;
|
||||
uint8_t day;
|
||||
|
||||
[[nodiscard]]
|
||||
friend constexpr bool operator == (date lhs, date rhs) noexcept
|
||||
{
|
||||
return lhs.year == rhs.year
|
||||
&& lhs.month == rhs.month
|
||||
&& lhs.day == rhs.day;
|
||||
}
|
||||
|
||||
[[nodiscard]]
|
||||
friend constexpr bool operator != (date lhs, date rhs) noexcept
|
||||
{
|
||||
return lhs.year != rhs.year
|
||||
|| lhs.month != rhs.month
|
||||
|| lhs.day != rhs.day;
|
||||
}
|
||||
};
|
||||
|
||||
struct time final
|
||||
{
|
||||
uint8_t hour;
|
||||
uint8_t minute;
|
||||
uint8_t second;
|
||||
uint32_t nanosecond;
|
||||
|
||||
[[nodiscard]]
|
||||
friend constexpr bool operator == (time lhs, time rhs) noexcept
|
||||
{
|
||||
return lhs.hour == rhs.hour
|
||||
&& lhs.minute == rhs.minute
|
||||
&& lhs.second == rhs.second
|
||||
&& lhs.nanosecond == rhs.nanosecond;
|
||||
}
|
||||
|
||||
[[nodiscard]]
|
||||
friend constexpr bool operator != (time lhs, time rhs) noexcept
|
||||
{
|
||||
return lhs.hour != rhs.hour
|
||||
|| lhs.minute != rhs.minute
|
||||
|| lhs.second != rhs.second
|
||||
|| lhs.nanosecond != rhs.nanosecond;
|
||||
}
|
||||
};
|
||||
|
||||
struct time_offset final
|
||||
{
|
||||
int8_t hours;
|
||||
int8_t minutes;
|
||||
|
||||
[[nodiscard]]
|
||||
friend constexpr bool operator == (time_offset lhs, time_offset rhs) noexcept
|
||||
{
|
||||
return lhs.hours == rhs.hours
|
||||
&& lhs.minutes == rhs.minutes;
|
||||
}
|
||||
|
||||
[[nodiscard]]
|
||||
friend constexpr bool operator != (time_offset lhs, time_offset rhs) noexcept
|
||||
{
|
||||
return lhs.hours != rhs.hours
|
||||
|| lhs.minutes != rhs.minutes;
|
||||
}
|
||||
};
|
||||
|
||||
struct date_time final
|
||||
{
|
||||
toml::date date;
|
||||
toml::time time;
|
||||
std::optional<toml::time_offset> time_offset;
|
||||
|
||||
[[nodiscard]]
|
||||
friend constexpr bool operator == (const date_time& lhs, const date_time& rhs) noexcept
|
||||
{
|
||||
return lhs.date == rhs.date
|
||||
&& lhs.time == rhs.time
|
||||
&& lhs.time_offset == rhs.time_offset;
|
||||
}
|
||||
|
||||
[[nodiscard]]
|
||||
friend constexpr bool operator != (const date_time& lhs, const date_time& rhs) noexcept
|
||||
{
|
||||
return lhs.date != rhs.date
|
||||
|| lhs.time != rhs.time
|
||||
|| lhs.time_offset != rhs.time_offset;
|
||||
}
|
||||
};
|
||||
|
||||
#if TOML_CHAR_8_STRINGS
|
||||
|
||||
using string_char = char8_t;
|
||||
@ -392,125 +306,43 @@ namespace toml
|
||||
using string_map = std::map<string, T, std::less<>>; //heterogeneous lookup
|
||||
|
||||
class node;
|
||||
template <typename T>
|
||||
class node_view;
|
||||
template <typename T>
|
||||
class value;
|
||||
template <typename T> class node_view;
|
||||
template <typename T> class value;
|
||||
class array;
|
||||
class table;
|
||||
|
||||
class default_formatter;
|
||||
class json_formatter;
|
||||
|
||||
namespace impl
|
||||
enum class node_type : uint8_t
|
||||
{
|
||||
#if defined(__cpp_lib_remove_cvref) || (defined(_MSC_VER) && defined(_HAS_CXX20))
|
||||
table,
|
||||
array,
|
||||
string,
|
||||
integer,
|
||||
floating_point,
|
||||
boolean,
|
||||
date,
|
||||
time,
|
||||
date_time
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
using remove_cvref_t = std::remove_cvref_t<T>;
|
||||
#if TOML_LARGE_FILES
|
||||
|
||||
#else
|
||||
using source_index = uint32_t;
|
||||
|
||||
template <typename T>
|
||||
using remove_cvref_t = std::remove_cv_t<std::remove_reference_t<T>>;
|
||||
#else
|
||||
|
||||
#endif
|
||||
using source_index = uint16_t;
|
||||
|
||||
template <typename T>
|
||||
struct is_generic_invocable
|
||||
{
|
||||
template <typename U>
|
||||
static constexpr auto test(U&&) -> decltype(std::declval<T>()(std::declval<U&&>()), std::true_type{});
|
||||
static constexpr std::false_type test(...);
|
||||
#endif
|
||||
|
||||
struct tester {};
|
||||
static constexpr auto value = decltype(test(tester{}))::value;
|
||||
};
|
||||
|
||||
|
||||
template <typename T>
|
||||
inline constexpr bool is_generic_invocable_v = is_generic_invocable<T>::value;
|
||||
|
||||
class parser;
|
||||
class formatter;
|
||||
|
||||
template <typename T>
|
||||
inline constexpr bool is_value =
|
||||
std::is_same_v<T, string>
|
||||
|| std::is_same_v<T, int64_t>
|
||||
|| std::is_same_v<T, double>
|
||||
|| std::is_same_v<T, bool>
|
||||
|| std::is_same_v<T, date>
|
||||
|| std::is_same_v<T, time>
|
||||
|| std::is_same_v<T, date_time>;
|
||||
|
||||
template <typename T>
|
||||
inline constexpr bool is_value_or_promotable =
|
||||
is_value<T>
|
||||
|| std::is_same_v<T, string_view>
|
||||
|| std::is_same_v<T, int32_t>
|
||||
|| std::is_same_v<T, int16_t>
|
||||
|| std::is_same_v<T, int8_t>
|
||||
|| std::is_same_v<T, uint32_t>
|
||||
|| std::is_same_v<T, uint16_t>
|
||||
|| std::is_same_v<T, uint8_t>
|
||||
|| std::is_same_v<T, float>;
|
||||
|
||||
template <typename T>
|
||||
inline constexpr bool is_value_or_node =
|
||||
is_value<T>
|
||||
|| std::is_same_v<T, array>
|
||||
|| std::is_same_v<T, table>;
|
||||
|
||||
template <typename T> struct node_wrapper { using type = T; };
|
||||
template <> struct node_wrapper<string> { using type = value<string>; };
|
||||
template <> struct node_wrapper<int64_t> { using type = value<int64_t>; };
|
||||
template <> struct node_wrapper<double> { using type = value<double>; };
|
||||
template <> struct node_wrapper<bool> { using type = value<bool>; };
|
||||
template <> struct node_wrapper<date> { using type = value<date>; };
|
||||
template <> struct node_wrapper<time> { using type = value<time>; };
|
||||
template <> struct node_wrapper<date_time> { using type = value<date_time>; };
|
||||
|
||||
template <typename T> struct value_promoter { using type = T; };
|
||||
template <> struct value_promoter<string_view> { using type = string; };
|
||||
template <> struct value_promoter<int32_t> { using type = int64_t; };
|
||||
template <> struct value_promoter<int16_t> { using type = int64_t; };
|
||||
template <> struct value_promoter<int8_t> { using type = int64_t; };
|
||||
template <> struct value_promoter<uint32_t> { using type = int64_t; };
|
||||
template <> struct value_promoter<uint16_t> { using type = int64_t; };
|
||||
template <> struct value_promoter<uint8_t> { using type = int64_t; };
|
||||
template <> struct value_promoter<float> { using type = double; };
|
||||
|
||||
template <typename T>
|
||||
std::optional<size_t> find(const std::vector<T>& haystack, const T& needle) noexcept
|
||||
{
|
||||
// Q: "why not use std::find??"
|
||||
// A: Because <algorithm> is _huge_ and std::find would be the only thing I used from it.
|
||||
// I don't want to impose such a heavy burden on users.
|
||||
|
||||
const auto end = haystack.size();
|
||||
for (size_t i = 0; i < end; i++)
|
||||
if (haystack[i] == needle)
|
||||
return i;
|
||||
return {};
|
||||
}
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
using node_of = typename impl::node_wrapper<T>::type;
|
||||
|
||||
template <typename T>
|
||||
using value_of = typename impl::value_promoter<T>::type;
|
||||
|
||||
struct source_position
|
||||
struct source_position final
|
||||
{
|
||||
uint32_t line; //begins at 1
|
||||
uint32_t column; //begins at 1
|
||||
source_index line; //begins at 1
|
||||
source_index column; //begins at 1
|
||||
|
||||
[[nodiscard]]
|
||||
explicit constexpr operator bool () const noexcept
|
||||
{
|
||||
return line > 0u && column > 0u;
|
||||
return line > source_index{} && column > source_index{};
|
||||
}
|
||||
|
||||
[[nodiscard]]
|
||||
@ -551,28 +383,15 @@ namespace toml
|
||||
|
||||
using source_path_ptr = std::shared_ptr<const std::string>;
|
||||
|
||||
struct source_region
|
||||
struct source_region final
|
||||
{
|
||||
source_position begin;
|
||||
source_position end;
|
||||
source_path_ptr path;
|
||||
};
|
||||
|
||||
enum class node_type : uint8_t
|
||||
{
|
||||
table,
|
||||
array,
|
||||
string,
|
||||
integer,
|
||||
floating_point,
|
||||
boolean,
|
||||
date,
|
||||
time,
|
||||
date_time
|
||||
};
|
||||
|
||||
TOML_PUSH_WARNINGS
|
||||
TOML_DISABLE_FIELD_INIT_WARNING
|
||||
TOML_DISABLE_INIT_WARNINGS
|
||||
|
||||
#if TOML_EXCEPTIONS
|
||||
|
||||
@ -580,77 +399,337 @@ namespace toml
|
||||
: public std::runtime_error
|
||||
{
|
||||
private:
|
||||
source_region rgn;
|
||||
source_region source_;
|
||||
|
||||
public:
|
||||
|
||||
TOML_NODISCARD_CTOR
|
||||
parse_error(const char* description, source_region&& region) noexcept
|
||||
TOML_NODISCARD_CTOR TOML_GCC_ATTR(nonnull)
|
||||
parse_error(const char* description, source_region&& source) noexcept
|
||||
: std::runtime_error{ description },
|
||||
rgn{ std::move(region) }
|
||||
source_{ std::move(source) }
|
||||
{}
|
||||
|
||||
TOML_NODISCARD_CTOR
|
||||
parse_error(const char* description, const source_region& region) noexcept
|
||||
: std::runtime_error{ description },
|
||||
rgn{ region }
|
||||
TOML_NODISCARD_CTOR TOML_GCC_ATTR(nonnull)
|
||||
parse_error(const char* description, const source_region& source) noexcept
|
||||
: parse_error{ description, source_region{ source } }
|
||||
{}
|
||||
|
||||
TOML_NODISCARD_CTOR
|
||||
parse_error(const char* description, const source_position& position, const source_path_ptr& source_path) noexcept
|
||||
: std::runtime_error{ description },
|
||||
rgn{ position, position, source_path }
|
||||
{}
|
||||
|
||||
TOML_NODISCARD_CTOR
|
||||
parse_error(const char* description, const source_position& position) noexcept
|
||||
: std::runtime_error{ description },
|
||||
rgn{ position, position }
|
||||
TOML_NODISCARD_CTOR TOML_GCC_ATTR(nonnull)
|
||||
parse_error(const char* description, const source_position& position, const source_path_ptr& path = {}) noexcept
|
||||
: parse_error{ description, source_region{ position, position, path } }
|
||||
{}
|
||||
|
||||
[[nodiscard]]
|
||||
const source_region& where() const noexcept
|
||||
{
|
||||
return rgn;
|
||||
return source_;
|
||||
}
|
||||
};
|
||||
|
||||
#else
|
||||
|
||||
struct parse_error final
|
||||
class parse_error final
|
||||
{
|
||||
std::string what;
|
||||
source_region where;
|
||||
private:
|
||||
std::string description_;
|
||||
source_region source_;
|
||||
|
||||
TOML_NODISCARD_CTOR
|
||||
parse_error() noexcept = default;
|
||||
public:
|
||||
|
||||
TOML_NODISCARD_CTOR
|
||||
parse_error(const char* description, source_region&& region) noexcept
|
||||
: what{ description },
|
||||
where{ std::move(region) }
|
||||
{}
|
||||
TOML_NODISCARD_CTOR
|
||||
parse_error(std::string&& description, source_region&& source) noexcept
|
||||
: description_{ std::move(description) },
|
||||
source_{ std::move(source) }
|
||||
{}
|
||||
|
||||
TOML_NODISCARD_CTOR
|
||||
parse_error(const char* description, const source_region& region) noexcept
|
||||
: what{ description },
|
||||
where{ region }
|
||||
{}
|
||||
TOML_NODISCARD_CTOR
|
||||
parse_error(std::string&& description, const source_region& source) noexcept
|
||||
: parse_error{ std::move(description), source_region{ source } }
|
||||
{}
|
||||
|
||||
TOML_NODISCARD_CTOR
|
||||
parse_error(const char* description, const source_position& position, const source_path_ptr& source_path) noexcept
|
||||
: what{ description },
|
||||
where{ position, position, source_path }
|
||||
{}
|
||||
TOML_NODISCARD_CTOR
|
||||
parse_error(std::string&& description, const source_position& position, const source_path_ptr& path = {}) noexcept
|
||||
: parse_error{ std::move(description), source_region{ position, position, path } }
|
||||
{}
|
||||
|
||||
TOML_NODISCARD_CTOR
|
||||
parse_error(const char* description, const source_position& position) noexcept
|
||||
: what{ description },
|
||||
where{ position, position }
|
||||
{}
|
||||
[[nodiscard]]
|
||||
std::string_view what() const noexcept
|
||||
{
|
||||
return description_;
|
||||
}
|
||||
|
||||
[[nodiscard]]
|
||||
const source_region& where() const noexcept
|
||||
{
|
||||
return source_;
|
||||
}
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
TOML_POP_WARNINGS
|
||||
|
||||
struct date final
|
||||
{
|
||||
uint16_t year;
|
||||
uint8_t month;
|
||||
uint8_t day;
|
||||
|
||||
[[nodiscard]]
|
||||
friend constexpr bool operator == (date lhs, date rhs) noexcept
|
||||
{
|
||||
return lhs.year == rhs.year
|
||||
&& lhs.month == rhs.month
|
||||
&& lhs.day == rhs.day;
|
||||
}
|
||||
|
||||
[[nodiscard]]
|
||||
friend constexpr bool operator != (date lhs, date rhs) noexcept
|
||||
{
|
||||
return lhs.year != rhs.year
|
||||
|| lhs.month != rhs.month
|
||||
|| lhs.day != rhs.day;
|
||||
}
|
||||
};
|
||||
|
||||
struct time final
|
||||
{
|
||||
uint8_t hour;
|
||||
uint8_t minute;
|
||||
uint8_t second;
|
||||
uint32_t nanosecond;
|
||||
|
||||
[[nodiscard]]
|
||||
friend constexpr bool operator == (const time& lhs, const time& rhs) noexcept
|
||||
{
|
||||
return lhs.hour == rhs.hour
|
||||
&& lhs.minute == rhs.minute
|
||||
&& lhs.second == rhs.second
|
||||
&& lhs.nanosecond == rhs.nanosecond;
|
||||
}
|
||||
|
||||
[[nodiscard]]
|
||||
friend constexpr bool operator != (const time& lhs, const time& rhs) noexcept
|
||||
{
|
||||
return !(lhs == rhs);
|
||||
}
|
||||
};
|
||||
|
||||
struct time_offset final
|
||||
{
|
||||
int16_t minutes;
|
||||
|
||||
[[nodiscard]]
|
||||
static constexpr time_offset from_hh_mm(int8_t hours, int8_t minutes) noexcept
|
||||
{
|
||||
return time_offset{ static_cast<int16_t>(hours * 60 + minutes) };
|
||||
}
|
||||
|
||||
[[nodiscard]]
|
||||
friend constexpr bool operator == (time_offset lhs, time_offset rhs) noexcept
|
||||
{
|
||||
return lhs.minutes == rhs.minutes;
|
||||
}
|
||||
|
||||
[[nodiscard]]
|
||||
friend constexpr bool operator != (time_offset lhs, time_offset rhs) noexcept
|
||||
{
|
||||
return lhs.minutes != rhs.minutes;
|
||||
}
|
||||
};
|
||||
|
||||
struct date_time final
|
||||
{
|
||||
toml::date date;
|
||||
toml::time time;
|
||||
std::optional<toml::time_offset> time_offset;
|
||||
|
||||
[[nodiscard]]
|
||||
friend constexpr bool operator == (const date_time& lhs, const date_time& rhs) noexcept
|
||||
{
|
||||
return lhs.date == rhs.date
|
||||
&& lhs.time == rhs.time
|
||||
&& lhs.time_offset == rhs.time_offset;
|
||||
}
|
||||
|
||||
[[nodiscard]]
|
||||
friend constexpr bool operator != (const date_time& lhs, const date_time& rhs) noexcept
|
||||
{
|
||||
return lhs.date != rhs.date
|
||||
|| lhs.time != rhs.time
|
||||
|| lhs.time_offset != rhs.time_offset;
|
||||
}
|
||||
|
||||
};
|
||||
}
|
||||
|
||||
namespace toml::impl
|
||||
{
|
||||
#if defined(__cpp_lib_remove_cvref) || (defined(_MSC_VER) && defined(_HAS_CXX20))
|
||||
|
||||
template <typename T>
|
||||
using remove_cvref_t = std::remove_cvref_t<T>;
|
||||
|
||||
#else
|
||||
|
||||
template <typename T>
|
||||
using remove_cvref_t = std::remove_cv_t<std::remove_reference_t<T>>;
|
||||
|
||||
#endif
|
||||
|
||||
// Q: "why not use std::find??"
|
||||
// A: Because <algorithm> is _huge_ and std::find would be the only thing I used from it.
|
||||
// I don't want to impose such a heavy burden on users.
|
||||
|
||||
template <typename T>
|
||||
inline std::optional<size_t> find(const std::vector<T>& haystack, const T& needle) noexcept
|
||||
{
|
||||
const auto end = haystack.size();
|
||||
for (size_t i = 0; i < end; i++)
|
||||
if (haystack[i] == needle)
|
||||
return i;
|
||||
return {};
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
struct is_generic_invocable
|
||||
{
|
||||
template <typename U>
|
||||
static constexpr auto test(U&&) -> decltype(std::declval<T>()(std::declval<U&&>()), std::true_type{});
|
||||
static constexpr std::false_type test(...);
|
||||
|
||||
struct tester {};
|
||||
static constexpr auto value = decltype(test(tester{}))::value;
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
inline constexpr bool is_generic_invocable_v = is_generic_invocable<T>::value;
|
||||
|
||||
class parser;
|
||||
|
||||
template <typename T>
|
||||
inline constexpr bool is_value =
|
||||
std::is_same_v<T, string>
|
||||
|| std::is_same_v<T, int64_t>
|
||||
|| std::is_same_v<T, double>
|
||||
|| std::is_same_v<T, bool>
|
||||
|| std::is_same_v<T, date>
|
||||
|| std::is_same_v<T, time>
|
||||
|| std::is_same_v<T, date_time>;
|
||||
|
||||
template <typename T>
|
||||
inline constexpr bool is_value_or_promotable =
|
||||
is_value<T>
|
||||
|| std::is_same_v<T, string_view>
|
||||
|| std::is_same_v<T, int32_t>
|
||||
|| std::is_same_v<T, int16_t>
|
||||
|| std::is_same_v<T, int8_t>
|
||||
|| std::is_same_v<T, uint32_t>
|
||||
|| std::is_same_v<T, uint16_t>
|
||||
|| std::is_same_v<T, uint8_t>
|
||||
|| std::is_same_v<T, float>;
|
||||
|
||||
template <typename T>
|
||||
inline constexpr bool is_value_or_node =
|
||||
is_value<T>
|
||||
|| std::is_same_v<T, array>
|
||||
|| std::is_same_v<T, table>;
|
||||
|
||||
template <typename T> struct node_wrapper { using type = T; };
|
||||
template <> struct node_wrapper<string> { using type = value<string>; };
|
||||
template <> struct node_wrapper<int64_t> { using type = value<int64_t>; };
|
||||
template <> struct node_wrapper<double> { using type = value<double>; };
|
||||
template <> struct node_wrapper<bool> { using type = value<bool>; };
|
||||
template <> struct node_wrapper<date> { using type = value<date>; };
|
||||
template <> struct node_wrapper<time> { using type = value<time>; };
|
||||
template <> struct node_wrapper<date_time> { using type = value<date_time>; };
|
||||
|
||||
template <typename T> struct node_unwrapper { using type = T; };
|
||||
template <typename T> struct node_unwrapper<value<T>> { using type = T; };
|
||||
|
||||
template <typename T> struct value_promoter { using type = T; };
|
||||
template <> struct value_promoter<string_view> { using type = string; };
|
||||
template <> struct value_promoter<int32_t> { using type = int64_t; };
|
||||
template <> struct value_promoter<int16_t> { using type = int64_t; };
|
||||
template <> struct value_promoter<int8_t> { using type = int64_t; };
|
||||
template <> struct value_promoter<uint32_t> { using type = int64_t; };
|
||||
template <> struct value_promoter<uint16_t> { using type = int64_t; };
|
||||
template <> struct value_promoter<uint8_t> { using type = int64_t; };
|
||||
template <> struct value_promoter<float> { using type = double; };
|
||||
|
||||
inline constexpr toml::string_view low_character_escape_table[] =
|
||||
{
|
||||
TOML_STRING_PREFIX("\\u0000"sv),
|
||||
TOML_STRING_PREFIX("\\u0001"sv),
|
||||
TOML_STRING_PREFIX("\\u0002"sv),
|
||||
TOML_STRING_PREFIX("\\u0003"sv),
|
||||
TOML_STRING_PREFIX("\\u0004"sv),
|
||||
TOML_STRING_PREFIX("\\u0005"sv),
|
||||
TOML_STRING_PREFIX("\\u0006"sv),
|
||||
TOML_STRING_PREFIX("\\u0007"sv),
|
||||
TOML_STRING_PREFIX("\\b"sv),
|
||||
TOML_STRING_PREFIX("\\t"sv),
|
||||
TOML_STRING_PREFIX("\\n"sv),
|
||||
TOML_STRING_PREFIX("\\u000B"sv),
|
||||
TOML_STRING_PREFIX("\\f"sv),
|
||||
TOML_STRING_PREFIX("\\r"sv),
|
||||
TOML_STRING_PREFIX("\\u000E"sv),
|
||||
TOML_STRING_PREFIX("\\u000F"sv),
|
||||
TOML_STRING_PREFIX("\\u0010"sv),
|
||||
TOML_STRING_PREFIX("\\u0011"sv),
|
||||
TOML_STRING_PREFIX("\\u0012"sv),
|
||||
TOML_STRING_PREFIX("\\u0013"sv),
|
||||
TOML_STRING_PREFIX("\\u0014"sv),
|
||||
TOML_STRING_PREFIX("\\u0015"sv),
|
||||
TOML_STRING_PREFIX("\\u0016"sv),
|
||||
TOML_STRING_PREFIX("\\u0017"sv),
|
||||
TOML_STRING_PREFIX("\\u0018"sv),
|
||||
TOML_STRING_PREFIX("\\u0019"sv),
|
||||
TOML_STRING_PREFIX("\\u001A"sv),
|
||||
TOML_STRING_PREFIX("\\u001B"sv),
|
||||
TOML_STRING_PREFIX("\\u001C"sv),
|
||||
TOML_STRING_PREFIX("\\u001D"sv),
|
||||
TOML_STRING_PREFIX("\\u001E"sv),
|
||||
TOML_STRING_PREFIX("\\u001F"sv),
|
||||
};
|
||||
|
||||
inline constexpr std::string_view node_type_friendly_names[] =
|
||||
{
|
||||
"table"sv,
|
||||
"array"sv,
|
||||
"string"sv,
|
||||
"integer"sv,
|
||||
"floating-point"sv,
|
||||
"boolean"sv,
|
||||
"date"sv,
|
||||
"time"sv,
|
||||
"date-time"sv
|
||||
};
|
||||
}
|
||||
|
||||
namespace toml
|
||||
{
|
||||
template <typename T>
|
||||
using node_of = typename impl::node_wrapper<T>::type;
|
||||
|
||||
template <typename T>
|
||||
using value_of = typename impl::node_unwrapper<T>::type;
|
||||
|
||||
template <typename T>
|
||||
using promoted = typename impl::value_promoter<T>::type;
|
||||
|
||||
template <typename CHAR>
|
||||
inline std::basic_ostream<CHAR>& operator << (std::basic_ostream<CHAR>& lhs, node_type rhs) TOML_MAY_THROW
|
||||
{
|
||||
using underlying_t = std::underlying_type_t<node_type>;
|
||||
if constexpr (std::is_same_v<CHAR, char>)
|
||||
return lhs << impl::node_type_friendly_names[static_cast<underlying_t>(rhs)];
|
||||
else if constexpr (sizeof(CHAR) == 1)
|
||||
{
|
||||
const auto str = impl::node_type_friendly_names[static_cast<underlying_t>(rhs)];
|
||||
return lhs << std::basic_string_view<CHAR>{ reinterpret_cast<const CHAR*>(str.data()), str.length() };
|
||||
}
|
||||
else
|
||||
return lhs << lhs.data();
|
||||
}
|
||||
}
|
||||
|
@ -4,18 +4,131 @@
|
||||
#include "toml_array.h"
|
||||
#include "toml_utf8.h"
|
||||
|
||||
namespace toml::impl
|
||||
{
|
||||
inline toml::string default_formatter_make_key_segment(const toml::string& str) noexcept
|
||||
{
|
||||
if (str.empty())
|
||||
return TOML_STRING_PREFIX("''"s);
|
||||
else
|
||||
{
|
||||
bool requiresQuotes = false;
|
||||
{
|
||||
impl::utf8_decoder decoder;
|
||||
for (size_t i = 0; i < str.length() && !requiresQuotes; i++)
|
||||
{
|
||||
decoder(static_cast<uint8_t>(str[i]));
|
||||
if (decoder.error())
|
||||
requiresQuotes = true;
|
||||
else if (decoder.has_code_point())
|
||||
requiresQuotes = !impl::is_bare_key_character(decoder.codepoint);
|
||||
}
|
||||
}
|
||||
|
||||
if (requiresQuotes)
|
||||
{
|
||||
toml::string s;
|
||||
s.reserve(str.length() + 2_sz);
|
||||
s += TOML_STRING_PREFIX('"');
|
||||
for (auto c : str)
|
||||
s.append(escape_string_character(c));
|
||||
s += TOML_STRING_PREFIX('"');
|
||||
return s;
|
||||
}
|
||||
else
|
||||
return str;
|
||||
}
|
||||
}
|
||||
|
||||
inline size_t default_formatter_inline_columns(const node& node) noexcept
|
||||
{
|
||||
return node.visit([](const auto& n) noexcept
|
||||
-> size_t
|
||||
{
|
||||
using node_t = impl::remove_cvref_t<decltype(n)>;
|
||||
if constexpr (std::is_same_v<node_t, table>)
|
||||
{
|
||||
if (n.empty())
|
||||
return 2_sz; // "{}"
|
||||
size_t weight = 3_sz; // "{ }"
|
||||
for (auto [k, v] : n)
|
||||
weight += k.length() + default_formatter_inline_columns(v) + 2_sz; // + ", "
|
||||
return weight;
|
||||
}
|
||||
else if constexpr (std::is_same_v<node_t, array>)
|
||||
{
|
||||
if (n.empty())
|
||||
return 2_sz; // "[]"
|
||||
size_t weight = 3_sz; // "[ ]"
|
||||
for (auto& elem : n)
|
||||
weight += default_formatter_inline_columns(elem) + 2_sz; // + ", "
|
||||
return weight;
|
||||
}
|
||||
else if constexpr (std::is_same_v<node_t, value<string>>)
|
||||
{
|
||||
return n.get().length() + 2_sz; // + ""
|
||||
}
|
||||
else if constexpr (std::is_same_v<node_t, value<int64_t>>)
|
||||
{
|
||||
auto v = n.get();
|
||||
if (!v)
|
||||
return 1_sz;
|
||||
size_t weight = {};
|
||||
if (v < 0)
|
||||
{
|
||||
weight += 1;
|
||||
v *= -1;
|
||||
}
|
||||
return weight + static_cast<size_t>(std::log10(static_cast<double>(v)));
|
||||
}
|
||||
else if constexpr (std::is_same_v<node_t, value<double>>)
|
||||
{
|
||||
auto v = n.get();
|
||||
if (v == 0.0)
|
||||
return 3_sz;
|
||||
size_t weight = 2_sz; // ".0"
|
||||
if (v < 0.0)
|
||||
{
|
||||
weight += 1;
|
||||
v *= -1.0;
|
||||
}
|
||||
return weight + static_cast<size_t>(std::log10(v));
|
||||
}
|
||||
else if constexpr (std::is_same_v<node_t, value<bool>>)
|
||||
{
|
||||
return 5_sz;
|
||||
}
|
||||
else if constexpr (std::is_same_v<node_t, value<date>> || std::is_same_v<node_t, value<time>>)
|
||||
{
|
||||
return 10_sz;
|
||||
}
|
||||
else if constexpr (std::is_same_v<node_t, value<date_time>>)
|
||||
{
|
||||
return 30_sz;
|
||||
}
|
||||
TOML_UNREACHABLE;
|
||||
});
|
||||
}
|
||||
|
||||
inline bool default_formatter_forces_multiline(const node& node, size_t starting_column_bias = 0) noexcept
|
||||
{
|
||||
return (default_formatter_inline_columns(node) + starting_column_bias) > 120_sz;
|
||||
}
|
||||
}
|
||||
|
||||
namespace toml
|
||||
{
|
||||
class default_formatter final : impl::formatter
|
||||
template <typename CHAR = char>
|
||||
class default_formatter final : impl::formatter<CHAR>
|
||||
{
|
||||
private:
|
||||
using base = impl::formatter;
|
||||
using base = impl::formatter<CHAR>;
|
||||
std::vector<toml::string> key_path;
|
||||
|
||||
static toml::string make_key_segment(const toml::string& str) noexcept
|
||||
void print_key_segment(const toml::string& str) TOML_MAY_THROW
|
||||
{
|
||||
if (str.empty())
|
||||
return TOML_STRING_PREFIX("''"s);
|
||||
impl::print_to_stream("''"sv, base::stream());
|
||||
else
|
||||
{
|
||||
bool requiresQuotes = false;
|
||||
@ -32,245 +145,136 @@ namespace toml
|
||||
}
|
||||
|
||||
if (requiresQuotes)
|
||||
{
|
||||
toml::string s;
|
||||
s.reserve(str.length() + 2_sz);
|
||||
s += TOML_STRING_PREFIX('"');
|
||||
for (auto c : str)
|
||||
s.append(escape_string_character(c));
|
||||
s += TOML_STRING_PREFIX('"');
|
||||
return s;
|
||||
}
|
||||
base::print_quoted_string(str);
|
||||
else
|
||||
return str;
|
||||
impl::print_to_stream(str, base::stream());
|
||||
}
|
||||
base::clear_naked_newline();
|
||||
}
|
||||
|
||||
void write_key_segment(const toml::string& str) TOML_MAY_THROW
|
||||
{
|
||||
if (str.empty())
|
||||
base::write("''"sv);
|
||||
else
|
||||
{
|
||||
bool requiresQuotes = false;
|
||||
{
|
||||
impl::utf8_decoder decoder;
|
||||
for (size_t i = 0; i < str.length() && !requiresQuotes; i++)
|
||||
{
|
||||
decoder(static_cast<uint8_t>(str[i]));
|
||||
if (decoder.error())
|
||||
requiresQuotes = true;
|
||||
else if (decoder.has_code_point())
|
||||
requiresQuotes = !impl::is_bare_key_character(decoder.codepoint);
|
||||
}
|
||||
}
|
||||
|
||||
if (requiresQuotes)
|
||||
write_quoted_string(str);
|
||||
else
|
||||
base::write(str);
|
||||
}
|
||||
}
|
||||
|
||||
void write_key_path() TOML_MAY_THROW
|
||||
void print_key_path() TOML_MAY_THROW
|
||||
{
|
||||
for (const auto& segment : key_path)
|
||||
{
|
||||
if (std::addressof(segment) > key_path.data())
|
||||
base::write('.');
|
||||
base::write(segment);
|
||||
impl::print_to_stream('.', base::stream());
|
||||
impl::print_to_stream(segment, base::stream());
|
||||
}
|
||||
naked_newline = false;
|
||||
base::clear_naked_newline();
|
||||
}
|
||||
|
||||
inline void write_inline(const table& /*tbl*/) TOML_MAY_THROW;
|
||||
inline void print_inline(const table& /*tbl*/) TOML_MAY_THROW;
|
||||
|
||||
static size_t inline_columns(const node& node) noexcept
|
||||
{
|
||||
return node.visit([](const auto& n) noexcept
|
||||
-> size_t
|
||||
{
|
||||
using node_t = impl::remove_cvref_t<decltype(n)>;
|
||||
if constexpr (std::is_same_v<node_t, table>)
|
||||
{
|
||||
const auto& values = n.values;
|
||||
if (values.empty())
|
||||
return 2_sz; // "{}"
|
||||
size_t weight = 3_sz; // "{ }"
|
||||
for (const auto& [k, v] : values)
|
||||
weight += k.length() + inline_columns(*v) + 2_sz; // + ", "
|
||||
return weight;
|
||||
}
|
||||
else if constexpr (std::is_same_v<node_t, array>)
|
||||
{
|
||||
if (n.empty())
|
||||
return 2_sz; // "[]"
|
||||
size_t weight = 3_sz; // "[ ]"
|
||||
for (size_t i = 0; i < n.size(); i++)
|
||||
weight += inline_columns(*n.get(i)) + 2_sz; // + ", "
|
||||
return weight;
|
||||
}
|
||||
else if constexpr (std::is_same_v<node_t, value<string>>)
|
||||
{
|
||||
return n.get().length() + 2_sz; // + ""
|
||||
}
|
||||
else if constexpr (std::is_same_v<node_t, value<int64_t>>)
|
||||
{
|
||||
auto v = n.get();
|
||||
if (!v)
|
||||
return 1_sz;
|
||||
size_t weight = {};
|
||||
if (v < 0)
|
||||
{
|
||||
weight += 1;
|
||||
v *= -1;
|
||||
}
|
||||
return weight + static_cast<size_t>(std::log10(static_cast<double>(v)));
|
||||
}
|
||||
else if constexpr (std::is_same_v<node_t, value<double>>)
|
||||
{
|
||||
auto v = n.get();
|
||||
if (v == 0.0)
|
||||
return 3_sz;
|
||||
size_t weight = 2_sz; // ".0"
|
||||
if (v < 0.0)
|
||||
{
|
||||
weight += 1;
|
||||
v *= -1.0;
|
||||
}
|
||||
return weight + static_cast<size_t>(std::log10(v));
|
||||
}
|
||||
else if constexpr (std::is_same_v<node_t, value<bool>>)
|
||||
{
|
||||
return 5_sz;
|
||||
}
|
||||
else if constexpr (std::is_same_v<node_t, value<date>> || std::is_same_v<node_t, value<time>>)
|
||||
{
|
||||
return 10_sz;
|
||||
}
|
||||
else if constexpr (std::is_same_v<node_t, value<date_time>>)
|
||||
{
|
||||
return 30_sz;
|
||||
}
|
||||
TOML_UNREACHABLE;
|
||||
});
|
||||
}
|
||||
|
||||
static bool forces_multiline(const node& node, size_t starting_column_bias = 0) noexcept
|
||||
{
|
||||
return (inline_columns(node) + starting_column_bias) > 120_sz;
|
||||
}
|
||||
|
||||
void write(const array& arr) TOML_MAY_THROW
|
||||
void print(const array& arr) TOML_MAY_THROW
|
||||
{
|
||||
if (arr.empty())
|
||||
base::write("[]"sv);
|
||||
impl::print_to_stream("[]"sv, base::stream());
|
||||
else
|
||||
{
|
||||
const auto multiline = forces_multiline(
|
||||
const auto original_indent = base::indent();
|
||||
const auto multiline = impl::default_formatter_forces_multiline(
|
||||
arr,
|
||||
base::indent_string_columns * static_cast<size_t>(indent_level < 0 ? 0 : indent_level)
|
||||
base::indent_columns() * static_cast<size_t>(original_indent < 0 ? 0 : original_indent)
|
||||
);
|
||||
const auto original_indent = indent_level;
|
||||
base::write("["sv);
|
||||
impl::print_to_stream("["sv, base::stream());
|
||||
if (multiline)
|
||||
{
|
||||
if (indent_level < 0)
|
||||
indent_level++;
|
||||
indent_level++;
|
||||
if (original_indent < 0)
|
||||
base::indent(0);
|
||||
base::increase_indent();
|
||||
}
|
||||
else
|
||||
base::write(' ');
|
||||
impl::print_to_stream(' ', base::stream());
|
||||
|
||||
for (size_t i = 0; i < arr.size(); i++)
|
||||
{
|
||||
if (i > 0_sz)
|
||||
{
|
||||
base::write(',');
|
||||
impl::print_to_stream(',', base::stream());
|
||||
if (!multiline)
|
||||
base::write(' ');
|
||||
impl::print_to_stream(' ', base::stream());
|
||||
}
|
||||
|
||||
if (multiline)
|
||||
{
|
||||
write_newline(true);
|
||||
write_indent();
|
||||
base::print_newline(true);
|
||||
base::print_indent();
|
||||
}
|
||||
|
||||
auto v = arr.get(i);
|
||||
const auto type = v->type();
|
||||
auto& v = arr[i];
|
||||
const auto type = v.type();
|
||||
switch (type)
|
||||
{
|
||||
case node_type::table: write_inline(*reinterpret_cast<const table*>(v)); break;
|
||||
case node_type::array: write(*reinterpret_cast<const array*>(v)); break;
|
||||
case node_type::table: print_inline(*reinterpret_cast<const table*>(&v)); break;
|
||||
case node_type::array: print(*reinterpret_cast<const array*>(&v)); break;
|
||||
default:
|
||||
write_value(v, type);
|
||||
base::print(v, type);
|
||||
}
|
||||
|
||||
}
|
||||
if (multiline)
|
||||
{
|
||||
indent_level = original_indent;
|
||||
write_newline(true);
|
||||
write_indent();
|
||||
base::indent(original_indent);
|
||||
base::print_newline(true);
|
||||
base::print_indent();
|
||||
}
|
||||
else
|
||||
base::write(' ');
|
||||
base::write("]"sv);
|
||||
impl::print_to_stream(' ', base::stream());
|
||||
impl::print_to_stream("]"sv, base::stream());
|
||||
}
|
||||
naked_newline = false;
|
||||
base::clear_naked_newline();
|
||||
}
|
||||
|
||||
void write(const table& tbl) TOML_MAY_THROW
|
||||
void print(const table& tbl) TOML_MAY_THROW
|
||||
{
|
||||
//values, arrays, and inline tables
|
||||
for (const auto& [k, v] : tbl.values)
|
||||
for (auto [k, v] : tbl)
|
||||
{
|
||||
const auto type = v->type();
|
||||
if ((type == node_type::table && !reinterpret_cast<const table*>(v.get())->is_inline())
|
||||
|| (type == node_type::array && reinterpret_cast<const array*>(v.get())->is_array_of_tables()))
|
||||
const auto type = v.type();
|
||||
if ((type == node_type::table && !reinterpret_cast<const table*>(&v)->is_inline())
|
||||
|| (type == node_type::array && reinterpret_cast<const array*>(&v)->is_array_of_tables()))
|
||||
continue;
|
||||
|
||||
write_newline();
|
||||
write_indent();
|
||||
write_key_segment(k);
|
||||
base::write(" = "sv);
|
||||
base::print_newline();
|
||||
base::print_indent();
|
||||
print_key_segment(k);
|
||||
impl::print_to_stream(" = "sv, base::stream());
|
||||
switch (type)
|
||||
{
|
||||
case node_type::table: write_inline(*reinterpret_cast<const table*>(v.get())); break;
|
||||
case node_type::array: write(*reinterpret_cast<const array*>(v.get())); break;
|
||||
case node_type::table: print_inline(*reinterpret_cast<const table*>(&v)); break;
|
||||
case node_type::array: print(*reinterpret_cast<const array*>(&v)); break;
|
||||
default:
|
||||
write_value(v.get(), type);
|
||||
base::print(v, type);
|
||||
}
|
||||
}
|
||||
|
||||
//non-inline tables
|
||||
for (const auto& [k, v] : tbl.values)
|
||||
for (auto [k, v] : tbl)
|
||||
{
|
||||
const auto type = v->type();
|
||||
if (type != node_type::table || reinterpret_cast<const table*>(v.get())->is_inline())
|
||||
const auto type = v.type();
|
||||
if (type != node_type::table || reinterpret_cast<const table*>(&v)->is_inline())
|
||||
continue;
|
||||
auto& child_tbl = *reinterpret_cast<const table*>(v.get());
|
||||
auto& child_tbl = *reinterpret_cast<const table*>(&v);
|
||||
|
||||
//we can skip indenting and emitting the headers for tables that only contain other tables
|
||||
//(so we don't over-nest)
|
||||
size_t child_value_count{}; //includes inline tables and non-table arrays
|
||||
size_t child_table_count{};
|
||||
size_t child_table_array_count{};
|
||||
for (auto& [child_k, child_v] : child_tbl.values)
|
||||
for (auto [child_k, child_v] : child_tbl)
|
||||
{
|
||||
const auto child_type = child_v->type();
|
||||
const auto child_type = child_v.type();
|
||||
switch (child_type)
|
||||
{
|
||||
case node_type::table:
|
||||
if (reinterpret_cast<const table*>(child_v.get())->is_inline())
|
||||
if (reinterpret_cast<const table*>(&child_v)->is_inline())
|
||||
child_value_count++;
|
||||
else
|
||||
child_table_count++;
|
||||
break;
|
||||
|
||||
case node_type::array:
|
||||
if (reinterpret_cast<const array*>(child_v.get())->is_array_of_tables())
|
||||
if (reinterpret_cast<const array*>(&child_v)->is_array_of_tables())
|
||||
child_table_array_count++;
|
||||
else
|
||||
child_value_count++;
|
||||
@ -285,65 +289,60 @@ namespace toml
|
||||
skip_self = true;
|
||||
|
||||
if (!skip_self)
|
||||
indent_level++;
|
||||
key_path.push_back(make_key_segment(k));
|
||||
base::increase_indent();
|
||||
key_path.push_back(impl::default_formatter_make_key_segment(k));
|
||||
|
||||
if (!skip_self)
|
||||
{
|
||||
write_newline();
|
||||
write_newline(true);
|
||||
write_indent();
|
||||
base::write("["sv);
|
||||
write_key_path();
|
||||
base::write("]"sv);
|
||||
write_newline(true);
|
||||
base::print_newline();
|
||||
base::print_newline(true);
|
||||
base::print_indent();
|
||||
impl::print_to_stream("["sv, base::stream());
|
||||
print_key_path();
|
||||
impl::print_to_stream("]"sv, base::stream());
|
||||
base::print_newline(true);
|
||||
}
|
||||
|
||||
write(child_tbl);
|
||||
print(child_tbl);
|
||||
|
||||
key_path.pop_back();
|
||||
if (!skip_self)
|
||||
indent_level--;
|
||||
base::decrease_indent();
|
||||
}
|
||||
|
||||
//table arrays
|
||||
for (const auto& [k, v] : tbl.values)
|
||||
for (auto [k, v] : tbl)
|
||||
{
|
||||
const auto type = v->type();
|
||||
if (type != node_type::array || !reinterpret_cast<const array*>(v.get())->is_array_of_tables())
|
||||
const auto type = v.type();
|
||||
if (type != node_type::array || !reinterpret_cast<const array*>(&v)->is_array_of_tables())
|
||||
continue;
|
||||
auto& arr = *reinterpret_cast<const array*>(v.get());
|
||||
auto& arr = *reinterpret_cast<const array*>(&v);
|
||||
|
||||
indent_level++;
|
||||
key_path.push_back(make_key_segment(k));
|
||||
base::increase_indent();
|
||||
key_path.push_back(impl::default_formatter_make_key_segment(k));
|
||||
|
||||
for (size_t i = 0; i < arr.size(); i++)
|
||||
{
|
||||
write_newline();
|
||||
write_newline(true);
|
||||
write_indent();
|
||||
base::write("[["sv);
|
||||
write_key_path();
|
||||
base::write("]]"sv);
|
||||
write_newline(true);
|
||||
write(*reinterpret_cast<const table*>(arr.get(i)));
|
||||
base::print_newline();
|
||||
base::print_newline(true);
|
||||
base::print_indent();
|
||||
impl::print_to_stream("[["sv, base::stream());
|
||||
print_key_path();
|
||||
impl::print_to_stream("]]"sv, base::stream());
|
||||
base::print_newline(true);
|
||||
print(*reinterpret_cast<const table*>(&arr[i]));
|
||||
}
|
||||
|
||||
key_path.pop_back();
|
||||
indent_level--;
|
||||
base::decrease_indent();
|
||||
}
|
||||
}
|
||||
|
||||
void write() TOML_MAY_THROW
|
||||
{
|
||||
write(source);
|
||||
}
|
||||
|
||||
public:
|
||||
|
||||
TOML_NODISCARD_CTOR
|
||||
default_formatter(const toml::table& source_, toml::string_view indent_string = {}) noexcept
|
||||
: formatter{
|
||||
: base{
|
||||
source_,
|
||||
impl::formatter_options{
|
||||
indent_string,
|
||||
@ -352,63 +351,63 @@ namespace toml
|
||||
}
|
||||
{}
|
||||
|
||||
template <typename CHAR>
|
||||
friend std::basic_ostream<CHAR>& operator << (std::basic_ostream<CHAR>& lhs, default_formatter& rhs)
|
||||
template <typename T>
|
||||
friend std::basic_ostream<CHAR>& operator << (std::basic_ostream<T>& lhs, default_formatter& rhs)
|
||||
TOML_MAY_THROW
|
||||
{
|
||||
auto fw = impl::formatter_writer{ lhs };
|
||||
rhs.attach(fw);
|
||||
rhs.indent_level--; //starts at -1 so root kvps and first-level child tables have the same indent
|
||||
rhs.attach(lhs);
|
||||
rhs.base::decrease_indent(); //starts at -1 so root kvps and first-level child tables have the same indent
|
||||
rhs.key_path.clear();
|
||||
rhs.write();
|
||||
rhs.print(rhs.source());
|
||||
rhs.detach();
|
||||
return lhs;
|
||||
}
|
||||
|
||||
template <typename CHAR>
|
||||
friend std::basic_ostream<CHAR>& operator << (std::basic_ostream<CHAR>& lhs, default_formatter&& rhs)
|
||||
template <typename T>
|
||||
friend std::basic_ostream<CHAR>& operator << (std::basic_ostream<T>& lhs, default_formatter&& rhs)
|
||||
TOML_MAY_THROW
|
||||
{
|
||||
return lhs << rhs; //as lvalue
|
||||
}
|
||||
};
|
||||
|
||||
inline void default_formatter::write_inline(const toml::table& tbl) TOML_MAY_THROW
|
||||
template <typename CHAR>
|
||||
inline void default_formatter<CHAR>::print_inline(const toml::table& tbl) TOML_MAY_THROW
|
||||
{
|
||||
if (tbl.values.empty())
|
||||
base::write("{}"sv);
|
||||
if (tbl.empty())
|
||||
impl::print_to_stream("{}"sv, base::stream());
|
||||
else
|
||||
{
|
||||
base::write("{ "sv);
|
||||
impl::print_to_stream("{ "sv, base::stream());
|
||||
|
||||
bool first = false;
|
||||
for (auto& [k, v] : tbl.values)
|
||||
for (auto [k, v] : tbl)
|
||||
{
|
||||
if (first)
|
||||
base::write(", "sv);
|
||||
impl::print_to_stream(", "sv, base::stream());
|
||||
first = true;
|
||||
|
||||
write_key_segment(k);
|
||||
base::write(" = "sv);
|
||||
print_key_segment(k);
|
||||
impl::print_to_stream(" = "sv, base::stream());
|
||||
|
||||
const auto type = v->type();
|
||||
const auto type = v.type();
|
||||
switch (type)
|
||||
{
|
||||
case node_type::table: write_inline(*reinterpret_cast<const table*>(v.get())); break;
|
||||
case node_type::array: write(*reinterpret_cast<const array*>(v.get())); break;
|
||||
case node_type::table: print_inline(*reinterpret_cast<const table*>(&v)); break;
|
||||
case node_type::array: print(*reinterpret_cast<const array*>(&v)); break;
|
||||
default:
|
||||
write_value(v.get(), type);
|
||||
base::print(v, type);
|
||||
}
|
||||
}
|
||||
|
||||
base::write(" }"sv);
|
||||
impl::print_to_stream(" }"sv, base::stream());
|
||||
}
|
||||
naked_newline = false;
|
||||
base::clear_naked_newline();
|
||||
}
|
||||
|
||||
template <typename CHAR>
|
||||
inline std::basic_ostream<CHAR>& operator << (std::basic_ostream<CHAR>& lhs, const table& rhs) TOML_MAY_THROW
|
||||
{
|
||||
return lhs << default_formatter{ rhs };
|
||||
return lhs << default_formatter<CHAR>{ rhs };
|
||||
}
|
||||
}
|
||||
|
@ -3,49 +3,24 @@
|
||||
|
||||
namespace toml::impl
|
||||
{
|
||||
struct TOML_INTERFACE formatter_writer_interface
|
||||
TOML_PUSH_WARNINGS
|
||||
TOML_DISABLE_ALL_WARNINGS // some compilers will complain about a tautological unsigned >= 0.
|
||||
// TINAE - char can have signed _or_ unsigned semantics and I can't
|
||||
// be arsed handling this differently
|
||||
|
||||
inline toml::string_view escape_string_character(const toml::string_char& c) noexcept
|
||||
{
|
||||
virtual void operator() (const char*, size_t) TOML_MAY_THROW = 0;
|
||||
virtual void operator() (char) TOML_MAY_THROW = 0;
|
||||
if (c >= TOML_STRING_PREFIX('\x00') && c <= TOML_STRING_PREFIX('\x1F')) TOML_UNLIKELY
|
||||
return low_character_escape_table[c];
|
||||
else if (c == TOML_STRING_PREFIX('\x7F')) TOML_UNLIKELY
|
||||
return TOML_STRING_PREFIX("\\u007F"sv);
|
||||
else if (c == TOML_STRING_PREFIX('"')) TOML_UNLIKELY
|
||||
return TOML_STRING_PREFIX("\\\""sv);
|
||||
else
|
||||
return toml::string_view{ &c, 1_sz };
|
||||
}
|
||||
|
||||
virtual ~formatter_writer_interface() noexcept = default;
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
class formatter_writer;
|
||||
|
||||
template <typename CHAR>
|
||||
class TOML_EMPTY_BASES formatter_writer<std::basic_ostream<CHAR>> : public formatter_writer_interface
|
||||
{
|
||||
static_assert(
|
||||
sizeof(CHAR) == 1,
|
||||
"The stream's underlying character type must be 1 byte in size."
|
||||
);
|
||||
|
||||
private:
|
||||
std::basic_ostream<CHAR>& target_;
|
||||
|
||||
public:
|
||||
|
||||
void operator() (const char* characters, size_t num) TOML_MAY_THROW override
|
||||
{
|
||||
TOML_ASSERT(data && size);
|
||||
target_.write(reinterpret_cast<const CHAR*>(characters), static_cast<std::streamsize>(num));
|
||||
}
|
||||
|
||||
void operator() (char character) TOML_MAY_THROW override
|
||||
{
|
||||
target_.put(static_cast<CHAR>(character));
|
||||
}
|
||||
|
||||
TOML_NODISCARD_CTOR
|
||||
formatter_writer(std::basic_ostream<CHAR>& target) noexcept
|
||||
: target_{ target }
|
||||
{}
|
||||
};
|
||||
|
||||
template <typename CHAR>
|
||||
formatter_writer(std::basic_ostream<CHAR>&) -> formatter_writer<std::basic_ostream<CHAR>>;
|
||||
TOML_POP_WARNINGS
|
||||
|
||||
struct formatter_options final
|
||||
{
|
||||
@ -53,335 +28,138 @@ namespace toml::impl
|
||||
bool quote_dates_and_times;
|
||||
};
|
||||
|
||||
template <typename CHAR = char>
|
||||
class formatter
|
||||
{
|
||||
private:
|
||||
formatter_writer_interface* writer_ = nullptr;
|
||||
formatter_options opts;
|
||||
const toml::table& source_;
|
||||
std::basic_ostream<CHAR>* stream_ = nullptr;
|
||||
formatter_options options_;
|
||||
int indent_;
|
||||
bool naked_newline_;
|
||||
size_t indent_columns_;
|
||||
|
||||
protected:
|
||||
|
||||
[[nodiscard]] TOML_ALWAYS_INLINE
|
||||
const formatter_options& options() const noexcept
|
||||
{
|
||||
return opts;
|
||||
}
|
||||
|
||||
[[nodiscard]] const toml::table& source() const noexcept { return source_; }
|
||||
[[nodiscard]] const formatter_options& options() const noexcept { return options_; }
|
||||
[[nodiscard]] std::basic_ostream<CHAR>& stream() const noexcept { return *stream_; }
|
||||
|
||||
const toml::table& source;
|
||||
size_t indent_string_columns = {};
|
||||
int indent_level;
|
||||
bool naked_newline;
|
||||
[[nodiscard]] int indent() const noexcept { return indent_; }
|
||||
void indent(int level) noexcept { indent_ = level; }
|
||||
void increase_indent() noexcept { indent_++; }
|
||||
void decrease_indent() noexcept { indent_--; }
|
||||
size_t indent_columns() const noexcept { return indent_columns_; }
|
||||
|
||||
void attach(formatter_writer_interface& writer) noexcept
|
||||
void clear_naked_newline() noexcept { naked_newline_ = false; }
|
||||
|
||||
void attach(std::basic_ostream<CHAR>& stream) noexcept
|
||||
{
|
||||
indent_level = 0;
|
||||
naked_newline = true;
|
||||
writer_ = &writer;
|
||||
indent_ = 0;
|
||||
naked_newline_ = true;
|
||||
stream_ = &stream;
|
||||
}
|
||||
|
||||
void detach() noexcept
|
||||
{
|
||||
writer_ = nullptr;
|
||||
stream_ = nullptr;
|
||||
}
|
||||
|
||||
TOML_PUSH_WARNINGS
|
||||
TOML_DISABLE_ALL_WARNINGS // some compilers will complain about a tautological unsigned >= 0.
|
||||
// TINAE - char can have signed _or_ unsigned semantics and I can't
|
||||
// be arsed handling this differently
|
||||
|
||||
static toml::string_view escape_string_character(const toml::string_char& c) noexcept
|
||||
void print_newline(bool force = false) TOML_MAY_THROW
|
||||
{
|
||||
static constexpr toml::string_view low_character_escape_table[]
|
||||
if (!naked_newline_ || force)
|
||||
{
|
||||
TOML_STRING_PREFIX("\\u0000"sv),
|
||||
TOML_STRING_PREFIX("\\u0001"sv),
|
||||
TOML_STRING_PREFIX("\\u0002"sv),
|
||||
TOML_STRING_PREFIX("\\u0003"sv),
|
||||
TOML_STRING_PREFIX("\\u0004"sv),
|
||||
TOML_STRING_PREFIX("\\u0005"sv),
|
||||
TOML_STRING_PREFIX("\\u0006"sv),
|
||||
TOML_STRING_PREFIX("\\u0007"sv),
|
||||
TOML_STRING_PREFIX("\\b"sv),
|
||||
TOML_STRING_PREFIX("\\t"sv),
|
||||
TOML_STRING_PREFIX("\\n"sv),
|
||||
TOML_STRING_PREFIX("\\u000B"sv),
|
||||
TOML_STRING_PREFIX("\\f"sv),
|
||||
TOML_STRING_PREFIX("\\r"sv),
|
||||
TOML_STRING_PREFIX("\\u000E"sv),
|
||||
TOML_STRING_PREFIX("\\u000F"sv),
|
||||
TOML_STRING_PREFIX("\\u0010"sv),
|
||||
TOML_STRING_PREFIX("\\u0011"sv),
|
||||
TOML_STRING_PREFIX("\\u0012"sv),
|
||||
TOML_STRING_PREFIX("\\u0013"sv),
|
||||
TOML_STRING_PREFIX("\\u0014"sv),
|
||||
TOML_STRING_PREFIX("\\u0015"sv),
|
||||
TOML_STRING_PREFIX("\\u0016"sv),
|
||||
TOML_STRING_PREFIX("\\u0017"sv),
|
||||
TOML_STRING_PREFIX("\\u0018"sv),
|
||||
TOML_STRING_PREFIX("\\u0019"sv),
|
||||
TOML_STRING_PREFIX("\\u001A"sv),
|
||||
TOML_STRING_PREFIX("\\u001B"sv),
|
||||
TOML_STRING_PREFIX("\\u001C"sv),
|
||||
TOML_STRING_PREFIX("\\u001D"sv),
|
||||
TOML_STRING_PREFIX("\\u001E"sv),
|
||||
TOML_STRING_PREFIX("\\u001F"sv),
|
||||
};
|
||||
|
||||
if (c >= TOML_STRING_PREFIX('\x00') && c <= TOML_STRING_PREFIX('\x1F')) TOML_UNLIKELY
|
||||
return low_character_escape_table[c];
|
||||
else if (c == TOML_STRING_PREFIX('\x7F')) TOML_UNLIKELY
|
||||
return TOML_STRING_PREFIX("\\u007F"sv);
|
||||
else if (c == TOML_STRING_PREFIX('"')) TOML_UNLIKELY
|
||||
return TOML_STRING_PREFIX("\\\""sv);
|
||||
else
|
||||
return toml::string_view{ &c, 1_sz };
|
||||
}
|
||||
|
||||
TOML_POP_WARNINGS
|
||||
|
||||
TOML_ALWAYS_INLINE
|
||||
void write(char character) TOML_MAY_THROW
|
||||
{
|
||||
(*writer_)(character);
|
||||
}
|
||||
|
||||
template <typename CHAR>
|
||||
TOML_ALWAYS_INLINE
|
||||
void write(const CHAR* characters, size_t num) TOML_MAY_THROW
|
||||
{
|
||||
static_assert(sizeof(CHAR) == 1_sz);
|
||||
|
||||
(*writer_)(reinterpret_cast<const char*>(characters), num);
|
||||
}
|
||||
|
||||
template <typename CHAR>
|
||||
TOML_ALWAYS_INLINE
|
||||
void write(std::basic_string_view<CHAR> strv) TOML_MAY_THROW
|
||||
{
|
||||
static_assert(sizeof(CHAR) == 1_sz);
|
||||
|
||||
(*writer_)(reinterpret_cast<const char*>(strv.data()), strv.length());
|
||||
}
|
||||
|
||||
template <typename CHAR>
|
||||
TOML_ALWAYS_INLINE
|
||||
void write(const std::basic_string<CHAR>& str) TOML_MAY_THROW
|
||||
{
|
||||
static_assert(sizeof(CHAR) == 1_sz);
|
||||
|
||||
(*writer_)(reinterpret_cast<const char*>(str.data()), str.length());
|
||||
}
|
||||
|
||||
void write_newline(bool force = false) TOML_MAY_THROW
|
||||
{
|
||||
if (!naked_newline || force)
|
||||
{
|
||||
write('\n');
|
||||
naked_newline = true;
|
||||
print_to_stream('\n', *stream_);
|
||||
naked_newline_ = true;
|
||||
}
|
||||
}
|
||||
|
||||
void write_indent() TOML_MAY_THROW
|
||||
void print_indent() TOML_MAY_THROW
|
||||
{
|
||||
for (int i = 0; i < indent_level; i++)
|
||||
for (int i = 0; i < indent_; i++)
|
||||
{
|
||||
write(opts.indent_string);
|
||||
naked_newline = false;
|
||||
print_to_stream(options_.indent_string, *stream_);
|
||||
naked_newline_ = false;
|
||||
}
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
void write_integer(T val) TOML_MAY_THROW
|
||||
{
|
||||
char buf[20_sz]; //strlen("-9223372036854775808")
|
||||
const auto res = std::to_chars(buf, buf + sizeof(buf), val);
|
||||
write(buf, static_cast<size_t>(res.ptr - buf));
|
||||
naked_newline = false;
|
||||
}
|
||||
|
||||
void write(const value<int64_t>& val) TOML_MAY_THROW
|
||||
{
|
||||
write_integer(val.get());
|
||||
}
|
||||
|
||||
void write(const value<double>& val) TOML_MAY_THROW
|
||||
{
|
||||
#if TOML_USE_STREAMS_FOR_FLOATS
|
||||
{
|
||||
std::ostringstream oss;
|
||||
oss << val.get();
|
||||
write(oss.str());
|
||||
if (val.get() == 0.0)
|
||||
write(".0"sv);
|
||||
}
|
||||
#else
|
||||
{
|
||||
char buf[32_sz];
|
||||
const auto res = std::to_chars(buf, buf + sizeof(buf), val.get());
|
||||
const auto sv = std::string_view{ buf, static_cast<size_t>(res.ptr - buf) };
|
||||
write(sv);
|
||||
bool needs_decimal_point = true;
|
||||
for (auto ch : sv)
|
||||
{
|
||||
if (ch == 'e' || ch == 'E' || ch == '.')
|
||||
{
|
||||
needs_decimal_point = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (needs_decimal_point)
|
||||
write(".0"sv);
|
||||
}
|
||||
#endif
|
||||
naked_newline = false;
|
||||
}
|
||||
|
||||
void write(const value<bool>& val) TOML_MAY_THROW
|
||||
{
|
||||
write(val.get() ? "true"sv : "false"sv);
|
||||
naked_newline = false;
|
||||
}
|
||||
|
||||
void write_quoted_string(toml::string_view str) TOML_MAY_THROW
|
||||
void print_quoted_string(toml::string_view str) TOML_MAY_THROW
|
||||
{
|
||||
if (str.empty())
|
||||
write("\"\""sv);
|
||||
print_to_stream("\"\""sv, *stream_);
|
||||
else
|
||||
{
|
||||
write('"');
|
||||
print_to_stream('"', *stream_);
|
||||
for (auto c : str)
|
||||
write(escape_string_character(c));
|
||||
write('"');
|
||||
print_to_stream(escape_string_character(c), *stream_);
|
||||
print_to_stream('"', *stream_);
|
||||
}
|
||||
naked_newline = false;
|
||||
}
|
||||
|
||||
void write(const value<toml::string>& val) TOML_MAY_THROW
|
||||
{
|
||||
write_quoted_string(string_view{ val.get() });
|
||||
naked_newline_ = false;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
void write_zero_padded_integer(T intval, size_t digits) TOML_MAY_THROW
|
||||
void print(const value<T>& val) TOML_MAY_THROW
|
||||
{
|
||||
static_assert(std::is_unsigned_v<T>);
|
||||
|
||||
char buf[19_sz]; //strlen("9223372036854775807")
|
||||
const auto res = std::to_chars(buf, buf + sizeof(buf), intval);
|
||||
const auto len = static_cast<size_t>(res.ptr - buf);
|
||||
for (size_t i = len; i < digits; i++)
|
||||
write('0');
|
||||
write(buf, len);
|
||||
naked_newline = false;
|
||||
}
|
||||
|
||||
void write(const time& tm) TOML_MAY_THROW
|
||||
{
|
||||
write_zero_padded_integer(tm.hour, 2_sz);
|
||||
write(':');
|
||||
write_zero_padded_integer(tm.minute, 2_sz);
|
||||
write(':');
|
||||
write_zero_padded_integer(tm.second, 2_sz);
|
||||
|
||||
if (tm.nanosecond && tm.nanosecond <= 999999999u)
|
||||
if constexpr (std::is_same_v<T, string>)
|
||||
{
|
||||
write('.');
|
||||
auto ns = tm.nanosecond;
|
||||
size_t digits = 9_sz;
|
||||
while (ns % 10u == 0u)
|
||||
print_quoted_string(val.get());
|
||||
}
|
||||
else
|
||||
{
|
||||
static constexpr auto is_date_time =
|
||||
std::is_same_v<T, date>
|
||||
|| std::is_same_v<T, time>
|
||||
|| std::is_same_v<T, date_time>;
|
||||
|
||||
if constexpr (is_date_time)
|
||||
{
|
||||
ns /= 10u;
|
||||
digits--;
|
||||
if (options_.quote_dates_and_times)
|
||||
print_to_stream('"', *stream_);
|
||||
}
|
||||
write_integer(ns);
|
||||
|
||||
*stream_ << val;
|
||||
|
||||
if constexpr (is_date_time)
|
||||
{
|
||||
if (options_.quote_dates_and_times)
|
||||
print_to_stream('"', *stream_);
|
||||
}
|
||||
|
||||
naked_newline_ = false;
|
||||
}
|
||||
}
|
||||
|
||||
void write(const date& dt) TOML_MAY_THROW
|
||||
{
|
||||
write_zero_padded_integer(dt.year, 4_sz);
|
||||
write('-');
|
||||
write_zero_padded_integer(dt.month, 2_sz);
|
||||
write('-');
|
||||
write_zero_padded_integer(dt.day, 2_sz);
|
||||
}
|
||||
|
||||
void write(const date_time& dt) TOML_MAY_THROW
|
||||
{
|
||||
write(dt.date);
|
||||
write('T');
|
||||
write(dt.time);
|
||||
if (dt.time_offset)
|
||||
{
|
||||
const auto& to = *dt.time_offset;
|
||||
if (!to.hours && !to.minutes)
|
||||
write('Z');
|
||||
else
|
||||
{
|
||||
write(to.hours < 0 || to.minutes < 0 ? '-' : '+');
|
||||
write_zero_padded_integer(static_cast<uint8_t>(to.hours < 0 ? -to.hours : to.hours), 2_sz);
|
||||
write(':');
|
||||
write_zero_padded_integer(static_cast<uint8_t>(to.minutes < 0 ? -to.minutes : to.minutes), 2_sz);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void write(const value<time>& val) TOML_MAY_THROW
|
||||
{
|
||||
if (opts.quote_dates_and_times)
|
||||
write('"');
|
||||
write(val.get());
|
||||
if (opts.quote_dates_and_times)
|
||||
write('"');
|
||||
}
|
||||
|
||||
void write(const value<date>& val) TOML_MAY_THROW
|
||||
{
|
||||
if (opts.quote_dates_and_times)
|
||||
write('"');
|
||||
write(val.get());
|
||||
if (opts.quote_dates_and_times)
|
||||
write('"');
|
||||
}
|
||||
|
||||
void write(const value<date_time>& val) TOML_MAY_THROW
|
||||
{
|
||||
if (opts.quote_dates_and_times)
|
||||
write('"');
|
||||
write(val.get());
|
||||
if (opts.quote_dates_and_times)
|
||||
write('"');
|
||||
}
|
||||
|
||||
void write_value(const node* val_node, node_type type) TOML_MAY_THROW
|
||||
void print(const node& val_node, node_type type) TOML_MAY_THROW
|
||||
{
|
||||
switch (type)
|
||||
{
|
||||
|
||||
case node_type::string: write(*val_node->reinterpret_as<string>()); break;
|
||||
case node_type::integer: write(*val_node->reinterpret_as<int64_t>()); break;
|
||||
case node_type::floating_point: write(*val_node->reinterpret_as<double>()); break;
|
||||
case node_type::boolean: write(*val_node->reinterpret_as<bool>()); break;
|
||||
case node_type::date: write(*val_node->reinterpret_as<date>()); break;
|
||||
case node_type::time: write(*val_node->reinterpret_as<time>()); break;
|
||||
case node_type::date_time: write(*val_node->reinterpret_as<date_time>()); break;
|
||||
case node_type::string: print(*reinterpret_cast<const value<string>*>(&val_node)); break;
|
||||
case node_type::integer: print(*reinterpret_cast<const value<int64_t>*>(&val_node)); break;
|
||||
case node_type::floating_point: print(*reinterpret_cast<const value<double>*>(&val_node)); break;
|
||||
case node_type::boolean: print(*reinterpret_cast<const value<bool>*>(&val_node)); break;
|
||||
case node_type::date: print(*reinterpret_cast<const value<date>*>(&val_node)); break;
|
||||
case node_type::time: print(*reinterpret_cast<const value<time>*>(&val_node)); break;
|
||||
case node_type::date_time: print(*reinterpret_cast<const value<date_time>*>(&val_node)); break;
|
||||
TOML_NO_DEFAULT_CASE;
|
||||
}
|
||||
}
|
||||
|
||||
formatter(const toml::table& source_, formatter_options&& options_) noexcept
|
||||
: opts{ std::move(options_) },
|
||||
source{ source_ }
|
||||
formatter(const toml::table& source, formatter_options&& options) noexcept
|
||||
: source_{ source },
|
||||
options_{ std::move(options) }
|
||||
|
||||
{
|
||||
if (opts.indent_string.empty())
|
||||
if (options_.indent_string.empty())
|
||||
{
|
||||
opts.indent_string = TOML_STRING_PREFIX(" "sv);
|
||||
indent_string_columns = 4;
|
||||
options_.indent_string = TOML_STRING_PREFIX(" "sv);
|
||||
indent_columns_ = 4_sz;
|
||||
}
|
||||
else
|
||||
{
|
||||
for (auto c : opts.indent_string)
|
||||
indent_string_columns += c == '\t' ? 4_sz : 1_sz;
|
||||
indent_columns_ = {};
|
||||
for (auto c : options_.indent_string)
|
||||
indent_columns_ += c == '\t' ? 4_sz : 1_sz;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
@ -3,57 +3,53 @@
|
||||
|
||||
namespace toml
|
||||
{
|
||||
class json_formatter final : impl::formatter
|
||||
template <typename CHAR = char>
|
||||
class json_formatter final : impl::formatter<CHAR>
|
||||
{
|
||||
private:
|
||||
using base = impl::formatter;
|
||||
using base = impl::formatter<CHAR>;
|
||||
|
||||
inline void write(const toml::table& tbl) TOML_MAY_THROW;
|
||||
inline void print(const toml::table& tbl) TOML_MAY_THROW;
|
||||
|
||||
void write(const array& arr) TOML_MAY_THROW
|
||||
void print(const array& arr) TOML_MAY_THROW
|
||||
{
|
||||
if (arr.empty())
|
||||
base::write("[]"sv);
|
||||
impl::print_to_stream("[]"sv, base::stream());
|
||||
else
|
||||
{
|
||||
base::write('[');
|
||||
indent_level++;
|
||||
impl::print_to_stream('[', base::stream());
|
||||
base::increase_indent();
|
||||
for (size_t i = 0; i < arr.size(); i++)
|
||||
{
|
||||
if (i > 0_sz)
|
||||
base::write(',');
|
||||
write_newline(true);
|
||||
write_indent();
|
||||
impl::print_to_stream(',', base::stream());
|
||||
base::print_newline(true);
|
||||
base::print_indent();
|
||||
|
||||
auto v = arr.get(i);
|
||||
const auto type = v->type();
|
||||
auto& v = arr[i];
|
||||
const auto type = v.type();
|
||||
switch (type)
|
||||
{
|
||||
case node_type::table: write(*reinterpret_cast<const table*>(v)); break;
|
||||
case node_type::array: write(*reinterpret_cast<const array*>(v)); break;
|
||||
case node_type::table: print(*reinterpret_cast<const table*>(&v)); break;
|
||||
case node_type::array: print(*reinterpret_cast<const array*>(&v)); break;
|
||||
default:
|
||||
write_value(v, type);
|
||||
base::print(v, type);
|
||||
}
|
||||
|
||||
}
|
||||
indent_level--;
|
||||
write_newline(true);
|
||||
write_indent();
|
||||
base::write(']');
|
||||
base::decrease_indent();
|
||||
base::print_newline(true);
|
||||
base::print_indent();
|
||||
impl::print_to_stream(']', base::stream());
|
||||
}
|
||||
naked_newline = false;
|
||||
}
|
||||
|
||||
void write() TOML_MAY_THROW
|
||||
{
|
||||
write(source);
|
||||
base::clear_naked_newline();
|
||||
}
|
||||
|
||||
public:
|
||||
|
||||
TOML_NODISCARD_CTOR
|
||||
json_formatter(const toml::table& source_, toml::string_view indent_string = {}) noexcept
|
||||
: formatter{
|
||||
: base{
|
||||
source_,
|
||||
impl::formatter_options{
|
||||
indent_string,
|
||||
@ -62,61 +58,61 @@ namespace toml
|
||||
}
|
||||
{}
|
||||
|
||||
template <typename CHAR>
|
||||
friend std::basic_ostream<CHAR>& operator << (std::basic_ostream<CHAR>& lhs, json_formatter& rhs)
|
||||
template <typename T>
|
||||
friend std::basic_ostream<T>& operator << (std::basic_ostream<T>& lhs, json_formatter& rhs)
|
||||
TOML_MAY_THROW
|
||||
{
|
||||
auto fw = impl::formatter_writer{ lhs };
|
||||
rhs.attach(fw);
|
||||
rhs.write();
|
||||
rhs.attach(lhs);
|
||||
rhs.print(rhs.source());
|
||||
rhs.detach();
|
||||
return lhs;
|
||||
}
|
||||
|
||||
template <typename CHAR>
|
||||
friend std::basic_ostream<CHAR>& operator << (std::basic_ostream<CHAR>& lhs, json_formatter&& rhs)
|
||||
template <typename T>
|
||||
friend std::basic_ostream<CHAR>& operator << (std::basic_ostream<T>& lhs, json_formatter&& rhs)
|
||||
TOML_MAY_THROW
|
||||
{
|
||||
return lhs << rhs; //as lvalue
|
||||
}
|
||||
};
|
||||
|
||||
inline void json_formatter::write(const toml::table& tbl) TOML_MAY_THROW
|
||||
template <typename CHAR>
|
||||
inline void json_formatter<CHAR>::print(const toml::table& tbl) TOML_MAY_THROW
|
||||
{
|
||||
if (tbl.empty())
|
||||
base::write("{}"sv);
|
||||
impl::print_to_stream("{}"sv, base::stream());
|
||||
else
|
||||
{
|
||||
base::write('{');
|
||||
indent_level++;
|
||||
impl::print_to_stream('{', base::stream());
|
||||
base::increase_indent();
|
||||
bool first = false;
|
||||
for (auto& [k, v] : tbl.values)
|
||||
for (auto [k, v] : tbl)
|
||||
{
|
||||
if (first)
|
||||
base::write(", "sv);
|
||||
impl::print_to_stream(", "sv, base::stream());
|
||||
first = true;
|
||||
write_newline(true);
|
||||
write_indent();
|
||||
base::print_newline(true);
|
||||
base::print_indent();
|
||||
|
||||
base::write_quoted_string(k);
|
||||
base::write(" : "sv);
|
||||
base::print_quoted_string(k);
|
||||
impl::print_to_stream(" : "sv, base::stream());
|
||||
|
||||
const auto type = v->type();
|
||||
const auto type = v.type();
|
||||
switch (type)
|
||||
{
|
||||
case node_type::table: write(*reinterpret_cast<const table*>(v.get())); break;
|
||||
case node_type::array: write(*reinterpret_cast<const array*>(v.get())); break;
|
||||
case node_type::table: print(*reinterpret_cast<const table*>(&v)); break;
|
||||
case node_type::array: print(*reinterpret_cast<const array*>(&v)); break;
|
||||
default:
|
||||
write_value(v.get(), type);
|
||||
base::print(v, type);
|
||||
}
|
||||
|
||||
}
|
||||
indent_level--;
|
||||
write_newline(true);
|
||||
write_indent();
|
||||
base::write('}');
|
||||
base::decrease_indent();
|
||||
base::print_newline(true);
|
||||
base::print_indent();
|
||||
impl::print_to_stream('}', base::stream());
|
||||
}
|
||||
naked_newline = false;
|
||||
base::clear_naked_newline();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -7,18 +7,17 @@ namespace toml
|
||||
{
|
||||
private:
|
||||
friend class impl::parser;
|
||||
friend class impl::formatter;
|
||||
source_region rgn{};
|
||||
source_region source_{};
|
||||
|
||||
protected:
|
||||
|
||||
node(node&& other) noexcept
|
||||
: rgn{ std::move(other.rgn) }
|
||||
: source_{ std::move(other.source_) }
|
||||
{}
|
||||
|
||||
node& operator= (node&& rhs) noexcept
|
||||
{
|
||||
rgn = std::move(rhs.rgn);
|
||||
source_ = std::move(rhs.source_);
|
||||
return *this;
|
||||
}
|
||||
|
||||
@ -43,7 +42,12 @@ namespace toml
|
||||
node& operator= (const node&) = delete;
|
||||
virtual ~node() noexcept = default;
|
||||
|
||||
[[nodiscard]] virtual bool is_value() const noexcept { return false; }
|
||||
[[nodiscard]] virtual node_type type() const noexcept = 0;
|
||||
|
||||
[[nodiscard]] virtual bool is_table() const noexcept = 0;
|
||||
[[nodiscard]] virtual bool is_array() const noexcept = 0;
|
||||
[[nodiscard]] virtual bool is_value() const noexcept = 0;
|
||||
|
||||
[[nodiscard]] virtual bool is_string() const noexcept { return false; }
|
||||
[[nodiscard]] virtual bool is_integer() const noexcept { return false; }
|
||||
[[nodiscard]] virtual bool is_floating_point() const noexcept { return false; }
|
||||
@ -51,30 +55,30 @@ namespace toml
|
||||
[[nodiscard]] virtual bool is_date() const noexcept { return false; }
|
||||
[[nodiscard]] virtual bool is_time() const noexcept { return false; }
|
||||
[[nodiscard]] virtual bool is_date_time() const noexcept { return false; }
|
||||
[[nodiscard]] virtual bool is_array() const noexcept { return false; }
|
||||
[[nodiscard]] virtual bool is_table() const noexcept { return false; }
|
||||
[[nodiscard]] virtual bool is_array_of_tables() const noexcept { return false; }
|
||||
|
||||
template <typename T>
|
||||
[[nodiscard]] bool is() const noexcept
|
||||
{
|
||||
using type = T;
|
||||
using type = value_of<T>;
|
||||
static_assert(
|
||||
impl::is_value_or_node<type>,
|
||||
"Template type parameter must be one of the basic value types, a toml::table, or a toml::array"
|
||||
);
|
||||
|
||||
if constexpr (std::is_same_v<type, string>) return is_string();
|
||||
if constexpr (std::is_same_v<type, table>) return is_table();
|
||||
else if constexpr (std::is_same_v<type, array>) return is_array();
|
||||
else if constexpr (std::is_same_v<type, string>) return is_string();
|
||||
else if constexpr (std::is_same_v<type, int64_t>) return is_integer();
|
||||
else if constexpr (std::is_same_v<type, double>) return is_floating_point();
|
||||
else if constexpr (std::is_same_v<type, bool>) return is_boolean();
|
||||
else if constexpr (std::is_same_v<type, date>) return is_date();
|
||||
else if constexpr (std::is_same_v<type, time>) return is_time();
|
||||
else if constexpr (std::is_same_v<type, date_time>) return is_date_time();
|
||||
else if constexpr (std::is_same_v<type, array>) return is_array();
|
||||
else if constexpr (std::is_same_v<type, table>) return is_table();
|
||||
}
|
||||
|
||||
[[nodiscard]] virtual table* as_table() noexcept { return nullptr; }
|
||||
[[nodiscard]] virtual array* as_array() noexcept { return nullptr; }
|
||||
[[nodiscard]] virtual value<string>* as_string() noexcept { return nullptr; }
|
||||
[[nodiscard]] virtual value<int64_t>* as_integer() noexcept { return nullptr; }
|
||||
[[nodiscard]] virtual value<double>* as_floating_point() noexcept { return nullptr; }
|
||||
@ -82,10 +86,9 @@ namespace toml
|
||||
[[nodiscard]] virtual value<date>* as_date() noexcept { return nullptr; }
|
||||
[[nodiscard]] virtual value<time>* as_time() noexcept { return nullptr; }
|
||||
[[nodiscard]] virtual value<date_time>* as_date_time() noexcept { return nullptr; }
|
||||
[[nodiscard]] virtual array* as_array() noexcept { return nullptr; }
|
||||
[[nodiscard]] virtual table* as_table() noexcept { return nullptr; }
|
||||
|
||||
|
||||
[[nodiscard]] virtual const table* as_table() const noexcept { return nullptr; }
|
||||
[[nodiscard]] virtual const array* as_array() const noexcept { return nullptr; }
|
||||
[[nodiscard]] virtual const value<string>* as_string() const noexcept { return nullptr; }
|
||||
[[nodiscard]] virtual const value<int64_t>* as_integer() const noexcept { return nullptr; }
|
||||
[[nodiscard]] virtual const value<double>* as_floating_point() const noexcept { return nullptr; }
|
||||
@ -93,56 +96,52 @@ namespace toml
|
||||
[[nodiscard]] virtual const value<date>* as_date() const noexcept { return nullptr; }
|
||||
[[nodiscard]] virtual const value<time>* as_time() const noexcept { return nullptr; }
|
||||
[[nodiscard]] virtual const value<date_time>* as_date_time() const noexcept { return nullptr; }
|
||||
[[nodiscard]] virtual const array* as_array() const noexcept { return nullptr; }
|
||||
[[nodiscard]] virtual const table* as_table() const noexcept { return nullptr; }
|
||||
|
||||
template <typename T>
|
||||
[[nodiscard]] TOML_ALWAYS_INLINE
|
||||
node_of<T>* as() noexcept
|
||||
{
|
||||
using type = T;
|
||||
using type = value_of<T>;
|
||||
static_assert(
|
||||
impl::is_value_or_node<type>,
|
||||
"Template type parameter must be one of the basic value types, a toml::table, or a toml::array"
|
||||
);
|
||||
|
||||
if constexpr (std::is_same_v<type, string>) return as_string();
|
||||
if constexpr (std::is_same_v<type, table>) return as_table();
|
||||
else if constexpr (std::is_same_v<type, array>) return as_array();
|
||||
else if constexpr (std::is_same_v<type, string>) return as_string();
|
||||
else if constexpr (std::is_same_v<type, int64_t>) return as_integer();
|
||||
else if constexpr (std::is_same_v<type, double>) return as_floating_point();
|
||||
else if constexpr (std::is_same_v<type, bool>) return as_boolean();
|
||||
else if constexpr (std::is_same_v<type, date>) return as_date();
|
||||
else if constexpr (std::is_same_v<type, time>) return as_time();
|
||||
else if constexpr (std::is_same_v<type, date_time>) return as_date_time();
|
||||
else if constexpr (std::is_same_v<type, array>) return as_array();
|
||||
else if constexpr (std::is_same_v<type, table>) return as_table();
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
[[nodiscard]] TOML_ALWAYS_INLINE
|
||||
const node_of<T>* as() const noexcept
|
||||
{
|
||||
using type = T;
|
||||
using type = value_of<T>;
|
||||
static_assert(
|
||||
impl::is_value_or_node<type>,
|
||||
"Template type parameter must be one of the basic value types, a toml::table, or a toml::array"
|
||||
);
|
||||
|
||||
if constexpr (std::is_same_v<type, string>) return as_string();
|
||||
if constexpr (std::is_same_v<type, table>) return as_table();
|
||||
else if constexpr (std::is_same_v<type, array>) return as_array();
|
||||
else if constexpr (std::is_same_v<type, string>) return as_string();
|
||||
else if constexpr (std::is_same_v<type, int64_t>) return as_integer();
|
||||
else if constexpr (std::is_same_v<type, double>) return as_floating_point();
|
||||
else if constexpr (std::is_same_v<type, bool>) return as_boolean();
|
||||
else if constexpr (std::is_same_v<type, date>) return as_date();
|
||||
else if constexpr (std::is_same_v<type, time>) return as_time();
|
||||
else if constexpr (std::is_same_v<type, date_time>) return as_date_time();
|
||||
else if constexpr (std::is_same_v<type, array>) return as_array();
|
||||
else if constexpr (std::is_same_v<type, table>) return as_table();
|
||||
}
|
||||
|
||||
[[nodiscard]] virtual node_type type() const noexcept = 0;
|
||||
|
||||
[[nodiscard]] const source_region& region() const noexcept
|
||||
[[nodiscard]] const source_region& source() const noexcept
|
||||
{
|
||||
return rgn;
|
||||
return source_;
|
||||
}
|
||||
|
||||
private:
|
||||
@ -151,6 +150,7 @@ namespace toml
|
||||
// (otherwise I'd have to implement this function twice)
|
||||
// (const propagation in C++: a modern horror story)
|
||||
template <typename N, typename FUNC>
|
||||
TOML_GCC_ATTR(nonnull)
|
||||
static decltype(auto) do_visit(N* node, FUNC&& visitor) TOML_MAY_THROW
|
||||
{
|
||||
static_assert(
|
||||
|
@ -11,7 +11,7 @@ namespace toml::impl
|
||||
template <>
|
||||
struct node_view_traits<const table>
|
||||
{
|
||||
using member_type = const table*;
|
||||
using haystack_type = const table*;
|
||||
using key_type = string_view;
|
||||
|
||||
[[nodiscard]] static const node* get(const table* tbl, key_type key) noexcept
|
||||
@ -29,7 +29,7 @@ namespace toml::impl
|
||||
template <>
|
||||
struct node_view_traits<table>
|
||||
{
|
||||
using member_type = table*;
|
||||
using haystack_type = table*;
|
||||
using key_type = string_view;
|
||||
|
||||
[[nodiscard]] static node* get(table* tbl, key_type key) noexcept
|
||||
@ -61,30 +61,30 @@ namespace toml::impl
|
||||
template <typename T>
|
||||
struct node_view_traits<sub_view<T, string_view>>
|
||||
{
|
||||
using member_type = T;
|
||||
using haystack_type = T;
|
||||
using key_type = string_view;
|
||||
|
||||
[[nodiscard]] static auto get(member_type& view, string_view key) noexcept
|
||||
[[nodiscard]] static auto get(haystack_type& view, string_view key) noexcept
|
||||
{
|
||||
auto parent = view.as_table();
|
||||
return parent ? parent->get(key) : nullptr;
|
||||
}
|
||||
|
||||
[[nodiscard]] static const node* get(const member_type& view, string_view key) noexcept
|
||||
[[nodiscard]] static const node* get(const haystack_type& view, string_view key) noexcept
|
||||
{
|
||||
auto parent = view.as_table();
|
||||
return parent ? parent->get(key) : nullptr;
|
||||
}
|
||||
|
||||
template <typename U>
|
||||
[[nodiscard]] static auto as(member_type& view, string_view key) noexcept
|
||||
[[nodiscard]] static auto as(haystack_type& view, string_view key) noexcept
|
||||
{
|
||||
auto parent = view.as_table();
|
||||
return parent ? parent->template get_as<U>(key) : nullptr;
|
||||
}
|
||||
|
||||
template <typename U>
|
||||
[[nodiscard]] static const node_of<U>* as(const member_type& view, string_view key) noexcept
|
||||
[[nodiscard]] static const node_of<U>* as(const haystack_type& view, string_view key) noexcept
|
||||
{
|
||||
auto parent = view.as_table();
|
||||
return parent ? parent->template get_as<U>(key) : nullptr;
|
||||
@ -94,30 +94,30 @@ namespace toml::impl
|
||||
template <typename T>
|
||||
struct node_view_traits<sub_view<T, size_t>>
|
||||
{
|
||||
using member_type = T;
|
||||
using haystack_type = T;
|
||||
using key_type = size_t;
|
||||
|
||||
[[nodiscard]] static auto get(member_type& view, size_t index) noexcept
|
||||
[[nodiscard]] static auto get(haystack_type& view, size_t index) noexcept
|
||||
{
|
||||
auto parent = view.as_array();
|
||||
return parent ? parent->get(index) : nullptr;
|
||||
}
|
||||
|
||||
[[nodiscard]] static const node* get(const member_type& view, size_t index) noexcept
|
||||
[[nodiscard]] static const node* get(const haystack_type& view, size_t index) noexcept
|
||||
{
|
||||
auto parent = view.as_array();
|
||||
return parent ? parent->get(index) : nullptr;
|
||||
}
|
||||
|
||||
template <typename U>
|
||||
[[nodiscard]] static auto as(member_type& view, size_t index) noexcept
|
||||
[[nodiscard]] static auto as(haystack_type& view, size_t index) noexcept
|
||||
{
|
||||
auto parent = view.as_array();
|
||||
return parent ? parent->template get_as<U>(index) : nullptr;
|
||||
}
|
||||
|
||||
template <typename U>
|
||||
[[nodiscard]] static const node_of<U>* as(const member_type& view, size_t index) noexcept
|
||||
[[nodiscard]] static const node_of<U>* as(const haystack_type& view, size_t index) noexcept
|
||||
{
|
||||
auto parent = view.as_array();
|
||||
return parent ? parent->template get_as<U>(index) : nullptr;
|
||||
@ -135,20 +135,20 @@ namespace toml
|
||||
using key_type = typename traits::key_type;
|
||||
|
||||
private:
|
||||
using member_type = typename traits::member_type;
|
||||
member_type obj_;
|
||||
using haystack_type = typename traits::haystack_type;
|
||||
haystack_type haystack_;
|
||||
key_type key_;
|
||||
|
||||
public:
|
||||
|
||||
TOML_NODISCARD_CTOR
|
||||
node_view(member_type obj, key_type key) noexcept
|
||||
: obj_{ obj },
|
||||
node_view(haystack_type obj, key_type key) noexcept
|
||||
: haystack_{ obj },
|
||||
key_{ key }
|
||||
{}
|
||||
|
||||
[[nodiscard]] auto get() noexcept { return traits::get(obj_, key_); }
|
||||
[[nodiscard]] const node* get() const noexcept { return traits::get(obj_, key_); }
|
||||
[[nodiscard]] auto get() noexcept { return traits::get(haystack_, key_); }
|
||||
[[nodiscard]] const node* get() const noexcept { return traits::get(haystack_, key_); }
|
||||
|
||||
[[nodiscard]] explicit operator bool() const noexcept { return !!get(); }
|
||||
|
||||
@ -160,7 +160,7 @@ namespace toml
|
||||
"Template type parameter must be one of the basic value types, a toml::table, or a toml::array"
|
||||
);
|
||||
|
||||
return traits::template as<U>(obj_, key_);
|
||||
return traits::template as<U>(haystack_, key_);
|
||||
}
|
||||
|
||||
template <typename U>
|
||||
@ -171,7 +171,7 @@ namespace toml
|
||||
"Template type parameter must be one of the basic value types, a toml::table, or a toml::array"
|
||||
);
|
||||
|
||||
return traits::template as<U>(obj_, key_);
|
||||
return traits::template as<U>(haystack_, key_);
|
||||
}
|
||||
|
||||
[[nodiscard]] auto as_string() noexcept { return as<string>(); }
|
||||
@ -199,7 +199,7 @@ namespace toml
|
||||
template <typename U>
|
||||
[[nodiscard]] static bool value_equality(const node_view& lhs, const U& rhs) noexcept
|
||||
{
|
||||
const auto val = lhs.as<value_of<U>>();
|
||||
const auto val = lhs.as<promoted<U>>();
|
||||
return val && val->get() == rhs;
|
||||
}
|
||||
|
||||
@ -210,7 +210,7 @@ namespace toml
|
||||
|
||||
static_assert(
|
||||
impl::is_value_or_promotable<elem_t>,
|
||||
"Container element type must be (or be convertible to) one of the basic value types"
|
||||
"Container element type must be (or be promotable to) one of the basic value types"
|
||||
);
|
||||
|
||||
const array* arr = lhs.as<array>();
|
||||
@ -222,7 +222,7 @@ namespace toml
|
||||
size_t i{};
|
||||
for (auto& list_elem : rhs)
|
||||
{
|
||||
const auto elem = arr->get_as<value_of<elem_t>>(i++);
|
||||
const auto elem = arr->get_as<promoted<elem_t>>(i++);
|
||||
if (!elem || elem->get() != list_elem)
|
||||
return false;
|
||||
}
|
||||
@ -282,6 +282,10 @@ namespace toml
|
||||
return { this, key };
|
||||
}
|
||||
|
||||
// inline constexpr auto kek1 = sizeof(node_view<table>);
|
||||
// inline constexpr auto kek2 = sizeof(decltype(std::declval<node_view<table>>()[0]));
|
||||
// inline constexpr auto kek3 = sizeof(decltype(std::declval<node_view<table>>()["kek"sv]));
|
||||
|
||||
inline node_view<const table> table::operator[] (string_view key) const noexcept
|
||||
{
|
||||
return { this, key };
|
||||
|
File diff suppressed because it is too large
Load Diff
286
include/toml++/toml_print_to_stream.h
Normal file
286
include/toml++/toml_print_to_stream.h
Normal file
@ -0,0 +1,286 @@
|
||||
#pragma once
|
||||
#include "toml_common.h"
|
||||
|
||||
namespace toml::impl
|
||||
{
|
||||
// Q: "why does print_to_stream() exist? why not just use ostream::write(), ostream::put() etc?"
|
||||
// A: - I'm supporting C++20's char8_t as well; wrapping streams allows switching string modes transparently.
|
||||
// - <charconv> is locale-independent.
|
||||
// - I can avoid forcing users to drag in <sstream> and <iomanip>.
|
||||
|
||||
template <typename CHAR1, typename CHAR2>
|
||||
TOML_ALWAYS_INLINE
|
||||
void print_to_stream(std::basic_string_view<CHAR1> str, std::basic_ostream<CHAR2>& stream) TOML_MAY_THROW
|
||||
{
|
||||
static_assert(sizeof(CHAR1) == 1);
|
||||
static_assert(sizeof(CHAR2) == 1);
|
||||
stream.write(str.data(), str.length());
|
||||
}
|
||||
|
||||
template <typename CHAR1, typename CHAR2>
|
||||
TOML_ALWAYS_INLINE
|
||||
void print_to_stream(const std::basic_string<CHAR1>& str, std::basic_ostream<CHAR2>& stream) TOML_MAY_THROW
|
||||
{
|
||||
static_assert(sizeof(CHAR1) == 1);
|
||||
static_assert(sizeof(CHAR2) == 1);
|
||||
stream.write(str.data(), str.length());
|
||||
}
|
||||
|
||||
template <typename CHAR>
|
||||
TOML_ALWAYS_INLINE
|
||||
void print_to_stream(char character, std::basic_ostream<CHAR>& stream) TOML_MAY_THROW
|
||||
{
|
||||
static_assert(sizeof(CHAR) == 1);
|
||||
stream.put(static_cast<CHAR>(character));
|
||||
}
|
||||
|
||||
template <typename CHAR>
|
||||
TOML_GCC_ATTR(nonnull) TOML_ALWAYS_INLINE
|
||||
void print_to_stream(const char* str, size_t len, std::basic_ostream<CHAR>& stream) TOML_MAY_THROW
|
||||
{
|
||||
static_assert(sizeof(CHAR) == 1);
|
||||
stream.write(reinterpret_cast<const CHAR*>(str), static_cast<std::streamsize>(len));
|
||||
}
|
||||
|
||||
#if defined(__cpp_lib_char8_t)
|
||||
|
||||
template <typename CHAR>
|
||||
TOML_ALWAYS_INLINE
|
||||
void print_to_stream(char8_t character, std::basic_ostream<CHAR>& stream) TOML_MAY_THROW
|
||||
{
|
||||
static_assert(sizeof(CHAR) == 1);
|
||||
stream.put(static_cast<CHAR>(character));
|
||||
}
|
||||
|
||||
template <typename CHAR>
|
||||
TOML_GCC_ATTR(nonnull) TOML_ALWAYS_INLINE
|
||||
void print_to_stream(const char8_t* str, size_t len, std::basic_ostream<CHAR>& stream) TOML_MAY_THROW
|
||||
{
|
||||
static_assert(sizeof(CHAR) == 1);
|
||||
stream.write(reinterpret_cast<const CHAR*>(str), static_cast<std::streamsize>(len));
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
template <typename T> inline constexpr size_t charconv_buffer_length = 0;
|
||||
template <> inline constexpr size_t charconv_buffer_length<double> = 60;
|
||||
template <> inline constexpr size_t charconv_buffer_length<float> = 40;
|
||||
template <> inline constexpr size_t charconv_buffer_length<uint64_t> = 20; //strlen("18446744073709551615")
|
||||
template <> inline constexpr size_t charconv_buffer_length<int64_t> = 20; //strlen("-9223372036854775808")
|
||||
template <> inline constexpr size_t charconv_buffer_length<int32_t> = 11; //strlen("-2147483648")
|
||||
template <> inline constexpr size_t charconv_buffer_length<int16_t> = 6; //strlen("-32768")
|
||||
template <> inline constexpr size_t charconv_buffer_length<int8_t> = 4; //strlen("-128")
|
||||
template <> inline constexpr size_t charconv_buffer_length<uint32_t> = 10; //strlen("4294967295")
|
||||
template <> inline constexpr size_t charconv_buffer_length<uint16_t> = 5; //strlen("65535")
|
||||
template <> inline constexpr size_t charconv_buffer_length<uint8_t> = 3; //strlen("255")
|
||||
|
||||
template <typename T, typename CHAR>
|
||||
inline void print_integer_to_stream(T val, std::basic_ostream<CHAR>& stream) TOML_MAY_THROW
|
||||
{
|
||||
static_assert(
|
||||
sizeof(CHAR) == 1,
|
||||
"The stream's underlying character type must be 1 byte in size."
|
||||
);
|
||||
|
||||
TOML_GCC_ATTR(uninitialized) char buf[charconv_buffer_length<T>];
|
||||
const auto res = std::to_chars(buf, buf + sizeof(buf), val);
|
||||
print_to_stream(buf, static_cast<size_t>(res.ptr - buf), stream);
|
||||
}
|
||||
|
||||
#define TOML_P2S_OVERLOAD(type) \
|
||||
template <typename CHAR> \
|
||||
TOML_ALWAYS_INLINE \
|
||||
void print_to_stream(type val, std::basic_ostream<CHAR>& stream) TOML_MAY_THROW \
|
||||
{ \
|
||||
static_assert(sizeof(CHAR) == 1); \
|
||||
print_integer_to_stream(val, stream); \
|
||||
}
|
||||
|
||||
TOML_P2S_OVERLOAD(int8_t)
|
||||
TOML_P2S_OVERLOAD(int16_t)
|
||||
TOML_P2S_OVERLOAD(int32_t)
|
||||
TOML_P2S_OVERLOAD(int64_t)
|
||||
TOML_P2S_OVERLOAD(uint8_t)
|
||||
TOML_P2S_OVERLOAD(uint16_t)
|
||||
TOML_P2S_OVERLOAD(uint32_t)
|
||||
TOML_P2S_OVERLOAD(uint64_t)
|
||||
|
||||
#undef TOML_P2S_OVERLOAD
|
||||
|
||||
template <typename T, typename CHAR>
|
||||
inline void print_floating_point_to_stream(T val, std::basic_ostream<CHAR>& stream) TOML_MAY_THROW
|
||||
{
|
||||
static_assert(
|
||||
sizeof(CHAR) == 1,
|
||||
"The stream's underlying character type must be 1 byte in size."
|
||||
);
|
||||
|
||||
static constexpr auto needs_decimal_point = [](auto&& s) noexcept
|
||||
{
|
||||
for (auto c : s)
|
||||
if (c == '.' || c == 'E' || c == 'e')
|
||||
return false;
|
||||
return true;
|
||||
};
|
||||
|
||||
#if TOML_USE_STREAMS_FOR_FLOATS
|
||||
{
|
||||
std::ostringstream oss;
|
||||
oss << val;
|
||||
const auto str = oss.str();
|
||||
print_to_stream(str, stream);
|
||||
if (needs_decimal_point(str))
|
||||
print_to_stream(".0"sv, stream);
|
||||
}
|
||||
#else
|
||||
{
|
||||
TOML_GCC_ATTR(uninitialized) char buf[charconv_buffer_length<T>];
|
||||
const auto res = std::to_chars(buf, buf + sizeof(buf), val);
|
||||
const auto str = std::string_view{ buf, static_cast<size_t>(res.ptr - buf) };
|
||||
print_to_stream(str, stream);
|
||||
if (needs_decimal_point(str))
|
||||
print_to_stream(".0"sv, stream);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
#define TOML_P2S_OVERLOAD(type) \
|
||||
template <typename CHAR> \
|
||||
TOML_ALWAYS_INLINE \
|
||||
void print_to_stream(type val, std::basic_ostream<CHAR>& stream) TOML_MAY_THROW \
|
||||
{ \
|
||||
static_assert(sizeof(CHAR) == 1); \
|
||||
print_floating_point_to_stream(val, stream); \
|
||||
}
|
||||
|
||||
TOML_P2S_OVERLOAD(float)
|
||||
TOML_P2S_OVERLOAD(double)
|
||||
|
||||
#undef TOML_P2S_OVERLOAD
|
||||
|
||||
template <typename CHAR>
|
||||
TOML_ALWAYS_INLINE
|
||||
void print_to_stream(bool& val, std::basic_ostream<CHAR>& stream) TOML_MAY_THROW
|
||||
{
|
||||
static_assert(sizeof(CHAR) == 1);
|
||||
print_to_stream(val ? "true"sv : "false"sv, stream);
|
||||
}
|
||||
|
||||
template <typename T, typename CHAR>
|
||||
inline void print_to_stream(T val, std::basic_ostream<CHAR>& stream, size_t zero_pad_to_digits) TOML_MAY_THROW
|
||||
{
|
||||
static_assert(sizeof(CHAR) == 1);
|
||||
TOML_GCC_ATTR(uninitialized) char buf[charconv_buffer_length<T>];
|
||||
const auto res = std::to_chars(buf, buf + sizeof(buf), val);
|
||||
const auto len = static_cast<size_t>(res.ptr - buf);
|
||||
for (size_t i = len; i < zero_pad_to_digits; i++)
|
||||
print_to_stream('0', stream);
|
||||
print_to_stream(buf, static_cast<size_t>(res.ptr - buf), stream);
|
||||
}
|
||||
|
||||
template <typename CHAR>
|
||||
inline void print_to_stream(const toml::date& val, std::basic_ostream<CHAR>& stream) TOML_MAY_THROW
|
||||
{
|
||||
static_assert(sizeof(CHAR) == 1);
|
||||
print_to_stream(val.year, stream, 4_sz);
|
||||
print_to_stream('-', stream);
|
||||
print_to_stream(val.month, stream, 2_sz);
|
||||
print_to_stream('-', stream);
|
||||
print_to_stream(val.day, stream, 2_sz);
|
||||
}
|
||||
|
||||
template <typename CHAR>
|
||||
inline void print_to_stream(const toml::time& val, std::basic_ostream<CHAR>& stream) TOML_MAY_THROW
|
||||
{
|
||||
static_assert(sizeof(CHAR) == 1);
|
||||
print_to_stream(val.hour, stream, 2_sz);
|
||||
print_to_stream(':', stream);
|
||||
print_to_stream(val.minute, stream, 2_sz);
|
||||
print_to_stream(':', stream);
|
||||
print_to_stream(val.second, stream, 2_sz);
|
||||
if (val.nanosecond && val.nanosecond <= 999999999u)
|
||||
{
|
||||
print_to_stream('.', stream);
|
||||
auto ns = val.nanosecond;
|
||||
size_t digits = 9_sz;
|
||||
while (ns % 10u == 0u)
|
||||
{
|
||||
ns /= 10u;
|
||||
digits--;
|
||||
}
|
||||
print_to_stream(ns, stream, digits);
|
||||
}
|
||||
}
|
||||
|
||||
template <typename CHAR>
|
||||
inline void print_to_stream(const toml::time_offset& val, std::basic_ostream<CHAR>& stream) TOML_MAY_THROW
|
||||
{
|
||||
static_assert(sizeof(CHAR) == 1);
|
||||
if (!val.minutes)
|
||||
print_to_stream('Z', stream);
|
||||
else
|
||||
{
|
||||
auto mins = static_cast<int>(val.minutes);
|
||||
if (mins < 0)
|
||||
{
|
||||
print_to_stream('-', stream);
|
||||
mins = -mins;
|
||||
}
|
||||
else
|
||||
print_to_stream('+', stream);
|
||||
const auto hours = mins / 60;
|
||||
if (hours)
|
||||
{
|
||||
print_to_stream(static_cast<unsigned int>(hours), stream, 2_sz);
|
||||
mins -= hours * 60;
|
||||
}
|
||||
else
|
||||
print_to_stream("00"sv, stream);
|
||||
print_to_stream(':', stream);
|
||||
print_to_stream(static_cast<unsigned int>(mins), stream, 2_sz);
|
||||
}
|
||||
}
|
||||
|
||||
template <typename CHAR>
|
||||
inline void print_to_stream(const toml::date_time& val, std::basic_ostream<CHAR>& stream) TOML_MAY_THROW
|
||||
{
|
||||
static_assert(sizeof(CHAR) == 1);
|
||||
|
||||
print_to_stream(val.date, stream);
|
||||
print_to_stream('T', stream);
|
||||
print_to_stream(val.time, stream);
|
||||
if (val.time_offset)
|
||||
print_to_stream(*val.time_offset, stream);
|
||||
}
|
||||
}
|
||||
|
||||
namespace toml
|
||||
{
|
||||
template <typename CHAR>
|
||||
inline std::basic_ostream<CHAR>& operator << (std::basic_ostream<CHAR>& lhs, const date& rhs) TOML_MAY_THROW
|
||||
{
|
||||
impl::print_to_stream(rhs, lhs);
|
||||
return lhs;
|
||||
}
|
||||
|
||||
template <typename CHAR>
|
||||
inline std::basic_ostream<CHAR>& operator << (std::basic_ostream<CHAR>& lhs, const time& rhs) TOML_MAY_THROW
|
||||
{
|
||||
impl::print_to_stream(rhs, lhs);
|
||||
return lhs;
|
||||
}
|
||||
|
||||
template <typename CHAR>
|
||||
inline std::basic_ostream<CHAR>& operator << (std::basic_ostream<CHAR>& lhs, const time_offset& rhs) TOML_MAY_THROW
|
||||
{
|
||||
impl::print_to_stream(rhs, lhs);
|
||||
return lhs;
|
||||
}
|
||||
|
||||
template <typename CHAR>
|
||||
inline std::basic_ostream<CHAR>& operator << (std::basic_ostream<CHAR>& lhs, const date_time& rhs) TOML_MAY_THROW
|
||||
{
|
||||
impl::print_to_stream(rhs, lhs);
|
||||
return lhs;
|
||||
}
|
||||
}
|
@ -1,6 +1,90 @@
|
||||
#pragma once
|
||||
#include "toml_node.h"
|
||||
|
||||
namespace toml::impl
|
||||
{
|
||||
template <bool is_const>
|
||||
struct table_iterator_ref_proxy final
|
||||
{
|
||||
using value_type = std::conditional_t<is_const, const node, node>;
|
||||
|
||||
const string& key;
|
||||
value_type& value;
|
||||
};
|
||||
|
||||
template <bool is_const>
|
||||
class table_iterator final
|
||||
{
|
||||
private:
|
||||
friend class toml::table;
|
||||
|
||||
using raw_iterator = std::conditional_t<
|
||||
is_const,
|
||||
string_map<std::unique_ptr<node>>::const_iterator,
|
||||
string_map<std::unique_ptr<node>>::iterator
|
||||
>;
|
||||
|
||||
mutable raw_iterator raw_;
|
||||
|
||||
table_iterator(const raw_iterator& raw) noexcept
|
||||
: raw_{ raw }
|
||||
{}
|
||||
|
||||
table_iterator(raw_iterator&& raw) noexcept
|
||||
: raw_{ std::move(raw) }
|
||||
{}
|
||||
|
||||
public:
|
||||
|
||||
table_iterator() noexcept = default;
|
||||
|
||||
using reference = table_iterator_ref_proxy<is_const>;
|
||||
|
||||
table_iterator& operator++() noexcept // ++pre
|
||||
{
|
||||
++raw_;
|
||||
return *this;
|
||||
}
|
||||
|
||||
table_iterator operator++(int) noexcept // post++
|
||||
{
|
||||
table_iterator out{ raw_ };
|
||||
++raw_;
|
||||
return out;
|
||||
}
|
||||
|
||||
table_iterator& operator--() noexcept // --pre
|
||||
{
|
||||
--raw_;
|
||||
return *this;
|
||||
}
|
||||
|
||||
table_iterator operator--(int) noexcept // post--
|
||||
{
|
||||
table_iterator out{ raw_ };
|
||||
--raw_;
|
||||
return out;
|
||||
}
|
||||
|
||||
reference operator* () const noexcept
|
||||
{
|
||||
return { raw_->first, *raw_->second.get() };
|
||||
}
|
||||
|
||||
[[nodiscard]]
|
||||
friend constexpr bool operator == (const table_iterator& lhs, const table_iterator& rhs) noexcept
|
||||
{
|
||||
return lhs.raw_ == rhs.raw_;
|
||||
}
|
||||
|
||||
[[nodiscard]]
|
||||
friend constexpr bool operator != (const table_iterator& lhs, const table_iterator& rhs) noexcept
|
||||
{
|
||||
return lhs.raw_ != rhs.raw_;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
namespace toml
|
||||
{
|
||||
class table final
|
||||
@ -8,14 +92,15 @@ namespace toml
|
||||
{
|
||||
private:
|
||||
friend class impl::parser;
|
||||
friend class default_formatter;
|
||||
friend class json_formatter;
|
||||
friend class node_view<table>;
|
||||
|
||||
string_map<std::unique_ptr<node>> values;
|
||||
bool inline_ = false;
|
||||
|
||||
public:
|
||||
|
||||
using iterator = impl::table_iterator<false>;
|
||||
using const_iterator = impl::table_iterator<true>;
|
||||
|
||||
TOML_NODISCARD_CTOR
|
||||
table() noexcept {}
|
||||
@ -35,45 +120,107 @@ namespace toml
|
||||
return *this;
|
||||
}
|
||||
|
||||
[[nodiscard]] node_type type() const noexcept override { return node_type::table; }
|
||||
|
||||
[[nodiscard]] bool is_table() const noexcept override { return true; }
|
||||
[[nodiscard]] bool is_array() const noexcept override { return false; }
|
||||
[[nodiscard]] bool is_value() const noexcept override { return false; }
|
||||
|
||||
[[nodiscard]] bool is_inline() const noexcept { return inline_; }
|
||||
|
||||
[[nodiscard]] table* as_table() noexcept override { return this; }
|
||||
[[nodiscard]] const table* as_table() const noexcept override { return this; }
|
||||
|
||||
[[nodiscard]] node_type type() const noexcept override { return node_type::table; }
|
||||
|
||||
[[nodiscard]] bool empty() const noexcept { return values.empty(); }
|
||||
|
||||
[[nodiscard]] size_t size() const noexcept { return values.size(); }
|
||||
|
||||
[[nodiscard]] node* get(string_view key) noexcept
|
||||
[[nodiscard]] iterator begin() noexcept { return { values.begin() }; }
|
||||
[[nodiscard]] const_iterator begin() const noexcept { return { values.begin() }; }
|
||||
[[nodiscard]] const_iterator cbegin() const noexcept { return { values.cbegin() }; }
|
||||
|
||||
[[nodiscard]] iterator end() noexcept { return { values.end() }; }
|
||||
[[nodiscard]] const_iterator end() const noexcept { return { values.end() };}
|
||||
[[nodiscard]] const_iterator cend() const noexcept { return { values.cend() }; }
|
||||
|
||||
iterator erase(iterator pos) noexcept
|
||||
{
|
||||
return { values.erase(pos.raw_) };
|
||||
}
|
||||
iterator erase(const_iterator pos) noexcept
|
||||
{
|
||||
return { values.erase(pos.raw_) };
|
||||
}
|
||||
iterator erase(const_iterator first, const_iterator last) noexcept
|
||||
{
|
||||
return { values.erase(first.raw_, last.raw_) };
|
||||
}
|
||||
bool erase(const string& key) noexcept
|
||||
{
|
||||
return values.erase(key) > 0_sz;
|
||||
}
|
||||
bool erase(string_view key) noexcept
|
||||
{
|
||||
if (auto it = values.find(key); it != values.end())
|
||||
return it->second.get();
|
||||
return nullptr;
|
||||
{
|
||||
values.erase(it);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
[[nodiscard]] const node* get(string_view key) const noexcept
|
||||
private:
|
||||
|
||||
template <typename MAP, typename KEY>
|
||||
[[nodiscard]] static auto do_get(MAP& vals, const KEY& key) noexcept
|
||||
{
|
||||
if (auto it = values.find(key); it != values.end())
|
||||
return it->second.get();
|
||||
return nullptr;
|
||||
using return_type = std::conditional_t<
|
||||
std::is_const_v<MAP>,
|
||||
const node*,
|
||||
node*
|
||||
>;
|
||||
|
||||
if (auto it = vals.find(key); it != vals.end())
|
||||
return return_type{ it->second.get() };
|
||||
return return_type{};
|
||||
}
|
||||
|
||||
template <typename T, typename MAP, typename KEY>
|
||||
[[nodiscard]] static auto do_get_as(MAP& vals, const KEY& key) noexcept
|
||||
{
|
||||
const auto node = do_get(vals, key);
|
||||
return node ? node->template as<T>() : nullptr;
|
||||
}
|
||||
|
||||
template <typename MAP, typename KEY>
|
||||
[[nodiscard]] TOML_ALWAYS_INLINE
|
||||
static bool do_contains(MAP& vals, const KEY& key) noexcept
|
||||
{
|
||||
#if TOML_CPP >= 20
|
||||
return vals.contains(key);
|
||||
#else
|
||||
return do_get(vals, key) != nullptr;
|
||||
#endif
|
||||
}
|
||||
|
||||
public:
|
||||
|
||||
[[nodiscard]] node* get(string_view key) noexcept { return do_get(values, key); }
|
||||
[[nodiscard]] node* get(const string& key) noexcept { return do_get(values, key); }
|
||||
[[nodiscard]] const node* get(string_view key) const noexcept { return do_get(values, key); }
|
||||
[[nodiscard]] const node* get(const string& key) const noexcept { return do_get(values, key); }
|
||||
|
||||
template <typename T>
|
||||
[[nodiscard]] node_of<T>* get_as(string_view key) noexcept
|
||||
{
|
||||
const auto node = get(key);
|
||||
return node ? node->as<T>() : nullptr;
|
||||
}
|
||||
|
||||
[[nodiscard]] node_of<T>* get_as(string_view key) noexcept { return do_get_as<T>(values, key); }
|
||||
template <typename T>
|
||||
[[nodiscard]] const node_of<T>* get_as(string_view key) const noexcept
|
||||
{
|
||||
const auto node = get(key);
|
||||
return node ? node->as<T>() : nullptr;
|
||||
}
|
||||
[[nodiscard]] node_of<T>* get_as(const string& key) noexcept { return do_get_as<T>(values, key); }
|
||||
template <typename T>
|
||||
[[nodiscard]] const node_of<T>* get_as(string_view key) const noexcept { return do_get_as<T>(values, key); }
|
||||
template <typename T>
|
||||
[[nodiscard]] const node_of<T>* get_as(const string& key) const noexcept { return do_get_as<T>(values, key); }
|
||||
|
||||
[[nodiscard]] bool contains(const string& key) const noexcept { return do_contains(values, key); }
|
||||
[[nodiscard]] bool contains(string_view key) const noexcept { return do_contains(values, key); }
|
||||
|
||||
[[nodiscard]] inline node_view<table> operator[] (string_view) noexcept;
|
||||
[[nodiscard]] inline node_view<const table> operator[] (string_view) const noexcept;
|
||||
|
@ -42,8 +42,7 @@ namespace toml::impl
|
||||
[[nodiscard]] TOML_ALWAYS_INLINE
|
||||
constexpr bool is_string_delimiter(char32_t codepoint) noexcept
|
||||
{
|
||||
return codepoint == U'"'
|
||||
|| codepoint == U'\'';
|
||||
return codepoint == U'"' || codepoint == U'\'';
|
||||
}
|
||||
|
||||
[[nodiscard]] TOML_ALWAYS_INLINE
|
||||
@ -71,7 +70,7 @@ namespace toml::impl
|
||||
return (codepoint >= U'0' && codepoint <= U'9');
|
||||
}
|
||||
|
||||
[[nodiscard]] TOML_ALWAYS_INLINE
|
||||
[[nodiscard]]
|
||||
constexpr bool is_hex_digit(char32_t codepoint) noexcept
|
||||
{
|
||||
return (codepoint >= U'a' && codepoint <= U'f')
|
||||
@ -87,7 +86,7 @@ namespace toml::impl
|
||||
|| codepoint == U'-'
|
||||
|| codepoint == U'_'
|
||||
#if TOML_LANG_HIGHER_THAN(0, 5, 0) // toml/issues/644 & toml/issues/687
|
||||
|| codepoint == U'+'
|
||||
|| codepoint == U'+'
|
||||
|| is_unicode_letter(codepoint)
|
||||
|| is_unicode_number(codepoint)
|
||||
#endif
|
||||
@ -334,16 +333,16 @@ namespace toml::impl
|
||||
|
||||
#if TOML_EXCEPTIONS
|
||||
#define TOML_ERROR_CHECK (void)0
|
||||
#define TOML_ERROR(...) throw toml::parse_error{ __VA_ARGS__ }
|
||||
#define TOML_ERROR throw toml::parse_error
|
||||
#else
|
||||
#define TOML_ERROR_CHECK if (err) return nullptr
|
||||
#define TOML_ERROR(...) err.emplace( __VA_ARGS__ )
|
||||
#define TOML_ERROR err.emplace
|
||||
#endif
|
||||
|
||||
struct TOML_INTERFACE utf8_reader_interface
|
||||
{
|
||||
[[nodiscard]]
|
||||
virtual const std::shared_ptr<const std::string>& source_path() const noexcept = 0;
|
||||
virtual const toml::source_path_ptr& source_path() const noexcept = 0;
|
||||
|
||||
[[nodiscard]]
|
||||
virtual const utf8_codepoint* read_next() TOML_MAY_THROW = 0;
|
||||
@ -367,7 +366,7 @@ namespace toml::impl
|
||||
utf8_decoder decoder;
|
||||
utf8_codepoint prev{}, current{};
|
||||
uint8_t current_byte_count{};
|
||||
std::shared_ptr<const std::string> source_path_;
|
||||
source_path_ptr source_path_;
|
||||
#if !TOML_EXCEPTIONS
|
||||
std::optional<toml::parse_error> err;
|
||||
#endif
|
||||
@ -379,14 +378,14 @@ namespace toml::impl
|
||||
TOML_CONDITIONAL_NOEXCEPT(std::is_nothrow_constructible_v<utf8_byte_stream<T>, U&&>)
|
||||
: stream{ std::forward<U>(source) }
|
||||
{
|
||||
current.position = { 1u, 1u };
|
||||
current.position = { 1, 1 };
|
||||
|
||||
if (!source_path.empty())
|
||||
source_path_ = std::make_shared<const std::string>(std::forward<STR>(source_path));
|
||||
}
|
||||
|
||||
[[nodiscard]]
|
||||
const std::shared_ptr<const std::string>& source_path() const noexcept override
|
||||
const source_path_ptr& source_path() const noexcept override
|
||||
{
|
||||
return source_path_;
|
||||
}
|
||||
@ -463,7 +462,7 @@ namespace toml::impl
|
||||
if (is_line_break<false>(prev.value))
|
||||
{
|
||||
current.position.line++;
|
||||
current.position.column = 1u;
|
||||
current.position.column = 1;
|
||||
}
|
||||
else
|
||||
current.position.column++;
|
||||
@ -506,10 +505,10 @@ namespace toml::impl
|
||||
: public utf8_reader_interface
|
||||
{
|
||||
public:
|
||||
static constexpr auto max_history_length = 64_sz;
|
||||
static constexpr size_t max_history_length = 64;
|
||||
|
||||
private:
|
||||
static constexpr auto history_buffer_size = max_history_length - 1_sz; //'head' is stored in the reader
|
||||
static constexpr size_t history_buffer_size = max_history_length - 1; //'head' is stored in the reader
|
||||
utf8_reader_interface& reader;
|
||||
struct
|
||||
{
|
||||
@ -528,7 +527,7 @@ namespace toml::impl
|
||||
{}
|
||||
|
||||
[[nodiscard]]
|
||||
const std::shared_ptr<const std::string>& source_path() const noexcept override
|
||||
const toml::source_path_ptr& source_path() const noexcept override
|
||||
{
|
||||
return reader.source_path();
|
||||
}
|
||||
|
@ -1,5 +1,6 @@
|
||||
#pragma once
|
||||
#include "toml_node.h"
|
||||
#include "toml_print_to_stream.h"
|
||||
|
||||
namespace toml
|
||||
{
|
||||
@ -58,6 +59,21 @@ namespace toml
|
||||
return *this;
|
||||
}
|
||||
|
||||
[[nodiscard]] node_type type() const noexcept override
|
||||
{
|
||||
if constexpr (std::is_same_v<T, string>) return node_type::string;
|
||||
else if constexpr (std::is_same_v<T, int64_t>) return node_type::integer;
|
||||
else if constexpr (std::is_same_v<T, double>) return node_type::floating_point;
|
||||
else if constexpr (std::is_same_v<T, bool>) return node_type::boolean;
|
||||
else if constexpr (std::is_same_v<T, date>) return node_type::date;
|
||||
else if constexpr (std::is_same_v<T, time>) return node_type::time;
|
||||
else if constexpr (std::is_same_v<T, date_time>) return node_type::date_time;
|
||||
}
|
||||
|
||||
[[nodiscard]] bool is_table() const noexcept override { return false; }
|
||||
[[nodiscard]] bool is_array() const noexcept override { return false; }
|
||||
[[nodiscard]] bool is_value() const noexcept override { return true; }
|
||||
|
||||
[[nodiscard]] bool is_string() const noexcept override { return std::is_same_v<T, string>; }
|
||||
[[nodiscard]] bool is_integer() const noexcept override { return std::is_same_v<T, int64_t>; }
|
||||
[[nodiscard]] bool is_floating_point() const noexcept override { return std::is_same_v<T, double>; }
|
||||
@ -82,19 +98,24 @@ namespace toml
|
||||
[[nodiscard]] const value<time>* as_time() const noexcept override { return as_value<time>(); }
|
||||
[[nodiscard]] const value<date_time>* as_date_time() const noexcept override { return as_value<date_time>(); }
|
||||
|
||||
[[nodiscard]] node_type type() const noexcept override
|
||||
{
|
||||
if constexpr (std::is_same_v<T, string>) return node_type::string;
|
||||
else if constexpr (std::is_same_v<T, int64_t>) return node_type::integer;
|
||||
else if constexpr (std::is_same_v<T, double>) return node_type::floating_point;
|
||||
else if constexpr (std::is_same_v<T, bool>) return node_type::boolean;
|
||||
else if constexpr (std::is_same_v<T, date>) return node_type::date;
|
||||
else if constexpr (std::is_same_v<T, time>) return node_type::time;
|
||||
else if constexpr (std::is_same_v<T, date_time>) return node_type::date_time;
|
||||
}
|
||||
[[nodiscard]] T& get() & noexcept { return val_; }
|
||||
[[nodiscard]] T&& get() && noexcept { return std::move(val_); }
|
||||
[[nodiscard]] const T& get() const & noexcept { return val_; }
|
||||
|
||||
[[nodiscard]] T& get() noexcept { return val_; }
|
||||
[[nodiscard]] const T& get() const noexcept { return val_; }
|
||||
[[nodiscard]] T& operator* () & noexcept { return val_; }
|
||||
[[nodiscard]] T&& operator* () && noexcept { return std::move(val_); }
|
||||
[[nodiscard]] const T& operator* () const& noexcept { return val_; }
|
||||
|
||||
[[nodiscard]] operator T& () & noexcept { return val_; }
|
||||
[[nodiscard]] operator T&& () && noexcept { return std::move(val_); }
|
||||
[[nodiscard]] operator const T& () const& noexcept { return val_; }
|
||||
|
||||
template <typename CHAR>
|
||||
friend std::basic_ostream<CHAR>& operator << (std::basic_ostream<CHAR>& lhs, const value& rhs) TOML_MAY_THROW
|
||||
{
|
||||
impl::print_to_stream(rhs.val_, lhs);
|
||||
return lhs;
|
||||
}
|
||||
};
|
||||
|
||||
value(const string_char*) -> value<string>;
|
||||
|
@ -58,11 +58,7 @@ class Preprocessor:
|
||||
|
||||
if (self.current_level == 1):
|
||||
header_text = '↓ ' + raw_incl
|
||||
calc_pad = lambda i : 15 + i * 18
|
||||
lpad = calc_pad(self.header_indent)
|
||||
if (self.header_indent > 0 and (lpad + len(header_text) + 4) > 120):
|
||||
self.header_indent = 0
|
||||
lpad = calc_pad(self.header_indent)
|
||||
lpad = 23 + ((25 * (self.header_indent % 4)) - int((len(header_text) + 4) / 2))
|
||||
self.header_indent += 1
|
||||
return '\n{}\n#pragma region {}\n\n{}\n\n#pragma endregion {}\n{}'.format(
|
||||
make_divider(header_text, lpad), '', text, '', make_divider('↑ ' + raw_incl, lpad))
|
||||
|
@ -8,27 +8,27 @@ TEST_CASE("lifetime - tables")
|
||||
S(R"(test = { val1 = "foo" })"sv),
|
||||
[&](table&& tbl) noexcept
|
||||
{
|
||||
CHECK(tbl.region().begin == source_position{ 1, 1 });
|
||||
CHECK(tbl.region().end == source_position{ 1, 25 });
|
||||
CHECK(tbl.region().path);
|
||||
CHECK(*tbl.region().path == filename);
|
||||
CHECK(tbl.source().begin == source_position{ 1, 1 });
|
||||
CHECK(tbl.source().end == source_position{ 1, 25 });
|
||||
CHECK(tbl.source().path);
|
||||
CHECK(*tbl.source().path == filename);
|
||||
CHECK(tbl.size() == 1_sz);
|
||||
REQUIRE(tbl[S("test")].as<table>());
|
||||
CHECK(tbl[S("test")].as<table>()->size() == 1_sz);
|
||||
CHECK(tbl[S("test")][S("val1")] == S("foo"sv));
|
||||
|
||||
table test_table;
|
||||
CHECK(test_table.region().begin == source_position{});
|
||||
CHECK(test_table.region().end == source_position{});
|
||||
CHECK(!test_table.region().path);
|
||||
CHECK(test_table.source().begin == source_position{});
|
||||
CHECK(test_table.source().end == source_position{});
|
||||
CHECK(!test_table.source().path);
|
||||
CHECK(test_table.size() == 0_sz);
|
||||
CHECK(!test_table[S("test")].as<table>());
|
||||
|
||||
test_table = std::move(tbl);
|
||||
CHECK(test_table.region().begin == source_position{ 1, 1 });
|
||||
CHECK(test_table.region().end == source_position{ 1, 25 });
|
||||
CHECK(test_table.region().path);
|
||||
CHECK(*test_table.region().path == filename);
|
||||
CHECK(test_table.source().begin == source_position{ 1, 1 });
|
||||
CHECK(test_table.source().end == source_position{ 1, 25 });
|
||||
CHECK(test_table.source().path);
|
||||
CHECK(*test_table.source().path == filename);
|
||||
CHECK(test_table.size() == 1_sz);
|
||||
REQUIRE(test_table[S("test")].as<table>());
|
||||
CHECK(test_table[S("test")].as<table>()->size() == 1_sz);
|
||||
|
@ -1,7 +1,7 @@
|
||||
#include "tests.h"
|
||||
|
||||
TOML_PUSH_WARNINGS
|
||||
TOML_DISABLE_FIELD_INIT_WARNING
|
||||
TOML_DISABLE_INIT_WARNINGS
|
||||
|
||||
TEST_CASE("parsing dates and times")
|
||||
{
|
||||
@ -20,9 +20,9 @@ lt2 = 00:32:00.999999
|
||||
{
|
||||
static constexpr auto odt1 = date_time{ { 1979, 5, 27 }, { 7, 32 }, time_offset{} };
|
||||
CHECK(tbl[S("odt1")] == odt1);
|
||||
static constexpr auto odt2 = date_time{ { 1979, 5, 27 }, { 0, 32 }, time_offset{ -7 } };
|
||||
static constexpr auto odt2 = date_time{ { 1979, 5, 27 }, { 0, 32 }, time_offset::from_hh_mm(-7, 0) };
|
||||
CHECK(tbl[S("odt2")] == odt2);
|
||||
static constexpr auto odt3 = date_time{ { 1979, 5, 27 }, { 0, 32, 0, 999999000u }, time_offset{ -7 } };
|
||||
static constexpr auto odt3 = date_time{ { 1979, 5, 27 }, { 0, 32, 0, 999999000u }, time_offset::from_hh_mm(-7, 0) };
|
||||
CHECK(tbl[S("odt3")] == odt3);
|
||||
static constexpr auto odt4 = date_time{ { 1979, 5, 27 }, { 7, 32 }, time_offset{} };
|
||||
CHECK(tbl[S("odt4")] == odt4);
|
||||
@ -38,6 +38,91 @@ lt2 = 00:32:00.999999
|
||||
CHECK(tbl[S("lt2")] == lt2);
|
||||
}
|
||||
);
|
||||
|
||||
//value tests
|
||||
parse_expected_value( "1987-03-16"sv, date{ 1987, 3, 16 } );
|
||||
parse_expected_value( "10:20:30"sv, toml::time{ 10, 20, 30 } );
|
||||
parse_expected_value( "10:20:30.04"sv, toml::time{ 10, 20, 30, 40000000 } );
|
||||
{
|
||||
const auto val = date_time{ { 1987, 3, 16 }, { 10, 20, 30 } };
|
||||
parse_expected_value("1987-03-16T10:20:30"sv, val);
|
||||
parse_expected_value("1987-03-16 10:20:30"sv, val);
|
||||
}
|
||||
{
|
||||
const auto val = date_time{ { 1987, 3, 16 }, { 10, 20, 30 }, time_offset::from_hh_mm( -9, -30 ) };
|
||||
parse_expected_value("1987-03-16T10:20:30-09:30"sv, val);
|
||||
parse_expected_value("1987-03-16 10:20:30-09:30"sv, val);
|
||||
}
|
||||
{
|
||||
const auto val = date_time{ { 1987, 3, 16 }, { 10, 20, 30 }, time_offset::from_hh_mm( 9, 30 ) };
|
||||
parse_expected_value("1987-03-16T10:20:30+09:30"sv, val);
|
||||
parse_expected_value("1987-03-16 10:20:30+09:30"sv, val);
|
||||
}
|
||||
{
|
||||
const auto val = date_time{ { 1987, 3, 16 }, { 10, 20, 30, 40000000 } };
|
||||
parse_expected_value("1987-03-16T10:20:30.04"sv, val);
|
||||
parse_expected_value("1987-03-16 10:20:30.04"sv, val);
|
||||
}
|
||||
{
|
||||
const auto val = date_time{ { 1987, 3, 16 }, { 10, 20, 30, 40000000 }, time_offset::from_hh_mm( -9, -30 ) };
|
||||
parse_expected_value("1987-03-16T10:20:30.04-09:30"sv, val);
|
||||
parse_expected_value("1987-03-16 10:20:30.04-09:30"sv, val);
|
||||
}
|
||||
{
|
||||
const auto val = date_time{ { 1987, 3, 16 }, { 10, 20, 30, 40000000 }, time_offset::from_hh_mm( 9, 30 ) };
|
||||
parse_expected_value("1987-03-16T10:20:30.04+09:30"sv, val);
|
||||
parse_expected_value("1987-03-16 10:20:30.04+09:30"sv, val);
|
||||
}
|
||||
{
|
||||
const auto val = date_time{ { 1987, 3, 16 }, { 10, 20, 30 }, time_offset{} };
|
||||
parse_expected_value("1987-03-16T10:20:30Z"sv, val);
|
||||
parse_expected_value("1987-03-16 10:20:30Z"sv, val);
|
||||
}
|
||||
{
|
||||
const auto val = date_time{ { 1987, 3, 16 }, { 10, 20, 30, 40000000 }, time_offset{} };
|
||||
parse_expected_value("1987-03-16T10:20:30.04Z"sv, val);
|
||||
parse_expected_value("1987-03-16 10:20:30.04Z"sv, val);
|
||||
}
|
||||
|
||||
// toml/issues/671 - omitting seconds
|
||||
#if TOML_LANG_HIGHER_THAN(0, 5, 0)
|
||||
|
||||
parse_expected_value( "10:20"sv, toml::time{ 10, 20 } );
|
||||
{
|
||||
const auto val = date_time{ { 1987, 3, 16 }, { 10, 20 } };
|
||||
parse_expected_value("1987-03-16T10:20"sv, val );
|
||||
parse_expected_value("1987-03-16 10:20"sv, val );
|
||||
}
|
||||
{
|
||||
const auto val = date_time{ { 1987, 3, 16 }, { 10, 20 }, time_offset::from_hh_mm( -9, -30 ) };
|
||||
parse_expected_value("1987-03-16T10:20-09:30"sv, val);
|
||||
parse_expected_value("1987-03-16 10:20-09:30"sv, val);
|
||||
}
|
||||
{
|
||||
const auto val = date_time{ { 1987, 3, 16 }, { 10, 20 }, time_offset::from_hh_mm( 9, 30 ) };
|
||||
parse_expected_value("1987-03-16T10:20+09:30"sv, val);
|
||||
parse_expected_value("1987-03-16 10:20+09:30"sv, val);
|
||||
}
|
||||
{
|
||||
const auto val = date_time{ { 1987, 3, 16 }, { 10, 20 }, time_offset{} };
|
||||
parse_expected_value("1987-03-16T10:20Z"sv, val);
|
||||
parse_expected_value("1987-03-16 10:20Z"sv, val);
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
parsing_should_fail("10:20"sv);
|
||||
parsing_should_fail("1987-03-16T10:20"sv);
|
||||
parsing_should_fail("1987-03-16 10:20"sv);
|
||||
parsing_should_fail("1987-03-16T10:20-09:30"sv);
|
||||
parsing_should_fail("1987-03-16 10:20-09:30"sv);
|
||||
parsing_should_fail("1987-03-16T10:20+09:30"sv);
|
||||
parsing_should_fail("1987-03-16 10:20+09:30"sv);
|
||||
parsing_should_fail("1987-03-16T10:20Z"sv);
|
||||
parsing_should_fail("1987-03-16 10:20Z"sv);
|
||||
|
||||
|
||||
#endif
|
||||
}
|
||||
|
||||
TOML_POP_WARNINGS
|
||||
|
@ -47,43 +47,60 @@ flt8 = 224_617.445_991_228
|
||||
);
|
||||
|
||||
//value tests
|
||||
parse_expected_value( "1e1"sv, 1e1 );
|
||||
parse_expected_value( "1e-1"sv, 1e-1 );
|
||||
parse_expected_value( "1e+1"sv, 1e+1 );
|
||||
parse_expected_value( "1.0"sv, 1.0 );
|
||||
parse_expected_value( "0.1"sv, 0.1 );
|
||||
parse_expected_value( "0.001"sv, 0.001 );
|
||||
parse_expected_value( "0.100"sv, 0.1 );
|
||||
parse_expected_value( "+3.14"sv, 3.14 );
|
||||
parse_expected_value( "-3.14"sv, -3.14 );
|
||||
parse_expected_value( "3.1415_9265_3589"sv, 3.141592653589 );
|
||||
parse_expected_value( "+3.1415_9265_3589"sv, 3.141592653589 );
|
||||
parse_expected_value( "-3.1415_9265_3589"sv, -3.141592653589 );
|
||||
parse_expected_value( "123_456.789"sv, 123456.789 );
|
||||
parse_expected_value( "+123_456.789"sv, 123456.789 );
|
||||
parse_expected_value( "-123_456.789"sv, -123456.789 );
|
||||
parse_expected_value( "+0.0"sv, 0.0 );
|
||||
parse_expected_value( "-0.0"sv, -0.0 );
|
||||
parse_expected_value( "1e10"sv, 1e10 );
|
||||
parse_expected_value( "1e+10"sv, 1e10 );
|
||||
parse_expected_value( "1e-10"sv, 1e-10 );
|
||||
parse_expected_value( "+1e10"sv, 1e10 );
|
||||
parse_expected_value( "+1e+10"sv, 1e10 );
|
||||
parse_expected_value( "+1e-10"sv, 1e-10 );
|
||||
parse_expected_value( "-1e10"sv, -1e10 );
|
||||
parse_expected_value( "-1e+10"sv, -1e10 );
|
||||
parse_expected_value( "-1e-10"sv, -1e-10 );
|
||||
parse_expected_value( "123e-10"sv, 123e-10 );
|
||||
parse_expected_value( "1E10"sv, 1e10 );
|
||||
parse_expected_value( "1E+10"sv, 1e10 );
|
||||
parse_expected_value( "1E-10"sv, 1e-10 );
|
||||
parse_expected_value( "123E-10"sv, 123e-10 );
|
||||
parse_expected_value( "1_2_3E-10"sv, 123e-10 );
|
||||
parse_expected_value( "1_2_3E-1_0"sv, 123e-10 );
|
||||
parse_expected_value( "+0e0"sv, 0.0 );
|
||||
parse_expected_value( "-0e0"sv, -0.0 );
|
||||
parse_expected_value( "1_2_3E-01"sv, 123e-1 );
|
||||
parse_expected_value( "1_2_3E-0_1"sv, 123e-1 );
|
||||
parse_expected_value( "6.02e23"sv, 6.02e23 );
|
||||
parse_expected_value( "6.02e+23"sv, 6.02e23 );
|
||||
parse_expected_value( "1.112_650_06e-17"sv, 1.11265006e-17 );
|
||||
parse_expected_value( "1.0e1"sv, 1.0e1 );
|
||||
parse_expected_value( "1.0e-1"sv, 1.0e-1 );
|
||||
parse_expected_value( "1.0e+1"sv, 1.0e+1 );
|
||||
parse_expected_value( "+1e1"sv, +1e1 );
|
||||
parse_expected_value( "+1.0"sv, +1.0 );
|
||||
parse_expected_value( "+1.0e1"sv, +1.0e1 );
|
||||
parse_expected_value( "+1.0e+1"sv, +1.0e+1 );
|
||||
parse_expected_value( "+1.0e-1"sv, +1.0e-1 );
|
||||
parse_expected_value( "-1.0e+1"sv, -1.0e+1 );
|
||||
parse_expected_value( "-1e1"sv, -1e1 );
|
||||
parse_expected_value( "-1.0"sv, -1.0 );
|
||||
parse_expected_value( "-1.0e1"sv, -1.0e1 );
|
||||
parse_expected_value( "-1.0e-1"sv, -1.0e-1 );
|
||||
parse_expected_value( "1.0"sv, 1.0 );
|
||||
parse_expected_value( "0.1"sv, 0.1 );
|
||||
parse_expected_value( "0.001"sv, 0.001 );
|
||||
parse_expected_value( "0.100"sv, 0.100 );
|
||||
parse_expected_value( "+3.14"sv, +3.14 );
|
||||
parse_expected_value( "-3.14"sv, -3.14 );
|
||||
parse_expected_value( "3.1415_9265_3589"sv, 3.141592653589 );
|
||||
parse_expected_value( "+3.1415_9265_3589"sv, +3.141592653589 );
|
||||
parse_expected_value( "-3.1415_9265_3589"sv, -3.141592653589 );
|
||||
parse_expected_value( "123_456.789"sv, 123456.789 );
|
||||
parse_expected_value( "+123_456.789"sv, +123456.789 );
|
||||
parse_expected_value( "-123_456.789"sv, -123456.789 );
|
||||
parse_expected_value( "+0.0"sv, +0.0 );
|
||||
parse_expected_value( "-0.0"sv, -0.0 );
|
||||
parse_expected_value( "1e10"sv, 1e10 );
|
||||
parse_expected_value( "1e+10"sv, 1e+10 );
|
||||
parse_expected_value( "1e-10"sv, 1e-10 );
|
||||
parse_expected_value( "+1e10"sv, +1e10 );
|
||||
parse_expected_value( "+1e+10"sv, +1e+10 );
|
||||
parse_expected_value( "+1e-10"sv, +1e-10 );
|
||||
parse_expected_value( "-1e10"sv, -1e10 );
|
||||
parse_expected_value( "-1e+10"sv, -1e+10 );
|
||||
parse_expected_value( "-1e-10"sv, -1e-10 );
|
||||
parse_expected_value( "123e-10"sv, 123e-10 );
|
||||
parse_expected_value( "1E10"sv, 1E10 );
|
||||
parse_expected_value( "1E+10"sv, 1E+10 );
|
||||
parse_expected_value( "1E-10"sv, 1E-10 );
|
||||
parse_expected_value( "123E-10"sv, 123E-10 );
|
||||
parse_expected_value( "1_2_3E-10"sv, 123E-10 );
|
||||
parse_expected_value( "1_2_3E-1_0"sv, 123E-10 );
|
||||
parse_expected_value( "+0e0"sv, +0e0 );
|
||||
parse_expected_value( "-0e0"sv, -0e0 );
|
||||
parse_expected_value( "1_2_3E-01"sv, 123E-01 );
|
||||
parse_expected_value( "1_2_3E-0_1"sv, 123E-01 );
|
||||
parse_expected_value( "6.02e23"sv, 6.02e23 );
|
||||
parse_expected_value( "6.02e+23"sv, 6.02e+23 );
|
||||
parse_expected_value( "1.112_650_06e-17"sv, 1.11265006e-17 );
|
||||
|
||||
//toml/issues/562 - hexfloat literals
|
||||
#if TOML_LANG_HIGHER_THAN(0, 5, 0) && !TOML_USE_STREAMS_FOR_FLOATS
|
||||
|
@ -1,7 +1,7 @@
|
||||
#include "tests.h"
|
||||
|
||||
TOML_PUSH_WARNINGS
|
||||
TOML_DISABLE_FIELD_INIT_WARNING
|
||||
TOML_DISABLE_INIT_WARNINGS
|
||||
|
||||
TEST_CASE("parsing TOML spec example")
|
||||
{
|
||||
@ -49,7 +49,7 @@ hosts = [
|
||||
CHECK(tbl[S("owner")]);
|
||||
CHECK(tbl[S("owner")].as<table>());
|
||||
CHECK(tbl[S("owner")][S("name")] == S("Tom Preston-Werner"sv));
|
||||
const auto dob = date_time{ { 1979, 5, 27 }, { 7, 32 }, time_offset{ -8 } };
|
||||
const auto dob = date_time{ { 1979, 5, 27 }, { 7, 32 }, time_offset::from_hh_mm(-8, 0) };
|
||||
CHECK(tbl[S("owner")][S("dob")] == dob);
|
||||
|
||||
CHECK(tbl[S("database")].as<table>());
|
||||
|
@ -16,14 +16,14 @@ void parsing_should_succeed(std::basic_string_view<CHAR> toml_str, FUNC&& func,
|
||||
{
|
||||
constexpr auto validate_table = [](table&& tabl, std::string_view path) noexcept -> table&&
|
||||
{
|
||||
CHECK(tabl.region().begin != source_position{});
|
||||
CHECK(tabl.region().end != source_position{});
|
||||
CHECK(tabl.source().begin != source_position{});
|
||||
CHECK(tabl.source().end != source_position{});
|
||||
if (path.empty())
|
||||
CHECK(tabl.region().path == nullptr);
|
||||
CHECK(tabl.source().path == nullptr);
|
||||
else
|
||||
{
|
||||
REQUIRE(tabl.region().path != nullptr);
|
||||
CHECK(*tabl.region().path == path);
|
||||
REQUIRE(tabl.source().path != nullptr);
|
||||
CHECK(*tabl.source().path == path);
|
||||
}
|
||||
|
||||
return std::move(tabl);
|
||||
@ -58,9 +58,9 @@ void parsing_should_succeed(std::basic_string_view<CHAR> toml_str, FUNC&& func,
|
||||
else
|
||||
{
|
||||
FAIL(
|
||||
"Parse error on line "sv << result.error().where.begin.line
|
||||
<< ", column "sv << result.error().where.begin.column
|
||||
<< ":\n"sv << result.error().what
|
||||
"Parse error on line "sv << result.error().where().begin.line
|
||||
<< ", column "sv << result.error().where().begin.column
|
||||
<< ":\n"sv << result.error().what()
|
||||
);
|
||||
return;
|
||||
}
|
||||
@ -75,9 +75,9 @@ void parsing_should_succeed(std::basic_string_view<CHAR> toml_str, FUNC&& func,
|
||||
else
|
||||
{
|
||||
FAIL(
|
||||
"Parse error on line "sv << result.error().where.begin.line
|
||||
<< ", column "sv << result.error().where.begin.column
|
||||
<< ":\n"sv << result.error().what
|
||||
"Parse error on line "sv << result.error().where().begin.line
|
||||
<< ", column "sv << result.error().where().begin.column
|
||||
<< ":\n"sv << result.error().what()
|
||||
);
|
||||
return;
|
||||
}
|
||||
@ -160,13 +160,13 @@ void parse_expected_value(std::string_view value_str, const T& expected) noexcep
|
||||
|
||||
static constexpr auto is_val = [](char32_t codepoint) noexcept
|
||||
{
|
||||
if constexpr (std::is_same_v<string, value_of<T>>)
|
||||
if constexpr (std::is_same_v<string, promoted<T>>)
|
||||
return codepoint == U'"' || codepoint == U'\'';
|
||||
else
|
||||
return !impl::is_whitespace(codepoint);
|
||||
};
|
||||
|
||||
source_position pos{ 1, static_cast<uint32_t>(value_key.length()) };
|
||||
source_position pos{ 1, static_cast<source_index>(value_key.length()) };
|
||||
source_position begin{}, end{};
|
||||
impl::utf8_decoder decoder;
|
||||
for (auto c : value_str)
|
||||
@ -180,7 +180,7 @@ void parse_expected_value(std::string_view value_str, const T& expected) noexcep
|
||||
if (decoder.codepoint != U'\r')
|
||||
{
|
||||
pos.line++;
|
||||
pos.column = 1u;
|
||||
pos.column = source_index{ 1 };
|
||||
}
|
||||
continue;
|
||||
}
|
||||
@ -201,9 +201,9 @@ void parse_expected_value(std::string_view value_str, const T& expected) noexcep
|
||||
parsing_should_succeed(std::string_view{ value }, [&](table&& tbl) noexcept
|
||||
{
|
||||
CHECK(tbl.size() == 1);
|
||||
REQUIRE(tbl[S("val"sv)].as<value_of<T>>());
|
||||
CHECK(tbl[S("val"sv)].as<value_of<T>>()->get() == expected);
|
||||
CHECK(tbl[S("val"sv)].get()->region().begin == begin);
|
||||
CHECK(tbl[S("val"sv)].get()->region().end == end);
|
||||
REQUIRE(tbl[S("val"sv)].as<promoted<T>>());
|
||||
CHECK(tbl[S("val"sv)].as<promoted<T>>()->get() == expected);
|
||||
CHECK(tbl[S("val"sv)].get()->source().begin == begin);
|
||||
CHECK(tbl[S("val"sv)].get()->source().end == end);
|
||||
});
|
||||
}
|
||||
|
@ -65,6 +65,7 @@
|
||||
<ClInclude Include="..\include\toml++\toml_node.h" />
|
||||
<ClInclude Include="..\include\toml++\toml_parser.h" />
|
||||
<ClInclude Include="..\include\toml++\toml_node_view.h" />
|
||||
<ClInclude Include="..\include\toml++\toml_print_to_stream.h" />
|
||||
<ClInclude Include="..\include\toml++\toml_utf8_generated.h" />
|
||||
<ClInclude Include="..\include\toml++\toml_table.h" />
|
||||
<ClInclude Include="..\include\toml++\toml_utf8.h" />
|
||||
|
@ -15,6 +15,7 @@
|
||||
<ClInclude Include="..\include\toml++\toml_json_formatter.h" />
|
||||
<ClInclude Include="..\include\toml++\toml_formatter.h" />
|
||||
<ClInclude Include="..\include\toml++\toml_version.h" />
|
||||
<ClInclude Include="..\include\toml++\toml_print_to_stream.h" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<None Include="..\.editorconfig" />
|
||||
|
Loading…
Reference in New Issue
Block a user