123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506openBaseopenPpxlibopenAst_builder.DefaultmoduleInvariant=structletset_to_stringset=set|>Set.to_list|>List.map~f:(funfield_name->Printf.sprintf"`%s'"field_name)|>String.concat~sep:", ";;letall_disjoints~loc~add~remove~modify~set=letcheck(n1,s1)(n2,s2)=letcommon=Set.inters1s2inifnot(Set.is_emptycommon)thenLocation.raise_errorf~loc"Sets `%s' and `%s' must be disjoint but they are not: %s found in both"n1n2(set_to_stringcommon)inleta="add",addinletb="remove",removeinletc="modify",modifyinletd="set",setincheckab;checkac;checkad;checkbc;checkbd;checkcd;;letthings_are_known~loc~all~thing_name~supposed_to_beto_remove=letunknown_fields=Set.diffto_removeallinifnot(Set.is_emptyunknown_fields)then(letstr=set_to_stringunknown_fieldsinLocation.raise_errorf~loc"Some %s were supposed to be %s but they were not found: %s"thing_namesupposed_to_bestr);;end(* {1 Some helper functions} *)letname_of_type_name~dir~source~type_name=matchtype_name.ptyp_descwith|Ptyp_constr({txt=type_name;_},_)->letfun_name=Printf.sprintf"%s_%s"(matchdirwith|`To->"to"|`Of->"of")(String.concat~sep:"_"(Longident.flatten_exntype_name))in(matchsourcewith|"t"->fun_name|_->source^"_"^fun_name)|_->assertfalse;;letstable_variant_name~type_name=matchtype_namewith|"t"->"Stable_variant"|_->"Stable_variant_of_"^type_name;;letstable_variants~type_=matchtype_.ptyp_descwith|Ptyp_constr({txt=type_;_},_)->lettype_name=List.hd_exn(List.rev(Longident.flatten_exntype_))instable_variant_name~type_name|_->assertfalse;;letmodify_field_namename="modify_"^nameletremove_field_namename="remove_"^nameletmk_lident~locstr=Located.mk~loc(Longident.Lidentstr)letmk_module~loc~name~items=pstr_module~loc(module_binding~loc~name:(Located.mk~loc(Somename))~expr:(pmod_structure~locitems));;(* fun ~name:name -> exp *)letmk_pexp_fun~loc~nameexp=pexp_fun~loc(Labelledname)None(pvar~locname)expletconvert_record~loc~source_fields~target_fields~modified_fields~set_fields~source_type~target_type=letrecord_pat=letrecord_pat=List.map(Set.to_listsource_fields)~f:(funname->ifSet.memtarget_fieldsname&¬(Set.memset_fieldsname)thenmk_lident~locname,ppat_var~loc(Located.mk~locname)elsemk_lident~locname,ppat_any~loc)inppat_record~locrecord_patClosedinletrhs_record=letrhs_fields=List.map(Set.to_listtarget_fields)~f:(funname->ifSet.memmodified_fieldsnamethen(letf=evar~loc(modify_field_namename)inletapplication=pexp_apply~locf[Asttypes.Nolabel,evar~locname]inmk_lident~locname,application)else(letlongident=mk_lident~locnameinletexpr=pexp_ident~loclongidentinlongident,expr))inpexp_record~locrhs_fieldsNoneinletacc=[%expr([%erhs_record]:[%ttarget_type])]inletacc=Set.fold(Set.unionset_fields(Set.difftarget_fieldssource_fields))~init:acc~f:(funaccname->mk_pexp_fun~loc~nameacc)inletacc=Set.fold_rightmodified_fields~init:acc~f:(funnameacc->letname=modify_field_namenameinmk_pexp_fun~loc~nameacc)in[%exprfun([%precord_pat]:[%tsource_type])->[%eacc]];;letgenerate_stable_variant_module~td~loc~cdl=letalias_argscd=matchcd.pcd_argswith|Pcstr_recordlds->List.mapilds~f:(funi_->"v"^Int.to_stringi)|Pcstr_tuplefields->List.mapifields~f:(funi_->"v"^Int.to_stringi)inletalias_fun_labelname=name^"_fun"inletlabels_and_aliasescd=matchcd.pcd_argswith|Pcstr_tuple_->List.map(alias_argscd)~f:(funalias->Nolabel,alias)|Pcstr_recordlds->List.map2_exn(alias_argscd)lds~f:(funaliasld->Labelledld.pld_name.txt,alias)inletconstructors=List.mapcdl~f:(funcd->letarg_aliases=alias_argscdinletexpr=letconstructed_value=letarg=matchcd.pcd_argswith|Pcstr_tuple_->pexp_tuple_opt~loc(List.maparg_aliases~f:(funalias->evar~localias))|Pcstr_recordlds->Some(pexp_record~loc(List.map2_exnarg_aliaseslds~f:(funaliasld->Located.lident~locld.pld_name.txt,evar~localias))None)inpexp_construct~loc(Located.lident~loccd.pcd_name.txt)arginifList.is_emptyarg_aliasesthen[%exprfun()->[%econstructed_value]]elseList.fold_right(labels_and_aliasescd)~init:constructed_value~f:(fun(label,alias)acc->pexp_fun~loclabelNone(pvar~localias)acc)inpstr_value~locNonrecursive[value_binding~loc~pat:(pvar~loc(String.lowercasecd.pcd_name.txt))~expr])inletmap_function=letcases=List.mapcdl~f:(funcd->letlowercase=String.lowercasecd.pcd_name.txtinletarg_aliases=alias_argscdinletpattern=letarg=matchcd.pcd_argswith|Pcstr_tuple_->ppat_tuple_opt~loc(List.maparg_aliases~f:(funalias->pvar~localias))|Pcstr_recordlds->Some(ppat_record~loc(List.map2_exnldsarg_aliases~f:(funldalias->Located.lident~locld.pld_name.txt,pvar~localias))Closed)inppat_construct~loc(Located.lident~loccd.pcd_name.txt)arginletvalue=letfun_expr=evar~loc(alias_fun_labellowercase)inifList.is_emptyarg_aliasesthen[%expr[%efun_expr]()]elsepexp_apply~locfun_expr(List.map(labels_and_aliasescd)~f:(fun(lbl,alias)->lbl,evar~localias))incase~guard:None~lhs:pattern~rhs:value)inletexpr=List.fold_right~init:(pexp_function~loccases)cdl~f:(funcdacc->letname=String.lowercasecd.pcd_name.txtinpexp_fun~loc(Labelledname)None(pvar~loc(alias_fun_labelname))acc)inmk_module~loc~name:"Helper"~items:[pstr_value~locNonrecursive[value_binding~loc~pat:(pvar~loc"map")~expr]]in[mk_module~loc~name:(stable_variant_name~type_name:td.ptype_name.txt)~items:(constructors@[map_function])];;letconvert_variant~loc~source_variants~target_variants~modified_variants~target_type~source_type=(* Create pexp_ident scoped to the same module as [which_type]. *)letvariants_longident~loc~which_typepath=letaddlongident_optx=matchlongident_optwith|Somel->Some(Ldot(l,x))|None->Some(Lidentx)inletinit=matchwhich_type.ptyp_descwith|Ptyp_constr(lid_loc,_)->(matchlid_loc.txtwith|Lapply_->Location.raise_errorf~loc"Unexpected Lapply"|Lident_->None|Ldot(t,_)->Somet)|_->assertfalseinletlongident=Option.value_exn(List.fold~initpath~f:add)inpexp_ident~loc(Located.mk~loclongident)inletacc=[%expr[%evariants_longident~loc~which_type:source_type[stable_variants~type_:source_type;"Helper";"map"]]v]inletrhs=Set.foldsource_variants~init:acc~f:(funaccname->letf=ifSet.memmodified_variantsnamethenevar~loc(modify_field_namename)elseifnot(Set.memtarget_variantsname)thenevar~loc(remove_field_namename)elsevariants_longident~loc~which_type:target_type[stable_variants~type_:target_type;String.lowercasename]inpexp_apply~locacc[Labelled(String.lowercasename),f])inletacc=[%exprfun(v:[%tsource_type])->([%erhs]:[%ttarget_type])]inletacc=Set.fold(Set.diffsource_variantstarget_variants)~init:acc~f:(funaccname->letname=remove_field_namenameinmk_pexp_fun~loc~nameacc)inSet.fold_rightmodified_variants~init:acc~f:(funnameacc->letname=modify_field_namenameinmk_pexp_fun~loc~nameacc);;letconversions_of_td~ppx_name~target_type?(add=[])?(remove=[])?(modify=[])?(set=[])td=letloc=td.ptype_locinletadd=Set.of_list(moduleString)addinletmodify=Set.of_list(moduleString)modifyinletremove=Set.of_list(moduleString)removeinletset=Set.of_list(moduleString)setinInvariant.all_disjoints~loc~add~modify~remove~set;letcurrent_type=Ast_helper.Typ.constr~loc(Located.map_lidenttd.ptype_name)(List.map~f:fsttd.ptype_params)inletconversions=matchtarget_typewith|None->[]|Sometarget_type->letto_target_name=name_of_type_name~dir:`To~source:td.ptype_name.txt~type_name:target_typeinletof_target_name=name_of_type_name~dir:`Of~source:td.ptype_name.txt~type_name:target_typeinletto_target,of_target=matchtd.ptype_kindwith|Ptype_recordlds->letcurrent_fields=Set.of_list(moduleString)(List.maplds~f:(funld->ld.pld_name.txt))inInvariant.things_are_known~thing_name:"fields"~supposed_to_be:"removed"~loc~all:current_fieldsremove;Invariant.things_are_known~thing_name:"fields"~supposed_to_be:"modified"~loc~all:current_fieldsmodify;Invariant.things_are_known~thing_name:"fields"~supposed_to_be:"set"~loc~all:current_fieldsset;letother_fields=Set.diff(Set.unioncurrent_fieldsadd)removeinletto_target=convert_record~loc~source_fields:current_fields~target_fields:other_fields~modified_fields:modify~set_fields:set~target_type~source_type:current_typeinletof_target=convert_record~loc~source_fields:other_fields~target_fields:current_fields~modified_fields:modify~set_fields:set~target_type:current_type~source_type:target_typeinto_target,of_target|Ptype_variantcdl->letcurrent_variants=Set.of_list(moduleString)(List.mapcdl~f:(funcd->cd.pcd_name.txt))inInvariant.things_are_known~thing_name:"variants"~supposed_to_be:"removed"~loc~all:current_variantsremove;Invariant.things_are_known~thing_name:"variants"~supposed_to_be:"modified"~loc~all:current_variantsmodify;ifnot(Set.is_emptyset)thenLocation.raise_errorf~loc"[set] is for record only";letother_variants=Set.diff(Set.unioncurrent_variantsadd)removeinletto_target=convert_variant~loc~source_variants:current_variants~target_variants:other_variants~modified_variants:modify~target_type~source_type:current_typeinletof_target=convert_variant~loc~source_variants:other_variants~target_variants:current_variants~modified_variants:modify~target_type:current_type~source_type:target_typeinto_target,of_target|Ptype_open->Location.raise_errorf~loc"%s: open types not supported"ppx_name|Ptype_abstract->Location.raise_errorf~loc"%s: abstract types not supported"ppx_namein[[%strilet[%ppvar~locto_target_name]=[%eto_target]];[%strilet[%ppvar~locof_target_name]=[%eof_target]]]inletextra_struct=matchtd.ptype_kind,target_typewith|Ptype_variantcdl,_->generate_stable_variant_module~loc~cdl~td|Ptype_record_,None->Location.raise_errorf~loc"%s: missing target version"ppx_name|Ptype_record_,Some_|(Ptype_open|Ptype_abstract),_->[]inextra_struct@conversions;;letfields_or_constructors=letopenAst_patterninletrec_fields_pat=elist(pexp_ident(lident__))inletconstrs_pat=elist(pexp_construct(lident__)none)inaltrec_fields_patconstrs_pat;;lettype_pattern=letopenAst_patterninletident=map'(pexp_ident__)~f:(funloc_lid->Some(Ast_builder.Default.ptyp_constr~loc(Located.mk~loclid)[]))inlettype_=map'(* make sure we get a type constructor. *)(pexp_extension(extension(string"stable")(ptyp(ptyp_constr__'__))))~f:(funloc_lidparams->Some(Ast_builder.Default.ptyp_constr~loclidparams))inaltidenttype_;;letargs=Deriving.Args.(empty+>arg"version"type_pattern+>arg"add"fields_or_constructors+>arg"modify"fields_or_constructors+>arg"set"fields_or_constructors+>arg"remove"fields_or_constructors);;(* That's actually useless, it's just here so ppxlib's driver doesn't complain *)letrewrite_type_ext=Extension.declare"stable"Extension.Context.expressionAst_pattern.(ptyp(ptyp_constr__'__))(fun~loc~path:___->[%expr`Do_not_use_percent_stable_outside_of_deriving_stable]);;let()=Driver.register_transformation"stable"~extensions:[rewrite_type_ext]letgenppx_name~loc~path:_(_rec,tds)target_typeaddmodifysetremove=matchtdswith|[td]->conversions_of_td~ppx_name~target_type?add?remove?modify?settd|_->Location.raise_errorf~loc"mutually recursive types are not supported by ppx_stable_type";;letstable_record=letname="stable_record"inletstr_type_decl=Deriving.Generator.makeargs(genname)inDeriving.addname~str_type_decl;;letstable_variant=letname="stable_variant"inletstr_type_decl=Deriving.Generator.makeargs(genname)~deps:[]inDeriving.addname~str_type_decl;;