Legend:
Page
Library
Module
Module type
Parameter
Class
Class type
Source
Source file expander.ml
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445openPpxlibletapply_substitution~orig~substi=letmapper=objectinheritAst_traverse.mapassupermethod!expressionexpr=leteqe1e2=(* TODO: terrible way to compare expressions *)Pprintast.string_of_expressione1=Pprintast.string_of_expressione2inifeqexprorigthensubstielsesuper#expressionexprendinmapper#expressionletgenerate_attributev~loc=letopenAst_builder.Defaultinpstr_attribute~loc(attribute~loc~name:(Located.mk~loc"ocaml.warning")~payload:(PStr[pstr_eval~loc(estring~locv)[]]))letsuppress_warning_27~loc=generate_attribute~loc"-27"letenforce_warning_27~loc=generate_attribute~loc"+27"letwrap_27xs=(suppress_warning_27~loc:Location.none::xs)@[enforce_warning_27~loc:Location.none](* let suppress_warning_27 = *)(* let suppress_warning_27 = *)(* let loc = Location.none in *)(* let payload = *)(* PStr *)(* [ *)(* Ast_helper.Str.eval *)(* (Ast_helper.Exp.constant (Pconst_string ("-27", loc, None))); *)(* ] *)(* in *)(* let attr_name = "ocaml.warning" *)(* in *)(* let attribute = Ast_builder.Default.attribute ~loc ~name:attr_name ~payload in *)(* Ast_builder.Default.pstr_attribute ~loc attribute *)(* let enforce_warning_27 = _ *)letto_decoder_namei=i^"_decoder"letdecoder_pvar_of_type_decltype_decl=Ast_builder.Default.pvar~loc:type_decl.ptype_name.loc(to_decoder_nametype_decl.ptype_name.txt)letdecoder_evar_of_type_decltype_decl=Ast_builder.Default.evar~loc:type_decl.ptype_name.loc(to_decoder_nametype_decl.ptype_name.txt)(** We take an expr implementation with name NAME and turn it into:
let rec NAME_AUX = fun () -> expr in NAME_AUX ().
This is so that we can later do:
let NAME =
let rec NAME_AUX () = expr in NAME_AUX ()
Thus fixing the recursive type issue.
*)letwrap_as_aux~loc~name~expr=letopenAst_builder.Defaultinletaux_fn_p=pvar~loc(name^"_aux")in[%exprD.fix(fun[%paux_fn_p]->[%eexpr])]letpexp_fun_multiarg~locfun_imple(args:patternlist)=(* Making something like fun arg1 arg2 ... -> fun_imple *)letfolderfarg=Ast_builder.Default.pexp_fun~locNolabelNoneargfin(* TODO: remove inefficient list reversal *)letargs_rev=List.revargsinCCList.fold_leftfolderfun_impleargs_revletlident_of_constructor_decl(cd:constructor_declaration)=letloc=cd.pcd_name.locinletname=cd.pcd_name.txtinAst_builder.Default.Located.lident~locnameletrecexpr_of_typ(typ:core_type)~(substitutions:(core_type*expression)list):expression=letloc={typ.ptyp_locwithloc_ghost=true}inmatchtypwith|[%type:unit]|[%type:unit]->Ast_builder.Default.evar~loc"D.null"|[%type:int]->Ast_builder.Default.evar~loc"D.int"|[%type:int32]|[%type:Int32.t]|[%type:int64]|[%type:Int64.t]|[%type:nativeint]|[%type:Nativeint.t]->failwith"Cannot yet handle any int-like but int"|[%type:float]->Ast_builder.Default.evar~loc"D.float"|[%type:bool]->Ast_builder.Default.evar~loc"D.bool"|[%type:char]->failwith"Cannot directly handle character; please cast to string first"|[%type:string]|[%type:String.t]->Ast_builder.Default.evar~loc"D.string"|[%type:bytes]|[%type:Bytes.t]->failwith"Cannot handle Bytes"(* TODO: figure out strategy *)|[%type:[%t?inner_typ]list]->letlist_decoder=Ast_builder.Default.evar~loc"D.list"inletsub_expr=expr_of_typ~substitutionsinner_typinAst_helper.Exp.apply~loclist_decoder[(Nolabel,sub_expr)]|[%type:[%t?inner_typ]array]->letarray_decoder=Ast_builder.Default.evar~loc"D.array"inletsub_expr=expr_of_typ~substitutionsinner_typinAst_helper.Exp.apply~locarray_decoder[(Nolabel,sub_expr)]|[%type:[%t?inner_typ]option]->letopt_decoder=Ast_builder.Default.evar~loc"D.nullable"inletsub_expr=expr_of_typ~substitutionsinner_typinAst_helper.Exp.apply~locopt_decoder[(Nolabel,sub_expr)]|{ptyp_desc=Ptyp_tupletyps;_}->expr_of_tuple~substitutions~loctyps(* | { ptyp_desc = Ptyp_variant (fields, _, _); ptyp_loc; _ } -> _ *)(* | { ptyp_desc = Ptyp_alias _; _ } -> *)(* failwith *)(* (Format.sprintf "This alias was a failure...: %s\n" *)(* (string_of_core_type typ)) *)|{ptyp_desc=Ptyp_constr({txt=Lidentlid;_},[]);_}asother_type->((* In the case where our type is truly recursive, we need to instead do `type_aux ()` *)leteq(ct1:core_type)(ct2:core_type)=(* TODO: This is a terrible way to compare the types... *)string_of_core_typect1=string_of_core_typect2inmatchCCList.assoc_opt~eqother_typesubstitutionswith|Somereplacement->replacement|None->Ast_builder.Default.evar~loc(to_decoder_namelid))|_->Location.raise_errorf~loc"Cannot construct decoder for %s"(string_of_core_typetyp)andexpr_of_tuple~loc~substitutions?lifttyps=(* To help understand what this function is doing, imagine we had
a type [type t = int * string * bool]. Then this will render the decoder:
let t_decoder : t D.decoder =
let open D in
let ( >>=:: ) fst rest = uncons rest fst in
int >>=:: fun arg1 ->
string >>=:: fun arg2 ->
bool >>=:: fun arg3 -> succeed (arg1, arg2, arg3)
Though if lift is present (lift is a type constructor), we will instead get:
let t_decoder : t D.decoder =
let open D in
let ( >>=:: ) fst rest = uncons rest fst in
int >>=:: fun arg1 ->
string >>=:: fun arg2 ->
bool >>=:: fun arg3 -> succeed (lift (arg1, arg2, arg3))
*)letargn=Printf.sprintf"arg%d"inlettyp_decoder_exprs=List.map(expr_of_typ~substitutions)typsinletbase=(* Consists of the initial setup partial function def, which is the inport and local definition,
as well as a running count of how many arguments there are *)((funbody->[%exprletopenDinlet(>>=::)fstrest=unconsrestfstin[%ebody]]),0)inletfn_builder(partial_expr,i)next_decoder=letvar=argniinletvar_pat=Ast_builder.Default.pvar~locvarin((funbody->partial_expr[%expr[%enext_decoder]>>=::fun[%pvar_pat]->[%ebody]]),i+1)inletcomplete_partial_expr,var_count=List.fold_leftfn_builderbasetyp_decoder_exprsinletvar_names=CCList.initvar_countargninletvar_tuple=letexpr_list=List.map(funs->[%expr[%eAst_builder.Default.evar~locs]])var_namesinAst_builder.Default.pexp_tuple~locexpr_listinmatchliftwith|Somelift->letvar_tuple_lift=Ast_builder.Default.pexp_construct~loclift(Some[%expr[%evar_tuple]])incomplete_partial_expr[%exprsucceed[%evar_tuple_lift]]|None->complete_partial_expr[%exprsucceed[%evar_tuple]]andexpr_of_constr_decl~substitutions({pcd_args;pcd_loc=loc;_}ascstr_decl:constructor_declaration)=(* We assume at this point that the decomposition into indiviaul fields is handled by caller *)ifpcd_args=Pcstr_tuple[]thenletcstr=lident_of_constructor_declcstr_declinletcstr=Ast_builder.Default.pexp_construct~loccstrNonein[%exprsucceed[%ecstr]]elseletcstr=lident_of_constructor_declcstr_declinletsub_expr=expr_of_constr_arg~substitutions~loc~cstrpcd_argsinsub_exprandexpr_of_constr_arg~loc~cstr~substitutions(arg:constructor_arguments)=matchargwith|Pcstr_tupletups->expr_of_tuple~substitutions~lift:cstr~loctups|Pcstr_recordlabl_decls->expr_of_record~loc~substitutions~lift:cstrlabl_declsandexpr_of_record~loc~substitutions?liftlabel_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 decoder:
let t_decoder : t D.decoder =
let open D in
let open D.Infix in
let* i = field "i" int in
let* s = field "s" string in
succeed {i; s}
or optionally:
let t_decoder : t D.decoder =
let open D in
let open D.Infix in
let* i = field "i" int in
let* s = field "s" string in
succeed (lift {i; s})
*)letbase(* Consists of the initial setup partial function def, which is the inport and local definition,
as well as a running count of how many arguments there are *)body=[%exprletopenDinletopenD.Infixin[%ebody]]inletfn_builderpartial_expr({pld_name;pld_type;_}:label_declaration)=letsubexpr=expr_of_typ~substitutionspld_typeinletvar_pat=Ast_builder.Default.pvar~loc:pld_name.locpld_name.txtin(* TODO correct loc? *)letstr=Ast_builder.Default.estring~loc:pld_name.locpld_name.txtinfunbody->partial_expr[%exprlet*[%pvar_pat]=field[%estr][%esubexpr]in[%ebody]]inletcomplete_partial_expr=List.fold_leftfn_builderbaselabel_declsinletvar_names=CCList.map(fun(label_decl:label_declaration)->Ast_builder.Default.({txt=Longident.Lidentlabel_decl.pld_name.txt;loc},evar~loclabel_decl.pld_name.txt))label_declsinletrecord=Ast_builder.Default.pexp_record~locvar_namesNoneinmatchliftwith|None->complete_partial_expr[%exprsucceed[%erecord]]|Somelift->letrecord_lift=Ast_builder.Default.pexp_construct~loclift(Some[%expr[%erecord]])incomplete_partial_expr[%exprsucceed[%erecord_lift]]letimplementation_generator~(loc:location)~rec_flag~substitutionstype_decl:expression=letrec_flag=really_recursiverec_flag[type_decl]inletname=to_decoder_nametype_decl.ptype_name.txtinletimple_expr=match(type_decl.ptype_kind,type_decl.ptype_manifest)with|Ptype_abstract,Somemanifest->expr_of_typ~substitutionsmanifest|Ptype_variantcstrs,None->letconstr_decs=Ast_builder.Default.(elist~loc(List.map(funcstr->lets=estring~loccstr.pcd_name.txtinlets_p=pstring~loccstr.pcd_name.txtinifcstr.pcd_args=Pcstr_tuple[]thenletlid=lident_of_constructor_declcstrinletcstr=Ast_builder.Default.pexp_construct~loclidNoneinpexp_tuple~loc[s;[%exprD.string>>=function|[%ps_p]->succeed[%ecstr]|_->fail"Failure"];(* TODO better failure message *)]elsepexp_tuple~loc[s;[%exprD.field[%es][%eexpr_of_constr_decl~substitutionscstr]];])cstrs))inletone_of_decoder=Ast_builder.Default.evar~loc"one_of"inletfull_dec=Ast_helper.Exp.apply~locone_of_decoder[(Nolabel,constr_decs)]in[%exprletopenDin[%efull_dec]]|Ptype_recordlabel_decs,_->expr_of_record~substitutions~loclabel_decs|Ptype_open,_->Location.raise_errorf~loc"Unhandled open"|_->Location.raise_errorf~loc"Unhandled mystery"inmatchrec_flagwith|Nonrecursive->imple_expr|Recursive->wrap_as_aux~loc~name~expr:imple_exprletsingle_type_decoder_gen~(loc:location)~rec_flagtype_decl:structure_itemlist=letrec_flag=really_recursiverec_flag[type_decl]inletname=to_decoder_nametype_decl.ptype_name.txtinletsubstitutions=matchrec_flagwith|Nonrecursive->[]|Recursive->[(core_type_of_type_declarationtype_decl,Ast_builder.Default.evar~loc(name^"_aux"));]inletimple=implementation_generator~loc~rec_flag~substitutionstype_declinletname=to_decoder_nametype_decl.ptype_name.txtin[%strlet[%pAst_builder.Default.pvar~locname]=[%eimple]]letrecmutual_rec_fun_gen~loc~substitutions(* These only generate the decoder in terms of one another, prior to utilizing `fix` *)(type_decls:type_declarationlist)=letopenAst_builder.Defaultinmatchtype_declswith|type_decl::rest->letvar=pvar~loc:type_decl.ptype_name.loc(to_decoder_nametype_decl.ptype_name.txt)inletsubstitutions=matchreally_recursiveRecursive[type_decl]with|Recursive->letname=to_decoder_nametype_decl.ptype_name.txtinletsubsti=Ast_builder.Default.evar~loc(name^"_aux")inletnew_substitution=(core_type_of_type_declarationtype_decl,substi)in(* TODO this should be bundled into a module *)letupdated_orig_substitutions=letopenCCList.Infixinlet+typ,expr=substitutionsinletorig=decoder_evar_of_type_decltype_declin(typ,apply_substitution~orig~substiexpr)innew_substitution::updated_orig_substitutions|Nonrecursive->substitutionsinletimple=implementation_generator~loc~rec_flag:Recursive~substitutionstype_declinletargs=(* If we are on the last decoder, it needs to take itself in as a param *)ifrest=[]then[var]elseList.map(funtype_decl->letname=to_decoder_nametype_decl.ptype_name.txtinpvar~loc:type_decl.ptype_name.locname)restinletimple_as_lambda=pexp_fun_multiarg~locimpleargsinletdec=[%strilet[%pvar]=[%eimple_as_lambda]]inletsubsti=pexp_apply~loc(evar~loc(to_decoder_nametype_decl.ptype_name.txt))(List.map(fundecl->(Nolabel,evar~loc(to_decoder_namedecl.ptype_name.txt)))rest)inletnew_substitution=(core_type_of_type_declarationtype_decl,substi)in(* TODO this should be bundled into a module *)letupdated_orig_substitutions=letopenCCList.Infixinlet+typ,expr=substitutionsinletorig=decoder_evar_of_type_decltype_declin(typ,apply_substitution~orig~substiexpr)inletsubstitutions=new_substitution::updated_orig_substitutionsindec::mutual_rec_fun_gen~loc~substitutionsrest|[]->[]letrecfix_mutual_rec_funs~loctype_decls=letopenAst_builder.Defaultinmatchtype_declswith|[]->[]|[type_decl]->letvar_p=decoder_pvar_of_type_decltype_declinletvar_e=decoder_evar_of_type_decltype_declin[[%strilet[%pvar_p]=D.fix[%evar_e]]]|type_decl::rest->letvar_p=decoder_pvar_of_type_decltype_declinletvar_e=decoder_evar_of_type_decltype_declinletargs=List.map(fundecl->(Nolabel,decoder_evar_of_type_decldecl))restinletappli=pexp_apply~locvar_eargsinletdec=[%strilet[%pvar_p]=[%eappli]]in(* TODO: inefficient list append *)fix_mutual_rec_funs~locrest@[dec]letstr_gens~(loc:location)~(path:label)((rec_flag:rec_flag),type_decls):structure_itemlist=let_path=pathinmatch(really_recursiverec_flagtype_decls,type_decls)with|Nonrecursive,_->List.(flatten(map(single_type_decoder_gen~loc~rec_flag)type_decls))|Recursive,[type_decl]->wrap_27@@single_type_decoder_gen~loc~rec_flagtype_decl|Recursive,_type_decls->wrap_27@@mutual_rec_fun_gen~substitutions:[]~loctype_decls@fix_mutual_rec_funs~loctype_decls