open! Base open Expect_test_helpers_base open Sexplib0 let () = sexp_style := Sexp_style.simple_pretty module type S = sig type t [@@deriving equal, sexp] end let test (type a) (module M : S with type t = a) string = let sexp = Parsexp.Single.parse_string_exn string in let result = Or_error.try_with (fun () -> M.t_of_sexp sexp) in print_s [%sexp (result : M.t Or_error.t)] ;; let%expect_test "simple record" = let module M = struct type t = { x : int ; y : int } [@@deriving equal, sexp_of] let t_of_sexp = Sexp_conv_record.record_of_sexp ~caller:"M.t" ~fields: (Field { name = "x" ; kind = Required ; conv = int_of_sexp ; rest = Field { name = "y"; kind = Required; conv = int_of_sexp; rest = Empty } }) ~index_of_field:(function | "x" -> 0 | "y" -> 1 | _ -> -1) ~allow_extra_fields:false ~create:(fun (x, (y, ())) -> { x; y }) ;; end in let test = test (module M) in (* in order *) test "((x 1) (y 2))"; [%expect {| (Ok ((x 1) (y 2))) |}]; (* reverse order *) test "((y 2) (x 1))"; [%expect {| (Ok ((x 1) (y 2))) |}]; (* duplicate fields *) test "((x 1) (x 2) (y 3) (y 4))"; [%expect {| (Error (Of_sexp_error "M.t_of_sexp: duplicate fields: x y" (invalid_sexp ((x 1) (x 2) (y 3) (y 4))))) |}]; (* extra fields *) test "((a 1) (b 2) (c 3))"; [%expect {| (Error (Of_sexp_error "M.t_of_sexp: extra fields: a b c" (invalid_sexp ((a 1) (b 2) (c 3))))) |}]; (* missing field *) test "((x 1))"; [%expect {| (Error (Of_sexp_error "M.t_of_sexp: the following record elements were undefined: y" (invalid_sexp ((x 1))))) |}]; (* other missing field *) test "((y 2))"; [%expect {| (Error (Of_sexp_error "M.t_of_sexp: the following record elements were undefined: x" (invalid_sexp ((y 2))))) |}]; (* multiple missing fields *) test "()"; [%expect {| (Error (Of_sexp_error "M.t_of_sexp: the following record elements were undefined: x y" (invalid_sexp ()))) |}]; () ;; let%expect_test "record with extra fields" = let module M = struct type t = { x : int ; y : int } [@@deriving equal, sexp_of] let t_of_sexp = Sexp_conv_record.record_of_sexp ~caller:"M.t" ~fields: (Field { name = "x" ; kind = Required ; conv = int_of_sexp ; rest = Field { name = "y"; kind = Required; conv = int_of_sexp; rest = Empty } }) ~index_of_field:(function | "x" -> 0 | "y" -> 1 | _ -> -1) ~allow_extra_fields:true ~create:(fun (x, (y, ())) -> { x; y }) ;; end in let test = test (module M) in (* in order *) test "((x 1) (y 2))"; [%expect {| (Ok ((x 1) (y 2))) |}]; (* reversed order *) test "((y 2) (x 1))"; [%expect {| (Ok ((x 1) (y 2))) |}]; (* extra field *) test "((x 1) (y 2) (z 3))"; [%expect {| (Ok ((x 1) (y 2))) |}]; (* missing field *) test "((x 1))"; [%expect {| (Error (Of_sexp_error "M.t_of_sexp: the following record elements were undefined: y" (invalid_sexp ((x 1))))) |}]; (* other missing field *) test "((y 2))"; [%expect {| (Error (Of_sexp_error "M.t_of_sexp: the following record elements were undefined: x" (invalid_sexp ((y 2))))) |}]; (* multiple missing fields *) test "()"; [%expect {| (Error (Of_sexp_error "M.t_of_sexp: the following record elements were undefined: x y" (invalid_sexp ()))) |}]; () ;; let%expect_test "record with defaults" = let module M = struct type t = { x : int ; y : int } [@@deriving equal, sexp_of] let t_of_sexp = Sexp_conv_record.record_of_sexp ~caller:"M.t" ~fields: (Field { name = "x" ; kind = Default (fun () -> 0) ; conv = int_of_sexp ; rest = Field { name = "y" ; kind = Default (fun () -> 0) ; conv = int_of_sexp ; rest = Empty } }) ~index_of_field:(function | "x" -> 0 | "y" -> 1 | _ -> -1) ~allow_extra_fields:false ~create:(fun (x, (y, ())) -> { x; y }) ;; end in let test = test (module M) in (* in order *) test "((x 1) (y 2))"; [%expect {| (Ok ((x 1) (y 2))) |}]; (* reverse order *) test "((y 2) (x 1))"; [%expect {| (Ok ((x 1) (y 2))) |}]; (* extra field *) test "((x 1) (y 2) (z 3))"; [%expect {| (Error (Of_sexp_error "M.t_of_sexp: extra fields: z" (invalid_sexp ((x 1) (y 2) (z 3))))) |}]; (* missing field *) test "((x 1))"; [%expect {| (Ok ((x 1) (y 0))) |}]; (* other missing field *) test "((y 2))"; [%expect {| (Ok ((x 0) (y 2))) |}]; (* multiple missing fields *) test "()"; [%expect {| (Ok ((x 0) (y 0))) |}]; () ;; let%expect_test "record with omit nil" = let module M = struct type t = { a : int option ; b : int list } [@@deriving equal, sexp_of] let t_of_sexp = Sexp_conv_record.record_of_sexp ~caller:"M.t" ~fields: (Field { name = "a" ; kind = Omit_nil ; conv = option_of_sexp int_of_sexp ; rest = Field { name = "b" ; kind = Omit_nil ; conv = list_of_sexp int_of_sexp ; rest = Empty } }) ~index_of_field:(function | "a" -> 0 | "b" -> 1 | _ -> -1) ~allow_extra_fields:false ~create:(fun (a, (b, ())) -> { a; b }) ;; end in let test = test (module M) in (* in order *) test "((a (1)) (b (2 3)))"; [%expect {| (Ok ((a (1)) (b (2 3)))) |}]; (* reverse order *) test "((b ()) (a ()))"; [%expect {| (Ok ((a ()) (b ()))) |}]; (* extra field *) test "((a (1)) (b (2 3)) (z ()))"; [%expect {| (Error (Of_sexp_error "M.t_of_sexp: extra fields: z" (invalid_sexp ((a (1)) (b (2 3)) (z ()))))) |}]; (* missing field *) test "((a (1)))"; [%expect {| (Ok ((a (1)) (b ()))) |}]; (* other missing field *) test "((b (2 3)))"; [%expect {| (Ok ((a ()) (b (2 3)))) |}]; (* multiple missing fields *) test "()"; [%expect {| (Ok ((a ()) (b ()))) |}]; () ;; let%expect_test "record with sexp types" = let module M = struct type t = { a : int option ; b : int list ; c : int array ; d : bool } [@@deriving equal, sexp_of] let t_of_sexp = Sexp_conv_record.record_of_sexp ~caller:"M.t" ~fields: (Field { name = "a" ; kind = Sexp_option ; conv = int_of_sexp ; rest = Field { name = "b" ; kind = Sexp_list ; conv = int_of_sexp ; rest = Field { name = "c" ; kind = Sexp_array ; conv = int_of_sexp ; rest = Field { name = "d"; kind = Sexp_bool; conv = (); rest = Empty } } } }) ~index_of_field:(function | "a" -> 0 | "b" -> 1 | "c" -> 2 | "d" -> 3 | _ -> -1) ~allow_extra_fields:false ~create:(fun (a, (b, (c, (d, ())))) -> { a; b; c; d }) ;; end in let test = test (module M) in (* in order *) test "((a 1) (b (2 3)) (c (4 5)) (d))"; [%expect {| (Ok ((a (1)) (b (2 3)) (c (4 5)) (d true))) |}]; (* reverse order *) test "((d) (c ()) (b ()) (a 1))"; [%expect {| (Ok ((a (1)) (b ()) (c ()) (d true))) |}]; (* missing field d *) test "((a 1) (b (2 3)) (c (4 5)))"; [%expect {| (Ok ((a (1)) (b (2 3)) (c (4 5)) (d false))) |}]; (* missing field c *) test "((a 1) (b (2 3)) (d))"; [%expect {| (Ok ((a (1)) (b (2 3)) (c ()) (d true))) |}]; (* missing field b *) test "((a 1) (c (2 3)) (d))"; [%expect {| (Ok ((a (1)) (b ()) (c (2 3)) (d true))) |}]; (* missing field a *) test "((b (1 2)) (c (3 4)) (d))"; [%expect {| (Ok ((a ()) (b (1 2)) (c (3 4)) (d true))) |}]; (* extra field *) test "((a 1) (b (2 3)) (c (4 5)) (d) (e (6 7)))"; [%expect {| (Error (Of_sexp_error "M.t_of_sexp: extra fields: e" (invalid_sexp ((a 1) (b (2 3)) (c (4 5)) (d) (e (6 7)))))) |}]; (* all fields missing *) test "()"; [%expect {| (Ok ((a ()) (b ()) (c ()) (d false))) |}]; () ;; let%expect_test "record with polymorphic fields" = let module M = struct type t = { a : 'a. 'a list ; b : 'a 'b. ('a, 'b) Result.t option } [@@deriving sexp_of] let equal = Poly.equal let t_of_sexp = let open struct type a = { a : 'a. 'a list } [@@unboxed] type b = { b : 'a 'b. ('a, 'b) Result.t option } [@@unboxed] end in let caller = "M.t" in Sexp_conv_record.record_of_sexp ~caller ~fields: (Field { name = "a" ; kind = Required ; conv = (fun sexp -> { a = list_of_sexp (Sexplib.Conv_error.record_poly_field_value caller) sexp }) ; rest = Field { name = "b" ; kind = Required ; conv = (fun sexp -> { b = Option.t_of_sexp (Result.t_of_sexp (Sexplib.Conv_error.record_poly_field_value caller) (Sexplib.Conv_error.record_poly_field_value caller)) sexp }) ; rest = Empty } }) ~index_of_field:(function | "a" -> 0 | "b" -> 1 | _ -> -1) ~allow_extra_fields:false ~create:(fun ({ a }, ({ b }, ())) -> { a; b }) ;; end in let test = test (module M) in (* in order *) test "((a ()) (b ()))"; [%expect {| (Ok ((a ()) (b ()))) |}]; (* reverse order *) test "((b ()) (a ()))"; [%expect {| (Ok ((a ()) (b ()))) |}]; (* attempt to deserialize paramter to [a] *) test "((a (_)) (b ()))"; [%expect {| (Error (Of_sexp_error "M.t_of_sexp: cannot convert values of types resulting from polymorphic record fields" (invalid_sexp _))) |}]; (* attempt to deserialize first parameter to [b] *) test "((a ()) (b ((Ok _))))"; [%expect {| (Error (Of_sexp_error "M.t_of_sexp: cannot convert values of types resulting from polymorphic record fields" (invalid_sexp _))) |}]; (* attempt to deserialize second parameter to [b] *) test "((a ()) (b ((Error _))))"; [%expect {| (Error (Of_sexp_error "M.t_of_sexp: cannot convert values of types resulting from polymorphic record fields" (invalid_sexp _))) |}]; (* multiple missing fields *) test "()"; [%expect {| (Error (Of_sexp_error "M.t_of_sexp: the following record elements were undefined: a b" (invalid_sexp ()))) |}]; () ;; let%expect_test _ = let big_string = String.init 5_000_000 ~f:(fun i -> String.get (Int.to_string i) 0) in let sexp = [%sexp (big_string : string)] in let sexp_string = (* In an experimental compiler version, this would overflow the stack. *) Sexp.to_string sexp in print_endline (Int.to_string_hum (String.length sexp_string)); [%expect {| 5_000_000 |}] ;;