[m-rev.] for review: escape characters in strings returned by deconstruct.functor/4

Julien Fischer jfischer at opturion.com
Wed Jul 11 00:56:50 AEST 2018


For review by anyone.

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

Escape characters in strings returned by deconstruct.functor/4.

runtime/mercury_string.[ch]:
     Add a function that returns a copy of a string inside double
     quotes and with escapes for any control characters inserted.

     Add macro that tests if a character is a control character.

runtime/mercury_ml_expand_body.h:
     Use the above function to escape characters in the representation
     of string functors.

library/rtti_implementation.m:
     Do the same for the C# and Java backends.

NEWS:
     Announce the above change to deconstruct.functor/4, as well
     the earlier change to its handling of characters.

tests/hard_coded/deconstruct_arg.m:
tests/hard_coded/deconstruct_arg.exp:
     Extend this test case to cover strings.

Julien.

diff --git a/NEWS b/NEWS
index cb97697..3fdc989 100644
--- a/NEWS
+++ b/NEWS
@@ -525,6 +525,10 @@ Changes to the Mercury standard library:
    - get_uint8/1, unsafe_get_uint8/1
    - set_uint8/4, unsafe_set_uint8/4

+* The functor/4 predicate in the deconstruct module has been modified so
+  that for character and string data any control characters in the functor
+  representation are escaped.
+
  Changes to the Mercury compiler:

  * We have extended tail call optimization from self recursive calls only
diff --git a/library/rtti_implementation.m b/library/rtti_implementation.m
index 51320ff..5133d6d 100644
--- a/library/rtti_implementation.m
+++ b/library/rtti_implementation.m
@@ -145,6 +145,7 @@
  :- import_module maybe.
  :- import_module require.
  :- import_module string.
+:- import_module term_io.
  :- import_module type_desc.

  %---------------------------------------------------------------------------%
