allow lowercase 't' and 'z' in datetimes (per spec)

also:
- updated conformance tests
This commit is contained in:
Mark Gillard 2021-07-04 13:58:33 +03:00
parent ba754462b8
commit 4f21332bdd
6 changed files with 123 additions and 51 deletions

2
external/toml-test vendored

@ -1 +1 @@
Subproject commit 1f6389604dc6053f23cc4679c557a0b0e69eaf5d
Subproject commit 269931e74e3fef9d81729fe0f351f21f65cfce4b

View File

@ -1741,7 +1741,7 @@ TOML_IMPL_NAMESPACE_START
{
if (is_eof()
|| is_value_terminator(*cp)
|| (part_of_datetime && is_match(*cp, U'+', U'-', U'Z')))
|| (part_of_datetime && is_match(*cp, U'+', U'-', U'Z', U'z')))
return time;
}
else
@ -1764,7 +1764,7 @@ TOML_IMPL_NAMESPACE_START
// '.' (early-exiting is allowed; fractional is optional)
if (is_eof()
|| is_value_terminator(*cp)
|| (part_of_datetime && is_match(*cp, U'+', U'-', U'Z')))
|| (part_of_datetime && is_match(*cp, U'+', U'-', U'Z', U'z')))
return time;
if (*cp != U'.')
set_error_and_return_default("expected '.', saw '"sv, to_sv(*cp), "'"sv);
@ -1812,9 +1812,9 @@ TOML_IMPL_NAMESPACE_START
auto date = parse_date(true);
set_error_and_return_if_eof({});
// ' ' or 'T'
if (!is_match(*cp, U' ', U'T'))
set_error_and_return_default("expected space or 'T', saw '"sv, to_sv(*cp), "'"sv);
// ' ', 'T' or 't'
if (!is_match(*cp, U' ', U'T', U't'))
set_error_and_return_default("expected space, 'T' or 't', saw '"sv, to_sv(*cp), "'"sv);
advance_and_return_if_error_or_eof({});
// "HH:MM:SS.FFFFFFFFF"
@ -1825,9 +1825,9 @@ TOML_IMPL_NAMESPACE_START
if (is_eof() || is_value_terminator(*cp))
return { date, time };
// zero offset ("Z")
// zero offset ('Z' or 'z')
time_offset offset;
if (*cp == U'Z')
if (is_match(*cp, U'Z', U'z'))
advance_and_return_if_error({});
// explicit offset ("+/-HH:MM")
@ -2161,21 +2161,20 @@ TOML_IMPL_NAMESPACE_START
// (as opposed to the fallback "could not determine type" message)
if (has_any(has_p))
val = new value{ parse_hex_float() };
else if (has_any(has_x))
else if (has_any(has_x | has_o | has_b))
{
val = new value{ parse_integer<16>() };
int64_t i;
if (has_any(has_x))
i = parse_integer<16>();
else if (has_any(has_o))
i = parse_integer<8>();
else // has_b
i = parse_integer<2>();
return_if_error({});
val = new value{ i };
reinterpret_cast<value<int64_t>*>(val.get())->flags(value_flags::format_as_hexadecimal);
}
else if (has_any(has_o))
{
val = new value{ parse_integer<8>() };
reinterpret_cast<value<int64_t>*>(val.get())->flags(value_flags::format_as_octal);
}
else if (has_any(has_b))
{
val = new value{ parse_integer<2>() };
reinterpret_cast<value<int64_t>*>(val.get())->flags(value_flags::format_as_binary);
}
else if (has_any(has_e) || (has_any(begins_zero | begins_digit) && chars[1] == U'.'))
val = new value{ parse_float() };
else if (has_any(begins_sign))

View File

