<div dir="ltr">This looks fine.</div><br><div class="gmail_quote"><div dir="ltr" class="gmail_attr">On Wed, Oct 16, 2019 at 5:06 PM Peter Wang <<a href="mailto:novalazy@gmail.com">novalazy@gmail.com</a>> wrote:<br></div><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left:1px solid rgb(204,204,204);padding-left:1ex">library/string.m:<br>
    As above.<br>
<br>
tests/hard_coded/Mmakefile:<br>
tests/hard_coded/string_fold_ilseq.exp:<br>
tests/hard_coded/string_fold_ilseq.exp2:<br>
tests/hard_coded/string_fold_ilseq.m:<br>
    Add test case.<br>
---<br>
 library/string.m                        | 27 +++++---------<br>
 tests/hard_coded/Mmakefile              |  1 +<br>
 tests/hard_coded/string_fold_ilseq.exp  |  5 +++<br>
 tests/hard_coded/string_fold_ilseq.exp2 |  5 +++<br>
 tests/hard_coded/string_fold_ilseq.m    | 49 +++++++++++++++++++++++++<br>
 5 files changed, 69 insertions(+), 18 deletions(-)<br>
 create mode 100644 tests/hard_coded/string_fold_ilseq.exp<br>
 create mode 100644 tests/hard_coded/string_fold_ilseq.exp2<br>
 create mode 100644 tests/hard_coded/string_fold_ilseq.m<br>
