123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417(*
* Copyright (c) 2019-2020 Craig Ferguson <me@craigfe.io>
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*)openPpxlibincludeEngine_intfletmap_lidentf=function|Lapply_->invalid_arg"Lident.Lapply not supported"|Ldot(l,s)->Ldot(l,fs)|Lidents->Lident(fs)moduleLocated(Attributes:Attributes.S)(A:Ast_builder.S):S=structtypestate={rec_flag:rec_flag;type_name:string;lib:stringoption;repr_name:string;rec_detected:boolref;var_repr:([`Any|`Varofstring]->expressionoption)ref;(** Given a type variable in a type, get its corresponding typerep (if
the variable is properly bound). *)}letadd_var_repr:typeab.(a->boption)ref->a*b->unit=funf_ref(a,b)->letf_old=!f_refinletf_newa'=ifa=a'thenSomebelsef_olda'inf_ref:=f_newopenUtilsopenUtils.Make(A)moduleReader=Monad.ReadermoduleAlgebraic=structincludeAlgebraicincludeAlgebraic.Located(A)(Reader)endopenAopenReader.SyntaxopenReaderletall_unlabelled=List.map(funx->(Nolabel,x))letrecursive~libfparame=letmu=evar(matchlibwithSomes->s^".mu"|None->"mu")in[%expr[%emu](fun[%ppvarfparam]->[%ee])]letmutually_recursive~lib(e1,n1)(e2,n2)=letmu2=evar(matchlibwithSomes->s^".mu2"|None->"mu2")in[%expr[%emu2](fun[%ppvarn1][%ppvarn2]->([%ee1],[%ee2]))]letrepr_name_of_type_name=function"t"->"t"|x->x^"_t"letin_lib~libx=matchlibwithSomelib->lib^"."^x|None->xletcontains_tvartvartyp=(objectinherit[bool]Ast_traverse.foldassupermethod!core_type_desct=super#core_type_desct>>funacc->acc||matchtwithPtyp_varvwhenv=tvar->true|_->falseend)#core_typetypfalseletrowfield_is_inherit=function|{prf_desc=Rinherit_;_}->true|_->falseletrecderive_coretyp=let*{type_name;lib;var_repr;_}=askinletloc=typ.ptyp_locinmatchtyp.ptyp_descwith|Ptyp_constr({txt=const_name;_},args)->(matchAttribute.getAttributes.reprtypwith|Somee->returne|None->letnobuiltin=Option.to_bool(Attribute.getAttributes.nobuiltintyp)inlet*lident=derive_lident~nobuiltinconst_nameinlet+cons_args=args>|=derive_core|>sequence|>mapall_unlabelledinpexp_apply(pexp_identlident)cons_args)|Ptyp_variant(_,Open,_)->Raise.Unsupported.type_open_polyvar~loctyp|Ptyp_variant(rowfields,Closed,_labellist)->ifList.existsrowfield_is_inheritrowfieldsthenRaise.Unsupported.polyvar_inherit_case~loctyp;derive_polyvarianttype_namerowfields|Ptyp_poly_->Raise.Unsupported.type_poly~loctyp|Ptyp_tupleargs->derive_tupleargs|Ptyp_arrow_->Raise.Unsupported.type_arrow~loctyp|Ptyp_any->Location.raise_errorf~loc"Unbound type variable"|Ptyp_varv->(match!var_repr(`Varv)with|Somer->returnr|None->Location.raise_errorf~loc"Unbound type variable"v)|Ptyp_package_->Raise.Unsupported.type_package~loctyp|Ptyp_extension_->Raise.Unsupported.type_extension~loctyp|Ptyp_alias(c,var)->ifcontains_tvarvarcthen(add_var_reprvar_repr(`Varvar,evarvar);let+inner=derive_corecinrecursive~libvarinner)elsederive_corec|Ptyp_object_|Ptyp_class_->invalid_arg"unsupported"andderive_tupleargs=let*{lib;_}=askinmatchargswith|[t]->(* This case can occur when the tuple type is nested inside a variant *)derive_coret|_->lettuple_type=(matchList.lengthargswith|2->"pair"|3->"triple"|n->Raise.Unsupported.tuple_size~locn)|>in_lib~lib|>evarinargs>|=derive_core|>sequence|>map(all_unlabelled>>pexp_applytuple_type)andderive_recordls=let*{type_name;lib;_}=askinletsubderivelabel_decl=letfield_name=label_decl.pld_name.txtinlet+field_repr=derive_corelabel_decl.pld_typeinAlgebraic.Typ.{field_name;field_repr}inAlgebraic.(encodeTyp.Record)~subderive~lib~type_namelsandderive_variantcs=let*{type_name;lib;_}=askinletsubderivec=letcase_name=c.pcd_name.txtinlet+case_cons=matchc.pcd_argswith|Pcstr_record_->invalid_arg"Inline record types unsupported"|Pcstr_tuple[]->returnNone|Pcstr_tuplecs->let+tuple_typ=derive_tuplecsinSome(tuple_typ,List.lengthcs)inAlgebraic.Typ.{case_name;case_cons}inAlgebraic.(encodeVariant)~subderive~lib~type_namecsandderive_polyvariantnamerowfields=let*{lib;_}=askinletsubderivef=let+case_name,case_cons=matchf.prf_descwith|Rtag(label,_,[])->return(label.txt,None)|Rtag(label,_,typs)->let+tuple_typ=derive_tupletypsin(label.txt,Some(tuple_typ,List.lengthtyps))|Rinherit_->assertfalseinAlgebraic.Typ.{case_name;case_cons}inAlgebraic.(encodePolyvariant)~subderive~lib~type_name:namerowfieldsandderive_lident:nobuiltin:bool->longident->(longidentloc,state)Reader.t=fun~nobuiltintxt->let+{lib;type_name;rec_flag;rec_detected;repr_name;_}=askinmatch(rec_flag,txt)with|Recursive,Lidentconst_namewhenString.equalconst_nametype_name->(* If this type is the one we are deriving and the 'nonrec'
keyword hasn't been used, replace with the repr
name *)rec_detected:=true;Located.lidentrepr_name|_->(match(nobuiltin,Dsl.type_to_combinator_nametxt)with|true,(Some_|None)|false,None->map_lidentrepr_name_of_type_nametxt|>Located.mk|false,Somecombinator_name->in_lib~libcombinator_name|>Located.lident)letderive_type_decl:type_declaration->(expression,state)Reader.t=funtyp->matchtyp.ptype_kindwith|Ptype_abstract->(matchtyp.ptype_manifestwith|None->invalid_arg"No manifest"|Somec->(matchc.ptyp_descwith(* No need to open library module *)|Ptyp_constr({txt;loc=_},[])->(matchAttribute.getAttributes.reprcwith|Somerepr->returnrepr|None->letnobuiltin=matchAttribute.getAttributes.nobuiltincwith|Some()->true|None->falseinlet+name=derive_lident~nobuiltintxtinpexp_identname)(* Type constructor: list, tuple, etc. *)|_->derive_corec))|Ptype_variantcs->derive_variantcs|Ptype_recordls->derive_recordls|Ptype_open->Raise.Unsupported.type_open~locletparse_libexpr=letpattern=letopenAst_patterninletnone=map0~f:None@@pexp_construct(lident(string"None"))noneinletsome=map1~f:Option.some@@pexp_construct(lident(string"Some"))(some(estring__))innone|||someinAst_pattern.parsepatternlocexpr(funk->k)~on_error:(fun()->Location.raise_errorf~loc:expr.pexp_loc"Could not process `lib' argument: must be either `Some \"Lib\"' or \
`None'")(* Remove duplicate elements from a list (preserving the order of the first
occurrence of each duplicate). *)letlist_uniq_stable=letrecinner~seenacc=function|[]->List.revacc|x::xswhennot(List.memxseen)->inner~seen:(x::seen)(x::acc)xs|_::xs(* seen *)->inner~seenaccxsininner~seen:[][]moduleUnbound_tvars=structtypeacc={free:stringlist;ctx_bound:stringlist}(* Find all unbound type variables, renaming any instances of [Ptyp_any] to a
fresh variable. *)letfindtyp=(objectinherit[acc]Ast_traverse.fold_mapassupermethod!core_type_desctacc=matchtwith|Ptyp_varvwhennot(List.memvacc.ctx_bound)->(t,{accwithfree=v::acc.free})|Ptyp_any->letname=gen_symbol()in(Ptyp_varname,{accwithfree=name::acc.free})|Ptyp_alias(c,v)->(* Push [v] to the bound stack, traverse the alias, then remove it. *)letc,acc=super#core_typec{accwithctx_bound=v::acc.ctx_bound}inletctx_bound=matchacc.ctx_boundwith|v'::ctx_boundwhenv=v'->ctx_bound|_->assertfalsein(Ptyp_alias(c,v),{accwithctx_bound})|_->super#core_type_desctaccend)#core_typetyp{free=[];ctx_bound=[]}endletexpand_typ?libtyp=lettyp,Unbound_tvars.{free=tvars;_}=Unbound_tvars.findtypinlettvars=List.revtvars|>list_uniq_stableinletenv={rec_flag=Nonrecursive;type_name="t";repr_name="t";rec_detected=reffalse;lib;var_repr=ref(function|`Any->assertfalse(* We already renamed all instances of [Ptyp_any] *)|`Varx->Some(evarx));}inrun(derive_coretyp)env|>lambdatvarsletderive_sig?name?lib(_rec_flag,type_declarations)=ListLabels.maptype_declarations~f:(funtyp->lettype_name=typ.ptype_name.txtinletname=Located.mk(matchnamewith|Somen->n|None->repr_name_of_type_nametype_name)inletty_lident=(matchlibwith|Some_->in_lib~lib"t"|None->((* This type decl may shadow the repr type ['a t] *)matchname.txtwith"t"->"ty"|_->"t"))|>Located.lidentinlettype_=combinator_type_of_type_declarationtyp~f:(fun~loc:_t->ptyp_constrty_lident[t])inpsig_value(value_description~name~type_~prim:[]))letrepr_of_type_decl~(handle_recursion:bool)~(lib:stringoption)~rec_flagtyprepr_name:pattern*expression*[`Param_requiredofbool]=lettparams=typ.ptype_params|>List.map(function|{ptyp_desc=Ptyp_varv;_},_->v|{ptyp_desc=Ptyp_any;_},_->"_"|_->assertfalse)inletenv=lettype_name=typ.ptype_name.txtinletrec_detected=reffalseinletvar_repr=ref(function|`Any->Raise.Unsupported.type_any~loc|`Varv->ifList.memvtparamsthenSome(evarv)elseNone)in{rec_flag;type_name;repr_name;rec_detected;lib;var_repr}inletexpr=run(derive_type_decltyp)envin(* If the type is syntactically self-referential, wrap with [mu] *)letexpr=ifhandle_recursion&&!(env.rec_detected)thenrecursive~lib:env.libenv.repr_nameexprelseexprinletexpr=lambdatparamsexprinletpat=pvarenv.repr_namein(pat,expr,`Param_required(List.lengthtparams>0))letderive_str?name?lib=function|Recursive,[]->assertfalse|Recursive,tdswhenList.lengthtds>2->failwith"Mutually-recursive groups of size > 2 supported"|rec_flag,type_declarations->letmultiple_tds=List.lengthtype_declarations>1inletrepr_names=match(name,multiple_tds)with|Some_,true->failwith"Cannot specify name of mutually-recursive group"|Somen,false->[n]|None,_->ListLabels.maptype_declarations~f:(funtyp->repr_name_of_type_nametyp.ptype_name.txt)inletpats,named_exprs=(* If there is only one type declaration – and it's potentially
recursive – we might want to add a [mu] combinator inside the repr
derivation.
Mutually-recursive declarations are handled separately with [mu2]
combinators below. *)lethandle_recursion=rec_flag=Recursive&¬multiple_tdsinListLabels.map2type_declarationsrepr_names~f:(funtyprepr_name->letpat,expr,`Param_requiredpr=repr_of_type_decl~rec_flag~handle_recursion~libtyprepr_nameinifpr&&multiple_tdsthenfailwith"Can't support mutually-recursive types with type parameters";(pat,(expr,repr_name)))|>List.splitinletpat,expr=match(pats,named_exprs)with|[p1],[(e1,_)]->(p1,e1)|ps,es->letpat=List.reduce_exnps~f:(funp1p2->[%pat?[%pp1],[%pp2]])inletexpr=ifrec_flag=Recursivethenmatcheswith|[e1;e2]->mutually_recursive~libe1e2|_->(* Recursive groups of size n > 2 rejected above *)assertfalseelseList.mapfstes|>List.reduce_exn~f:(fune1e2->[%expr[%ee1],[%ee2]])in(pat,expr)in[pstr_valueNonrecursive[value_binding~pat~expr]]end