fixed is_unicode_XXXXXX functions being wrong in some cases

also:
- added tests for unicode functions
- changed `TOML_LIKELY` semantics to work with gcc-style intrinsics
- greatly improved unicode-related codegen
- parser refactoring
This commit is contained in:
Mark Gillard 2020-04-18 16:14:07 +03:00
parent 0c2279d15a
commit 5e683e9a73
45 changed files with 5126 additions and 2297 deletions

View File

@ -195,7 +195,7 @@
/// return 0;
/// }
/// \ecpp
///
///
/// Instances of toml::parse_error can be printed directly to streams:
/// \cpp
/// try
@ -217,7 +217,16 @@
///
/// If the default error formatting is not be suitable for your use-case you can access the error's
/// toml::source_region and description directly from the error object (as in the examples above).
///
///
/// \m_class{m-note m-warning}
///
/// \parblock
/// <h3>Don't forget &lt;fstream&gt;!</h3>
/// Not everyone who uses the library is going to work directly from files, so not everybody is forced to pay
/// the compilation overhead of including `<fstream>`. You need to explicitly include it if you're going to be calling
/// toml::parse_file().
/// \endparblock
///
/// \see
/// - toml::parse_file()
/// - toml::parse_result

View File

@ -49,11 +49,11 @@ namespace toml::impl
s += TOML_STRING_PREFIX('"');
for (auto c : str)
{
if (c >= TOML_STRING_PREFIX('\x00') && c <= TOML_STRING_PREFIX('\x1F')) TOML_UNLIKELY
if TOML_UNLIKELY(c >= TOML_STRING_PREFIX('\x00') && c <= TOML_STRING_PREFIX('\x1F'))
s.append(low_character_escape_table[c]);
else if (c == TOML_STRING_PREFIX('\x7F')) TOML_UNLIKELY
else if TOML_UNLIKELY(c == TOML_STRING_PREFIX('\x7F'))
s.append(TOML_STRING_PREFIX("\\u007F"sv));
else if (c == TOML_STRING_PREFIX('"')) TOML_UNLIKELY
else if TOML_UNLIKELY(c == TOML_STRING_PREFIX('"'))
s.append(TOML_STRING_PREFIX("\\\""sv));
else
s += c;

View File

@ -95,9 +95,9 @@ namespace TOML_INTERNAL_NAMESPACE
else if constexpr (std::is_same_v<arg_t, utf8_codepoint>)
{
string_view cp_view;
if (arg.value <= U'\x1F') TOML_UNLIKELY
if TOML_UNLIKELY(arg.value <= U'\x1F')
cp_view = low_character_escape_table[arg.value];
else if (arg.value == U'\x7F') TOML_UNLIKELY
else if TOML_UNLIKELY(arg.value == U'\x7F')
cp_view = TOML_STRING_PREFIX("\\u007F"sv);
else
cp_view = arg.template as_view<string_char>();
@ -635,7 +635,7 @@ namespace toml::impl
if (!skipped_escaped_codepoint)
advance_and_return_if_error_or_eof({});
}
else TOML_LIKELY
else
{
// handle closing delimiters
if (*cp == U'"')
@ -1661,11 +1661,24 @@ namespace toml::impl
assert_or_assume(!is_value_terminator(*cp));
push_parse_scope("value"sv);
// check if it begins with some control character
// (note that this will also fail for whitespace but we're assuming we've
// called consume_leading_whitespace() before calling parse_value())
if TOML_UNLIKELY(is_control_character(*cp))
set_error_and_return_default("unexpected control character"sv);
// underscores at the beginning
else if (*cp == U'_')
set_error_and_return_default("values may not begin with underscores"sv);
const auto begin_pos = cp->position;
std::unique_ptr<node> val;
do
{
assert_or_assume(!is_control_character(*cp));
assert_or_assume(*cp != U'_');
// detect the value type and parse accordingly,
// starting with value types that can be detected
// unambiguously from just one character.
@ -1704,10 +1717,6 @@ namespace toml::impl
else if (is_match(*cp, U'i', U'n', U'I', U'N'))
val = std::make_unique<value<double>>(parse_inf_or_nan());
// underscores at the beginning
else if (*cp == U'_')
set_error_and_return_default("values may not begin with underscores"sv);
return_if_error({});
if (val)
break;
@ -1760,68 +1769,76 @@ namespace toml::impl
bool eof_while_scanning = false;
const auto scan = [&]() TOML_MAY_THROW
{
while (advance_count < utf8_buffered_reader::max_history_length)
if (is_eof())
return;
assert_or_assume(!is_value_terminator(*cp));
do
{
if (!cp || is_value_terminator(*cp))
if (const auto c = **cp; c != U'_')
{
eof_while_scanning = !cp;
break;
}
chars[char_count++] = c;
if (*cp != U'_')
{
chars[char_count++] = *cp;
switch (*cp)
if (is_decimal_digit(c))
add_trait(has_digits);
else if (is_ascii_letter(c))
{
case U'B': [[fallthrough]];
case U'b':
if (char_count == 2_sz && has_any(begins_zero))
add_trait(has_b);
break;
assert_or_assume((c >= U'a' && c <= U'z') || (c >= U'A' && c <= U'Z'));
switch (static_cast<char32_t>(c | 32u))
{
case U'b':
if (char_count == 2_sz && has_any(begins_zero))
add_trait(has_b);
break;
case U'E': [[fallthrough]];
case U'e':
if (char_count > 1_sz
&& has_none(has_b | has_o | has_p | has_t | has_x | has_z | has_colon)
&& (has_none(has_plus | has_minus) || has_any(begins_sign)))
add_trait(has_e);
break;
case U'e':
if (char_count > 1_sz
&& has_none(has_b | has_o | has_p | has_t | has_x | has_z | has_colon)
&& (has_none(has_plus | has_minus) || has_any(begins_sign)))
add_trait(has_e);
break;
case U'O': [[fallthrough]];
case U'o':
if (char_count == 2_sz && has_any(begins_zero))
add_trait(has_o);
break;
case U'o':
if (char_count == 2_sz && has_any(begins_zero))
add_trait(has_o);
break;
case U'P': [[fallthrough]];
case U'p':
if (has_any(has_x))
add_trait(has_p);
break;
case U'p':
if (has_any(has_x))
add_trait(has_p);
break;
case U'X': [[fallthrough]];
case U'x':
if ((char_count == 2_sz && has_any(begins_zero))
|| (char_count == 3_sz && has_any(begins_sign) && chars[1] == U'0'))
add_trait(has_x);
break;
case U'x':
if ((char_count == 2_sz && has_any(begins_zero))
|| (char_count == 3_sz && has_any(begins_sign) && chars[1] == U'0'))
add_trait(has_x);
break;
case U'T': add_trait(has_t); break;
case U'Z': add_trait(has_z); break;
case U'+': add_trait(has_plus); break;
case U'-': add_trait(has_minus); break;
case U'.': add_trait(has_dot); break;
case U':': add_trait(has_colon); break;
default:
if (is_decimal_digit(*cp))
add_trait(has_digits);
case U't': add_trait(has_t); break;
case U'z': add_trait(has_z); break;
}
}
else if (c <= U':')
{
assert_or_assume(c < U'0' || c > U'9');
switch (c)
{
case U'+': add_trait(has_plus); break;
case U'-': add_trait(has_minus); break;
case U'.': add_trait(has_dot); break;
case U':': add_trait(has_colon); break;
}
}
}
advance_and_return_if_error();
advance_count++;
eof_while_scanning = is_eof();
}
while (advance_count < utf8_buffered_reader::max_history_length
&& !is_eof()
&& !is_value_terminator(*cp)
);
};
scan();
return_if_error({});
@ -1831,7 +1848,7 @@ namespace toml::impl
&& traits == (bdigit_msk | has_minus)
&& chars[4] == U'-'
&& chars[7] == U'-'
&& cp
&& !is_eof()
&& *cp == U' ')
{
const auto pre_advance_count = advance_count;
@ -1850,7 +1867,7 @@ namespace toml::impl
advance_and_return_if_error({});
advance_count++;
if (!cp || !is_decimal_digit(*cp))
if (is_eof() || !is_decimal_digit(*cp))
backpedal();
else
{

View File

@ -96,6 +96,8 @@
#else
#define TOML_COMPILER_EXCEPTIONS 0
#endif
#define TOML_LIKELY(...) (__builtin_expect(!!(__VA_ARGS__), 1) )
#define TOML_UNLIKELY(...) (__builtin_expect(!!(__VA_ARGS__), 0) )
//floating-point from_chars and to_chars are not implemented in any version of clang as of 1/1/2020
#ifndef TOML_FLOAT_CHARCONV
@ -158,10 +160,8 @@
#else
#define TOML_COMPILER_EXCEPTIONS 0
#endif
// these pass the __has_attribute() test but cause warnings on if/else branches =/
#define TOML_LIKELY
#define TOML_UNLIKELY
#define TOML_LIKELY(...) (__builtin_expect(!!(__VA_ARGS__), 1) )
#define TOML_UNLIKELY(...) (__builtin_expect(!!(__VA_ARGS__), 0) )
// floating-point from_chars and to_chars are not implemented in any version of gcc as of 1/1/2020
#ifndef TOML_FLOAT_CHARCONV
@ -284,10 +284,10 @@
#if !TOML_DOXYGEN && !defined(__INTELLISENSE__)
#if !defined(TOML_LIKELY) && __has_cpp_attribute(likely)
#define TOML_LIKELY [[likely]]
#define TOML_LIKELY(...) (__VA_ARGS__) [[likely]]
#endif
#if !defined(TOML_UNLIKELY) && __has_cpp_attribute(unlikely)
#define TOML_UNLIKELY [[unlikely]]
#define TOML_UNLIKELY(...) (__VA_ARGS__) [[unlikely]]
#endif
#if __has_cpp_attribute(nodiscard) >= 201907L
#define TOML_NODISCARD_CTOR [[nodiscard]]
@ -295,10 +295,10 @@
#endif
#ifndef TOML_LIKELY
#define TOML_LIKELY
#define TOML_LIKELY(...) (__VA_ARGS__)
#endif
#ifndef TOML_UNLIKELY
#define TOML_UNLIKELY
#define TOML_UNLIKELY(...) (__VA_ARGS__)
#endif
#ifndef TOML_NODISCARD_CTOR
#define TOML_NODISCARD_CTOR

View File

@ -343,13 +343,13 @@ namespace toml::impl
static_assert(sizeof(Char) == 1);
for (auto c : str)
{
if (c >= TOML_STRING_PREFIX('\x00') && c <= TOML_STRING_PREFIX('\x1F')) TOML_UNLIKELY
if TOML_UNLIKELY(c >= TOML_STRING_PREFIX('\x00') && c <= TOML_STRING_PREFIX('\x1F'))
print_to_stream(low_character_escape_table[c], stream);
else if (c == TOML_STRING_PREFIX('\x7F')) TOML_UNLIKELY
else if TOML_UNLIKELY(c == TOML_STRING_PREFIX('\x7F'))
print_to_stream(TOML_STRING_PREFIX("\\u007F"sv), stream);
else if (c == TOML_STRING_PREFIX('"')) TOML_UNLIKELY
else if TOML_UNLIKELY(c == TOML_STRING_PREFIX('"'))
print_to_stream(TOML_STRING_PREFIX("\\\""sv), stream);
else if (c == TOML_STRING_PREFIX('\\')) TOML_UNLIKELY
else if TOML_UNLIKELY(c == TOML_STRING_PREFIX('\\'))
print_to_stream(TOML_STRING_PREFIX("\\\\"sv), stream);
else
print_to_stream(c, stream);

View File

@ -121,16 +121,6 @@ namespace toml::impl
return (codepoint >= U'0' && codepoint <= U'9');
}
[[nodiscard]]
TOML_GNU_ATTR(const)
constexpr bool is_hexadecimal_digit(char32_t codepoint) noexcept
{
return (codepoint >= U'a' && codepoint <= U'f')
|| (codepoint >= U'A' && codepoint <= U'F')
|| is_decimal_digit(codepoint)
;
}
[[nodiscard]]
TOML_GNU_ATTR(const)
TOML_ALWAYS_INLINE
@ -185,6 +175,14 @@ namespace toml::impl
;
}
[[nodiscard]]
TOML_GNU_ATTR(const)
TOML_ALWAYS_INLINE
constexpr bool is_control_character(char32_t codepoint) noexcept
{
return codepoint <= U'\u001F' || codepoint == U'\u007F';
}
[[nodiscard]]
TOML_GNU_ATTR(const)
TOML_ALWAYS_INLINE

File diff suppressed because it is too large Load Diff

View File

@ -162,6 +162,14 @@ namespace toml::impl
{
return value;
}
[[nodiscard]]
TOML_GNU_ATTR(pure)
TOML_ALWAYS_INLINE
constexpr const char32_t& operator* () const noexcept
{
return value;
}
};
static_assert(std::is_trivial_v<utf8_codepoint>);
static_assert(std::is_standard_layout_v<utf8_codepoint>);
@ -399,13 +407,13 @@ namespace toml::impl
else
{
// first character read from stream
if (!history.count && !head) TOML_UNLIKELY
if TOML_UNLIKELY(!history.count && !head)
head = reader.read_next();
// subsequent characters and not eof
else if (head)
{
if (history.count < history_buffer_size) TOML_UNLIKELY
if TOML_UNLIKELY(history.count < history_buffer_size)
history.buffer[history.count++] = *head;
else
history.buffer[(history.first++ + history_buffer_size) % history_buffer_size] = *head;

File diff suppressed because it is too large Load Diff

View File

@ -7,7 +7,7 @@ TEST_CASE("arrays - moving")
parsing_should_succeed(
FILE_LINE_ARGS,
S(R"(test = [ "foo" ])"sv),
[&](table&& tbl) noexcept
[&](table&& tbl)
{
CHECK(tbl.source().begin == source_position{ 1, 1 });
CHECK(tbl.source().end == source_position{ 1, 17 });

View File

@ -7,7 +7,7 @@ TEST_CASE("tables - moving")
parsing_should_succeed(
FILE_LINE_ARGS,
S(R"(test = { val1 = "foo" })"sv),
[&](table&& tbl) noexcept
[&](table&& tbl)
{
CHECK(tbl.source().begin == source_position{ 1, 1 });
CHECK(tbl.source().end == source_position{ 1, 24 });
@ -136,7 +136,7 @@ TEST_CASE("tables - equality")
namespace
{
template <typename T>
static auto advance(T iter, ptrdiff_t offset) noexcept
static auto advance(T iter, ptrdiff_t offset)
{
while (offset > 0)
{

View File

@ -2,7 +2,7 @@
TEST_CASE("values - printing")
{
static constexpr auto print_value = [](auto&& raw) noexcept
static constexpr auto print_value = [](auto&& raw)
{
auto val = toml::value{ std::forward<decltype(raw)>(raw) };
std::stringstream ss;

View File

@ -15,6 +15,8 @@ test_sources = [
'manipulating_arrays.cpp',
'manipulating_tables.cpp',
'manipulating_values.cpp',
'unicode.cpp',
'unicode_generated.cpp',
]
compiler_supports_char8_strings = compiler.compiles('''

View File

@ -18,7 +18,7 @@ nested_array_of_int = [ [ 1, 2 ], [3, 4, 5] ]
nested_mixed_array = [ [ 1, 2 ], ["a", "b", "c"] ]
string_array = [ "all", 'strings', """are the same""", '''type''' ]
)"sv),
[](table&& tbl) noexcept
[](table&& tbl)
{
REQUIRE(tbl[S("integers")].as<array>());
CHECK(tbl[S("integers")].as<array>()->is_homogeneous());
@ -106,7 +106,7 @@ contributors = [
{ name = "Baz Qux", email = "bazqux@example.com", url = "https://example.com/bazqux" }
]
)"sv),
[](table&& tbl) noexcept
[](table&& tbl)
{
REQUIRE(tbl[S("numbers")].as<array>());
CHECK(!tbl[S("numbers")].as<array>()->is_homogeneous());

View File

@ -8,7 +8,7 @@ TEST_CASE("parsing - booleans")
bool1 = true
bool2 = false
)"sv),
[](table&& tbl) noexcept
[](table&& tbl)
{
CHECK(tbl[S("bool1")] == true);
CHECK(tbl[S("bool2")] == false);

View File

@ -9,7 +9,7 @@ TEST_CASE("parsing - comments")
key = "value" # This is a comment at the end of a line
another = "# This is not a comment"
)"sv),
[](table&& tbl) noexcept
[](table&& tbl)
{
CHECK(tbl.size() == 2);
CHECK(tbl[S("key")] == S("value"sv));
@ -20,7 +20,7 @@ another = "# This is not a comment"
parsing_should_succeed(
FILE_LINE_ARGS,
S(R"(# this = "looks like a KVP but is commented out)"sv),
[](table&& tbl) noexcept
[](table&& tbl)
{
CHECK(tbl.size() == 0);
}

View File

@ -18,7 +18,7 @@ ld1 = 1979-05-27
lt1 = 07:32:00
lt2 = 00:32:00.999999
)"sv),
[](table&& tbl) noexcept
[](table&& tbl)
{
static constexpr auto odt1 = date_time{ { 1979, 5, 27 }, { 7, 32 }, {} };
CHECK(tbl[S("odt1")] == odt1);

View File

@ -22,7 +22,7 @@ flt7 = 6.626e-34
flt8 = 224_617.445_991_228
)"sv),
[](table&& tbl) noexcept
[](table&& tbl)
{
CHECK(tbl[S("flt1")] == 1.0);
CHECK(tbl[S("flt2")] == 3.1415);
@ -44,7 +44,7 @@ flt8 = 224_617.445_991_228
parsing_should_succeed(
FILE_LINE_ARGS,
S(R"(zeroes = [-0.0, +0.0])"sv),
[](table&& tbl) noexcept
[](table&& tbl)
{
CHECK(tbl[S("zeroes")][0] == -0.0);
CHECK(tbl[S("zeroes")][1] == +0.0);
@ -188,7 +188,7 @@ sf4 = nan # actual sNaN/qNaN encoding is implementation specific
sf5 = +nan # same as `nan`
sf6 = -nan # valid, actual encoding is implementation specific
)"sv),
[](table&& tbl) noexcept
[](table&& tbl)
{
CHECK(tbl[S("sf1")] == std::numeric_limits<double>::infinity());
CHECK(tbl[S("sf2")] == std::numeric_limits<double>::infinity());

View File

@ -13,7 +13,7 @@ int5 = 1_000
int6 = 5_349_221
int7 = 1_2_3_4_5 # VALID but discouraged
)"sv),
[](table&& tbl) noexcept
[](table&& tbl)
{
CHECK(tbl[S("int1")] == 99);
CHECK(tbl[S("int2")] == 42);
@ -43,7 +43,7 @@ int7 = 1_2_3_4_5 # VALID but discouraged
parsing_should_succeed(
FILE_LINE_ARGS,
S("zeroes = [-0, +0]"sv),
[](table&& tbl) noexcept
[](table&& tbl)
{
CHECK(tbl[S("zeroes")][0] == 0);
CHECK(tbl[S("zeroes")][1] == 0);
@ -87,7 +87,7 @@ oct2 = 0o755 # useful for Unix file permissions
# binary with prefix `0b`
bin1 = 0b11010110
)"sv),
[](table&& tbl) noexcept
[](table&& tbl)
{
CHECK(tbl[S("hex1")] == 0xDEADBEEF);
CHECK(tbl[S("hex2")] == 0xDEADBEEF);
@ -118,7 +118,7 @@ oct1 = 0o0001234567
oct2 = 0o000755
bin1 = 0b0000011010110
)"sv),
[](table&& tbl) noexcept
[](table&& tbl)
{
CHECK(tbl[S("hex1")] == 0xDEADBEEF);
CHECK(tbl[S("hex2")] == 0xDEADBEEF);

View File

@ -11,7 +11,7 @@ bare-key = "value"
1234 = "value"
"" = "blank"
)"sv),
[](table&& tbl) noexcept
[](table&& tbl)
{
CHECK(tbl.size() == 5);
CHECK(tbl[S("key")] == S("value"sv));
@ -34,7 +34,7 @@ bare-key = "value"
'quoted "value"' = "value"
'' = 'blank'
)"sv),
[](table&& tbl) noexcept
[](table&& tbl)
{
CHECK(tbl[S("127.0.0.1")] == S("value"sv));
CHECK(tbl[S("character encoding")] == S("value"sv));
@ -66,7 +66,7 @@ physical.shape = "round"
site."google.com" = true
3.14159 = "pi"
)"sv),
[](table&& tbl) noexcept
[](table&& tbl)
{
CHECK(tbl.size() == 4);
CHECK(tbl[S("name")] == S("Orange"sv));
@ -84,7 +84,7 @@ site."google.com" = true
fruit.apple.smooth = true
fruit.orange = 2
)"sv),
[](table&& tbl) noexcept
[](table&& tbl)
{
CHECK(tbl[S("fruit")][S("apple")][S("smooth")] == true);
CHECK(tbl[S("fruit")][S("orange")] == 2);
@ -111,7 +111,7 @@ orange.skin = "thick"
apple.color = "red"
orange.color = "orange"
)"sv),
[](table&& tbl) noexcept
[](table&& tbl)
{
CHECK(tbl[S("apple")][S("type")] == S("fruit"sv));
CHECK(tbl[S("apple")][S("skin")] == S("thin"sv));
@ -135,7 +135,7 @@ orange.type = "fruit"
orange.skin = "thick"
orange.color = "orange"
)"sv),
[](table&& tbl) noexcept
[](table&& tbl)
{
CHECK(tbl[S("apple")][S("type")] == S("fruit"sv));
CHECK(tbl[S("apple")][S("skin")] == S("thin"sv));
@ -154,7 +154,7 @@ orange.color = "orange"
key+1 = 0
ʎǝʞ2 = 0
)"sv),
[](table&& tbl) noexcept
[](table&& tbl)
{
CHECK(tbl.size() == 2);
CHECK(tbl[S("key+1")] == 0);

View File

@ -43,7 +43,7 @@ hosts = [
parsing_should_succeed(
FILE_LINE_ARGS,
toml_text,
[](table&& tbl) noexcept
[](table&& tbl)
{
CHECK(tbl.size() == 5);

View File

@ -16,7 +16,7 @@ str2 = """
Roses are red
Violets are blue"""
)"sv),
[](table&& tbl) noexcept
[](table&& tbl)
{
CHECK(tbl[S("str")] == S("I'm a string. \"You can quote me\". Name\tJos\u00E9\nLocation\tSF."sv));
CHECK(tbl[S("str1")] == S("Roses are red\nViolets are blue"sv));
@ -51,7 +51,7 @@ str6 = """Here are fifteen quotation marks: ""\"""\"""\"""\"""\"."""
# "This," she said, "is just a pointless statement."
str7 = """"This," she said, "is just a pointless statement.""""
)"sv),
[](table&& tbl) noexcept
[](table&& tbl)
{
static constexpr auto quick_brown_fox = S("The quick brown fox jumps over the lazy dog."sv);
CHECK(tbl[S("str1")] == quick_brown_fox);
@ -89,7 +89,7 @@ trimmed in raw strings.
is preserved.
'''
)"sv),
[](table&& tbl) noexcept
[](table&& tbl)
{
CHECK(tbl[S("winpath")] == S(R"(C:\Users\nodejs\templates)"sv));
CHECK(tbl[S("winpath2")] == S(R"(\\ServerX\admin$\system32\)"sv));
@ -121,7 +121,7 @@ apos15 = "Here are fifteen apostrophes: '''''''''''''''"
# 'That's still pointless', she said.
str = ''''That's still pointless', she said.'''
)"sv),
[](table&& tbl) noexcept
[](table&& tbl)
{
CHECK(tbl[S("quot15")] == S(R"(Here are fifteen quotation marks: """"""""""""""")"sv));
CHECK(tbl[S("apos15")] == S(R"(Here are fifteen apostrophes: ''''''''''''''')"sv));

View File

@ -38,7 +38,7 @@ apple.taste.sweet = true
smooth = true
)"sv),
[](table&& tbl) noexcept
[](table&& tbl)
{
REQUIRE(tbl[S("table")].as<table>());
CHECK(tbl[S("table")].as<table>()->size() == 0_sz);
@ -128,7 +128,7 @@ apple.taste.sweet = true
[animal]
[fruit.orange]
)"sv),
[](table&& tbl) noexcept
[](table&& tbl)
{
REQUIRE(tbl[S("animal")].as<table>());
CHECK(tbl[S("animal")].as<table>()->size() == 0_sz);
@ -150,7 +150,7 @@ apple.taste.sweet = true
[fruit.orange]
[animal]
)"sv),
[](table&& tbl) noexcept
[](table&& tbl)
{
REQUIRE(tbl[S("animal")].as<table>());
CHECK(tbl[S("animal")].as<table>()->size() == 0_sz);
@ -176,7 +176,7 @@ animal = { type.name = "pug" }
[product]
type = { name = "Nail" }
)"sv),
[](table&& tbl) noexcept
[](table&& tbl)
{
REQUIRE(tbl[S("name")].as<table>());
CHECK(tbl[S("name")].as<table>()->size() == 2_sz);
@ -223,7 +223,7 @@ test = { val1 = "foo", val2 = [
3
], val3 = "bar" }
)"sv),
[](table&& tbl) noexcept
[](table&& tbl)
{
REQUIRE(tbl[S("test")].as<table>());
CHECK(tbl[S("test")].as<table>()->size() == 3_sz);
@ -248,7 +248,7 @@ name = {
last = "Preston-Werner",
}
)"sv),
[](table&& tbl) noexcept
[](table&& tbl)
{
REQUIRE(tbl[S("name")].as<table>());
CHECK(tbl[S("name")].as<table>()->size() == 2_sz);
@ -317,7 +317,7 @@ color = "gray"
name = "plantain"
)"sv),
[](table&& tbl) noexcept
[](table&& tbl)
{
REQUIRE(tbl[S("points")].as<array>());
CHECK(tbl[S("points")].as<array>()->size() == 3_sz);

View File

@ -1,11 +1,11 @@
#include "tests.h"
template void parse_expected_value(std::string_view, uint32_t, std::string_view, const int&) noexcept;
template void parse_expected_value(std::string_view, uint32_t, std::string_view, const unsigned int&) noexcept;
template void parse_expected_value(std::string_view, uint32_t, std::string_view, const bool&) noexcept;
template void parse_expected_value(std::string_view, uint32_t, std::string_view, const float&) noexcept;
template void parse_expected_value(std::string_view, uint32_t, std::string_view, const double&) noexcept;
template void parse_expected_value(std::string_view, uint32_t, std::string_view, const toml::string_view&) noexcept;
template void parse_expected_value(std::string_view, uint32_t, std::string_view, const int&);
template void parse_expected_value(std::string_view, uint32_t, std::string_view, const unsigned int&);
template void parse_expected_value(std::string_view, uint32_t, std::string_view, const bool&);
template void parse_expected_value(std::string_view, uint32_t, std::string_view, const float&);
template void parse_expected_value(std::string_view, uint32_t, std::string_view, const double&);
template void parse_expected_value(std::string_view, uint32_t, std::string_view, const toml::string_view&);
namespace std
{

View File

@ -46,14 +46,14 @@ inline void parsing_should_succeed(
uint32_t test_line,
std::basic_string_view<Char> toml_str,
Func&& func = {},
std::string_view source_path = {}) noexcept
std::string_view source_path = {})
{
INFO(
"["sv << test_file << ", line "sv << test_line << "] "sv
<< "parsing_should_succeed('"sv << std::string_view(reinterpret_cast<const char*>(toml_str.data()), toml_str.length()) << "')"sv
)
constexpr auto validate_table = [](table&& tabl, std::string_view path) noexcept -> table&&
constexpr auto validate_table = [](table&& tabl, std::string_view path) -> table&&
{
INFO("Validating table source information"sv)
CHECK(tabl.source().begin != source_position{});
@ -155,7 +155,7 @@ template <typename Char>
inline void parsing_should_fail(
std::string_view test_file,
uint32_t test_line,
std::basic_string_view<Char> toml_str) noexcept
std::basic_string_view<Char> toml_str)
{
INFO(
"["sv << test_file << ", line "sv << test_line << "] "sv
@ -164,7 +164,7 @@ inline void parsing_should_fail(
#if TOML_EXCEPTIONS
static constexpr auto run_tests = [](auto&& fn) noexcept
static constexpr auto run_tests = [](auto&& fn)
{
try
{
@ -196,7 +196,7 @@ inline void parsing_should_fail(
#else
static constexpr auto run_tests = [](auto&& fn) noexcept
static constexpr auto run_tests = [](auto&& fn)
{
parse_result result = fn();
if (result)
@ -212,8 +212,8 @@ inline void parsing_should_fail(
}
};
if (run_tests([=]() noexcept { return toml::parse(toml_str); }))
run_tests([=]() noexcept
if (run_tests([=]() { return toml::parse(toml_str); }))
run_tests([=]()
{
std::basic_stringstream<Char, std::char_traits<Char>, std::allocator<Char>> ss;
ss.write(toml_str.data(), static_cast<std::streamsize>(toml_str.length()));
@ -228,7 +228,7 @@ inline void parse_expected_value(
std::string_view test_file,
uint32_t test_line,
std::string_view value_str,
const T& expected) noexcept
const T& expected)
{
INFO("["sv << test_file << ", line "sv << test_line << "] "sv << "parse_expected_value('"sv << value_str << "')"sv)
@ -238,7 +238,7 @@ inline void parse_expected_value(
val.append(key);
val.append(value_str);
static constexpr auto is_val = [](char32_t codepoint) noexcept
static constexpr auto is_val = [](char32_t codepoint)
{
if constexpr (std::is_same_v<string, impl::promoted<T>>)
return codepoint == U'"' || codepoint == U'\'';
@ -287,7 +287,7 @@ inline void parse_expected_value(
test_file,
test_line,
std::string_view{ val },
[&](table&& tbl) noexcept
[&](table&& tbl)
{
REQUIRE(tbl.size() == 1);
auto nv = tbl[S("val"sv)];
@ -347,7 +347,7 @@ inline void parse_expected_value(
test_file,
test_line,
std::string_view{ str },
[&](table&& tbl) noexcept
[&](table&& tbl)
{
REQUIRE(tbl.size() == 1);
auto nv = tbl[S("val"sv)];
@ -368,12 +368,12 @@ inline void parse_expected_value(
}
// manually instantiate some templates to reduce test compilation time (chosen using ClangBuildAnalyzer)
extern template void parse_expected_value(std::string_view, uint32_t, std::string_view, const int&) noexcept;
extern template void parse_expected_value(std::string_view, uint32_t, std::string_view, const unsigned int&) noexcept;
extern template void parse_expected_value(std::string_view, uint32_t, std::string_view, const bool&) noexcept;
extern template void parse_expected_value(std::string_view, uint32_t, std::string_view, const float&) noexcept;
extern template void parse_expected_value(std::string_view, uint32_t, std::string_view, const double&) noexcept;
extern template void parse_expected_value(std::string_view, uint32_t, std::string_view, const toml::string_view&) noexcept;
extern template void parse_expected_value(std::string_view, uint32_t, std::string_view, const int&);
extern template void parse_expected_value(std::string_view, uint32_t, std::string_view, const unsigned int&);
extern template void parse_expected_value(std::string_view, uint32_t, std::string_view, const bool&);
extern template void parse_expected_value(std::string_view, uint32_t, std::string_view, const float&);
extern template void parse_expected_value(std::string_view, uint32_t, std::string_view, const double&);
extern template void parse_expected_value(std::string_view, uint32_t, std::string_view, const toml::string_view&);
namespace std
{
extern template class unique_ptr<const Catch::IExceptionTranslator>;

86
tests/unicode.cpp Normal file
View File

@ -0,0 +1,86 @@
#include "tests.h"
#include "unicode.h"
using namespace toml::impl;
TEST_CASE("unicode - is_ascii_letter")
{
constexpr auto fn = is_ascii_letter;
REQUIRE(not_in(fn, { U'\0', U'@' }));
REQUIRE(in_only(fn, { U'A', U'Z' }));
REQUIRE(not_in(fn, { U'[', U'`' }));
REQUIRE(in_only(fn, { U'a', U'z' }));
REQUIRE(not_in(fn, { U'{', unimax }));
}
TEST_CASE("unicode - is_ascii_whitespace")
{
constexpr auto fn = is_ascii_whitespace;
REQUIRE(not_in(fn, { U'\0', U'\u0008' }));
REQUIRE(in_only(fn, U'\t' ));
REQUIRE(not_in(fn, { U'\n', U'\u001F' }));
REQUIRE(in_only(fn, U' ' ));
REQUIRE(not_in(fn, { U'!', unimax }));
}
TEST_CASE("unicode - is_ascii_line_break")
{
constexpr auto fn = is_ascii_line_break<true>;
REQUIRE(not_in(fn, { U'\0', U'\t' }));
REQUIRE(in_only(fn, { U'\n', U'\r' }));
REQUIRE(not_in(fn, { U'\u000E', unimax }));
}
TEST_CASE("unicode - is_decimal_digit")
{
constexpr auto fn = is_decimal_digit;
REQUIRE(not_in(fn, { U'\0', U'/' }));
REQUIRE(in_only(fn, { U'0', U'9' }));
REQUIRE(not_in(fn, { U':', unimax }));
}
TEST_CASE("unicode - is_string_delimiter")
{
constexpr auto fn = is_string_delimiter;
REQUIRE(not_in(fn, { U'\0', U'!' }));
REQUIRE(in_only(fn, U'"' ));
REQUIRE(not_in(fn, { U'#', U'&' }));
REQUIRE(in_only(fn, U'\'' ));
REQUIRE(not_in(fn, { U'(', unimax }));
}
TEST_CASE("unicode - is_unicode_whitespace")
{
constexpr auto fn = is_unicode_whitespace;
REQUIRE(not_in(fn, { U'\0', U'\u009F' }));
REQUIRE(in_only(fn, U'\u00A0' ));
REQUIRE(not_in(fn, { U'\u00A1', U'\u167F' }));
REQUIRE(in_only(fn, U'\u1680' ));
REQUIRE(not_in(fn, { U'\u1681', U'\u1FFF' }));
REQUIRE(in_only(fn, { U'\u2000', U'\u200A' }));
REQUIRE(not_in(fn, { U'\u200B', U'\u202E' }));
REQUIRE(in_only(fn, U'\u202F' ));
REQUIRE(not_in(fn, { U'\u2030', U'\u205E' }));
REQUIRE(in_only(fn, U'\u205F' ));
REQUIRE(not_in(fn, { U'\u2060', U'\u2FFF' }));
REQUIRE(in_only(fn, U'\u3000' ));
REQUIRE(not_in(fn, { U'\u3001', unimax }));
}
TEST_CASE("unicode - is_unicode_line_break")
{
constexpr auto fn = is_unicode_line_break;
REQUIRE(not_in(fn, { U'\0', U'\u0084' }));
REQUIRE(in_only(fn, U'\u0085' ));
REQUIRE(not_in(fn, { U'\u0086', U'\u2027' }));
REQUIRE(in_only(fn, { U'\u2028', U'\u2029' }));
REQUIRE(not_in(fn, { U'\u202A', unimax }));
}
TEST_CASE("unicode - is_unicode_surrogate")
{
constexpr auto fn = is_unicode_surrogate;
REQUIRE(not_in(fn, { U'\0', 0xD7FFu }));
REQUIRE(in_only(fn, { 0xD800u, 0xDFFF }));
REQUIRE(not_in(fn, { 0xE000, unimax }));
}

84
tests/unicode.h Normal file
View File

@ -0,0 +1,84 @@
#pragma once
#include "tests.h"
using func_type = bool(char32_t);
inline constexpr func_type* funcs[] =
{
// these must be mutually-exclusive
impl::is_ascii_letter,
impl::is_ascii_whitespace,
impl::is_ascii_line_break<true>,
impl::is_decimal_digit,
impl::is_string_delimiter,
impl::is_unicode_whitespace,
impl::is_unicode_line_break,
impl::is_unicode_surrogate,
#if TOML_LANG_UNRELEASED
impl::is_unicode_letter,
impl::is_unicode_number,
impl::is_unicode_combining_mark,
#endif
};
template <typename T>
inline bool in_only(func_type* fptr, T cp) noexcept
{
if (!fptr(static_cast<char32_t>(cp)))
return false;
for (auto fn : funcs)
{
if (fn == fptr)
continue;
if (fn(static_cast<char32_t>(cp)))
return false;
}
return true;
}
inline constexpr uint32_t unimax = 0x10FFFFu;
struct codepoint_range
{
char32_t first;
char32_t last;
template <typename T, typename U>
codepoint_range(T first_, U last_) noexcept
: first{ static_cast<char32_t>(first_) },
last{ static_cast<char32_t>(last_) }
{
if (last < first)
std::swap(first, last);
}
template <typename T>
codepoint_range(T first_) noexcept
: first{ static_cast<char32_t>(first_) },
last{ first }
{}
};
inline bool in(func_type* fptr, codepoint_range range) noexcept
{
for (auto cp = range.first; cp <= range.last; cp++)
if (!fptr(cp))
return false;
return true;
}
inline bool in_only(func_type* fptr, codepoint_range range) noexcept
{
for (auto cp = range.first; cp <= range.last; cp++)
if (!in_only(fptr, cp))
return false;
return true;
}
inline bool not_in(func_type* fptr, codepoint_range range) noexcept
{
for (auto cp = range.first; cp <= range.last; cp++)
if (fptr(cp))
return false;
return true;
}

2040
tests/unicode_generated.cpp Normal file

File diff suppressed because it is too large Load Diff

1773
toml.hpp

File diff suppressed because it is too large Load Diff

View File

@ -51,7 +51,7 @@
<ClCompile>
<AdditionalIncludeDirectories>..\tests;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
<PreprocessorDefinitions>TOML_CHAR_8_STRINGS=0;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<PreprocessorDefinitions>TOML_UNRELEASED_FEATURES=1;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<PreprocessorDefinitions>TOML_UNRELEASED_FEATURES=1;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<PrecompiledHeader>Use</PrecompiledHeader>
<PrecompiledHeaderFile>tests.h</PrecompiledHeaderFile>
<PreprocessorDefinitions Condition="'%(PrecompiledHeader)'=='Use'">USING_PCH=1;%(PreprocessorDefinitions)</PreprocessorDefinitions>
@ -84,12 +84,15 @@
<ClCompile Include="..\tests\tests.cpp">
<PrecompiledHeader>Create</PrecompiledHeader>
</ClCompile>
<ClCompile Include="..\tests\unicode.cpp" />
<ClCompile Include="..\tests\unicode_generated.cpp" />
</ItemGroup>
<ItemGroup>
<Natvis Include="toml++.natvis" />
</ItemGroup>
<ItemGroup>
<ClInclude Include="..\tests\tests.h" />
<ClInclude Include="..\tests\unicode.h" />
</ItemGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
</Project>

View File

@ -84,6 +84,8 @@
<ClCompile Include="..\tests\tests.cpp">
<PrecompiledHeader>Create</PrecompiledHeader>
</ClCompile>
<ClCompile Include="..\tests\unicode.cpp" />
<ClCompile Include="..\tests\unicode_generated.cpp" />
</ItemGroup>
<ItemGroup>
<Natvis Include="toml++.natvis" />

View File

@ -86,6 +86,8 @@
<ClCompile Include="..\tests\tests.cpp">
<PrecompiledHeader>Create</PrecompiledHeader>
</ClCompile>
<ClCompile Include="..\tests\unicode.cpp" />
<ClCompile Include="..\tests\unicode_generated.cpp" />
</ItemGroup>
<ItemGroup>
<Natvis Include="toml++.natvis" />

View File

@ -84,6 +84,8 @@
<ClCompile Include="..\tests\tests.cpp">
<PrecompiledHeader>Create</PrecompiledHeader>
</ClCompile>
<ClCompile Include="..\tests\unicode.cpp" />
<ClCompile Include="..\tests\unicode_generated.cpp" />
</ItemGroup>
<ItemGroup>
<Natvis Include="toml++.natvis" />

View File

@ -86,6 +86,8 @@
<ClCompile Include="..\tests\tests.cpp">
<PrecompiledHeader>Create</PrecompiledHeader>
</ClCompile>
<ClCompile Include="..\tests\unicode.cpp" />
<ClCompile Include="..\tests\unicode_generated.cpp" />
</ItemGroup>
<ItemGroup>
<Natvis Include="toml++.natvis" />

View File

@ -86,6 +86,8 @@
<ClCompile Include="..\tests\tests.cpp">
<PrecompiledHeader>Create</PrecompiledHeader>
</ClCompile>
<ClCompile Include="..\tests\unicode.cpp" />
<ClCompile Include="..\tests\unicode_generated.cpp" />
</ItemGroup>
<ItemGroup>
<Natvis Include="toml++.natvis" />

View File

@ -84,6 +84,8 @@
<ClCompile Include="..\tests\tests.cpp">
<PrecompiledHeader>Create</PrecompiledHeader>
</ClCompile>
<ClCompile Include="..\tests\unicode.cpp" />
<ClCompile Include="..\tests\unicode_generated.cpp" />
</ItemGroup>
<ItemGroup>
<Natvis Include="toml++.natvis" />

View File

@ -86,6 +86,8 @@
<ClCompile Include="..\tests\tests.cpp">
<PrecompiledHeader>Create</PrecompiledHeader>
</ClCompile>
<ClCompile Include="..\tests\unicode.cpp" />
<ClCompile Include="..\tests\unicode_generated.cpp" />
</ItemGroup>
<ItemGroup>
<Natvis Include="toml++.natvis" />

View File

@ -84,6 +84,8 @@
<ClCompile Include="..\tests\tests.cpp">
<PrecompiledHeader>Create</PrecompiledHeader>
</ClCompile>
<ClCompile Include="..\tests\unicode.cpp" />
<ClCompile Include="..\tests\unicode_generated.cpp" />
</ItemGroup>
<ItemGroup>
<Natvis Include="toml++.natvis" />

View File

@ -84,6 +84,8 @@
<ClCompile Include="..\tests\tests.cpp">
<PrecompiledHeader>Create</PrecompiledHeader>
</ClCompile>
<ClCompile Include="..\tests\unicode.cpp" />
<ClCompile Include="..\tests\unicode_generated.cpp" />
</ItemGroup>
<ItemGroup>
<Natvis Include="toml++.natvis" />

View File

@ -86,6 +86,8 @@
<ClCompile Include="..\tests\tests.cpp">
<PrecompiledHeader>Create</PrecompiledHeader>
</ClCompile>
<ClCompile Include="..\tests\unicode.cpp" />
<ClCompile Include="..\tests\unicode_generated.cpp" />
</ItemGroup>
<ItemGroup>
<Natvis Include="toml++.natvis" />

View File

@ -84,6 +84,8 @@
<ClCompile Include="..\tests\tests.cpp">
<PrecompiledHeader>Create</PrecompiledHeader>
</ClCompile>
<ClCompile Include="..\tests\unicode.cpp" />
<ClCompile Include="..\tests\unicode_generated.cpp" />
</ItemGroup>
<ItemGroup>
<Natvis Include="toml++.natvis" />

View File

@ -86,6 +86,8 @@
<ClCompile Include="..\tests\tests.cpp">
<PrecompiledHeader>Create</PrecompiledHeader>
</ClCompile>
<ClCompile Include="..\tests\unicode.cpp" />
<ClCompile Include="..\tests\unicode_generated.cpp" />
</ItemGroup>
<ItemGroup>
<Natvis Include="toml++.natvis" />

View File

@ -86,6 +86,8 @@
<ClCompile Include="..\tests\tests.cpp">
<PrecompiledHeader>Create</PrecompiledHeader>
</ClCompile>
<ClCompile Include="..\tests\unicode.cpp" />
<ClCompile Include="..\tests\unicode_generated.cpp" />
</ItemGroup>
<ItemGroup>
<Natvis Include="toml++.natvis" />

View File

@ -84,6 +84,8 @@
<ClCompile Include="..\tests\tests.cpp">
<PrecompiledHeader>Create</PrecompiledHeader>
</ClCompile>
<ClCompile Include="..\tests\unicode.cpp" />
<ClCompile Include="..\tests\unicode_generated.cpp" />
</ItemGroup>
<ItemGroup>
<Natvis Include="toml++.natvis" />

View File

@ -86,6 +86,8 @@
<ClCompile Include="..\tests\tests.cpp">
<PrecompiledHeader>Create</PrecompiledHeader>
</ClCompile>
<ClCompile Include="..\tests\unicode.cpp" />
<ClCompile Include="..\tests\unicode_generated.cpp" />
</ItemGroup>
<ItemGroup>
<Natvis Include="toml++.natvis" />