fixed inconsistent error messages for overlong ints/floats (closes #133)

also:
- added `at_path()` (closes #118)
- added `node_view::operator==`
- updated conformance tests
This commit is contained in:
Mark Gillard 2022-01-06 13:26:01 +02:00
parent 47241d003e
commit 3f4a540ca6
61 changed files with 1392 additions and 336 deletions

View File

@ -29,9 +29,11 @@ AttributeMacros:
- TOML_CLOSED_FLAGS_ENUM
- TOML_EMPTY_BASES
- TOML_FLAGS_ENUM
- TOML_LIKELY_CASE
- TOML_OPEN_ENUM
- TOML_OPEN_FLAGS_ENUM
- TOML_TRIVIAL_ABI
- TOML_UNLIKELY_CASE
BinPackArguments: false
BinPackParameters: false
BraceWrapping:

View File

@ -17,29 +17,35 @@ template:
## Unreleased
This release will be a major version bump, so it's ABI breaks all around. Any API changes that might necessitate
code changes at callsites or in build systems are indicated with ⚠️.
This release will be a major version bump, so it's ABI breaks all around.
Any changes that might cause code or build systems to break are indicated with ⚠️.
Highlights are indicated with ❤️.
#### Fixes:
- ⚠️ fixed incorrect `noexcept` specifications on many functions
- ⚠️ fixed `toml::table` init-list constructor requiring double-brackets
- ⚠️ fixed `TOML_API` + extern templates causing linker errors in some circumstances
- ⚠️ fixed incorrect `noexcept` specifications on many functions
- ⚠️ fixed missing `TOML_API` on some interfaces
- fixed `toml::json_formatter` not formatting inf and nan incorrectly
- fixed `TOML_API` + extern templates causing linker errors in some circumstances
- fixed a number of spec conformance issues (#127, #128, #129, #130, #131, #132) (@moorereason)
- fixed an illegal table redefinition edge case (#112) (@python36)
- fixed documentation issues
- fixed incorrect source position in redefinition error messages
- fixed incorrect handling of vertical whitespace in keys when printing TOML to streams
- fixed incorrect source position in redefinition error messages
- fixed memory leak during parse failures when compiled using GCC (#123, #124) (@rsmmr, @ronalabraham)
- fixed missing `#include <initializer_list>`
- fixed missing `#include <utility>`
- fixed missing `TOML_API` on interfaces
- fixed parser not correctly round-tripping the format of binary and octal integers in some cases
- fixed strong exception guarantee edge-cases in `toml::table` and `toml::array`
- fixed some incorrect unicode scalar sequence transformations (#125)
- fixed a number of spec conformance issues (#127, #128, #129, #130, #131, #132) (@moorereason)
- fixed strong exception guarantee edge-cases in `toml::table` and `toml::array`
#### Additions:
- added `operator->` to `toml::value` for class types
- ❤&#xFE0F; added `operator->` to `toml::value` for class types
- ❤&#xFE0F; added `toml::at_path()`, `toml::node::at_path()` and `toml::node_view::at_path()` for qualified path-based lookups (#118) (@ben-crowhurst)
- ❤&#xFE0F; added `toml::key` - provides a facility to access the source_regions of parsed keys (#82) (@vaartis)
- ❤&#xFE0F; added `toml::yaml_formatter`
- ❤&#xFE0F; added support for Unicode 14.0
- added `parse_benchmark` example
- added `toml::array::at()` (same semantics as `std::vector::at()`)
- added `toml::array::prune()`
@ -51,22 +57,19 @@ code changes at callsites or in build systems are indicated with ⚠&#xFE0F;.
- added `toml::format_flags::allow_octal_integers`
- added `toml::format_flags::allow_real_tabs_in_strings`
- added `toml::format_flags::allow_unicode_strings`
- added `toml::format_flags::indent_array_elements`
- added `toml::format_flags::indent_sub_tables`
- added `toml::format_flags::indent_array_elements` (#120) (@W4RH4WK)
- added `toml::format_flags::indent_sub_tables` (#120) (@W4RH4WK)
- added `toml::format_flags::quote_infinities_and_nans`
- added `toml::key` - provides a facility to access the source_regions of parsed keys (#82) (@vaartis)
- added `toml::is_key<>` and toml::is_key_or_convertible<>` metafunctions
- added `toml::node_view::operator==`
- added `toml::table::at()` (same semantics as `std::map::at()`)
- added `toml::table::emplace_hint()` (same semantics as `std::map::emplace_hint()`)
- added `toml::table::lower_bound()` (same semantics as `std::map::lower_bound()`)
- added `toml::table::prune()`
- added `toml::value` copy+move constructor overloads with flags override
- added `toml::yaml_formatter`
- added `TOML_ENABLE_FORMATTERS` option
- added clang's enum annotation attributes to all enums
- added formatter indentation flags (#120) (@W4RH4WK)
- added magic `toml::value_flags` constant `toml::preserve_source_value_flags`
- added support for Unicode 14.0
- added value flags to array + table insert methods (#44) (@levicki)
#### Changes:
@ -79,14 +82,14 @@ code changes at callsites or in build systems are indicated with ⚠&#xFE0F;.
- ⚠&#xFE0F; renamed `TOML_PARSER` option to `TOML_ENABLE_PARSER` (`TOML_PARSER` will continue to work but is deprecated)
- ⚠&#xFE0F; renamed `TOML_UNRELEASED_FEATURES` to `TOML_ENABLE_UNRELEASED_FEATURES` (`TOML_UNRELEASED_FEATURES` will continue to work but is deprecated)
- ⚠&#xFE0F; renamed `TOML_WINDOWS_COMPAT` to `TOML_ENABLE_WINDOWS_COMPAT` (`TOML_WINDOWS_COMPAT` will continue to work but is deprecated)
- `toml::node::ref()` now supports explicit ref categories and cv-qualifiers
- applied clang-format to all the things 🎉&#xFE0F;
- ❤&#xFE0F; `toml::node::ref()` now supports explicit ref categories and cv-qualifiers
- ❤&#xFE0F; applied clang-format to all the things 🎉&#xFE0F;
- exposed `TOML_NAMESPACE_START` and `TOML_NAMESPACE_END` macros to help with ADL specialization scenarios
- improved performance of parser
- made date/time constructors accept any integral types
- moved all implementation headers to `/impl`
- renamed all implementation headers to `.h` and 'source' headers to `.inl`
- updated conformance tests
- exposed `TOML_NAMESPACE_START` and `TOML_NAMESPACE_END` macros to help with ADL specialization scenarios
#### Removals:
- ⚠&#xFE0F; removed `toml::format_flags::allow_value_format_flags`

View File

@ -33,3 +33,5 @@
#define TOML_RETURNS_BY_THROWING
#define TOML_TRIVIAL_ABI
#define TOML_UNLIKELY(...) (__VA_ARGS__)
#define TOML_LIKELY_CASE
#define TOML_UNLIKELY_CASE

View File

@ -98,7 +98,7 @@ namespace
R"(val = 01 )"sv,
"########## floats"sv,
R"(val = 9999999999999999999999999999999999999999999999999999999999999995.0)"sv,
R"(val = 100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000.0)"sv,
};
inline constexpr auto divider =

2
external/toml-test vendored

@ -1 +1 @@
Subproject commit 4634fdf3a6ecd6aaea5f4cdcd98b2733c2694993
Subproject commit 442dea55c5173bc89c2afa1b023ecbf0e042ceac

View File

@ -0,0 +1,82 @@
//# This file is a part of toml++ and is subject to the the terms of the MIT license.
//# Copyright (c) Mark Gillard <mark.gillard@outlook.com.au>
//# See https://github.com/marzer/tomlplusplus/blob/master/LICENSE for the full license text.
// SPDX-License-Identifier: MIT
#pragma once
#include "forward_declarations.h"
TOML_NAMESPACE_START
{
/// \brief Returns a view of the node matching a fully-qualified "TOML path".
///
/// \detail \cpp
/// auto config = R"(
///
/// [foo]
/// bar = [ 0, 1, 2, [ 3 ], { kek = 4 } ]
///
/// )"_toml;
///
/// std::cout << toml::at_path(config, "foo.bar[2]") << "\n";
/// std::cout << toml::at_path(config, "foo.bar[3][0]") << "\n";
/// std::cout << toml::at_path(config, "foo.bar[4].kek") << "\n";
/// \ecpp
///
/// \out
/// 2
/// 3
/// 4
/// \eout
///
///
/// \warning Keys in paths are interpreted literally, so whitespace (or lack thereof) matters:
/// \cpp
/// toml::at_path(config, "foo.bar") // same as config["foo"]["bar"]
/// toml::at_path(config, "foo. bar") // same as config["foo"][" bar"]
/// toml::at_path(config, "foo..bar") // same as config["foo"][""]["bar"]
/// toml::at_path(config, ".foo.bar") // same as config[""]["foo"]["bar"]
/// \ecpp
/// <br>
/// Additionally, TOML allows '.' (period) characters to appear in keys if they are quoted strings.
/// This function makes no allowance for this this, instead treating all period characters as sub-table delimiters.
/// If you have periods in your table keys, first consider:
/// 1. Not doing that
/// 2. Using node_view::operator[] instead.
///
/// \param root The root node from which the path will be traversed.
/// \param path The "TOML path" to traverse.
TOML_NODISCARD
TOML_API
node_view<node> at_path(node & root, std::string_view path) noexcept;
/// \brief Returns a const view of the node matching a fully-qualified "TOML path".
///
/// \see #toml::at_path(node&, std::string_view)
TOML_NODISCARD
TOML_API
node_view<const node> at_path(const node& root, std::string_view path) noexcept;
#if TOML_ENABLE_WINDOWS_COMPAT
/// \brief Returns a view of the node matching a fully-qualified "TOML path".
///
/// \availability This overload is only available when #TOML_ENABLE_WINDOWS_COMPAT is enabled.
///
/// \see #toml::at_path(node&, std::string_view)
TOML_NODISCARD
TOML_API
node_view<node> at_path(node & root, std::wstring_view path);
/// \brief Returns a const view of the node matching a fully-qualified "TOML path".
///
/// \availability This overload is only available when #TOML_ENABLE_WINDOWS_COMPAT is enabled.
///
/// \see #toml::at_path(node&, std::string_view)
TOML_NODISCARD
TOML_API
node_view<const node> at_path(const node& root, std::wstring_view path);
#endif
}
TOML_NAMESPACE_END;

View File

@ -0,0 +1,228 @@
//# This file is a part of toml++ and is subject to the the terms of the MIT license.
//# Copyright (c) Mark Gillard <mark.gillard@outlook.com.au>
//# See https://github.com/marzer/tomlplusplus/blob/master/LICENSE for the full license text.
// SPDX-License-Identifier: MIT
#pragma once
//# {{
#include "preprocessor.h"
#if !TOML_IMPLEMENTATION
#error This is an implementation-only header.
#endif
//# }}
#include "at_path.h"
#include "array.h"
#include "table.h"
TOML_DISABLE_WARNINGS;
#if TOML_INT_CHARCONV
#include <charconv>
#else
#include <sstream>
#endif
TOML_ENABLE_WARNINGS;
#include "header_start.h"
TOML_ANON_NAMESPACE_START
{
TOML_INTERNAL_LINKAGE
node* get_at_path(node & root, std::string_view path) noexcept
{
if (root.is_value()) // values don't have child nodes
return nullptr;
size_t pos = 0;
const auto end = path.length();
node* current = &root;
bool prev_was_array_indexer = false;
bool prev_was_dot = root.is_table(); // implicit '.' at the start for tables
do
{
// start of an array indexer
if (path[pos] == '[')
{
const auto current_array = current->as<array>();
if (!current_array)
return nullptr;
// get array index substring
const auto index_start = pos + 1u; // first position after '['
const auto index_end = path.find(']', index_start); // position of ']'
if (index_end == std::string_view::npos || index_end == index_start)
return nullptr;
auto index_str = path.substr(index_start, index_end - index_start);
// trim whitespace from either side of the index
const auto first_non_ws = index_str.find_first_not_of(" \t"sv);
const auto last_non_ws = index_str.find_last_not_of(" \t"sv);
if (first_non_ws == std::string_view::npos)
return nullptr;
TOML_ASSERT_ASSUME(last_non_ws != std::string_view::npos);
index_str = index_str.substr(first_non_ws, (last_non_ws - first_non_ws) + 1u);
// parse the actual array index
size_t index;
if (index_str.length() == 1u && index_str[0] >= '0' && index_str[0] <= '9')
index = static_cast<size_t>(index_str[0] - '0');
else
{
#if TOML_INT_CHARCONV
auto fc_result = std::from_chars(index_str.data(), index_str.data() + index_str.length(), index);
if (fc_result.ec != std::errc{})
return nullptr;
#else
std::stringstream ss;
ss.imbue(std::locale::classic());
ss.write(index_str.data(), static_cast<std::streamsize>(index_str.length()));
if (!(ss >> index))
return nullptr;
#endif
}
current = current_array->get(index);
pos = index_end + 1u;
prev_was_dot = false;
prev_was_array_indexer = true;
}
// start of a new table child
else if (path[pos] == '.')
{
const auto current_table = current->as<table>();
if (!current_table)
return nullptr;
// a dot immediately following another dot (or at the beginning of the string) is as if we'd asked
// for an empty child in between, e.g.
//
// foo..bar
//
// is equivalent to
//
// "foo".""."bar"
//
if (prev_was_dot)
current = current_table->get(""sv);
pos++;
prev_was_dot = true;
prev_was_array_indexer = false;
}
// some regular subkey
else
{
// get subkey text
const auto subkey_start = pos;
const auto subkey_len =
impl::min(path.find_first_of(".["sv, subkey_start + 1u), path.length()) - subkey_start;
const auto subkey = path.substr(subkey_start, subkey_len);
// a regular subkey segment immediately after an array indexer is OK if it was all whitespace, e.g.:
//
// "foo[0] .bar"
// ^^ skip this
//
// otherwise its an error (since it would have to be preceeded by a dot)
if (prev_was_array_indexer)
{
auto non_ws = subkey.find_first_not_of(" \t");
if (non_ws == std::string_view::npos)
{
pos += subkey_len;
prev_was_dot = false;
prev_was_array_indexer = false;
continue;
}
else
return nullptr;
}
const auto current_table = current->as<table>();
if (!current_table)
return nullptr;
current = current_table->get(subkey);
pos += subkey_len;
prev_was_dot = false;
prev_was_array_indexer = false;
}
}
while (pos < end && current);
// a dot at the end is as if we'd asked for an empty child at the end, e.g.
//
// foo.bar.
//
// is equivalent to
//
// "foo"."bar".""
//
if (current && prev_was_dot)
{
const auto current_table = current->as<table>();
if (!current_table)
return nullptr;
current = current_table->get(""sv);
}
return current;
}
#if TOML_ENABLE_WINDOWS_COMPAT
TOML_INTERNAL_LINKAGE
node* get_at_path(node & root, std::wstring_view path) noexcept
{
if (auto tbl = root.as_table(); tbl && tbl->empty())
return {};
if (auto arr = root.as_array(); arr && arr->empty())
return {};
return get_at_path(root, impl::narrow(path));
}
#endif // TOML_ENABLE_WINDOWS_COMPAT
}
TOML_ANON_NAMESPACE_END;
TOML_NAMESPACE_START
{
TOML_EXTERNAL_LINKAGE
node_view<node> at_path(node & root, std::string_view path) noexcept
{
return node_view<node>{ TOML_ANON_NAMESPACE::get_at_path(root, path) };
}
TOML_EXTERNAL_LINKAGE
node_view<const node> at_path(const node& root, std::string_view path) noexcept
{
return node_view<const node>{ TOML_ANON_NAMESPACE::get_at_path(const_cast<node&>(root), path) };
}
#if TOML_ENABLE_WINDOWS_COMPAT
TOML_EXTERNAL_LINKAGE
node_view<node> at_path(node & root, std::wstring_view path)
{
return node_view<node>{ TOML_ANON_NAMESPACE::get_at_path(root, path) };
}
TOML_EXTERNAL_LINKAGE
node_view<const node> at_path(const node& root, std::wstring_view path)
{
return node_view<const node>{ TOML_ANON_NAMESPACE::get_at_path(const_cast<node&>(root), path) };
}
#endif // TOML_ENABLE_WINDOWS_COMPAT
}
TOML_NAMESPACE_END;
#include "header_end.h"

View File

@ -140,7 +140,7 @@ TOML_IMPL_NAMESPACE_START
case '\'': traits |= formatted_string_traits::single_quotes; break;
default:
{
if (is_control_character(c))
if TOML_UNLIKELY(is_control_character(c))
traits |= formatted_string_traits::control_chars;
if (!is_ascii_bare_key_character(static_cast<char32_t>(c)))
@ -191,7 +191,7 @@ TOML_IMPL_NAMESPACE_START
case U'\'': traits |= formatted_string_traits::single_quotes; break;
default:
{
if (is_control_character(decoder.codepoint)
if TOML_UNLIKELY(is_control_character(decoder.codepoint)
|| is_non_ascii_vertical_whitespace(decoder.codepoint))
traits |= formatted_string_traits::control_chars;

View File

@ -1051,7 +1051,7 @@ TOML_IMPL_NAMESPACE_START
template <typename T>
TOML_PURE_GETTER
inline const T& min(const T& a, const T& b) noexcept //
constexpr const T& min(const T& a, const T& b) noexcept //
{
return a < b ? a : b;
}

View File

@ -296,12 +296,14 @@ TOML_NAMESPACE_START
/// \name Iterators
/// @{
/// \brief Returns an iterator to the first character in the key's backing string.
TOML_PURE_INLINE_GETTER
const_iterator begin() const noexcept
{
return key_.data();
}
/// \brief Returns an iterator to one-past-the-last character in the key's backing string.
TOML_PURE_INLINE_GETTER
const_iterator end() const noexcept
{
@ -313,12 +315,14 @@ TOML_NAMESPACE_START
/// \name Iterators (ADL)
/// @{
/// \brief Returns an iterator to the first character in a key's backing string.
TOML_PURE_INLINE_GETTER
friend const_iterator begin(const key& k) noexcept
{
return k.begin();
}
/// \brief Returns an iterator to one-past-the-last character in a key's backing string.
TOML_PURE_INLINE_GETTER
friend const_iterator end(const key& k) noexcept
{

View File

@ -655,14 +655,6 @@ TOML_NAMESPACE_START
TOML_NODISCARD
auto value_or(T&& default_value) const noexcept(impl::value_retrieval_is_nothrow<T>);
//# template <typename T>
//# TOML_NODISCARD
//# std::vector<T> select_exact() const noexcept;
//# template <typename T>
//# TOML_NODISCARD
//# std::vector<T> select() const noexcept;
/// \brief Gets a raw reference to a node's underlying data.
///
/// \warning This function is dangerous if used carelessly and **WILL** break your code if the
@ -968,9 +960,55 @@ TOML_NAMESPACE_START
TOML_NODISCARD
explicit operator node_view<const node>() const noexcept;
/// \brief Returns a view of the subnode matching a fully-qualified "TOML path".
///
/// \see #toml::at_path(node&, std::string_view)
TOML_NODISCARD
TOML_API
node_view<node> at_path(std::string_view path) noexcept;
/// \brief Returns a const view of the subnode matching a fully-qualified "TOML path".
///
/// \see #toml::at_path(node&, std::string_view)
TOML_NODISCARD
TOML_API
node_view<const node> at_path(std::string_view path) const noexcept;
#if TOML_ENABLE_WINDOWS_COMPAT
/// \brief Returns a view of the subnode matching a fully-qualified "TOML path".
///
/// \availability This overload is only available when #TOML_ENABLE_WINDOWS_COMPAT is enabled.
///
/// \see #toml::at_path(node&, std::string_view)
TOML_NODISCARD
TOML_API
node_view<node> at_path(std::wstring_view path);
/// \brief Returns a const view of the subnode matching a fully-qualified "TOML path".
///
/// \availability This overload is only available when #TOML_ENABLE_WINDOWS_COMPAT is enabled.
///
/// \see #toml::at_path(node&, std::string_view)
TOML_NODISCARD
TOML_API
node_view<const node> at_path(std::wstring_view path) const;
#endif // TOML_ENABLE_WINDOWS_COMPAT
/// @}
};
}
TOML_NAMESPACE_END;
/// \cond
TOML_IMPL_NAMESPACE_START
{
TOML_PURE_GETTER
TOML_API
bool node_deep_equality(const node*, const node*) noexcept;
}
TOML_IMPL_NAMESPACE_END;
/// \endcond
#include "header_end.h"

View File

@ -13,6 +13,10 @@
#include "node.h"
#include "node_view.h"
#include "at_path.h"
#include "table.h"
#include "array.h"
#include "value.h"
#include "header_start.h"
TOML_NAMESPACE_START
@ -51,7 +55,62 @@ TOML_NAMESPACE_START
TOML_EXTERNAL_LINKAGE
node::~node() noexcept = default;
TOML_EXTERNAL_LINKAGE
node_view<node> node::at_path(std::string_view path) noexcept
{
return toml::at_path(*this, path);
}
TOML_EXTERNAL_LINKAGE
node_view<const node> node::at_path(std::string_view path) const noexcept
{
return toml::at_path(*this, path);
}
#if TOML_ENABLE_WINDOWS_COMPAT
TOML_EXTERNAL_LINKAGE
node_view<node> node::at_path(std::wstring_view path)
{
return toml::at_path(*this, path);
}
TOML_EXTERNAL_LINKAGE
node_view<const node> node::at_path(std::wstring_view path) const
{
return toml::at_path(*this, path);
}
#endif // TOML_ENABLE_WINDOWS_COMPAT
}
TOML_NAMESPACE_END;
TOML_IMPL_NAMESPACE_START
{
TOML_EXTERNAL_LINKAGE
bool node_deep_equality(const node* lhs, const node* rhs) noexcept
{
// both same or both null
if (lhs == rhs)
return true;
// lhs null != rhs null or different types
if ((!lhs != !rhs) || lhs->type() != rhs->type())
return false;
bool same;
lhs->visit(
[=, &same](auto& l) noexcept
{
using concrete_type = remove_cvref<decltype(l)>;
same = (l == *(rhs->as<concrete_type>()));
});
return same;
}
}
TOML_IMPL_NAMESPACE_END;
#include "header_end.h"

View File

@ -68,7 +68,7 @@ TOML_NAMESPACE_START
private:
template <typename T>
friend class TOML_NAMESPACE::node_view;
friend class node_view;
mutable viewed_type* node_ = nullptr;
@ -587,6 +587,23 @@ TOML_NAMESPACE_START
/// \name Equality
/// @{
public:
/// \brief Returns true if the two views refer to nodes of the same type and value.
template <typename T>
TOML_PURE_GETTER
friend bool operator==(const node_view& lhs, const node_view<T>& rhs) noexcept
{
return impl::node_deep_equality(lhs.node_, rhs.node_);
}
/// \brief Returns true if the two views do not refer to nodes of the same type and value.
template <typename T>
TOML_PURE_GETTER
friend bool operator!=(const node_view& lhs, const node_view<T>& rhs) noexcept
{
return !impl::node_deep_equality(lhs.node_, rhs.node_);
}
/// \brief Returns true if the viewed node is a table with the same contents as RHS.
TOML_NODISCARD
friend bool operator==(const node_view& lhs, const table& rhs) noexcept
@ -686,7 +703,16 @@ TOML_NAMESPACE_START
{
if (auto tbl = this->as_table())
return node_view{ tbl->get(key) };
return node_view{ nullptr };
return {};
}
/// \brief Returns a view of the subnode matching a fully-qualified "TOML path".
///
/// \see #toml::at_path(node&, std::string_view)
TOML_NODISCARD
node_view at_path(std::string_view path) const noexcept
{
return node_ ? node_->at_path(path) : node_view{};
}
#if TOML_ENABLE_WINDOWS_COMPAT
@ -704,7 +730,18 @@ TOML_NAMESPACE_START
{
if (auto tbl = this->as_table())
return node_view{ tbl->get(key) };
return node_view{ nullptr };
return {};
}
/// \brief Returns a view of the subnode matching a fully-qualified "TOML path".
///
/// \availability This overload is only available when #TOML_ENABLE_WINDOWS_COMPAT is enabled.
///
/// \see #toml::at_path(node&, std::string_view)
TOML_NODISCARD
node_view at_path(std::wstring_view path) const
{
return node_ ? node_->at_path(path) : node_view{};
}
#endif // TOML_ENABLE_WINDOWS_COMPAT
@ -720,7 +757,7 @@ TOML_NAMESPACE_START
{
if (auto arr = this->as_array())
return node_view{ arr->get(index) };
return node_view{ nullptr };
return {};
}
/// @}
@ -767,12 +804,12 @@ TOML_NAMESPACE_START
{
inline node::operator node_view<node>() noexcept
{
return node_view<node>(this);
return node_view<node>{ this };
}
inline node::operator node_view<const node>() const noexcept
{
return node_view<const node>(this);
return node_view<const node>{ this };
}
}
TOML_NAMESPACE_END;

View File

@ -495,7 +495,7 @@ TOML_ANON_NAMESPACE_START
class TOML_EMPTY_BASES utf8_buffered_reader
{
public:
static constexpr size_t max_history_length = 72;
static constexpr size_t max_history_length = 128;
private:
static constexpr size_t history_buffer_size = max_history_length - 1; //'head' is stored in the reader
@ -619,40 +619,40 @@ TOML_ANON_NAMESPACE_START
template <>
struct parse_integer_traits<2>
{
static constexpr auto scope_qualifier = "binary integer"sv;
static constexpr auto is_digit = impl::is_binary_digit;
static constexpr auto is_signed = false;
static constexpr auto buffer_length = 63;
static constexpr auto prefix_codepoint = U'b';
static constexpr auto prefix = "b"sv;
static constexpr auto scope_qualifier = "binary integer"sv;
static constexpr auto is_digit = impl::is_binary_digit;
static constexpr auto is_signed = false;
static constexpr auto min_buffer_length = 63;
static constexpr auto prefix_codepoint = U'b';
static constexpr auto prefix = "b"sv;
};
template <>
struct parse_integer_traits<8>
{
static constexpr auto scope_qualifier = "octal integer"sv;
static constexpr auto is_digit = impl::is_octal_digit;
static constexpr auto is_signed = false;
static constexpr auto buffer_length = 21; // strlen("777777777777777777777")
static constexpr auto prefix_codepoint = U'o';
static constexpr auto prefix = "o"sv;
static constexpr auto scope_qualifier = "octal integer"sv;
static constexpr auto is_digit = impl::is_octal_digit;
static constexpr auto is_signed = false;
static constexpr auto min_buffer_length = 21; // strlen("777777777777777777777")
static constexpr auto prefix_codepoint = U'o';
static constexpr auto prefix = "o"sv;
};
template <>
struct parse_integer_traits<10>
{
static constexpr auto scope_qualifier = "decimal integer"sv;
static constexpr auto is_digit = impl::is_decimal_digit;
static constexpr auto is_signed = true;
static constexpr auto buffer_length = 19; // strlen("9223372036854775807")
static constexpr auto scope_qualifier = "decimal integer"sv;
static constexpr auto is_digit = impl::is_decimal_digit;
static constexpr auto is_signed = true;
static constexpr auto min_buffer_length = 19; // strlen("9223372036854775807")
};
template <>
struct parse_integer_traits<16>
{
static constexpr auto scope_qualifier = "hexadecimal integer"sv;
static constexpr auto is_digit = impl::is_hexadecimal_digit;
static constexpr auto is_signed = false;
static constexpr auto buffer_length = 16; // strlen("7FFFFFFFFFFFFFFF")
static constexpr auto prefix_codepoint = U'x';
static constexpr auto prefix = "x"sv;
static constexpr auto scope_qualifier = "hexadecimal integer"sv;
static constexpr auto is_digit = impl::is_hexadecimal_digit;
static constexpr auto is_signed = false;
static constexpr auto min_buffer_length = 16; // strlen("7FFFFFFFFFFFFFFF")
static constexpr auto prefix_codepoint = U'x';
static constexpr auto prefix = "x"sv;
};
TOML_PURE_GETTER
@ -1157,11 +1157,11 @@ TOML_IMPL_NAMESPACE_START
{
advance_and_return_if_error({}); // skip \r
if (is_eof())
set_error_and_return_default("expected \\n, saw EOF"sv);
if TOML_UNLIKELY(is_eof())
set_error_and_return_default("expected \\n after \\r, saw EOF"sv);
if (*cp != U'\n')
set_error_and_return_default("expected \\n, saw '"sv, to_sv(*cp), "'"sv);
if TOML_UNLIKELY(*cp != U'\n')
set_error_and_return_default("expected \\n after \\r, saw '"sv, to_sv(*cp), "'"sv);
}
else if (*cp != U'\n')
return false;
@ -1204,18 +1204,19 @@ TOML_IMPL_NAMESPACE_START
return true;
return_if_error({});
if constexpr (TOML_LANG_AT_LEAST(1, 0, 0))
{
// toml/issues/567 (disallow non-TAB control characters in comments)
if (is_nontab_control_character(*cp))
set_error_and_return_default(
"control characters other than TAB (U+0009) are explicitly prohibited in comments"sv);
#if TOML_LANG_AT_LEAST(1, 0, 0)
// toml/issues/567 (disallow non-TAB control characters in comments)
if TOML_UNLIKELY(is_nontab_control_character(*cp))
set_error_and_return_default(
"control characters other than TAB (U+0009) are explicitly prohibited in comments"sv);
// toml/pull/720 (disallow surrogates in comments)
else if TOML_UNLIKELY(is_unicode_surrogate(*cp))
set_error_and_return_default(
"unicode surrogates (U+D800 to U+DFFF) are explicitly prohibited in comments"sv);
#endif
// toml/pull/720 (disallow surrogates in comments)
else if (is_unicode_surrogate(*cp))
set_error_and_return_default(
"unicode surrogates (U+D800 to U+DFFF) are explicitly prohibited in comments"sv);
}
advance_and_return_if_error({});
}
@ -1312,9 +1313,11 @@ TOML_IMPL_NAMESPACE_START
if (multi_line && is_whitespace(*cp))
{
consume_leading_whitespace();
if (!consume_line_break())
if TOML_UNLIKELY(!consume_line_break())
set_error_and_return_default(
"line-ending backslashes must be the last non-whitespace character on the line"sv);
skipping_whitespace = true;
return_if_error({});
continue;
@ -1354,17 +1357,19 @@ TOML_IMPL_NAMESPACE_START
while (place_value)
{
set_error_and_return_if_eof({});
if (!is_hexadecimal_digit(*cp))
if TOML_UNLIKELY(!is_hexadecimal_digit(*cp))
set_error_and_return_default("expected hex digit, saw '"sv, to_sv(*cp), "'"sv);
sequence_value += place_value * hex_to_dec(*cp);
place_value /= 16u;
advance_and_return_if_error({});
}
if (is_unicode_surrogate(sequence_value))
if TOML_UNLIKELY(is_unicode_surrogate(sequence_value))
set_error_and_return_default(
"unicode surrogates (U+D800 - U+DFFF) are explicitly prohibited"sv);
else if (sequence_value > 0x10FFFFu)
else if TOML_UNLIKELY(sequence_value > 0x10FFFFu)
set_error_and_return_default("values greater than U+10FFFF are invalid"sv);
if (sequence_value < 0x80)
@ -1392,7 +1397,8 @@ TOML_IMPL_NAMESPACE_START
break;
}
// ???
// ???
TOML_UNLIKELY_CASE
default: set_error_and_return_default("unknown escape sequence '\\"sv, to_sv(*cp), "'"sv);
}
@ -1475,17 +1481,17 @@ TOML_IMPL_NAMESPACE_START
}
// handle control characters
if (is_nontab_control_character(*cp))
if TOML_UNLIKELY(is_nontab_control_character(*cp))
set_error_and_return_default(
"unescaped control characters other than TAB (U+0009) are explicitly prohibited"sv);
// handle surrogates in strings (1.0.0 and later)
if constexpr (TOML_LANG_AT_LEAST(1, 0, 0))
{
if (is_unicode_surrogate(*cp))
set_error_and_return_default(
"unescaped unicode surrogates (U+D800 to U+DFFF) are explicitly prohibited"sv);
}
#if TOML_LANG_AT_LEAST(1, 0, 0)
// handle surrogates in strings
if TOML_UNLIKELY(is_unicode_surrogate(*cp))
set_error_and_return_default(
"unescaped unicode surrogates (U+D800 to U+DFFF) are explicitly prohibited"sv);
#endif
if (multi_line)
{
@ -1590,17 +1596,16 @@ TOML_IMPL_NAMESPACE_START
}
// handle control characters
if (is_nontab_control_character(*cp))
if TOML_UNLIKELY(is_nontab_control_character(*cp))
set_error_and_return_default(
"control characters other than TAB (U+0009) are explicitly prohibited"sv);
// handle surrogates in strings (1.0.0 and later)
if constexpr (TOML_LANG_AT_LEAST(1, 0, 0))
{
if (is_unicode_surrogate(*cp))
set_error_and_return_default(
"unicode surrogates (U+D800 - U+DFFF) are explicitly prohibited"sv);
}
#if TOML_LANG_AT_LEAST(1, 0, 0)
// handle surrogates in strings
if TOML_UNLIKELY(is_unicode_surrogate(*cp))
set_error_and_return_default("unicode surrogates (U+D800 - U+DFFF) are explicitly prohibited"sv);
#endif
str.append(cp->bytes, cp->count);
advance_and_return_if_error({});
@ -1746,13 +1751,19 @@ TOML_IMPL_NAMESPACE_START
advance_and_return_if_error_or_eof({});
// consume value chars
char chars[64];
char chars[utf8_buffered_reader::max_history_length];
size_t length = {};
const utf8_codepoint* prev = {};
bool seen_decimal = false, seen_exponent = false;
char first_integer_part = '\0';
while (!is_eof() && !is_value_terminator(*cp))
{
if TOML_UNLIKELY(length == sizeof(chars))
set_error_and_return_default("exceeds maximum length of "sv,
static_cast<uint64_t>(sizeof(chars)),
" characters"sv,
(seen_exponent ? ""sv : " (consider using exponent notation)"sv));
if (*cp == U'_')
{
if (!prev || !is_decimal_digit(*prev))
@ -1819,11 +1830,6 @@ TOML_IMPL_NAMESPACE_START
else
set_error_and_return_default("expected decimal digit, saw '"sv, to_sv(*cp), "'"sv);
if (length == sizeof(chars))
set_error_and_return_default("exceeds maximum length of "sv,
static_cast<uint64_t>(sizeof(chars)),
" characters"sv);
chars[length++] = static_cast<char>(cp->bytes[0]);
prev = cp;
advance_and_return_if_error({});
@ -1851,6 +1857,7 @@ TOML_IMPL_NAMESPACE_START
auto fc_result = std::from_chars(chars, chars + length, result);
switch (fc_result.ec)
{
TOML_LIKELY_CASE
case std::errc{}: // ok
return result * sign;
@ -2103,7 +2110,9 @@ TOML_IMPL_NAMESPACE_START
}
// consume value chars
char chars[traits::buffer_length];
static constexpr size_t underscore_padding = 32;
static constexpr size_t buffer_length = traits::min_buffer_length + underscore_padding;
char chars[buffer_length];
size_t length = {};
const utf8_codepoint* prev = {};
while (!is_eof() && !is_value_terminator(*cp))
@ -2609,7 +2618,7 @@ TOML_IMPL_NAMESPACE_START
advance_count++;
eof_while_scanning = is_eof();
}
while (advance_count < utf8_buffered_reader::max_history_length && !is_eof()
while (advance_count < (utf8_buffered_reader::max_history_length - 1u) && !is_eof()
&& !is_value_terminator(*cp));
};
scan();
@ -2658,8 +2667,6 @@ TOML_IMPL_NAMESPACE_START
// set the reader back to where we started
go_back(advance_count);
if (char_count < utf8_buffered_reader::max_history_length - 1u)
chars[char_count] = U'\0';
// if after scanning ahead we still only have one value character,
// the only valid value type is an integer.
@ -2765,7 +2772,23 @@ TOML_IMPL_NAMESPACE_START
case bzero_msk: [[fallthrough]];
case bdigit_msk: [[fallthrough]];
case begins_sign | has_digits | has_minus: [[fallthrough]];
case begins_sign | has_digits | has_plus: val.reset(new value{ parse_integer<10>() }); break;
case begins_sign | has_digits | has_plus:
{
// if the value was so long we exhausted the history buffer it's reasonable to assume
// there was more and the value's actual type is impossible to identify without making the
// buffer bigger (since it could have actually been a float), so emit an error.
//
// (this will likely only come up during fuzzing and similar scenarios)
static constexpr size_t max_numeric_value_length =
utf8_buffered_reader::max_history_length - 2u;
if TOML_UNLIKELY(!eof_while_scanning && advance_count > max_numeric_value_length)
set_error_and_return_default("numeric value too long to identify type - cannot exceed "sv,
max_numeric_value_length,
" characters"sv);
val.reset(new value{ parse_integer<10>() });
break;
}
// hexadecimal integers
// 0x10
@ -2923,17 +2946,6 @@ TOML_IMPL_NAMESPACE_START
return_after_error({});
}
#if !TOML_LANG_AT_LEAST(1, 0, 0) // toml/issues/665 (heterogeneous arrays)
{
if (auto arr = val->as_array(); arr && !arr->is_homogeneous())
{
delete arr;
set_error_at(begin_pos, "arrays cannot contain values of different types before TOML 1.0.0"sv);
return_after_error({});
}
}
#endif
val->source_ = { begin_pos, current_position(1), reader.source_path() };
return val;
}

View File

@ -358,7 +358,6 @@
#elif TOML_CPP_VERSION < 201703L
#error toml++ requires C++17 or higher. For a TOML library supporting C++11 see https://github.com/ToruNiina/toml11
#endif
#undef TOML_CPP_VERSION
//#====================================================================================================================
//# USER CONFIGURATION
@ -621,19 +620,35 @@
#define TOML_HAS_ATTR(...) 0
#endif
#if !defined(TOML_LIKELY) && TOML_HAS_ATTR(likely) >= 201803
#define TOML_LIKELY(...) (__VA_ARGS__) [[likely]]
#if TOML_HAS_ATTR(likely) >= 201803
#ifndef TOML_LIKELY
#define TOML_LIKELY(...) (__VA_ARGS__) [[likely]]
#endif
#ifndef TOML_LIKELY_CASE
#define TOML_LIKELY_CASE [[likely]]
#endif
#endif
#ifndef TOML_LIKELY
#define TOML_LIKELY(...) (__VA_ARGS__)
#endif
#ifndef TOML_LIKELY_CASE
#define TOML_LIKELY_CASE
#endif
#if !defined(TOML_UNLIKELY) && TOML_HAS_ATTR(unlikely) >= 201803
#define TOML_UNLIKELY(...) (__VA_ARGS__) [[unlikely]]
#if TOML_HAS_ATTR(unlikely) >= 201803
#ifndef TOML_UNLIKELY
#define TOML_UNLIKELY(...) (__VA_ARGS__) [[unlikely]]
#endif
#ifndef TOML_UNLIKELY_CASE
#define TOML_UNLIKELY_CASE [[unlikely]]
#endif
#endif
#ifndef TOML_UNLIKELY
#define TOML_UNLIKELY(...) (__VA_ARGS__)
#endif
#ifndef TOML_UNLIKELY_CASE
#define TOML_UNLIKELY_CASE
#endif
#if TOML_HAS_ATTR(nodiscard)
#define TOML_NODISCARD [[nodiscard]]

View File

@ -678,29 +678,29 @@ TOML_NAMESPACE_START
/// node ["a"] was an integer with value 42
/// \eout
///
/// \tparam ValueType One of the TOML node or value types.
/// \param key The node's key.
/// \tparam T One of the TOML node or value types.
/// \param key The node's key.
///
/// \returns A pointer to the node at the specified key if it was of the given type, or nullptr.
template <typename ValueType>
template <typename T>
TOML_PURE_GETTER
impl::wrap_node<ValueType>* get_as(std::string_view key) noexcept
impl::wrap_node<T>* get_as(std::string_view key) noexcept
{
const auto n = this->get(key);
return n ? n->template as<ValueType>() : nullptr;
return n ? n->template as<T>() : nullptr;
}
/// \brief Gets the node at a specific key if it is a particular type (const overload).
///
/// \tparam ValueType One of the TOML node or value types.
/// \param key The node's key.
/// \tparam T One of the TOML node or value types.
/// \param key The node's key.
///
/// \returns A pointer to the node at the specified key if it was of the given type, or nullptr.
template <typename ValueType>
template <typename T>
TOML_PURE_GETTER
const impl::wrap_node<ValueType>* get_as(std::string_view key) const noexcept
const impl::wrap_node<T>* get_as(std::string_view key) const noexcept
{
return const_cast<table&>(*this).template get_as<ValueType>(key);
return const_cast<table&>(*this).template get_as<T>(key);
}
#if TOML_ENABLE_WINDOWS_COMPAT
@ -709,33 +709,33 @@ TOML_NAMESPACE_START
///
/// \availability This overload is only available when #TOML_ENABLE_WINDOWS_COMPAT is enabled.
///
/// \tparam ValueType One of the TOML node or value types.
/// \param key The node's key.
/// \tparam T One of the TOML node or value types.
/// \param key The node's key.
///
/// \returns A pointer to the node at the specified key if it was of the given type, or nullptr.
template <typename ValueType>
template <typename T>
TOML_NODISCARD
impl::wrap_node<ValueType>* get_as(std::wstring_view key)
impl::wrap_node<T>* get_as(std::wstring_view key)
{
if (empty())
return nullptr;
return get_as<ValueType>(impl::narrow(key));
return get_as<T>(impl::narrow(key));
}
/// \brief Gets the node at a specific key if it is a particular type (const overload).
///
/// \availability This overload is only available when #TOML_ENABLE_WINDOWS_COMPAT is enabled.
///
/// \tparam ValueType One of the TOML node or value types.
/// \param key The node's key.
/// \tparam T One of the TOML node or value types.
/// \param key The node's key.
///
/// \returns A pointer to the node at the specified key if it was of the given type, or nullptr.
template <typename ValueType>
template <typename T>
TOML_NODISCARD
const impl::wrap_node<ValueType>* get_as(std::wstring_view key) const
const impl::wrap_node<T>* get_as(std::wstring_view key) const
{
return const_cast<table&>(*this).template get_as<ValueType>(key);
return const_cast<table&>(*this).template get_as<T>(key);
}
#endif // TOML_ENABLE_WINDOWS_COMPAT
@ -1571,7 +1571,7 @@ TOML_NAMESPACE_START
/// \name Node views
/// @{
/// \brief Gets a node_view for the selected key-value pair.
/// \brief Gets a node_view for the selected value.
///
/// \param key The key used for the lookup.
///
@ -1588,7 +1588,7 @@ TOML_NAMESPACE_START
return node_view<node>{ get(key) };
}
/// \brief Gets a node_view for the selected key-value pair (const overload).
/// \brief Gets a node_view for the selected value (const overload).
///
/// \param key The key used for the lookup.
///
@ -1607,7 +1607,7 @@ TOML_NAMESPACE_START
#if TOML_ENABLE_WINDOWS_COMPAT
/// \brief Gets a node_view for the selected key-value pair.
/// \brief Gets a node_view for the selected value.
///
/// \availability This overload is only available when #TOML_ENABLE_WINDOWS_COMPAT is enabled.
///
@ -1626,7 +1626,7 @@ TOML_NAMESPACE_START
return node_view<node>{ get(key) };
}
/// \brief Gets a node_view for the selected key-value pair (const overload).
/// \brief Gets a node_view for the selected value (const overload).
///
/// \availability This overload is only available when #TOML_ENABLE_WINDOWS_COMPAT is enabled.
///
@ -1700,39 +1700,6 @@ TOML_NAMESPACE_START
#endif
};
//# template <typename T>
//# inline std::vector<T> node::select_exact() const noexcept
//# {
//# using namespace impl;
//#
//# static_assert(
//# !is_wide_string<T> || TOML_ENABLE_WINDOWS_COMPAT,
//# "Retrieving values as wide-character strings with node::select_exact() is only "
//# "supported on Windows with TOML_ENABLE_WINDOWS_COMPAT enabled."
//# );
//#
//# static_assert(
//# (is_native<T> || can_represent_native<T>) && !is_cvref<T>,
//# TOML_SA_VALUE_EXACT_FUNC_MESSAGE("return type of node::select_exact()")
//# );
//# }
//# template <typename T>
//# inline std::vector<T> node::select() const noexcept
//# {
//# using namespace impl;
//#
//# static_assert(
//# !is_wide_string<T> || TOML_ENABLE_WINDOWS_COMPAT,
//# "Retrieving values as wide-character strings with node::select() is only "
//# "supported on Windows with TOML_ENABLE_WINDOWS_COMPAT enabled."
//# );
//# static_assert(
//# (is_native<T> || can_represent_native<T> || can_partially_represent_native<T>) && !is_cvref<T>,
//# TOML_SA_VALUE_FUNC_MESSAGE("return type of node::select()")
//# );
//# }
}
TOML_NAMESPACE_END;

View File

@ -16,13 +16,18 @@ TOML_PUSH_WARNINGS;
TOML_DISABLE_SPAM_WARNINGS;
TOML_DISABLE_SWITCH_WARNINGS;
TOML_DISABLE_SUGGEST_ATTR_WARNINGS;
// misc warning false-positives
#if TOML_MSVC
#pragma warning(disable : 5031) // #pragma warning(pop): likely mismatch (false-positive)
#elif TOML_CLANG && !TOML_HEADER_ONLY && TOML_IMPLEMENTATION
#pragma clang diagnostic ignored "-Wheader-hygiene" // false-positive
#pragma warning(disable : 5031) // #pragma warning(pop): likely mismatch
#elif TOML_CLANG
#pragma clang diagnostic ignored "-Wheader-hygiene"
#if TOML_CLANG >= 12
#pragma clang diagnostic ignored "-Wc++20-extensions"
#endif
#if TOML_CLANG == 13
#pragma clang diagnostic ignored "-Wreserved-identifier" // false-positive
#pragma clang diagnostic ignored "-Wreserved-identifier"
#endif
#endif
#include "impl/std_new.h"
@ -32,6 +37,7 @@ TOML_DISABLE_SUGGEST_ATTR_WARNINGS;
#include "impl/print_to_stream.h"
#include "impl/source_region.h"
#include "impl/date_time.h"
#include "impl/at_path.h"
#include "impl/node.h"
#include "impl/node_view.h"
#include "impl/value.h"
@ -54,6 +60,7 @@ TOML_DISABLE_SUGGEST_ATTR_WARNINGS;
#include "impl/print_to_stream.inl"
#include "impl/node.inl"
#include "impl/node_view.inl"
#include "impl/at_path.inl"
#include "impl/value.inl"
#include "impl/array.inl"
#include "impl/table.inl"
@ -92,6 +99,7 @@ TOML_POP_WARNINGS;
#undef TOML_CONST_GETTER
#undef TOML_CONST_INLINE_GETTER
#undef TOML_CONSTRAINED_TEMPLATE
#undef TOML_CPP_VERSION
#undef TOML_DELETE_DEFAULTS
#undef TOML_DISABLE_ARITHMETIC_WARNINGS
#undef TOML_DISABLE_CODE_ANALYSIS_WARNINGS
@ -138,6 +146,7 @@ TOML_POP_WARNINGS;
#undef TOML_LAUNDER
#undef TOML_LIFETIME_HOOKS
#undef TOML_LIKELY
#undef TOML_LIKELY_CASE
#undef TOML_MAKE_FLAGS
#undef TOML_MAKE_FLAGS_
#undef TOML_MAKE_VERSION
@ -172,6 +181,7 @@ TOML_POP_WARNINGS;
#undef TOML_TRIVIAL_ABI
#undef TOML_UINT128
#undef TOML_UNLIKELY
#undef TOML_UNLIKELY_CASE
#undef TOML_UNREACHABLE
#undef TOML_UNUSED
#endif

115
tests/at_path.cpp Normal file
View File

@ -0,0 +1,115 @@
// This file is a part of toml++ and is subject to the the terms of the MIT license.
// Copyright (c) Mark Gillard <mark.gillard@outlook.com.au>
// See https://github.com/marzer/tomlplusplus/blob/master/LICENSE for the full license text.
// SPDX-License-Identifier: MIT
#include "tests.h"
TOML_DISABLE_SPAM_WARNINGS;
TEST_CASE("at_path")
{
// clang-format off
const auto tbl = table
{
{ ""sv, 0 }, // blank key
{ "a"sv, 1 },
{
"b"sv,
array
{
2,
array{ 3 },
table { { "c", 4 } }
},
},
{ "d", table{ {"e", 5, }, {""sv, -1 } } }
};
// clang-format on
/*
# equivalent to the following TOML:
"" = 0
a = 1
b = [
2,
[ 3 ],
{ "c" = 4 }
]
d = { "e" = 5, "" = -1 }
*/
SECTION("table")
{
// this section uses the free function version of at_path
CHECK(tbl[""]);
CHECK(tbl[""] == at_path(tbl, ""));
CHECK(tbl["a"]);
CHECK(tbl["a"] == at_path(tbl, "a"));
CHECK(tbl["a"] != at_path(tbl, ".a")); // equivalent to ""."a"
CHECK(!at_path(tbl, ".a"));
CHECK(tbl["b"]);
CHECK(tbl["b"] == at_path(tbl, "b"));
CHECK(tbl["b"][0]);
CHECK(tbl["b"][0] == at_path(tbl, "b[0]"));
CHECK(tbl["b"][0] == at_path(tbl, "b[0] "));
CHECK(tbl["b"][0] == at_path(tbl, "b[ 0\t]")); // whitespace is allowed inside array indexer
CHECK(tbl["b"][1]);
CHECK(tbl["b"][1] != tbl["b"][0]);
CHECK(tbl["b"][1] == at_path(tbl, "b[1]"));
CHECK(tbl["b"][1][0]);
CHECK(tbl["b"][1][0] == at_path(tbl, "b[1][0]"));
CHECK(tbl["b"][1][0] == at_path(tbl, "b[1] \t [0]")); // whitespace is allowed after array
// indexers
CHECK(tbl["b"][2]["c"]);
CHECK(tbl["b"][2]["c"] == at_path(tbl, "b[2].c"));
CHECK(tbl["b"][2]["c"] == at_path(tbl, "b[2] \t.c")); // whitespace is allowed after array indexers
CHECK(tbl["d"]);
CHECK(tbl["d"] == at_path(tbl, "d"));
CHECK(tbl["d"]["e"]);
CHECK(tbl["d"]["e"] == at_path(tbl, "d.e"));
CHECK(tbl["d"]["e"] != at_path(tbl, "d. e")); // equivalent to "d"." e"
CHECK(!at_path(tbl, "d. e"));
CHECK(tbl["d"][""]);
CHECK(tbl["d"][""] == at_path(tbl, "d."));
}
SECTION("array")
{
// this section uses the node_view member function version of at_path
auto arr = tbl["b"];
CHECK(tbl["b"][0]);
CHECK(tbl["b"][0] == arr.at_path("[0]"));
CHECK(tbl["b"][0] == arr.at_path("[0] "));
CHECK(tbl["b"][0] == arr.at_path("[ 0\t]")); // whitespace is allowed inside array indexer
CHECK(tbl["b"][1]);
CHECK(tbl["b"][1].node() != arr[0].node());
CHECK(tbl["b"][1] == arr.at_path("[1]"));
CHECK(tbl["b"][1][0]);
CHECK(tbl["b"][1][0] == arr.at_path("[1][0]"));
CHECK(tbl["b"][1][0] == arr.at_path("[1] \t [0]")); // whitespace is allowed after array
// indexers
CHECK(tbl["b"][2]["c"]);
CHECK(tbl["b"][2]["c"] == arr.at_path("[2].c"));
CHECK(tbl["b"][2]["c"] == arr.at_path("[2] \t.c")); // whitespace is allowed after array indexers
}
}

View File

@ -46,29 +46,34 @@ fruit = []
static constexpr auto bool_wrong_case_false = R"(b = FALSE)"sv;
static constexpr auto bool_wrong_case_true = R"(a = TRUE)"sv;
static constexpr auto control_bare_null = "bare-null = \"some value\" \x00"sv;
static constexpr auto control_comment_cr = "comment-cr = \"Carriage return in comment\" # \ra=1"sv;
static constexpr auto control_comment_del = "comment-del = \"0x7f\" # \x7F"sv;
static constexpr auto control_comment_lf = "comment-lf = \"ctrl-P\" # \x10"sv;
static constexpr auto control_comment_null = "comment-null = \"null\" # \x00"sv;
static constexpr auto control_comment_us = "comment-us = \"ctrl-_\" # \x1F"sv;
static constexpr auto control_multi_del = "multi-del = \"\"\"null\x7F\"\"\""sv;
static constexpr auto control_multi_lf = "multi-lf = \"\"\"null\x10\"\"\""sv;
static constexpr auto control_multi_null = "multi-null = \"\"\"null\x00\"\"\""sv;
static constexpr auto control_multi_us = "multi-us = \"\"\"null\x1F\"\"\""sv;
static constexpr auto control_rawmulti_del = "rawmulti-del = '''null\x7F'''"sv;
static constexpr auto control_rawmulti_lf = "rawmulti-lf = '''null\x10'''"sv;
static constexpr auto control_rawmulti_null = "rawmulti-null = '''null\x00'''"sv;
static constexpr auto control_rawmulti_us = "rawmulti-us = '''null\x1F'''"sv;
static constexpr auto control_rawstring_del = "rawstring-del = 'null\x7F'"sv;
static constexpr auto control_rawstring_lf = "rawstring-lf = 'null\x10'"sv;
static constexpr auto control_rawstring_null = "rawstring-null = 'null\x00'"sv;
static constexpr auto control_rawstring_us = "rawstring-us = 'null\x1F'"sv;
static constexpr auto control_string_bs = "string-bs = \"backspace\x08\""sv;
static constexpr auto control_string_del = "string-del = \"null\x7F\""sv;
static constexpr auto control_string_lf = "string-lf = \"null\x10\""sv;
static constexpr auto control_string_null = "string-null = \"null\x00\""sv;
static constexpr auto control_string_us = "string-us = \"null\x1F\""sv;
static constexpr auto control_bare_cr =
"# The following line contains a single carriage return control character\r\n"
"\r"sv;
static constexpr auto control_bare_formfeed = "bare-formfeed = \f"sv;
static constexpr auto control_bare_null = "bare-null = \"some value\" \x00"sv;
static constexpr auto control_bare_vertical_tab = "bare-vertical-tab = \v"sv;
static constexpr auto control_comment_cr = "comment-cr = \"Carriage return in comment\" # \ra=1"sv;
static constexpr auto control_comment_del = "comment-del = \"0x7f\" # \x7F"sv;
static constexpr auto control_comment_lf = "comment-lf = \"ctrl-P\" # \x10"sv;
static constexpr auto control_comment_null = "comment-null = \"null\" # \x00"sv;
static constexpr auto control_comment_us = "comment-us = \"ctrl-_\" # \x1F"sv;
static constexpr auto control_multi_del = "multi-del = \"\"\"null\x7F\"\"\""sv;
static constexpr auto control_multi_lf = "multi-lf = \"\"\"null\x10\"\"\""sv;
static constexpr auto control_multi_null = "multi-null = \"\"\"null\x00\"\"\""sv;
static constexpr auto control_multi_us = "multi-us = \"\"\"null\x1F\"\"\""sv;
static constexpr auto control_rawmulti_del = "rawmulti-del = '''null\x7F'''"sv;
static constexpr auto control_rawmulti_lf = "rawmulti-lf = '''null\x10'''"sv;
static constexpr auto control_rawmulti_null = "rawmulti-null = '''null\x00'''"sv;
static constexpr auto control_rawmulti_us = "rawmulti-us = '''null\x1F'''"sv;
static constexpr auto control_rawstring_del = "rawstring-del = 'null\x7F'"sv;
static constexpr auto control_rawstring_lf = "rawstring-lf = 'null\x10'"sv;
static constexpr auto control_rawstring_null = "rawstring-null = 'null\x00'"sv;
static constexpr auto control_rawstring_us = "rawstring-us = 'null\x1F'"sv;
static constexpr auto control_string_bs = "string-bs = \"backspace\x08\""sv;
static constexpr auto control_string_del = "string-del = \"null\x7F\""sv;
static constexpr auto control_string_lf = "string-lf = \"null\x10\""sv;
static constexpr auto control_string_null = "string-null = \"null\x00\""sv;
static constexpr auto control_string_us = "string-us = \"null\x1F\""sv;
static constexpr auto datetime_hour_over = R"(# time-hour = 2DIGIT ; 00-23
d = 2006-01-01T24:00:00-00:00)"sv;
@ -169,6 +174,9 @@ trailing-us-exp2 = 1.2_e2)"sv;
static constexpr auto float_us_after_point = R"(us-after-point = 1._2)"sv;
static constexpr auto float_us_before_point = R"(us-before-point = 1_.2)"sv;
static constexpr auto inline_table_add = R"(a={}
# Inline tables are immutable and can't be extended
[a.b])"sv;
static constexpr auto inline_table_double_comma = R"(t = {x=3,,y=4})"sv;
static constexpr auto inline_table_duplicate_key = R"(# Duplicate keys within an inline table are invalid
a={b=1, b=2})"sv;
@ -206,6 +214,9 @@ abc = { abc = 123, })"sv;
static constexpr auto integer_double_sign_nex = R"(double-sign-nex = --99)"sv;
static constexpr auto integer_double_sign_plus = R"(double-sign-plus = ++99)"sv;
static constexpr auto integer_double_us = R"(double-us = 1__23)"sv;
static constexpr auto integer_incomplete_bin = R"(incomplete-bin = 0b)"sv;
static constexpr auto integer_incomplete_hex = R"(incomplete-hex = 0x)"sv;
static constexpr auto integer_incomplete_oct = R"(incomplete-oct = 0o)"sv;
static constexpr auto integer_invalid_bin = R"(invalid-bin = 0b0012)"sv;
static constexpr auto integer_invalid_hex = R"(invalid-hex = 0xaafz)"sv;
static constexpr auto integer_invalid_oct = R"(invalid-oct = 0o778)"sv;
@ -436,8 +447,14 @@ TEST_CASE("conformance - burntsushi/invalid")
parsing_should_fail(FILE_LINE_ARGS, bool_wrong_case_true); // bool-wrong-case-true
parsing_should_fail(FILE_LINE_ARGS, control_bare_cr); // control-bare-cr
parsing_should_fail(FILE_LINE_ARGS, control_bare_formfeed); // control-bare-formfeed
parsing_should_fail(FILE_LINE_ARGS, control_bare_null); // control-bare-null
parsing_should_fail(FILE_LINE_ARGS, control_bare_vertical_tab); // control-bare-vertical-tab
parsing_should_fail(FILE_LINE_ARGS, control_comment_cr); // control-comment-cr
parsing_should_fail(FILE_LINE_ARGS, control_comment_del); // control-comment-del
@ -590,6 +607,8 @@ TEST_CASE("conformance - burntsushi/invalid")
parsing_should_fail(FILE_LINE_ARGS, float_us_before_point); // float-us-before-point
parsing_should_fail(FILE_LINE_ARGS, inline_table_add); // inline-table-add
parsing_should_fail(FILE_LINE_ARGS, inline_table_double_comma); // inline-table-double-comma
parsing_should_fail(FILE_LINE_ARGS, inline_table_duplicate_key); // inline-table-duplicate-key
@ -626,6 +645,12 @@ TEST_CASE("conformance - burntsushi/invalid")
parsing_should_fail(FILE_LINE_ARGS, integer_double_us); // integer-double-us
parsing_should_fail(FILE_LINE_ARGS, integer_incomplete_bin); // integer-incomplete-bin
parsing_should_fail(FILE_LINE_ARGS, integer_incomplete_hex); // integer-incomplete-hex
parsing_should_fail(FILE_LINE_ARGS, integer_incomplete_oct); // integer-incomplete-oct
parsing_should_fail(FILE_LINE_ARGS, integer_invalid_bin); // integer-invalid-bin
parsing_should_fail(FILE_LINE_ARGS, integer_invalid_hex); // integer-invalid-hex

View File

@ -446,6 +446,11 @@ notunicode3 = "This string does not have a unicode \\u0075 escape."
notunicode4 = "This string does not have a unicode \\\u0075 escape."
delete = "This string has a \u007F delete control code."
unitseparator = "This string has a \u001F unit separator control code.")"sv;
static constexpr auto string_multiline_escaped_crlf =
"# The following line should be an unescaped backslash followed by a Windows\r\n"
"# newline sequence (\"\\r\\n\")\r\n"
"0=\"\"\"\\\r\n"
"\"\"\""sv;
static constexpr auto string_multiline_quotes =
R"(# Make sure that quotes inside multiline strings are allowed, including right
# after the opening '''/""" and before the closing '''/"""
@ -2141,7 +2146,7 @@ another line)"sv },
{ R"(backspace)"sv, "This string has a \x08 backspace character."sv },
{ R"(carriage)"sv, "This string has a \r carriage return character."sv },
{ R"(delete)"sv, "This string has a \x7F delete control code."sv },
{ R"(formfeed)"sv, "This string has a \x0C form feed character."sv },
{ R"(formfeed)"sv, "This string has a \f form feed character."sv },
{ R"(newline)"sv, R"(This string has a
new line character.)"sv },
{ R"(notunicode1)"sv, R"(This string does not have a unicode \u escape.)"sv },
@ -2155,6 +2160,16 @@ another line)"sv },
REQUIRE(tbl == expected);
});
parsing_should_succeed(FILE_LINE_ARGS,
string_multiline_escaped_crlf,
[](toml::table&& tbl) // string-multiline-escaped-crlf
{
const auto expected = toml::table{
{ R"(0)"sv, ""sv },
};
REQUIRE(tbl == expected);
});
parsing_should_succeed(
FILE_LINE_ARGS,
string_multiline_quotes,

View File

@ -1466,7 +1466,7 @@ Violets are blue)"sv },
[](toml::table&& tbl) // spec-string-escape-4
{
const auto expected = toml::table{
{ R"(a)"sv, "\x0C"sv },
{ R"(a)"sv, "\f"sv },
};
REQUIRE(tbl == expected);
});

View File

@ -1,4 +1,5 @@
test_sources = [
'at_path.cpp',
'conformance_burntsushi_invalid.cpp',
'conformance_burntsushi_valid.cpp',
'conformance_iarna_invalid.cpp',

View File

@ -95,7 +95,7 @@ TEST_CASE("user feedback")
"\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c' 'ml'\n\n%\x87");
parsing_should_fail(
FILE_LINE_ARGS,
R"(t =[ 9, 2, 1,"r", 9999999999999999999999999999999999999999999999999999999999999995.0 ])");
R"(t =[ 9, 2, 1,"r", 100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000.0 ])");
}
SECTION("github/issues/67") // https://github.com/marzer/tomlplusplus/issues/67

View File

@ -73,6 +73,7 @@
<LocalDebuggerWorkingDirectory>$(ProjectDir)..\</LocalDebuggerWorkingDirectory>
</PropertyGroup>
<ItemGroup>
<ClCompile Include="..\at_path.cpp" />
<ClCompile Include="..\conformance_burntsushi_invalid.cpp" />
<ClCompile Include="..\conformance_burntsushi_valid.cpp" />
<ClCompile Include="..\conformance_iarna_invalid.cpp" />

View File

@ -73,6 +73,7 @@
<LocalDebuggerWorkingDirectory>$(ProjectDir)..\</LocalDebuggerWorkingDirectory>
</PropertyGroup>
<ItemGroup>
<ClCompile Include="..\at_path.cpp" />
<ClCompile Include="..\conformance_burntsushi_invalid.cpp" />
<ClCompile Include="..\conformance_burntsushi_valid.cpp" />
<ClCompile Include="..\conformance_iarna_invalid.cpp" />

View File

@ -73,6 +73,7 @@
<LocalDebuggerWorkingDirectory>$(ProjectDir)..\</LocalDebuggerWorkingDirectory>
</PropertyGroup>
<ItemGroup>
<ClCompile Include="..\at_path.cpp" />
<ClCompile Include="..\conformance_burntsushi_invalid.cpp" />
<ClCompile Include="..\conformance_burntsushi_valid.cpp" />
<ClCompile Include="..\conformance_iarna_invalid.cpp" />

View File

@ -73,6 +73,7 @@
<LocalDebuggerWorkingDirectory>$(ProjectDir)..\</LocalDebuggerWorkingDirectory>
</PropertyGroup>
<ItemGroup>
<ClCompile Include="..\at_path.cpp" />
<ClCompile Include="..\conformance_burntsushi_invalid.cpp" />
<ClCompile Include="..\conformance_burntsushi_valid.cpp" />
<ClCompile Include="..\conformance_iarna_invalid.cpp" />

View File

@ -73,6 +73,7 @@
<LocalDebuggerWorkingDirectory>$(ProjectDir)..\</LocalDebuggerWorkingDirectory>
</PropertyGroup>
<ItemGroup>
<ClCompile Include="..\at_path.cpp" />
<ClCompile Include="..\conformance_burntsushi_invalid.cpp" />
<ClCompile Include="..\conformance_burntsushi_valid.cpp" />
<ClCompile Include="..\conformance_iarna_invalid.cpp" />

View File

@ -73,6 +73,7 @@
<LocalDebuggerWorkingDirectory>$(ProjectDir)..\</LocalDebuggerWorkingDirectory>
</PropertyGroup>
<ItemGroup>
<ClCompile Include="..\at_path.cpp" />
<ClCompile Include="..\conformance_burntsushi_invalid.cpp" />
<ClCompile Include="..\conformance_burntsushi_valid.cpp" />
<ClCompile Include="..\conformance_iarna_invalid.cpp" />

View File

@ -73,6 +73,7 @@
<LocalDebuggerWorkingDirectory>$(ProjectDir)..\</LocalDebuggerWorkingDirectory>
</PropertyGroup>
<ItemGroup>
<ClCompile Include="..\at_path.cpp" />
<ClCompile Include="..\conformance_burntsushi_invalid.cpp" />
<ClCompile Include="..\conformance_burntsushi_valid.cpp" />
<ClCompile Include="..\conformance_iarna_invalid.cpp" />

View File

@ -73,6 +73,7 @@
<LocalDebuggerWorkingDirectory>$(ProjectDir)..\</LocalDebuggerWorkingDirectory>
</PropertyGroup>
<ItemGroup>
<ClCompile Include="..\at_path.cpp" />
<ClCompile Include="..\conformance_burntsushi_invalid.cpp" />
<ClCompile Include="..\conformance_burntsushi_valid.cpp" />
<ClCompile Include="..\conformance_iarna_invalid.cpp" />

View File

@ -73,6 +73,7 @@
<LocalDebuggerWorkingDirectory>$(ProjectDir)..\</LocalDebuggerWorkingDirectory>
</PropertyGroup>
<ItemGroup>
<ClCompile Include="..\at_path.cpp" />
<ClCompile Include="..\conformance_burntsushi_invalid.cpp" />
<ClCompile Include="..\conformance_burntsushi_valid.cpp" />
<ClCompile Include="..\conformance_iarna_invalid.cpp" />

View File

@ -73,6 +73,7 @@
<LocalDebuggerWorkingDirectory>$(ProjectDir)..\</LocalDebuggerWorkingDirectory>
</PropertyGroup>
<ItemGroup>
<ClCompile Include="..\at_path.cpp" />
<ClCompile Include="..\conformance_burntsushi_invalid.cpp" />
<ClCompile Include="..\conformance_burntsushi_valid.cpp" />
<ClCompile Include="..\conformance_iarna_invalid.cpp" />

View File

@ -73,6 +73,7 @@
<LocalDebuggerWorkingDirectory>$(ProjectDir)..\</LocalDebuggerWorkingDirectory>
</PropertyGroup>
<ItemGroup>
<ClCompile Include="..\at_path.cpp" />
<ClCompile Include="..\conformance_burntsushi_invalid.cpp" />
<ClCompile Include="..\conformance_burntsushi_valid.cpp" />
<ClCompile Include="..\conformance_iarna_invalid.cpp" />

View File

@ -73,6 +73,7 @@
<LocalDebuggerWorkingDirectory>$(ProjectDir)..\</LocalDebuggerWorkingDirectory>
</PropertyGroup>
<ItemGroup>
<ClCompile Include="..\at_path.cpp" />
<ClCompile Include="..\conformance_burntsushi_invalid.cpp" />
<ClCompile Include="..\conformance_burntsushi_valid.cpp" />
<ClCompile Include="..\conformance_iarna_invalid.cpp" />

View File

@ -73,6 +73,7 @@
<LocalDebuggerWorkingDirectory>$(ProjectDir)..\</LocalDebuggerWorkingDirectory>
</PropertyGroup>
<ItemGroup>
<ClCompile Include="..\at_path.cpp" />
<ClCompile Include="..\conformance_burntsushi_invalid.cpp" />
<ClCompile Include="..\conformance_burntsushi_valid.cpp" />
<ClCompile Include="..\conformance_iarna_invalid.cpp" />

View File

@ -73,6 +73,7 @@
<LocalDebuggerWorkingDirectory>$(ProjectDir)..\</LocalDebuggerWorkingDirectory>
</PropertyGroup>
<ItemGroup>
<ClCompile Include="..\at_path.cpp" />
<ClCompile Include="..\conformance_burntsushi_invalid.cpp" />
<ClCompile Include="..\conformance_burntsushi_valid.cpp" />
<ClCompile Include="..\conformance_iarna_invalid.cpp" />

View File

@ -73,6 +73,7 @@
<LocalDebuggerWorkingDirectory>$(ProjectDir)..\</LocalDebuggerWorkingDirectory>
</PropertyGroup>
<ItemGroup>
<ClCompile Include="..\at_path.cpp" />
<ClCompile Include="..\conformance_burntsushi_invalid.cpp" />
<ClCompile Include="..\conformance_burntsushi_valid.cpp" />
<ClCompile Include="..\conformance_iarna_invalid.cpp" />

View File

@ -73,6 +73,7 @@
<LocalDebuggerWorkingDirectory>$(ProjectDir)..\</LocalDebuggerWorkingDirectory>
</PropertyGroup>
<ItemGroup>
<ClCompile Include="..\at_path.cpp" />
<ClCompile Include="..\conformance_burntsushi_invalid.cpp" />
<ClCompile Include="..\conformance_burntsushi_valid.cpp" />
<ClCompile Include="..\conformance_iarna_invalid.cpp" />

View File

@ -73,6 +73,7 @@
<LocalDebuggerWorkingDirectory>$(ProjectDir)..\</LocalDebuggerWorkingDirectory>
</PropertyGroup>
<ItemGroup>
<ClCompile Include="..\at_path.cpp" />
<ClCompile Include="..\conformance_burntsushi_invalid.cpp" />
<ClCompile Include="..\conformance_burntsushi_valid.cpp" />
<ClCompile Include="..\conformance_iarna_invalid.cpp" />

View File

@ -73,6 +73,7 @@
<LocalDebuggerWorkingDirectory>$(ProjectDir)..\</LocalDebuggerWorkingDirectory>
</PropertyGroup>
<ItemGroup>
<ClCompile Include="..\at_path.cpp" />
<ClCompile Include="..\conformance_burntsushi_invalid.cpp" />
<ClCompile Include="..\conformance_burntsushi_valid.cpp" />
<ClCompile Include="..\conformance_iarna_invalid.cpp" />

View File

@ -73,6 +73,7 @@
<LocalDebuggerWorkingDirectory>$(ProjectDir)..\</LocalDebuggerWorkingDirectory>
</PropertyGroup>
<ItemGroup>
<ClCompile Include="..\at_path.cpp" />
<ClCompile Include="..\conformance_burntsushi_invalid.cpp" />
<ClCompile Include="..\conformance_burntsushi_valid.cpp" />
<ClCompile Include="..\conformance_iarna_invalid.cpp" />

View File

@ -73,6 +73,7 @@
<LocalDebuggerWorkingDirectory>$(ProjectDir)..\</LocalDebuggerWorkingDirectory>
</PropertyGroup>
<ItemGroup>
<ClCompile Include="..\at_path.cpp" />
<ClCompile Include="..\conformance_burntsushi_invalid.cpp" />
<ClCompile Include="..\conformance_burntsushi_valid.cpp" />
<ClCompile Include="..\conformance_iarna_invalid.cpp" />

View File

@ -73,6 +73,7 @@
<LocalDebuggerWorkingDirectory>$(ProjectDir)..\</LocalDebuggerWorkingDirectory>
</PropertyGroup>
<ItemGroup>
<ClCompile Include="..\at_path.cpp" />
<ClCompile Include="..\conformance_burntsushi_invalid.cpp" />
<ClCompile Include="..\conformance_burntsushi_valid.cpp" />
<ClCompile Include="..\conformance_iarna_invalid.cpp" />

View File

@ -73,6 +73,7 @@
<LocalDebuggerWorkingDirectory>$(ProjectDir)..\</LocalDebuggerWorkingDirectory>
</PropertyGroup>
<ItemGroup>
<ClCompile Include="..\at_path.cpp" />
<ClCompile Include="..\conformance_burntsushi_invalid.cpp" />
<ClCompile Include="..\conformance_burntsushi_valid.cpp" />
<ClCompile Include="..\conformance_iarna_invalid.cpp" />

View File

@ -73,6 +73,7 @@
<LocalDebuggerWorkingDirectory>$(ProjectDir)..\</LocalDebuggerWorkingDirectory>
</PropertyGroup>
<ItemGroup>
<ClCompile Include="..\at_path.cpp" />
<ClCompile Include="..\conformance_burntsushi_invalid.cpp" />
<ClCompile Include="..\conformance_burntsushi_valid.cpp" />
<ClCompile Include="..\conformance_iarna_invalid.cpp" />

View File

@ -73,6 +73,7 @@
<LocalDebuggerWorkingDirectory>$(ProjectDir)..\</LocalDebuggerWorkingDirectory>
</PropertyGroup>
<ItemGroup>
<ClCompile Include="..\at_path.cpp" />
<ClCompile Include="..\conformance_burntsushi_invalid.cpp" />
<ClCompile Include="..\conformance_burntsushi_valid.cpp" />
<ClCompile Include="..\conformance_iarna_invalid.cpp" />

View File

@ -73,6 +73,7 @@
<LocalDebuggerWorkingDirectory>$(ProjectDir)..\</LocalDebuggerWorkingDirectory>
</PropertyGroup>
<ItemGroup>
<ClCompile Include="..\at_path.cpp" />
<ClCompile Include="..\conformance_burntsushi_invalid.cpp" />
<ClCompile Include="..\conformance_burntsushi_valid.cpp" />
<ClCompile Include="..\conformance_iarna_invalid.cpp" />

View File

@ -73,6 +73,7 @@
<LocalDebuggerWorkingDirectory>$(ProjectDir)..\</LocalDebuggerWorkingDirectory>
</PropertyGroup>
<ItemGroup>
<ClCompile Include="..\at_path.cpp" />
<ClCompile Include="..\conformance_burntsushi_invalid.cpp" />
<ClCompile Include="..\conformance_burntsushi_valid.cpp" />
<ClCompile Include="..\conformance_iarna_invalid.cpp" />

View File

@ -73,6 +73,7 @@
<LocalDebuggerWorkingDirectory>$(ProjectDir)..\</LocalDebuggerWorkingDirectory>
</PropertyGroup>
<ItemGroup>
<ClCompile Include="..\at_path.cpp" />
<ClCompile Include="..\conformance_burntsushi_invalid.cpp" />
<ClCompile Include="..\conformance_burntsushi_valid.cpp" />
<ClCompile Include="..\conformance_iarna_invalid.cpp" />

View File

@ -73,6 +73,7 @@
<LocalDebuggerWorkingDirectory>$(ProjectDir)..\</LocalDebuggerWorkingDirectory>
</PropertyGroup>
<ItemGroup>
<ClCompile Include="..\at_path.cpp" />
<ClCompile Include="..\conformance_burntsushi_invalid.cpp" />
<ClCompile Include="..\conformance_burntsushi_valid.cpp" />
<ClCompile Include="..\conformance_iarna_invalid.cpp" />

View File

@ -73,6 +73,7 @@
<LocalDebuggerWorkingDirectory>$(ProjectDir)..\</LocalDebuggerWorkingDirectory>
</PropertyGroup>
<ItemGroup>
<ClCompile Include="..\at_path.cpp" />
<ClCompile Include="..\conformance_burntsushi_invalid.cpp" />
<ClCompile Include="..\conformance_burntsushi_valid.cpp" />
<ClCompile Include="..\conformance_iarna_invalid.cpp" />

View File

@ -73,6 +73,7 @@
<LocalDebuggerWorkingDirectory>$(ProjectDir)..\</LocalDebuggerWorkingDirectory>
</PropertyGroup>
<ItemGroup>
<ClCompile Include="..\at_path.cpp" />
<ClCompile Include="..\conformance_burntsushi_invalid.cpp" />
<ClCompile Include="..\conformance_burntsushi_valid.cpp" />
<ClCompile Include="..\conformance_iarna_invalid.cpp" />

View File

@ -73,6 +73,7 @@
<LocalDebuggerWorkingDirectory>$(ProjectDir)..\</LocalDebuggerWorkingDirectory>
</PropertyGroup>
<ItemGroup>
<ClCompile Include="..\at_path.cpp" />
<ClCompile Include="..\conformance_burntsushi_invalid.cpp" />
<ClCompile Include="..\conformance_burntsushi_valid.cpp" />
<ClCompile Include="..\conformance_iarna_invalid.cpp" />

View File

@ -73,6 +73,7 @@
<LocalDebuggerWorkingDirectory>$(ProjectDir)..\</LocalDebuggerWorkingDirectory>
</PropertyGroup>
<ItemGroup>
<ClCompile Include="..\at_path.cpp" />
<ClCompile Include="..\conformance_burntsushi_invalid.cpp" />
<ClCompile Include="..\conformance_burntsushi_valid.cpp" />
<ClCompile Include="..\conformance_iarna_invalid.cpp" />

View File

@ -37,6 +37,7 @@
<ItemGroup>
<ClInclude Include="include\toml++\impl\array.h" />
<ClInclude Include="include\toml++\impl\array.inl" />
<ClInclude Include="include\toml++\impl\at_path.h" />
<ClInclude Include="include\toml++\impl\date_time.h" />
<ClInclude Include="include\toml++\impl\key.h" />
<ClInclude Include="include\toml++\impl\simd.h" />
@ -96,6 +97,7 @@
<None Include="CHANGELOG.md" />
<None Include="CODE_OF_CONDUCT.md" />
<None Include="CONTRIBUTING.md" />
<None Include="include\toml++\impl\at_path.inl" />
<None Include="include\toml++\impl\unicode.inl" />
<None Include="include\toml++\impl\node_view_extern.inl" />
<None Include="include\toml++\impl\yaml_formatter.inl" />

View File

@ -136,6 +136,9 @@
<ClInclude Include="include\toml++\impl\key.h">
<Filter>include\impl</Filter>
</ClInclude>
<ClInclude Include="include\toml++\impl\at_path.h">
<Filter>include\impl</Filter>
</ClInclude>
</ItemGroup>
<ItemGroup>
<None Include="toml++.props" />
@ -220,6 +223,9 @@
<None Include=".github\ISSUE_TEMPLATE\spec_bug_report.md">
<Filter>.github</Filter>
</None>
<None Include="include\toml++\impl\at_path.inl">
<Filter>include\impl</Filter>
</None>
</ItemGroup>
<ItemGroup>
<Filter Include=".circleci">

View File

@ -2,19 +2,20 @@
> &#xFE0F; _The contents of this directory relate to testing with the [toml-test] suite, **not** anything to do with running the library's unit-tests.
> For information about running the unit-tests see [CONTRIBUTING](../CONTRIBUTING.md)._
>
> &#xFE0F; _All command snippets in this document assume the working directory is the toml++ repository root._
<br>
## Prequisites
For this document to make sense, you will need to:
1. Install some recent version of Go
2. Follow the installation instructions from the [toml-test] README to compile the `toml-test` runner
3. Add `toml-test` as an alias or have it on the system PATH
4. Clone the toml++ repository's [nlohmann/json] submodule:
1. Follow the installation instructions from the [toml-test] README to compile the `toml-test` runner
2. Add `toml-test` as an alias or have it on the system PATH
3. Clone the toml++ repository's [nlohmann/json] submodule:
```bash
git submodule update --init --depth 1 external/json
```
5. **Linux only:** Install `ninja` and `meson`:
4. **Linux only:** Install `ninja` and `meson`:
```bash
sudo apt update && sudo apt install -y locales python3 python3-pip ninja-build
sudo pip3 install meson
@ -23,7 +24,6 @@ sudo pip3 install meson
<br>
## Building and Testing the Encoder and Decoder
> &#xFE0F; _Commands in this section assume the starting directory is the toml++ repository root directory._
### Windows with Visual Studio
Open `toml++.sln` and build the two projects in the `toml-test` solution folder. They'll be compiled in some target-specific subfolder under `/bin` in the repo root. Then run `toml-test` against them:

619
toml.hpp
View File

@ -377,7 +377,6 @@
#elif TOML_CPP_VERSION < 201703L
#error toml++ requires C++17 or higher. For a TOML library supporting C++11 see https://github.com/ToruNiina/toml11
#endif
#undef TOML_CPP_VERSION
#ifdef TOML_CONFIG_HEADER
#include TOML_CONFIG_HEADER
@ -631,19 +630,35 @@
#define TOML_HAS_ATTR(...) 0
#endif
#if !defined(TOML_LIKELY) && TOML_HAS_ATTR(likely) >= 201803
#define TOML_LIKELY(...) (__VA_ARGS__) [[likely]]
#if TOML_HAS_ATTR(likely) >= 201803
#ifndef TOML_LIKELY
#define TOML_LIKELY(...) (__VA_ARGS__) [[likely]]
#endif
#ifndef TOML_LIKELY_CASE
#define TOML_LIKELY_CASE [[likely]]
#endif
#endif
#ifndef TOML_LIKELY
#define TOML_LIKELY(...) (__VA_ARGS__)
#endif
#ifndef TOML_LIKELY_CASE
#define TOML_LIKELY_CASE
#endif
#if !defined(TOML_UNLIKELY) && TOML_HAS_ATTR(unlikely) >= 201803
#define TOML_UNLIKELY(...) (__VA_ARGS__) [[unlikely]]
#if TOML_HAS_ATTR(unlikely) >= 201803
#ifndef TOML_UNLIKELY
#define TOML_UNLIKELY(...) (__VA_ARGS__) [[unlikely]]
#endif
#ifndef TOML_UNLIKELY_CASE
#define TOML_UNLIKELY_CASE [[unlikely]]
#endif
#endif
#ifndef TOML_UNLIKELY
#define TOML_UNLIKELY(...) (__VA_ARGS__)
#endif
#ifndef TOML_UNLIKELY_CASE
#define TOML_UNLIKELY_CASE
#endif
#if TOML_HAS_ATTR(nodiscard)
#define TOML_NODISCARD [[nodiscard]]
@ -942,13 +957,18 @@ TOML_PUSH_WARNINGS;
TOML_DISABLE_SPAM_WARNINGS;
TOML_DISABLE_SWITCH_WARNINGS;
TOML_DISABLE_SUGGEST_ATTR_WARNINGS;
// misc warning false-positives
#if TOML_MSVC
#pragma warning(disable : 5031) // #pragma warning(pop): likely mismatch (false-positive)
#elif TOML_CLANG && !TOML_HEADER_ONLY && TOML_IMPLEMENTATION
#pragma clang diagnostic ignored "-Wheader-hygiene" // false-positive
#pragma warning(disable : 5031) // #pragma warning(pop): likely mismatch
#elif TOML_CLANG
#pragma clang diagnostic ignored "-Wheader-hygiene"
#if TOML_CLANG >= 12
#pragma clang diagnostic ignored "-Wc++20-extensions"
#endif
#if TOML_CLANG == 13
#pragma clang diagnostic ignored "-Wreserved-identifier" // false-positive
#pragma clang diagnostic ignored "-Wreserved-identifier"
#endif
#endif
//******** impl/std_new.h ********************************************************************************************
@ -1956,7 +1976,7 @@ TOML_IMPL_NAMESPACE_START
template <typename T>
TOML_PURE_GETTER
inline const T& min(const T& a, const T& b) noexcept //
constexpr const T& min(const T& a, const T& b) noexcept //
{
return a < b ? a : b;
}
@ -2524,6 +2544,32 @@ TOML_NAMESPACE_END;
#endif
TOML_POP_WARNINGS;
//******** impl/at_path.h ********************************************************************************************
TOML_NAMESPACE_START
{
TOML_NODISCARD
TOML_API
node_view<node> at_path(node & root, std::string_view path) noexcept;
TOML_NODISCARD
TOML_API
node_view<const node> at_path(const node& root, std::string_view path) noexcept;
#if TOML_ENABLE_WINDOWS_COMPAT
TOML_NODISCARD
TOML_API
node_view<node> at_path(node & root, std::wstring_view path);
TOML_NODISCARD
TOML_API
node_view<const node> at_path(const node& root, std::wstring_view path);
#endif
}
TOML_NAMESPACE_END;
//******** impl/node.h ***********************************************************************************************
TOML_DISABLE_WARNINGS;
@ -3086,10 +3132,38 @@ TOML_NAMESPACE_START
TOML_NODISCARD
explicit operator node_view<const node>() const noexcept;
TOML_NODISCARD
TOML_API
node_view<node> at_path(std::string_view path) noexcept;
TOML_NODISCARD
TOML_API
node_view<const node> at_path(std::string_view path) const noexcept;
#if TOML_ENABLE_WINDOWS_COMPAT
TOML_NODISCARD
TOML_API
node_view<node> at_path(std::wstring_view path);
TOML_NODISCARD
TOML_API
node_view<const node> at_path(std::wstring_view path) const;
#endif // TOML_ENABLE_WINDOWS_COMPAT
};
}
TOML_NAMESPACE_END;
TOML_IMPL_NAMESPACE_START
{
TOML_PURE_GETTER
TOML_API
bool node_deep_equality(const node*, const node*) noexcept;
}
TOML_IMPL_NAMESPACE_END;
#ifdef _MSC_VER
#pragma pop_macro("min")
#pragma pop_macro("max")
@ -3127,7 +3201,7 @@ TOML_NAMESPACE_START
private:
template <typename T>
friend class TOML_NAMESPACE::node_view;
friend class node_view;
mutable viewed_type* node_ = nullptr;
@ -3417,6 +3491,22 @@ TOML_NAMESPACE_START
return return_type{};
}
public:
template <typename T>
TOML_PURE_GETTER
friend bool operator==(const node_view& lhs, const node_view<T>& rhs) noexcept
{
return impl::node_deep_equality(lhs.node_, rhs.node_);
}
template <typename T>
TOML_PURE_GETTER
friend bool operator!=(const node_view& lhs, const node_view<T>& rhs) noexcept
{
return !impl::node_deep_equality(lhs.node_, rhs.node_);
}
TOML_NODISCARD
friend bool operator==(const node_view& lhs, const table& rhs) noexcept
{
@ -3499,7 +3589,13 @@ TOML_NAMESPACE_START
{
if (auto tbl = this->as_table())
return node_view{ tbl->get(key) };
return node_view{ nullptr };
return {};
}
TOML_NODISCARD
node_view at_path(std::string_view path) const noexcept
{
return node_ ? node_->at_path(path) : node_view{};
}
#if TOML_ENABLE_WINDOWS_COMPAT
@ -3509,7 +3605,13 @@ TOML_NAMESPACE_START
{
if (auto tbl = this->as_table())
return node_view{ tbl->get(key) };
return node_view{ nullptr };
return {};
}
TOML_NODISCARD
node_view at_path(std::wstring_view path) const
{
return node_ ? node_->at_path(path) : node_view{};
}
#endif // TOML_ENABLE_WINDOWS_COMPAT
@ -3519,7 +3621,7 @@ TOML_NAMESPACE_START
{
if (auto arr = this->as_array())
return node_view{ arr->get(index) };
return node_view{ nullptr };
return {};
}
#if TOML_ENABLE_FORMATTERS
@ -3618,12 +3720,12 @@ TOML_NAMESPACE_START
{
inline node::operator node_view<node>() noexcept
{
return node_view<node>(this);
return node_view<node>{ this };
}
inline node::operator node_view<const node>() const noexcept
{
return node_view<const node>(this);
return node_view<const node>{ this };
}
}
TOML_NAMESPACE_END;
@ -6627,38 +6729,38 @@ TOML_NAMESPACE_START
#endif // TOML_ENABLE_WINDOWS_COMPAT
template <typename ValueType>
template <typename T>
TOML_PURE_GETTER
impl::wrap_node<ValueType>* get_as(std::string_view key) noexcept
impl::wrap_node<T>* get_as(std::string_view key) noexcept
{
const auto n = this->get(key);
return n ? n->template as<ValueType>() : nullptr;
return n ? n->template as<T>() : nullptr;
}
template <typename ValueType>
template <typename T>
TOML_PURE_GETTER
const impl::wrap_node<ValueType>* get_as(std::string_view key) const noexcept
const impl::wrap_node<T>* get_as(std::string_view key) const noexcept
{
return const_cast<table&>(*this).template get_as<ValueType>(key);
return const_cast<table&>(*this).template get_as<T>(key);
}
#if TOML_ENABLE_WINDOWS_COMPAT
template <typename ValueType>
template <typename T>
TOML_NODISCARD
impl::wrap_node<ValueType>* get_as(std::wstring_view key)
impl::wrap_node<T>* get_as(std::wstring_view key)
{
if (empty())
return nullptr;
return get_as<ValueType>(impl::narrow(key));
return get_as<T>(impl::narrow(key));
}
template <typename ValueType>
template <typename T>
TOML_NODISCARD
const impl::wrap_node<ValueType>* get_as(std::wstring_view key) const
const impl::wrap_node<T>* get_as(std::wstring_view key) const
{
return const_cast<table&>(*this).template get_as<ValueType>(key);
return const_cast<table&>(*this).template get_as<T>(key);
}
#endif // TOML_ENABLE_WINDOWS_COMPAT
@ -9719,9 +9821,64 @@ TOML_NAMESPACE_START
TOML_EXTERNAL_LINKAGE
node::~node() noexcept = default;
TOML_EXTERNAL_LINKAGE
node_view<node> node::at_path(std::string_view path) noexcept
{
return toml::at_path(*this, path);
}
TOML_EXTERNAL_LINKAGE
node_view<const node> node::at_path(std::string_view path) const noexcept
{
return toml::at_path(*this, path);
}
#if TOML_ENABLE_WINDOWS_COMPAT
TOML_EXTERNAL_LINKAGE
node_view<node> node::at_path(std::wstring_view path)
{
return toml::at_path(*this, path);
}
TOML_EXTERNAL_LINKAGE
node_view<const node> node::at_path(std::wstring_view path) const
{
return toml::at_path(*this, path);
}
#endif // TOML_ENABLE_WINDOWS_COMPAT
}
TOML_NAMESPACE_END;
TOML_IMPL_NAMESPACE_START
{
TOML_EXTERNAL_LINKAGE
bool node_deep_equality(const node* lhs, const node* rhs) noexcept
{
// both same or both null
if (lhs == rhs)
return true;
// lhs null != rhs null or different types
if ((!lhs != !rhs) || lhs->type() != rhs->type())
return false;
bool same;
lhs->visit(
[=, &same](auto& l) noexcept
{
using concrete_type = remove_cvref<decltype(l)>;
same = (l == *(rhs->as<concrete_type>()));
});
return same;
}
}
TOML_IMPL_NAMESPACE_END;
#ifdef _MSC_VER
#pragma pop_macro("min")
#pragma pop_macro("max")
@ -9810,6 +9967,231 @@ TOML_NAMESPACE_END;
#endif
TOML_POP_WARNINGS;
//******** impl/at_path.inl ******************************************************************************************
TOML_DISABLE_WARNINGS;
#if TOML_INT_CHARCONV
#include <charconv>
#else
#include <sstream>
#endif
TOML_ENABLE_WARNINGS;
TOML_PUSH_WARNINGS;
#ifdef _MSC_VER
#pragma push_macro("min")
#pragma push_macro("max")
#undef min
#undef max
#endif
TOML_ANON_NAMESPACE_START
{
TOML_INTERNAL_LINKAGE
node* get_at_path(node & root, std::string_view path) noexcept
{
if (root.is_value()) // values don't have child nodes
return nullptr;
size_t pos = 0;
const auto end = path.length();
node* current = &root;
bool prev_was_array_indexer = false;
bool prev_was_dot = root.is_table(); // implicit '.' at the start for tables
do
{
// start of an array indexer
if (path[pos] == '[')
{
const auto current_array = current->as<array>();
if (!current_array)
return nullptr;
// get array index substring
const auto index_start = pos + 1u; // first position after '['
const auto index_end = path.find(']', index_start); // position of ']'
if (index_end == std::string_view::npos || index_end == index_start)
return nullptr;
auto index_str = path.substr(index_start, index_end - index_start);
// trim whitespace from either side of the index
const auto first_non_ws = index_str.find_first_not_of(" \t"sv);
const auto last_non_ws = index_str.find_last_not_of(" \t"sv);
if (first_non_ws == std::string_view::npos)
return nullptr;
TOML_ASSERT_ASSUME(last_non_ws != std::string_view::npos);
index_str = index_str.substr(first_non_ws, (last_non_ws - first_non_ws) + 1u);
// parse the actual array index
size_t index;
if (index_str.length() == 1u && index_str[0] >= '0' && index_str[0] <= '9')
index = static_cast<size_t>(index_str[0] - '0');
else
{
#if TOML_INT_CHARCONV
auto fc_result = std::from_chars(index_str.data(), index_str.data() + index_str.length(), index);
if (fc_result.ec != std::errc{})
return nullptr;
#else
std::stringstream ss;
ss.imbue(std::locale::classic());
ss.write(index_str.data(), static_cast<std::streamsize>(index_str.length()));
if (!(ss >> index))
return nullptr;
#endif
}
current = current_array->get(index);
pos = index_end + 1u;
prev_was_dot = false;
prev_was_array_indexer = true;
}
// start of a new table child
else if (path[pos] == '.')
{
const auto current_table = current->as<table>();
if (!current_table)
return nullptr;
// a dot immediately following another dot (or at the beginning of the string) is as if we'd asked
// for an empty child in between, e.g.
//
// foo..bar
//
// is equivalent to
//
// "foo".""."bar"
//
if (prev_was_dot)
current = current_table->get(""sv);
pos++;
prev_was_dot = true;
prev_was_array_indexer = false;
}
// some regular subkey
else
{
// get subkey text
const auto subkey_start = pos;
const auto subkey_len =
impl::min(path.find_first_of(".["sv, subkey_start + 1u), path.length()) - subkey_start;
const auto subkey = path.substr(subkey_start, subkey_len);
// a regular subkey segment immediately after an array indexer is OK if it was all whitespace, e.g.:
//
// "foo[0] .bar"
// ^^ skip this
//
// otherwise its an error (since it would have to be preceeded by a dot)
if (prev_was_array_indexer)
{
auto non_ws = subkey.find_first_not_of(" \t");
if (non_ws == std::string_view::npos)
{
pos += subkey_len;
prev_was_dot = false;
prev_was_array_indexer = false;
continue;
}
else
return nullptr;
}
const auto current_table = current->as<table>();
if (!current_table)
return nullptr;
current = current_table->get(subkey);
pos += subkey_len;
prev_was_dot = false;
prev_was_array_indexer = false;
}
}
while (pos < end && current);
// a dot at the end is as if we'd asked for an empty child at the end, e.g.
//
// foo.bar.
//
// is equivalent to
//
// "foo"."bar".""
//
if (current && prev_was_dot)
{
const auto current_table = current->as<table>();
if (!current_table)
return nullptr;
current = current_table->get(""sv);
}
return current;
}
#if TOML_ENABLE_WINDOWS_COMPAT
TOML_INTERNAL_LINKAGE
node* get_at_path(node & root, std::wstring_view path) noexcept
{
if (auto tbl = root.as_table(); tbl && tbl->empty())
return {};
if (auto arr = root.as_array(); arr && arr->empty())
return {};
return get_at_path(root, impl::narrow(path));
}
#endif // TOML_ENABLE_WINDOWS_COMPAT
}
TOML_ANON_NAMESPACE_END;
TOML_NAMESPACE_START
{
TOML_EXTERNAL_LINKAGE
node_view<node> at_path(node & root, std::string_view path) noexcept
{
return node_view<node>{ TOML_ANON_NAMESPACE::get_at_path(root, path) };
}
TOML_EXTERNAL_LINKAGE
node_view<const node> at_path(const node& root, std::string_view path) noexcept
{
return node_view<const node>{ TOML_ANON_NAMESPACE::get_at_path(const_cast<node&>(root), path) };
}
#if TOML_ENABLE_WINDOWS_COMPAT
TOML_EXTERNAL_LINKAGE
node_view<node> at_path(node & root, std::wstring_view path)
{
return node_view<node>{ TOML_ANON_NAMESPACE::get_at_path(root, path) };
}
TOML_EXTERNAL_LINKAGE
node_view<const node> at_path(const node& root, std::wstring_view path)
{
return node_view<const node>{ TOML_ANON_NAMESPACE::get_at_path(const_cast<node&>(root), path) };
}
#endif // TOML_ENABLE_WINDOWS_COMPAT
}
TOML_NAMESPACE_END;
#ifdef _MSC_VER
#pragma pop_macro("min")
#pragma pop_macro("max")
#endif
TOML_POP_WARNINGS;
//******** impl/value.inl ********************************************************************************************
TOML_PUSH_WARNINGS;
@ -11115,7 +11497,7 @@ TOML_ANON_NAMESPACE_START
class TOML_EMPTY_BASES utf8_buffered_reader
{
public:
static constexpr size_t max_history_length = 72;
static constexpr size_t max_history_length = 128;
private:
static constexpr size_t history_buffer_size = max_history_length - 1; //'head' is stored in the reader
@ -11235,40 +11617,40 @@ TOML_ANON_NAMESPACE_START
template <>
struct parse_integer_traits<2>
{
static constexpr auto scope_qualifier = "binary integer"sv;
static constexpr auto is_digit = impl::is_binary_digit;
static constexpr auto is_signed = false;
static constexpr auto buffer_length = 63;
static constexpr auto prefix_codepoint = U'b';
static constexpr auto prefix = "b"sv;
static constexpr auto scope_qualifier = "binary integer"sv;
static constexpr auto is_digit = impl::is_binary_digit;
static constexpr auto is_signed = false;
static constexpr auto min_buffer_length = 63;
static constexpr auto prefix_codepoint = U'b';
static constexpr auto prefix = "b"sv;
};
template <>
struct parse_integer_traits<8>
{
static constexpr auto scope_qualifier = "octal integer"sv;
static constexpr auto is_digit = impl::is_octal_digit;
static constexpr auto is_signed = false;
static constexpr auto buffer_length = 21; // strlen("777777777777777777777")
static constexpr auto prefix_codepoint = U'o';
static constexpr auto prefix = "o"sv;
static constexpr auto scope_qualifier = "octal integer"sv;
static constexpr auto is_digit = impl::is_octal_digit;
static constexpr auto is_signed = false;
static constexpr auto min_buffer_length = 21; // strlen("777777777777777777777")
static constexpr auto prefix_codepoint = U'o';
static constexpr auto prefix = "o"sv;
};
template <>
struct parse_integer_traits<10>
{
static constexpr auto scope_qualifier = "decimal integer"sv;
static constexpr auto is_digit = impl::is_decimal_digit;
static constexpr auto is_signed = true;
static constexpr auto buffer_length = 19; // strlen("9223372036854775807")
static constexpr auto scope_qualifier = "decimal integer"sv;
static constexpr auto is_digit = impl::is_decimal_digit;
static constexpr auto is_signed = true;
static constexpr auto min_buffer_length = 19; // strlen("9223372036854775807")
};
template <>
struct parse_integer_traits<16>
{
static constexpr auto scope_qualifier = "hexadecimal integer"sv;
static constexpr auto is_digit = impl::is_hexadecimal_digit;
static constexpr auto is_signed = false;
static constexpr auto buffer_length = 16; // strlen("7FFFFFFFFFFFFFFF")
static constexpr auto prefix_codepoint = U'x';
static constexpr auto prefix = "x"sv;
static constexpr auto scope_qualifier = "hexadecimal integer"sv;
static constexpr auto is_digit = impl::is_hexadecimal_digit;
static constexpr auto is_signed = false;
static constexpr auto min_buffer_length = 16; // strlen("7FFFFFFFFFFFFFFF")
static constexpr auto prefix_codepoint = U'x';
static constexpr auto prefix = "x"sv;
};
TOML_PURE_GETTER
@ -11773,11 +12155,11 @@ TOML_IMPL_NAMESPACE_START
{
advance_and_return_if_error({}); // skip \r
if (is_eof())
set_error_and_return_default("expected \\n, saw EOF"sv);
if TOML_UNLIKELY(is_eof())
set_error_and_return_default("expected \\n after \\r, saw EOF"sv);
if (*cp != U'\n')
set_error_and_return_default("expected \\n, saw '"sv, to_sv(*cp), "'"sv);
if TOML_UNLIKELY(*cp != U'\n')
set_error_and_return_default("expected \\n after \\r, saw '"sv, to_sv(*cp), "'"sv);
}
else if (*cp != U'\n')
return false;
@ -11820,18 +12202,19 @@ TOML_IMPL_NAMESPACE_START
return true;
return_if_error({});
if constexpr (TOML_LANG_AT_LEAST(1, 0, 0))
{
// toml/issues/567 (disallow non-TAB control characters in comments)
if (is_nontab_control_character(*cp))
set_error_and_return_default(
"control characters other than TAB (U+0009) are explicitly prohibited in comments"sv);
#if TOML_LANG_AT_LEAST(1, 0, 0)
// toml/issues/567 (disallow non-TAB control characters in comments)
if TOML_UNLIKELY(is_nontab_control_character(*cp))
set_error_and_return_default(
"control characters other than TAB (U+0009) are explicitly prohibited in comments"sv);
// toml/pull/720 (disallow surrogates in comments)
else if TOML_UNLIKELY(is_unicode_surrogate(*cp))
set_error_and_return_default(
"unicode surrogates (U+D800 to U+DFFF) are explicitly prohibited in comments"sv);
#endif
// toml/pull/720 (disallow surrogates in comments)
else if (is_unicode_surrogate(*cp))
set_error_and_return_default(
"unicode surrogates (U+D800 to U+DFFF) are explicitly prohibited in comments"sv);
}
advance_and_return_if_error({});
}
@ -11928,9 +12311,11 @@ TOML_IMPL_NAMESPACE_START
if (multi_line && is_whitespace(*cp))
{
consume_leading_whitespace();
if (!consume_line_break())
if TOML_UNLIKELY(!consume_line_break())
set_error_and_return_default(
"line-ending backslashes must be the last non-whitespace character on the line"sv);
skipping_whitespace = true;
return_if_error({});
continue;
@ -11970,17 +12355,19 @@ TOML_IMPL_NAMESPACE_START
while (place_value)
{
set_error_and_return_if_eof({});
if (!is_hexadecimal_digit(*cp))
if TOML_UNLIKELY(!is_hexadecimal_digit(*cp))
set_error_and_return_default("expected hex digit, saw '"sv, to_sv(*cp), "'"sv);
sequence_value += place_value * hex_to_dec(*cp);
place_value /= 16u;
advance_and_return_if_error({});
}
if (is_unicode_surrogate(sequence_value))
if TOML_UNLIKELY(is_unicode_surrogate(sequence_value))
set_error_and_return_default(
"unicode surrogates (U+D800 - U+DFFF) are explicitly prohibited"sv);
else if (sequence_value > 0x10FFFFu)
else if TOML_UNLIKELY(sequence_value > 0x10FFFFu)
set_error_and_return_default("values greater than U+10FFFF are invalid"sv);
if (sequence_value < 0x80)
@ -12008,7 +12395,8 @@ TOML_IMPL_NAMESPACE_START
break;
}
// ???
// ???
TOML_UNLIKELY_CASE
default: set_error_and_return_default("unknown escape sequence '\\"sv, to_sv(*cp), "'"sv);
}
@ -12091,17 +12479,17 @@ TOML_IMPL_NAMESPACE_START
}
// handle control characters
if (is_nontab_control_character(*cp))
if TOML_UNLIKELY(is_nontab_control_character(*cp))
set_error_and_return_default(
"unescaped control characters other than TAB (U+0009) are explicitly prohibited"sv);
// handle surrogates in strings (1.0.0 and later)
if constexpr (TOML_LANG_AT_LEAST(1, 0, 0))
{
if (is_unicode_surrogate(*cp))
set_error_and_return_default(
"unescaped unicode surrogates (U+D800 to U+DFFF) are explicitly prohibited"sv);
}
#if TOML_LANG_AT_LEAST(1, 0, 0)
// handle surrogates in strings
if TOML_UNLIKELY(is_unicode_surrogate(*cp))
set_error_and_return_default(
"unescaped unicode surrogates (U+D800 to U+DFFF) are explicitly prohibited"sv);
#endif
if (multi_line)
{
@ -12206,17 +12594,16 @@ TOML_IMPL_NAMESPACE_START
}
// handle control characters
if (is_nontab_control_character(*cp))
if TOML_UNLIKELY(is_nontab_control_character(*cp))
set_error_and_return_default(
"control characters other than TAB (U+0009) are explicitly prohibited"sv);
// handle surrogates in strings (1.0.0 and later)
if constexpr (TOML_LANG_AT_LEAST(1, 0, 0))
{
if (is_unicode_surrogate(*cp))
set_error_and_return_default(
"unicode surrogates (U+D800 - U+DFFF) are explicitly prohibited"sv);
}
#if TOML_LANG_AT_LEAST(1, 0, 0)
// handle surrogates in strings
if TOML_UNLIKELY(is_unicode_surrogate(*cp))
set_error_and_return_default("unicode surrogates (U+D800 - U+DFFF) are explicitly prohibited"sv);
#endif
str.append(cp->bytes, cp->count);
advance_and_return_if_error({});
@ -12362,13 +12749,19 @@ TOML_IMPL_NAMESPACE_START
advance_and_return_if_error_or_eof({});
// consume value chars
char chars[64];
char chars[utf8_buffered_reader::max_history_length];
size_t length = {};
const utf8_codepoint* prev = {};
bool seen_decimal = false, seen_exponent = false;
char first_integer_part = '\0';
while (!is_eof() && !is_value_terminator(*cp))
{
if TOML_UNLIKELY(length == sizeof(chars))
set_error_and_return_default("exceeds maximum length of "sv,
static_cast<uint64_t>(sizeof(chars)),
" characters"sv,
(seen_exponent ? ""sv : " (consider using exponent notation)"sv));
if (*cp == U'_')
{
if (!prev || !is_decimal_digit(*prev))
@ -12435,11 +12828,6 @@ TOML_IMPL_NAMESPACE_START
else
set_error_and_return_default("expected decimal digit, saw '"sv, to_sv(*cp), "'"sv);
if (length == sizeof(chars))
set_error_and_return_default("exceeds maximum length of "sv,
static_cast<uint64_t>(sizeof(chars)),
" characters"sv);
chars[length++] = static_cast<char>(cp->bytes[0]);
prev = cp;
advance_and_return_if_error({});
@ -12467,6 +12855,7 @@ TOML_IMPL_NAMESPACE_START
auto fc_result = std::from_chars(chars, chars + length, result);
switch (fc_result.ec)
{
TOML_LIKELY_CASE
case std::errc{}: // ok
return result * sign;
@ -12719,7 +13108,9 @@ TOML_IMPL_NAMESPACE_START
}
// consume value chars
char chars[traits::buffer_length];
static constexpr size_t underscore_padding = 32;
static constexpr size_t buffer_length = traits::min_buffer_length + underscore_padding;
char chars[buffer_length];
size_t length = {};
const utf8_codepoint* prev = {};
while (!is_eof() && !is_value_terminator(*cp))
@ -13224,7 +13615,7 @@ TOML_IMPL_NAMESPACE_START
advance_count++;
eof_while_scanning = is_eof();
}
while (advance_count < utf8_buffered_reader::max_history_length && !is_eof()
while (advance_count < (utf8_buffered_reader::max_history_length - 1u) && !is_eof()
&& !is_value_terminator(*cp));
};
scan();
@ -13273,8 +13664,6 @@ TOML_IMPL_NAMESPACE_START
// set the reader back to where we started
go_back(advance_count);
if (char_count < utf8_buffered_reader::max_history_length - 1u)
chars[char_count] = U'\0';
// if after scanning ahead we still only have one value character,
// the only valid value type is an integer.
@ -13380,7 +13769,23 @@ TOML_IMPL_NAMESPACE_START
case bzero_msk: [[fallthrough]];
case bdigit_msk: [[fallthrough]];
case begins_sign | has_digits | has_minus: [[fallthrough]];
case begins_sign | has_digits | has_plus: val.reset(new value{ parse_integer<10>() }); break;
case begins_sign | has_digits | has_plus:
{
// if the value was so long we exhausted the history buffer it's reasonable to assume
// there was more and the value's actual type is impossible to identify without making the
// buffer bigger (since it could have actually been a float), so emit an error.
//
// (this will likely only come up during fuzzing and similar scenarios)
static constexpr size_t max_numeric_value_length =
utf8_buffered_reader::max_history_length - 2u;
if TOML_UNLIKELY(!eof_while_scanning && advance_count > max_numeric_value_length)
set_error_and_return_default("numeric value too long to identify type - cannot exceed "sv,
max_numeric_value_length,
" characters"sv);
val.reset(new value{ parse_integer<10>() });
break;
}
// hexadecimal integers
// 0x10
@ -13538,17 +13943,6 @@ TOML_IMPL_NAMESPACE_START
return_after_error({});
}
#if !TOML_LANG_AT_LEAST(1, 0, 0) // toml/issues/665 (heterogeneous arrays)
{
if (auto arr = val->as_array(); arr && !arr->is_homogeneous())
{
delete arr;
set_error_at(begin_pos, "arrays cannot contain values of different types before TOML 1.0.0"sv);
return_after_error({});
}
}
#endif
val->source_ = { begin_pos, current_position(1), reader.source_path() };
return val;
}
@ -14556,7 +14950,7 @@ TOML_IMPL_NAMESPACE_START
case '\'': traits |= formatted_string_traits::single_quotes; break;
default:
{
if (is_control_character(c))
if TOML_UNLIKELY(is_control_character(c))
traits |= formatted_string_traits::control_chars;
if (!is_ascii_bare_key_character(static_cast<char32_t>(c)))
@ -14607,7 +15001,7 @@ TOML_IMPL_NAMESPACE_START
case U'\'': traits |= formatted_string_traits::single_quotes; break;
default:
{
if (is_control_character(decoder.codepoint)
if TOML_UNLIKELY(is_control_character(decoder.codepoint)
|| is_non_ascii_vertical_whitespace(decoder.codepoint))
traits |= formatted_string_traits::control_chars;
@ -15625,6 +16019,7 @@ TOML_POP_WARNINGS;
#undef TOML_CONST_GETTER
#undef TOML_CONST_INLINE_GETTER
#undef TOML_CONSTRAINED_TEMPLATE
#undef TOML_CPP_VERSION
#undef TOML_DELETE_DEFAULTS
#undef TOML_DISABLE_ARITHMETIC_WARNINGS
#undef TOML_DISABLE_CODE_ANALYSIS_WARNINGS
@ -15671,6 +16066,7 @@ TOML_POP_WARNINGS;
#undef TOML_LAUNDER
#undef TOML_LIFETIME_HOOKS
#undef TOML_LIKELY
#undef TOML_LIKELY_CASE
#undef TOML_MAKE_FLAGS
#undef TOML_MAKE_FLAGS_
#undef TOML_MAKE_VERSION
@ -15705,6 +16101,7 @@ TOML_POP_WARNINGS;
#undef TOML_TRIVIAL_ABI
#undef TOML_UINT128
#undef TOML_UNLIKELY
#undef TOML_UNLIKELY_CASE
#undef TOML_UNREACHABLE
#undef TOML_UNUSED
#endif

View File

@ -82,10 +82,14 @@ def make_string_literal(val, escape_all = False, escape_any = False):
buf.write(r'\"')
elif c_ord == 0x5C: # \
buf.write(r'\\')
elif c_ord == 0x0D: # \r
buf.write(r'\r')
elif c_ord == 0x0A: # \n
buf.write('\\n"\n\t\t"')
elif c_ord == 0x0B: # \v
buf.write(r'\v')
elif c_ord == 0x0C: # \f
buf.write(r'\f')
elif c_ord == 0x0D: # \r
buf.write(r'\r')
elif is_problematic_control_char(c_ord):
if c_ord <= 0xFF:
buf.write(rf'\x{c_ord:02X}')
@ -105,10 +109,10 @@ def make_string_literal(val, escape_all = False, escape_any = False):
def python_value_to_tomlpp(val):
if isinstance(val, str):
if re.fullmatch(r'^[+-]?[0-9]+[eE][+-]?[0-9]+$', val, re.M):
return str(float(val))
elif not val:
if not val:
return r'""sv'
elif re.fullmatch(r'^[+-]?[0-9]+[eE][+-]?[0-9]+$', val, re.M):
return str(float(val))
else:
return rf'{make_string_literal(val, escape_any = has_problematic_control_chars(val))}sv'
elif isinstance(val, bool):
@ -399,6 +403,8 @@ def load_tests(source_folder, is_valid_set, ignore_list = None):
for f,n in files:
ignored = False
for ignore in ignore_list:
if ignore is None:
continue
if isinstance(ignore, str):
if n == ignore:
ignored = True
@ -411,10 +417,7 @@ def load_tests(source_folder, is_valid_set, ignore_list = None):
files = files_
tests = []
for f,n in files:
#try:
tests.append(TomlTest(f, n, is_valid_set))
#except Exception as e:
#print(rf'Error reading {f}, skipping...', file=sys.stderr)
tests.append(TomlTest(f, n, is_valid_set))
return tests

View File

@ -116,6 +116,7 @@ def main():
<LocalDebuggerWorkingDirectory>$(ProjectDir)..\</LocalDebuggerWorkingDirectory>
</PropertyGroup>
<ItemGroup>
<ClCompile Include="..\at_path.cpp" />
<ClCompile Include="..\conformance_burntsushi_invalid.cpp" />
<ClCompile Include="..\conformance_burntsushi_valid.cpp" />
<ClCompile Include="..\conformance_iarna_invalid.cpp" />