@ -15,10 +15,18 @@ namespace
static constexpr auto array_bool = R"(a = [true, false])"sv;
static constexpr auto array_empty = R"(thevoid = [[[[[]]]]])"sv;
static constexpr auto array_hetergeneous = R"(mixed = [[1, 2], ["a", "b"], [1.1, 2.1]])"sv;
static constexpr auto array_mix_string_table = R"(numbers = [ 0.1, 0.2, 0.5, 1, 2, 5 ]
contributors = [
static constexpr auto array_mixed_int_array = R"(arrays-and-ints = [1, ["Arrays are not integers."]])"sv;
static constexpr auto array_mixed_int_float = R"(ints-and-floats = [1, 1.1])"sv;
static constexpr auto array_mixed_int_string = R"(strings-and-ints = ["hi", 42])"sv;
static constexpr auto array_mixed_string_table = R"(contributors = [
"Foo Bar <foo@example.com>",
{ name = "Baz Qux", email = "bazqux@example.com", url = "https://example.com/bazqux" }
])"sv;
static constexpr auto array_nested_double = R"(nest = [
[
["a"],
[1, 2, [3]]
]
])"sv;
static constexpr auto array_nested_inline_table = R"(a = [ { b = {} } ])"sv;
static constexpr auto array_nested = R"(nest = [["a"], ["b"]])"sv;
@ -585,7 +593,48 @@ TEST_CASE("conformance - burntsushi/valid")
REQUIRE(tbl == expected);
});
parsing_should_succeed(FILE_LINE_ARGS, array_mix_string_table, [](toml::table&& tbl)
parsing_should_succeed(FILE_LINE_ARGS, array_mixed_int_array, [](toml::table&& tbl)
{
const auto expected = toml::table{{
{
R"(arrays-and-ints)"sv, toml::array{
1,
toml::array{
R"(Arrays are not integers.)"sv,
},
}
},
}};
REQUIRE(tbl == expected);
});
parsing_should_succeed(FILE_LINE_ARGS, array_mixed_int_float, [](toml::table&& tbl)
{
const auto expected = toml::table{{
{
R"(ints-and-floats)"sv, toml::array{
1,
1.1,
}
},
}};
REQUIRE(tbl == expected);
});
parsing_should_succeed(FILE_LINE_ARGS, array_mixed_int_string, [](toml::table&& tbl)
{
const auto expected = toml::table{{
{
R"(strings-and-ints)"sv, toml::array{
R"(hi)"sv,
42,
}
},
}};
REQUIRE(tbl == expected);
});
parsing_should_succeed(FILE_LINE_ARGS, array_mixed_string_table, [](toml::table&& tbl)
{
const auto expected = toml::table{{
{
@ -598,14 +647,27 @@ TEST_CASE("conformance - burntsushi/valid")
}},
}
},
}};
REQUIRE(tbl == expected);
});
parsing_should_succeed(FILE_LINE_ARGS, array_nested_double, [](toml::table&& tbl)
{
const auto expected = toml::table{{
{
R"(numbers)"sv, toml::array{
0.1,
0.2,
0.5,
1,
2,
5,
R"(nest)"sv, toml::array{
toml::inserter{toml::array{
toml::array{
R"(a)"sv,
},
toml::array{
1,
2,
toml::array{
3,
},
},
}},
}
},
}};

View File

@ -52,42 +52,56 @@ TEST_CASE("parsing - dates and times")
{
const auto val = date_time{ { 1987, 3, 16 }, { 10, 20, 30 } };
parse_expected_value(FILE_LINE_ARGS, "1987-03-16T10:20:30"sv, val);
parse_expected_value(FILE_LINE_ARGS, "1987-03-16t10:20:30"sv, val);
parse_expected_value(FILE_LINE_ARGS, "1987-03-16 10:20:30"sv, val);
}
{
const auto val = date_time{ { 1987, 3, 16 }, { 10, 20, 30 }, { -9, -30 } };
parse_expected_value(FILE_LINE_ARGS, "1987-03-16T10:20:30-09:30"sv, val);
parse_expected_value(FILE_LINE_ARGS, "1987-03-16t10:20:30-09:30"sv, val);
parse_expected_value(FILE_LINE_ARGS, "1987-03-16 10:20:30-09:30"sv, val);
}
{
const auto val = date_time{ { 1987, 3, 16 }, { 10, 20, 30 }, { 9, 30 } };
parse_expected_value(FILE_LINE_ARGS, "1987-03-16T10:20:30+09:30"sv, val);
parse_expected_value(FILE_LINE_ARGS, "1987-03-16t10:20:30+09:30"sv, val);
parse_expected_value(FILE_LINE_ARGS, "1987-03-16 10:20:30+09:30"sv, val);
}
{
const auto val = date_time{ { 1987, 3, 16 }, { 10, 20, 30, 40000000 } };
parse_expected_value(FILE_LINE_ARGS, "1987-03-16T10:20:30.04"sv, val);
parse_expected_value(FILE_LINE_ARGS, "1987-03-16t10:20:30.04"sv, val);
parse_expected_value(FILE_LINE_ARGS, "1987-03-16 10:20:30.04"sv, val);
}
{
const auto val = date_time{ { 1987, 3, 16 }, { 10, 20, 30, 40000000 }, { -9, -30 } };
parse_expected_value(FILE_LINE_ARGS, "1987-03-16T10:20:30.04-09:30"sv, val);
parse_expected_value(FILE_LINE_ARGS, "1987-03-16t10:20:30.04-09:30"sv, val);
parse_expected_value(FILE_LINE_ARGS, "1987-03-16 10:20:30.04-09:30"sv, val);
}
{
const auto val = date_time{ { 1987, 3, 16 }, { 10, 20, 30, 40000000 }, { 9, 30 } };
parse_expected_value(FILE_LINE_ARGS, "1987-03-16T10:20:30.04+09:30"sv, val);
parse_expected_value(FILE_LINE_ARGS, "1987-03-16t10:20:30.04+09:30"sv, val);
parse_expected_value(FILE_LINE_ARGS, "1987-03-16 10:20:30.04+09:30"sv, val);
}
{
const auto val = date_time{ { 1987, 3, 16 }, { 10, 20, 30 }, {} };
parse_expected_value(FILE_LINE_ARGS, "1987-03-16T10:20:30Z"sv, val);
parse_expected_value(FILE_LINE_ARGS, "1987-03-16t10:20:30Z"sv, val);
parse_expected_value(FILE_LINE_ARGS, "1987-03-16 10:20:30Z"sv, val);
parse_expected_value(FILE_LINE_ARGS, "1987-03-16T10:20:30z"sv, val);
parse_expected_value(FILE_LINE_ARGS, "1987-03-16t10:20:30z"sv, val);
parse_expected_value(FILE_LINE_ARGS, "1987-03-16 10:20:30z"sv, val);
}
{
const auto val = date_time{ { 1987, 3, 16 }, { 10, 20, 30, 40000000 }, {} };
parse_expected_value(FILE_LINE_ARGS, "1987-03-16T10:20:30.04Z"sv, val);
parse_expected_value(FILE_LINE_ARGS, "1987-03-16t10:20:30.04Z"sv, val);
parse_expected_value(FILE_LINE_ARGS, "1987-03-16 10:20:30.04Z"sv, val);
parse_expected_value(FILE_LINE_ARGS, "1987-03-16T10:20:30.04z"sv, val);
parse_expected_value(FILE_LINE_ARGS, "1987-03-16t10:20:30.04z"sv, val);
parse_expected_value(FILE_LINE_ARGS, "1987-03-16 10:20:30.04z"sv, val);
}
// toml/issues/671 (allow omission of seconds)

View File

@ -10434,7 +10434,7 @@ TOML_IMPL_NAMESPACE_START
{
if (is_eof()
|| is_value_terminator(*cp)
|| (part_of_datetime && is_match(*cp, U'+', U'-', U'Z')))
|| (part_of_datetime && is_match(*cp, U'+', U'-', U'Z', U'z')))
return time;
}
else
@ -10456,7 +10456,7 @@ TOML_IMPL_NAMESPACE_START
// '.' (early-exiting is allowed; fractional is optional)
if (is_eof()
|| is_value_terminator(*cp)
|| (part_of_datetime && is_match(*cp, U'+', U'-', U'Z')))
|| (part_of_datetime && is_match(*cp, U'+', U'-', U'Z', U'z')))
return time;
if (*cp != U'.')
set_error_and_return_default("expected '.', saw '"sv, to_sv(*cp), "'"sv);
@ -10504,9 +10504,9 @@ TOML_IMPL_NAMESPACE_START
auto date = parse_date(true);
set_error_and_return_if_eof({});
// ' ' or 'T'
if (!is_match(*cp, U' ', U'T'))
set_error_and_return_default("expected space or 'T', saw '"sv, to_sv(*cp), "'"sv);
// ' ', 'T' or 't'
if (!is_match(*cp, U' ', U'T', U't'))
set_error_and_return_default("expected space, 'T' or 't', saw '"sv, to_sv(*cp), "'"sv);
advance_and_return_if_error_or_eof({});
// "HH:MM:SS.FFFFFFFFF"
@ -10517,9 +10517,9 @@ TOML_IMPL_NAMESPACE_START
if (is_eof() || is_value_terminator(*cp))
return { date, time };
// zero offset ("Z")
// zero offset ('Z' or 'z')
time_offset offset;
if (*cp == U'Z')
if (is_match(*cp, U'Z', U'z'))
advance_and_return_if_error({});
// explicit offset ("+/-HH:MM")
@ -10852,21 +10852,20 @@ TOML_IMPL_NAMESPACE_START
// (as opposed to the fallback "could not determine type" message)
if (has_any(has_p))
val = new value{ parse_hex_float() };
else if (has_any(has_x))
else if (has_any(has_x | has_o | has_b))
{
val = new value{ parse_integer<16>() };
int64_t i;
if (has_any(has_x))
i = parse_integer<16>();
else if (has_any(has_o))
i = parse_integer<8>();
else // has_b
i = parse_integer<2>();
return_if_error({});
val = new value{ i };
reinterpret_cast<value<int64_t>*>(val.get())->flags(value_flags::format_as_hexadecimal);
}
else if (has_any(has_o))
{
val = new value{ parse_integer<8>() };
reinterpret_cast<value<int64_t>*>(val.get())->flags(value_flags::format_as_octal);
}
else if (has_any(has_b))
{
val = new value{ parse_integer<2>() };
reinterpret_cast<value<int64_t>*>(val.get())->flags(value_flags::format_as_binary);
}
else if (has_any(has_e) || (has_any(begins_zero | begins_digit) && chars[1] == U'.'))
val = new value{ parse_float() };
else if (has_any(begins_sign))

View File

@ -353,8 +353,6 @@ def load_valid_inputs(tests, extern_root):
def load_invalid_inputs(tests, extern_root):
tests['invalid']['burntsushi'] = load_tests(Path(extern_root, 'toml-test', 'tests', 'invalid'), False, (
# false negatives after TOML 0.4.0
re.compile('array-mixed.*'),
# these break IO/git/visual studio (i test them elsewhere)
re.compile('.*(bom|control).*'),
'encoding-utf16',