From 0eacce6080e65486c62bc359184a09def22a1706 Mon Sep 17 00:00:00 2001 From: Zach Laine Date: Fri, 31 Oct 2025 15:29:47 -0500 Subject: [PATCH] Add special-casing of nope attributes in seq_parser, so that seq_parser does not assign over valid values in a sequence's attribute after successfully parsing a subsquent nope-attributes parser. Test cases by Andreas Buhr. Fixes #279 Fixes #285 --- include/boost/parser/parser.hpp | 9 ++- test/github_issues.cpp | 128 +++++++++++++++++++++++++++----- 2 files changed, 114 insertions(+), 23 deletions(-) diff --git a/include/boost/parser/parser.hpp b/include/boost/parser/parser.hpp index 8cdea90c..4b9562ea 100644 --- a/include/boost/parser/parser.hpp +++ b/include/boost/parser/parser.hpp @@ -4533,7 +4533,8 @@ namespace boost { namespace parser { if constexpr ( (out_container == attr_container && - !was_merged_into_adjacent_container) || + !was_merged_into_adjacent_container && + !detail::is_nope_v) || is_in_a_group) { parser.call( first, last, context, skip, flags, success, out); @@ -4559,7 +4560,9 @@ namespace boost { namespace parser { } using just_x = attr_t; using just_out = detail::remove_cv_ref_t; - if constexpr ( + if constexpr (detail::is_nope_v) { + // nothing to do + } if constexpr ( (!out_container || !std::is_same_v) && std::is_assignable_v && @@ -4732,7 +4735,7 @@ namespace boost { namespace parser { } else { // If you see an error here, it's because you are using an // invocable for a semantic action that returns a non-void - // type Ret, but values fo type Ret is not assignable to + // type Ret, but values of type Ret is not assignable to // _val(ctx). To fix this, only use this invocable within // a rule whose attribute type is assignable from Ret, or // remove the non-void return statement(s) from your diff --git a/test/github_issues.cpp b/test/github_issues.cpp index b4fd52b9..7f572eac 100644 --- a/test/github_issues.cpp +++ b/test/github_issues.cpp @@ -348,22 +348,36 @@ void github_issue_248() namespace github_issue_268_ { namespace bp = boost::parser; constexpr bp::rule name = "name"; - auto name_def = bp::string_view[bp::lexeme[+(bp::lower | bp::upper | bp::digit | bp::char_("_"))]]; + auto name_def = bp::string_view[bp::lexeme[+( + bp::lower | bp::upper | bp::digit | bp::char_("_"))]]; BOOST_PARSER_DEFINE_RULES(name) constexpr bp::rule> qd_vec = "qd_vec"; - auto qd_vec_def = bp::lit("\"") >> bp::double_ % (bp::lit(",") | (bp::lit("\"") >> bp::lit(",") >> bp::lit("\""))) >> bp::lit('\"'); + auto qd_vec_def = bp::lit("\"") >> + bp::double_ % + (bp::lit(",") | + (bp::lit("\"") >> bp::lit(",") >> bp::lit("\""))) >> + bp::lit('\"'); BOOST_PARSER_DEFINE_RULES(qd_vec) struct lu_table_template_1 { std::vector index_1; std::string_view variable_1; }; - constexpr boost::parser::rule lu_table_template_1_rule = "lu_table_template_1"; - auto lu_table_template_1_rule_def = (bp::lit("index_1") >> '(' >> qd_vec >> ')' >> ';') >> (bp::lit("variable_1") >> ':' >> name >> ';'); + constexpr boost::parser:: + rule + lu_table_template_1_rule = "lu_table_template_1"; + auto lu_table_template_1_rule_def = (bp::lit("index_1") >> '(' >> qd_vec >> + ')' >> ';') >> + (bp::lit("variable_1") >> ':' >> name >> + ';'); BOOST_PARSER_DEFINE_RULES(lu_table_template_1_rule) - constexpr boost::parser::rule lu_table_template_1_permut_rule = "lu_table_template_1"; - auto lu_table_template_1_permut_rule_def = (bp::lit("index_1") >> '(' >> qd_vec >> ')' >> ';') || (bp::lit("variable_1") >> ':' >> name >> ';'); + constexpr boost::parser:: + rule + lu_table_template_1_permut_rule = "lu_table_template_1"; + auto lu_table_template_1_permut_rule_def = + (bp::lit("index_1") >> '(' >> qd_vec >> ')' >> ';') || + (bp::lit("variable_1") >> ':' >> name >> ';'); BOOST_PARSER_DEFINE_RULES(lu_table_template_1_permut_rule) } #endif @@ -377,22 +391,32 @@ void github_issue_268() auto const def_result = bp::parse( inputstring, lu_table_template_1_rule_def, bp::blank, bp::trace::off); - std::cout<< "seq_parser generates this type:\n" << typeid(def_result.value()).name() << std::endl; + std::cout << "seq_parser generates this type:\n" + << typeid(def_result.value()).name() << std::endl; BOOST_TEST(def_result); auto const permut_def_result = bp::parse( - inputstring, lu_table_template_1_permut_rule_def, bp::blank, bp::trace::off); - std::cout<< "permut_parser generates this type:\n" << typeid(permut_def_result.value()).name() << std::endl; + inputstring, + lu_table_template_1_permut_rule_def, + bp::blank, + bp::trace::off); + std::cout << "permut_parser generates this type:\n" + << typeid(permut_def_result.value()).name() << std::endl; BOOST_TEST(permut_def_result); auto const result = bp::parse( inputstring, lu_table_template_1_rule, bp::blank, bp::trace::off); - std::cout<< "seq_parser in rule generates this type:\n" << typeid(result.value()).name() << std::endl; + std::cout << "seq_parser in rule generates this type:\n" + << typeid(result.value()).name() << std::endl; BOOST_TEST(result); auto const permut_result = bp::parse( - inputstring, lu_table_template_1_permut_rule, bp::blank, bp::trace::off); - std::cout<< "permut_parser generates this type:\n" << typeid(permut_result.value()).name() << std::endl; + inputstring, + lu_table_template_1_permut_rule, + bp::blank, + bp::trace::off); + std::cout << "permut_parser generates this type:\n" + << typeid(permut_result.value()).name() << std::endl; BOOST_TEST(permut_result); #endif } @@ -401,17 +425,80 @@ void github_issue_279() { namespace bp = boost::parser; - constexpr auto condition_clause = bp::lit(U"while") > - bp::lit(U"someexpression") >> bp::attr(true); + { + constexpr auto condition_clause = + bp::lit(U"while") > bp::lit(U"someexpression") >> bp::attr(true); - constexpr auto do_statement = - bp::lexeme[bp::lit(U"do") >> &bp::ws] > -condition_clause > bp::eol; + constexpr auto do_statement = + bp::lexeme[bp::lit(U"do") >> &bp::ws] > -condition_clause > bp::eol; - auto const result = bp::parse( - U"do\n", do_statement, bp::blank, bp::trace::off); + auto const result = + bp::parse(U"do\n", do_statement, bp::blank, bp::trace::off); + BOOST_TEST(result); + std::optional const & condition = result.value(); + BOOST_TEST(!condition.has_value()); + } + + { + constexpr auto condition_clause = + bp::lit(U"while") > bp::lit(U"someexpression") >> bp::attr(true); + + constexpr auto do_statement_reverse = + -condition_clause > bp::lexeme[bp::lit(U"do") >> &bp::ws] > bp::eol; + + auto const result = + bp::parse(U"do\n", do_statement_reverse, bp::blank, bp::trace::off); + BOOST_TEST(result); + std::optional const & condition = result.value(); + BOOST_TEST(!condition.has_value()); + } +} + +namespace github_issue_285_ { + namespace bp = boost::parser; + + struct Content + { + ~Content() + { + int setbreakpointhere = 0; + (void)setbreakpointhere; + } + }; + constexpr bp::rule> content = + "content"; + constexpr auto content_action = [](auto & ctx) { + std::shared_ptr & result = _val(ctx); + result = std::make_shared(); + }; + constexpr auto content_def = + (bp::lit(U"content") >> bp::eol)[content_action]; + BOOST_PARSER_DEFINE_RULES(content); +} + +void github_issue_285() +{ + using namespace github_issue_285_; + namespace bp = boost::parser; + + constexpr auto prolog = bp::lit(U"prolog") >> bp::eol; + + constexpr auto epilog = + bp::no_case[bp::lexeme[bp::lit(U"epi") >> bp::lit(U"log")]] >> bp::eol; + + constexpr auto full_parser = prolog >> content >> epilog; + + std::string teststring = + "prolog\n" + "content\n" + "epilog\n"; + + // "content" produces a shared_ptr with the result. + // The "epilog" parser must not delete the result. + + auto const result = bp::parse(teststring, full_parser, bp::blank); BOOST_TEST(result); - std::optional const & condition = result.value(); - BOOST_TEST(!condition.has_value()); + BOOST_TEST(result.value().get() != nullptr); } @@ -428,5 +515,6 @@ int main() github_issue_248(); github_issue_268(); github_issue_279(); + github_issue_285(); return boost::report_errors(); }