diff --git a/src/lang/text/include/sourcemeta/core/text.h b/src/lang/text/include/sourcemeta/core/text.h index e137a0297..93e316804 100644 --- a/src/lang/text/include/sourcemeta/core/text.h +++ b/src/lang/text/include/sourcemeta/core/text.h @@ -191,6 +191,31 @@ inline constexpr auto is_alpha(const Character character) noexcept -> bool { (character >= 'A' && character <= 'Z'); } +/// @ingroup text +/// +/// Return whether a string is non-empty and consists entirely of ASCII letters +/// (A-Z or a-z). An empty string is not considered a match. For example: +/// +/// ```cpp +/// #include +/// #include +/// +/// assert(sourcemeta::core::is_alpha("abc")); +/// assert(!sourcemeta::core::is_alpha("ab1")); +/// assert(!sourcemeta::core::is_alpha("")); +/// ``` +inline constexpr auto is_alpha(const std::string_view value) noexcept -> bool { + if (value.empty()) { + return false; + } + for (const auto character : value) { + if (!is_alpha(character)) { + return false; + } + } + return true; +} + /// @ingroup text /// /// Return whether a character is an ASCII digit (0-9). For example: @@ -211,6 +236,31 @@ inline constexpr auto is_digit(const Character character) noexcept -> bool { return character >= '0' && character <= '9'; } +/// @ingroup text +/// +/// Return whether a string is non-empty and consists entirely of ASCII digits +/// (0-9). An empty string is not considered a match. For example: +/// +/// ```cpp +/// #include +/// #include +/// +/// assert(sourcemeta::core::is_digit("123")); +/// assert(!sourcemeta::core::is_digit("12a")); +/// assert(!sourcemeta::core::is_digit("")); +/// ``` +inline constexpr auto is_digit(const std::string_view value) noexcept -> bool { + if (value.empty()) { + return false; + } + for (const auto character : value) { + if (!is_digit(character)) { + return false; + } + } + return true; +} + /// @ingroup text /// /// Return whether a character is an ASCII letter or digit. For example: @@ -232,6 +282,32 @@ inline constexpr auto is_alphanum(const Character character) noexcept -> bool { return is_alpha(character) || is_digit(character); } +/// @ingroup text +/// +/// Return whether a string is non-empty and consists entirely of ASCII letters +/// or digits. An empty string is not considered a match. For example: +/// +/// ```cpp +/// #include +/// #include +/// +/// assert(sourcemeta::core::is_alphanum("abc123")); +/// assert(!sourcemeta::core::is_alphanum("abc-123")); +/// assert(!sourcemeta::core::is_alphanum("")); +/// ``` +inline constexpr auto is_alphanum(const std::string_view value) noexcept + -> bool { + if (value.empty()) { + return false; + } + for (const auto character : value) { + if (!is_alphanum(character)) { + return false; + } + } + return true; +} + /// @ingroup text /// /// Truncate a string in place to at most `maximum_length` bytes, appending diff --git a/test/text/text_is_alpha_test.cc b/test/text/text_is_alpha_test.cc index e578702a5..ffc15dc1a 100644 --- a/test/text/text_is_alpha_test.cc +++ b/test/text/text_is_alpha_test.cc @@ -32,3 +32,19 @@ TEST(Text_is_alpha, ascii_boundaries) { EXPECT_FALSE(sourcemeta::core::is_alpha('`')); EXPECT_FALSE(sourcemeta::core::is_alpha('{')); } + +TEST(Text_is_alpha, string_all_letters) { + EXPECT_TRUE(sourcemeta::core::is_alpha("abcXYZ")); +} + +TEST(Text_is_alpha, string_with_digit) { + EXPECT_FALSE(sourcemeta::core::is_alpha("abc1")); +} + +TEST(Text_is_alpha, string_with_punctuation) { + EXPECT_FALSE(sourcemeta::core::is_alpha("ab-cd")); +} + +TEST(Text_is_alpha, empty_string) { + EXPECT_FALSE(sourcemeta::core::is_alpha("")); +} diff --git a/test/text/text_is_alphanum_test.cc b/test/text/text_is_alphanum_test.cc index 9b6edb3f1..1bc4404b4 100644 --- a/test/text/text_is_alphanum_test.cc +++ b/test/text/text_is_alphanum_test.cc @@ -18,3 +18,15 @@ TEST(Text_is_alphanum, punctuation) { EXPECT_FALSE(sourcemeta::core::is_alphanum('.')); EXPECT_FALSE(sourcemeta::core::is_alphanum(' ')); } + +TEST(Text_is_alphanum, string_letters_and_digits) { + EXPECT_TRUE(sourcemeta::core::is_alphanum("abc123XYZ")); +} + +TEST(Text_is_alphanum, string_with_punctuation) { + EXPECT_FALSE(sourcemeta::core::is_alphanum("abc-123")); +} + +TEST(Text_is_alphanum, empty_string) { + EXPECT_FALSE(sourcemeta::core::is_alphanum("")); +} diff --git a/test/text/text_is_digit_test.cc b/test/text/text_is_digit_test.cc index c2ebdb50e..aba44acf1 100644 --- a/test/text/text_is_digit_test.cc +++ b/test/text/text_is_digit_test.cc @@ -23,3 +23,15 @@ TEST(Text_is_digit, ascii_boundaries) { EXPECT_FALSE(sourcemeta::core::is_digit('/')); EXPECT_FALSE(sourcemeta::core::is_digit(':')); } + +TEST(Text_is_digit, string_all_digits) { + EXPECT_TRUE(sourcemeta::core::is_digit("0123456789")); +} + +TEST(Text_is_digit, string_with_letter) { + EXPECT_FALSE(sourcemeta::core::is_digit("12a")); +} + +TEST(Text_is_digit, empty_string) { + EXPECT_FALSE(sourcemeta::core::is_digit("")); +}