(*************************************************************************)
(*                                                                       *)
(*                            Objective Caml                             *)
(*                                                                       *)
               , 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$ 

 A testbed file for the module  . 



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 ! ) 

   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

         " scan_int_list : char % i waiting for ' ] ' or ' ; ' but found % c " 
         ( Scanning.char_count ib ) ( Scanning.peek_char ib )
        "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; ]");;
(* 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 ) . 
 
   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  string : 
    the format is " \"%s@\ " " . 
    A better way would be to add a % S ( String.escaped ) , a % C ( Char.escaped ) . 
    This is now available .
   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 =      ; ; 

    For instance , a suitable [ scan_elem ] for integers could be : 

    let scan_integer_elem = scan_elem " % i " ; ; 


   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 . 


   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 . 


   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 can not 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 ) ) 
 ; ; 

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 .
   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 .
       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 > 

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 .
   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  ( elements are ` ` blank + semicolon + blank '' 
    separated , and the list is enclosed in brackets ) .
   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 .
     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  
  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  
  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 .
   + 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  
  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  
  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);;
*)

***********************************************************************
                                                                       
                            Objective Caml                             
                                                                       
                                                                       
   en Automatique.  All rights reserved.  This file is distributed     
                                                                       
***********************************************************************
 The ``continuation'' that returns the scanned value. 
 Testing space scanning. 
 Testing integer scanning %i and %d. 
 Testing float scanning. 
 f style. 
 e style. 
 g style. 
 Testing boolean scanning. 
 Testing char scanning. 
 Here \n, \t, and \032 are skipped due to the space semantics of scanf. 
 Testing string scanning. 
 %S and %s styles. 
 Testing the result of reading a %S string. 
 %[] style 
 Empty string (end of input) testing. 
 Scanning lists. 
 Statically known lists can be scanned directly. 
 A general list scanner that always fails to succeed. 
 A general int list scanner. 
 A general list scanner that always succeeds. 
 Should fail but succeeds! 
 Should fail but succeeds! 
 begginning of list: could find either
           - an int, if the list is not empty,
           - the char ], if the list is empty. 
 Those properly fail 
 Should lead to a bad input error. 
 The general int list scanner. 
 A generic polymorphic item scanner, 
 Derivation of list scanners from the generic polymorphic item scanner
   applications. 
 We use [kscanf], so that:
     if the element reader fails, we can return the list of elements read so
     far. 
 The reader: once an element has been read it returns the new accu. 
 Cannot find a semi-colon: no more elements to read. 
 We found a semi-colon: go on with the new accu. 
 The element scanner builder. 
 Promote an element reader format to an element list reader. 
 The prototype of a [scan_elem] function for the generic [scan_list]
   functional.
   This [scan_elem] scans a floating point number. 
 In the following list scanners, we directly give the [scan_elem] function
   as an immediate function value argument to the polymorphic
   [scan_list]. 
 The killer way to define [scan_float_list_list]. 
 Programming with continuations :) 
 Testing the %N format. 
 The prefered reader functionnals. 
 The element reader builder. 
 Promote an element reader format to an element list reader. 
 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. 
 Promote an element reader format to an element list reader. 
 Testing the %n format. 
 Weird tests to empty strings or formats. 
 Testing end of input condition. 
 Weird tests on empty buffers. 
 Testing ranges. 
 Testing end of file condition (bug found). 
 Testing small range scanning (bug found once). 
 Testing printing of meta formats. 
 Testing scanning of meta formats. 
 Testing format_from_string. 
 Complex test of scanning a meta format specified in the scanner input
     format string and extraction of its specification from a string. 
 Testing scanf format string replacement 
 Testing stoppers after ranges. 
 Simple tests may also fail!
   Ensure this is not the case with the current version for module [Scanf]. 
 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. 
 Routines to create the file that tscanf uses as a testbed case. 
 The tscanf testbed case file name. 
 The contents of the tscanf testbed case file. 
 We write the tscanf testbed case file. 
 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). 
 Simpy test that the list of lines read from the file are the list of lines
   written to it!. 
 Creating digests for files. 
 Testing the number of characters read. 
 Testing the scanning of formats. 
 Testing format_from_string. 
 Complex test of scanning a meta format specified in the scanner input
     format string and extraction of its specification from a string. 

let test58 () =
;;

test (test58 ())
;;

 To be continued ...
