/*============================================================================= Copyright (c) 2025 Nana Sakisaka Distributed under the Boost Software License, Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) =============================================================================*/ #include "test.hpp" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include // NOLINTBEGIN(readability-container-size-empty) TEST_CASE("as") { using namespace std::string_view_literals; using x4::eps; using x4::string; using x4::_attr; using x4::_rule_var; using x4::_as_var; using It = std::string_view::const_iterator; using Se = It; using Context = unused_type; // as { constexpr auto p = x4::as(eps); using Underlying = std::remove_const_t; using AsParser = std::remove_const_t; static_assert(std::same_as::attribute_type, unused_type>); static_assert(x4::is_nothrow_parsable_v); static_assert(x4::is_nothrow_parsable_v); // Arbitrary exposed attribute static_assert(x4::is_nothrow_parsable_v); static_assert(x4::is_nothrow_parsable_v); // Arbitrary exposed attribute std::string_view input; It first = input.begin(); Se const last = input.end(); std::string attr; (void)p.parse(first, last, unused, unused); (void)p.parse(first, last, unused, attr); } // as { constexpr auto p = x4::as(eps); using Underlying = std::remove_const_t; using AsParser = std::remove_const_t; static_assert(std::same_as::attribute_type, unused_type>); static_assert(x4::is_nothrow_parsable_v); static_assert(x4::is_nothrow_parsable_v); // Arbitrary exposed attribute static_assert(x4::is_nothrow_parsable_v); static_assert(x4::is_nothrow_parsable_v); // Arbitrary exposed attribute std::string_view input; It first = input.begin(); Se const last = input.end(); long attr = 0; (void)p.parse(first, last, unused, unused); (void)p.parse(first, last, unused, attr); } constexpr auto disable_attr = eps[([](auto&) {})]; constexpr auto quoted_string = '\'' >> *~x4::char_('\'') >> '\''; // `as` only { std::string attr; REQUIRE(parse("'foo'", quoted_string, attr)); CHECK(attr == "foo"sv); } { std::string attr; REQUIRE(parse("'foo'", x4::as(quoted_string), attr)); CHECK(attr == "foo"sv); } { std::string attr; REQUIRE(parse("'fo", quoted_string | x4::string("'fo"), attr)); CHECK(attr == "'fo"sv); } { std::string attr; REQUIRE(parse("'fo", x4::as(quoted_string) | x4::string("'fo"), attr)); CHECK(attr == "'fo"sv); } // `as` + `rule` { constexpr x4::rule rule_maker{"rule_maker"}; // Non-forced attribute, `operator=` { // Attribute is disabled because the sub parser has semantic action and the operator is `=` constexpr auto rule_without_attr = rule_maker = quoted_string >> disable_attr; std::string str; REQUIRE(parse("'foo'", rule_without_attr, str)); CHECK(str == ""sv); // Disabled attribute should yield default-constructed attribute } { // Attribute is disabled because the sub parser has semantic action and the operator is `=` constexpr auto rule_without_attr = rule_maker = x4::as(quoted_string) >> disable_attr; std::string str; REQUIRE(parse("'foo'", rule_without_attr, str)); CHECK(str == ""sv); // Disabled attribute should yield default-constructed attribute } { // Attribute is forced because `as_directive` sets `::has_action` to `false` constexpr auto rule_without_attr = rule_maker = x4::as(quoted_string >> disable_attr); std::string str; REQUIRE(parse("'foo'", rule_without_attr, str)); CHECK(str == "foo"sv); // Forced attribute should hold the parsed value } // Forced attribute, `operator%=` { constexpr auto rule_with_forced_attr = rule_maker %= quoted_string >> disable_attr; std::string str; REQUIRE(parse("'foo'", rule_with_forced_attr, str)); CHECK(str == "foo"sv); // Forced attribute should hold the parsed value } { constexpr auto rule_with_forced_attr = rule_maker %= x4::as(quoted_string) >> disable_attr; std::string str; REQUIRE(parse("'foo'", rule_with_forced_attr, str)); CHECK(str == "foo"sv); // `as` should not create a temporary; it should directly parse into the exposed variable } { constexpr auto rule_with_forced_attr = rule_maker %= x4::as(quoted_string >> disable_attr); std::string str; REQUIRE(parse("'foo'", rule_with_forced_attr, str)); CHECK(str == "foo"sv); // `as` should not create a temporary; it should directly parse into the exposed variable } } // `_as_var(ctx)` (with auto attribute propagation) { std::string result; constexpr auto string_rule = x4::rule{""} = x4::as( eps[([](auto&& ctx) { _rule_var(ctx) = "default"; })] >> eps[([](auto&& ctx) { _as_var(ctx) = "foo"; })] ); std::string_view const input; It first = input.begin(); Se const last = input.end(); REQUIRE(string_rule.parse(first, last, unused, result)); CHECK(result == "foo"sv); } // `_as_var(ctx)` (with disabled attribute) { std::string result; constexpr auto string_rule = x4::rule{""} = x4::as( eps[([](auto&& ctx) { _rule_var(ctx) = "default"; })] >> eps[([]([[maybe_unused]] auto&& ctx) { static_assert(std::same_as, unused_type>); })] ) >> disable_attr; // <---------- std::string_view const input; It first = input.begin(); Se const last = input.end(); REQUIRE(string_rule.parse(first, last, unused, result)); CHECK(result == "default"sv); } // `_as_var(ctx)` (within `as(as(...))`) { std::string result{"default"}; constexpr auto unused_rule = x4::as( x4::as( eps[([]([[maybe_unused]] auto&& ctx) { static_assert(std::same_as, unused_type>); })] ) ); std::string_view const input; It first = input.begin(); Se const last = input.end(); REQUIRE(unused_rule.parse(first, last, unused, result)); CHECK(result == "default"sv); } // `_as_var(ctx)` (within `as(as(...))`) { std::string result; /*constexpr*/ auto unused_rule = x4::as( x4::attr("default") >> eps[([]([[maybe_unused]] auto&& ctx) { static_assert(std::same_as, std::string>); })] >> x4::as( eps[([]([[maybe_unused]] auto&& ctx) { static_assert(std::same_as, unused_type>); })] ) ); std::string_view const input; It first = input.begin(); Se const last = input.end(); REQUIRE(unused_rule.parse(first, last, unused, result)); CHECK(result == "default"sv); } // Use `_rule_var(ctx)` inside `as(...)` { struct StringLiteral { bool is_quoted = false; std::string text; }; StringLiteral result; constexpr auto string_literal = x4::rule{"StringLiteral"} = eps[([](auto& ctx) { _rule_var(ctx).is_quoted = false; })] >> x4::as( x4::lit('"')[([](auto&& ctx) { StringLiteral& rule_var = _rule_var(ctx); rule_var.is_quoted = true; })] >> *~x4::char_('"') >> '"' )[([](auto&& ctx) { _rule_var(ctx).text = std::move(_attr(ctx)); })]; std::string_view input = R"("foo")"; It first = input.begin(); Se const last = input.end(); REQUIRE(string_literal.parse(first, last, unused, result)); CHECK(result.is_quoted == true); CHECK(result.text == "foo"sv); } } // NOLINTEND(readability-container-size-empty)