123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394openMigrate_parsetreeopenOCaml_404.AstopenAst_mapperopenAst_helperopenAsttypesopenParsetreeopenAst_convenience_404(** {2 Convenient stuff} *)letwith_locf{txt;loc=_loc}=(ftxt)[@metaloc_loc]letdef_loctxt={txt;loc=!default_loc}(** Test if a case is a catchall. *)letis_catchallcase=letrecis_catchall_patp=matchp.ppat_descwith|Ppat_any|Ppat_var_->true|Ppat_alias(p,_)->is_catchall_patp|_->falseincase.pc_guard=None&&is_catchall_patcase.pc_lhs(** Add a wildcard case in there is none. Useful for exception handlers. *)letadd_wildcard_casecases=lethas_wildcard=List.existsis_catchallcasesinifnothas_wildcardthencases@[Exp.case[%pat?exn][%exprLwt.failexn]][@metalocLocation.none]elsecases(** {3 Internal names} *)letlwt_prefix="__ppx_lwt_"(** {2 Here we go!} *)letwarn_let_lwt_reclocattrs=letattr=attribute_of_warningloc"\"let%lwt rec\" is not a recursive Lwt binding"inattr::attrsletdebug=reftrueletlog=reffalseletsequence=reftrueletstrict_seq=reftrue(** let%lwt related functions *)letgen_namei=lwt_prefix^string_of_inti(** [p = x] ≡ [__ppx_lwt_$i = x] *)letgen_bindingsl=letauxibinding={bindingwithpvb_pat=pvar~loc:binding.pvb_expr.pexp_loc(gen_namei)}inList.mapiauxl(** [p = x] and e ≡ [Lwt.bind __ppx_lwt_$i (fun p -> e)] *)letgen_bindse_locle=letrecauxibindings=matchbindingswith|[]->e|binding::t->letname=(* __ppx_lwt_$i, at the position of $x$ *)evar~loc:binding.pvb_expr.pexp_loc(gen_namei)inletfun_=[%expr(fun[%pbinding.pvb_pat]->[%eaux(i+1)t])][@metaloce_loc]inletnew_exp=if!debugthen[%exprLwt.backtrace_bind(funexn->tryraiseexnwithexn->exn)[%ename][%efun_]][@metaloce_loc]else[%exprLwt.bind[%ename][%efun_]][@metaloce_loc]in{new_expwithpexp_attributes=binding.pvb_attributes}inaux0l(** [p = x and p' = x' and ...] ≡
[p, p', ... = Lwt_main.run (
Lwt.bind x (fun __ppx_lwt_$i ->
Lwt.bind x' (fun __ppx_lwt_$i' ->
...
Lwt.return (__ppx_lwt_$i, __ppx_lwt_$i', ...))))] *)letgen_top_bindsvbs=letgen_expvbsi=matchvbswith|{pvb_expr;_}::_rest->if!debugthen[%exprLwt.backtrace_bind(funexn->tryraiseexnwithexn->exn)[%epvb_expr](fun[%ppvar(gen_namei)]->gen_exp_rest(i+1))]else[%exprLwt.bind[%epvb_expr](fun[%ppvar(gen_namei)]->gen_exprest(i+1))]|[]->letrecnamesi=ifi>=0thenevar(gen_namei)::names(i-1)else[]inExp.tuple(namesi)in[Vb.mk(Pat.tuple(vbs|>List.map(fun{pvb_pat;_}->pvb_pat)))[%exprLwt_main.run[%egen_expvbs0]]](** For expressions only *)(* We only expand the first level after a %lwt.
After that, we call the mapper to expand sub-expressions. *)letlwt_expressionmapperexpattributes=default_loc:=exp.pexp_loc;letpexp_attributes=attributes@exp.pexp_attributesinmatchexp.pexp_descwith(* [let%lwt $p$ = $e$ in $e'$] ≡ [Lwt.bind $e$ (fun $p$ -> $e'$)] *)|Pexp_let(Nonrecursive,vbl,e)->letnew_exp=Exp.let_Nonrecursive(gen_bindingsvbl)(gen_bindsexp.pexp_locvble)inmapper.exprmapper{new_expwithpexp_attributes}(* [match%lwt $e$ with $c$] ≡ [Lwt.bind $e$ (function $c$)]
[match%lwt $e$ with exception $x$ | $c$] ≡
[Lwt.try_bind (fun () -> $e$) (function $c$) (function $x$)] *)|Pexp_match(e,cases)->letexns,cases=cases|>List.partition(function|{pc_lhs=[%pat?exception[%p?_]];_}->true|_->false)inletexns=exns|>List.map(function|{pc_lhs=[%pat?exception[%p?pat]];_}ascase->{casewithpc_lhs=pat}|_->assertfalse)inletexns=add_wildcard_caseexnsinletnew_exp=matchexnswith|[]->[%exprLwt.bind[%ee][%eExp.function_cases]]|_->[%exprLwt.try_bind(fun()->[%ee])[%eExp.function_cases][%eExp.function_exns]]inmapper.exprmapper{new_expwithpexp_attributes}(* [assert%lwt $e$] ≡
[try Lwt.return (assert $e$) with exn -> Lwt.fail exn] *)|Pexp_asserte->letnew_exp=[%exprtryLwt.return(assert[%ee])withexn->Lwt.failexn]inmapper.exprmapper{new_expwithpexp_attributes}(* [while%lwt $cond$ do $body$ done] ≡
[let rec __ppx_lwt_loop () =
if $cond$ then Lwt.bind $body$ __ppx_lwt_loop
else Lwt.return_unit
in __ppx_lwt_loop]
*)|Pexp_while(cond,body)->letnew_exp=[%exprletrec__ppx_lwt_loop()=if[%econd]thenLwt.bind[%ebody]__ppx_lwt_loopelseLwt.return_unitin__ppx_lwt_loop()]inmapper.exprmapper{new_expwithpexp_attributes}(* [for%lwt $p$ = $start$ (to|downto) $end$ do $body$ done] ≡
[let __ppx_lwt_bound = $end$ in
let rec __ppx_lwt_loop $p$ =
if $p$ COMP __ppx_lwt_bound then Lwt.return_unit
else Lwt.bind $body$ (fun () -> __ppx_lwt_loop ($p$ OP 1))
in __ppx_lwt_loop $start$]
*)|Pexp_for({ppat_desc=Ppat_varp_var;_}asp,start,bound,dir,body)->letcomp,op=matchdirwith|Upto->evar">",evar"+"|Downto->evar"<",evar"-"inletp'=with_loc(funs->evars)p_varinletexp_bound=[%expr__ppx_lwt_bound][@metalocbound.pexp_loc]inletpat_bound=[%pat?__ppx_lwt_bound][@metalocbound.pexp_loc]inletnew_exp=[%exprlet[%ppat_bound]:int=[%ebound]inletrec__ppx_lwt_loop[%pp]=if[%ecomp][%ep'][%eexp_bound]thenLwt.return_unitelseLwt.bind[%ebody](fun()->__ppx_lwt_loop([%eop][%ep']1))in__ppx_lwt_loop[%estart]]inmapper.exprmapper{new_expwithpexp_attributes}(* [try%lwt $e$ with $c$] ≡
[Lwt.catch (fun () -> $e$) (function $c$)]
*)|Pexp_try(expr,cases)->letcases=add_wildcard_casecasesinletnew_exp=if!debugthen[%exprLwt.backtrace_catch(funexn->tryraiseexnwithexn->exn)(fun()->[%eexpr])[%eExp.function_cases]]else[%exprLwt.catch(fun()->[%eexpr])[%eExp.function_cases]]inmapper.exprmapper{new_expwithpexp_attributes}(* [if%lwt $c$ then $e1$ else $e2$] ≡
[match%lwt $c$ with true -> $e1$ | false -> $e2$]
[if%lwt $c$ then $e1$] ≡
[match%lwt $c$ with true -> $e1$ | false -> Lwt.return_unit]
*)|Pexp_ifthenelse(cond,e1,e2)->lete2=matche2withNone->[%exprLwt.return_unit]|Somee->einletcases=[Exp.case[%pat?true]e1;Exp.case[%pat?false]e2;]inletnew_exp=[%exprLwt.bind[%econd][%eExp.function_cases]]inmapper.exprmapper{new_expwithpexp_attributes}(* [[%lwt $e$]] ≡ [Lwt.catch (fun () -> $e$) Lwt.fail] *)|_->letexp=matchexpwith|{pexp_loc;pexp_desc=Pexp_let(Recursive,_,_);pexp_attributes}->letattr=attribute_of_warningpexp_loc"\"let%lwt rec\" is not a recursive Lwt binding"in{expwithpexp_attributes=attr::pexp_attributes}|_->expinletnew_exp=if!debugthen[%exprLwt.backtrace_catch(funexn->tryraiseexnwithexn->exn)(fun()->[%eexp])Lwt.fail]else[%exprLwt.catch(fun()->[%eexp])Lwt.fail]inmapper.exprmapper{new_expwithpexp_attributes}letmake_loc{Location.loc_start;_}=let(file,line,char)=Location.get_pos_infoloc_startin[%expr([%estrfile],[%eintline],[%eintchar])](**
[Lwt_log.error "message"] ≡
[let __pa_log_section = Lwt_log.Section.main in
if Lwt_log.Error >= (Lwt_log.Section.level __pa_log_section)
then Lwt_log.error ~location:("foo.ml", 1, 0) ~section:__pa_log_section "message"
else Lwt.return_unit];
[Lwt_log.error ~section "message"] ≡
[let __pa_log_section = section in ...].
Additionally, remove debug-level statements if -no-debug is given. **)letlwt_logmapperfnargsattrsloc=letopenLongidentinmatchfnwith|{pexp_desc=Pexp_ident{txt=Ldot(Lident"Lwt_log",func);_};_}->letlen=String.lengthfuncinletfmt=len>=2&&func.[len-2]='_'&&func.[len-1]='f'andign=len>=4&&func.[0]='i'&&func.[1]='g'&&func.[2]='n'&&func.[3]='_'inletlevel=matchfmt,ignwith|false,false->func|true,false->String.subfunc0(len-2)|false,true->String.subfunc4(len-4)|true,true->String.subfunc4(len-6)inletlevel=(String.capitalize[@ocaml.warning"-3"])leveliniflevel="Debug"&&(not!debug)thenletnew_exp=ifignthen[%expr()]else[%exprLwt.return_unit]inmapper.exprmapper{new_expwithpexp_attributes=attrs}elseifList.memlevel["Fatal";"Error";"Warning";"Notice";"Info";"Debug"]thenletargs=List.map(fun(l,e)->l,mapper.exprmappere)argsinletnew_exp=letargs=(Label.labelled"location",make_locloc)::(Label.labelled"section",[%expr__pa_log_section])::List.remove_assoc(Label.labelled"section")argsin[%exprif[%eExp.construct(def_loc(Ldot(Lident"Lwt_log",level)))None]>=Lwt_log.Section.level__pa_log_sectionthen[%eExp.apply(Exp.ident(def_loc(Ldot(Lident"Lwt_log",func))))args]else[%eifignthen[%expr()]else[%exprLwt.return_unit]]]intryletsection=List.assoc(Label.labelled"section")argsin[%exprlet__pa_log_section=[%esection]in[%enew_exp]]withNot_found->[%exprlet__pa_log_section=Lwt_log.Section.mainin[%enew_exp]]elsedefault_mapper.exprmapper(Exp.apply~attrsfnargs)|_->default_mapper.exprmapper(Exp.apply~attrsfnargs)letmapper={default_mapperwithexpr=(funmapperexpr->matchexprwith|[%expr[%lwt[%e?exp]]]->lwt_expressionmapperexpexpr.pexp_attributes(* [($e$)[%finally $f$]] ≡
[Lwt.finalize (fun () -> $e$) (fun () -> $f$)] *)|[%expr[%e?exp][%finally[%e?finally]]]|[%expr[%e?exp][%lwt.finally[%e?finally]]]->letnew_exp=if!debugthen[%exprLwt.backtrace_finalize(funexn->tryraiseexnwithexn->exn)(fun()->[%eexp])(fun()->[%efinally])]else[%exprLwt.finalize(fun()->[%eexp])(fun()->[%efinally])]inmapper.exprmapper{new_expwithpexp_attributes=expr.pexp_attributes@exp.pexp_attributes}|[%expr[%finally[%e?_]]]|[%expr[%lwt.finally[%e?_]]]->raise(Location.Error(Location.errorf~loc:expr.pexp_loc"Lwt's finally should be used only with the syntax: \"(<expr>)[%%finally ...]\"."))|[%expr[%e?lhs]>>[%e?rhs]]ase->if!sequencethenletpat=if!strict_seqthen[%pat?()]else[%pat?_]inletlhs,rhs=mapper.exprmapperlhs,mapper.exprmapperrhsinletop=matche.Parsetree.pexp_descwith|Parsetree.Pexp_apply(op,_)->op|_->assertfalseinif!debugthenAst_helper.Exp.attr[%exprLwt.backtrace_bind(funexn->tryraiseexnwithexn->exn)[%elhs](fun[%ppat]->[%erhs])](Ast_mapper.attribute_of_warningop.Parsetree.pexp_loc"The operator >> is deprecated")elseAst_helper.Exp.attr[%expr(Lwt.bind[%elhs](fun[%ppat]->[%erhs]))](Ast_mapper.attribute_of_warningop.Parsetree.pexp_loc"The operator >> is deprecated")elsedefault_mapper.exprmapperexpr|{pexp_desc=Pexp_apply(fn,args);pexp_attributes;pexp_loc}when!log->default_loc:=pexp_loc;lwt_logmapperfnargspexp_attributespexp_loc|_->default_mapper.exprmapperexpr);structure_item=(funmapperstri->default_loc:=stri.pstr_loc;matchstriwith|[%strilet%lwt[%p?var]=[%e?exp]]->[%strilet[%pvar]=Lwt_main.run[%emapper.exprmapperexp]]|{pstr_desc=Pstr_extension(({txt="lwt";_},PStr[{pstr_desc=Pstr_value(Recursive,_);_}])ascontent,attrs);pstr_loc}->{striwithpstr_desc=Pstr_extension(content,warn_let_lwt_recpstr_locattrs)}|{pstr_desc=Pstr_extension(({txt="lwt";_},PStr[{pstr_desc=Pstr_value(Nonrecursive,vbs);_}]),_);_}->mapper.structure_itemmapper(Str.valueNonrecursive(gen_top_bindsvbs))|x->default_mapper.structure_itemmapperx);}letargs=Arg.(["-no-debug",Cleardebug,"disable debug mode";"-log",Setlog,"enable logging";"-no-log",Clearlog,"disable logging";"-no-sequence",Clearsequence,"disable sequence operator";"-no-strict-sequence",Clearstrict_seq,"allow non-unit sequence operations";])let()=Driver.register~name:"ppx_lwt"~argsVersions.ocaml_404(fun_config_cookies->mapper)