From f487e1fad499d935d6f20845c10685df575cd46d Mon Sep 17 00:00:00 2001 From: Andreas Buhr Date: Tue, 3 Feb 2026 13:54:58 +0100 Subject: [PATCH] Allow move-only attributes This tests the usage of move-only attributes. parser.hpp and printing.hpp are slightly modified to make it work. --- include/boost/parser/detail/printing.hpp | 2 +- include/boost/parser/parser.hpp | 10 +- test/github_issues.cpp | 181 +++++++++++++++++++++++ 3 files changed, 187 insertions(+), 6 deletions(-) diff --git a/include/boost/parser/detail/printing.hpp b/include/boost/parser/detail/printing.hpp index 52facf7b..e5dfe2f2 100644 --- a/include/boost/parser/detail/printing.hpp +++ b/include/boost/parser/detail/printing.hpp @@ -593,7 +593,7 @@ namespace boost { namespace parser { namespace detail { } template - auto resolve(Context const & context, T const & x); + decltype(auto) resolve(Context const & context, T const & x); template auto resolve(Context const &, nope n); diff --git a/include/boost/parser/parser.hpp b/include/boost/parser/parser.hpp index caf72e85..0446cb62 100644 --- a/include/boost/parser/parser.hpp +++ b/include/boost/parser/parser.hpp @@ -1020,7 +1020,7 @@ namespace boost { namespace parser { bool Callable = is_detected_v> struct resolve_impl { - static auto call(Context const &, T const & x) { return x; } + static auto& call(Context const &, T const & x) { return x; } }; template @@ -1033,7 +1033,7 @@ namespace boost { namespace parser { }; template - auto resolve(Context const & context, T const & x) + decltype(auto) resolve(Context const & context, T const & x) { return resolve_impl::call(context, x); } @@ -4328,7 +4328,7 @@ namespace boost { namespace parser { // A 1-tuple is converted to a scalar. if constexpr (detail::hl::size(retval) == llong<1>{}) { using namespace literals; - return parser::get(retval, 0_c); + return parser::get(std::move(retval), 0_c); } else { return retval; } @@ -5485,7 +5485,7 @@ namespace boost { namespace parser { dont_assign); if (success && !dont_assign) { if constexpr (!detail::is_nope_v) - detail::assign(retval, attr); + detail::assign(retval, std::move(attr)); } } @@ -5580,7 +5580,7 @@ namespace boost { namespace parser { container && container) { detail::move_back(retval, attr, detail::gen_attrs(flags)); } else { - detail::assign(retval, attr); + detail::assign(retval, std::move(attr)); } } } diff --git a/test/github_issues.cpp b/test/github_issues.cpp index b36d872c..03dd3099 100644 --- a/test/github_issues.cpp +++ b/test/github_issues.cpp @@ -597,6 +597,186 @@ void github_pr_297() } } +namespace github_issue_312_ { + /* + * Recursive descent parser for expressions. + * Supports addition (+), multiplication (*) and + * parethesized expressions, nothing else. + * + * Creates a tree of "evaluatable" objects which + * own their downstream objects in a unique_ptr + */ + + // base class for all tree nodes + struct evaluatable + { + virtual double evaluate() = 0; + virtual ~evaluatable() = default; + }; + + namespace bp = boost::parser; + + // top level parser + constexpr bp::rule> expression_parser = "expression_parser"; + + /* + * LITERAL EXPRESSION + */ + struct literal_evaluatable : evaluatable + { + explicit literal_evaluatable(double v) : value_(v) {} + double evaluate() override + { + return value_; + } + double value_; + }; + constexpr bp::rule> literal_parser = "literal_parser"; + constexpr auto literal_parser_action = [](auto& ctx) { + std::unique_ptr& val = _val(ctx); + double& parsed_value = _attr(ctx); + val = std::make_unique(parsed_value); + }; + constexpr auto literal_parser_def = + bp::double_[literal_parser_action]; + + /* + * PARENTHESIZED EXPRESSION + */ + struct parenthesized_evaluatable : evaluatable + { + explicit parenthesized_evaluatable(std::unique_ptr&& e) : evaluatable_(std::move(e)) {} + double evaluate() override + { + return evaluatable_->evaluate(); + } + std::unique_ptr evaluatable_; + }; + constexpr bp::rule> parenthesized_parser = "parenthesized_parser"; + constexpr auto parenthesized_action = [](auto& ctx) { + std::unique_ptr& val = _val(ctx); + std::unique_ptr& attr = _attr(ctx); + val = std::make_unique(std::move(attr)); + }; + constexpr auto parenthesized_parser_def = + ( + bp::lit('(') > expression_parser > bp::lit(')') + )[parenthesized_action]; + + /* + * ATOM EXPRESSION + */ + struct atom_evaluatable : evaluatable + { + explicit atom_evaluatable(std::unique_ptr&& e) : evaluatable_(std::move(e)) {} + double evaluate() override + { + return evaluatable_->evaluate(); + } + std::unique_ptr evaluatable_; + }; + constexpr bp::rule> atom_parser = "atom_parser"; + constexpr auto atom_action = [](auto& ctx) { + std::unique_ptr& val = _val(ctx); + std::unique_ptr& attr = _attr(ctx); + val = std::make_unique(std::move(attr)); + }; + constexpr auto atom_parser_def = + ( + parenthesized_parser + | + literal_parser + )[atom_action]; + + /* + * MULTIPLICATION EXPRESSION + */ + struct multiplication_evaluatable : evaluatable + { + multiplication_evaluatable(std::vector>&& e) + : evaluatables_(std::move(e)) + {} + double evaluate() override + { + double result = 1; + for (const auto& e : evaluatables_) { + result *= e->evaluate(); + } + return result; + } + std::vector> evaluatables_; + }; + constexpr bp::rule> mult_parser = "mult_parser"; + constexpr auto mult_parser_action = [](auto& ctx) { + std::unique_ptr& val = _val(ctx); + std::vector>& operands = _attr(ctx); + val = std::make_unique(std::move(operands)); + }; + constexpr auto mult_parser_def = + (atom_parser % bp::lit('*'))[mult_parser_action]; + + /* + * ADDITION EXPRESSION + */ + struct addition_evaluatable : evaluatable + { + addition_evaluatable(std::vector>&& e) + : evaluatables_(std::move(e)) + {} + double evaluate() override + { + double result = 0; + for (const auto& e : evaluatables_) { + result += e->evaluate(); + } + return result; + } + std::vector> evaluatables_; + }; + constexpr bp::rule> add_parser = "add_parser"; + constexpr auto add_parser_action = [](auto& ctx) { + std::unique_ptr& val = _val(ctx); + std::vector>& operands = _attr(ctx); + val = std::make_unique(std::move(operands)); + }; + constexpr auto add_parser_def = + (mult_parser % bp::lit('+'))[add_parser_action]; + + constexpr auto expression_parser_action = [](auto& ctx) { + std::unique_ptr& val = _val(ctx); + std::unique_ptr& attr = _attr(ctx); + val = std::move(attr); + }; + + /* + * EXPRESSION + */ + constexpr auto expression_parser_def = + add_parser[expression_parser_action]; + + BOOST_PARSER_DEFINE_RULES( + literal_parser, + mult_parser, + add_parser, + expression_parser, + parenthesized_parser, + atom_parser); +} + +void github_issue_312() +{ + namespace bp = boost::parser; + using namespace github_issue_312_; + + auto result = bp::parse("(2 + 3) + 3.1415 * 2", expression_parser, bp::blank); + BOOST_TEST(result); + BOOST_TEST(result.value()->evaluate() == (2 + 3) + 3.1415 * 2); + + result = bp::parse("((2*0.1) + 33.) * (2 + 3) + 3.1415 * 2", expression_parser, bp::blank); + BOOST_TEST(result); + BOOST_TEST(result.value()->evaluate() == ((2*0.1) + 33.) * (2 + 3) + 3.1415 * 2); +} + int main() { @@ -615,5 +795,6 @@ int main() github_pr_290(); github_issue_294(); github_pr_297(); + github_issue_312(); return boost::report_errors(); }