123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888openPpxlibopenAst_builder.Defaultopen!PrintfopenBasetypet={driver:longident;field_key:(label_declaration,string)Attribute.t;constr_key:(constructor_declaration,string)Attribute.t;constr_name:(constructor_declaration,string)Attribute.t;variant_key:(row_field,string)Attribute.t;variant_name:(row_field,string)Attribute.t;field_default:(label_declaration,expression)Attribute.t;}let(^^)=Caml.(^^)letraise_errorf?locfmt=Location.raise_errorf?loc("ppx_protocol_conv: "^^fmt)letdebug=falseletdebugfmt=matchdebugwith|true->eprintf(fmt^^"\n%!")|false->ifprintfStdio.stderrfmt[@@warning"-32"]letstring_of_ident_loc{loc;txt}=letrecinner=function|Lidents->s|Ldot(i,s)->inneri^"."^s|Lapply_->raise_errorf~loc"lapply???"in{loc;txt=innertxt}letpexp_ident_string_loc{loc;txt}=pexp_ident~loc{loc;txt=Lidenttxt}letdriver_funct~locname=pexp_ident~loc{loc;txt=Ldot(t.driver,name)}(** Concatinate the list of expressions into a single expression using
list concatenation *)letlist_expr~locl=List.fold_right~init:[%expr[]]~f:(funhdtl->[%expr[%ehd]::[%etl]])lletslist_expr~locl=List.fold_right~init:[%exprNil]~f:(funhdtl->[%exprCons([%ehd],[%etl])])lletident_of_module~loc=function|Some{pmod_desc=Pmod_ident{txt;_};_}->txt|Some_->raise_errorf~loc"must be a module identifier"|None->raise_errorf~loc"~driver argument missing"(** Test if a type is considered primitive *)letis_primitive_type=function|"string"|"bytes"|"int"|"int32"|"int64"|"nativeint"|"float"|"bool"|"char"|"unit"->true|_->false(** Test if the type is a type modifier *)letis_meta_type=function|"option"|"array"|"list"|"lazy_t"|"ref"|"result"->true|_->falseletmodule_name?loc=function|Lidents->String.uncapitalizes|Ldot(_,s)->String.uncapitalizes|Lapply_->raise_errorf?loc"lapply???"letprotocol_identdirdriver{loc;txt}=letprefix,suffix=matchdirwith|`Serialze->"to",""|`Deserialize->"of","_exn"in(* Match the name of the type *)letdriver_name=module_name~locdriverinlettxt=matchtxtwith|Lident"t"->Lident(sprintf"%s_%s%s"prefixdriver_namesuffix)|Lidentname->Lident(sprintf"%s_%s_%s%s"nameprefixdriver_namesuffix)|Ldot(l,"t")->Ldot(l,sprintf"%s_%s%s"prefixdriver_namesuffix)|Ldot(l,name)->Ldot(l,sprintf"%s_%s_%s%s"nameprefixdriver_namesuffix)|Lapply_->raise_errorf~loc"lapply???"inpexp_ident~loc{loc;txt}(** Test that all label names are distict after mapping
This function will raise an error is a conflict is found
*)letlocation_of_attribtname(attribs:attributes)=letprefix=module_namet.driverinlethas_names=String.equalsname||String.equals(sprintf"%s.%s"prefixname)inList.find_map_exn~f:(function{attr_name={txt;_};attr_payload=Parsetree.PStr[{pstr_loc;_}];_}whenhas_nametxt->Somepstr_loc|_->None)attribsletrow_locrow=row.prf_locletget_variant_nametrow=matchAttribute.gett.variant_namerow,Attribute.gett.variant_keyrowwith|Somename,None->Somename|None,Somename->Somename|Some_,Some_->raise_errorf~loc:(row_locrow)"Both 'key' and 'name' attributes supplied. Use of @@key is deprecated - use @@name instead"|None,None->Noneletget_constr_nametconstr=matchAttribute.gett.constr_nameconstr,Attribute.gett.constr_keyconstrwith|Somename,None->Somename|None,Somename->Somename|Some_,Some_->raise_errorf~loc:(constr.pcd_loc)"Both 'key' and 'name' attributes supplied. Use of @@key is deprecated - use @@name instead"|None,None->None(** Test that all constructor names are distict after mapping
This function will raise an error is a conflict is found
*)lettest_constructor_mappingtconstrs=letbase,mapped=List.partition_map~f:(funconstr->matchget_constr_nametconstrwith|SomenamewhenString.equalconstr.pcd_name.txtname->`Fstname|Somename->`Snd(name,constr.pcd_attributes)|None->`Fstconstr.pcd_name.txt)constrsinlet_:stringlist=List.fold_left~init:base~f:(funacc->function|(name,attrs)whenList.mem~equal:String.equalaccname->letloc=location_of_attribt"key"attrsin(* Should use the name of the attribute *)raise_errorf~loc"Mapped constructor name already in use: %s"name|(name,_)->name::acc)mappedin()lettest_row_mappingtrows=letbase,mapped=List.partition_map~f:(funrow->let(row_name,attrs)=matchrow.prf_descwith|Rinherit_->raise_errorf"Inherited polymorphic variant types not supported"|Rtag(name,_,_)->name,row.prf_attributesinmatchget_variant_nametrowwith|SomenamewhenString.equalrow_name.txtname->`Fstname|Somename->`Snd(name,attrs)|None->`Fstrow_name.txt)rowsinlet_:stringlist=List.fold_left~init:base~f:(funacc->function|(name,attrs)whenList.mem~equal:String.equalaccname->letloc=location_of_attribt"key"attrsin(* Should use the name of the attribute *)raise_errorf~loc"Mapped constructor name already in use: %s"name|(name,_)->name::acc)mappedin()lettest_label_mappingtlabels=letbase,mapped=List.partition_map~f:(funlabel->matchAttribute.gett.field_keylabelwith|SomenamewhenString.equallabel.pld_name.txtname->`Fstname|Somename->`Snd(name,label.pld_attributes)|None->`Fstlabel.pld_name.txt)labelsinlet_:stringlist=List.fold_left~init:base~f:(funacc->function|(name,attribs)whenList.mem~equal:String.equalaccname->letloc=location_of_attribt"key"attribsin(* Should use the name of the attribute *)raise_errorf~loc"Mapped label name in use: %s"name|(name,_)->name::acc)mappedin()(** @returns function, pattern and arguments *)letrecserialize_recordt~loclabels=test_label_mappingtlabels;letspec=List.map~f:(funlabel->letname=matchAttribute.gett.field_keylabelwith|None->estring~loc:label.pld_loclabel.pld_name.txt|Somename->estring~loc:label.pld_locnameinletof_t=serialize_expr_of_type_descrt~loclabel.pld_type.ptyp_descinletdefault=matchAttribute.gett.field_defaultlabelwith|None->[%exprNone]|Someexpr->[%exprSome[%eexpr]]in[%expr([%ename],[%eof_t],[%edefault])])labels|>slist_expr~loc|>funspec->[%exprProtocol_conv.Runtime.Record_out.([%espec])]inspec,ppat_record~loc(List.map~f:(funid->{loc;txt=Lidentid.txt},ppat_var~loc{loc;txt="r_"^id.txt})(List.map~f:(funld->ld.pld_name)labels))Closed,List.map~f:(funlabel->Nolabel,pexp_ident~loc{loc;txt=Lident("r_"^label.pld_name.txt)})labelsandserialize_tuplet~loccore_types=letspec=List.mapcore_types~f:(funct->serialize_expr_of_type_descrt~locct.ptyp_desc)|>slist_expr~locinletids=List.mapicore_types~f:(funi_->sprintf"t%d"i)inletargs=List.mapids~f:(funid->Nolabel,pexp_ident~loc{loc;txt=Lidentid})inletpatt=List.mapids~f:(funid->ppat_var~loc{loc;txt=id})|>ppat_tuple~locin[%exprlet_of_tuple=[%edriver_funct~loc"of_tuple"]Protocol_conv.Runtime.Tuple_out.([%espec])infun[%ppatt]->[%epexp_apply~loc[%expr_of_tuple]args]]andserialize_variantt~loctype_~name~aliaspcstr=letppat_constr~locnamepattern=matchtype_with|`Variant->ppat_variant~locnamepattern|`Construct->ppat_construct~loc{loc;txt=Lidentname}patterninletmk_patterncore_types=List.mapi~f:(funi(core_type:core_type)->ppat_var~loc:core_type.ptyp_loc{loc=core_type.ptyp_loc;txt=sprintf"c%d"i})core_types|>ppat_tuple_opt~locinmatchpcstrwith|Pcstr_recordlabels->letspec,patt,args=serialize_recordt~loclabelsinletpatt_tuple=List.map~f:(funlabel->ppat_var~loc{loc;txt="r_"^label.pld_name.txt})labels|>ppat_tuple~locinletf=[%exprletf=[%edriver_funct~loc"of_record"][%espec]inletf[%ppatt_tuple]=[%epexp_apply~loc(pexp_ident~loc{loc;txt=Lident"f"})args]inletspec=letopenProtocol_conv.Runtime.Tuple_outin(Cons(f,Nil))in[%edriver_funct~loc"of_variant"][%eestring~localias]spec]inletf_name={loc;txt=sprintf"_%s_of_record_"name}inletlhs=ppat_constr~locname(Somepatt)inletrhs=pexp_apply~loc(pexp_ident_string_locf_name)[Nolabel,pexp_tuple~loc(List.map~f:sndargs)]inletbinding=value_binding~loc~pat:{ppat_desc=Ppat_varf_name;ppat_loc=loc;ppat_attributes=[];ppat_loc_stack=[]}~expr:finbinding,case~lhs~guard:None~rhs|Pcstr_tuplecore_types->letf_name={loc;txt=sprintf"_%s_of_tuple"name}inletf=letspec=List.mapcore_types~f:(funct->serialize_expr_of_type_descrt~locct.ptyp_desc)|>slist_expr~locin[%expr[%edriver_funct~loc"of_variant"][%eestring~localias]Protocol_conv.Runtime.Tuple_out.([%espec])]inletbinding=value_binding~loc~pat:{ppat_desc=Ppat_varf_name;ppat_loc=loc;ppat_attributes=[];ppat_loc_stack=[]}~expr:finletlhs=ppat_constr~locname(mk_patterncore_types)inletargs=List.mapi~f:(funi_->pexp_ident~loc{loc;txt=Lident(sprintf"c%d"i)})core_typesinletrhs=pexp_apply~loc(pexp_ident_string_locf_name)(List.map~f:(funa->(Nolabel,a))args)inbinding,case~lhs~guard:None~rhsandserialize_expr_of_tdeclt~loctdecl=matchtdecl.ptype_kindwith|Ptype_abstract->beginmatchtdecl.ptype_manifestwith|Somecore_type->serialize_expr_of_type_descrt~loccore_type.ptyp_desc|None->raise_errorf~loc"Opaque types are not supported."end|Ptype_variantconstrs->test_constructor_mappingtconstrs;letbindings,cases=List.map~f:(fun({pcd_name;pcd_args=pcstr;pcd_loc=loc;_}asconstr)->letalias=matchget_constr_nametconstrwith|Somekey->key|None->pcd_name.txtinserialize_variantt~loc`Construct~name:pcd_name.txt~aliaspcstr)constrs|>List.unzipinpexp_let~locNonrecursivebindings@@pexp_function~loccases|Ptype_recordlabels->letspec,patt,args=serialize_recordt~loclabelsin[%exprlet_of_record=[%e(driver_funct~loc"of_record")][%espec]infun[%ppatt]->[%epexp_apply~loc[%expr_of_record]args]]|Ptype_open->raise_errorf~loc"Extensible variant types not supported"(** Serialization expression for a given type *)andserialize_expr_of_type_descrt~loc=function|Ptyp_constr({txt=Lidentident;loc},cts)whenis_meta_typeident->letargs=List.map~f:(funct->serialize_expr_of_type_descrt~locct.ptyp_desc)cts|>List.map~f:(funto_p->(Nolabel,to_p))inpexp_apply~loc(driver_func~loct("of_"^ident))args|Ptyp_constr({txt=Lidents;loc},_)whenis_primitive_types->driver_funct~loc("of_"^s)|Ptyp_constr(ident,cts)->(* Call recursivly to for app type parameters *)letargs=List.map~f:(fun{ptyp_desc;ptyp_loc;_}->serialize_expr_of_type_descrt~loc:ptyp_locptyp_desc)cts|>List.map~f:(funexpr->(Nolabel,expr))inletfunc=protocol_ident`Serialzet.driveridentinpexp_apply~locfuncargs|Ptyp_tuplecore_types->serialize_tuple~loctcore_types|Ptyp_poly_->raise_errorf~loc"Polymorphic variants not supported"|Ptyp_variant(rows,_closed,None)->test_row_mappingtrows;letbindings,cases=List.map~f:(funrow->matchrow.prf_descwith|Rinherit_->raise_errorf~loc"Inherited types not supported"|Rtag(name,_bool,core_types)->letalias=matchget_variant_nametrowwith|Somekey->key|None->name.txtinserialize_variantt~loc`Variant~name:name.txt~alias(Pcstr_tuplecore_types))rows|>List.unzipinpexp_let~locNonrecursivebindings@@pexp_function~loccases|Ptyp_varcore_type->pexp_ident~loc{loc;txt=Lident(sprintf"__param_to_%s"core_type)}|Ptyp_arrow_->raise_errorf~loc"Functions not supported"|Ptyp_variant_|Ptyp_any|Ptyp_object_|Ptyp_class_|Ptyp_alias_|Ptyp_package_|Ptyp_extension_->raise_errorf~loc"Unsupported type descr"(*=========== Deserialization functions ==============*)(* Create a tuple deserialization function *)letrecdeserialize_recordt~loc?map_resultlabels=test_label_mappingtlabels;letfield_ids=List.map~f:(funld->ld.pld_name)labelsinletconstructor=letrecord=pexp_record~loc(List.map~f:(funid->letid={loc;txt=Lidentid.txt}inid,pexp_ident~locid)field_ids)None|>funr->Option.value_map~default:r~f:(funf->fr)map_resultinList.fold_right~init:record~f:(funfieldexpr->pexp_fun~locNolabelNone(ppat_var~locfield)expr)field_idsinletspec=List.map~f:(funlabel->letfield_name=matchAttribute.gett.field_keylabelwith|None->estring~loc:label.pld_loclabel.pld_name.txt|Somename->estring~loc:label.pld_locnameinletfunc=deserialize_expr_of_type_descrt~loclabel.pld_type.ptyp_descinletdefault=matchAttribute.gett.field_defaultlabelwith|None->[%exprNone]|Someexpr->[%exprSome[%eexpr]]in[%expr([%efield_name],[%efunc],[%edefault])])labels|>slist_expr~locin([%exprProtocol_conv.Runtime.Record_in.([%espec])],constructor)anddeserialize_tuplet~loc~constrcts=letconstructor=letids=List.mapi~f:(funi_->{loc;txt=Lident(sprintf"x%d"i)})ctsinlettuple=pexp_tuple~loc(List.map~f:(pexp_ident~loc)ids)|>constrinList.fold_right~init:tuple~f:(funidexpr->pexp_fun~locNolabelNone(ppat_var~loc(string_of_ident_locid))expr)idsinletslist=List.map~f:(funct->deserialize_expr_of_type_descrt~locct.ptyp_desc)ctsin(slist_expr~locslist,constructor)anddeserialize_variantt~loctype_~namepcstrs=letpexp_constr~locname=matchtype_with|`Construct->pexp_construct~loc{loc;txt=Lidentname}|`Variant->pexp_variant~locnameinmatchpcstrswith|Pcstr_recordlabels->letmap_resultx=pexp_constr~locname(Somex)inletspec,constr=deserialize_recordt~loc~map_resultlabelsin(* Why not just create the function right there!!!! *)letf=pexp_apply~loc(driver_funct~loc"to_record")[Nolabel,spec;Nolabel,constr]inletspec=[%exprProtocol_conv.Runtime.Tuple_in.(Cons([%ef],Nil))]inletconstr=[%exprfunx->x]inspec,constr|Pcstr_tuple[]->[%exprProtocol_conv.Runtime.Tuple_in.Nil],pexp_constr~locnameNone|Pcstr_tuplecore_types->letspec=List.map~f:(funct->deserialize_expr_of_type_descrt~locct.ptyp_desc)core_types|>slist_expr~loc|>fune->[%exprProtocol_conv.Runtime.Tuple_in.([%ee])]inletconstructor=letarg_names=List.mapi~f:(funi_->{loc;txt=Lident(sprintf"v%d"i)})core_typesinletbody=pexp_tuple~loc(List.map~f:(pexp_ident~loc)arg_names)|>Option.some|>pexp_constr~locnameinList.fold_right~init:body~f:(funidexpr->pexp_fun~locNolabelNone(ppat_var~loc(string_of_ident_locid))expr)arg_namesinspec,constructoranddeserialize_expr_of_tdeclt~loctdecl=matchtdecl.ptype_kindwith|Ptype_abstract->beginmatchtdecl.ptype_manifestwith|Somecore_type->deserialize_expr_of_type_descrt~loccore_type.ptyp_desc|None->raise_errorf~loc"Manifest is none"end|Ptype_variantconstrs->test_constructor_mappingtconstrs;letmk_elem({pcd_name;pcd_args;pcd_loc=loc;_}asconstr)=letser_name=matchget_constr_nametconstrwith|Somekey->key|None->pcd_name.txtinletspec,constr=deserialize_variantt~loc`Construct~name:pcd_name.txtpcd_argsin(ser_name,spec,constr)inletspec=List.map~f:mk_elemconstrs|>List.map~f:(fun(name,spec,constr)->[%exprProtocol_conv.Runtime.Variant_in.Variant([%eestring~locname],[%espec],[%econstr])])|>list_expr~locinpexp_apply~loc(driver_funct~loc"to_variant")[Nolabel,spec]|Ptype_recordlabels->letspec,constructor=deserialize_recordt~loclabelsinpexp_apply~loc(driver_funct~loc"to_record")[Nolabel,spec;Nolabel,constructor]|Ptype_open->raise_errorf~loc"Extensible variant types not supported"(** Deserialization expression for a given type *)anddeserialize_expr_of_type_descrt~loc=function|Ptyp_constr({txt=Lidentident;loc},cts)whenis_meta_typeident->letargs=List.map~f:(funct->deserialize_expr_of_type_descrt~locct.ptyp_desc)cts|>List.map~f:(funto_t->(Nolabel,to_t))inpexp_apply~loc(driver_func~loct("to_"^ident))args|Ptyp_constr({txt=Lidents;loc},_)whenis_primitive_types->driver_funct~loc("to_"^s)|Ptyp_constr(ident,cts)->(* Construct all arguments to of ... *)letargs=List.map~f:(fun{ptyp_desc;ptyp_loc;_}->deserialize_expr_of_type_descrt~loc:ptyp_locptyp_desc)cts|>List.map~f:(funexpr->(Nolabel,expr))inletfunc=protocol_ident`Deserializet.driveridentinpexp_apply~locfuncargs|Ptyp_tuplects->let(spec,constructor)=deserialize_tuplet~constr:Fn.id~locctsin[%exprletspec=Protocol_conv.Runtime.Tuple_in.([%espec])inletconstructor=[%econstructor]in[%edriver_funct~loc"to_tuple"]specconstructor]|Ptyp_poly_->raise_errorf~loc"Polymorphic variants not supported"|Ptyp_variant(_rows,_closed,Some_)->raise_errorf~loc"Variant with some"|Ptyp_variant(rows,_closed,None)->test_row_mappingtrows;letmk_elemrow=matchrow.prf_descwith|Rinherit_->raise_errorf~loc"Inherited variant types not supported"|Rtag(name,_bool,core_types)->letser_name=matchget_variant_nametrowwith|Somekey->key|None->name.txtinletspec,constr=deserialize_variantt~loc`Variant~name:name.txt(Pcstr_tuplecore_types)in(ser_name,spec,constr)inletspec=List.map~f:mk_elemrows|>List.map~f:(fun(name,spec,constr)->[%exprProtocol_conv.Runtime.Variant_in.Variant([%eestring~locname],[%espec],[%econstr])])|>list_expr~locinpexp_apply~loc(driver_funct~loc"to_variant")[Nolabel,spec]|Ptyp_varname->pexp_ident~loc{loc;txt=Lident(sprintf"__param_of_%s"name)}|Ptyp_arrow_->raise_errorf~loc"Functions not supported"|Ptyp_any|Ptyp_object_|Ptyp_class_|Ptyp_alias_|Ptyp_package_|Ptyp_extension_->raise_errorf~loc"Unsupported type descr"letserialize_function_name~loc~drivername=letprefix=matchname.txtwith|"t"->""|name->name^"_"insprintf"%sto_%s"prefix(module_name~locdriver)|>Located.mk~locletdeserialize_function_name?(as_result=false)~loc~drivername=letprefix=matchname.txtwith|"t"->""|name->name^"_"inletsuffix=matchas_resultwith|true->""|false->"_exn"insprintf"%sof_%s%s"prefix(module_name~locdriver)suffix|>Located.mk~loclet_deserialize_function_name_result~loc~drivername=letprefix=matchname.txtwith|"t"->""|name->name^"_"insprintf"%sof_%s"prefix(module_name~locdriver)|>Located.mk~locletpstr_value_of_funcs~locrec_flagelements=List.map~f:(fun(name,signature,expr)->letpat=letp=(ppat_var~locname)inOption.value_map~default:p~f:(ppat_constraint~locp)signatureinAst_helper.Vb.mk~locpatexpr)elements|>pstr_value~locrec_flagletname_of_core_type~prefix=function|{ptyp_desc=Ptyp_varvar;ptyp_loc;_}->{loc=ptyp_loc;txt=sprintf"__param_%s_%s"prefixvar}|{ptyp_desc=Ptyp_any;ptyp_loc;_}->raise_errorf~loc:ptyp_loc"Generalized algebraic datatypes not supported"|{ptyp_desc=Ptyp_arrow(_,_,_);_}->failwith"Ptyp_arrow "|{ptyp_desc=Ptyp_tuple_;_}->failwith"Ptyp_tuple "|{ptyp_desc=Ptyp_constr(_,_);_}->failwith"Ptyp_constr "|{ptyp_desc=Ptyp_object(_,_);_}->failwith"Ptyp_object "|{ptyp_desc=Ptyp_class(_,_);_}->failwith"Ptyp_class "|{ptyp_desc=Ptyp_alias(_,_);_}->failwith"Ptyp_alias "|{ptyp_desc=Ptyp_variant(_,_,_);_}->failwith"Ptyp_variant "|{ptyp_desc=Ptyp_poly(_,_);_}->failwith"Ptyp_poly "|{ptyp_desc=Ptyp_package_;_}->failwith"Ptyp_package "|{ptyp_desc=Ptyp_extension_;_}->failwith"Ptyp_extension "letrecis_recursive_cttypes=function|{ptyp_desc=Ptyp_varvar;_}->List.memtypesvar~equal:String.equal|{ptyp_desc=Ptyp_any;_}->false|{ptyp_desc=Ptyp_arrow_;_}->false|{ptyp_desc=Ptyp_tuplects;_}->List.exists~f:(is_recursive_cttypes)cts|{ptyp_desc=Ptyp_constr(l,cts);_}->List.memtypes(string_of_ident_locl).txt~equal:String.equal||List.exists~f:(is_recursive_cttypes)cts|{ptyp_desc=Ptyp_object_;_}->false|{ptyp_desc=Ptyp_class_;_}->false|{ptyp_desc=Ptyp_alias(c,_);_}->is_recursive_cttypesc|{ptyp_desc=Ptyp_variant(rows,_,_);_}->List.exists~f:(funrow->matchrow.prf_descwith|Rtag(_,_,cts)->List.exists~f:(is_recursive_cttypes)cts|Rinherit_->false)rows|{ptyp_desc=Ptyp_poly(_,ct);_}->is_recursive_cttypesct|{ptyp_desc=Ptyp_package_;_}->false|{ptyp_desc=Ptyp_extension_;_}->falseletis_recursivetypes=function|Ptype_abstract->false|Ptype_variant(cstr_decls)->List.exists~f:(function|{pcd_args=Pcstr_tuplects;_}->List.exists~f:(is_recursive_cttypes)cts|{pcd_args=Pcstr_recordldecls;_}->List.exists~f:(fun{pld_type=ct;_}->is_recursive_cttypesct)ldecls)cstr_decls|Ptype_recordldecls->List.exists~f:(fun{pld_type=ct;_}->is_recursive_cttypesct)ldecls|Ptype_open->false(** Test if a type references itself, in which case we cannot do eager evaluation,
and we create a reference cell to hold the evaluated function value for later.
In this case, we only modify the reference cell once, at which point the closure will be moved to the heap,
but thats ok, because all the closures will end up there anyways.
Return a function which will determin which of the tydecls references other types in the list of tydecls,
so only functions which needs this 'recursion optimization hack' will be wrapped.
*)letis_recursivetydecls=function|Nonrecursive->fun_->false|Recursive->letnames=List.map~f:(fun{ptype_name={txt=name;_};_}->name)tydeclsinfun{ptype_kind;ptype_params=ctvl;ptype_manifest;_}->is_recursivenamesptype_kind||List.exists~f:(fun(ct,_var)->is_recursive_ctnamesct)ctvl||Option.value_mapptype_manifest~default:false~f:(is_recursive_ctnames)(* Add type parameters *)letmk_typ~loctydecl=letparams=List.filter_map~f:(function({ptyp_desc=Ptyp_vars;_},_variance)->Somes|_->None)tydecl.ptype_params|>List.map~f:(funparam->ptyp_var~locparam)inptyp_constr~loc{loc;txt=Lidenttydecl.ptype_name.txt}paramslettype_of_to_func~locdrivertydecl=[%type:[%tmk_typ~loctydecl]->[%tptyp_constr~loc{loc;txt=Ldot(driver,"t")}[]]]lettype_of_of_func~locdriver~as_resulttydecl=lettyp=mk_typ~loctydeclinletresult_type=matchas_resultwith|false->typ|true->leterror_typ=ptyp_constr~loc{loc;txt=Ldot(driver,"error")}[]inptyp_constr~loc{loc;txt=Ldot(Ldot(Lident"Protocol_conv","Runtime"),"result")}[typ;error_typ]in[%type:[%tptyp_constr~loc{loc;txt=Ldot(driver,"t")}[]]->[%tresult_type]]letserialization_signature~loc~as_sigdrivertdecl=lettype_of=type_of_to_func~locdrivertdeclinletparams=List.filter_map~f:(function({ptyp_desc=Ptyp_vars;_},_variance)->Somes|_->None)tdecl.ptype_paramsinletsignature=List.fold_rightparams~init:type_of~f:(funnameacc->lettyp=ptyp_arrow~locNolabel(ptyp_var~locname)(ptyp_constr~loc{loc;txt=Ldot(driver,"t")}[])inptyp_arrow~locNolabeltypacc)inmatchas_sigwith|true->signature|false->ptyp_poly~loc(List.map~f:(funtxt->{loc;txt})params)signatureletdeserialization_signature~loc~as_sig~as_resultdrivertdecl=lettype_of=type_of_of_func~loc~as_resultdrivertdeclinletparams=List.filter_map~f:(function({ptyp_desc=Ptyp_vars;_},_variance)->Somes|_->None)tdecl.ptype_paramsinletsignature=List.fold_rightparams~init:type_of~f:(funnameacc->lettyp=ptyp_arrow~locNolabel(ptyp_constr~loc{loc;txt=Ldot(driver,"t")}[])(ptyp_var~locname)inptyp_arrow~locNolabeltypacc)inmatchas_sigwith|true->signature|false->ptyp_poly~loc(List.map~f:(funtxt->{loc;txt})params)signature(** Cache intermediate result. Unfortunatly we are not allowed to create a spec, so we cache in an option reference. *)letmake_recursive~loc(e:expression)=function|false->e|true->[%expr(letf=refNonein(funt->match!fwith|None->letf'=[%ee]inf:=Somef';f't|Somef->ft))]letto_protocol_str_type_declstrec_flag~loctydecls=let(defs,is_recursive)=letis_recursive_f=is_recursivetydeclsrec_flaginList.fold_right~init:([],false)~f:(funtdecl(acc,acc_recursive)->letis_recursive=is_recursive_ftdeclinletto_p=serialize_function_name~loc~driver:t.drivertdecl.ptype_nameinletexpr=make_recursive~loc(serialize_expr_of_tdeclt~loctdecl)is_recursiveinletexpr_param=List.fold_right~init:expr~f:(fun(ct,_variance)expr->letpatt=Ast_helper.Pat.var~loc(name_of_core_type~prefix:"to"ct)in[%exprfun[%ppatt]->[%eexpr]])tdecl.ptype_paramsinletsignature=serialization_signature~as_sig:falset.driver~loctdeclin(to_p,Somesignature,expr_param)::acc,(is_recursive||acc_recursive))tydeclsinpstr_value_of_funcs~loc(ifis_recursivethenRecursiveelseNonrecursive)defs|>funx->[x]letof_protocol_str_type_declstrec_flag~loctydecls=letexpr_param~loctdeclexpr=List.fold_right~init:expr~f:(fun(ct,_variance)expr->letpatt=Ast_helper.Pat.var~loc(name_of_core_type~prefix:"of"ct)in[%exprfun[%ppatt]->[%eexpr]])tdecl.ptype_paramsinletresult_expr~loctdeclof_p=letexpr=lettype_params=List.map~f:(fun(ct,_variance)->pexp_ident~loc{loc;txt=Lident(name_of_core_type~prefix:"of"ct).txt})tdecl.ptype_paramsin(*
let args =
type_params @ [ pexp_ident ~loc { loc; txt = Lident "t"} ]
|> List.map ~f:(fun e -> (Nolabel, e))
in
pexp_apply ~loc (pexp_ident ~loc { loc; txt = Lident of_p.txt}) args
*)letargs=type_params|>List.map~f:(fune->(Nolabel,e))inpexp_apply~loc(pexp_ident~loc{loc;txt=Lidentof_p.txt})argsinpexp_apply~loc(driver_funct~loc"try_with")[Nolabel,expr]inlet(defs,err_defs,is_recursive)=letis_recursive_f=is_recursivetydeclsrec_flaginList.fold_right~init:([],[],false)~f:(funtdecl(defs,err_defs,acc_recursive)->letis_recursive=is_recursive_ftdeclinletof_p=deserialize_function_name~loc~driver:t.drivertdecl.ptype_nameinletexpr=make_recursive~loc(deserialize_expr_of_tdeclt~loctdecl)is_recursiveinletsignature=deserialization_signature~as_sig:false~as_result:falset.driver~loctdeclinletof_p_result=deserialize_function_name~as_result:true~loc~driver:t.drivertdecl.ptype_nameinletresult_expr=result_expr~loctdeclof_pinletresult_sig=deserialization_signature~as_sig:false~as_result:truet.driver~loctdeclin(of_p,Somesignature,expr_param~loctdeclexpr)::defs,(of_p_result,Someresult_sig,expr_param~loctdeclresult_expr)::err_defs,(is_recursive||acc_recursive))tydeclsin[pstr_value_of_funcs~loc(ifis_recursivethenRecursiveelseNonrecursive)defs;pstr_value_of_funcs~locNonrecursiveerr_defs;]letprotocol_str_type_declstrec_flag~loctydecls=to_protocol_str_type_declstrec_flag~loctydecls@of_protocol_str_type_declstrec_flag~loctydeclsletto_protocol_sig_type_decls~loc~path:_(_rec_flag,tydecls)(driver:module_exproption)=letdriver=ident_of_module~locdriverinList.concat_map~f:(funtydecl->letsignature=serialization_signature~as_sig:true~locdrivertydeclinletto_p=serialize_function_name~loc~drivertydecl.ptype_nameinpsig_value~loc(value_description~loc~name:to_p~type_:signature~prim:[])::[])tydeclsletof_protocol_sig_type_decls~loc~path:_(_rec_flag,tydecls)(driver:module_exproption)=letdriver=ident_of_module~locdriverinList.concat_map~f:(funtydecl->letof_p=deserialize_function_name~loc~drivertydecl.ptype_nameinletsignature=deserialization_signature~as_sig:true~as_result:false~locdrivertydeclinletof_p_result=deserialize_function_name~as_result:true~loc~drivertydecl.ptype_nameinletresult_sig=deserialization_signature~as_sig:true~as_result:truedriver~loctydeclin[psig_value~loc(value_description~loc~name:of_p~type_:signature~prim:[]);psig_value~loc(value_description~loc~name:of_p_result~type_:result_sig~prim:[]);])tydeclsletprotocol_sig_type_decls~loc~path(rec_flag,tydecls)(driver:module_exproption)=to_protocol_sig_type_decls~loc~path(rec_flag,tydecls)driver@of_protocol_sig_type_decls~loc~path(rec_flag,tydecls)driverletmk_str_type_decl=(* Cache to avoid creating the same attributes twice. *)letattrib_table=Hashtbl.Poly.create()infunf~loc~path:_(recflag,tydecls)driver->(* Create T and pass on to f *)letdriver=ident_of_module~locdriverinletattrib_namename=sprintf"%s.%s"(module_namedriver)nameinletfield_key,constr_key,constr_name,variant_key,variant_name,field_default=letcreate()=letopenAttributeindeclare(attrib_name"key")Context.label_declarationAst_pattern.(single_expr_payload(estring__))(funx->x),declare(attrib_name"key")(* Deprecated *)Context.constructor_declarationAst_pattern.(single_expr_payload(estring__))(funx->x),declare(attrib_name"name")Context.constructor_declarationAst_pattern.(single_expr_payload(estring__))(funx->x),declare(attrib_name"key")(* Deprecated *)Context.rtagAst_pattern.(single_expr_payload(estring__))(funx->x),declare(attrib_name"name")Context.rtagAst_pattern.(single_expr_payload(estring__))(funx->x),declare(attrib_name"default")Context.label_declarationAst_pattern.(single_expr_payload(__))(funx->x)inHashtbl.find_or_addattrib_tabledriver~default:createinlett={driver;field_key;constr_key;constr_name;variant_key;variant_name;field_default;}inftrecflag~loctydeclslet()=letdriver=Ppxlib.Deriving.Args.(arg"driver"(pexp_pack__))inDeriving.add"protocol"~str_type_decl:(Deriving.Generator.makeDeriving.Args.(empty+>driver)(mk_str_type_declprotocol_str_type_decls))~sig_type_decl:(Deriving.Generator.makeDeriving.Args.(empty+>driver)protocol_sig_type_decls)|>Ppxlib.Deriving.ignore;Deriving.add"of_protocol"~str_type_decl:(Deriving.Generator.makeDeriving.Args.(empty+>driver)(mk_str_type_declof_protocol_str_type_decls))~sig_type_decl:(Deriving.Generator.makeDeriving.Args.(empty+>driver)of_protocol_sig_type_decls)|>Deriving.ignore;Deriving.add"to_protocol"~str_type_decl:(Deriving.Generator.makeDeriving.Args.(empty+>driver)(mk_str_type_declto_protocol_str_type_decls))~sig_type_decl:(Deriving.Generator.makeDeriving.Args.(empty+>driver)to_protocol_sig_type_decls)|>Deriving.ignore;