@@ -2846,7 +2847,7 @@ deconstruct_2(Term, TypeInfo, TypeCtorInfo, TypeCtorRep, NonCanon,
      ;
          TypeCtorRep = tcr_string,
          det_dynamic_cast(Term, String),
-        Functor = string.append_list(["\"", String, "\""]),
+        Functor = term_io.quoted_string(String),
          Ordinal = -1,
          Arity = 0,
          Arguments = []
diff --git a/library/term_io.m b/library/term_io.m
index 6c78246..c8ab618 100644
--- a/library/term_io.m
+++ b/library/term_io.m
@@ -728,8 +728,9 @@ write_escaped_char(Char, !IO) :-
  write_escaped_char(Stream, Char, !State) :-
      % Note: the code of add_escaped_char and write_escaped_char
      % should be kept in sync. The code of both is similar to code in
-    % compiler/parse_tree_out_pragma.m; any changes here may require
-    % similar changes there.
+    % compiler/parse_tree_out_pragma.m and MR_escape_string_quote
+    % in runtime/mercury_string.c; any changes here may require similar
+    % changes in those spots.
      ( if mercury_escape_special_char(Char, QuoteChar) then
          stream.put(Stream, ('\\'), !State),
          stream.put(Stream, QuoteChar, !State)
diff --git a/runtime/mercury_ml_expand_body.h b/runtime/mercury_ml_expand_body.h
index 8ec6089..c998f44 100644
--- a/runtime/mercury_ml_expand_body.h
+++ b/runtime/mercury_ml_expand_body.h
@@ -895,10 +895,7 @@ EXPAND_FUNCTION_NAME(MR_TypeInfo type_info, MR_Word *data_word_ptr,
                      default:
                          // Print remaining control characters using octal
                          // escapes.
-                        if ( 
-                            (0x00 <= data_word && data_word <= 0x1f) ||
-                            (0x7f <= data_word && data_word <= 0x9f)
-                        ) { 
+                        if (MR_is_control(data_word)) {
                              sprintf(buf,
                                  "\'\\%03" MR_INTEGER_LENGTH_MODIFIER "o\\\'",
                                  data_word);
@@ -952,14 +949,17 @@ EXPAND_FUNCTION_NAME(MR_TypeInfo type_info, MR_Word *data_word_ptr,
          case MR_TYPECTOR_REP_STRING:
  #ifdef  EXPAND_FUNCTOR_FIELD
              {
-                // XXX Should escape characters correctly.
                  MR_Word data_word;
                  char    *str;
-
+
                  data_word = *data_word_ptr;
-                MR_make_aligned_string_copy_saved_hp_quote(str,
-                        (MR_String) data_word, NULL);
-                expand_info->EXPAND_FUNCTOR_FIELD = str;
+                if (MR_escape_string_quote(&str, (MR_ConstString)data_word)) {
+                    expand_info->EXPAND_FUNCTOR_FIELD = str;
+                } else {
+                    // XXX should throw an exception.
+                    MR_fatal_error(MR_STRINGIFY(EXPAND_FUNCTION_NAME)
+                        ": invalid string encoding");
+                }
              }
  #endif  // EXPAND_FUNCTOR_FIELD

diff --git a/runtime/mercury_string.c b/runtime/mercury_string.c
index 4c11f3f..a061509 100644
--- a/runtime/mercury_string.c
+++ b/runtime/mercury_string.c
@@ -91,6 +91,129 @@ MR_make_string(MR_AllocSiteInfoPtr alloc_id, const char *fmt, ...)
      return result;
  }

+// The code for this function should be kept in sync with that of the
+// quote_string predicates in library/term_io.m.
+MR_bool
+MR_escape_string_quote(MR_String *ptr, const char * string)
+{
+    MR_Integer pos = 0;
+    size_t  num_code_units = 0;
+    MR_Char ch;
+    MR_bool must_escape = MR_FALSE;
+
+    // Check if we need to add character escapes to the string.
+    //
+    while ((ch = MR_utf8_get_next((MR_String)string, &pos)) > 0) {
+        switch (ch) {
+            case '\a':
+            case '\b':
+            case '\f':
+            case '\n':
+            case '\t':
+            case '\r':
+            case '\v':
+            case '\"':
+            case '\\':
+                num_code_units += 2;
+                must_escape = MR_TRUE;
+                break;
+            default:
+                if (MR_is_control(ch)) {
+                    // All control characters that do not have a specific
+                    // backslash escape are octal escaped.
+                    // This takes five code units.
+                    num_code_units += 5;
+                    must_escape = MR_TRUE;
+                } else {
+                    num_code_units += MR_utf8_width(ch);
+                }
+        }
+    }
+
+    // Check that there the string's encoding was valid.
+    //
+    if (ch < 0) {
+        *ptr = NULL;
+        return MR_FALSE;
+    }
+
+    if (must_escape) {
+
+        char *dst;
+ 
+        MR_allocate_aligned_string_saved_hp(*ptr,
+            num_code_units + 2 /* quotes */ + 1 /* \0 */,
+            NULL);
+
+        dst = *ptr;
+        dst[0] = '\"';
+        dst++;
+        pos = 0;
+        while ((ch = MR_utf8_get_next((MR_String)string, &pos)) > 0) {
+            switch (ch) {
+                case '\a':
+                    dst[0] = '\\';
+                    dst[1] = 'a';
+                    dst += 2;
+                    break; 
+                case '\b':
+                    dst[0] = '\\';
+                    dst[1] = 'b';
+                    dst += 2;
+                    break; 
+                case '\f':
+                    dst[0] = '\\';
+                    dst[1] = 'f';
+                    dst += 2;
+                    break; 
+                case '\n':
+                    dst[0] = '\\';
+                    dst[1] = 'n';
+                    dst += 2;
+                    break; 
+                case '\t':
+                    dst[0] = '\\';
+                    dst[1] = 't';
+                    dst += 2;
+                    break; 
+                case '\r':
+                    dst[0] = '\\';
+                    dst[1] = 'r';
+                    dst += 2;
+                    break; 
+                case '\v':
+                    dst[0] = '\\';
+                    dst[1] = 'b';
+                    dst += 2;
+                    break; 
+                case '\"':
+                    dst[0] = '\\';
+                    dst[1] = '\"';
+                    dst += 2;
+                    break; 
+                case '\\':
+                    dst[0] = '\\';
+                    dst[1] = '\\';
+                    dst += 2;
+                    break;
+                default:
+                    if (MR_is_control(ch)) {
+                        sprintf(dst, "\\%03" MR_INTEGER_LENGTH_MODIFIER "o\\",
+                            (MR_Integer)ch);
+                        dst += 5;
+                    } else {
+                        dst += MR_utf8_encode(dst, ch);
+                    }
+             }
+        }
+        dst[0] = '\"';
+        dst[1] = '\0';
+    } else {
+        MR_make_aligned_string_copy_saved_hp_quote(*ptr, string, NULL);
+    }
+    return MR_TRUE;
+}
+
  // Note that MR_hash_string{,2,3,4,5,6} are actually defined as macros in
  // mercury_string.h, if we are using GNU C.
  // We define them here whether or not we are using gcc, so that users
diff --git a/runtime/mercury_string.h b/runtime/mercury_string.h
index 7d1bd99..4d65c39 100644
--- a/runtime/mercury_string.h
+++ b/runtime/mercury_string.h
@@ -383,6 +383,15 @@ MR_Integer      MR_hash_string6(MR_ConstString);

  MR_String MR_make_string(MR_AllocSiteInfoPtr alloc_id, const char *fmt, ...);

+// Given a Mercury string `string', make a copy that inserts any required
+// character escapes and places double quote marks at the start and end of
+// the copy. On success, returns MR_TRUE and sets `ptr' to point to the copy
+// of the string. Returns MR_FALSE if `string' is not a valid UTF-8 encoded
+// string.
+//
+extern MR_bool MR_escape_string_quote(MR_String *ptr,
+    const char * string);
+
  // True if c is an ASCII code point, i.e. U+0000..U+007f.

  #define MR_is_ascii(c)              ((unsigned)(c) <= 0x7f)
@@ -391,6 +400,12 @@ MR_String MR_make_string(MR_AllocSiteInfoPtr alloc_id, const char *fmt, ...);

  #define MR_is_surrogate(c)          (((unsigned)(c) & 0xF800) == 0xD800)

+// True if c is a Unicode control code point, i.e. U+0000..U+001f,
+// U+007f..U+009f.
+
+#define MR_is_control(c) ((0x00 <= (unsigned)(c) && (unsigned)(c) <= 0x1f) || \
+                          (0x7f <= (unsigned)(c) && (unsigned)(c) <= 0x9f))
+
  // UTF-8 manipulation

  #define MR_utf8_is_single_byte(c)   (((unsigned)(c) & 0x80) == 0)
diff --git a/tests/hard_coded/deconstruct_arg.exp b/tests/hard_coded/deconstruct_arg.exp
index 7f4ef4f..49ed6e9 100644
--- a/tests/hard_coded/deconstruct_arg.exp
+++ b/tests/hard_coded/deconstruct_arg.exp
@@ -376,6 +376,104 @@ deconstruct deconstruct: functor 'Ω' arity 0
  deconstruct limited deconstruct 3 of 'Ω'
  functor 'Ω' arity 0 []

+deconstruct functor: ""/0
+deconstruct argument 0 of "" doesn't exist
+deconstruct argument 1 of "" doesn't exist
+deconstruct argument 2 of "" doesn't exist
+deconstruct argument 'moo' doesn't exist
+deconstruct argument 'mooo!' doesn't exist
+deconstruct argument 'packed1' doesn't exist
+deconstruct argument 'packed2' doesn't exist
+deconstruct argument 'packed3' doesn't exist
+deconstruct deconstruct: functor "" arity 0
+[]
+deconstruct limited deconstruct 3 of ""
+functor "" arity 0 []
+
+deconstruct functor: "azBZ09"/0
+deconstruct argument 0 of "azBZ09" doesn't exist
+deconstruct argument 1 of "azBZ09" doesn't exist
+deconstruct argument 2 of "azBZ09" doesn't exist
+deconstruct argument 'moo' doesn't exist
+deconstruct argument 'mooo!' doesn't exist
+deconstruct argument 'packed1' doesn't exist
+deconstruct argument 'packed2' doesn't exist
+deconstruct argument 'packed3' doesn't exist
+deconstruct deconstruct: functor "azBZ09" arity 0
+[]
+deconstruct limited deconstruct 3 of "azBZ09"
+functor "azBZ09" arity 0 []
+
+deconstruct functor: "α∀🜓"/0
+deconstruct argument 0 of "α∀🜓" doesn't exist
+deconstruct argument 1 of "α∀🜓" doesn't exist
+deconstruct argument 2 of "α∀🜓" doesn't exist
+deconstruct argument 'moo' doesn't exist
+deconstruct argument 'mooo!' doesn't exist
+deconstruct argument 'packed1' doesn't exist
+deconstruct argument 'packed2' doesn't exist
+deconstruct argument 'packed3' doesn't exist
+deconstruct deconstruct: functor "α∀🜓" arity 0
+[]
+deconstruct limited deconstruct 3 of "α∀🜓"
+functor "α∀🜓" arity 0 []
+
+deconstruct functor: "\a\b\f\n\t\r\b\"\\"/0
+deconstruct argument 0 of "\a\b\f\n\t\r\v\"\\" doesn't exist
+deconstruct argument 1 of "\a\b\f\n\t\r\v\"\\" doesn't exist
+deconstruct argument 2 of "\a\b\f\n\t\r\v\"\\" doesn't exist
+deconstruct argument 'moo' doesn't exist
+deconstruct argument 'mooo!' doesn't exist
+deconstruct argument 'packed1' doesn't exist
+deconstruct argument 'packed2' doesn't exist
+deconstruct argument 'packed3' doesn't exist
+deconstruct deconstruct: functor "\a\b\f\n\t\r\b\"\\" arity 0
+[]
+deconstruct limited deconstruct 3 of "\a\b\f\n\t\r\v\"\\"
+functor "\a\b\f\n\t\r\b\"\\" arity 0 []
+
+deconstruct functor: "\001\\a\037\AZ[`az~\177\"/0
+deconstruct argument 0 of "\001\\a\037\AZ[`az~\177\" doesn't exist
+deconstruct argument 1 of "\001\\a\037\AZ[`az~\177\" doesn't exist
+deconstruct argument 2 of "\001\\a\037\AZ[`az~\177\" doesn't exist
+deconstruct argument 'moo' doesn't exist
+deconstruct argument 'mooo!' doesn't exist
+deconstruct argument 'packed1' doesn't exist
+deconstruct argument 'packed2' doesn't exist
+deconstruct argument 'packed3' doesn't exist
+deconstruct deconstruct: functor "\001\\a\037\AZ[`az~\177\" arity 0
+[]
+deconstruct limited deconstruct 3 of "\001\\a\037\AZ[`az~\177\"
+functor "\001\\a\037\AZ[`az~\177\" arity 0 []
+
+deconstruct functor: "\200\\a\237\ Àÿ"/0
+deconstruct argument 0 of "\200\\a\237\ Àÿ" doesn't exist
+deconstruct argument 1 of "\200\\a\237\ Àÿ" doesn't exist
+deconstruct argument 2 of "\200\\a\237\ Àÿ" doesn't exist
+deconstruct argument 'moo' doesn't exist
+deconstruct argument 'mooo!' doesn't exist
+deconstruct argument 'packed1' doesn't exist
+deconstruct argument 'packed2' doesn't exist
+deconstruct argument 'packed3' doesn't exist
+deconstruct deconstruct: functor "\200\\a\237\ Àÿ" arity 0
+[]
+deconstruct limited deconstruct 3 of "\200\\a\237\ Àÿ"
+functor "\200\\a\237\ Àÿ" arity 0 []
+
+deconstruct functor: "α\nβ\tγ,a\nα\001\α\001\a\001\α"/0
+deconstruct argument 0 of "α\nβ\tγ,a\nα\001\α\001\a\001\α" doesn't exist
+deconstruct argument 1 of "α\nβ\tγ,a\nα\001\α\001\a\001\α" doesn't exist
+deconstruct argument 2 of "α\nβ\tγ,a\nα\001\α\001\a\001\α" doesn't exist
+deconstruct argument 'moo' doesn't exist
+deconstruct argument 'mooo!' doesn't exist
+deconstruct argument 'packed1' doesn't exist
+deconstruct argument 'packed2' doesn't exist
+deconstruct argument 'packed3' doesn't exist
+deconstruct deconstruct: functor "α\nβ\tγ,a\nα\001\α\001\a\001\α" arity 0
+[]
+deconstruct limited deconstruct 3 of "α\nβ\tγ,a\nα\001\α\001\a\001\α"
+functor "α\nβ\tγ,a\nα\001\α\001\a\001\α" arity 0 []
+
  deconstruct functor: 0.12345678901234566/0
  deconstruct argument 0 of 0.12345678901234566 doesn't exist
  deconstruct argument 1 of 0.12345678901234566 doesn't exist
@@ -600,7 +698,7 @@ deconstruct deconstruct: functor newline arity 0
  deconstruct limited deconstruct 3 of '<<predicate>>'
  functor newline arity 0 []

-deconstruct functor: lambda_deconstruct_arg_m_182/1
+deconstruct functor: lambda_deconstruct_arg_m_193/1
  deconstruct argument 0 of '<<predicate>>' is [1, 2]
  deconstruct argument 1 of '<<predicate>>' doesn't exist
  deconstruct argument 2 of '<<predicate>>' doesn't exist
@@ -609,10 +707,10 @@ deconstruct argument 'mooo!' doesn't exist
  deconstruct argument 'packed1' doesn't exist
  deconstruct argument 'packed2' doesn't exist
  deconstruct argument 'packed3' doesn't exist
-deconstruct deconstruct: functor lambda_deconstruct_arg_m_182 arity 1
+deconstruct deconstruct: functor lambda_deconstruct_arg_m_193 arity 1
  [[1, 2]]
  deconstruct limited deconstruct 3 of '<<predicate>>'
-functor lambda_deconstruct_arg_m_182 arity 1 [[1, 2]]
+functor lambda_deconstruct_arg_m_193 arity 1 [[1, 2]]

  deconstruct functor: p/3
  deconstruct argument 0 of '<<predicate>>' is 1
diff --git a/tests/hard_coded/deconstruct_arg.exp2 b/tests/hard_coded/deconstruct_arg.exp2
index bc508fa..eb16866 100644
--- a/tests/hard_coded/deconstruct_arg.exp2
+++ b/tests/hard_coded/deconstruct_arg.exp2
@@ -376,6 +376,104 @@ deconstruct deconstruct: functor 'Ω' arity 0
  deconstruct limited deconstruct 3 of 'Ω'
  functor 'Ω' arity 0 []

+deconstruct functor: ""/0
+deconstruct argument 0 of "" doesn't exist
+deconstruct argument 1 of "" doesn't exist
+deconstruct argument 2 of "" doesn't exist
+deconstruct argument 'moo' doesn't exist
+deconstruct argument 'mooo!' doesn't exist
+deconstruct argument 'packed1' doesn't exist
+deconstruct argument 'packed2' doesn't exist
+deconstruct argument 'packed3' doesn't exist
+deconstruct deconstruct: functor "" arity 0
+[]
+deconstruct limited deconstruct 3 of ""
+functor "" arity 0 []
+
+deconstruct functor: "azBZ09"/0
+deconstruct argument 0 of "azBZ09" doesn't exist
+deconstruct argument 1 of "azBZ09" doesn't exist
+deconstruct argument 2 of "azBZ09" doesn't exist
+deconstruct argument 'moo' doesn't exist
+deconstruct argument 'mooo!' doesn't exist
+deconstruct argument 'packed1' doesn't exist
+deconstruct argument 'packed2' doesn't exist
+deconstruct argument 'packed3' doesn't exist
+deconstruct deconstruct: functor "azBZ09" arity 0
+[]
+deconstruct limited deconstruct 3 of "azBZ09"
+functor "azBZ09" arity 0 []
+
+deconstruct functor: "α∀🜓"/0
+deconstruct argument 0 of "α∀🜓" doesn't exist
+deconstruct argument 1 of "α∀🜓" doesn't exist
+deconstruct argument 2 of "α∀🜓" doesn't exist
+deconstruct argument 'moo' doesn't exist
+deconstruct argument 'mooo!' doesn't exist
+deconstruct argument 'packed1' doesn't exist
+deconstruct argument 'packed2' doesn't exist
+deconstruct argument 'packed3' doesn't exist
+deconstruct deconstruct: functor "α∀🜓" arity 0
+[]
+deconstruct limited deconstruct 3 of "α∀🜓"
+functor "α∀🜓" arity 0 []
+
+deconstruct functor: "\a\b\f\n\t\r\b\"\\"/0
+deconstruct argument 0 of "\a\b\f\n\t\r\v\"\\" doesn't exist
+deconstruct argument 1 of "\a\b\f\n\t\r\v\"\\" doesn't exist
+deconstruct argument 2 of "\a\b\f\n\t\r\v\"\\" doesn't exist
+deconstruct argument 'moo' doesn't exist
+deconstruct argument 'mooo!' doesn't exist
+deconstruct argument 'packed1' doesn't exist
+deconstruct argument 'packed2' doesn't exist
+deconstruct argument 'packed3' doesn't exist
+deconstruct deconstruct: functor "\a\b\f\n\t\r\b\"\\" arity 0
+[]
+deconstruct limited deconstruct 3 of "\a\b\f\n\t\r\v\"\\"
+functor "\a\b\f\n\t\r\b\"\\" arity 0 []
+
+deconstruct functor: "\001\\a\037\AZ[`az~\177\"/0
+deconstruct argument 0 of "\001\\a\037\AZ[`az~\177\" doesn't exist
+deconstruct argument 1 of "\001\\a\037\AZ[`az~\177\" doesn't exist
+deconstruct argument 2 of "\001\\a\037\AZ[`az~\177\" doesn't exist
+deconstruct argument 'moo' doesn't exist
+deconstruct argument 'mooo!' doesn't exist
+deconstruct argument 'packed1' doesn't exist
+deconstruct argument 'packed2' doesn't exist
+deconstruct argument 'packed3' doesn't exist
+deconstruct deconstruct: functor "\001\\a\037\AZ[`az~\177\" arity 0
+[]
+deconstruct limited deconstruct 3 of "\001\\a\037\AZ[`az~\177\"
+functor "\001\\a\037\AZ[`az~\177\" arity 0 []
+
+deconstruct functor: "\200\\a\237\ Àÿ"/0
+deconstruct argument 0 of "\200\\a\237\ Àÿ" doesn't exist
+deconstruct argument 1 of "\200\\a\237\ Àÿ" doesn't exist
+deconstruct argument 2 of "\200\\a\237\ Àÿ" doesn't exist
+deconstruct argument 'moo' doesn't exist
+deconstruct argument 'mooo!' doesn't exist
+deconstruct argument 'packed1' doesn't exist
+deconstruct argument 'packed2' doesn't exist
+deconstruct argument 'packed3' doesn't exist
+deconstruct deconstruct: functor "\200\\a\237\ Àÿ" arity 0
+[]
+deconstruct limited deconstruct 3 of "\200\\a\237\ Àÿ"
+functor "\200\\a\237\ Àÿ" arity 0 []
+
+deconstruct functor: "α\nβ\tγ,a\nα\001\α\001\a\001\α"/0
+deconstruct argument 0 of "α\nβ\tγ,a\nα\001\α\001\a\001\α" doesn't exist
+deconstruct argument 1 of "α\nβ\tγ,a\nα\001\α\001\a\001\α" doesn't exist
+deconstruct argument 2 of "α\nβ\tγ,a\nα\001\α\001\a\001\α" doesn't exist
+deconstruct argument 'moo' doesn't exist
+deconstruct argument 'mooo!' doesn't exist
+deconstruct argument 'packed1' doesn't exist
+deconstruct argument 'packed2' doesn't exist
+deconstruct argument 'packed3' doesn't exist
+deconstruct deconstruct: functor "α\nβ\tγ,a\nα\001\α\001\a\001\α" arity 0
+[]
+deconstruct limited deconstruct 3 of "α\nβ\tγ,a\nα\001\α\001\a\001\α"
+functor "α\nβ\tγ,a\nα\001\α\001\a\001\α" arity 0 []
+
  deconstruct functor: 0.12345678901234566/0
  deconstruct argument 0 of 0.12345678901234566 doesn't exist
  deconstruct argument 1 of 0.12345678901234566 doesn't exist
diff --git a/tests/hard_coded/deconstruct_arg.m b/tests/hard_coded/deconstruct_arg.m
index 4e7f89f..a530ac6 100644
--- a/tests/hard_coded/deconstruct_arg.m
+++ b/tests/hard_coded/deconstruct_arg.m
@@ -146,6 +146,17 @@ main(!IO) :-
      % UTF-8 encoding.
      test_all('Ω', !IO),

+    % test strings (that do not require escapes)
+    test_all("", !IO),
+    test_all("azBZ09", !IO),
+    test_all("\u03b1\u2200\U0001f713", !IO),
+
+    % test strings (that do require escapes)
+    test_all("\a\b\f\n\t\r\v\"\\", !IO),
+    test_all("\x1\\a\x1f\AZ[`az~\x7f\", !IO),
+    test_all("\x80\\a\x9f\\xa0\\xc0\\xff\", !IO),
+    test_all("α\nβ\tγ,a\nα\001\α\001\a\001\α", !IO),
+
      % test a float which requires 17 digits of precision
      test_all(0.12345678901234566, !IO),

@@ -157,7 +168,7 @@ main(!IO) :-
      % test integers
      test_all(4, !IO),

-    % test unigned integers
+    % test unsigned integers
      test_all(561u, !IO),

      % test fixed size integers.


More information about the reviews mailing list