summaryrefslogtreecommitdiff
path: root/ocaml-binary-annot/testsuite/tests/lib-scanf/tscanf.ml
diff options
context:
space:
mode:
Diffstat (limited to 'ocaml-binary-annot/testsuite/tests/lib-scanf/tscanf.ml')
-rw-r--r--ocaml-binary-annot/testsuite/tests/lib-scanf/tscanf.ml1477
1 files changed, 1477 insertions, 0 deletions
diff --git a/ocaml-binary-annot/testsuite/tests/lib-scanf/tscanf.ml b/ocaml-binary-annot/testsuite/tests/lib-scanf/tscanf.ml
new file mode 100644
index 0000000000..cab4d7be4d
--- /dev/null
+++ b/ocaml-binary-annot/testsuite/tests/lib-scanf/tscanf.ml
@@ -0,0 +1,1477 @@
+(*************************************************************************)
+(* *)
+(* Objective Caml *)
+(* *)
+(* Pierre Weis, projet Cristal, INRIA Rocquencourt *)
+(* *)
+(* Copyright 2002 Institut National de Recherche en Informatique et *)
+(* en Automatique. All rights reserved. This file is distributed *)
+(* under the terms of the Q Public License version 1.0. *)
+(* *)
+(*************************************************************************)
+
+(* $Id: tscanf.ml 9306 2009-07-03 08:36:54Z weis $
+
+A testbed file for the module Scanf.
+
+*)
+
+open Testing;;
+
+open Scanf;;
+
+(* The ``continuation'' that returns the scanned value. *)
+let id x = x;;
+
+(* Testing space scanning. *)
+let test0 () =
+ (sscanf "" "" id) 1 +
+ (sscanf "" " " id) 2 +
+ (sscanf " " " " id) 3 +
+ (sscanf "\t" " " id) 4 +
+ (sscanf "\n" " " id) 5 +
+ (sscanf "\n\t 6" " %d" id)
+;;
+
+test (test0 () = 21)
+;;
+
+(* Testing integer scanning %i and %d. *)
+let test1 () =
+ sscanf "1" "%d" id +
+ sscanf " 2" " %d" id +
+ sscanf " -2" " %d" id +
+ sscanf " +2" " %d" id +
+ sscanf " 2a " " %da" id
+;;
+
+test (test1 () = 5)
+;;
+
+let test2 () =
+ sscanf "123" "%2i" id +
+ sscanf "245" "%d" id +
+ sscanf " 2a " " %1da" id
+;;
+
+test (test2 () = 259)
+;;
+
+let test3 () =
+ sscanf "0xff" "%3i" id +
+ sscanf "0XEF" "%3i" id +
+ sscanf "x=-245" " x = %d" id +
+ sscanf " 2a " " %1da" id
+;;
+
+test (test3 () = -214)
+;;
+
+(* Testing float scanning. *)
+(* f style. *)
+let test4 () =
+ bscanf (Scanning.from_string "1")
+ "%f" (fun b0 -> b0 = 1.0) &&
+ bscanf (Scanning.from_string "-1")
+ "%f" (fun b0 -> b0 = -1.0) &&
+ bscanf (Scanning.from_string "+1")
+ "%f" (fun b0 -> b0 = 1.0) &&
+ bscanf (Scanning.from_string "1.")
+ "%f" (fun b0 -> b0 = 1.0) &&
+ bscanf (Scanning.from_string ".1")
+ "%f" (fun b0 -> b0 = 0.1) &&
+ bscanf (Scanning.from_string "-.1")
+ "%f" (fun b0 -> b0 = -0.1) &&
+ bscanf (Scanning.from_string "+.1")
+ "%f" (fun b0 -> b0 = 0.1) &&
+ bscanf (Scanning.from_string "+1.")
+ "%f" (fun b0 -> b0 = 1.0) &&
+ bscanf (Scanning.from_string "-1.")
+ "%f" (fun b0 -> b0 = -1.0) &&
+ bscanf (Scanning.from_string "0 1. 1.3")
+ "%f %f %f" (fun b0 b1 b2 -> b0 = 0.0 && b1 = 1.0 && b2 = 1.3) &&
+ bscanf (Scanning.from_string "0.113")
+ "%4f" (fun b0 -> b0 = 0.11) &&
+ bscanf (Scanning.from_string "0.113")
+ "%5f" (fun b0 -> b0 = 0.113) &&
+ bscanf (Scanning.from_string "000.113")
+ "%15f" (fun b0 -> b0 = 0.113) &&
+ bscanf (Scanning.from_string "+000.113")
+ "%15f" (fun b0 -> b0 = 0.113) &&
+ bscanf (Scanning.from_string "-000.113")
+ "%15f" (fun b0 -> b0 = -0.113)
+;;
+test (test4 ())
+;;
+
+(* e style. *)
+let test5 () =
+ bscanf (Scanning.from_string "1e1")
+ "%e" (fun b -> b = 10.0) &&
+ bscanf (Scanning.from_string "1e+1")
+ "%e" (fun b -> b = 10.0) &&
+ bscanf (Scanning.from_string "10e-1")
+ "%e" (fun b -> b = 1.0) &&
+ bscanf (Scanning.from_string "10.e-1")
+ "%e" (fun b -> b = 1.0) &&
+ bscanf (Scanning.from_string "1e1 1.e+1 1.3e-1")
+ "%e %e %e" (fun b1 b2 b3 -> b1 = 10.0 && b2 = b1 && b3 = 0.13) &&
+
+(* g style. *)
+ bscanf (Scanning.from_string "1 1.1 0e+1 1.3e-1")
+ "%g %g %g %g"
+ (fun b1 b2 b3 b4 ->
+ b1 = 1.0 && b2 = 1.1 && b3 = 0.0 && b4 = 0.13)
+;;
+
+test (test5 ())
+;;
+
+(* Testing boolean scanning. *)
+let test6 () =
+ bscanf (Scanning.from_string "truetrue") "%B%B"
+ (fun b1 b2 -> (b1, b2) = (true, true)) &&
+ bscanf (Scanning.from_string "truefalse") "%B%B"
+ (fun b1 b2 -> (b1, b2) = (true, false)) &&
+ bscanf (Scanning.from_string "falsetrue") "%B%B"
+ (fun b1 b2 -> (b1, b2) = (false, true)) &&
+ bscanf (Scanning.from_string "falsefalse") "%B%B"
+ (fun b1 b2 -> (b1, b2) = (false, false)) &&
+ bscanf (Scanning.from_string "true false") "%B %B"
+ (fun b1 b2 -> (b1, b2) = (true, false))
+;;
+
+test (test6 ())
+;;
+
+(* Testing char scanning. *)
+
+let test7 () =
+ bscanf (Scanning.from_string "'a' '\n' '\t' '\000' '\032'")
+ "%C %C %C %C %C"
+ (fun c1 c2 c3 c4 c5 ->
+ c1 = 'a' && c2 = '\n' && c3 = '\t' && c4 = '\000' && c5 = '\032') &&
+
+(* Here \n, \t, and \032 are skipped due to the space semantics of scanf. *)
+ bscanf (Scanning.from_string "a \n \t \000 \032b")
+ "%c %c %c "
+ (fun c1 c2 c3 ->
+ c1 = 'a' && c2 = '\000' && c3 = 'b')
+;;
+
+test (test7 ())
+;;
+
+let verify_read c =
+ let s = Printf.sprintf "%C" c in
+ let ib = Scanning.from_string s in
+ assert (bscanf ib "%C" id = c)
+;;
+
+let verify_scan_Chars () =
+ for i = 0 to 255 do verify_read (char_of_int i) done
+;;
+
+let test8 () = verify_scan_Chars () = ();;
+
+test (test8 ())
+;;
+
+(* Testing string scanning. *)
+
+(* %S and %s styles. *)
+let unit fmt s =
+ let ib = Scanning.from_string (Printf.sprintf "%S" s) in
+ Scanf.bscanf ib fmt id
+;;
+
+let test_fmt fmt s = unit fmt s = s;;
+
+(* The following test9_string is a result for test9 scanning.
+ Test9_string is the string "",
+ that is character i tréma, followed by french right guillemet,
+ followed by inverted question mark.
+ It is NOT the string "Ôªø",
+ that is uppercase o with circonflex accent, followed by commercial a,
+ followed by empty set.
+
+ In other words, the string "" has the following 3 characters
+ "\239\187\191".
+ It has NOT the characters "\212\170\248"!
+
+ Beware with automatic translation by your own local settings
+ (being your locale or your OS!)
+*)
+let test9_string = "";;
+
+let test_S = test_fmt "%S";;
+let test9 () =
+ test_S "poi" &&
+ test_S "a\"b" &&
+ test_S "a\nb" &&
+ test_S "a\010b" &&
+ test_S "a\\\n\
+ b \\\n\
+ c\010\\\n\
+ b" &&
+ test_S "a\\\n\
+ \\\n\
+ \\\n\
+ b \\\n\
+ c\010\\\n\
+ b" &&
+ test_S "\xef" &&
+ test_S "\\xef" &&
+ Scanf.sscanf "\"\\xef\"" "%S" (fun s -> s) =
+ "\xef" &&
+ Scanf.sscanf "\"\\xef\\xbb\\xbf\"" "%S" (fun s -> s) =
+ test9_string &&
+ Scanf.sscanf "\"\\xef\\xbb\\xbf\"" "%S" (fun s -> s) =
+ "\239\187\191" &&
+ Scanf.sscanf "\"\xef\xbb\xbf\"" "%S" (fun s -> s) =
+ test9_string &&
+ Scanf.sscanf "\"\\\\xef\\\\xbb\\\\xbf\"" "%S" (fun s -> s) =
+ "\\xef\\xbb\\xbf" &&
+ Scanf.sscanf "\"\ \"" "%S" (fun s -> s) =
+ "\ "
+;;
+
+test (test9 ())
+;;
+
+let test10 () =
+ let unit s =
+ let ib = Scanning.from_string s in
+ Scanf.bscanf ib "%S" id in
+
+ let res =
+ sscanf "Une chaîne: \"celle-ci\" et \"celle-là\"!"
+ "%s %s %S %s %S %s"
+ (fun s1 s2 s3 s4 s5 s6 -> s1 ^ s2 ^ s3 ^ s4 ^ s5 ^ s6) in
+ res = "Unechaîne:celle-cietcelle-là!" &&
+ (* Testing the result of reading a %S string. *)
+ unit "\"a\\\n b\"" = "ab" &&
+ unit "\"\\\n ab\"" = "ab" &&
+ unit "\"\n\\\n ab\"" = "\nab" &&
+ unit "\"\n\\\n a\nb\"" = "\na\nb" &&
+ unit "\"\n\\\n \\\n a\nb\"" = "\na\nb" &&
+ unit "\"\n\\\n a\n\\\nb\\\n\"" = "\na\nb" &&
+ unit "\"a\\\n \"" = "a" &&
+ true
+;;
+
+test (test10 ())
+;;
+
+(* %[] style *)
+let test11 () =
+ sscanf "Pierre Weis 70" "%s %s %s"
+ (fun prenom nom poids ->
+ prenom = "Pierre" && nom = "Weis" && int_of_string poids = 70)
+ &&
+ sscanf "Jean-Luc de Léage 68" "%[^ ] %[^ ] %d"
+ (fun prenom nom poids ->
+ prenom = "Jean-Luc" && nom = "de Léage" && poids = 68)
+ &&
+ sscanf "Daniel de Rauglaudre 66" "%s@\t %s@\t %d"
+ (fun prenom nom poids ->
+ prenom = "Daniel" && nom = "de Rauglaudre" && poids = 66)
+;;
+
+(* Empty string (end of input) testing. *)
+let test110 () =
+ sscanf "" " " (fun x -> x) "" = "" &&
+ sscanf "" "%s" (fun x -> x = "") &&
+ sscanf "" "%s%s" (fun x y -> x = "" && y = "") &&
+ sscanf "" "%s " (fun x -> x = "") &&
+ sscanf "" " %s" (fun x -> x = "") &&
+ sscanf "" " %s " (fun x -> x = "") &&
+ sscanf "" "%[^\n]" (fun x -> x = "") &&
+ sscanf "" "%[^\n] " (fun x -> x = "") &&
+ sscanf " " "%s" (fun x -> x = "") &&
+ sscanf " " "%s%s" (fun x y -> x = "" && y = "") &&
+ sscanf " " " %s " (fun x -> x = "") &&
+ sscanf " " " %s %s" (fun x y -> x = "" && x = y) &&
+ sscanf " " " %s@ %s" (fun x y -> x = "" && x = y) &&
+ sscanf " poi !" " %s@ %s@." (fun x y -> x = "poi" && y = "!") &&
+ sscanf " poi !" "%s@ %s@." (fun x y -> x = "" && y = "poi !")
+;;
+
+let test111 () = sscanf "" "%[^\n]@\n" (fun x -> x = "");;
+
+test (test11 () && test110 () && test111 ())
+;;
+
+(* Scanning lists. *)
+let ib () = Scanning.from_string "[1;2;3;4; ]";;
+
+(* Statically known lists can be scanned directly. *)
+let f ib =
+ bscanf ib " [" ();
+ bscanf ib " %i;" (fun i ->
+ bscanf ib " %i;" (fun j ->
+ bscanf ib " %i;" (fun k ->
+ bscanf ib " %i;" (fun l ->
+ bscanf ib " ]" ();
+ [i; j; k; l]))));;
+
+let test12 () = f (ib ()) = [1; 2; 3; 4];;
+
+test (test12 ())
+;;
+
+(* A general list scanner that always fails to succeed. *)
+let rec scan_elems ib accu =
+ try bscanf ib " %i;" (fun i -> scan_elems ib (i :: accu)) with
+ | _ -> accu
+;;
+
+let g ib = bscanf ib "[ " (); List.rev (scan_elems ib []);;
+
+let test13 () = g (ib ()) = [1; 2; 3; 4];;
+
+test (test13 ())
+;;
+
+(* A general int list scanner. *)
+let rec scan_int_list ib =
+ bscanf ib "[ " ();
+ let accu = scan_elems ib [] in
+ bscanf ib " ]" ();
+ List.rev accu
+;;
+
+let test14 () = scan_int_list (ib ()) = [1; 2; 3; 4];;
+
+test (test14 ())
+;;
+
+(* A general list scanner that always succeeds. *)
+let rec scan_elems ib accu =
+ bscanf ib " %i %c"
+ (fun i -> function
+ | ';' -> scan_elems ib (i :: accu)
+ | ']' -> List.rev (i :: accu)
+ | c -> failwith "scan_elems")
+;;
+
+let rec scan_int_list ib =
+ bscanf ib "[ " ();
+ scan_elems ib []
+;;
+
+let test15 () =
+ scan_int_list (Scanning.from_string "[1;2;3;4]") = [1; 2; 3; 4];;
+
+test (test15 ())
+;;
+
+let rec scan_elems ib accu =
+ try
+ bscanf ib "%c %i"
+ (fun c i ->
+ match c with
+ | ';' -> scan_elems ib (i :: accu)
+ | ']' -> List.rev (i :: accu)
+ | '[' when accu = [] -> scan_elems ib (i :: accu)
+ | c -> print_endline (String.make 1 c); failwith "scan_elems")
+ with
+ | Scan_failure _ -> bscanf ib "]" (); accu
+ | End_of_file -> accu
+;;
+
+let scan_int_list ib = scan_elems ib [];;
+
+let test16 () =
+ scan_int_list (Scanning.from_string "[]") = List.rev [] &&
+ scan_int_list (Scanning.from_string "[1;2;3;4]") = List.rev [1;2;3;4] &&
+ scan_int_list (Scanning.from_string "[1;2;3;4; ]") = List.rev [1;2;3;4] &&
+ (* Should fail but succeeds! *)
+ scan_int_list (Scanning.from_string "[1;2;3;4") = List.rev [1;2;3;4];;
+
+test (test16 ())
+;;
+
+let rec scan_elems ib accu =
+ bscanf ib " %i%[]; \t\n\r]"
+ (fun i s ->
+ match s with
+ | ";" -> scan_elems ib (i :: accu)
+ | "]" -> List.rev (i :: accu)
+ | s -> List.rev (i :: accu))
+;;
+
+let scan_int_list ib =
+ bscanf ib " [" ();
+ scan_elems ib []
+;;
+
+let test17 () =
+ scan_int_list (Scanning.from_string "[1;2;3;4]") = [1;2;3;4] &&
+ scan_int_list (Scanning.from_string "[1;2;3;4; ]") = [1;2;3;4] &&
+ (* Should fail but succeeds! *)
+ scan_int_list (Scanning.from_string "[1;2;3;4 5]") = [1;2;3;4];;
+
+test (test17 ())
+;;
+
+let rec scan_elems ib accu =
+ bscanf ib " %c " (fun c ->
+ match c with
+ | '[' when accu = [] ->
+ (* begginning of list: could find either
+ - an int, if the list is not empty,
+ - the char ], if the list is empty. *)
+ bscanf ib "%[]]"
+ (function
+ | "]" -> accu
+ | _ ->
+ bscanf ib " %i " (fun i ->
+ scan_rest ib (i :: accu)))
+ | _ -> failwith "scan_elems")
+
+and scan_rest ib accu =
+ bscanf ib " %c " (fun c ->
+ match c with
+ | ';' ->
+ bscanf ib "%[]]"
+ (function
+ | "]" -> accu
+ | _ ->
+ bscanf ib " %i " (fun i ->
+ scan_rest ib (i :: accu)))
+ | ']' -> accu
+ | _ -> failwith "scan_rest")
+;;
+
+let scan_int_list ib = List.rev (scan_elems ib []);;
+
+let test18 () =
+ scan_int_list (Scanning.from_string "[]") = [] &&
+ scan_int_list (Scanning.from_string "[ ]") = [] &&
+ scan_int_list (Scanning.from_string "[1;2;3;4]") = [1;2;3;4] &&
+ scan_int_list (Scanning.from_string "[1;2;3;4; ]") = [1;2;3;4];;
+
+test (test18 ())
+;;
+
+(* Those properly fail *)
+
+let test19 () =
+ failure_test
+ scan_int_list (Scanning.from_string "[1;2;3;4 5]")
+ "scan_rest"
+;;
+
+(test19 ())
+;;
+
+let test20 () =
+ scan_failure_test
+ scan_int_list (Scanning.from_string "[1;2;3;4;; 5]");;
+
+(test20 ())
+;;
+
+let test21 () =
+ scan_failure_test
+ scan_int_list (Scanning.from_string "[1;2;3;4;;");;
+
+(test21 ())
+;;
+
+let rec scan_elems ib accu =
+ bscanf ib "%1[];]" (function
+ | "]" -> accu
+ | ";" -> scan_rest ib accu
+ | _ ->
+ failwith
+ (Printf.sprintf "scan_int_list" (*
+ "scan_int_list: char %i waiting for ']' or ';' but found %c"
+ (Scanning.char_count ib) (Scanning.peek_char ib)*)))
+
+and scan_rest ib accu =
+ bscanf ib "%[]]" (function
+ | "]" -> accu
+ | _ -> scan_elem ib accu)
+
+and scan_elem ib accu =
+ bscanf ib " %i " (fun i -> scan_elems ib (i :: accu))
+;;
+
+let scan_int_list ib =
+ bscanf ib " [ " ();
+ List.rev (scan_rest ib [])
+;;
+
+let test22 () =
+ scan_int_list (Scanning.from_string "[]") = [] &&
+ scan_int_list (Scanning.from_string "[ ]") = [] &&
+ scan_int_list (Scanning.from_string "[1]") = [1] &&
+ scan_int_list (Scanning.from_string "[1;2;3;4]") = [1;2;3;4] &&
+ scan_int_list (Scanning.from_string "[1;2;3;4;]") = [1;2;3;4];;
+
+test (test22 ())
+;;
+
+(* Should work but does not with this version of scan_int_list!
+scan_int_list (Scanning.from_string "[1;2;3;4; ]");;
+(* Should lead to a bad input error. *)
+scan_int_list (Scanning.from_string "[1;2;3;4 5]");;
+scan_int_list (Scanning.from_string "[1;2;3;4;;");;
+scan_int_list (Scanning.from_string "[1;2;3;4;; 5]");;
+scan_int_list (Scanning.from_string "[1;2;3;4;; 23]");;
+*)
+
+let rec scan_elems ib accu =
+ try bscanf ib " %i %1[;]" (fun i s ->
+ if s = "" then i :: accu else scan_elems ib (i :: accu)) with
+ | Scan_failure _ -> accu
+;;
+
+(* The general int list scanner. *)
+let rec scan_int_list ib =
+ bscanf ib "[ " ();
+ let accu = scan_elems ib [] in
+ bscanf ib " ]" ();
+ List.rev accu
+;;
+
+(* The general HO list scanner.
+ This version does not fix the separator, nor the spacing before and after
+ the separator (it uses the functional argument [scan_elem] to parse the
+ separator, its spacing, and the item).
+ *)
+let rec scan_elems ib scan_elem accu =
+ try scan_elem ib (fun i s ->
+ let accu = i :: accu in
+ if s = "" then accu else scan_elems ib scan_elem accu) with
+ | Scan_failure _ -> accu
+;;
+
+let scan_list scan_elem ib =
+ bscanf ib "[ " ();
+ let accu = scan_elems ib scan_elem [] in
+ bscanf ib " ]" ();
+ List.rev accu
+;;
+
+(* Deriving particular list scanners from the HO list scanner. *)
+let scan_int_elem ib = bscanf ib " %i %1[;]";;
+let scan_int_list = scan_list scan_int_elem;;
+
+let test23 () =
+ scan_int_list (Scanning.from_string "[]") = [] &&
+ scan_int_list (Scanning.from_string "[ ]") = [] &&
+ scan_int_list (Scanning.from_string "[1]") = [1] &&
+ scan_int_list (Scanning.from_string "[1;2;3;4]") = [1;2;3;4] &&
+ scan_int_list (Scanning.from_string "[1;2;3;4;]") = [1;2;3;4];;
+
+test (test23 ())
+;;
+
+let test24 () =
+ scan_failure_test scan_int_list (Scanning.from_string "[1;2;3;4 5]")
+and test25 () =
+ scan_failure_test scan_int_list (Scanning.from_string "[1;2;3;4;;")
+and test26 () =
+ scan_failure_test scan_int_list (Scanning.from_string "[1;2;3;4;; 5]")
+and test27 () =
+ scan_failure_test scan_int_list (Scanning.from_string "[1;2;3;4;; 23]");;
+
+ (test24 ()) &&
+ (test25 ()) &&
+ (test26 ()) &&
+ (test27 ())
+;;
+
+(* To scan a Caml string:
+ the format is "\"%s@\"".
+ A better way would be to add a %S (String.escaped), a %C (Char.escaped).
+ This is now available. *)
+let scan_string_elem ib = bscanf ib " \"%s@\" %1[;]";;
+let scan_string_list = scan_list scan_string_elem;;
+
+let scan_String_elem ib = bscanf ib " %S %1[;]";;
+let scan_String_list = scan_list scan_String_elem;;
+
+let test28 () =
+ scan_string_list (Scanning.from_string "[]") = [] &&
+ scan_string_list (Scanning.from_string "[\"Le\"]") = ["Le"] &&
+ scan_string_list
+ (Scanning.from_string "[\"Le\";\"langage\";\"Objective\";\"Caml\"]") =
+ ["Le"; "langage"; "Objective"; "Caml"] &&
+ scan_string_list
+ (Scanning.from_string "[\"Le\";\"langage\";\"Objective\";\"Caml\"; ]") =
+ ["Le"; "langage"; "Objective"; "Caml"] &&
+
+ scan_String_list (Scanning.from_string "[]") = [] &&
+ scan_String_list (Scanning.from_string "[\"Le\"]") = ["Le"] &&
+ scan_String_list
+ (Scanning.from_string "[\"Le\";\"langage\";\"Objective\";\"Caml\"]") =
+ ["Le"; "langage"; "Objective"; "Caml"] &&
+ scan_String_list
+ (Scanning.from_string "[\"Le\";\"langage\";\"Objective\";\"Caml\"; ]") =
+ ["Le"; "langage"; "Objective"; "Caml"];;
+
+test (test28 ())
+;;
+
+(* The general HO list scanner with continuations. *)
+let rec scan_elems ib scan_elem accu =
+ scan_elem ib
+ (fun i s ->
+ let accu = i :: accu in
+ if s = "" then accu else scan_elems ib scan_elem accu)
+ (fun ib exc -> accu)
+;;
+
+let scan_list scan_elem ib =
+ bscanf ib "[ " ();
+ let accu = scan_elems ib scan_elem [] in
+ bscanf ib " ]" ();
+ List.rev accu
+;;
+
+(* Deriving particular list scanners from the HO list scanner. *)
+let scan_int_elem ib f ek = kscanf ib ek " %i %1[;]" f;;
+let scan_int_list = scan_list scan_int_elem;;
+
+let test29 () =
+ scan_int_list (Scanning.from_string "[]") = [] &&
+ scan_int_list (Scanning.from_string "[ ]") = [] &&
+ scan_int_list (Scanning.from_string "[1]") = [1] &&
+ scan_int_list (Scanning.from_string "[1;2;3;4]") = [1;2;3;4] &&
+ scan_int_list (Scanning.from_string "[1;2;3;4;]") = [1;2;3;4];;
+
+test (test29 ())
+;;
+
+let scan_string_elem ib f ek = kscanf ib ek " %S %1[;]" f;;
+let scan_string_list = scan_list scan_string_elem;;
+
+let test30 () =
+ scan_string_list (Scanning.from_string "[]") = [] &&
+ scan_string_list (Scanning.from_string "[ ]") = [] &&
+ scan_string_list (Scanning.from_string "[ \"1\" ]") = ["1"] &&
+ scan_string_list
+ (Scanning.from_string "[\"1\"; \"2\"; \"3\"; \"4\"]") =
+ ["1"; "2"; "3"; "4"] &&
+ scan_string_list
+ (Scanning.from_string "[\"1\"; \"2\"; \"3\"; \"4\";]") =
+ ["1"; "2"; "3"; "4"];;
+
+test (test30 ())
+;;
+
+(* A generic polymorphic item scanner, *)
+let scan_elem fmt ib f ek = kscanf ib ek fmt f;;
+
+(* Derivation of list scanners from the generic polymorphic item scanner
+ applications. *)
+let scan_int_list = scan_list (scan_elem " %i %1[;]");;
+let scan_string_list = scan_list (scan_elem " %S %1[;]");;
+let scan_bool_list = scan_list (scan_elem " %B %1[;]");;
+let scan_char_list = scan_list (scan_elem " %C %1[;]");;
+let scan_float_list = scan_list (scan_elem " %f %1[;]");;
+
+(* In this version the [scan_elem] function should be a [kscanf] like
+ scanning function: we give it an error continuation.
+
+ The [scan_elem] argument, probably use some partial application of the
+ following generic [scan_elem]:
+
+ let scan_elem fmt ib f ek = kscanf ib ek fmt f;;
+
+ For instance, a suitable [scan_elem] for integers could be:
+
+ let scan_integer_elem = scan_elem " %i";;
+
+*)
+let rec scan_elems ib scan_elem accu =
+ scan_elem ib
+ (fun i ->
+ let accu = i :: accu in
+ kscanf ib
+ (fun ib exc -> accu)
+ " %1[;]"
+ (fun s -> if s = "" then accu else scan_elems ib scan_elem accu))
+ (fun ib exc -> accu)
+;;
+
+let scan_list scan_elem ib =
+ bscanf ib "[ " ();
+ let accu = scan_elems ib scan_elem [] in
+ bscanf ib " ]" ();
+ List.rev accu
+;;
+
+let scan_int_list = scan_list (scan_elem " %i");;
+let scan_string_list = scan_list (scan_elem " %S");;
+let scan_bool_list = scan_list (scan_elem " %B");;
+let scan_char_list = scan_list (scan_elem " %C");;
+let scan_float_list = scan_list (scan_elem " %f");;
+
+let test31 () =
+ scan_int_list (Scanning.from_string "[]") = [] &&
+ scan_int_list (Scanning.from_string "[ ]") = [] &&
+ scan_int_list (Scanning.from_string "[1]") = [1] &&
+ scan_int_list (Scanning.from_string "[1;2;3;4]") = [1;2;3;4] &&
+ scan_int_list (Scanning.from_string "[1;2;3;4;]") = [1;2;3;4];;
+
+test (test31 ())
+;;
+
+let test32 () =
+ scan_string_list (Scanning.from_string "[]") = [] &&
+ scan_string_list (Scanning.from_string "[ ]") = [] &&
+ scan_string_list (Scanning.from_string "[ \"1\" ]") = ["1"] &&
+ scan_string_list
+ (Scanning.from_string "[\"1\"; \"2\"; \"3\"; \"4\"]") =
+ ["1"; "2"; "3"; "4"] &&
+ scan_string_list
+ (Scanning.from_string "[\"1\"; \"2\"; \"3\"; \"4\";]") =
+ ["1"; "2"; "3"; "4"];;
+
+test (test32 ())
+;;
+
+(* Using [kscanf] only.
+
+ We use format values to stand for ``functional'' specifications to scan
+ the elements of lists.
+
+ The list item separator and the separator spacing are builtin into the
+ [scan_elems] iterator and thus are conveniently omitted from the
+ definitional format for item scanning.
+*)
+let rec scan_elems ib scan_elem_fmt accu =
+ kscanf ib (fun ib exc -> accu)
+ scan_elem_fmt
+ (fun i ->
+ let accu = i :: accu in
+ bscanf ib
+ " %1[;] "
+ (function
+ | "" -> accu
+ | _ -> scan_elems ib scan_elem_fmt accu)
+ )
+;;
+
+let scan_list scan_elem_fmt ib =
+ bscanf ib "[ " ();
+ let accu = scan_elems ib scan_elem_fmt [] in
+ bscanf ib " ]" ();
+ List.rev accu
+;;
+
+let scan_int_list = scan_list "%i";;
+let scan_string_list = scan_list "%S";;
+let scan_bool_list = scan_list "%B";;
+let scan_char_list = scan_list "%C";;
+let scan_float_list = scan_list "%f";;
+
+let test33 () =
+ scan_int_list (Scanning.from_string "[]") = [] &&
+ scan_int_list (Scanning.from_string "[ ]") = [] &&
+ scan_int_list (Scanning.from_string "[ 1 ]") = [1] &&
+ scan_int_list (Scanning.from_string "[ 1; 2; 3; 4 ]") = [1; 2; 3; 4] &&
+ scan_int_list (Scanning.from_string "[1;2;3;4;]") = [1; 2; 3; 4];;
+
+test (test33 ())
+;;
+
+let test34 () =
+ scan_string_list (Scanning.from_string "[]") = [] &&
+ scan_string_list (Scanning.from_string "[ ]") = [] &&
+ scan_string_list (Scanning.from_string "[ \"1\" ]") = ["1"] &&
+ scan_string_list
+ (Scanning.from_string "[\"1\"; \"2\"; \"3\"; \"4\"]") =
+ ["1"; "2"; "3"; "4"] &&
+ scan_string_list
+ (Scanning.from_string "[\"1\"; \"2\"; \"3\"; \"4\";]") =
+ ["1"; "2"; "3"; "4"];;
+
+test (test34 ())
+;;
+
+(* Using kscanf only.
+
+ Same as the preceding functional, except that we no more use format values
+ to scan items: we use scanners that scan elements of the list on the
+ fly.
+*)
+(* This version cannot handle empty lists!
+let rec scan_elems ib scan_elem accu =
+ scan_elem ib
+ (fun elem ->
+ let accu = elem :: accu in
+ kscanf ib (fun ib exc -> accu)
+ " %1[;] "
+ (function
+ | "" -> accu
+ | _ -> scan_elems ib scan_elem accu))
+;;
+*)
+
+(* We use [kscanf] with a [%r] format ! *)
+let rec scan_elems scan_elem accu ib =
+ kscanf ib (fun ib exc -> accu)
+ "%r"
+ (function ib ->
+ scan_elem ib
+ (function elem ->
+ let accu = elem :: accu in
+ bscanf ib
+ " %1[;] "
+ (function
+ | "" -> accu
+ | _ -> scan_elems scan_elem accu ib)))
+ (function l -> l)
+;;
+
+let scan_list scan_elem ib =
+ bscanf ib "[ " ();
+ let accu = scan_elems scan_elem [] ib in
+ bscanf ib " ]" ();
+ List.rev accu
+;;
+
+(* We may also try a version with only one format:
+ We also changed the type of [scan_elem] to partially apply it to its
+ ``natural'' continuation.
+let rec scan_elems scan_elem accu ib =
+ (* We use [kscanf], so that:
+ if the element reader fails, we can return the list of elements read so
+ far. *)
+ kscanf ib (fun ib exc -> accu)
+ (* The format string for [kscanf]: we read an element using [scan_elem],
+ then find a semi-colon if any, in order to decide if we stop reading
+ or go on with other elements. *)
+ "%r %1[;] "
+ (* The reader: once an element has been read it returns the new accu. *)
+ (scan_elem (function elem -> elem :: accu))
+ (fun accu s ->
+ (* Cannot find a semi-colon: no more elements to read. *)
+ if s = "" then accu
+ (* We found a semi-colon: go on with the new accu. *)
+ else scan_elems scan_elem accu ib)
+;;
+
+let scan_list scan_elem ib =
+ bscanf ib "[ %r ]" (scan_elems scan_elem []) List.rev
+;;
+
+(* For instance:
+let scan_float f ib = Scanf.bscanf ib "%f" f;;
+# scan_list scan_float;;
+- : Scanf.Scanning.scanbuf -> float list = <fun>
+*)
+
+(* The element scanner builder. *)
+let make_scan_elem fmt f ib = Scanf.bscanf ib fmt f;;
+
+(* Promote an element reader format to an element list reader. *)
+let list_scanner fmt = scan_list (make_scan_elem fmt);;
+
+let scan_float = make_scan_elem "%f";;
+
+scan_list scan_float;;
+
+list_scanner "%f";;
+- : Scanf.Scanning.scanbuf -> float list = <fun>
+*)
+
+(* The prototype of a [scan_elem] function for the generic [scan_list]
+ functional.
+ This [scan_elem] scans a floating point number. *)
+let scan_float ib = Scanf.bscanf ib "%f";;
+let scan_float_list = scan_list scan_float;;
+
+(* In the following list scanners, we directly give the [scan_elem] function
+ as an immediate function value argument to the polymorphic
+ [scan_list]. *)
+let scan_int_list = scan_list (fun ib -> Scanf.bscanf ib "%i");;
+let scan_string_list = scan_list (fun ib -> Scanf.bscanf ib "%S");;
+let scan_bool_list = scan_list (fun ib -> Scanf.bscanf ib "%B");;
+let scan_char_list = scan_list (fun ib -> Scanf.bscanf ib "%C");;
+
+(* [scan_list] is truely polymorphic: scanning a list of lists of items
+ is a one liner!
+
+ Here we scan list of lists of floats. *)
+let scan_float_list_list =
+ scan_list
+ (fun ib k -> k (scan_list (fun ib -> Scanf.bscanf ib "%f") ib))
+;;
+
+let scan_float_list_list =
+ scan_list
+ (fun ib k -> k (scan_list scan_float ib))
+;;
+
+let scan_float_list_list =
+ scan_list
+ (fun ib k -> k (scan_float_list ib))
+;;
+
+(* The killer way to define [scan_float_list_list]. *)
+(* let scan_float_list_list = scan_list scan_float_list;; *)
+
+let test340 () =
+ scan_float_list_list
+ (Scanning.from_string "[[1.0] ; []; [2.0; 3; 5.0; 6.];]") =
+ [[1.]; []; [2.; 3.; 5.; 6.]]
+;;
+
+(* A general scan_list_list functional. *)
+let scan_list_list scan_elems ib =
+ scan_list
+ (fun ib k -> k (scan_elems ib)) ib
+;;
+
+let scan_float_list_list = scan_list_list scan_float_list;;
+
+(* Programming with continuations :) *)
+let scan_float_item ib k = k (scan_float ib (fun x -> x));;
+let scan_float_list ib k = k (scan_list scan_float_item ib);;
+let scan_float_list_list ib k = k (scan_list scan_float_list ib);;
+
+(* Testing the %N format. *)
+let test35 () =
+ sscanf "" "%N" (fun x -> x) = 0 &&
+ sscanf "456" "%N" (fun x -> x) = 0 &&
+ sscanf "456" "%d%N" (fun x y -> x, y) = (456, 1) &&
+ sscanf " " "%N%s%N" (fun x s y -> x, s, y) = (0, "", 1)
+;;
+
+test (test340 () && test35 ())
+;;
+
+(* The prefered reader functionnals. *)
+
+(* To read a list as in Caml (elements are ``blank + semicolon + blank''
+ separated, and the list is enclosed in brackets). *)
+let rec read_elems read_elem accu ib =
+ kscanf ib (fun ib exc -> accu)
+ "%r %1[;] "
+ (read_elem (function elem -> elem :: accu))
+ (fun accu s -> if s = "" then accu else read_elems read_elem accu ib)
+;;
+
+let read_list read_elem ib =
+ bscanf ib "[ %r ]" (read_elems read_elem []) List.rev
+;;
+
+(* The element reader builder. *)
+let make_read_elem fmt f ib = Scanf.bscanf ib fmt f;;
+
+(* Promote an element reader format to an element list reader. *)
+let scan_List fmt = read_list (make_read_elem fmt);;
+
+(* Example for list of floatting point numbers. *)
+(*
+scan_List "%f";;
+- : Scanf.Scanning.scanbuf -> float list = <fun>
+
+(* To read a list as a succession of elements separated by a blank. *)
+let rec read_elems read_elem accu ib =
+ kscanf ib (fun ib exc -> accu)
+ "%r "
+ (read_elem (function elem -> elem :: accu))
+ (fun accu -> read_elems read_elem accu ib)
+;;
+
+let read_list read_elem ib =
+ List.rev (read_elems read_elem [] ib)
+;;
+
+(* Promote an element reader format to an element list reader. *)
+let scan_list fmt = read_list (make_read_elem fmt);;
+
+scan_list "%f";;
+*)
+
+(* Testing the %n format. *)
+let test36 () =
+ sscanf "" "%n" (fun x -> x) = 0 &&
+ sscanf "456" "%n" (fun x -> x) = 0 &&
+ sscanf "456" "%d%n" (fun x y -> x, y) = (456, 3) &&
+ sscanf " " "%n%s%n" (fun x s y -> x, s, y) = (0, "", 0)
+;;
+
+test (test36 ())
+;;
+
+(* Weird tests to empty strings or formats. *)
+let test37 () =
+ sscanf "" "" true &&
+ sscanf "" "" (fun x -> x) 1 = 1 &&
+ sscanf "123" "" (fun x -> x) 1 = 1
+;;
+
+test (test37 ())
+;;
+
+(* Testing end of input condition. *)
+let test38 () =
+ sscanf "a" "a%!" true &&
+ sscanf "a" "a%!%!" true &&
+ sscanf " a" " a%!" true &&
+ sscanf "a " "a %!" true &&
+ sscanf "" "%!" true &&
+ sscanf " " " %!" true &&
+ sscanf "" " %!" true &&
+ sscanf "" " %!%!" true
+;;
+
+test (test38 ())
+;;
+
+(* Weird tests on empty buffers. *)
+let test39 () =
+ let is_empty_buff ib =
+ Scanning.beginning_of_input ib &&
+ Scanning.end_of_input ib in
+
+ let ib = Scanning.from_string "" in
+ is_empty_buff ib &&
+ (* Do it twice since testing empty buff could incorrectly
+ thraw an exception or wrongly change the beginning_of_input condition. *)
+ is_empty_buff ib
+;;
+
+test (test39 ())
+;;
+
+(* Testing ranges. *)
+let test40 () =
+ let s = "cba" in
+ let ib = Scanning.from_string s in
+ bscanf ib "%[^ab]%s%!" (fun s1 s2 -> s1 = "c" && s2 = "ba")
+;;
+
+test (test40 ())
+;;
+
+let test41 () =
+ let s = "cba" in
+ let ib = Scanning.from_string s in
+ bscanf ib "%[^abc]%[cba]%!" (fun s1 s2 -> s1 = "" && s2 = "cba")
+;;
+
+test (test41 ())
+;;
+
+let test42 () =
+ let s = "defcbaaghi" in
+ let ib = Scanning.from_string s in
+ bscanf ib "%[^abc]%[abc]%s%!" (fun s1 s2 s3 ->
+ s1 = "def" && s2 = "cbaa" && s3 = "ghi") &&
+ let ib = Scanning.from_string s in
+ bscanf ib "%s@\t" (fun s -> s = "defcbaaghi")
+;;
+
+test (test42 ())
+;;
+
+(* Testing end of file condition (bug found). *)
+let test43, test44 =
+ let s = "" in
+ let ib = Scanning.from_string s in
+ (fun () -> bscanf ib "%i%!" (fun i -> i)),
+ (fun () -> bscanf ib "%!%i" (fun i -> i))
+;;
+
+test_raises_this_exc End_of_file test43 () &&
+test_raises_this_exc End_of_file test44 ()
+;;
+
+(* Testing small range scanning (bug found once). *)
+let test45 () =
+ let s = "12.2" in
+ let ib = Scanning.from_string s in
+ bscanf ib "%[0-9].%[0-9]%s%!" (fun s1 s2 s3 ->
+ s1 = "12" && s2 = "2" && s3 = "")
+;;
+
+test (test45 ())
+;;
+
+(* Testing printing of meta formats. *)
+
+let test46, test47 =
+ (fun () ->
+ Printf.sprintf "%i %(%s%)."
+ 1 "spells one, %s" "in english"),
+ (fun () ->
+ Printf.sprintf "%i ,%{%s%}, %s."
+ 1 "spells one %s" "in english")
+;;
+
+test (test46 () = "1 spells one, in english.")
+;;
+test (test47 () = "1 ,%s, in english.")
+;;
+
+(* Testing scanning of meta formats. *)
+let test48 () =
+ (* Testing format_from_string. *)
+ let test_meta_read s fmt efmt = format_from_string s fmt = efmt in
+ (* Test if format %i is indeed read as %i. *)
+ let s, fmt = "%i", format_of_string "%i" in
+ test_meta_read s fmt fmt &&
+ (* Test if format %i is compatible with %d and indeed read as %i. *)
+ let s, fmt = "%i", format_of_string "%d" in
+ test_meta_read s fmt "%i" &&
+ (* Complex test of scanning a meta format specified in the scanner input
+ format string and extraction of its specification from a string. *)
+ sscanf "12 \"%i\"89 " "%i %{%d%}%s %!"
+ (fun i f s -> i = 12 && f = "%i" && s = "89") &&
+ (* Testing scanf format string replacement *)
+ let k s =
+ Scanf.sscanf s
+ "%(%f%)" (fun _fmt i -> i) in
+ k "\" : %1f\": 987654321" = 9.0 &&
+ k "\" : %2f\": 987654321" = 98.0 &&
+ k "\" : %3f\": 9.87654321" = 9.8 &&
+ k "\" : %4f\": 9.87654321" = 9.87 &&
+
+ let h s =
+ Scanf.sscanf s
+ "Read integers with %(%i%)" (fun _fmt i -> i) in
+ h "Read integers with \"%1d\"987654321" = 9 &&
+ h "Read integers with \"%2d\"987654321" = 98 &&
+ h "Read integers with \"%3u\"987654321" = 987 &&
+ h "Read integers with \"%4x\"987654321" = 39030 &&
+
+ let i s =
+ Scanf.sscanf s
+ "with %(%i %s%)" (fun _fmt amount currency -> amount, currency) in
+ i "with \" : %d %s\" : 21 euros" = (21, "euros") &&
+ i "with \" : %d %s\" : 987654321 dollars" = (987654321, "dollars") &&
+ i "with \" : %u %s\" : 54321 pounds" = (54321, "pounds") &&
+ i "with \" : %x %s\" : 321 yens" = (801, "yens") &&
+
+ let j s =
+ Scanf.sscanf s
+ "with %(%i %_s %s%)" (fun _fmt amount currency -> amount, currency) in
+ j "with \" : %1d %_s %s\" : 987654321 euros" = (9, "euros") &&
+ j "with \" : %2d %_s %s\" : 987654321 dollars" = (98, "dollars") &&
+ j "with \" : %3u %_s %s\" : 987654321 pounds" = (987, "pounds") &&
+ j "with \" : %4x %_s %s\" : 987654321 yens" = (39030, "yens")
+;;
+
+test (test48 ())
+;;
+
+(* Testing stoppers after ranges. *)
+let test49 () =
+ sscanf "as" "%[\\]" (fun s -> s = "") &&
+ sscanf "as" "%[\\]%s" (fun s t -> s = "" && t = "as") &&
+ sscanf "as" "%[\\]%s%!" (fun s t -> s = "" && t = "as") &&
+ sscanf "as" "%[a..z]" (fun s -> s = "a") &&
+ sscanf "as" "%[a-z]" (fun s -> s = "as") &&
+ sscanf "as" "%[a..z]%s" (fun s t -> s = "a" && t = "s") &&
+ sscanf "as" "%[a-z]%s" (fun s t -> s = "as" && t = "") &&
+ sscanf "-as" "%[-a-z]" (fun s -> s = "-as") &&
+ sscanf "-as" "%[-a-z]@s" (fun s -> s = "-a") &&
+ sscanf "-as" "-%[a]@s" (fun s -> s = "a") &&
+ sscanf "-asb" "-%[a]@sb%!" (fun s -> s = "a") &&
+ sscanf "-asb" "-%[a]@s%s" (fun s t -> s = "a" && t = "b")
+;;
+
+test (test49 ())
+;;
+
+(* Testing buffers defined via functions
+ + co-routines that read and write from the same buffers
+ + range chars and proper handling of \n
+ + the end of file condition. *)
+let next_char ob () =
+ let s = Buffer.contents ob in
+ let len = String.length s in
+ if len = 0 then raise End_of_file else
+ let c = s.[0] in
+ Buffer.clear ob;
+ Buffer.add_string ob (String.sub s 1 (len - 1));
+ c
+;;
+
+let send_string ob s =
+ Buffer.add_string ob s; Buffer.add_char ob '\n';;
+let send_int ob i = send_string ob (string_of_int i);;
+
+let rec reader =
+ let count = ref 0 in
+ (fun ib ob ->
+ if Scanf.Scanning.beginning_of_input ib then begin
+ count := 0; send_string ob "start"; writer ib ob end else
+ Scanf.bscanf ib "%[^\n]\n" (function
+ | "stop" -> send_string ob "stop"; writer ib ob
+ | s ->
+ let l = String.length s in
+ count := l + !count;
+ if !count >= 100 then begin
+ send_string ob "stop";
+ send_int ob !count
+ end else
+ send_int ob l;
+ writer ib ob))
+
+and writer ib ob =
+ Scanf.bscanf ib "%s\n" (function
+ | "start" -> send_string ob "Hello World!"; reader ib ob
+ | "stop" -> Scanf.bscanf ib "%i" (function i -> i)
+ | s -> send_int ob (int_of_string s); reader ib ob);;
+
+let go () =
+ let ob = Buffer.create 17 in
+ let ib = Scanf.Scanning.from_function (next_char ob) in
+ reader ib ob
+;;
+
+let test50 () = go () = 100;;
+
+test (test50 ())
+;;
+
+(* Simple tests may also fail!
+ Ensure this is not the case with the current version for module [Scanf]. *)
+let test51 () =
+ sscanf "Hello" "%s" id = "Hello" &&
+ sscanf "Hello\n" "%s\n" id = "Hello" &&
+ sscanf "Hello\n" "%s%s\n" (fun s1 s2 ->
+ s1 = "Hello" && s2 = "") &&
+ sscanf "Hello\nWorld" "%s\n%s%!" (fun s1 s2 ->
+ s1 = "Hello" && s2 = "World") &&
+ sscanf "Hello\nWorld!" "%s\n%s" (fun s1 s2 ->
+ s1 = "Hello" && s2 = "World!") &&
+ sscanf "Hello\n" "%s@\n%s" (fun s1 s2 ->
+ s1 = "Hello" && s2 = "") &&
+ sscanf "Hello \n" "%s@\n%s" (fun s1 s2 ->
+ s1 = "Hello " && s2 = "")
+;;
+
+test (test51 ())
+;;
+
+(* Tests that indeed the [%s@c] format works properly.
+ Also tests the difference between [\n] and [@\n] is correctly handled.
+ In particular, tests that if no [c] character can be found in the
+ input, then the token obtained for [%s@c] spreads to the end of
+ input. *)
+let test52 () =
+ sscanf "Hello\n" "%s@\n" id = "Hello" &&
+ sscanf "Hello" "%s@\n" id = "Hello" &&
+ sscanf "Hello" "%s%s@\n" (fun s1 s2 ->
+ s1 = "Hello" && s2 = "") &&
+ sscanf "Hello\nWorld" "%s@\n%s%!" (fun s1 s2 ->
+ s1 = "Hello" && s2 = "World") &&
+ sscanf "Hello\nWorld!" "%s@\n%s@\n" (fun s1 s2 ->
+ s1 = "Hello" && s2 = "World!") &&
+ sscanf "Hello\n" "%s@\n%s" (fun s1 s2 ->
+ s1 = "Hello" && s2 = "") &&
+ sscanf "Hello \n" "%s%s@\n" (fun s1 s2 ->
+ s1 = "Hello" && s2 = " ") &&
+sscanf "Hello \n" "%s%s%_1[ ]\n" (fun s1 s2 ->
+ s1 = "Hello" && s2 = "") &&
+ sscanf "Hello \n" "%s%_1[ ]%s\n" (fun s1 s2 ->
+ s1 = "Hello" && s2 = "") &&
+ sscanf "Hello\nWorld" "%s\n%s%!" (fun s1 s2 ->
+ s1 = "Hello" && s2 = "World") &&
+ sscanf "Hello\nWorld!" "%s\n%s%!" (fun s1 s2 ->
+ s1 = "Hello" && s2 = "World!") &&
+ sscanf "Hello\nWorld!" "%s\n%s@!%!" (fun s1 s2 ->
+ s1 = "Hello" && s2 = "World")
+;;
+
+test (test52 ())
+;;
+
+(* Reading native, int32 and int64 numbers. *)
+let test53 () =
+ sscanf "123" "%nd" id = 123n &&
+ sscanf "124" "%nd" (fun i -> Nativeint.pred i = 123n) &&
+
+ sscanf "123" "%ld" id = 123l &&
+ sscanf "124" "%ld" (fun i -> Int32.succ i = 125l) &&
+
+ sscanf "123" "%Ld" id = 123L &&
+ sscanf "124" "%Ld" (fun i -> Int64.pred i = 123L)
+;;
+
+test (test53 ())
+;;
+
+(* Routines to create the file that tscanf uses as a testbed case. *)
+let create_tscanf_data ob lines =
+ let add_line (p, e) =
+ Buffer.add_string ob (Printf.sprintf "%S" p);
+ Buffer.add_string ob " -> ";
+ Buffer.add_string ob (Printf.sprintf "%S" e);
+ Buffer.add_string ob ";\n" in
+ List.iter add_line lines
+;;
+
+let write_tscanf_data_file fname lines =
+ let oc = open_out fname in
+ let ob = Buffer.create 42 in
+ create_tscanf_data ob lines;
+ Buffer.output_buffer oc ob;
+ close_out oc
+;;
+
+(* The tscanf testbed case file name. *)
+let tscanf_data_file = "tscanf_data";;
+(* The contents of the tscanf testbed case file. *)
+let tscanf_data_file_lines = [
+ "Objective", "Caml";
+]
+;;
+(* We write the tscanf testbed case file. *)
+write_tscanf_data_file tscanf_data_file tscanf_data_file_lines
+;;
+
+(* Then we verify that its contents is indeed correct:
+ the lines written into the [tscanf_data] file should be the same as the
+ lines read from it. *)
+
+(* Reading back tscanf_data_file_lines (hence, testing data file reading as
+ well). *)
+let get_lines fname =
+ let ib = Scanf.Scanning.from_file fname in
+ let l = ref [] in
+ try
+ while not (Scanf.Scanning.end_of_input ib) do
+ Scanf.bscanf ib " %S -> %S; " (fun x y ->
+ l := (x, y) :: !l)
+ done;
+ List.rev !l
+ with
+ | Scanf.Scan_failure s ->
+ failwith (Printf.sprintf "in file %s, %s" fname s)
+ | End_of_file ->
+ failwith (Printf.sprintf "in file %s, unexpected end of file" fname)
+;;
+
+(* Simpy test that the list of lines read from the file are the list of lines
+ written to it!. *)
+let test54 () =
+ get_lines tscanf_data_file = tscanf_data_file_lines
+;;
+
+test (test54 ())
+;;
+
+(* Creating digests for files. *)
+let add_digest_ib ob ib =
+ let digest s = String.uppercase (Digest.to_hex (Digest.string s)) in
+ let scan_line ib f = Scanf.bscanf ib "%[^\n\r]\n" f in
+ let output_line_digest s =
+ Buffer.add_string ob s;
+ Buffer.add_char ob '#'; Buffer.add_string ob (digest s);
+ Buffer.add_char ob '\n' in
+ try while true do scan_line ib output_line_digest done; with
+ | End_of_file -> ()
+;;
+
+let digest_file fname =
+ let ib = Scanf.Scanning.from_file fname in
+ let ob = Buffer.create 42 in
+ add_digest_ib ob ib;
+ Buffer.contents ob
+;;
+
+let test55 () =
+ let ob = Buffer.create 42 in
+ let ib =
+ create_tscanf_data ob tscanf_data_file_lines;
+ let s = Buffer.contents ob in
+ Buffer.clear ob;
+ Scanning.from_string s in
+ let tscanf_data_file_lines_digest = add_digest_ib ob ib; Buffer.contents ob in
+ digest_file tscanf_data_file = tscanf_data_file_lines_digest
+;;
+
+test (test55 ())
+;;
+
+(* Testing the number of characters read. *)
+let test56 () =
+ let g s = Scanf.sscanf s "%d%n" (fun i n -> (i, n)) in
+ g "99" = (99, 2) &&
+ g "99 syntaxes all in a row" = (99, 2) &&
+ g "-20 degrees Celsius" = (-20, 3)
+;;
+
+test (test56 ())
+;;
+
+(* Testing the scanning of formats. *)
+let test57 () =
+ (* Testing format_from_string. *)
+ let test_format_scan s fmt efmt =
+ format_from_string s fmt = efmt in
+ (* Test if format %i is indeed read as %i. *)
+ let s, fmt = " %i ", format_of_string "%i" in
+ test_format_scan s fmt " %i " &&
+ (* Test if format %i is compatible with %d and indeed read as %i. *)
+ let s, fmt = "%i", format_of_string "%d" in
+ test_format_scan s fmt "%i" &&
+
+ let s, fmt =
+ "Read an int %i then a string %s.",
+ format_of_string "Spec%difi%scation" in
+ test_format_scan s fmt "Read an int %i then a string %s." &&
+
+ let s, fmt =
+ "Read an int %i then a string \"%s\".",
+ format_of_string "Spec%difi%Scation" in
+ test_format_scan s fmt "Read an int %i then a string \"%s\"." &&
+
+ let s, fmt =
+ "Read an int %i then a string \"%s\".",
+ format_of_string "Spec%difi%scation" in
+ test_format_scan s fmt "Read an int %i then a string \"%s\"." &&
+
+ (* Complex test of scanning a meta format specified in the scanner input
+ format string and extraction of its specification from a string. *)
+ sscanf "12 \"%i\"89 " "%i %{%d%}%s %!"
+ (fun i f s -> i = 12 && f = "%i" && s = "89")
+;;
+
+test (test57 ())
+;;
+
+(*
+let test58 () =
+;;
+
+test (test58 ())
+;;
+*)
+
+(* To be continued ...
+(* Trying to scan records. *)
+let rec scan_fields ib scan_field accu =
+ kscanf ib (fun ib exc -> accu)
+ scan_field
+ (fun i ->
+ let accu = i :: accu in
+ kscanf ib (fun ib exc -> accu)
+ " %1[;] "
+ (fun s ->
+ if s = "" then accu else scan_fields ib scan_field accu))
+;;
+
+let scan_record scan_field ib =
+ bscanf ib "{ " ();
+ let accu = scan_fields ib scan_field [] in
+ bscanf ib " }" ();
+ List.rev accu
+;;
+
+let scan_field ib =
+ bscanf ib "%s = %[^;]" (fun finame ficont -> finame, ficont);;
+*)