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:
Mark Gillard 2020-01-07 17:52:50 +02:00
parent 40ffee43fb
commit 537eb30080
25 changed files with 4037 additions and 2963 deletions

View File

@ -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]. |

View File

@ -10,6 +10,7 @@ int2 = 9223372036854775807
flt1 = 0.00000000001
flt2 = 1e-11
flt3 = 11.0
flt4 = +1.0
# hexadecimal with prefix `0x`
hex1 = 0xDEADBEEF

View File

@ -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()

View File

@ -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

View File

@ -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() }; }
};
}

View File

@ -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();
}
}

View File

@ -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 };
}
}

View File

@ -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;
}
}
};

View File

@ -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();
}
}

View File

@ -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(

View File

@ -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

View 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;
}
}

View File

@ -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;

View File

@ -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();
}

View File

@ -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>;

View File

@ -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))

View File

@ -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);

View File

@ -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

View File

@ -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

View File

@ -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>());

View File

@ -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);
});
}

3393
toml.hpp

File diff suppressed because it is too large Load Diff

View File

@ -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" />

View File

@ -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" />