123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371(*
* OCanren PPX
* Copyright (C) 2016-2020
* Dmitrii Kosarev aka Kakadu, Petr Lozov
* St.Petersburg State University, JetBrains Research
*)openBaseopenPpxlibopenPpxlib.Ast_builder.DefaultopenPpxlib.Ast_helperopenPrintflet(@@)=Caml.(@@)moduleNaming=structletfabst_name=sprintf"g%s"letfunctor_name=sprintf"For_%s"endmoduleTypeNameMap=Map.M(String)moduleFoldInfo=struct(* using fields of structure below we can generate ground type and the logic type *)typeitem={param_name:string;rtyp:core_type;ltyp:core_type}exceptionItemFoundofitemtypet=itemlistletparam_for_rtyptypts=lettyp_repr=Pprintast.core_typeCaml.Format.str_formattertyp;Caml.Format.flush_str_formatter()intryList.iterts~f:(funi->letnew_repr=Caml.Format.asprintf"%a"Pprintast.core_typei.rtypinifString.equalnew_reprtyp_reprthenraise(ItemFoundi));NonewithItemFoundi->Someiletmap~f(xs:t)=List.map~fxsletempty=[]letis_empty:t->bool=List.is_emptyletextendparam_namertypltypts=(* printf "extending by `%s`\n%!" param_name;*){param_name;rtyp;ltyp}::tsend(* TODO: maybe use Ppxlib.name_type_params_in_td ? *)letextract_names=List.map~f:(funtyp->matchtyp.ptyp_descwith|Ptyp_vars->s|_->failwith(Caml.Format.asprintf"Don't know what to do with %a"Pprintast.core_typetyp))letnolabel=Asttypes.Nolabelletget_param_namespcd_args=letPcstr_tuplepcd_args=pcd_argsinextract_namespcd_argsletmangle_construct_namename=letlow=String.mapi~f:(function0->Char.lowercase|_->Fn.id)nameinmatchlowwith|"val"|"if"|"else"|"for"|"do"|"let"|"open"|"not"->low^"_"|_->lowletlower_lidlid=Location.{lidwithtxt=mangle_construct_namelid.Location.txt}moduleLocated=structincludeLocated(* let mknoloc txt = { txt; loc = Location.none } *)letmap_loc~fl={lwithtxt=fl.txt}endmoduleExp=structincludeExpletmytuple~loc?(attrs=[])=function|[]->failwith"bad_argument: mytuple"|[x]->x|xs->tuple~loc~attrs:attrsxsletapply~locf=function|[]->f|xs->apply~locfxsendletprepare_distribs~locfully_abstract_tnametdeclfmap_decl=letopenLongidentinletconstructors=matchtdecl.ptype_kindwith|Ptype_variantc->c|_->failwith"not implemented"inletgen_module_str=Naming.functor_namefully_abstract_tnameinletdistrib_lid=Located.mk~locLongident.(Ldot(Lidentgen_module_str,"distrib"))in[Str.module_~loc@@Mb.mk~loc(Located.mk~loc(Some"T"))(Mod.structure~loc[fmap_decl;Str.type_~locNonrecursive[Type.mk~loc~params:tdecl.ptype_params~kind:Ptype_abstract~priv:Public~cstrs:[]~manifest:(Typ.constr~loc(Located.mk~loc@@lidenttdecl.ptype_name.txt)@@List.map~f:fsttdecl.ptype_params)(Located.mk~loc"t")]]);Str.module_~loc@@Mb.mk~loc(Located.mk~loc@@Somegen_module_str)(Mod.apply~loc(Mod.ident~loc(Located.mk~loc@@Lident(matchtdecl.ptype_paramswith[]->"Fmap"|xs->sprintf"Fmap%d"(List.lengthxs))))(Mod.ident~loc(Located.mk~loc@@Lident"T")))]@(List.mapconstructors~f:(fun{pcd_name;pcd_args}->letnames=get_param_namespcd_args|>List.mapi~f:(funi_->sprintf"a%d"i)inletbody=letconstr_itself=function|[]->Exp.construct(Located.mk~loc@@lidentpcd_name.txt)None|args->Exp.construct(Located.mk~loc@@lidentpcd_name.txt)@@Option.some@@(matchargswith[x]->x|args->Exp.mytuple~locargs)inmatchnameswith|[]->constr_itself[]|[one]->constr_itself[Exp.ident~loc@@Located.mk~loc(Lidentone)]|xs->(* construct tuple here *)constr_itself(List.map~f:(funname->Exp.ident~loc@@Located.mk~loc(Lidentname))xs)inletbody=[%exprinj[%eExp.apply~loc(Exp.ident~locdistrib_lid)[nolabel,body]]]inStr.value~locAsttypes.Nonrecursive[Vb.mk~loc(Pat.var~loc@@lower_lidpcd_name)(matchnameswith|[]->Exp.fun_~locnolabelNone(Pat.construct~loc(Located.mk~loc(Lident"()"))None)body|names->List.fold_right~f:(funnameacc->Exp.fun_~locnolabelNone(Pat.var~loc@@Located.mk~locname)acc)names~init:body)]))(* At the moment we genrate fmap here but it is totally fine to reuse the one genrated by GT *)letprepare_fmap~loctdecl=[%striletrecfmap_eta=GT.gmap[%eExp.ident~loc(Located.mk~loc@@lidenttdecl.ptype_name.txt)]_eta]letmangle_strings=s^"_ltyp"letmap_deepest_lident~flident=letrechelper=function|Lidents->Lident(fs)|Ldot(l,s)->Ldot(l,fs)|Lapply(l,r)->Lapply(l,helperr)inhelperlidentletmangle_lidentlident=map_deepest_lident~f:mangle_stringlidentletmangle_core_typetyp=letrechelpertyp=letloc=typ.ptyp_locinmatchtypwith|[%type:_]->assertfalse|[%type:string]->[%type:stringlogic]|_->matchtyp.ptyp_descwith|Ptyp_vars->typ|Ptyp_constr({txt;loc},params)->Typ.constr~loc{loc;txt=mangle_lidenttxt}@@List.map~f:helperparams|_->failwith"should not happen"inhelpertypletmangle_reifiertyp=letrechelpertyp=letloc=typ.ptyp_locinmatchtypwith|[%type:_]->assertfalse|[%type:string]|[%type:int]->[%exprOCanren.reify]|_->matchtyp.ptyp_descwith|Ptyp_vars->Exp.ident~loc@@Located.lident~loc("f"^s)|Ptyp_constr({txt;loc},params)->Exp.apply~loc(Exp.ident~loc@@Located.mk~loc(map_deepest_lident~f:(funs->s^"_reify")txt))@@List.map~f:(funtyp->Nolabel,helpertyp)params|_->failwith"should not happen"inhelpertypletrevisit_adt~locother_attrstdeclctors=letder_typ_name=tdecl.ptype_name.Asttypes.txtin(* Let's forget about mutal recursion for now *)(* For every constructor argument we need to put ground types to parameters *)letmapa,full_t=List.fold_right~f:(funcd(n,acc_map,cs)->letn,map2,new_args=List.fold_right~f:(funtyp(n,map,args)->matchtyp.ptyp_descwith|Ptyp_any->assertfalse|Ptyp_vars->(n,map,typ::args)|Ptyp_constr({txt;loc},params)->beginmatchFoldInfo.param_for_rtyptypmapwith|Some{FoldInfo.param_name}->(n,map,(ptyp_var~locparam_name)::args)|None->(* We need to mangle whole constructor *)letltyp=mangle_core_typetypinletnew_name=sprintf"a%d"nin(n+1,FoldInfo.extendnew_nametypltypmap,(ptyp_var~locnew_name)::args)end|_->matchFoldInfo.param_for_rtyptypmapwith|Some{FoldInfo.param_name}->(n,map,(ptyp_var~locparam_name)::args)|None->letnew_name=sprintf"a%d"nin(n+1,FoldInfo.extendnew_nametyptypmap,(ptyp_var~locnew_name)::args))(matchcd.pcd_argswithPcstr_tuplett->tt|Pcstr_record_->assertfalse)~init:(n,acc_map,[])inletnew_args=Pcstr_tuplenew_argsin(n,map2,{cdwithpcd_args=new_args}::cs))ctors~init:(0,FoldInfo.empty,[])|>(fun(_,mapa,cs)->mapa,{tdeclwithptype_kind=Ptype_variantcs;ptype_attributes=other_attrs})in(* now we need to add some parameters if we collected ones *)letans=ifFoldInfo.is_emptymapathenletfmap_for_typ=prepare_fmap~locfull_tinletltyp=pstr_type~locRecursive[{tdeclwithptype_kind=Ptype_abstract;ptype_name=Located.mk~loc(mangle_stringder_typ_name);ptype_manifest=Some(ptyp_constr~loc(Located.lident~loc"logic")[ptyp_constr~loc(Located.lident~locder_typ_name)@@List.map~f:fsttdecl.ptype_params]);ptype_attributes=other_attrs}]inletground_typ=pstr_type~locNonrecursive[{full_twithptype_attributes=other_attrs}]inletthe_reifier=letreifiers=FoldInfo.map~f:(fun{FoldInfo.rtyp}->mangle_reifierrtyp)mapainpstr_value~locRecursive[value_binding~loc~pat:(ppat_var~loc@@Located.mk~loc(der_typ_name^"_reify"))~expr:[%exprfunh->[%epexp_apply~loc(pexp_ident~loc@@Located.mk~locLongident.(Ldot(Lident(Naming.functor_nametdecl.ptype_name.txt),"reify")))(List.map~f:(funt->(Nolabel,t))(reifiers@[[%exprh]]))]]]inground_typ::ltyp::(prepare_distribsder_typ_name~locfull_tfmap_for_typ)@[the_reifier]elseletfunctorized_type=Naming.fabst_namefull_t.ptype_name.txtinletfully_abstract_typ=(* a type name for which we will generate `fmap` *)letextra_params=FoldInfo.mapmapa~f:(funfi->(Ast_helper.Typ.varfi.FoldInfo.param_name,(Asttypes.NoVariance,Asttypes.NoInjectivity)))inletopenLocationin{full_twithptype_params=full_t.ptype_params@extra_params;ptype_name={full_t.ptype_namewithtxt=functorized_type}}inletfully_abstract_tdecl=pstr_type~locNonrecursive[fully_abstract_typ]inletground_typ=letalias=letold_params=List.map~f:fsttdecl.ptype_paramsinletextra_params=FoldInfo.map~f:(fun{FoldInfo.rtyp}->rtyp)mapainTyp.constr~loc(Located.lident~locfully_abstract_typ.ptype_name.Asttypes.txt)(old_params@extra_params)inpstr_type~locRecursive[{tdeclwithptype_manifest=(Somealias);ptype_kind=Ptype_abstract;ptype_attributes=other_attrs}]inletlogic_typ=letalias=letold_params=List.map~f:fsttdecl.ptype_paramsinletextra_params=FoldInfo.map~f:(fun{FoldInfo.ltyp}->ltyp)mapainTyp.constr~loc(Located.lident~loc"logic")[Typ.constr~loc(Located.lident~locfully_abstract_typ.ptype_name.Asttypes.txt)(old_params@extra_params)]inpstr_type~locRecursive[{tdeclwithptype_kind=Ptype_abstract;ptype_manifest=Somealias;ptype_name=Located.mapmangle_stringtdecl.ptype_name;ptype_attributes=other_attrs}]inletfmap_for_typ=prepare_fmap~locfully_abstract_typinletdistribs=prepare_distribs~locder_typ_namefully_abstract_typfmap_for_typinletthe_reifier=letreifiers=FoldInfo.map~f:(fun{FoldInfo.rtyp}->mangle_reifierrtyp)mapainpstr_value~locRecursive[value_binding~loc~pat:(ppat_var~loc@@Located.mk~loc(der_typ_name^"_reify"))~expr:[%exprfuneta->[%epexp_apply~loc(pexp_ident~loc@@Located.mk~locLongident.(Ldot(Lident(Naming.functor_nametdecl.ptype_name.txt),"reify")))(List.map~f:(funt->(Nolabel,t))(reifiers@[[%expreta]]))]]]infully_abstract_tdecl::ground_typ::logic_typ::distribs@[the_reifier]inanslethas_to_gen_attr(xs:attributes)=letours,others=List.partition_mapxs~f:(fun({attr_name={txt};_}asattr)->ifString.equaltxt"distrib"thenFirstattrelseSecondattr)inassert(List.lengthours<=1);matchourswith|[]->None|[h]->Some(h,others)|_->failwith"to many distrib attributes"letsuitable_tydecl_wrap~on_ok~on_failtdecl=matchtdecl.ptype_kindwith|Ptype_variantcswhenOption.is_nonetdecl.ptype_manifest->beginmatchhas_to_gen_attrtdecl.ptype_attributeswith|None->on_fail()|Some(our,other_attrs)->Attribute.explicitly_drop#type_declarationtdecl;on_okcsother_attrs{tdeclwithptype_attributes=[]}end|_->on_fail()letsuitable_tydecl=suitable_tydecl_wrap~on_ok:(fun___->true)~on_fail:(fun()->false)letstr_type_decl~loc(flg,tdls)=letwrap_tydeclslocts=letftdecl=suitable_tydecl_wraptdecl~on_ok:(funcsother_attrstdecl->revisit_adt~locother_attrstdeclcs)~on_fail:(fun()->failwith"Only variant types without manifest are supported")inList.concat(List.map~fts)inwrap_tydeclsloctdls