From a6c0b0bb6ab5f667e1f8cb049ba705ed7e86f8be Mon Sep 17 00:00:00 2001 From: Xeverous <20820409+Xeverous@users.noreply.github.com> Date: Fri, 3 Oct 2025 16:53:42 +0200 Subject: [PATCH] X3: rollback iterator when rule fails to parse This commit adjusts work of commit 2db3fde. It corrected error_handler which should not skip whitespaces but introduced a regression where handlers returning fail were still moving the iterator forward. This commit adds iterator rollback and synchronizes rule's and guard's iterators behavior. --- .../home/x3/nonterminal/detail/rule.hpp | 5 ++++- test/x3/rule4.cpp | 21 +++++++++++++++++++ 2 files changed, 25 insertions(+), 1 deletion(-) diff --git a/include/boost/spirit/home/x3/nonterminal/detail/rule.hpp b/include/boost/spirit/home/x3/nonterminal/detail/rule.hpp index b44a92400..422355c95 100644 --- a/include/boost/spirit/home/x3/nonterminal/detail/rule.hpp +++ b/include/boost/spirit/home/x3/nonterminal/detail/rule.hpp @@ -236,13 +236,16 @@ namespace boost { namespace spirit { namespace x3 { namespace detail { for (;;) { + Iterator it = first; + #if BOOST_SPIRIT_X3_THROW_EXPECTATION_FAILURE try #endif { if (parse_rhs_main( - rhs, first, last, context, rcontext, attr, mpl::false_())) + rhs, it, last, context, rcontext, attr, mpl::false_())) { + first = it; return true; } } diff --git a/test/x3/rule4.cpp b/test/x3/rule4.cpp index df04e5c09..4c4a983a7 100644 --- a/test/x3/rule4.cpp +++ b/test/x3/rule4.cpp @@ -12,6 +12,7 @@ #include #include #include +#include #include "test.hpp" namespace x3 = boost::spirit::x3; @@ -90,6 +91,8 @@ main() using boost::spirit::x3::rule; using boost::spirit::x3::int_; using boost::spirit::x3::lit; + using boost::spirit::x3::lexeme; + using boost::spirit::x3::eol; { // show that ra = rb and ra %= rb works as expected rule ra; @@ -144,6 +147,8 @@ main() auto r = rule() = '(' > int_ > ',' > int_ > ')'; + got_it = 0; + BOOST_TEST(test("(123,456)", r)); BOOST_TEST(!test("(abc,def)", r)); BOOST_TEST(!test("(123,456]", r)); @@ -153,6 +158,22 @@ main() BOOST_TEST(got_it == 1); } + { // regression test for #833 + // rules which return error_handler_result::fail should trigger iterator rollback + + auto string_literal = rule() + = lexeme['"' > *~char_("\"\n\r") > '"']; + auto r = rule>() + = *string_literal > eol; + + got_it = 0; + + BOOST_TEST(test("\"abc\"\n", r)); + BOOST_TEST_THROWS(test("\"abc\n", r), x3::expectation_failure); + + BOOST_TEST(got_it == 1); + } + { // on_success gets pre-skipped iterator auto r = rule() = lit("b");