/** * Copyright (C) 2024 T. Zachary Laine * * 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 #include #include "ill_formed.hpp" #include #if defined(_MSC_VER) && !BOOST_PARSER_USE_CONCEPTS int main() {} #else namespace bp = boost::parser; #if BOOST_PARSER_USE_CONCEPTS namespace deduction { using namespace std::literals; std::string str; auto const parser = bp::char_; auto const skip = bp::ws; auto deduced_1 = bp::replace_view(str, parser, skip, "foo", bp::trace::on); auto deduced_2 = bp::replace_view(str, parser, skip, "foo"); auto deduced_3 = bp::replace_view(str, parser, "foo", bp::trace::on); auto deduced_4 = bp::replace_view(str, parser, "foo"); } #endif // MSVC and older Clangs produce hard errors here, so ill_formed does not // work. #if defined(__cpp_char8_t) && !defined(_MSC_VER) && \ (!defined(__clang_major__) || 16 <= __clang_major__) char const empty_str[] = ""; template using char_str_utf8_replacement = decltype(std::declval() | bp::replace(bp::lit("XYZ"), std::declval() | bp::as_utf8)); static_assert(ill_formed{}); template using char_str_utf16_replacement = decltype(std::declval() | bp::replace(bp::lit("XYZ"), std::declval() | bp::as_utf16)); static_assert(ill_formed{}); #endif static_assert( bp::detail::range_utf_format() == bp::detail::no_format); int main() { // bind_back { auto f = [](auto x) { static_assert(std::is_same_v< decltype(x), BOOST_PARSER_SUBRANGE>); BOOST_TEST(x.size() == 4u); // stripped off null }; bp::detail::stl_interfaces::bind_back(f, "text"); } // either_iterator { { std::list l({1, 2, 3}); std::vector v({4, 5, 6}); bp::detail::either_iterator, std::vector> either_l_begin(l.begin()); bp::detail::either_iterator, std::vector> either_l_end(l.end()); bp::detail::either_iterator, std::vector> either_v_begin(v.begin()); bp::detail::either_iterator, std::vector> either_v_end(v.end()); int const l_array[] = {1, 2, 3}; auto l_array_curr = l_array; for (auto it = either_l_begin; it != either_l_end; ++it, ++l_array_curr) { BOOST_TEST(*it == *l_array_curr); } int const v_array[] = {4, 5, 6}; auto v_array_curr = v_array; for (auto it = either_v_begin; it != either_v_end; ++it, ++v_array_curr) { BOOST_TEST(*it == *v_array_curr); } } } // replace { { auto r = bp::replace("", bp::lit("XYZ"), bp::ws, "foo"); int count = 0; for (auto subrange : r) { (void)subrange; ++count; } BOOST_TEST(count == 0); } { char const str[] = "aaXYZb"; auto r = bp::replace(str, bp::lit("XYZ"), bp::ws, "foo"); int count = 0; std::string_view const strs[] = {"aa", "foo", "b"}; for (auto subrange : r) { std::string str(subrange.begin(), subrange.end()); BOOST_TEST(str == strs[count]); ++count; } BOOST_TEST(count == 3); } { char const str[] = "a a XYZ baa ba XYZ"; auto r = str | bp::replace(bp::lit("XYZ"), bp::ws, "foo", bp::trace::off); int count = 0; std::string_view const strs[] = {"a a ", "foo", " baa ba ", "foo"}; for (auto subrange : r) { std::string str(subrange.begin(), subrange.end()); BOOST_TEST(str == strs[count]); ++count; } BOOST_TEST(count == 4); } #if !defined(__GNUC__) || 12 <= __GNUC__ // Older GCCs don't like the use of temporaries like the // std::string("foo") below. { char const str[] = "aaXYZbaabaXYZ"; auto r = str | bp::replace( bp::lit("XYZ"), std::string("foo"), bp::trace::off); int count = 0; std::string_view const strs[] = {"aa", "foo", "baaba", "foo"}; for (auto subrange : r) { std::string str(subrange.begin(), subrange.end()); BOOST_TEST(str == strs[count]); ++count; } BOOST_TEST(count == 4); } #endif { char const str[] = "aaXYZbaabaXYZ"; const auto r = str | bp::replace(bp::lit("XYZ"), "foo"); int count = 0; std::string_view const strs[] = {"aa", "foo", "baaba", "foo"}; for (auto subrange : r) { std::string str(subrange.begin(), subrange.end()); BOOST_TEST(str == strs[count]); ++count; } BOOST_TEST(count == 4); } { char const str[] = "aaXYZbaabaXYZXYZ"; auto r = str | bp::replace(bp::lit("XYZ"), "foo"); int count = 0; std::string_view const strs[] = {"aa", "foo", "baaba", "foo", "foo"}; for (auto subrange : r) { std::string str(subrange.begin(), subrange.end()); BOOST_TEST(str == strs[count]); ++count; } BOOST_TEST(count == 5); } { char const str[] = "XYZaaXYZbaabaXYZXYZ"; auto r = str | bp::replace(bp::lit("XYZ"), "foo"); int count = 0; std::string_view const strs[] = { "foo", "aa", "foo", "baaba", "foo", "foo"}; for (auto subrange : r) { std::string str(subrange.begin(), subrange.end()); BOOST_TEST(str == strs[count]); ++count; } BOOST_TEST(count == 6); } { char const str[] = "XYZXYZaaXYZbaabaXYZXYZ"; auto r = str | bp::replace(bp::lit("XYZ"), "foo"); int count = 0; std::string_view const strs[] = { "foo", "foo", "aa", "foo", "baaba", "foo", "foo"}; for (auto subrange : r) { std::string str(subrange.begin(), subrange.end()); BOOST_TEST(str == strs[count]); ++count; } BOOST_TEST(count == 7); } { char const * str = "XYZXYZaaXYZbaabaXYZXYZ"; char const * replacement = "foo"; auto r = bp::null_term(str) | bp::replace(bp::lit("XYZ"), bp::null_term(replacement)); int count = 0; std::string_view const strs[] = { "foo", "foo", "aa", "foo", "baaba", "foo", "foo"}; for (auto subrange : r) { std::string str(subrange.begin(), subrange.end()); BOOST_TEST(str == strs[count]); ++count; } BOOST_TEST(count == 7); } { char const * str = "XYZXYZaaXYZbaabaXYZXYZ"; char const * replacement = "foo"; auto const r = bp::null_term(str) | bp::replace(bp::lit("XYZ"), bp::null_term(replacement)); int count = 0; std::string_view const strs[] = { "foo", "foo", "aa", "foo", "baaba", "foo", "foo"}; for (auto subrange : r) { std::string str(subrange.begin(), subrange.end()); BOOST_TEST(str == strs[count]); ++count; } BOOST_TEST(count == 7); } } // replace_unicode { { char const str_[] = ""; auto str = str_ | bp::as_utf8; auto r = bp::replace(str, bp::lit("XYZ"), bp::ws, "foo" | bp::as_utf8); int count = 0; for (auto subrange : r) { (void)subrange; ++count; } BOOST_TEST(count == 0); } { char const * str_ = "aaXYZb"; auto str = bp::null_term(str_) | bp::as_utf16; auto r = bp::replace(str, bp::lit("XYZ"), bp::ws, "foo" | bp::as_utf16); int count = 0; std::string_view const strs[] = {"aa", "foo", "b"}; for (auto subrange : r) { auto u8sub = subrange | bp::as_utf8; std::string str(u8sub.begin(), u8sub.end()); BOOST_TEST(str == strs[count]); ++count; } BOOST_TEST(count == 3); } { char const str_[] = "aaXYZbaabaXYZ"; auto str = str_ | bp::as_utf32; auto r = str | bp::replace( bp::lit("XYZ"), bp::ws, "foo" | bp::as_utf32, bp::trace::off); int count = 0; std::string_view const strs[] = {"aa", "foo", "baaba", "foo"}; for (auto subrange : r) { auto u8sub = subrange | bp::as_utf8; std::string str(u8sub.begin(), u8sub.end()); BOOST_TEST(str == strs[count]); ++count; } BOOST_TEST(count == 4); } { char const str_[] = "aaXYZbaabaXYZ"; auto str = str_ | bp::as_utf8; auto r = str | bp::replace( bp::lit("XYZ"), "foo" | bp::as_utf8, bp::trace::off); int count = 0; std::string_view const strs[] = {"aa", "foo", "baaba", "foo"}; for (auto subrange : r) { std::string str(subrange.begin(), subrange.end()); BOOST_TEST(str == strs[count]); ++count; } BOOST_TEST(count == 4); } { char const str_[] = "aaXYZbaabaXYZ"; auto str = str_ | bp::as_utf16; auto r = str | bp::replace(bp::lit("XYZ"), "foo"); int count = 0; std::string_view const strs[] = {"aa", "foo", "baaba", "foo"}; for (auto subrange : r) { auto u8sub = subrange | bp::as_utf8; std::string str(u8sub.begin(), u8sub.end()); BOOST_TEST(str == strs[count]); ++count; } BOOST_TEST(count == 4); } { char const str_[] = "aaXYZbaabaXYZXYZ"; auto str = str_ | bp::as_utf32; auto r = str | bp::replace(bp::lit("XYZ"), "foo"); int count = 0; std::string_view const strs[] = {"aa", "foo", "baaba", "foo", "foo"}; for (auto subrange : r) { auto u8sub = subrange | bp::as_utf8; std::string str(u8sub.begin(), u8sub.end()); BOOST_TEST(str == strs[count]); ++count; } BOOST_TEST(count == 5); } { char const str_[] = "XYZaaXYZbaabaXYZXYZ"; auto str = str_ | bp::as_utf8; auto r = str | bp::replace(bp::lit("XYZ"), "foo" | bp::as_utf8); int count = 0; std::string_view const strs[] = { "foo", "aa", "foo", "baaba", "foo", "foo"}; for (auto subrange : r) { std::string str(subrange.begin(), subrange.end()); BOOST_TEST(str == strs[count]); ++count; } BOOST_TEST(count == 6); } { char const str_[] = "XYZXYZaaXYZbaabaXYZXYZ"; auto str = str_ | bp::as_utf16; auto r = str | bp::replace(bp::lit("XYZ"), "foo"); int count = 0; std::string_view const strs[] = { "foo", "foo", "aa", "foo", "baaba", "foo", "foo"}; for (auto subrange : r) { auto u8sub = subrange | bp::as_utf8; std::string str(u8sub.begin(), u8sub.end()); BOOST_TEST(str == strs[count]); ++count; } BOOST_TEST(count == 7); } { char const str_[] = "XYZXYZaaXYZbaabaXYZXYZ"; auto str = str_ | bp::as_utf16; auto r = str | bp::replace(bp::lit("XYZ"), "foo" | bp::as_utf8); int count = 0; std::string_view const strs[] = { "foo", "foo", "aa", "foo", "baaba", "foo", "foo"}; for (auto subrange : r) { auto u8sub = subrange | bp::as_utf8; std::string str(u8sub.begin(), u8sub.end()); BOOST_TEST(str == strs[count]); ++count; } BOOST_TEST(count == 7); } { char const str_[] = "XYZXYZaaXYZbaabaXYZXYZ"; auto str = str_ | bp::as_utf16; auto r = str | bp::replace(bp::lit("XYZ"), "foo" | bp::as_utf32); int count = 0; std::string_view const strs[] = { "foo", "foo", "aa", "foo", "baaba", "foo", "foo"}; for (auto subrange : r) { auto u8sub = subrange | bp::as_utf8; std::string str(u8sub.begin(), u8sub.end()); BOOST_TEST(str == strs[count]); ++count; } BOOST_TEST(count == 7); } } #if BOOST_PARSER_USE_CONCEPTS && (!defined(__GNUC__) || 12 <= __GNUC__) // Older GCCs don't like the use of temporaries like the std::string("foo") // below. This causes | join to break. // join_compat) { { char const str[] = "XYZXYZaaXYZbaabaXYZXYZ"; auto rng = str | bp::as_utf32 | bp::replace(bp::lit("XYZ"), "foo" | bp::as_utf8) | std::views::join; std::string replace_result; for (auto ch : rng) { static_assert(std::is_same_v); replace_result.push_back((char)ch); } BOOST_TEST(replace_result == "foofooaafoobaabafoofoo"); } { char const str[] = "XYZXYZaaXYZbaabaXYZXYZ"; auto rng = str | bp::replace(bp::lit("XYZ"), "foo") | std::views::join; std::string replace_result; for (auto ch : rng) { replace_result.push_back(ch); } BOOST_TEST(replace_result == "foofooaafoobaabafoofoo"); } { std::string str = "XYZXYZaaXYZbaabaXYZXYZ"; auto rng = str | bp::replace(bp::lit("XYZ"), "foo") | std::views::join; std::string replace_result; for (auto ch : rng) { replace_result.push_back(ch); } BOOST_TEST(replace_result == "foofooaafoobaabafoofoo"); } { std::string const str = "XYZXYZaaXYZbaabaXYZXYZ"; auto rng = str | bp::replace(bp::lit("XYZ"), "foo") | std::views::join; std::string replace_result; for (auto ch : rng) { replace_result.push_back(ch); } BOOST_TEST(replace_result == "foofooaafoobaabafoofoo"); } { auto rng = std::string("XYZXYZaaXYZbaabaXYZXYZ") | bp::replace(bp::lit("XYZ"), "foo") | std::views::join; std::string replace_result; for (auto ch : rng) { replace_result.push_back(ch); } BOOST_TEST(replace_result == "foofooaafoobaabafoofoo"); } } #endif // doc_examples { // clang-format off { namespace bp = boost::parser; auto card_number = bp::int_ >> bp::repeat(3)['-' >> bp::int_]; auto rng = "My credit card number is 1234-5678-9012-3456." | bp::replace(card_number, "XXXX-XXXX-XXXX-XXXX"); int count = 0; // Prints My credit card number is XXXX-XXXX-XXXX-XXXX. for (auto subrange : rng) { std::cout << std::string_view(subrange.begin(), subrange.end() - subrange.begin()); ++count; } std::cout << "\n"; assert(count == 3); } #if BOOST_PARSER_USE_CONCEPTS && (!defined(__GNUC__) || 12 <= __GNUC__) { namespace bp = boost::parser; auto card_number = bp::int_ >> bp::repeat(3)['-' >> bp::int_]; auto rng = "My credit card number is 1234-5678-9012-3456." | bp::replace(card_number, "XXXX-XXXX-XXXX-XXXX") | std::views::join; std::string replace_result; for (auto ch : rng) { replace_result.push_back(ch); } assert(replace_result == "My credit card number is XXXX-XXXX-XXXX-XXXX."); } #endif // clang-format on } return boost::report_errors(); } #endif