123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290openPpxlibletto_encoder_namei=i^"_encoder"letrecflatten_longident~loc=function|Lidenttxt->txt|Ldot(longident,txt)->flatten_longidentlongident~loc^"."^txt|Lapply(fst,snd)->Location.raise_errorf~loc"Cannot handle functors:%s (%s)"(flatten_longident~locfst)(flatten_longident~locsnd)letlongident_to_encoder_name~loc=CCFun.(to_encoder_name%flatten_longident~loc)letname_to_encoder_name(i:stringloc)=to_encoder_namei.txtletrecexpr_of_typ(typ:core_type):expression=letloc={typ.ptyp_locwithloc_ghost=true}inmatchtypwith|[%type:unit]|[%type:unit]->Ast_builder.Default.evar~loc"E.null"|[%type:int]->Ast_builder.Default.evar~loc"E.int"|[%type:int32]|[%type:Int32.t]->letint_enc=Ast_builder.Default.evar~loc"E.int"in[%exprfuni->i|>Int32.to_int|>[%eint_enc]]|[%type:int64]|[%type:Int64.t]->letint_enc=Ast_builder.Default.evar~loc"E.int"in[%exprfuni->i|>Int64.to_int|>[%eint_enc]]|[%type:nativeint]|[%type:Nativeint.t]->letint_enc=Ast_builder.Default.evar~loc"E.int"in[%exprfuni->i|>Nativeint.to_int|>[%eint_enc]]|[%type:float]->Ast_builder.Default.evar~loc"E.float"|[%type:bool]->Ast_builder.Default.evar~loc"E.bool"|[%type:char]->[%exprfunc->E.string(String.make1c)]|[%type:string]|[%type:String.t]->Ast_builder.Default.evar~loc"E.string"|[%type:bytes]|[%type:Bytes.t]->Location.raise_errorf~loc"Cannot construct an encoder for bytes"|[%type:[%t?inner_typ]list]->letlist_encoder=Ast_builder.Default.evar~loc"E.list"inletsub_expr=expr_of_typinner_typinAst_helper.Exp.apply~loclist_encoder[(Nolabel,sub_expr)]|[%type:[%t?inner_typ]array]->letarray_encoder=Ast_builder.Default.evar~loc"E.array"inletsub_expr=expr_of_typinner_typinAst_helper.Exp.apply~locarray_encoder[(Nolabel,sub_expr)]|[%type:[%t?inner_typ]option]->letopt_encoder=Ast_builder.Default.evar~loc"E.nullable"inletsub_expr=expr_of_typ(* ~substitutions *)inner_typinAst_helper.Exp.apply~locopt_encoder[(Nolabel,sub_expr)]|{ptyp_desc=Ptyp_tupletyps;_}->expr_of_tuple~loctyps|{ptyp_desc=Ptyp_varvar;_}->Ast_builder.Default.evar~loc@@to_encoder_namevar|{ptyp_desc=Ptyp_constr({txt=Lidentlid;_},[]);_}->(* The assumption here is that if we get to this point, this type is recursive, and
we just assume that we already have an encoder available.
TODO: Is this really the case?
*)Ast_builder.Default.evar~loc(to_encoder_namelid)|{ptyp_desc=Ptyp_constr({txt=longident;loc},args);_}->letcstr_dec=Ast_builder.Default.evar~loc@@longident_to_encoder_name~loclongidentinletarg_decs=CCList.mapexpr_of_typargsinAst_builder.Default.eapply~loccstr_decarg_decs|{ptyp_desc=Ptyp_arrow_;_}->Location.raise_errorf~loc"Cannot construct encoder for %s: cannot encode functions"(string_of_core_typetyp)|{ptyp_desc=Ptyp_object_;_}->Location.raise_errorf~loc"Cannot construct encoder for %s: cannot encode objects"(string_of_core_typetyp)|{ptyp_desc=Ptyp_class_;_}->Location.raise_errorf~loc"Cannot construct encoder for %s: cannot encode classes"(string_of_core_typetyp)|{ptyp_desc=Ptyp_package_;_}->Location.raise_errorf~loc"Cannot construct encoder for %s: cannot encode packages"(string_of_core_typetyp)|{ptyp_desc=Ptyp_poly_;_}->Location.raise_errorf~loc"Cannot construct encoder for %s: cannot encode explicitly polymorphic \
types"(string_of_core_typetyp)|{ptyp_desc=Ptyp_any;_}->Location.raise_errorf~loc"Cannot construct encoder for %s: cannot encode wildcard in type "(string_of_core_typetyp)|{ptyp_desc=Ptyp_alias_;_}->Location.raise_errorf~loc"Cannot construct encoder for %s: cannot encode type alias"(string_of_core_typetyp)|{ptyp_desc=Ptyp_variant_;_}->Location.raise_errorf~loc"Cannot construct encoder for %s: cannot encode polymorphic variant"(string_of_core_typetyp)|{ptyp_desc=Ptyp_extension_;_}->Location.raise_errorf~loc"Cannot construct encoder for %s: cannot encode type extension point"(string_of_core_typetyp)andexpr_of_tuple~loc(* ~substitutions ?lift *)typs=(* Want to take type a * b * c and produce
fun (arg1,arg2,arg3) -> E.list E.value [E.a arg1; E.b arg2; E.c arg3]
*)lettyp_encoders_exprs=List.mapexpr_of_typ(* ~substitutions *)typsinleteargs=CCList.mapi(funidx_typ->Ast_builder.Default.evar~loc@@Utils.argnidx)typsinletencoded_args=Ast_builder.Default.elist~loc@@CCList.map2(funencoderarg->[%expr[%eencoder][%earg]])typ_encoders_exprseargsinletencoder_result=[%exprE.listE.value[%eencoded_args]]in[%expr[%eencoder_result]]andexpr_of_record~loc(* ~substitutions ?lift *)label_decls=(* To help understand what this function is doing, imagine we had
a type [type t = {i : int; s : string}]. Then this will render the encoder:
let t_encoder : t E.encoder =
fun {i; s} -> E.obj [("i", int i); ("s", string s)]
*)letencode_field{pld_name;pld_type;_}=Ast_builder.Default.(pexp_tuple~loc[estring~locpld_name.txt;eapply~loc(expr_of_typpld_type)[evar~locpld_name.txt];])inletencode_all=letopenAst_builder.Defaultineapply~loc(evar~loc"E.obj")@@[elist~loc(CCList.mapencode_fieldlabel_decls)]inencode_allandexpr_of_constr_arg~loc(arg:constructor_arguments)=matchargwith|Pcstr_tupletups->expr_of_tuple~loctups|Pcstr_recordlabl_decls->expr_of_record~loclabl_declsandexpr_of_constr_decl({pcd_args;pcd_loc=loc;_}ascstr_decl:constructor_declaration)=(* We assume at this point that the decomposition into indiviaul fields is handled by caller *)letcstr_name=Ast_builder.Default.estring~loccstr_decl.pcd_name.txtinletencoded_args=matchpcd_argswith|Pcstr_tuple[]->[%exprE.null]|Pcstr_tuple[single]->letenc=expr_of_typsingleinleton=Ast_builder.Default.evar~loc(Utils.argn0)in[%expr[%eenc][%eon]]|_->expr_of_constr_arg~locpcd_argsin[%exprE.obj[([%ecstr_name],[%eencoded_args])]]andexpr_of_variant~loccstrs=(* Producing from type `A | B of b | C of c`
to
function
| A -> {"A":null}
| B b -> {"B": b_encoder b}
| C c - {"C": c_encoder c}
*)letopenAst_builder.Defaultinletto_case(cstr:constructor_declaration)=letinner_pattern=matchcstr.pcd_argswith|Pcstr_tuple[]->None|Pcstr_tuple[_tuple]->Some(pvar~loc(Utils.argn0))|Pcstr_tupletuples->Some(ppat_tuple~loc@@CCList.mapi(funi_tup->pvar~loc(Utils.argni))tuples)|Pcstr_recordlbl_decls->letarg_fields=CCList.map(fun{pld_name;_}->({txt=Lidentpld_name.txt;loc},Ast_builder.Default.pvar~locpld_name.txt))lbl_declsinSome(Ast_builder.Default.ppat_record~locarg_fieldsClosed)inletvpat=ppat_construct~loc(Utils.lident_of_constructor_declcstr)inner_patterninletenc_expression=expr_of_constr_declcstrincase~lhs:vpat~guard:None~rhs:enc_expressioninletcases=List.mapto_casecstrsinpexp_function~loccasesletimplementation_generator~(loc:location)type_decl:expression=letimple_expr=match(type_decl.ptype_kind,type_decl.ptype_manifest)with|Ptype_abstract,Somemanifest->(letexpr=expr_of_typmanifestinmatchmanifestwith|{ptyp_desc=Ptyp_tupletyps;_}->(* In the case of a top level tuple, we need to explicitly wrap in a lambda with
the arguments
*)letargs=Ast_builder.Default.ppat_tuple~loc@@CCList.mapi(funi_typ->Ast_builder.Default.pvar~loc(Utils.argni))typsin[%exprfun[%pargs]->[%eexpr]]|_->expr)|Ptype_abstract,None->Location.raise_errorf~loc"Cannot construct encoder for %s: cannot encode abstract type"type_decl.ptype_name.txt|Ptype_variantcstrs,_->expr_of_variant~loccstrs|Ptype_recordlabel_decs,_->(* And in the case of a top-level record, we also need to explicitly wrap in a lambda with args *)letarg_fields=CCList.map(fun{pld_name;_}->({txt=Lidentpld_name.txt;loc},Ast_builder.Default.pvar~locpld_name.txt))label_decsinletargs=Ast_builder.Default.ppat_record~locarg_fieldsClosedinletexpr=expr_of_record~loclabel_decsin[%exprfun[%pargs]->[%eexpr]]|Ptype_open,_->Location.raise_errorf~loc"Cannot construct encoder for %s: cannot encode extensible type"type_decl.ptype_name.txtinimple_exprletsingle_type_encoder_gen~(loc:location)type_decl=letimple=implementation_generator~loctype_declinletname=to_encoder_nametype_decl.ptype_name.txtinletpat=Ast_builder.Default.pvar~locnameinletparams=(* TODO: can we drop the non type vars? What are these? *)CCList.filter_map(fun(param,_)->matchparam.ptyp_descwithPtyp_varvar->Somevar|_->None)type_decl.ptype_paramsinletargs=CCList.rev@@CCList.map(funparam->Ast_builder.Default.pvar~loc(to_encoder_nameparam))paramsinletimple=(* We need the type variables to become arguments *)CCList.fold_left(funimplarg->[%exprfun[%parg]->[%eimpl]])impleargsinAst_builder.Default.value_binding~loc~pat~expr:imple(* [%str let [%p Ast_builder.Default.pvar ~loc name] = [%e imple]] *)letstr_gens~(loc:location)~(path:label)((rec_flag:rec_flag),type_decls):structure_itemlist=let_path=pathinletrec_flag=really_recursiverec_flagtype_declsin(* CCList.flat_map (single_type_encoder_gen ~loc ~rec_flag) type_decls *)match(really_recursiverec_flagtype_decls,type_decls)with|Nonrecursive,_->[(Ast_builder.Default.pstr_value~locNonrecursive@@List.(map(single_type_encoder_gen~loc)type_decls));]|Recursive,type_decls->[(Ast_builder.Default.pstr_value~locRecursive@@List.(map(single_type_encoder_gen~loc)type_decls));]