<br>
diff --git a/library/string.m b/library/string.m<br>
index 884363c82..10bbf6f5b 100644<br>
--- a/library/string.m<br>
+++ b/library/string.m<br>
@@ -1006,8 +1006,15 @@<br>
     % foldl(Closure, String, !Acc):<br>
     %<br>
     % `Closure' is an accumulator predicate which is to be called for each<br>
-    % character (code point) of the string `String' in turn. The initial<br>
-    % value of the accumulator is `!.Acc' and the final value is `!:Acc'.<br>
+    % character (code point) of the string `String' in turn.<br>
+    % If `String' contains ill-formed sequences, `Closure' is called for each<br>
+    % code unit in an ill-formed sequences. If strings use UTF-8 encoding,<br>
+    % U+FFFD is passed to `Closure' in place each such code unit.<br>
+    % If strings use UTF-16 encoding, each code unit in an ill-formed sequence<br>
+    % is an unpaired surrogate code point, which will be passed to `Closure'.<br>
+    %<br>
+    % The initial value of the accumulator is `!.Acc' and the final value is<br>
+    % `!:Acc'.<br>
     % (foldl(Closure, String, !Acc)  is equivalent to<br>
     %   to_char_list(String, Chars),<br>
     %   list.foldl(Closure, Chars, !Acc)<br>
@@ -5202,20 +5209,6 @@ break_up_string_reverse(Str, N, Prev) = Strs :-<br>
 % Folds over the characters in strings.<br>
 %<br>
<br>
-% XXX ILSEQ The behaviour of foldl depends on unsafe_index_next.<br>
-% For UTF-16, we can call Closure for unpaired surrogate code points like any<br>
-% other code point. For UTF-8, bytes in ill-formed sequences cannot be passed<br>
-% as `char's since they are not code points. Perhaps foldl should throw an<br>
-% exception, or just should pass replacement char.<br>
-%<br>
-% We may want to introduce fold predicates with an accumulator of type:<br>
-%       pred(char_or_code_unit, A, A)<br>
-% or<br>
-%       pred(char, maybe(int), A, A)<br>
-% For the latter, the second argument would be set to `yes(CodeUnit)' for each<br>
-% code unit in an ill-formed sequence, and the first argument could be set to<br>
-% U+FFFD (replacement char).<br>
-<br>
 foldl(F, S, A) = B :-<br>
     P = ( pred(X::in, Y::in, Z::out) is det :- Z = F(X, Y) ),<br>
     foldl(P, S, A, B).<br>
@@ -5308,8 +5301,6 @@ foldl2_substring(Closure, String, Start, Count, !Acc1, !Acc2) :-<br>
<br>
 %---------------------%<br>
<br>
-% XXX ILSEQ Behaviour depends on unsafe_prev_index.<br>
-<br>
 foldr(F, String, Acc0) = Acc :-<br>
     Closure = ( pred(X::in, Y::in, Z::out) is det :- Z = F(X, Y)),<br>
     foldr(Closure, String, Acc0, Acc).<br>
diff --git a/tests/hard_coded/Mmakefile b/tests/hard_coded/Mmakefile<br>
index 7906dcbf3..626f60c6a 100644<br>
--- a/tests/hard_coded/Mmakefile<br>
+++ b/tests/hard_coded/Mmakefile<br>
@@ -360,6 +360,7 @@ ORDINARY_PROGS = \<br>
        string_codepoint \<br>
        string_count_codepoints_ilseq \<br>
        string_first_char \<br>
+       string_fold_ilseq \<br>
        string_index_ilseq \<br>
        string_index_next_ilseq \<br>
        string_loop \<br>
diff --git a/tests/hard_coded/string_fold_ilseq.exp b/tests/hard_coded/string_fold_ilseq.exp<br>
new file mode 100644<br>
index 000000000..bfc1435ba<br>
--- /dev/null<br>
+++ b/tests/hard_coded/string_fold_ilseq.exp<br>
@@ -0,0 +1,5 @@<br>
+string.foldl:<br>
+a b c 😀 0xfffd 0xfffd 0xfffd x y z <br>
+<br>
+string.foldr:<br>
+z y x 0xfffd 0xfffd 0xfffd 😀 c b a <br>
diff --git a/tests/hard_coded/string_fold_ilseq.exp2 b/tests/hard_coded/string_fold_ilseq.exp2<br>
new file mode 100644<br>
index 000000000..d28c67096<br>
--- /dev/null<br>
+++ b/tests/hard_coded/string_fold_ilseq.exp2<br>
@@ -0,0 +1,5 @@<br>
+string.foldl:<br>
+a b c 😀 0xd83d x y z <br>
+<br>
+string.foldr:<br>
+z y x 0xd83d 😀 c b a <br>
diff --git a/tests/hard_coded/string_fold_ilseq.m b/tests/hard_coded/string_fold_ilseq.m<br>
new file mode 100644<br>
index 000000000..ba8a64bcb<br>
--- /dev/null<br>
+++ b/tests/hard_coded/string_fold_ilseq.m<br>
@@ -0,0 +1,49 @@<br>
+%---------------------------------------------------------------------------%<br>
+% vim: ts=4 sw=4 et ft=mercury<br>
+%---------------------------------------------------------------------------%<br>
+%<br>
+% The .exp file is for backends using UTF-8 string encoding.<br>
+% The .exp2 file is for backends using UTF-16 string encoding.<br>
+%<br>
+%---------------------------------------------------------------------------%<br>
+<br>
+:- module string_fold_ilseq.<br>
+:- interface.<br>
+<br>
+:- import_module io.<br>
+<br>
+:- pred main(io::di, io::uo) is det.<br>
+<br>
+%---------------------------------------------------------------------------%<br>
+<br>
+:- implementation.<br>
+<br>
+:- import_module char.<br>
+:- import_module int.<br>
+:- import_module list.<br>
+:- import_module string.<br>
+<br>
+%---------------------------------------------------------------------------%<br>
+<br>
+main(!IO) :-<br>
+    S0 = "😀",<br>
+    S1 = string.between(S0, 0, count_code_units(S0) - 1),<br>
+    S = "abc" ++ S0 ++ S1 ++ "xyz",<br>
+<br>
+    io.write_string("string.foldl:\n", !IO),<br>
+    string.foldl(write_char_or_hex, S, !IO),<br>
+    io.write_string("\n\n", !IO),<br>
+<br>
+    io.write_string("string.foldr:\n", !IO),<br>
+    string.foldr(write_char_or_hex, S, !IO),<br>
+    io.write_string("\n", !IO).<br>
+<br>
+:- pred write_char_or_hex(char::in, io::di, io::uo) is det.<br>
+<br>
+write_char_or_hex(Char, !IO) :-<br>
+    ( if Char = '\ufffd' ; char.is_surrogate(Char) then<br>
+        io.format("%#x", [i(char.to_int(Char))], !IO)<br>
+    else<br>
+        io.write_char(Char, !IO)<br>
+    ),<br>
+    io.write_char(' ', !IO).<br>
-- <br>
2.23.0<br>
<br>
_______________________________________________<br>
reviews mailing list<br>
<a href="mailto:reviews@lists.mercurylang.org" target="_blank">reviews@lists.mercurylang.org</a><br>
<a href="https://lists.mercurylang.org/listinfo/reviews" rel="noreferrer" target="_blank">https://lists.mercurylang.org/listinfo/reviews</a><br>
</blockquote></div>