let sprintf = Printf.sprintf let ksprintf = Printf.ksprintf open Ppx_sexp_conv_lib.Conv module Time : sig type t val now : unit -> t val to_string : t -> string val to_string_sec : t -> string val to_string_abs : t -> string end = struct type t = string let now () = "Time.now ()" let to_string t = "[Time.to_string (" ^ t ^ ")]" let to_string_sec t = "[Time.to_string_sec (" ^ t ^ ")]" let to_string_abs t = "[Time.to_string_abs (" ^ t ^ ")]" end module Zone : sig type t val local : t val to_string : t -> string end = struct type t = string let local = "Zone.local" let to_string t = "[Zone.to_string " ^ t ^ "]" end let%test _ = sprintf !"The time is %{Time} and the timezone is %{Zone}.\n" (Time.now ()) Zone.local = "The time is [Time.to_string (Time.now ())] and the timezone is [Zone.to_string \ Zone.local].\n" ;; (* check that custom directives with nothing in between are properly translated *) let%test _ = sprintf !"%{sexp:int}%{sexp:int}%{sexp:int}%{sexp:int}" 1 2 3 4 = "1234" (* check that things works well when the conversion function take optional arguments *) let%test _ = let to_string ?foo:_ x = string_of_int x in sprintf !"%{to_string}\n" 42 = "42\n" ;; (* check the X#y kinds of format and that arguments are not reversed somehow *) let%test _ = let now = Time.now () in sprintf !"%{Time}, %{Time#sec}, %{Time.to_string_abs}\n%!" now now now = "[Time.to_string (Time.now ())], [Time.to_string_sec (Time.now ())], \ [Time.to_string_abs (Time.now ())]\n" ;; (* same as above, with empty module paths *) let%test _ = let open Time in let now = now () in sprintf !"%{}, %{#sec}, %{to_string_abs}\n%!" now now now = "[Time.to_string (Time.now ())], [Time.to_string_sec (Time.now ())], \ [Time.to_string_abs (Time.now ())]\n" ;; (* testing what happens if the expression to the left of the format string is a bit complicated *) let%test _ = let s = ksprintf (fun s -> s ^ " foo") !"%{Time} bar" (Time.now ()) in s = "[Time.to_string (Time.now ())] bar foo" ;; (* checking sexp: format *) let%test "sexp conversion" = sprintf !"The pair is: %{sexp:int * string}" (4, "asdf") = "The pair is: (4 asdf)" ;; (* checking sexp#mach: format *) let%test "sexp#mach conversion" = let module Ppx_sexp_conv_lib = struct module Sexp = struct include Ppx_sexp_conv_lib.Sexp let to_string_mach sexp = to_string sexp ^ " (in machine format)" end end in sprintf !"The pair is: %{sexp#mach:int * string}" (4, "asdf") = "The pair is: (4 asdf) (in machine format)" ;; (* checking tricky formats *) let%test _ = sprintf !"%d %%{foo" 3 = "3 %{foo" let%test _ = sprintf !"%d %%{%{Time}" 3 (Time.now ()) = "3 %{[Time.to_string (Time.now ())]" ;; (* checking that when we eta expand, we do not change side effects *) let%test _ = let side_effect1_happened = ref false in let side_effect2_happened = ref false in let _f : Zone.t -> string = (side_effect1_happened := true; sprintf) !"%{Time} %{Zone}" (side_effect2_happened := true; Time.now ()) in !side_effect1_happened && !side_effect2_happened ;; let%test _ = let to_string () = "plop" in sprintf !"%{ }" () = "plop" ;; let%test_unit _ = let f ~labeled_arg:() fmt = ksprintf (fun _ -> ()) fmt in (* Check that it compiles with the labeled argument applied both before and after the format string *) f ~labeled_arg:() !"hello"; f !"hello" ~labeled_arg:() ;; let%test_unit _ = let after1 = Some () in let f ~before:() fmt = ksprintf (fun _ ?after1:_ () ~after2:() -> ()) fmt in f ~before:() ?after1 !"hello" () ~after2:(); f ~before:() !"hello" ?after1 () ~after2:(); f !"hello" ~before:() ?after1 () ~after2:(); f !"hello" ?after1 ~before:() () ~after2:() ;; let%test_unit _ = let f ~label:() fmt = ksprintf (fun _ -> ()) fmt in let r = ref 0 in let g = f !"%{Time}" ~label:(incr r) in g (Time.now ()); g (Time.now ()); assert (!r = 1) ;; let%test "format subst" = sprintf !"%(%d%)" "[%d]" 1 = "[1]" let first_class_format1 = !"u = %{sexp:int * int}" let first_class_format2 = !"t = %{Time}" let first_class_format3 = first_class_format1 ^^ ", " ^^ first_class_format2 ^^ !", v = %{sexp:int}" ;; let%test _ = sprintf first_class_format1 (0, 42) = "u = (0 42)" let%test _ = sprintf first_class_format2 (Time.now ()) = "t = [Time.to_string (Time.now ())]" ;; let%test _ = sprintf first_class_format3 (0, 42) (Time.now ()) 10 = "u = (0 42), t = [Time.to_string (Time.now ())], v = 10" ;;