9#if !TOML_IMPLEMENTATION
10#error This is an implementation-only header.
27#if TOML_INT_CHARCONV || TOML_FLOAT_CHARCONV
30#if !TOML_INT_CHARCONV || !TOML_FLOAT_CHARCONV
45 class utf8_byte_stream;
50 template <
typename Char>
51 class utf8_byte_stream<
std::basic_string_view<Char>> {
52 static_assert(
sizeof(
Char) == 1);
55 std::basic_string_view<Char> source_;
56 size_t position_ = {};
68 constexpr bool error() const noexcept {
return false; }
71 constexpr bool eof() const noexcept {
return position_ >= source_.length(); }
74 explicit constexpr operator bool() const noexcept {
return !eof(); }
77 constexpr bool peek_eof() const noexcept {
return eof(); }
81 size_t operator()(
void* dest,
size_t num) noexcept {
84 num = impl::min(position_ +
num, source_.length()) - position_;
85 std::memcpy(dest, source_.data() + position_,
num);
92 class utf8_byte_stream<
std::istream> {
94 std::istream* source_;
103 const auto initial_pos = source_->tellg();
105 source_->read(bom, 3);
106 if (source_->bad() ||
111 source_->seekg(initial_pos, std::istream::beg);
115 bool error() const noexcept {
return !!(source_->rdstate() & std::istream::badbit); }
118 bool eof() const noexcept {
return !!(source_->rdstate() & std::istream::eofbit); }
121 explicit operator bool() const noexcept {
122 return !(source_->rdstate() & (std::istream::badbit | std::istream::eofbit));
127 return eof() || source_->peek() == std::istream::traits_type::eof();
135 source_->read(
static_cast<char*
>(dest),
static_cast<std::streamsize
>(
num));
136 return static_cast<size_t>(source_->gcount());
147 constexpr operator const char32_t&()
const noexcept {
return value; }
152 static_assert(std::is_trivial_v<utf8_codepoint>);
153 static_assert(std::is_standard_layout_v<utf8_codepoint>);
168 virtual optional<parse_error>&&
error() noexcept = 0;
176#define utf8_reader_error(...) throw parse_error(__VA_ARGS__)
177#define utf8_reader_return_after_error(...) static_assert(true)
178#define utf8_reader_error_check(...) static_assert(true)
180#define utf8_reader_error(...) err_.emplace(__VA_ARGS__)
181#define utf8_reader_return_after_error(...) return __VA_ARGS__
182#define utf8_reader_error_check(...) \
184 if TOML_UNLIKELY (err_) return __VA_ARGS__; \
189#if defined(__APPLE__) || defined(__MINGW32__) || defined(__MINGW64__)
190#define TOML_OVERALIGNED
192#define TOML_OVERALIGNED alignas(32)
195 template <
typename T>
198 static constexpr size_t block_capacity = 32;
199 utf8_byte_stream<T> stream_;
202 impl::utf8_decoder decoder_;
203 struct currently_decoding_t {
206 } currently_decoding_;
208 struct codepoints_t {
217 optional<parse_error> err_;
224 size_t raw_bytes_read;
227 if constexpr (
noexcept(stream_(raw_bytes, block_capacity)) || !
TOML_EXCEPTIONS) {
228 raw_bytes_read = stream_(raw_bytes, block_capacity);
233 raw_bytes_read = stream_(raw_bytes, block_capacity);
235 throw parse_error{exc.what(), next_pos_, source_path_};
237 throw parse_error{
"An unspecified error occurred", next_pos_, source_path_};
248 if (decoder_.needs_more_input())
250 next_pos_, source_path_);
253 next_pos_, source_path_);
259 std::memset(&codepoints_, 0,
sizeof(codepoints_));
262 const auto calc_positions = [&]()
noexcept {
263 for (
size_t i = 0;
i < codepoints_.count;
i++) {
264 auto& cp = codepoints_.buffer[
i];
265 cp.position = next_pos_;
269 next_pos_.
column = source_index{1};
277 const auto ascii_fast_path =
278 !decoder_.needs_more_input() && impl::is_ascii(raw_bytes, raw_bytes_read);
281 if (ascii_fast_path) {
283 currently_decoding_.count = {};
285 codepoints_.count = raw_bytes_read;
286 for (
size_t i = 0;
i < codepoints_.count;
i++) {
287 auto& cp = codepoints_.buffer[
i];
288 cp.value =
static_cast<char32_t>(raw_bytes[
i]);
289 cp.bytes[0] = raw_bytes[
i];
298 return codepoints_.count ? codepoints_.buffer[codepoints_.count - 1u].position
302 for (
size_t i = 0;
i < raw_bytes_read;
i++) {
303 decoder_(
static_cast<uint8_t
>(raw_bytes[
i]));
306 utf8_reader_error(
"Encountered invalid utf-8 sequence", error_pos(), source_path_);
310 currently_decoding_.bytes[currently_decoding_.count++] = raw_bytes[
i];
312 if (decoder_.has_code_point()) {
313 auto& cp = codepoints_.buffer[codepoints_.count++];
315 cp.value = decoder_.codepoint;
316 cp.count = currently_decoding_.count;
317 std::memcpy(cp.bytes, currently_decoding_.bytes, currently_decoding_.count);
318 currently_decoding_.count = {};
321 utf8_reader_error(
"Encountered overlong utf-8 sequence", error_pos(), source_path_);
325 if TOML_UNLIKELY (decoder_.needs_more_input() && stream_.eof()) {
328 error_pos(), source_path_);
339 utf8_reader_error(
"An I/O error occurred while reading from the underlying stream",
340 next_pos_, source_path_);
348 template <
typename U,
typename String = std::
string_view>
350 std::is_nothrow_constructible_v<utf8_byte_stream<T>,
U&&>)
351 : stream_{static_cast<
U&&>(source)} {
352 currently_decoding_.count = {};
354 codepoints_.current = {};
355 codepoints_.count = {};
357 if (!source_path.empty())
358 source_path_ = std::make_shared<const std::string>(
static_cast<String&&
>(source_path));
368 if (codepoints_.current == codepoints_.count) {
369 if TOML_UNLIKELY (!stream_ || !read_next_block())
return nullptr;
377 return &codepoints_.buffer[codepoints_.current++];
382 return stream_.peek_eof();
388 optional<parse_error>&&
error() noexcept final {
return std::move(err_); }
393 template <
typename Char>
396 template <
typename Char>
399 template <
typename Char>
401 template <
typename Char>
405#define utf8_buffered_reader_error_check(...) static_assert(true)
407#define utf8_buffered_reader_error_check(...) \
409 if TOML_UNLIKELY (reader_.error()) return __VA_ARGS__; \
416 static constexpr size_t max_history_length = 128;
419 static constexpr size_t history_buffer_size =
420 max_history_length - 1;
427 size_t negative_offset_ = {};
441 if (negative_offset_) {
445 if (!negative_offset_)
return head_;
449 return history_.buffer +
450 ((history_.first + history_.count - negative_offset_) % history_buffer_size);
458 history_.buffer[history_.count++] = *head_;
460 history_.buffer[(history_.first++ + history_buffer_size) % history_buffer_size] =
477 negative_offset_ +=
count;
479 return negative_offset_
481 ((history_.first + history_.count - negative_offset_) % history_buffer_size)
491 optional<parse_error>&&
error() noexcept {
return reader_.
error(); }
503#define TOML_RETURNS_BY_THROWING [[noreturn]]
505#define TOML_RETURNS_BY_THROWING
509 template <
typename...
T>
511 T... vals)
noexcept {
512 static_assert((std::is_same_v<char32_t, T> && ...));
513 return ((codepoint == vals) || ...);
517 struct parse_integer_traits;
519 struct parse_integer_traits<2> {
520 static constexpr auto scope_qualifier =
"binary integer"sv;
521 static constexpr auto is_digit = impl::is_binary_digit;
522 static constexpr auto is_signed =
false;
523 static constexpr auto max_digits = 63;
524 static constexpr auto prefix_codepoint =
U'b';
526 static constexpr auto full_prefix =
"0b"sv;
529 struct parse_integer_traits<8> {
530 static constexpr auto scope_qualifier =
"octal integer"sv;
531 static constexpr auto is_digit = impl::is_octal_digit;
532 static constexpr auto is_signed =
false;
533 static constexpr auto max_digits = 21;
534 static constexpr auto prefix_codepoint =
U'o';
536 static constexpr auto full_prefix =
"0o"sv;
539 struct parse_integer_traits<10> {
540 static constexpr auto scope_qualifier =
"decimal integer"sv;
541 static constexpr auto is_digit = impl::is_decimal_digit;
542 static constexpr auto is_signed =
true;
543 static constexpr auto max_digits = 19;
544 static constexpr auto full_prefix =
""sv;
547 struct parse_integer_traits<16> {
548 static constexpr auto scope_qualifier =
"hexadecimal integer"sv;
549 static constexpr auto is_digit = impl::is_hexadecimal_digit;
550 static constexpr auto is_signed =
false;
551 static constexpr auto max_digits = 16;
552 static constexpr auto prefix_codepoint =
U'x';
554 static constexpr auto full_prefix =
"0x"sv;
560 return impl::node_type_friendly_names[impl::unwrap_enum(
val)];
565 std::string_view
to_sv(
const std::string&
str)
noexcept {
566 return std::string_view{
str};
572 using namespace std::string_view_literals;
574 return val ?
"true"sv :
"false"sv;
580 if (cp.value <=
U'\x1F')
581 return impl::control_char_escapes[cp.value];
582 else if (cp.value ==
U'\x7F')
585 return std::string_view{cp.bytes, cp.count};
591 if (cp)
return to_sv(*cp);
599 template <
typename T>
602 const
T&
arg) noexcept {
605 using arg_type = impl::remove_cvref<T>;
608 if constexpr (std::is_same_v<arg_type, std::string_view>) {
609 const auto max_chars =
static_cast<size_t>(buf_end - write_pos);
610 const auto len = max_chars <
arg.length() ? max_chars :
arg.length();
611 std::memcpy(write_pos,
arg.data(),
len);
616 else if constexpr (std::is_same_v<arg_type, double>) {
617#if TOML_FLOAT_CHARCONV
618 const auto result = std::to_chars(write_pos, buf_end,
arg);
621 std::ostringstream ss;
622 ss.imbue(std::locale::classic());
623 ss.precision(std::numeric_limits<arg_type>::max_digits10);
630 else if constexpr (impl::is_one_of<arg_type, int64_t, uint64_t>) {
632 const auto result = std::to_chars(write_pos, buf_end,
arg);
635 std::ostringstream ss;
636 ss.imbue(std::locale::classic());
637 using cast_type = std::conditional_t<std::is_signed_v<arg_type>, int64_t, uint64_t>;
638 ss << static_cast<cast_type>(
arg);
644 else if constexpr (std::is_same_v<arg_type, escaped_codepoint>) {
645 if (
arg.cp.value <=
U'\x7F')
648 auto val =
static_cast<uint_least32_t
>(
arg.cp.value);
649 const auto digits =
val > 0xFFFFu ? 8u : 4u;
650 constexpr auto mask = uint_least32_t{0xFu};
651 char buf[10] = {
'\\',
digits > 4 ?
'U' :
'u'};
652 for (
auto i = 2u +
digits;
i-- > 2u;) {
653 const auto hexdig =
val & mask;
654 buf[
i] =
static_cast<char>(hexdig >= 0xAu ? (
'A' + (hexdig - 0xAu)) : (
'0' + hexdig));
662 else if constexpr (std::is_floating_point_v<arg_type>)
666 else if constexpr (std::is_arithmetic_v<arg_type> && std::is_integral_v<arg_type>) {
667 using cast_type = std::conditional_t<std::is_unsigned_v<arg_type>, uint64_t, int64_t>;
672 static_assert(impl::always_false<T>,
673 "concatenate() inputs are limited to std::string_views, integers, floats, and "
674 "escaped_codepoint");
691 template <
typename T>
701 throw parse_error{
buf,
pos, source_path};
703 return parse_error{std::string(
buf,
static_cast<size_t>(
write_pos -
buf)),
pos, source_path};
715 explicit parse_scope(std::string_view& current_scope, std::string_view new_scope) noexcept
724#define push_parse_scope_2(scope, line) parse_scope ps_##line(current_scope, scope)
725#define push_parse_scope_1(scope, line) push_parse_scope_2(scope, line)
726#define push_parse_scope(scope) push_parse_scope_1(scope, __LINE__)
732 std::vector<source_position>
ends;
754 std::string_view
back() const noexcept {
return (*
this)[
segments.size() - 1u]; }
809#define assert_not_eof() TOML_ASSERT_ASSUME(cp != nullptr)
810#define return_if_eof(...) \
812 if TOML_UNLIKELY (is_eof()) return __VA_ARGS__; \
816#define is_error() false
817#define return_after_error(...) TOML_UNREACHABLE
818#define assert_not_error() static_assert(true)
819#define return_if_error(...) static_assert(true)
820#define return_if_error_or_eof(...) return_if_eof(__VA_ARGS__)
822#define is_error() !!err
823#define return_after_error(...) return __VA_ARGS__
824#define assert_not_error() TOML_ASSERT(!is_error())
825#define return_if_error(...) \
827 if TOML_UNLIKELY (is_error()) return __VA_ARGS__; \
829#define return_if_error_or_eof(...) \
831 if TOML_UNLIKELY (is_eof() || is_error()) return __VA_ARGS__; \
835#if defined(TOML_BREAK_AT_PARSE_ERRORS) && TOML_BREAK_AT_PARSE_ERRORS
836#if defined(__has_builtin)
837#if __has_builtin(__builtin_debugtrap)
838#define parse_error_break() __builtin_debugtrap()
839#elif __has_builtin(__debugbreak)
840#define parse_error_break() __debugbreak()
843#ifndef parse_error_break
844#if TOML_MSVC || TOML_ICC
845#define parse_error_break() __debugbreak()
847#define parse_error_break() TOML_ASSERT(false)
851#define parse_error_break() static_assert(true)
854#define set_error_and_return(ret, ...) \
856 if (!is_error()) set_error(__VA_ARGS__); \
857 return_after_error(ret); \
860#define set_error_and_return_default(...) set_error_and_return({}, __VA_ARGS__)
862#define set_error_and_return_if_eof(...) \
864 if TOML_UNLIKELY (is_eof()) set_error_and_return(__VA_ARGS__, "encountered end-of-file"sv); \
867#define advance_and_return_if_error(...) \
871 return_if_error(__VA_ARGS__); \
874#define advance_and_return_if_error_or_eof(...) \
878 return_if_error(__VA_ARGS__); \
879 set_error_and_return_if_eof(__VA_ARGS__); \
895 std::vector<table*> implicit_tables;
896 std::vector<table*> dotted_key_tables;
897 std::vector<table*> open_inline_tables;
898 std::vector<array*> table_arrays;
900 std::string string_buffer;
901 std::string recording_buffer;
902 bool recording =
false, recording_whitespace =
true;
903 std::string_view current_scope;
904 size_t nested_values = {};
906 mutable optional<parse_error> err;
910 source_position current_position(source_index fallback_offset = 0)
const noexcept {
912 return {prev_pos.
line,
static_cast<source_index
>(prev_pos.
column + fallback_offset)};
915 template <
typename...
T>
917 const T&... reason)
const {
918 static_assert(
sizeof...(T) > 0);
922 (builder.append(reason), ...);
933 template <
typename...
T>
935 set_error_at(current_position(1), reason...);
938 void go_back(
size_t count = 1)
noexcept {
954 if (reader.
error()) {
955 err = std::move(reader.
error());
960 if (recording && !
is_eof()) {
961 if (recording_whitespace || !is_whitespace(*cp))
962 recording_buffer.append(cp->
bytes, cp->
count);
966 void start_recording(
bool include_current =
true)
noexcept {
970 recording_whitespace =
true;
971 recording_buffer.clear();
972 if (include_current && !
is_eof()) recording_buffer.append(cp->
bytes, cp->
count);
975 void stop_recording(
size_t pop_bytes = 0)
noexcept {
980 if (pop_bytes >= recording_buffer.length())
981 recording_buffer.clear();
982 else if (pop_bytes == 1u)
983 recording_buffer.pop_back();
985 recording_buffer.erase(recording_buffer.begin() +
986 static_cast<ptrdiff_t
>(recording_buffer.length() - pop_bytes),
987 recording_buffer.end());
991 bool consume_leading_whitespace() {
994 bool consumed =
false;
995 while (!
is_eof() && is_horizontal_whitespace(*cp)) {
1006 bool consume_line_break() {
1011 R
"(vertical tabs '\v' and form-feeds '\f' are not legal line breaks in TOML)"sv);
1022 }
else if (*cp !=
U'\n')
1029 bool consume_rest_of_line() {
1033 if (is_ascii_vertical_whitespace(*cp))
1034 return consume_line_break();
1043 bool consume_comment() {
1046 if (*cp !=
U'#')
return false;
1053 if (consume_line_break())
return true;
1056#if TOML_LANG_AT_LEAST(1, 0, 0)
1061 "control characters other than TAB (U+0009) are explicitly prohibited in comments"sv);
1066 "unicode surrogates (U+D800 to U+DFFF) are explicitly prohibited in comments"sv);
1076 bool consume_expected_sequence(std::u32string_view seq) {
1080 for (
auto c : seq) {
1082 if (*cp !=
c)
return false;
1088 template <
typename T>
1094 for (
size_t i = 0;
i <
len;
i++) {
1096 if (!is_decimal_digit(*cp))
return false;
1104 template <
typename T>
1111 for (;
i < max_len;
i++) {
1112 if (
is_eof() || !is_decimal_digit(*cp))
break;
1122 std::string_view parse_basic_string(
bool multi_line) {
1133 consume_line_break();
1138 auto&
str = string_buffer;
1140 bool escaped =
false;
1141 bool skipping_whitespace =
false;
1147 if (multi_line && is_whitespace(*cp)) {
1148 consume_leading_whitespace();
1152 "line-ending backslashes must be the last non-whitespace character on the line"sv);
1154 skipping_whitespace =
true;
1159 bool skip_escaped_codepoint =
true;
1185#if TOML_LANG_UNRELEASED
1192 "escape sequence '\\e' is not supported in TOML 1.0.0 and earlier"sv);
1195#if TOML_LANG_UNRELEASED
1201 "escape sequence '\\x' is not supported in TOML 1.0.0 and earlier"sv);
1210 skip_escaped_codepoint =
false;
1215 uint32_t sequence_value{};
1216 while (place_value) {
1222 sequence_value += place_value * hex_to_dec(*cp);
1229 "unicode surrogates (U+D800 - U+DFFF) are explicitly prohibited"sv);
1233 if (sequence_value < 0x80) {
1234 str +=
static_cast<char>(sequence_value);
1235 }
else if (sequence_value < 0x800u) {
1236 str +=
static_cast<char>((sequence_value >> 6) | 0xC0u);
1237 str +=
static_cast<char>((sequence_value & 0x3Fu) | 0x80u);
1238 }
else if (sequence_value < 0x10000u) {
1239 str +=
static_cast<char>((sequence_value >> 12) | 0xE0u);
1240 str +=
static_cast<char>(((sequence_value >> 6) & 0x3Fu) | 0x80u);
1241 str +=
static_cast<char>((sequence_value & 0x3Fu) | 0x80u);
1242 }
else if (sequence_value < 0x110000u) {
1243 str +=
static_cast<char>((sequence_value >> 18) | 0xF0u);
1244 str +=
static_cast<char>(((sequence_value >> 12) & 0x3Fu) | 0x80u);
1245 str +=
static_cast<char>(((sequence_value >> 6) & 0x3Fu) | 0x80u);
1246 str +=
static_cast<char>((sequence_value & 0x3Fu) | 0x80u);
1262 size_t lookaheads = {};
1263 size_t consecutive_delimiters = 1;
1268 consecutive_delimiters++;
1271 }
while (lookaheads < 4u);
1273 switch (consecutive_delimiters) {
1277 skipping_whitespace =
false;
1282 str.append(
"\"\""sv);
1283 skipping_whitespace =
false;
1297 str.append(
"\"\""sv);
1311 else if (*cp ==
U'\\') {
1313 skipping_whitespace =
false;
1319 if (multi_line && is_ascii_vertical_whitespace(*cp)) {
1320 consume_line_break();
1322 if (!skipping_whitespace)
str +=
'\n';
1329 "unescaped control characters other than TAB (U+0009) are explicitly prohibited"sv);
1331#if TOML_LANG_AT_LEAST(1, 0, 0)
1336 "unescaped unicode surrogates (U+D800 to U+DFFF) are explicitly prohibited"sv);
1340 if (!skipping_whitespace || !is_horizontal_whitespace(*cp)) {
1341 skipping_whitespace =
false;
1356 std::string_view parse_literal_string(
bool multi_line) {
1367 consume_line_break();
1372 auto&
str = string_buffer;
1380 size_t lookaheads = {};
1381 size_t consecutive_delimiters = 1;
1385 if (!
is_eof() && *cp ==
U'\'')
1386 consecutive_delimiters++;
1389 }
while (lookaheads < 4u);
1391 switch (consecutive_delimiters) {
1427 if (multi_line && is_ascii_vertical_whitespace(*cp)) {
1428 consume_line_break();
1437 "control characters other than TAB (U+0009) are explicitly prohibited"sv);
1439#if TOML_LANG_AT_LEAST(1, 0, 0)
1444 "unicode surrogates (U+D800 - U+DFFF) are explicitly prohibited"sv);
1463 const auto first = cp->
value;
1465 const auto second = cp->
value;
1467 const auto third = cp ? cp->
value :
U'\0';
1472 if (second == first)
return {};
1479 else if (first == second && first == third) {
1480 return {first ==
U'\'' ? parse_literal_string(
true) : parse_basic_string(
true),
true};
1489 return {first ==
U'\'' ? parse_literal_string(
false) : parse_basic_string(
false),
false};
1495 std::string_view parse_bare_key_segment() {
1500 string_buffer.clear();
1503 if (!is_bare_key_character(*cp))
break;
1509 return string_buffer;
1514 bool parse_boolean() {
1520 start_recording(
true);
1521 auto result = is_match(*cp,
U't',
U'T');
1522 if (!consume_expected_sequence(
result ?
U"true"sv :
U"false"sv))
1524 to_sv(recording_buffer),
"'"sv);
1527 if (cp && !is_value_terminator(*cp))
1535 double parse_inf_or_nan() {
1541 start_recording(
true);
1545 const bool inf = is_match(*cp,
U'i',
U'I');
1546 if (!consume_expected_sequence(inf ?
U"inf"sv :
U"nan"sv))
1548 to_sv(recording_buffer),
"'"sv);
1551 if (cp && !is_value_terminator(*cp))
1554 return inf ? (
negative ? -std::numeric_limits<double>::infinity()
1555 : std::numeric_limits<double>::infinity())
1556 : std::numeric_limits<double>::quiet_NaN();
1561 double parse_float() {
1568 const int sign = *cp ==
U'-' ? -1 : 1;
1575 bool seen_decimal =
false, seen_exponent =
false;
1576 char first_integer_part =
'\0';
1577 while (!
is_eof() && !is_value_terminator(*cp)) {
1579 if (!prev || !is_decimal_digit(*prev))
1585 }
else if TOML_UNLIKELY (prev && *prev ==
U'_' && !is_decimal_digit(*cp))
1589 "exceeds length limit of "sv,
sizeof(chars),
" digits"sv,
1590 (seen_exponent ?
""sv :
" (consider using exponent notation)"sv));
1591 else if (*cp ==
U'.') {
1595 if (!first_integer_part)
1599 else if (seen_exponent)
1605 else if (seen_decimal)
1608 seen_decimal =
true;
1609 }
else if (is_match(*cp,
U'e',
U'E')) {
1610 if (prev && !is_decimal_digit(*prev))
1614 else if (seen_exponent)
1617 seen_decimal =
true;
1618 seen_exponent =
true;
1619 }
else if (is_match(*cp,
U'+',
U'-')) {
1626 else if (!is_match(*prev,
U'e',
U'E'))
1628 }
else if (is_decimal_digit(*cp)) {
1629 if (!seen_decimal) {
1630 if (!first_integer_part)
1631 first_integer_part =
static_cast<char>(cp->
bytes[0]);
1632 else if (first_integer_part ==
'0')
1638 chars[length++] =
static_cast<char>(cp->
bytes[0]);
1645 if (*prev ==
U'_') {
1648 }
else if (is_match(*prev,
U'e',
U'E',
U'+',
U'-',
U'.')) {
1656#if TOML_FLOAT_CHARCONV
1658 auto fc_result = std::from_chars(chars, chars + length,
result);
1659 switch (fc_result.ec) {
1664 case std::errc::invalid_argument:
1666 "' could not be interpreted as a value"sv);
1669 case std::errc::result_out_of_range:
1671 "' is not representable in 64 bits"sv);
1676 "an unspecified error occurred while trying to interpret '"sv,
1677 std::string_view{chars, length},
"' as a value"sv);
1682 std::stringstream ss;
1683 ss.imbue(std::locale::classic());
1684 ss.write(chars,
static_cast<std::streamsize
>(length));
1689 "' could not be interpreted as a value"sv);
1696 double parse_hex_float() {
1702#if TOML_LANG_UNRELEASED
1705 const int sign = *cp ==
U'-' ? -1 : 1;
1713 if (!is_match(*cp,
U'x',
U'X'))
1725 fragment fragments[] = {
1730 fragment* current_fragment = fragments;
1732 int exponent_sign = 1;
1733 while (!
is_eof() && !is_value_terminator(*cp)) {
1735 if (!prev || !is_hexadecimal_digit(*prev))
1741 }
else if (prev && *prev ==
U'_' && !is_hexadecimal_digit(*cp))
1743 else if (*cp ==
U'.') {
1745 if (current_fragment == fragments + 2)
1749 else if (current_fragment == fragments + 1)
1754 }
else if (is_match(*cp,
U'p',
U'P')) {
1756 if (current_fragment == fragments + 2)
1761 else if (fragments[0].length == 0
u && fragments[1].length == 0
u)
1765 current_fragment = fragments + 2;
1766 }
else if (is_match(*cp,
U'+',
U'-')) {
1768 if (current_fragment != fragments + 2)
1773 else if (!is_match(*prev,
U'p',
U'P'))
1777 exponent_sign = *cp ==
U'-' ? -1 : 1;
1778 }
else if (current_fragment < fragments + 2 && !is_hexadecimal_digit(*cp))
1781 else if (current_fragment == fragments + 2 && !is_decimal_digit(*cp))
1784 else if (current_fragment->length ==
sizeof(fragment::chars))
1786 sizeof(fragment::chars),
" characters"sv);
1788 current_fragment->chars[current_fragment->length++] =
static_cast<char>(cp->
bytes[0]);
1795 if (current_fragment != fragments + 2 || current_fragment->length == 0
u) {
1798 }
else if (prev && *prev ==
U'_') {
1804 for (
int fragment_idx = 0; fragment_idx < 3; fragment_idx++) {
1805 auto&
f = fragments[fragment_idx];
1806 const uint32_t
base = fragment_idx == 2 ? 10u : 16u;
1809 const char*
c =
f.chars;
1811 while (
f.length && *
c ==
'0') {
1816 if (!
f.length)
continue;
1820 for (
size_t i = 0;
i <
f.length - 1u;
i++) place *=
base;
1824 val += place * hex_to_dec(*
c);
1826 val += place *
static_cast<uint32_t
>(*
c -
'0');
1827 if (fragment_idx == 1) sig++;
1831 f.value =
static_cast<double>(
val);
1834 if (fragment_idx == 1) {
1835 while (sig--)
f.value /=
base;
1839 return (fragments[0].
value + fragments[1].
value) *
1840 pow(2.0, fragments[2].
value * exponent_sign) * sign;
1845 "hexadecimal floating-point values are not supported "
1846 "in TOML 1.0.0 and earlier"sv);
1851 template <u
int64_t base>
1855 using traits = parse_integer_traits<base>;
1858 [[maybe_unused]] int64_t sign = 1;
1859 if constexpr (traits::is_signed) {
1860 sign = *cp ==
U'-' ? -1 : 1;
1864 if constexpr (
base == 10) {
1865 if (!traits::is_digit(*cp))
1874 if (*cp != traits::prefix_codepoint)
1879 if (!traits::is_digit(*cp))
1887 while (!
is_eof() && !is_value_terminator(*cp)) {
1889 if (!prev || !traits::is_digit(*prev))
1895 }
else if TOML_UNLIKELY (prev && *prev ==
U'_' && !traits::is_digit(*cp))
1902 digits[length++] =
static_cast<char>(cp->
bytes[0]);
1909 if (prev && *prev ==
U'_') {
1918 if constexpr (
base == 16)
1923 if constexpr (traits::is_signed)
result *= sign;
1930 const char* msd =
digits;
1931 if constexpr (
base != 10) {
1932 while (msd <
end && *msd ==
'0') msd++;
1933 if (msd ==
end)
return 0ll;
1945 "' is not representable in 64 bits"sv);
1952 while (--
end >= msd) {
1953 if constexpr (
base == 16)
1956 result += power *
static_cast<uint64_t
>(*
end -
'0');
1963 static constexpr auto i64_max =
1964 static_cast<uint64_t
>((std::numeric_limits<int64_t>::max)());
1967 "' is not representable in 64 bits"sv);
1969 if constexpr (traits::is_signed) {
1972 return (std::numeric_limits<int64_t>::min)();
1974 return static_cast<int64_t
>(
result) * sign;
1976 return static_cast<int64_t
>(
result);
1982 date parse_date(
bool part_of_datetime =
false) {
1990 if (!consume_digit_sequence(
digits, 4u))
1993 const auto is_leap_year = (year % 4u == 0
u) && ((year % 100u != 0
u) || (year % 400u == 0
u));
2001 if (!consume_digit_sequence(
digits, 2u))
2004 if (month == 0
u || month > 12u)
2006 const auto max_days_in_month =
2007 month == 2u ? (is_leap_year ? 29u : 28u)
2008 : (month == 4u || month == 6u || month == 9u || month == 11u ? 30u : 31u);
2016 if (!consume_digit_sequence(
digits, 2u))
2019 if (day == 0
u || day > max_days_in_month)
2021 " (inclusive), saw "sv, day);
2023 if (!part_of_datetime && !
is_eof() && !is_value_terminator(*cp))
2026 return {year, month, day};
2031 time parse_time(
bool part_of_datetime =
false) {
2037 static constexpr size_t max_digits = 64;
2039 uint32_t
digits[max_digits];
2042 if (!consume_digit_sequence(
digits, 2u))
2054 if (!consume_digit_sequence(
digits, 2u))
2060 auto time = toml::time{hour, minute};
2065 if (
is_eof() || is_value_terminator(*cp) ||
2066 (part_of_datetime && is_match(*cp,
U'+',
U'-',
U'Z',
U'z')))
2074 if (!consume_digit_sequence(
digits, 2u))
2083 if (
is_eof() || is_value_terminator(*cp) ||
2084 (part_of_datetime && is_match(*cp,
U'+',
U'-',
U'Z',
U'z')))
2090 size_t digit_count = consume_variable_length_digit_sequence(
digits, max_digits);
2095 if (digit_count == max_digits && is_decimal_digit(*cp))
2098 else if (!part_of_datetime && !is_value_terminator(*cp))
2102 uint32_t place = 1u;
2103 for (
auto i = impl::min<size_t>(digit_count, 9u);
i-- > 0
u;) {
2107 for (
auto i = digit_count;
i < 9u;
i++)
2122 auto date = parse_date(
true);
2126 if (!is_match(*cp,
U' ',
U'T',
U't'))
2131 auto time = parse_time(
true);
2135 if (
is_eof() || is_value_terminator(*cp))
return {date,
time};
2142 else if (is_match(*cp,
U'+',
U'-')) {
2146 int sign = *cp ==
U'-' ? -1 : 1;
2151 if (!consume_digit_sequence(
digits, 2u))
2163 if (!consume_digit_sequence(
digits, 2u))
2169 offset.minutes =
static_cast<decltype(
offset.minutes)
>((hour * 60 + minute) * sign);
2172 if (!
is_eof() && !is_value_terminator(*cp))
2179 node_ptr parse_array();
2182 node_ptr parse_inline_table();
2185 node_ptr parse_value_known_prefixes() {
2191 switch (cp->
value) {
2194 return parse_array();
2198 return parse_inline_table();
2202 return node_ptr{
new value{parse_float()}};
2208 return node_ptr{
new value{parse_string().
value}};
2211 const auto cp_upper =
static_cast<uint_least32_t
>(cp->
value) & ~0x20u;
2214 if (cp_upper == 70u || cp_upper == 84u)
2215 return node_ptr{
new value{parse_boolean()}};
2218 else if (cp_upper == 73u || cp_upper == 78u)
2219 return node_ptr{
new value{parse_inf_or_nan()}};
2229 node_ptr parse_value() {
2238 " (TOML_MAX_NESTED_VALUES)"sv);
2247 else if (*cp ==
U'_')
2250 const auto begin_pos = cp->
position;
2261 val = parse_value_known_prefixes();
2279 has_minus = 1 << 10,
2281 begins_sign = 1 << 12,
2282 begins_digit = 1 << 13,
2283 begins_zero = 1 << 14,
2285 signs_msk = has_plus | has_minus,
2286 bdigit_msk = has_digits | begins_digit,
2287 bzero_msk = bdigit_msk | begins_zero,
2289 value_traits
traits = has_nothing;
2290 const auto has_any = [&](
auto t)
noexcept {
return (
traits &
t) != has_nothing; };
2291 const auto has_none = [&](
auto t)
noexcept {
return (
traits &
t) == has_nothing; };
2292 const auto add_trait = [&](
auto t)
noexcept {
2298 if (is_decimal_digit(*cp)) {
2299 add_trait(begins_digit);
2300 if (*cp ==
U'0') add_trait(begins_zero);
2301 }
else if (is_match(*cp,
U'+',
U'-'))
2302 add_trait(begins_sign);
2308 size_t char_count = {}, advance_count = {};
2309 bool eof_while_scanning =
false;
2315 if (
const auto c = **cp;
c !=
U'_') {
2316 chars[char_count++] =
c;
2318 if (is_decimal_digit(
c))
2319 add_trait(has_digits);
2320 else if (is_ascii_letter(
c)) {
2322 switch (
static_cast<char32_t>(
c | 32u)) {
2324 if (char_count == 2u && has_any(begins_zero)) add_trait(has_b);
2328 if (char_count > 1u &&
2329 has_none(has_b | has_o | has_p | has_t | has_x | has_z | has_colon) &&
2330 (has_none(has_plus | has_minus) || has_any(begins_sign)))
2335 if (char_count == 2u && has_any(begins_zero)) add_trait(has_o);
2339 if (has_any(has_x)) add_trait(has_p);
2343 if ((char_count == 2u && has_any(begins_zero)) ||
2344 (char_count == 3u && has_any(begins_sign) && chars[1] ==
U'0'))
2355 }
else if (
c <=
U':') {
2359 add_trait(has_plus);
2362 add_trait(has_minus);
2368 add_trait(has_colon);
2376 eof_while_scanning =
is_eof();
2378 !is_value_terminator(*cp));
2384 if (char_count == 10u
2385 && (
traits | begins_zero) == (bzero_msk | has_minus)
2390 const auto pre_advance_count = advance_count;
2391 const auto pre_scan_traits =
traits;
2392 chars[char_count++] = *cp;
2395 const auto backpedal = [&]()
noexcept {
2396 go_back(advance_count - pre_advance_count);
2397 advance_count = pre_advance_count;
2398 traits = pre_scan_traits;
2405 if (
is_eof() || !is_decimal_digit(*cp))
2408 chars[char_count++] = *cp;
2416 if (char_count == 12u) backpedal();
2421 go_back(advance_count);
2425 if (char_count == 1u) {
2426 if (has_any(begins_digit)) {
2427 val.reset(
new value{
static_cast<int64_t
>(chars[0] -
U'0')});
2435 :
"could not determine value type"sv);
2446 val.reset(
new value{parse_hex_float()});
2447 else if (has_any(has_x | has_o | has_b)) {
2450 if (has_any(has_x)) {
2451 i = parse_integer<16>();
2452 flags = value_flags::format_as_hexadecimal;
2453 }
else if (has_any(has_o)) {
2454 i = parse_integer<8>();
2455 flags = value_flags::format_as_octal;
2458 i = parse_integer<2>();
2459 flags = value_flags::format_as_binary;
2465 }
else if (has_any(has_e) || (has_any(begins_digit) && chars[1] ==
U'.'))
2466 val.reset(
new value{parse_float()});
2467 else if (has_any(begins_sign)) {
2469 if (char_count == 2u && has_any(has_digits)) {
2471 new value{
static_cast<int64_t
>(chars[1] -
U'0') * (chars[0] ==
U'-' ? -1LL : 1LL)});
2478 if (is_decimal_digit(chars[1]) && chars[2] ==
U'.')
val.reset(
new value{parse_float()});
2481 else if (is_match(chars[1],
U'i',
U'n',
U'I',
U'N'))
2482 val.reset(
new value{parse_inf_or_nan()});
2492 switch (unwrap_enum(
traits)) {
2495 case bzero_msk | has_b:
2496 val.reset(
new value{parse_integer<2>()});
2497 val->ref_cast<int64_t>().
flags(value_flags::format_as_binary);
2502 case bzero_msk | has_o:
2503 val.reset(
new value{parse_integer<8>()});
2504 val->ref_cast<int64_t>().
flags(value_flags::format_as_octal);
2516 case begins_sign | has_digits | has_minus:
2518 case begins_sign | has_digits | has_plus: {
2524 static constexpr size_t max_numeric_value_length =
2526 if TOML_UNLIKELY (!eof_while_scanning && advance_count > max_numeric_value_length)
2528 "numeric value too long to identify type - cannot exceed "sv,
2529 max_numeric_value_length,
" characters"sv);
2531 val.reset(
new value{parse_integer<10>()});
2537 case bzero_msk | has_x:
2538 val.reset(
new value{parse_integer<16>()});
2539 val->ref_cast<int64_t>().
flags(value_flags::format_as_hexadecimal);
2550 case bzero_msk | has_e:
2552 case bzero_msk | has_e | has_minus:
2554 case bzero_msk | has_e | has_plus:
2556 case bzero_msk | has_dot:
2558 case bzero_msk | has_dot | has_e:
2560 case bzero_msk | has_dot | has_e | has_minus:
2562 case bzero_msk | has_dot | has_e | has_plus:
2571 case bdigit_msk | has_e:
2573 case bdigit_msk | has_e | has_minus:
2575 case bdigit_msk | has_e | has_plus:
2577 case bdigit_msk | has_dot:
2579 case bdigit_msk | has_dot | has_e:
2581 case bdigit_msk | has_dot | has_e | has_minus:
2583 case bdigit_msk | has_dot | has_e | has_plus:
2591 case begins_sign | has_digits | has_e | has_plus:
2593 case begins_sign | has_digits | has_dot | has_plus:
2595 case begins_sign | has_digits | has_dot | has_e | has_plus:
2597 case begins_sign | has_digits | has_dot | has_e | signs_msk:
2605 case begins_sign | has_digits | has_e | has_minus:
2607 case begins_sign | has_digits | has_e | signs_msk:
2609 case begins_sign | has_digits | has_dot | has_minus:
2611 case begins_sign | has_digits | has_dot | has_e | has_minus:
2612 val.reset(
new value{parse_float()});
2619 case bzero_msk | has_x | has_p:
2621 case bzero_msk | has_x | has_p | has_minus:
2623 case bzero_msk | has_x | has_p | has_plus:
2631 case begins_sign | has_digits | has_x | has_p | has_minus:
2633 case begins_sign | has_digits | has_x | has_p | has_plus:
2635 case begins_sign | has_digits | has_x | has_p | signs_msk:
2640 case bzero_msk | has_x | has_dot | has_p:
2642 case bzero_msk | has_x | has_dot | has_p | has_minus:
2644 case bzero_msk | has_x | has_dot | has_p | has_plus:
2652 case begins_sign | has_digits | has_x | has_dot | has_p | has_minus:
2654 case begins_sign | has_digits | has_x | has_dot | has_p | has_plus:
2656 case begins_sign | has_digits | has_x | has_dot | has_p | signs_msk:
2657 val.reset(
new value{parse_hex_float()});
2664 case bzero_msk | has_colon:
2666 case bzero_msk | has_colon | has_dot:
2668 case bdigit_msk | has_colon:
2670 case bdigit_msk | has_colon | has_dot:
2671 val.reset(
new value{parse_time()});
2676 case bzero_msk | has_minus:
2678 case bdigit_msk | has_minus:
2679 val.reset(
new value{parse_date()});
2695 case bzero_msk | has_minus | has_colon | has_t:
2697 case bzero_msk | signs_msk | has_colon | has_t:
2699 case bdigit_msk | has_minus | has_colon | has_t:
2701 case bdigit_msk | signs_msk | has_colon | has_t:
2709 case bzero_msk | has_minus | has_colon | has_dot | has_t:
2711 case bzero_msk | signs_msk | has_colon | has_dot | has_t:
2713 case bdigit_msk | has_minus | has_colon | has_dot | has_t:
2715 case bdigit_msk | signs_msk | has_colon | has_dot | has_t:
2723 case bzero_msk | has_minus | has_colon | has_z | has_t:
2725 case bzero_msk | has_minus | has_colon | has_dot | has_z | has_t:
2727 case bdigit_msk | has_minus | has_colon | has_z | has_t:
2729 case bdigit_msk | has_minus | has_colon | has_dot | has_z | has_t:
2730 val.reset(
new value{parse_date_time()});
2736 set_error_at(begin_pos,
"could not determine value type"sv);
2740 val->source_ = {begin_pos, current_position(1), reader.
source_path()};
2752 recording_whitespace =
false;
2755 std::string_view key_segment;
2756 const auto key_begin = current_position();
2759 if (is_bare_key_character(*cp)) key_segment = parse_bare_key_segment();
2762 else if (is_string_delimiter(*cp)) {
2763 const auto begin_pos = cp->position;
2765 recording_whitespace =
true;
2767 recording_whitespace =
false;
2770 if (
str.was_multi_line) {
2771 set_error_at(begin_pos,
"multi-line strings are prohibited in "sv,
2772 key_buffer.empty() ?
""sv :
"dotted "sv,
"keys"sv);
2775 key_segment =
str.value;
2781 "expected bare key starting character or string delimiter, saw '"sv,
to_sv(*cp),
2784 const auto key_end = current_position();
2787 consume_leading_whitespace();
2790 key_buffer.push_back(key_segment, key_begin, key_end);
2793 if (
is_eof() || *cp !=
U'.')
break;
2797 consume_leading_whitespace();
2806 key make_key(
size_t segment_index)
const {
2809 return key{key_buffer[segment_index],
2810 source_region{key_buffer.starts[segment_index], key_buffer.ends[segment_index],
2811 root.source().
path}};
2816 table* parse_table_header() {
2824 bool is_arr =
false;
2832 const bool had_leading_whitespace = consume_leading_whitespace();
2837 if (had_leading_whitespace)
2839 "[[array-of-table]] brackets must be contiguous (i.e. [ [ this ] ] is prohibited)"sv);
2845 consume_leading_whitespace();
2860 consume_leading_whitespace();
2871 header_end_pos = current_position(1);
2874 consume_leading_whitespace();
2875 if (!
is_eof() && !consume_comment() && !consume_line_break())
2882 table* parent = &root;
2883 for (
size_t i = 0,
e = key_buffer.size() - 1u;
i <
e;
i++) {
2884 const std::string_view segment = key_buffer[
i];
2888 if (pit != parent->
end() && pit->first == segment) {
2889 node&
p = pit->second;
2891 if (
auto tbl =
p.as_table()) {
2893 if (tbl->is_inline() &&
2894 !impl::find(open_inline_tables.begin(), open_inline_tables.end(), tbl))
2896 "' into existing inline table"sv);
2899 }
else if (
auto arr =
p.as_array();
2900 arr && impl::find(table_arrays.begin(), table_arrays.end(), arr)) {
2905 parent = &arr->back().ref_cast<
table>();
2907 if (!is_arr &&
p.type() == node_type::table)
2909 to_sv(recording_buffer),
"'"sv);
2912 to_sv(recording_buffer),
"' as "sv,
2913 is_arr ?
"array-of-tables"sv :
"table"sv);
2919 pit = parent->emplace_hint<
table>(pit, make_key(
i));
2921 p.source_ = {header_begin_pos, header_end_pos, reader.source_path()};
2923 implicit_tables.push_back(&
p);
2928 const auto last_segment = key_buffer.back();
2935 if (it != parent->
end() && it->first == last_segment) {
2936 node& matching_node = it->second;
2937 if (
auto arr = matching_node.as_array();
2938 is_arr && arr && impl::find(table_arrays.begin(), table_arrays.end(), arr)) {
2940 tbl.source_ = {header_begin_pos, header_end_pos, reader.source_path()};
2944 else if (
auto tbl = matching_node.as_table(); !is_arr && tbl && !implicit_tables.
empty()) {
2945 if (
auto found = impl::find(implicit_tables.begin(), implicit_tables.end(), tbl);
found) {
2947 if (!tbl->
empty()) {
2948 for (
auto& [_, child] : *tbl) {
2949 if (!child.is_table() && !child.is_array_of_tables()) {
2957 implicit_tables.
erase(implicit_tables.cbegin() + (
found - implicit_tables.data()));
2958 tbl->source_.
begin = header_begin_pos;
2959 tbl->source_.
end = header_end_pos;
2966 if (!is_arr && matching_node.type() == node_type::table) {
2967 set_error_at(header_begin_pos,
"cannot redefine existing table '"sv,
2968 to_sv(recording_buffer),
"'"sv);
2971 set_error_at(header_begin_pos,
"cannot redefine existing "sv,
to_sv(matching_node.type()),
2972 " '"sv,
to_sv(recording_buffer),
"' as "sv,
2973 is_arr ?
"array-of-tables"sv :
"table"sv);
2980 auto last_key = make_key(key_buffer.size() - 1u);
2985 it = parent->emplace_hint<
array>(it, std::move(last_key));
2986 array& tbl_arr = it->second.ref_cast<
array>();
2988 tbl_arr.source_ = {header_begin_pos, header_end_pos, reader.source_path()};
2991 tbl.source_ = {header_begin_pos, header_end_pos, reader.source_path()};
2997 it = parent->emplace_hint<
table>(it, std::move(last_key));
2999 tbl.source_ = {header_begin_pos, header_end_pos, reader.source_path()};
3006 bool parse_key_value_pair_and_insert(
table* tbl) {
3020 consume_leading_whitespace();
3028 consume_leading_whitespace();
3033 if (is_value_terminator(*cp))
3038 if (key_buffer.size() > 1u) {
3039 for (
size_t i = 0;
i < key_buffer.size() - 1u;
i++) {
3040 const std::string_view segment = key_buffer[
i];
3044 if (pit != tbl->
end() && pit->first == segment) {
3049 !(impl::find(dotted_key_tables.begin(), dotted_key_tables.end(),
p) ||
3050 impl::find(implicit_tables.begin(), implicit_tables.end(),
p))) {
3051 set_error_at(key_buffer.starts[
i],
"cannot redefine existing "sv,
3052 to_sv(pit->second.
type()),
" as dotted key-value pair"sv);
3061 pit = tbl->emplace_hint<
table>(pit, make_key(
i));
3063 p.source_ = pit->first.source();
3065 dotted_key_tables.push_back(&
p);
3072 const std::string_view last_segment = key_buffer.back();
3074 if (it != tbl->
end() && it->first == last_segment) {
3075 set_error(
"cannot redefine existing "sv,
to_sv(it->second.
type()),
" '"sv,
3076 to_sv(recording_buffer),
"'"sv);
3082 auto last_key = make_key(key_buffer.size() - 1u);
3085 node_ptr
val = parse_value();
3088 tbl->emplace_hint<node_ptr>(it, std::move(last_key), std::move(
val));
3092 void parse_document() {
3097 table* current_table = &root;
3103 if (consume_leading_whitespace() || consume_line_break() || consume_comment())
continue;
3108 if (*cp ==
U'[') current_table = parse_table_header();
3113 else if (is_bare_key_character(*cp) || is_string_delimiter(*cp)) {
3116 parse_key_value_pair_and_insert(current_table);
3121 consume_leading_whitespace();
3123 if (!
is_eof() && !consume_comment() && !consume_line_break())
3124 set_error(
"expected a comment or whitespace, saw '"sv,
to_sv(cp),
"'"sv);
3128 set_error(
"expected keys, tables, whitespace or comments, saw '"sv,
to_sv(cp),
"'"sv);
3131 auto eof_pos = current_position(1);
3132 root.source_.end = eof_pos;
3133 if (current_table && current_table != &root &&
3134 current_table->source_.
end <= current_table->source_.
begin)
3135 current_table->source_.
end = eof_pos;
3138 static void update_region_ends(node& nde)
noexcept {
3139 const auto type = nde.type();
3140 if (type > node_type::array)
return;
3142 if (type == node_type::table) {
3143 auto& tbl = nde.ref_cast<
table>();
3148 auto end = nde.source_.end;
3149 for (
auto&& [
k,
v] : tbl) {
3151 update_region_ends(
v);
3152 if (end <
v.source_.end)
end =
v.source_.end;
3156 auto& arr = nde.ref_cast<
array>();
3157 auto end = nde.source_.end;
3158 for (
auto&&
v : arr) {
3159 update_region_ends(
v);
3160 if (end <
v.source_.end)
end =
v.source_.end;
3162 nde.source_.end =
end;
3169 root.source_ = {prev_pos, prev_pos, reader.source_path()};
3171 if (!reader.peek_eof()) {
3172 cp = reader.read_next();
3175 if (reader.error()) {
3176 err = std::move(reader.error());
3181 if (cp) parse_document();
3184 update_region_ends(root);
3191 return {std::move(root)};
3205 node_ptr parser::parse_array() {
3214 node_ptr arr_ptr{
new array{}};
3217 parse_type prev = parse_type::none;
3220 while (consume_leading_whitespace() || consume_line_break() || consume_comment())
continue;
3225 if (prev == parse_type::val) {
3226 prev = parse_type::comma;
3234 else if (*cp ==
U']') {
3241 if (prev == parse_type::val) {
3245 prev = parse_type::val;
3247 auto val = parse_value();
3260 node_ptr parser::parse_inline_table() {
3269 node_ptr tbl_ptr{
new table{}};
3275 parse_type prev = parse_type::none;
3280 while (consume_leading_whitespace() || consume_line_break() || consume_comment())
continue;
3282 while (consume_leading_whitespace())
continue;
3289 if (prev == parse_type::kvp) {
3290 prev = parse_type::comma;
3297 else if (*cp ==
U'}') {
3301 if (prev == parse_type::comma) {
3303 "expected key-value pair, saw closing '}' (dangling comma)"sv);
3312 else if (is_string_delimiter(*cp) || is_bare_key_character(*cp)) {
3313 if (prev == parse_type::kvp)
3316 prev = parse_type::kvp;
3317 parse_key_value_pair_and_insert(&tbl);
3334#undef TOML_RETURNS_BY_THROWING
3335#undef advance_and_return_if_error
3336#undef advance_and_return_if_error_or_eof
3337#undef assert_not_eof
3338#undef assert_not_error
3341#undef parse_error_break
3342#undef push_parse_scope
3343#undef push_parse_scope_1
3344#undef push_parse_scope_2
3345#undef return_after_error
3347#undef return_if_error
3348#undef return_if_error_or_eof
3349#undef set_error_and_return
3350#undef set_error_and_return_default
3351#undef set_error_and_return_if_eof
3352#undef utf8_buffered_reader_error_check
3353#undef utf8_reader_error
3354#undef utf8_reader_error_check
3355#undef utf8_reader_return_after_error
3365 return impl::parser{std::move(reader)};
3370 parse_result do_parse_file(std::string_view file_path) {
3372#define TOML_PARSE_FILE_ERROR(msg, path) \
3373 throw parse_error{msg, source_position{}, std::make_shared<const std::string>(std::move(path))}
3375#define TOML_PARSE_FILE_ERROR(msg, path) \
3376 return parse_result { \
3378 msg, source_position{}, std::make_shared<const std::string>(std::move(path)) \
3383 std::string file_path_str(file_path);
3388 file.rdbuf()->pubsetbuf(file_buffer,
sizeof(file_buffer));
3390 file.open(impl::widen(file_path_str).c_str(),
3391 std::ifstream::in | std::ifstream::binary | std::ifstream::ate);
3393 file.open(file_path_str, std::ifstream::in | std::ifstream::binary | std::ifstream::ate);
3395 if (!file.is_open())
3399 const auto file_size = file.tellg();
3401 file.seekg(0, std::ifstream::beg);
3404 constexpr auto large_file_threshold = 1024 * 1024 * 2;
3405 if (file_size <= large_file_threshold) {
3406 std::vector<char> file_data;
3407 file_data.resize(
static_cast<size_t>(file_size));
3408 file.read(file_data.data(),
static_cast<std::streamsize
>(file_size));
3409 return parse(std::string_view{file_data.data(), file_data.size()}, std::move(file_path_str));
3414 return parse(file, std::move(file_path_str));
3416#undef TOML_PARSE_FILE_ERROR
3426 return TOML_ANON_NAMESPACE::do_parse(TOML_ANON_NAMESPACE::utf8_reader{doc, source_path});
3431 return TOML_ANON_NAMESPACE::do_parse(
3432 TOML_ANON_NAMESPACE::utf8_reader{doc, std::move(source_path)});
3437 return TOML_ANON_NAMESPACE::do_parse(TOML_ANON_NAMESPACE::utf8_reader{doc, source_path});
3442 return TOML_ANON_NAMESPACE::do_parse(
3443 TOML_ANON_NAMESPACE::utf8_reader{doc, std::move(source_path)});
3448 return TOML_ANON_NAMESPACE::do_parse_file(file_path);
3455 return TOML_ANON_NAMESPACE::do_parse(TOML_ANON_NAMESPACE::utf8_reader{doc, source_path});
3460 return TOML_ANON_NAMESPACE::do_parse(
3461 TOML_ANON_NAMESPACE::utf8_reader{doc, std::move(source_path)});
3466 std::string file_path_str;
3467 file_path_str.resize(file_path.length());
3468 memcpy(file_path_str.data(), file_path.data(), file_path.length());
3469 return TOML_ANON_NAMESPACE::do_parse_file(file_path_str);
3474#if TOML_ENABLE_WINDOWS_COMPAT
3478 return TOML_ANON_NAMESPACE::do_parse(
3479 TOML_ANON_NAMESPACE::utf8_reader{doc, impl::narrow(source_path)});
3484 return TOML_ANON_NAMESPACE::do_parse(
3485 TOML_ANON_NAMESPACE::utf8_reader{doc, impl::narrow(source_path)});
3490 return TOML_ANON_NAMESPACE::do_parse_file(impl::narrow(file_path));
3495#if TOML_HAS_CHAR8 && TOML_ENABLE_WINDOWS_COMPAT
3499 return TOML_ANON_NAMESPACE::do_parse(
3500 TOML_ANON_NAMESPACE::utf8_reader{doc, impl::narrow(source_path)});
3509#undef TOML_OVERALIGNED
void count(T, T, const U &,...)
TOML_EXPORTED_MEMBER_FUNCTION void reserve(size_t new_capacity)
Reserves internal storage capacity up to a pre-determined number of elements.
TOML_NODISCARD size_t capacity() const noexcept
Returns the current max number of elements that may be held in the array's internal storage.
decltype(auto) emplace_back(Args &&... args)
Emplaces a new element at the end of the array.
TOML_CONST_INLINE_GETTER node_type type() const noexcept final
Returns #toml::node_type::array.
void push_back(ElemType &&val, value_flags flags=preserve_source_value_flags)
Appends a new element to the end of the array.
The result of a parsing operation.
parser(utf8_reader_interface &&reader_)
TOML_PURE_INLINE_GETTER bool empty() const noexcept
Returns true if the table is empty.
TOML_CONST_INLINE_GETTER node_type type() const noexcept final
Returns #toml::node_type::table.
TOML_CONST_INLINE_GETTER table * as_table() noexcept final
Returns a pointer to the table.
TOML_PURE_INLINE_GETTER iterator begin() noexcept
Returns an iterator to the first key-value pair.
TOML_PURE_INLINE_GETTER bool is_inline() const noexcept
Returns true if this table is an inline table.
TOML_PURE_GETTER iterator lower_bound(std::string_view key) noexcept
Returns an iterator to the first key-value pair with key that is not less than the given key.
iterator erase(iterator pos) noexcept
Removes the specified key-value pair from the table.
TOML_PURE_INLINE_GETTER const source_path_ptr & source_path() const noexcept
TOML_NODISCARD const utf8_codepoint * read_next() noexcept(!TOML_COMPILER_HAS_EXCEPTIONS)
TOML_NODISCARD bool peek_eof() const noexcept(!TOML_COMPILER_HAS_EXCEPTIONS)
TOML_NODISCARD const utf8_codepoint * step_back(size_t count) noexcept
TOML_NODISCARD optional< parse_error > && error() noexcept
static constexpr size_t max_history_length
TOML_NODISCARD_CTOR utf8_buffered_reader(utf8_reader_interface &reader) noexcept
TOML_PURE_INLINE_GETTER constexpr bool peek_eof() const noexcept
TOML_NODISCARD_CTOR constexpr utf8_byte_stream(std::basic_string_view< Char > sv) noexcept
TOML_CONST_INLINE_GETTER constexpr bool error() const noexcept
TOML_PURE_INLINE_GETTER constexpr bool eof() const noexcept
TOML_PURE_INLINE_GETTER bool eof() const noexcept
TOML_NODISCARD_CTOR utf8_byte_stream(std::istream &stream) noexcept(!TOML_COMPILER_HAS_EXCEPTIONS)
TOML_NODISCARD bool peek_eof() const noexcept(!TOML_COMPILER_HAS_EXCEPTIONS)
TOML_PURE_INLINE_GETTER bool error() const noexcept
TOML_NODISCARD_CTOR utf8_reader(U &&source, String &&source_path={}) noexcept(std::is_nothrow_constructible_v< utf8_byte_stream< T >, U && >)
TOML_NODISCARD optional< parse_error > && error() noexcept final
TOML_PURE_INLINE_GETTER const source_path_ptr & source_path() const noexcept final
TOML_NODISCARD bool peek_eof() const noexcept(!TOML_COMPILER_HAS_EXCEPTIONS) final
TOML_NODISCARD const utf8_codepoint * read_next() noexcept(!TOML_COMPILER_HAS_EXCEPTIONS) final
enum TOML_OPEN_FLAGS_ENUM value_flags
Metadata associated with TOML values.
#define TOML_CALLCONV
Calling convention to apply to exported free/static functions. \detail Not defined by default (let th...
#define TOML_ASSERT(expr)
Sets the assert function used by the library. \detail Defaults to the standard C assert().
#define TOML_EXCEPTIONS
Sets whether the library uses exceptions to report parsing failures. \detail Defaults to 1 or 0 accor...
#define TOML_ASSERT_ASSUME(expr)
#define TOML_MAX_NESTED_VALUES
#define TOML_RETURNS_BY_THROWING
TOML_EXTERNAL_LINKAGE parse_result TOML_CALLCONV parse(std::string_view doc, std::string_view source_path)
Parses a TOML document from a string view.
#define set_error_and_return_if_eof(...)
TOML_ANON_NAMESPACE_START
#define set_error_and_return_default(...)
TOML_EXTERNAL_LINKAGE parse_result TOML_CALLCONV parse_file(std::string_view file_path)
Parses a TOML document from a file.
#define return_if_error_or_eof(...)
#define utf8_reader_error(...)
TOML_PURE_GETTER TOML_INTERNAL_LINKAGE std::string_view to_sv(node_type val) noexcept
#define TOML_PARSE_FILE_ERROR(msg, path)
#define return_if_error(...)
#define return_after_error(...)
#define push_parse_scope(scope)
#define utf8_reader_error_check(...)
#define assert_not_error()
#define advance_and_return_if_error_or_eof(...)
#define parse_error_break()
#define utf8_buffered_reader_error_check(...)
#define advance_and_return_if_error(...)
TOML_INTERNAL_LINKAGE constexpr auto utf8_byte_order_mark
TOML_INTERNAL_LINKAGE void concatenate(char *&write_pos, char *const buf_end, const T &arg) noexcept
TOML_IMPL_NAMESPACE_START
#define utf8_reader_return_after_error(...)
#define TOML_NODISCARD_CTOR
#define TOML_UNLIKELY(...)
#define TOML_ABI_NAMESPACE_BOOL(cond, T, F)
#define TOML_NEVER_INLINE
#define TOML_INTERNAL_LINKAGE
#define TOML_UNLIKELY_CASE
#define TOML_EXTERNAL_LINKAGE
#define TOML_CONST_GETTER
#define TOML_LANG_UNRELEASED
#define TOML_COMPILER_HAS_EXCEPTIONS
#define TOML_CLOSED_FLAGS_ENUM
#define TOML_PURE_INLINE_GETTER
#define TOML_CONST_INLINE_GETTER
#define TOML_ABSTRACT_INTERFACE
std::shared_ptr< const std::string > source_path_ptr
A pointer to a shared string resource containing a source path.
~depth_counter_scope() noexcept
TOML_DELETE_DEFAULTS(depth_counter_scope)
TOML_NODISCARD_CTOR depth_counter_scope(size_t &depth) noexcept
void append(const T &arg) noexcept
static constexpr std::size_t buf_size
TOML_NODISCARD_CTOR error_builder(std::string_view scope) noexcept
TOML_DELETE_DEFAULTS(error_builder)
TOML_RETURNS_BY_THROWING auto finish(const source_position &pos, const source_path_ptr &source_path) const
char *const max_write_pos
const utf8_codepoint & cp
TOML_PURE_INLINE_GETTER size_t size() const noexcept
std::vector< std::pair< size_t, size_t > > segments
std::vector< source_position > starts
std::vector< source_position > ends
TOML_PURE_INLINE_GETTER std::string_view back() const noexcept
void push_back(std::string_view segment, source_position b, source_position e)
TOML_PURE_INLINE_GETTER bool empty() const noexcept
TOML_PURE_INLINE_GETTER std::string_view operator[](size_t i) const noexcept
TOML_DELETE_DEFAULTS(parse_scope)
TOML_NODISCARD_CTOR parse_scope(std::string_view ¤t_scope, std::string_view new_scope) noexcept
std::string_view & storage_
A source document line-and-column pair.
source_index line
The line number.
source_index column
The column number.
A source document region.
source_path_ptr path
The path to the corresponding source document.
std::vector< table * > & tables
~table_vector_scope() noexcept
TOML_DELETE_DEFAULTS(table_vector_scope)
TOML_NODISCARD_CTOR table_vector_scope(std::vector< table * > &tables_, table &tbl)
uint32_t nanosecond
The fractional nanoseconds component, from 0 - 999999999.
uint8_t second
The second component, from 0 - 59.
TOML_PURE_INLINE_GETTER constexpr const char32_t & operator*() const noexcept
virtual TOML_NODISCARD optional< parse_error > && error() noexcept=0
virtual TOML_NODISCARD const source_path_ptr & source_path() const noexcept=0
virtual ~utf8_reader_interface() noexcept=default
virtual TOML_NODISCARD bool peek_eof() const noexcept(!TOML_COMPILER_HAS_EXCEPTIONS)=0
virtual TOML_NODISCARD const utf8_codepoint * read_next() noexcept(!TOML_COMPILER_HAS_EXCEPTIONS)=0
long prefix(const entry_type &entry)