/** * 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 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 static_assert( bp::detail::range_utf_format() == bp::detail::no_format); TEST(replace, 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) { EXPECT_EQ(*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) { EXPECT_EQ(*it, *v_array_curr); } } { auto r1 = bp::detail::to_range::call(""); auto r2 = bp::detail::to_range::call("foo"); bp::detail::either_iterator either_r1_begin( r1.begin()); bp::detail::either_iterator either_r1_end( r1.end()); bp::detail::either_iterator either_r2_begin( r2.begin()); bp::detail::either_iterator either_r2_end( r2.end()); EXPECT_EQ(either_r1_begin, either_r1_end); std::string copy; for (auto it = either_r2_begin; it != either_r2_end; ++it) { copy.push_back(*it); } EXPECT_EQ(copy, "foo"); } } TEST(replace, replace) { { auto r = bp::replace("", bp::lit("XYZ"), bp::ws, "foo"); int count = 0; for (auto subrange : r) { (void)subrange; ++count; } EXPECT_EQ(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()); EXPECT_EQ(str, strs[count]); ++count; } EXPECT_EQ(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()); EXPECT_EQ(str, strs[count]); ++count; } EXPECT_EQ(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()); EXPECT_EQ(str, strs[count]); ++count; } EXPECT_EQ(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()); EXPECT_EQ(str, strs[count]); ++count; } EXPECT_EQ(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()); EXPECT_EQ(str, strs[count]); ++count; } EXPECT_EQ(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()); EXPECT_EQ(str, strs[count]); ++count; } EXPECT_EQ(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()); EXPECT_EQ(str, strs[count]); ++count; } EXPECT_EQ(count, 7); } { char const * str = "XYZXYZaaXYZbaabaXYZXYZ"; char const * replacement = "foo"; auto r = str | bp::replace(bp::lit("XYZ"), 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()); EXPECT_EQ(str, strs[count]); ++count; } EXPECT_EQ(count, 7); } { char const * str = "XYZXYZaaXYZbaabaXYZXYZ"; char const * replacement = "foo"; auto const r = str | bp::replace(bp::lit("XYZ"), 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()); EXPECT_EQ(str, strs[count]); ++count; } EXPECT_EQ(count, 7); } } // MSVC produces hard errors here, so ill_formed does not work. #if defined(__cpp_char8_t) && !defined(_MSC_VER) 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{}); template using utf8_str_char_replacement = decltype(std::declval() | bp::as_utf8 | bp::replace(bp::lit("XYZ"), std::declval())); static_assert(ill_formed{}); #endif TEST(replace, 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; } EXPECT_EQ(count, 0); } { char const * str_ = "aaXYZb"; auto str = 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()); EXPECT_EQ(str, strs[count]); ++count; } EXPECT_EQ(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()); EXPECT_EQ(str, strs[count]); ++count; } EXPECT_EQ(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()); EXPECT_EQ(str, strs[count]); ++count; } EXPECT_EQ(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()); EXPECT_EQ(str, strs[count]); ++count; } EXPECT_EQ(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()); EXPECT_EQ(str, strs[count]); ++count; } EXPECT_EQ(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()); EXPECT_EQ(str, strs[count]); ++count; } EXPECT_EQ(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()); EXPECT_EQ(str, strs[count]); ++count; } EXPECT_EQ(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()); EXPECT_EQ(str, strs[count]); ++count; } EXPECT_EQ(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()); EXPECT_EQ(str, strs[count]); ++count; } EXPECT_EQ(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. TEST(replace, 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); } EXPECT_EQ(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); } EXPECT_EQ(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); } EXPECT_EQ(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); } EXPECT_EQ(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); } EXPECT_EQ(replace_result, "foofooaafoobaabafoofoo"); } } #endif TEST(replace, 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 } #endif