mte/unikernel/duniverse/ppx_sexp_conv/expander/expand_sexp_of.ml

823 lines
30 KiB
OCaml
Raw Normal View History

2025-11-11 02:07:51 +01:00
open! Base
open! Ppxlib
open Ast_builder.Default
open Helpers
open Lifted.Monad_infix
(* Generates the signature for type conversion to S-expressions *)
module Sig_generate_sexp_of = struct
let type_of_sexp_of ~loc t =
let loc = { loc with loc_ghost = true } in
[%type: [%t t] -> Sexplib0.Sexp.t]
;;
let mk_type td = combinator_type_of_type_declaration td ~f:type_of_sexp_of
let mk_sig ~loc:_ ~path:_ (_rf, tds) =
List.map tds ~f:(fun td ->
let loc = td.ptype_loc in
psig_value
~loc
(value_description
~loc
~name:(Located.map (( ^ ) "sexp_of_") td.ptype_name)
~type_:(mk_type td)
~prim:[]))
;;
let mk_sig_exn ~loc:_ ~path:_ _te = []
end
module Str_generate_sexp_of = struct
module Types_being_defined = struct
type t =
| Nonrec
| Rec of Set.M(String).t
let to_rec_flag = function
| Nonrec -> Nonrecursive
| Rec _ -> Recursive
;;
let to_values_being_defined = function
| Nonrec -> Set.empty (module String)
| Rec types -> Set.map (module String) types ~f:(fun s -> "sexp_of_" ^ s)
;;
end
let sexp_of_type_constr ~loc id args =
type_constr_conv ~loc id ~f:(fun s -> "sexp_of_" ^ s) args
;;
(* Conversion of types *)
let rec sexp_of_type ~renaming typ : Conversion.t =
let loc = { typ.ptyp_loc with loc_ghost = true } in
match Ppxlib_jane.Jane_syntax.Core_type.of_ast typ with
| Some (Jtyp_tuple alist, (_ : attributes)) ->
Conversion.of_lambda [ sexp_of_labeled_tuple ~renaming ~loc alist ]
| Some (Jtyp_layout _, _) | None ->
(match typ with
| _ when Option.is_some (Attribute.get Attrs.opaque typ) ->
Conversion.of_reference_exn [%expr Sexplib0.Sexp_conv.sexp_of_opaque]
| [%type: _] ->
Conversion.of_lambda [ ppat_any ~loc --> [%expr Sexplib0.Sexp.Atom "_"] ]
| [%type: [%t? _] sexp_opaque] ->
Conversion.of_reference_exn [%expr Sexplib0.Sexp_conv.sexp_of_opaque]
| { ptyp_desc = Ptyp_tuple tp; _ } ->
Conversion.of_lambda [ sexp_of_tuple ~renaming (loc, tp) ]
| { ptyp_desc = Ptyp_var parm; _ } ->
(match Renaming.binding_kind renaming parm ~loc with
| Universally_bound fresh ->
Conversion.of_reference_exn (Fresh_name.expression fresh)
| Existentially_bound -> sexp_of_type ~renaming [%type: _])
| { ptyp_desc = Ptyp_constr (id, args); _ } ->
Conversion.of_reference_exn
(sexp_of_type_constr
~loc
id
(List.map args ~f:(fun tp ->
Conversion.to_expression ~loc (sexp_of_type ~renaming tp))))
| { ptyp_desc = Ptyp_arrow (_, _, _); _ } ->
Conversion.of_lambda
[ ppat_any ~loc
--> [%expr Sexplib0.Sexp_conv.sexp_of_fun Sexplib0.Sexp_conv.ignore]
]
| { ptyp_desc = Ptyp_variant (row_fields, Closed, _); _ } ->
sexp_of_variant ~renaming (loc, row_fields)
| { ptyp_desc = Ptyp_poly (parms, poly_tp); _ } ->
sexp_of_poly ~renaming parms poly_tp
| { ptyp_desc = Ptyp_variant (_, Open, _); _ }
| { ptyp_desc = Ptyp_object (_, _); _ }
| { ptyp_desc = Ptyp_class (_, _); _ }
| { ptyp_desc = Ptyp_alias (_, _); _ }
| { ptyp_desc = Ptyp_package _; _ }
| { ptyp_desc = Ptyp_extension _; _ }
| { ptyp_desc = Ptyp_open _; _ } ->
Location.raise_errorf ~loc "Type unsupported for ppx [sexp_of] conversion")
(* Conversion of (unlabeled) tuples *)
and sexp_of_tuple ~renaming (loc, tps) =
let fps = List.map ~f:(fun tp -> sexp_of_type ~renaming tp) tps in
let ({ bindings; arguments; converted } : Conversion.Apply_all.t) =
Conversion.apply_all ~loc fps
in
let in_expr = [%expr Sexplib0.Sexp.List [%e elist ~loc converted]] in
let expr = pexp_let ~loc Nonrecursive bindings in_expr in
ppat_tuple ~loc arguments --> expr
(* Conversion of labeled tuples *)
and sexp_of_labeled_tuple ~renaming ~loc alist =
assert (Labeled_tuple.is_valid alist);
let ({ bindings; arguments; converted } : Conversion.Apply_all.t) =
List.map alist ~f:(fun (_, core_type) -> sexp_of_type ~renaming core_type)
|> Conversion.apply_all ~loc
in
let expr =
let sexp_exprs =
(* Constructor inference allows to to leave off [Sexplib0.Sexp.] here. *)
List.map2_exn alist converted ~f:(fun (label_option, _) expr ->
[%expr
List
[ Atom [%e estring ~loc (Labeled_tuple.atom_of_label label_option)]
; [%e expr]
]])
in
[%expr Sexplib0.Sexp.List [%e elist ~loc sexp_exprs]]
|> pexp_let ~loc Nonrecursive bindings
in
let pat =
( List.map2_exn alist arguments ~f:(fun (label_option, _) arg -> label_option, arg)
, Closed )
|> Ppxlib_jane.Jane_syntax.Labeled_tuples.pat_of ~loc
in
pat --> expr
(* Conversion of variant types *)
and sexp_of_variant ~renaming ((loc, row_fields) : Location.t * row_field list)
: Conversion.t
=
let item row =
match row.prf_desc with
| Rtag ({ txt = cnstr; _ }, true, []) ->
ppat_variant ~loc cnstr None
--> [%expr Sexplib0.Sexp.Atom [%e estring ~loc cnstr]]
| Rtag ({ txt = cnstr; _ }, _, [ tp ])
when Option.is_some (Attribute.get Attrs.list_poly row) ->
(match tp with
| [%type: [%t? tp] list] ->
let cnv_expr = Conversion.to_expression ~loc (sexp_of_type ~renaming tp) in
let name = Fresh_name.create "l" ~loc in
ppat_variant ~loc cnstr (Some (Fresh_name.pattern name))
--> [%expr
Sexplib0.Sexp.List
(Sexplib0.Sexp.Atom [%e estring ~loc cnstr]
:: Sexplib0.Sexp_conv.list_map
[%e cnv_expr]
[%e Fresh_name.expression name])]
| _ -> Attrs.invalid_attribute ~loc Attrs.list_poly "_ list")
| Rtag ({ txt = cnstr; _ }, _, [ [%type: [%t? tp] sexp_list] ]) ->
let cnv_expr = Conversion.to_expression ~loc (sexp_of_type ~renaming tp) in
let name = Fresh_name.create "l" ~loc in
ppat_variant ~loc cnstr (Some (Fresh_name.pattern name))
--> [%expr
Sexplib0.Sexp.List
(Sexplib0.Sexp.Atom [%e estring ~loc cnstr]
:: Sexplib0.Sexp_conv.list_map
[%e cnv_expr]
[%e Fresh_name.expression name])]
| Rtag ({ txt = cnstr; _ }, false, [ tp ]) ->
let cnstr_expr = [%expr Sexplib0.Sexp.Atom [%e estring ~loc cnstr]] in
let fresh = Fresh_name.create "v" ~loc in
let cnstr_arg =
Conversion.apply ~loc (sexp_of_type ~renaming tp) (Fresh_name.expression fresh)
in
let expr = [%expr Sexplib0.Sexp.List [%e elist ~loc [ cnstr_expr; cnstr_arg ]]] in
ppat_variant ~loc cnstr (Some (Fresh_name.pattern fresh)) --> expr
| Rinherit { ptyp_desc = Ptyp_constr (id, []); _ } ->
let name = Fresh_name.create "v" ~loc in
ppat_alias ~loc (ppat_type ~loc id) (Fresh_name.to_string_loc name)
--> sexp_of_type_constr ~loc id [ Fresh_name.expression name ]
| Rtag (_, true, [ _ ]) | Rtag (_, _, _ :: _ :: _) ->
Location.raise_errorf ~loc "unsupported: polymorphic variant intersection type"
| Rinherit ({ ptyp_desc = Ptyp_constr (id, _ :: _); _ } as typ) ->
let call = Conversion.to_expression ~loc (sexp_of_type ~renaming typ) in
let name = Fresh_name.create "v" ~loc in
ppat_alias ~loc (ppat_type ~loc id) (Fresh_name.to_string_loc name)
--> [%expr [%e call] [%e Fresh_name.expression name]]
| Rinherit _ ->
Location.raise_errorf
~loc
"unsupported: polymorphic variant with invalid (non-identifier) inherited type"
| Rtag (_, false, []) ->
Location.raise_errorf ~loc "unsupported: polymorphic variant empty type"
in
Conversion.of_lambda (List.map ~f:item row_fields)
(* Polymorphic record fields *)
and sexp_of_poly ~renaming parms tp =
let loc = tp.ptyp_loc in
let renaming =
List.fold_left
parms
~init:renaming
~f:(Renaming.add_universally_bound ~prefix:"_of_")
in
let bindings =
let mk_binding parm =
let name =
match Renaming.binding_kind renaming parm.txt ~loc:parm.loc with
| Universally_bound name -> name
| Existentially_bound -> assert false
in
value_binding
~loc
~pat:(Fresh_name.pattern name)
~expr:[%expr Sexplib0.Sexp_conv.sexp_of_opaque]
in
List.map ~f:mk_binding parms
in
Conversion.bind (sexp_of_type ~renaming tp) bindings
;;
(* Conversion of record types *)
let mk_rec_patt loc patt name fresh =
let p = Loc.make (Longident.Lident name) ~loc, Fresh_name.pattern fresh in
patt @ [ p ]
;;
type is_empty_expr =
| Inspect_value of (location -> expression -> expression)
| Inspect_sexp of (cnv_expr:expression -> location -> expression -> expression)
let sexp_of_record_field ~renaming ~bnds patt expr name tp ?sexp_of is_empty_expr =
let loc = tp.ptyp_loc in
let fresh = Fresh_name.create name ~loc in
let patt = mk_rec_patt loc patt name fresh in
let cnv_expr = Conversion.to_expression ~loc (sexp_of_type ~renaming tp) in
let cnv_expr =
match sexp_of with
| None -> cnv_expr
| Some sexp_of -> [%expr [%e sexp_of] [%e cnv_expr]]
in
let bnd = Fresh_name.create "bnd" ~loc in
let arg = Fresh_name.create "arg" ~loc in
let expr =
[%expr
let [%p Fresh_name.pattern bnds] =
[%e
match is_empty_expr with
| Inspect_value is_empty_expr ->
[%expr
if [%e is_empty_expr loc (Fresh_name.expression fresh)]
then [%e Fresh_name.expression bnds]
else (
let [%p Fresh_name.pattern arg] =
[%e cnv_expr] [%e Fresh_name.expression fresh]
in
let [%p Fresh_name.pattern bnd] =
Sexplib0.Sexp.List
[ Sexplib0.Sexp.Atom [%e estring ~loc name]
; [%e Fresh_name.expression arg]
]
in
([%e Fresh_name.expression bnd] :: [%e Fresh_name.expression bnds]
: _ Stdlib.List.t))]
| Inspect_sexp is_empty_expr ->
[%expr
let [%p Fresh_name.pattern arg] =
[%e cnv_expr] [%e Fresh_name.expression fresh]
in
if [%e is_empty_expr ~cnv_expr loc (Fresh_name.expression arg)]
then [%e Fresh_name.expression bnds]
else (
let [%p Fresh_name.pattern bnd] =
Sexplib0.Sexp.List
[ Sexplib0.Sexp.Atom [%e estring ~loc name]
; [%e Fresh_name.expression arg]
]
in
([%e Fresh_name.expression bnd] :: [%e Fresh_name.expression bnds]
: _ Stdlib.List.t))]]
in
[%e expr]]
in
patt, expr
;;
let disallow_type_variables_and_recursive_occurrences
~types_being_defined
~loc
~attr_name
tp
=
let disallow_variables =
let iter =
object
inherit Ast_traverse.iter as super
method! core_type_desc =
function
| Ptyp_var v ->
Location.raise_errorf
~loc
"[@%s] was used, but the type of the field contains a type variable: '%s.\n\
Comparison is not avaiable for type variables.\n\
Consider using [@sexp_drop_if _] or [@sexp_drop_default.sexp] instead."
attr_name
v
| t -> super#core_type_desc t
end
in
iter#core_type
in
let disallow_recursive_occurrences =
match (types_being_defined : Types_being_defined.t) with
| Nonrec -> fun _ -> ()
| Rec types_being_defined ->
let iter =
object
inherit Ast_traverse.iter as super
method! core_type_desc =
function
| Ptyp_constr ({ loc = _; txt = Lident s }, _) as t ->
if Set.mem types_being_defined s
then
Location.raise_errorf
~loc
"[@%s] was used, but the type of the field contains a type defined \
in the current recursive block: %s.\n\
This is not supported.\n\
Consider using [@sexp_drop_if _] or [@sexp_drop_default.sexp] \
instead."
attr_name
s;
super#core_type_desc t
| t -> super#core_type_desc t
end
in
iter#core_type
in
disallow_variables tp;
disallow_recursive_occurrences tp
;;
let sexp_of_default_field
~types_being_defined
how
~renaming
~bnds
patt
expr
name
tp
?sexp_of
default
=
let is_empty =
let inspect_value equality_f =
Inspect_value (fun loc expr -> [%expr [%e equality_f loc] [%e default] [%e expr]])
in
match (how : Record_field_attrs.Sexp_of.Drop.t) with
| Sexp ->
Inspect_sexp
(fun ~cnv_expr loc sexp_expr ->
[%expr Sexplib0.Sexp_conv.( = ) ([%e cnv_expr] [%e default]) [%e sexp_expr]])
|> Lifted.return
| No_arg ->
inspect_value (fun loc ->
[%expr
Sexplib0.Sexp_conv.( = ) [@ocaml.ppwarning
"[@sexp_drop_default] is deprecated: please use \
one of:\n\
- [@sexp_drop_default f] and give an explicit \
equality function ([f = Poly.(=)] corresponds to \
the old behavior)\n\
- [@sexp_drop_default.compare] if the type \
supports [%compare]\n\
- [@sexp_drop_default.equal] if the type \
supports [%equal]\n\
- [@sexp_drop_default.sexp] if you want to \
compare the sexp representations\n"]])
|> Lifted.return
| Func lifted -> lifted >>| fun f -> inspect_value (fun _ -> f)
| Compare ->
inspect_value (fun loc ->
disallow_type_variables_and_recursive_occurrences
~types_being_defined
~attr_name:"sexp_drop_default.compare"
~loc
tp;
[%expr [%compare.equal: [%t tp]]])
|> Lifted.return
| Equal ->
inspect_value (fun loc ->
disallow_type_variables_and_recursive_occurrences
~types_being_defined
~attr_name:"sexp_drop_default.equal"
~loc
tp;
[%expr [%equal: [%t tp]]])
|> Lifted.return
in
is_empty >>| sexp_of_record_field ~renaming ~bnds patt expr name tp ?sexp_of
;;
let sexp_of_label_declaration_list ~types_being_defined ~renaming loc flds ~wrap_expr =
let bnds = Fresh_name.create "bnds" ~loc in
let list_empty_expr =
Inspect_value
(fun loc lst ->
[%expr
match [%e lst] with
| [] -> true
| _ -> false])
in
let array_empty_expr =
Inspect_value
(fun loc arr ->
[%expr
match [%e arr] with
| [||] -> true
| _ -> false])
in
let coll lifted ld =
lifted
>>= fun ((patt : (Longident.t loc * pattern) list), expr) ->
let name = ld.pld_name.txt in
let loc = ld.pld_name.loc in
let fresh = Fresh_name.create name ~loc in
match Record_field_attrs.Sexp_of.create ~loc ld with
| Sexp_option tp ->
let v = Fresh_name.create "v" ~loc in
let bnd = Fresh_name.create "bnd" ~loc in
let arg = Fresh_name.create "arg" ~loc in
let patt = mk_rec_patt loc patt name fresh in
let vname = Fresh_name.expression v in
let cnv_expr = Conversion.apply ~loc (sexp_of_type ~renaming tp) vname in
let expr =
[%expr
let [%p Fresh_name.pattern bnds] =
match [%e Fresh_name.expression fresh] with
| Stdlib.Option.None -> [%e Fresh_name.expression bnds]
| Stdlib.Option.Some [%p Fresh_name.pattern v] ->
let [%p Fresh_name.pattern arg] = [%e cnv_expr] in
let [%p Fresh_name.pattern bnd] =
Sexplib0.Sexp.List
[ Sexplib0.Sexp.Atom [%e estring ~loc name]
; [%e Fresh_name.expression arg]
]
in
([%e Fresh_name.expression bnd] :: [%e Fresh_name.expression bnds]
: _ Stdlib.List.t)
in
[%e expr]]
in
Lifted.return (patt, expr)
| Sexp_bool ->
let patt = mk_rec_patt loc patt name fresh in
let bnd = Fresh_name.create "bnd" ~loc in
let expr =
[%expr
let [%p Fresh_name.pattern bnds] =
if [%e Fresh_name.expression fresh]
then (
let [%p Fresh_name.pattern bnd] =
Sexplib0.Sexp.List [ Sexplib0.Sexp.Atom [%e estring ~loc name] ]
in
([%e Fresh_name.expression bnd] :: [%e Fresh_name.expression bnds]
: _ Stdlib.List.t))
else [%e Fresh_name.expression bnds]
in
[%e expr]]
in
Lifted.return (patt, expr)
| Sexp_list tp ->
sexp_of_record_field
~renaming
~bnds
patt
expr
name
tp
~sexp_of:
(* deliberately using whatever [sexp_of_list] is in scope *)
[%expr sexp_of_list]
list_empty_expr
|> Lifted.return
| Sexp_array tp ->
sexp_of_record_field
~renaming
~bnds
patt
expr
name
tp
~sexp_of:
(* deliberately using whatever [sexp_of_array] is in scope *)
[%expr sexp_of_array]
array_empty_expr
|> Lifted.return
| Specific (Drop_default how) ->
let tp = ld.pld_type in
(match Attribute.get Attrs.default ld with
| None -> Location.raise_errorf ~loc "no default to drop"
| Some { to_lift = default } ->
Record_field_attrs.lift_default ~loc ld default
>>= sexp_of_default_field
~types_being_defined
how
~renaming
~bnds
patt
expr
name
tp)
| Specific (Drop_if test) ->
test
>>| fun test ->
let tp = ld.pld_type in
sexp_of_record_field
~renaming
~bnds
patt
expr
name
tp
(Inspect_value (fun loc expr -> [%expr [%e test] [%e expr]]))
| Omit_nil ->
let tp = ld.pld_type in
let patt = mk_rec_patt loc patt name fresh in
let vname = Fresh_name.expression fresh in
let arg = Fresh_name.create "arg" ~loc in
let cnv_expr = Conversion.apply ~loc (sexp_of_type ~renaming tp) vname in
let bnds_expr =
[%expr
match [%e cnv_expr] with
| Sexplib0.Sexp.List [] -> [%e Fresh_name.expression bnds]
| [%p Fresh_name.pattern arg] ->
(Sexplib0.Sexp.List
[ Sexplib0.Sexp.Atom [%e estring ~loc name]
; [%e Fresh_name.expression arg]
]
:: [%e Fresh_name.expression bnds]
: _ Stdlib.List.t)]
in
( patt
, [%expr
let [%p Fresh_name.pattern bnds] = [%e bnds_expr] in
[%e expr]] )
|> Lifted.return
| Specific Keep ->
let tp = ld.pld_type in
let patt = mk_rec_patt loc patt name fresh in
let vname = Fresh_name.expression fresh in
let arg = Fresh_name.create "arg" ~loc in
let cnv_expr = Conversion.apply ~loc (sexp_of_type ~renaming tp) vname in
let bnds_expr =
[%expr
let [%p Fresh_name.pattern arg] = [%e cnv_expr] in
(Sexplib0.Sexp.List
[ Sexplib0.Sexp.Atom [%e estring ~loc name]
; [%e Fresh_name.expression arg]
]
:: [%e Fresh_name.expression bnds]
: _ Stdlib.List.t)]
in
( patt
, [%expr
let [%p Fresh_name.pattern bnds] = [%e bnds_expr] in
[%e expr]] )
|> Lifted.return
in
let init_expr = wrap_expr (Fresh_name.expression bnds) in
List.fold_left ~f:coll ~init:(Lifted.return ([], init_expr)) flds
>>| fun (patt, expr) ->
( ppat_record ~loc patt Closed
, [%expr
let [%p Fresh_name.pattern bnds] = ([] : _ Stdlib.List.t) in
[%e expr]] )
;;
(* Conversion of sum types *)
let branch_sum
row
inline_attr
~types_being_defined
renaming
~loc
constr_lid
constr_str
args
=
match args with
| Pcstr_record lds ->
let cnstr_expr = [%expr Sexplib0.Sexp.Atom [%e constr_str]] in
sexp_of_label_declaration_list
~types_being_defined
~renaming
loc
lds
~wrap_expr:(fun expr -> [%expr Sexplib0.Sexp.List ([%e cnstr_expr] :: [%e expr])])
>>| fun (patt, expr) -> ppat_construct ~loc constr_lid (Some patt) --> expr
| Pcstr_tuple pcd_args ->
(match pcd_args with
| [] ->
ppat_construct ~loc constr_lid None
--> [%expr Sexplib0.Sexp.Atom [%e constr_str]]
|> Lifted.return
| args ->
(match args with
| [ tp ] when Option.is_some (Attribute.get inline_attr row) ->
(match tp with
| [%type: [%t? tp] list] ->
let cnv_expr = Conversion.to_expression ~loc (sexp_of_type ~renaming tp) in
let name = Fresh_name.create "l" ~loc in
ppat_construct ~loc constr_lid (Some (Fresh_name.pattern name))
--> [%expr
Sexplib0.Sexp.List
(Sexplib0.Sexp.Atom [%e constr_str]
:: Sexplib0.Sexp_conv.list_map
[%e cnv_expr]
[%e Fresh_name.expression name])]
| _ -> Attrs.invalid_attribute ~loc inline_attr "_ list")
| [ [%type: [%t? tp] sexp_list] ] ->
let cnv_expr = Conversion.to_expression ~loc (sexp_of_type ~renaming tp) in
let name = Fresh_name.create "l" ~loc in
ppat_construct ~loc constr_lid (Some (Fresh_name.pattern name))
--> [%expr
Sexplib0.Sexp.List
(Sexplib0.Sexp.Atom [%e constr_str]
:: Sexplib0.Sexp_conv.list_map
[%e cnv_expr]
[%e Fresh_name.expression name])]
| _ ->
let sexp_of_args = List.map ~f:(sexp_of_type ~renaming) args in
let cnstr_expr = [%expr Sexplib0.Sexp.Atom [%e constr_str]] in
let ({ bindings; arguments; converted } : Conversion.Apply_all.t) =
Conversion.apply_all ~loc sexp_of_args
in
let patt =
match arguments with
| [ arg ] -> arg
| _ -> ppat_tuple ~loc arguments
in
ppat_construct ~loc constr_lid (Some patt)
--> pexp_let
~loc
Nonrecursive
bindings
[%expr Sexplib0.Sexp.List [%e elist ~loc (cnstr_expr :: converted)]])
|> Lifted.return)
;;
let sexp_of_sum ~types_being_defined ~renaming tps cds =
List.map cds ~f:(fun cd ->
let renaming =
Renaming.with_constructor_declaration renaming ~type_parameters:tps cd
in
let constr_lid = Located.map lident cd.pcd_name in
let constr_str = estring ~loc:cd.pcd_name.loc cd.pcd_name.txt in
branch_sum
cd
Attrs.list_variant
~types_being_defined
renaming
~loc:cd.pcd_loc
constr_lid
constr_str
cd.pcd_args)
|> Lifted.all
>>| Conversion.of_lambda
;;
(* Empty type *)
let sexp_of_nil loc = Conversion.of_lambda [ ppat_any ~loc --> [%expr assert false] ]
(* Generate code from type definitions *)
let sexp_of_td ~types_being_defined td =
let td = name_type_params_in_td td in
let tps = List.map td.ptype_params ~f:get_type_param_name in
let { ptype_name = { txt = type_name; loc = _ }; ptype_loc = loc; _ } = td in
let renaming = Renaming.of_type_declaration td ~prefix:"_of_" in
let body =
let body =
match td.ptype_kind with
| Ptype_variant cds ->
sexp_of_sum
~renaming
~types_being_defined
(List.map tps ~f:(fun x -> x.txt))
cds
| Ptype_record lds ->
sexp_of_label_declaration_list
~renaming
loc
lds
~types_being_defined
~wrap_expr:(fun expr -> [%expr Sexplib0.Sexp.List [%e expr]])
>>| fun (patt, expr) -> Conversion.of_lambda [ patt --> expr ]
| Ptype_open ->
Location.raise_errorf ~loc "ppx_sexp_conv: open types not supported"
| Ptype_abstract ->
(match td.ptype_manifest with
| None -> sexp_of_nil loc
| Some ty -> sexp_of_type ~renaming ty)
|> Lifted.return
in
body
>>| fun body ->
let is_private_alias =
match td.ptype_kind, td.ptype_manifest, td.ptype_private with
| Ptype_abstract, Some _, Private -> true
| _ -> false
in
if is_private_alias
then (
(* Replace all type variable by _ to avoid generalization problems *)
let ty_src =
core_type_of_type_declaration td |> replace_variables_by_underscores
in
let manifest =
match td.ptype_manifest with
| Some manifest -> manifest
| None -> Location.raise_errorf ~loc "sexp_of_td/no-manifest"
in
let ty_dst = replace_variables_by_underscores manifest in
let v = Fresh_name.create "v" ~loc in
let coercion =
[%expr ([%e Fresh_name.expression v] : [%t ty_src] :> [%t ty_dst])]
in
[%expr fun [%p Fresh_name.pattern v] -> [%e Conversion.apply ~loc body coercion]])
else
(* Prevent violation of value restriction, problems with recursive types, and
top-level effects by eta-expanding function definitions *)
Conversion.to_value_expression
~loc
~rec_flag:(Types_being_defined.to_rec_flag types_being_defined)
~values_being_defined:
(Types_being_defined.to_values_being_defined types_being_defined)
body
in
let typ = Sig_generate_sexp_of.mk_type td in
let func_name = "sexp_of_" ^ type_name in
let body =
body
>>| fun body ->
let patts =
List.map tps ~f:(fun id ->
match Renaming.binding_kind renaming id.txt ~loc:id.loc with
| Universally_bound name -> Fresh_name.pattern name
| Existentially_bound -> assert false)
in
let rec_flag = Types_being_defined.to_rec_flag types_being_defined in
eta_reduce_if_possible_and_nonrec ~rec_flag (eabstract ~loc patts body)
in
let body = Lifted.let_bind_user_expressions ~loc body in
constrained_function_binding loc td typ ~tps ~func_name body
;;
let sexp_of_tds ~loc ~path:_ (rec_flag, tds) =
let rec_flag = really_recursive_respecting_opaque rec_flag tds in
let (types_being_defined : Types_being_defined.t) =
match rec_flag with
| Nonrecursive -> Nonrec
| Recursive ->
Rec (Set.of_list (module String) (List.map tds ~f:(fun td -> td.ptype_name.txt)))
in
let bindings = List.map tds ~f:(sexp_of_td ~types_being_defined) in
pstr_value_list ~loc rec_flag bindings
;;
let sexp_of_exn ~loc:_ ~path ec =
let renaming = Renaming.without_type () in
let get_full_cnstr str = path ^ "." ^ str in
let loc = ec.ptyexn_loc in
let expr =
match ec.ptyexn_constructor with
| { pext_name = cnstr
; pext_kind = Pext_decl (_, extension_constructor_kind, None)
; _
} ->
let constr_lid = Located.map lident cnstr in
branch_sum
ec
Attrs.list_exception
~types_being_defined:Nonrec
renaming
~loc
constr_lid
(estring ~loc (get_full_cnstr cnstr.txt))
extension_constructor_kind
>>| fun converter ->
let assert_false = ppat_any ~loc --> [%expr assert false] in
[%expr
Sexplib0.Sexp_conv.Exn_converter.add
[%extension_constructor [%e pexp_construct ~loc constr_lid None]]
[%e
Conversion.to_expression
~loc
(Conversion.of_lambda [ converter; assert_false ])]]
| { pext_kind = Pext_decl (_, _, Some _); _ } ->
Location.raise_errorf ~loc "sexp_of_exn/:"
| { pext_kind = Pext_rebind _; _ } ->
Location.raise_errorf ~loc "sexp_of_exn/rebind"
in
let expr = Lifted.let_bind_user_expressions ~loc expr in
[ pstr_value ~loc Nonrecursive [ value_binding ~loc ~pat:[%pat? ()] ~expr ] ]
;;
let sexp_of_core_type core_type =
let loc = { core_type.ptyp_loc with loc_ghost = true } in
sexp_of_type ~renaming:(Renaming.without_type ()) core_type
|> Conversion.to_value_expression
~loc
~rec_flag:Nonrecursive
~values_being_defined:(Set.empty (module String))
|> Merlin_helpers.hide_expression
;;
end