[m-rev.] for review: add string.contains_match/2

Julien Fischer jfischer at opturion.com
Mon Jul 25 15:38:10 AEST 2022


For review by anyone.

---------------------

Add string.contains_match/2.

Add a new predicate that tests if string contains any characters that succeed
for a given test predicate.

library/string.m:
     Add the new predicate.

compiler/options.m:
     Replace the predicate string_contains_whitespace/1 defined here
     with a call to the new library predicate.

NEWS:
     Announce the new predicate.

tests/hard_coded/Mmakefile:
tests/hard_coded/string_contains_match.{m,exp}:
     Add a test of the new predicate.

Julien.

diff --git a/NEWS b/NEWS
index 79317b8..bd9f2f6 100644
--- a/NEWS
+++ b/NEWS
@@ -353,6 +353,12 @@ Changes to the Mercury standard library

     - pred `empty/1`

+### Changes to the `string` module
+
+* The following predicate has been added:
+
+   - pred `contains_match/2`
+
  ### Changes to the `tree_bitset` module

  * The following obsolete predicates has been removed:
diff --git a/compiler/options.m b/compiler/options.m
index 9ac6043..99ebfc7 100644
--- a/compiler/options.m
+++ b/compiler/options.m
@@ -4108,7 +4108,7 @@ quote_arg(Arg0) = Arg :-
      % test whether we are using a Unix or Windows shell.
      ( if dir.use_windows_paths then
          ( if
-            ( string_contains_whitespace(Arg0)
+            ( string.contains_match(char.is_whitespace, Arg0)
              ; Arg0 = ""
              )
          then
@@ -4141,15 +4141,6 @@ quote_arg(Arg0) = Arg :-
          )
      ).

-:- pred string_contains_whitespace(string::in) is semidet.
-
-string_contains_whitespace(Str) :-
-    Chars = string.to_char_list(Str),
-    some [Char] (
-        list.member(Char, Chars),
-        char.is_whitespace(Char)
-    ).
-
  :- func quote_arg_unix(list(char)) = list(char).

  quote_arg_unix([]) = [].
diff --git a/library/string.m b/library/string.m
index 2fdb245..0750d07 100644
--- a/library/string.m
+++ b/library/string.m
@@ -578,6 +578,14 @@
      %
  :- pred all_match(pred(char)::in(pred(in) is semidet), string::in) is semidet.

+    % contains_match(TestPred, String):
+    %
+    % True iff String contains at least one code point that satisfies
+    % TestPred.
+    %
+:- pred contains_match(pred(char)::in(pred(in) is semidet), string::in)
+    is semidet.
+
      % contains_char(String, Char):
      %
      % Succeed if the code point Char occurs in String.
@@ -3292,6 +3300,22 @@ all_match_loop(P, String, Cur) :-

  %---------------------%

+contains_match(P, String) :-
+    contains_match_loop(P, String, 0).
+
+:- pred contains_match_loop(pred(char)::in(pred(in) is semidet), string::in,
+    int::in) is semidet.
+
+contains_match_loop(P, String, Cur) :-
+    unsafe_index_next_repl(String, Cur, Next, Char, not_replaced),
+    ( if P(Char) then
+        true
+    else
+        contains_match_loop(P, String, Next)
+    ).
+
+%---------------------%
+
  :- pragma foreign_proc("C",
      contains_char(Str::in, Ch::in),
      [will_not_call_mercury, promise_pure, thread_safe, will_not_modify_trail,
diff --git a/tests/hard_coded/Mmakefile b/tests/hard_coded/Mmakefile
index 2385e34..caa47e9 100644
--- a/tests/hard_coded/Mmakefile
+++ b/tests/hard_coded/Mmakefile
@@ -385,6 +385,7 @@ ORDINARY_PROGS = \
  	string_codepoint \
  	string_codepoint_offset_ilseq \
  	string_compare_substrings \
+	string_contains_match \
  	string_count_codepoints_ilseq \
  	string_first_char \
  	string_fold_ilseq \
diff --git a/tests/hard_coded/string_contains_match.exp b/tests/hard_coded/string_contains_match.exp
index e69de29..d6c8009 100644
--- a/tests/hard_coded/string_contains_match.exp
+++ b/tests/hard_coded/string_contains_match.exp
@@ -0,0 +1,9 @@
+contains_match(is_whitespace, "") ==> false.
+contains_match(is_whitespace, "abc") ==> false.
+contains_match(is_whitespace, " abc") ==> true.
+contains_match(is_whitespace, "a bc") ==> true.
+contains_match(is_whitespace, "abc ") ==> true.
+contains_match(is_whitespace, "μῆνιν ἄειδε θεὰ Πηληϊάδεω Ἀχιλῆος") ==> true.
+contains_match(is_chess_piece, "♜    ♞   ♝   ♛   ♚   ♝   ♞   ♜") ==> true.
+contains_match(is_chess_piece, "abc♜ def") ==> true.
+contains_match(is_chess_piece, "no chess pieces here!") ==> false.
diff --git a/tests/hard_coded/string_contains_match.m b/tests/hard_coded/string_contains_match.m
index e69de29..c88cd29 100644
--- a/tests/hard_coded/string_contains_match.m
+++ b/tests/hard_coded/string_contains_match.m
@@ -0,0 +1,61 @@
+%---------------------------------------------------------------------------%
+% vim: ts=4 sw=4 et ft=mercury
+%---------------------------------------------------------------------------%
+% A test of string.contains_match/2.
+%---------------------------------------------------------------------------%
+
+:- module string_contains_match.
+:- interface.
+
+:- import_module io.
+
+:- pred main(io::di, io::uo) is det.
+
+%---------------------------------------------------------------------------%
+%---------------------------------------------------------------------------%
+
+:- implementation.
+
+:- import_module char.
+:- import_module int.
+:- import_module list.
+:- import_module string.
+
+%---------------------------------------------------------------------------%
+
+main(!IO) :-
+    test_contains_match(is_whitespace, "is_whitespace", "", !IO),
+    test_contains_match(is_whitespace, "is_whitespace", "abc", !IO),
+    test_contains_match(is_whitespace, "is_whitespace", " abc", !IO),
+    test_contains_match(is_whitespace, "is_whitespace", "a bc", !IO),
+    test_contains_match(is_whitespace, "is_whitespace", "abc ", !IO),
+    test_contains_match(is_whitespace, "is_whitespace",
+        "μῆνιν ἄειδε θεὰ Πηληϊάδεω Ἀχιλῆος", !IO),
+    test_contains_match(is_chess_piece, "is_chess_piece",
+        "♜    ♞   ♝   ♛   ♚   ♝   ♞   ♜", !IO),
+    test_contains_match(is_chess_piece, "is_chess_piece",
+        "abc♜ def", !IO),
+    test_contains_match(is_chess_piece, "is_chess_piece",
+        "no chess pieces here!", !IO).
+
+:- pred test_contains_match(pred(char)::in(pred(in) is semidet),
+    string::in, string::in, io::di, io::uo) is det.
+
+test_contains_match(Pred, PredName, String, !IO) :-
+    ( if string.contains_match(Pred, String) then
+        Result = "true"
+    else
+        Result = "false"
+    ),
+    io.format("contains_match(%s, %s) ==> %s.\n",
+        [s(PredName), s(string(String)), s(Result)], !IO).
+
+:- pred is_chess_piece(char::in) is semidet.
+
+is_chess_piece(Char) :-
+    char.to_int(Char, CodePoint),
+    CodePoint >= 0x2654, CodePoint =< 0x265F.
+
+%---------------------------------------------------------------------------%
+:- end_module string_contains_match.
+%---------------------------------------------------------------------------%



More information about the reviews mailing list