diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md index f074427..f04197a 100644 --- a/.github/ISSUE_TEMPLATE/bug_report.md +++ b/.github/ISSUE_TEMPLATE/bug_report.md @@ -1,8 +1,8 @@ --- name: Bug report -about: Found a bug? Help me squash it. +about: Regular ol' bugs. title: '' -labels: bug +labels: [ "bug" ] assignees: marzer --- diff --git a/.github/ISSUE_TEMPLATE/spec_bug_report.md b/.github/ISSUE_TEMPLATE/spec_bug_report.md new file mode 100644 index 0000000..fbb3aea --- /dev/null +++ b/.github/ISSUE_TEMPLATE/spec_bug_report.md @@ -0,0 +1,55 @@ +--- +name: TOML spec conformance bug +about: Bugs relating to the library's TOML spec conformance (or lack thereof). +title: '' +labels: [ "bug", "TOML spec" ] +assignees: marzer + +--- + + + + +## The non-conforming TOML snippet +```toml + +# your TOML here + +``` + + +## What you expected + + + +## What you got + + + +## Environment +**toml++ version and/or commit hash:** + + +**Any other useful information:** + diff --git a/CHANGELOG.md b/CHANGELOG.md index 4a0e1cd..f327f87 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -35,9 +35,8 @@ code changes at callsites or in build systems are indicated with ⚠️. - 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) (@moorereason) -- fixed extended-precision fractional times causing parse error instead of truncating per the spec (#127) (@moorereason) -- fixed some non-spec vertical whitespace being accepted as line breaks (#128) (@moorereason) +- fixed some incorrect unicode scalar sequence transformations (#125) +- fixed a number of spec conformance issues (#127, #128, #129) (@moorereason) #### Additions: - added `operator->` to `toml::value` for class types diff --git a/include/toml++/impl/formatter.inl b/include/toml++/impl/formatter.inl index ce26391..ac7f979 100644 --- a/include/toml++/impl/formatter.inl +++ b/include/toml++/impl/formatter.inl @@ -23,7 +23,7 @@ TOML_IMPL_NAMESPACE_START { - enum class formatted_string_traits : unsigned + enum class TOML_CLOSED_FLAGS_ENUM formatted_string_traits : unsigned { none, line_breaks = 1u << 0, // \n diff --git a/include/toml++/impl/forward_declarations.h b/include/toml++/impl/forward_declarations.h index d6d25e9..d9639c6 100644 --- a/include/toml++/impl/forward_declarations.h +++ b/include/toml++/impl/forward_declarations.h @@ -264,7 +264,7 @@ TOML_NAMESPACE_START // abi namespace } /// \brief Metadata associated with TOML values. - enum class TOML_OPEN_FLAGS_ENUM value_flags : uint16_t + enum class TOML_OPEN_FLAGS_ENUM value_flags : uint16_t // being an "OPEN" flags enum is not an error { /// \brief None. none, diff --git a/include/toml++/impl/parser.inl b/include/toml++/impl/parser.inl index 66137d4..e3c15c1 100644 --- a/include/toml++/impl/parser.inl +++ b/include/toml++/impl/parser.inl @@ -2088,6 +2088,9 @@ TOML_IMPL_NAMESPACE_START if (*cp != traits::prefix_codepoint) set_error_and_return_default("expected '"sv, traits::prefix, "', saw '"sv, to_sv(*cp), "'"sv); advance_and_return_if_error_or_eof({}); + + if (!traits::is_digit(*cp)) + set_error_and_return_default("expected digit, saw '"sv, to_sv(*cp), "'"sv); } // consume value chars @@ -2400,7 +2403,6 @@ TOML_IMPL_NAMESPACE_START node_ptr parse_inline_table(); TOML_NODISCARD - TOML_NEVER_INLINE node_ptr parse_value_known_prefixes() { return_if_error({}); @@ -2505,8 +2507,8 @@ TOML_IMPL_NAMESPACE_START begins_zero = 1 << 14, signs_msk = has_plus | has_minus, - bzero_msk = begins_zero | has_digits, - bdigit_msk = begins_digit | has_digits, + bdigit_msk = has_digits | begins_digit, + bzero_msk = bdigit_msk | begins_zero, }; value_traits traits = has_nothing; const auto has_any = [&](auto t) noexcept { return (traits & t) != has_nothing; }; @@ -2516,7 +2518,11 @@ TOML_IMPL_NAMESPACE_START // examine the first character to get the 'begins with' traits // (good fail-fast opportunity; all the remaining types begin with numeric digits or signs) if (is_decimal_digit(*cp)) - add_trait(*cp == U'0' ? begins_zero : begins_digit); + { + add_trait(begins_digit); + if (*cp == U'0') + add_trait(begins_zero); + } else if (is_match(*cp, U'+', U'-')) add_trait(begins_sign); else @@ -2601,8 +2607,12 @@ TOML_IMPL_NAMESPACE_START return_if_error({}); // force further scanning if this could have been a date-time with a space instead of a T - if (char_count == 10u && traits == (bdigit_msk | has_minus) && chars[4] == U'-' && chars[7] == U'-' - && !is_eof() && *cp == U' ') + if (char_count == 10u // + && (traits | begins_zero) == (bzero_msk | has_minus) // + && chars[4] == U'-' // + && chars[7] == U'-' // + && !is_eof() // + && *cp == U' ') { const auto pre_advance_count = advance_count; const auto pre_scan_traits = traits; @@ -2646,7 +2656,7 @@ TOML_IMPL_NAMESPACE_START // the only valid value type is an integer. if (char_count == 1u) { - if (has_any(begins_zero | begins_digit)) + if (has_any(begins_digit)) { val.reset(new value{ static_cast(chars[0] - U'0') }); advance(); // skip the digit @@ -2692,7 +2702,7 @@ TOML_IMPL_NAMESPACE_START val.reset(new value{ i }); val->ref_cast().flags(flags); } - else if (has_any(has_e) || (has_any(begins_zero | begins_digit) && chars[1] == U'.')) + else if (has_any(has_e) || (has_any(begins_digit) && chars[1] == U'.')) val.reset(new value{ parse_float() }); else if (has_any(begins_sign)) { diff --git a/tests/parsing_integers.cpp b/tests/parsing_integers.cpp index 10e550e..582517e 100644 --- a/tests/parsing_integers.cpp +++ b/tests/parsing_integers.cpp @@ -152,6 +152,11 @@ TEST_CASE("parsing - integers (hex, bin, oct)") parsing_should_fail(FILE_LINE_ARGS, "val = 0o1000000000000000000000"sv); parsing_should_fail(FILE_LINE_ARGS, "val = 0b1000000000000000000000000000000000000000000000000000000000000000"sv); + // missing values after base prefix + parsing_should_fail(FILE_LINE_ARGS, "val = 0x "sv); + parsing_should_fail(FILE_LINE_ARGS, "val = 0o "sv); + parsing_should_fail(FILE_LINE_ARGS, "val = 0b "sv); + // value tests parse_expected_value(FILE_LINE_ARGS, "0xDEADBEEF"sv, 0xDEADBEEF); parse_expected_value(FILE_LINE_ARGS, "0xdeadbeef"sv, 0xDEADBEEF); diff --git a/tests/user_feedback.cpp b/tests/user_feedback.cpp index aaf8d61..93c6a6a 100644 --- a/tests/user_feedback.cpp +++ b/tests/user_feedback.cpp @@ -230,4 +230,21 @@ b = [] parsing_should_succeed(FILE_LINE_ARGS, "\t"sv); parsing_should_succeed(FILE_LINE_ARGS, "\n"sv); } + + SECTION("github/issues/129") // https://github.com/marzer/tomlplusplus/issues/129 + { + parsing_should_fail(FILE_LINE_ARGS, R"( + hex = 0x + oct = 0o + bin = 0b + )"sv); + } + + SECTION("github/issues/130") // https://github.com/marzer/tomlplusplus/issues/130 + { + parse_expected_value(FILE_LINE_ARGS, "0400-01-01 00:00:00"sv, toml::date_time{ { 400, 1, 1 }, { 0, 0, 0 } }); + parse_expected_value(FILE_LINE_ARGS, "0400-01-01 "sv, toml::date{ 400, 1, 1 }); + parse_expected_value(FILE_LINE_ARGS, "0400-01-01T00:00:00"sv, toml::date_time{ { 400, 1, 1 }, { 0, 0, 0 } }); + parse_expected_value(FILE_LINE_ARGS, "1000-01-01 00:00:00"sv, toml::date_time{ { 1000, 1, 1 }, { 0, 0, 0 } }); + } } diff --git a/toml++.vcxproj b/toml++.vcxproj index cc04f39..92fcf99 100644 --- a/toml++.vcxproj +++ b/toml++.vcxproj @@ -89,6 +89,7 @@ + diff --git a/toml++.vcxproj.filters b/toml++.vcxproj.filters index 14ef797..0cdb0f8 100644 --- a/toml++.vcxproj.filters +++ b/toml++.vcxproj.filters @@ -217,6 +217,9 @@ include\impl + + .github + diff --git a/toml.hpp b/toml.hpp index 9f453ad..ddcaf40 100644 --- a/toml.hpp +++ b/toml.hpp @@ -1242,7 +1242,7 @@ TOML_NAMESPACE_START // abi namespace } } - enum class TOML_OPEN_FLAGS_ENUM value_flags : uint16_t + enum class TOML_OPEN_FLAGS_ENUM value_flags : uint16_t // being an "OPEN" flags enum is not an error { none, format_as_binary = 1, @@ -12698,6 +12698,9 @@ TOML_IMPL_NAMESPACE_START if (*cp != traits::prefix_codepoint) set_error_and_return_default("expected '"sv, traits::prefix, "', saw '"sv, to_sv(*cp), "'"sv); advance_and_return_if_error_or_eof({}); + + if (!traits::is_digit(*cp)) + set_error_and_return_default("expected digit, saw '"sv, to_sv(*cp), "'"sv); } // consume value chars @@ -13010,7 +13013,6 @@ TOML_IMPL_NAMESPACE_START node_ptr parse_inline_table(); TOML_NODISCARD - TOML_NEVER_INLINE node_ptr parse_value_known_prefixes() { return_if_error({}); @@ -13114,8 +13116,8 @@ TOML_IMPL_NAMESPACE_START begins_digit = 1 << 13, begins_zero = 1 << 14, signs_msk = has_plus | has_minus, - bzero_msk = begins_zero | has_digits, - bdigit_msk = begins_digit | has_digits, + bdigit_msk = has_digits | begins_digit, + bzero_msk = bdigit_msk | begins_zero, }; value_traits traits = has_nothing; const auto has_any = [&](auto t) noexcept { return (traits & t) != has_nothing; }; @@ -13125,7 +13127,11 @@ TOML_IMPL_NAMESPACE_START // examine the first character to get the 'begins with' traits // (good fail-fast opportunity; all the remaining types begin with numeric digits or signs) if (is_decimal_digit(*cp)) - add_trait(*cp == U'0' ? begins_zero : begins_digit); + { + add_trait(begins_digit); + if (*cp == U'0') + add_trait(begins_zero); + } else if (is_match(*cp, U'+', U'-')) add_trait(begins_sign); else @@ -13210,8 +13216,12 @@ TOML_IMPL_NAMESPACE_START return_if_error({}); // force further scanning if this could have been a date-time with a space instead of a T - if (char_count == 10u && traits == (bdigit_msk | has_minus) && chars[4] == U'-' && chars[7] == U'-' - && !is_eof() && *cp == U' ') + if (char_count == 10u // + && (traits | begins_zero) == (bzero_msk | has_minus) // + && chars[4] == U'-' // + && chars[7] == U'-' // + && !is_eof() // + && *cp == U' ') { const auto pre_advance_count = advance_count; const auto pre_scan_traits = traits; @@ -13255,7 +13265,7 @@ TOML_IMPL_NAMESPACE_START // the only valid value type is an integer. if (char_count == 1u) { - if (has_any(begins_zero | begins_digit)) + if (has_any(begins_digit)) { val.reset(new value{ static_cast(chars[0] - U'0') }); advance(); // skip the digit @@ -13301,7 +13311,7 @@ TOML_IMPL_NAMESPACE_START val.reset(new value{ i }); val->ref_cast().flags(flags); } - else if (has_any(has_e) || (has_any(begins_zero | begins_digit) && chars[1] == U'.')) + else if (has_any(has_e) || (has_any(begins_digit) && chars[1] == U'.')) val.reset(new value{ parse_float() }); else if (has_any(begins_sign)) { @@ -14403,7 +14413,7 @@ TOML_PUSH_WARNINGS; TOML_IMPL_NAMESPACE_START { - enum class formatted_string_traits : unsigned + enum class TOML_CLOSED_FLAGS_ENUM formatted_string_traits : unsigned { none, line_breaks = 1u << 0, // \n