(* Trying to scan records. 
               , projet Cristal , INRIA Rocquencourt            
    Copyright 2002 Institut National de Recherche en Informatique et    
    under the terms of the Q Public License version 1.0 .                

  $ Id$ 

 A testbed file for the module  . 



A testbed file for the module Scanf.

*)

open Testing;;

open Scanf;;

let id x = x;;

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)
;;

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)
;;

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 ())
;;

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) &&

  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 ())
;;

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 ())
;;


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') &&

  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 ())
;;


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 ! ) 

   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à!" &&
  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 ())
;;

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)
;;

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 ())
;;

let ib () = Scanning.from_string "[1;2;3;4; ]";;

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 ())
;;

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 ())
;;

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 ())
;;

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] &&
  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] &&
  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 = [] ->
        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 ())
;;


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

         " scan_int_list : char % i waiting for ' ] ' or ' ; ' but found % c " 
         ( Scanning.char_count ib ) ( Scanning.peek_char ib )
        "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; ]");;
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
;;

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 ) . 
 
   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  string : 
    the format is " \"%s@\ " " . 
    A better way would be to add a % S ( String.escaped ) , a % C ( Char.escaped ) . 
    This is now available .
   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 ())
;;

let scan_elem fmt ib f ek = kscanf ib ek fmt f;;

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 =      ; ; 

    For instance , a suitable [ scan_elem ] for integers could be : 

    let scan_integer_elem = scan_elem " % i " ; ; 


   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 . 


   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 . 


   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 can not 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 ) ) 
 ; ; 

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 .
   We also changed the type of [scan_elem] to partially apply it to its
   ``natural'' continuation.
let rec scan_elems scan_elem accu ib =
  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 .
       then find a semi-colon if any, in order to decide if we stop reading
       or go on with other elements. *)
    "%r %1[;] "
    (scan_elem (function elem -> elem :: accu))
    (fun accu s ->
     if s = "" then 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 > 

let scan_float f ib = Scanf.bscanf ib "%f" f;;
# scan_list scan_float;;
- : Scanf.Scanning.scanbuf -> float list = <fun>
*)

let make_scan_elem fmt f ib = Scanf.bscanf ib fmt f;;

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>
*)

let scan_float ib = Scanf.bscanf ib "%f";;
let scan_float_list = scan_list scan_float;;

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 .
   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))
;;

  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;;

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);;

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 ())
;;


  To read a list as in  ( elements are ` ` blank + semicolon + blank '' 
    separated , and the list is enclosed in brackets ) .
   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
;;

let make_read_elem fmt f ib = Scanf.bscanf ib fmt f;;

let scan_List fmt = read_list (make_read_elem fmt);;

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)
;;

let scan_list fmt = read_list (make_read_elem fmt);;

scan_list "%f";;
*)

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 ())
;;

let test37 () =
  sscanf "" "" true &&
  sscanf "" "" (fun x -> x) 1 = 1 &&
  sscanf "123" "" (fun x -> x) 1 = 1
;;

test (test37 ())
;;

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 ())
;;

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 .
     thraw an exception or wrongly change the beginning_of_input condition. *)
  is_empty_buff ib
;;

test (test39 ())
;;

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 ())
;;

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 ()
;;

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 ())
;;


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.")
;;

let test48 () =
  let test_meta_read s fmt efmt = format_from_string s fmt = efmt in
  Test if format % i is indeed read as  
  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  
  let s, fmt = "%i", format_of_string "%d" in
  test_meta_read s fmt "%i" &&
  sscanf "12 \"%i\"89 " "%i %{%d%}%s %!"
    (fun i f s -> i = 12 && f = "%i" && s = "89") &&
  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 ())
;;

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 .
   + 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 ())
;;

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 ())
;;

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 ())
;;

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
;;

let tscanf_data_file = "tscanf_data";;
let tscanf_data_file_lines = [
    "Objective", "Caml";
]
;;
write_tscanf_data_file tscanf_data_file tscanf_data_file_lines
;;


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)
;;

let test54 () =
  get_lines tscanf_data_file = tscanf_data_file_lines
;;

test (test54 ())
;;

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 ())
;;

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 ())
;;

let test57 () =
  let test_format_scan s fmt efmt =
    format_from_string s fmt = efmt in
  Test if format % i is indeed read as  
  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  
  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\"." &&

  sscanf "12 \"%i\"89 " "%i %{%d%}%s %!"
    (fun i f s -> i = 12 && f = "%i" && s = "89")
;;

test (test57 ())
;;


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);;
*)
