From c674e94c3d409b6ab0ee05429361e8b50fe30320 Mon Sep 17 00:00:00 2001 From: Zach Laine Date: Sun, 27 Jul 2025 17:50:38 -0500 Subject: [PATCH] Don't reuse the attribute-generating path in rule_parser's out-param overload of call. Doing so was wiping out previous partial results, in cases like foo >> bar, where foo produces a T, and bar is a rule that produces vector. Fixes #248. --- include/boost/parser/parser.hpp | 25 ++++++++++++-- test/github_issues.cpp | 58 +++++++++++++++++++++++++++++++++ 2 files changed, 80 insertions(+), 3 deletions(-) diff --git a/include/boost/parser/parser.hpp b/include/boost/parser/parser.hpp index 09ea1bc9..c701ca49 100644 --- a/include/boost/parser/parser.hpp +++ b/include/boost/parser/parser.hpp @@ -5411,9 +5411,28 @@ namespace boost { namespace parser { if constexpr (CanUseCallbacks && Context::use_callbacks) { call(first, last, context, skip, flags, success); } else { - auto attr = call(first, last, context, skip, flags, success); - if (success) - detail::assign(retval, std::move(attr)); + locals_type locals = detail::make_locals(context); + auto params = detail::resolve_rule_params(context, params_); + tag_type * const tag_ptr = nullptr; + auto const rule_context = detail::make_rule_context( + context, tag_ptr, retval, locals, params); + + [[maybe_unused]] auto _ = detail::scoped_trace( + *this, first, last, rule_context, flags, retval); + + bool dont_assign = false; + parse_rule( + tag_ptr, + first, + last, + rule_context, + skip, + flags, + success, + dont_assign, + retval); + if (!success || dont_assign) + retval = Attribute_(); } } diff --git a/test/github_issues.cpp b/test/github_issues.cpp index 41701309..1aab4af2 100644 --- a/test/github_issues.cpp +++ b/test/github_issues.cpp @@ -287,6 +287,63 @@ void github_issue_223() } } +namespace github_issue_248_ { + namespace bp = boost::parser; + + static constexpr bp::rule symbol = "//"; + static constexpr bp::rule> list = + "(,)*"; + static constexpr bp::rule> working = + "working"; + static constexpr bp::rule> failing = + "failing"; + + static auto const symbol_def = bp::symbols{{"//", 0}}; + static constexpr auto list_def = bp::int_ % ','; + static constexpr auto working_def = -symbol >> (bp::int_ % ','); + static constexpr auto failing_def = -symbol >> list; + + BOOST_PARSER_DEFINE_RULES(symbol, list, working, failing); +} + +void github_issue_248() +{ + namespace bp = boost::parser; + + using namespace github_issue_248_; + + { + auto const result = bp::parse("//1,2,3", working, bp::ws); + auto const expected = std::vector{0, 1, 2, 3}; + BOOST_TEST(result.has_value()); + bool const equal = std::equal( + result->begin(), result->end(), expected.begin(), expected.end()); + BOOST_TEST(equal); + if (!equal) { + std::cout << "contents of *result:\n"; + for (auto x : *result) { + std::cout << x << '\n'; + } + std::cout << '\n'; + } + } + { + auto const result = bp::parse("//1,2,3", failing, bp::ws); + auto const expected = std::vector{0, 1, 2, 3}; + BOOST_TEST(result.has_value()); + bool const equal = std::equal( + result->begin(), result->end(), expected.begin(), expected.end()); + BOOST_TEST(equal); + if (!equal) { + std::cout << "contents of *result:\n"; + for (auto x : *result) { + std::cout << x << '\n'; + } + std::cout << '\n'; + } + } +} + int main() { @@ -298,5 +355,6 @@ int main() github_issue_125(); github_issue_209(); github_issue_223(); + github_issue_248(); return boost::report_errors(); }