123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895moduleParsetree=Ppxlib.ParsetreemoduleAsttypes=Ppxlib.AsttypesmoduleLongident=Ppxlib.LongidentmoduleLocation=Ppxlib.LocationopenPpxlib.AstopenPpxlib.Ast_helper(** Various misc functions *)letmkloctxtloc={txt;loc}letmkloc_opt?(loc=!default_loc)x=mklocxlocletunit?loc?attrs()=Exp.construct?loc?attrs(mkloc_opt?loc(Longident.Lident"()"))Noneletsequence?loc?attrs=function|[]->unit?loc?attrs()|hd::tl->List.fold_left(fune1e2->Exp.sequence?loc?attrse1e2)hdtlletstr?loc?attrss=Exp.constant?loc?attrs(Const.strings)letint?loc?attrss=Exp.constant?loc?attrs(Const.ints)letpunit?loc?attrs()=Pat.construct?loc?attrs(mkloc_opt?loc(Longident.Lident"()"))Noneletflatmapfl=List.flatten@@List.mapflletget_extension=function|{pexp_desc=Pexp_extension({txt},_)}->txt|_->invalid_arg"Eliom ppx: Should be an extension."letin_contextcrefcfx=letold=!crefincref:=c;letres=fxincref:=old;reslet(%)fgx=f(gx)letexp_add_attrsattre={ewithpexp_attributes=attr}leteid{Location.txt;loc}=Exp.ident~loc{loc;txt=Longident.Lidenttxt}letformat_args=function[]->unit()|[e]->e|l->Exp.tuplelletpat_args=function[]->punit()|[p]->p|l->Pat.tuplel(* We use a strong hash (MD5) of the file name.
We only keep the first 36 bit, which should be well enough: with
256 files, the likelihood of a collision is about one in two
millions.
These bits are encoded using an OCaml-compatible variant of Base
64, as the hash is used to generate OCaml identifiers. *)letfile_hashloc=lets=Digest.stringloc.Location.loc_start.pos_fnameinlete="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789_'"inleto=Bytes.create6inletgp=Char.codes.[p]infori=0to5doletp=i*6/8inletd=10-(i*6mod8)inBytes.setoie.[(((gplsl8)+g(p+1))lsrd)land63]done;fori=0to4do(* Prevent problematic '_ pattern. This confuses our sed
invocation in eliomc. Simply replacing the pattern here is
easier than tightening the sed expression. *)ifBytes.getoi='\''&&Bytes.geto(i+1)='_'thenBytes.setoi'Z'done;Bytes.to_stringoletid_file_hashloc=letprefix="__eliom__compilation_unit_id__"in{Location.loc;txt=prefix^file_hashloc}(** [let __eliom__compilation_unit_id__HASH = "HASH"]
We hoist the file hash at the beginning of each eliom file.
This makes the generated javascript code smaller.
*)letmodule_hash_declarationloc=letid=Pat.var~loc@@id_file_hashlocinStr.value~locNonrecursive[Vb.mk~locid@@str@@file_hashloc](** The first position in a file, if it exists.
We avoid {!Location.input_name}, as it's unreliable when reading multiple files.
*)letfile_positionstr=matchstrwith|{pstr_loc}::_->Location.in_file@@pstr_loc.loc_start.pos_fname|[]->Location.noneletlexing_position~locl=[%expr{Lexing.pos_fname=[%estrl.Lexing.pos_fname];Lexing.pos_lnum=[%eint@@l.Lexing.pos_lnum];Lexing.pos_bol=[%eint@@l.Lexing.pos_bol];Lexing.pos_cnum=[%eint@@l.Lexing.pos_cnum]}]letpositionloc=letstart=loc.Location.loc_startinletstop=loc.Location.loc_startinExp.tuple~loc[lexing_position~locstart;lexing_position~locstop]letis_annotationtxtl=List.exists(funs->txt=s||txt="eliom."^s)l(** Identifiers generation. *)moduleName=structletescaped_ident_fmt:_format6="_eliom_escaped_ident_%Ld"letfragment_ident_fmt:_format6="_eliom_fragment_%s"letinjected_ident_fmt:_format6="_eliom_injected_ident_%6s%d"(* Identifiers for the closure representing a fragment. *)letfragment_num_count=ref0letfragment_num_loc=incrfragment_num_count;Printf.sprintf"%s%d"(file_hash_loc)!fragment_num_countletfragment_identid=Printf.sprintffragment_ident_fmtid(* Globaly unique ident for escaped expression *)(* It's used for type inference and as argument name for the
closure representing the surrounding fragment. *)(* Inside a fragment, same ident share the global ident. *)letescaped_idents=ref[]letreset_escaped_ident()=escaped_idents:=[]letescaped_expr,escaped_ident=letr=ref0Linletmake()=r:=Int64.(addone)!r;Printf.sprintfescaped_ident_fmt!rinletfor_exprloc=mkloc(make())locinletfor_idlocid=lettxt=tryList.associd!escaped_identswithNot_found->letgen_id=make()inescaped_idents:=(id,gen_id)::!escaped_idents;gen_idin{Location.txt;loc}infor_expr,for_idletinjected_expr,injected_ident,reset_injected_ident=letinjected_idents=ref[]inletr=ref0inletgen_identloc=lethash=file_hashlocinincrr;lets=Printf.sprintfinjected_ident_fmthash!rin{Location.txt=s;loc}inletgen_injected_identloc(s:string)=tryList.assocs!injected_identswithNot_found->letgen_id=gen_identlocininjected_idents:=(s,gen_id)::!injected_idents;gen_idandreset()=injected_idents:=[]ingen_ident,gen_injected_ident,resetend(* WARNING: if you change this, also change inferred_type_prefix in
tools/eliomc.ml and ocamlbuild/ocamlbuild_eliom.ml *)letinferred_type_prefix="eliom_inferred_type_"moduleMli=structlettype_file=refNoneletget_type_file()=match!type_filewith|None->Filename.chop_extension!Ocaml_common.Location.input_name^".type_mli"|Somef->fletexists()=match!type_filewithSome_->true|_->falseletsuppress_underscore=letrename=letc=ref0infuns->incrc;Printf.sprintf"an_%s_%d"s!candhas_pfix=letlen=String.lengthinferred_type_prefixinfuns->String.lengths>=len&&String.subs0len=inferred_type_prefixin(objectinheritPpxlib.Ast_traverse.mapassupermethod!core_typety=matchty.ptyp_descwith(* | Ptyp_constr (_, Ast.TyAny _, ty) *)(* | Ptyp_constr (_, ty, Ast.TyAny _) -> ty *)|Ptyp_varvarwhenhas_pfixvar->super#core_type{tywithptyp_desc=Ptyp_var(renamevar)}|_->super#core_typetyend)#core_typeletis_injected_identid=tryScanf.sscanfidName.injected_ident_fmt(fun__->true)withScanf.Scan_failure_->falseletis_escaped_identid=tryScanf.sscanfidName.escaped_ident_fmt(fun_->true)withScanf.Scan_failure_->falseletis_fragment_identid=tryScanf.sscanfidName.fragment_ident_fmt(fun_->true)withScanf.Scan_failure_->falseletget_injected_ident_infoid=Scanf.sscanfidName.injected_ident_fmt(funun->u,n)letget_fragment_type=function|[%type:[%t?typ]Eliom_client_value.fragment]|[%type:[%t?typ]Eliom_client_value.t]->Sometyp|_->Noneletget_bindingsig_item=matchsig_item.psig_descwith|Psig_value{pval_name={txt};pval_type=[%type:[%t?typ]optionref]}->ifis_injected_identtxt||is_escaped_identtxtthenSome(txt,suppress_underscoretyp)elseifis_fragment_identtxtthenmatchget_fragment_typetypwith|Sometyp->Some(txt,suppress_underscoretyp)|None->NoneelseNone|_->Noneletload_filefile=tryletch=open_infileinletitems=Ppxlib.Parse.interface(Lexing.from_channelch)inclose_inch;leth=Hashtbl.create17inletfitem=matchget_bindingitemwith|Some(s,typ)->Hashtbl.addhstyp|None->()inList.iterfitems;hwithSys_errors->Location.raise_errorf~loc:(Location.in_filefile)"Eliom: Error while loading types: %s"sletinferred_sig=lazy(load_file(get_type_file()))letfinderr{Location.txt;loc}=tryHashtbl.find(Lazy.forceinferred_sig)txtwithNot_found->Typ.extension~loc@@Location.Error.to_extension@@Location.Error.make~loc~sub:[](Printf.sprintf"Error: Inferred type of %s not found. You need to regenerate %s."err(get_type_file()))letfind_escaped_ident=find"escaped ident"letfind_injected_ident=find"injected ident"letfind_fragment=find"client value"endmoduleCmo=structletfile=refNoneletexists()=!file<>Noneletrecord_eventseventsevl=letopenInstructinList.iter(funev->matchevwith|{ev_loc={loc_start={Lexing.pos_fname;pos_cnum};loc_end={Lexing.pos_cnum=pos_cnum'}};ev_kind=Event_afterty}->ifpos_cnum'=pos_cnum+1thenHashtbl.addevents(pos_fname,pos_cnum)ty|_->())evlletget_file()=match!filewithSomef->f|None->assertfalseletload()=letfile=get_file()inmatchopen_infilewith|exceptionSys_errors->Location.raise_errorf~loc:(Location.in_filefile)"Eliom: Error while loading types: %s"s|ic->letopenCmo_formatinletbuffer=really_input_stringic(String.lengthConfig.cmo_magic_number)inifbuffer<>Config.cmo_magic_numberthenLocation.raise_errorf~loc:(Location.in_filefile)"Eliom: Error while loading types: not an object file";letcu_pos=input_binary_inticinseek_iniccu_pos;letcu=(input_valueic:compilation_unit)inifcu.cu_debug=0thenLocation.raise_errorf~loc:(Location.in_filefile)"Eliom: Error while loading types: no debugging information";seek_iniccu.cu_debug;letevl=(input_valueic:Instruct.debug_eventlist)inletevents=Hashtbl.create100inrecord_eventseventsevl;close_inic;eventsletevents=lazy(load())letlabel_of_strings=ifs=""thenAsttypes.Nolabelelseifs.[0]='?'thenAsttypes.Optional(String.subs1(String.lengths-1))elseAsttypes.Labelledsletrecident_of_out_identid=letopenOutcometreeinletopenLongidentinmatchidwith|Oide_apply(id,id')->Lapply(ident_of_out_identid,ident_of_out_identid')|Oide_dot(id,nm)->Ldot(ident_of_out_identid,nm)|Oide_ident{printed_name=nm}->Lidentnmletcounter=ref0lettype_of_out_typety=letopenOutcometreeinletopenParsetreeinletmap=Hashtbl.create1inletvarx=tryHashtbl.findmapxwithNot_found->letx'=Printf.sprintf"%s%s_%d"inferred_type_prefixx!counterinincrcounter;Hashtbl.addmapxx';x'inletrectype_of_out_typety=matchtywith|Otyp_var(_,s)->Typ.var(vars)|Otyp_arrow(lab,ty1,ty2)->Typ.arrow(label_of_stringlab)(type_of_out_typety1)(type_of_out_typety2)|Otyp_tupletyl->Typ.tuple(List.maptype_of_out_typetyl)|Otyp_constr(id,tyl)->Typ.constr(mkloc(ident_of_out_identid)Location.none)(List.maptype_of_out_typetyl)|((Otyp_object{fields;open_row})[@ifocaml_version>=(5,1,0)])->letfields=List.map(fun(label,ty)->{pof_desc=Otag(mkloclabelLocation.none,type_of_out_typety);pof_loc=Location.none;pof_attributes=[]})fieldsinTyp.object_fields(ifopen_rowthenOpenelseClosed)|((Otyp_object(fields,rest))[@ifocaml_version<(5,1,0)])->letfields=List.map(fun(label,ty)->{pof_desc=Otag(mkloclabelLocation.none,type_of_out_typety);pof_loc=Location.none;pof_attributes=[]})fieldsinTyp.object_fields(ifrest=NonethenClosedelseOpen)|((Otyp_class(id,tyl))[@ifocaml_version>=(5,1,0)])->Typ.class_(mkloc(ident_of_out_identid)Location.none)(List.maptype_of_out_typetyl)|((Otyp_class(_,id,tyl))[@ifocaml_version<(5,1,0)])->Typ.class_(mkloc(ident_of_out_identid)Location.none)(List.maptype_of_out_typetyl)|((Otyp_alias{aliased;alias})[@ifocaml_version>=(5,1,0)])->Typ.alias(type_of_out_typealiased)(varalias)|((Otyp_alias(ty,s))[@ifocaml_version<(5,1,0)])->Typ.alias(type_of_out_typety)(vars)|((Otyp_variant(Ovar_typty,closed,tags))[@ifocaml_version>=(5,1,0)])->Typ.variant[Rf.mk(Rinherit(type_of_out_typety))](ifclosedthenClosedelseOpen)tags|((Otyp_variant(_,Ovar_typty,closed,tags))[@ifocaml_version<(5,1,0)])->Typ.variant[Rf.mk(Rinherit(type_of_out_typety))](ifclosedthenClosedelseOpen)tags|((Otyp_variant(Ovar_fieldslst,closed,tags))[@ifocaml_version>=(5,1,0)])->letrow_fields=List.map(fun(label,const,tyl)->Rf.mk(Rtag(mkloclabelLocation.none,const,List.maptype_of_out_typetyl)))lstinTyp.variantrow_fields(ifclosedthenClosedelseOpen)tags|((Otyp_variant(_,Ovar_fieldslst,closed,tags))[@ifocaml_version<(5,1,0)])->letrow_fields=List.map(fun(label,const,tyl)->Rf.mk(Rtag(mkloclabelLocation.none,const,List.maptype_of_out_typetyl)))lstinTyp.variantrow_fields(ifclosedthenClosedelseOpen)tags|Otyp_poly(sl,ty)->Typ.poly(List.map(funv->mkloc(varv)Location.none)sl)(type_of_out_typety)|Otyp_abstract|Otyp_open|Otyp_sum_|Otyp_manifest_|Otyp_record_|Otyp_module_|Otyp_attribute_|Otyp_stuff_->assertfalseintype_of_out_typetylettypty=letty=Printtyp.tree_of_type_schemetyintype_of_out_typetyletfinderrloc=let{Lexing.pos_fname;pos_cnum}=loc.Location.loc_startintrytyp(Hashtbl.find(Lazy.forceevents)(pos_fname,pos_cnum))withNot_found->Typ.extension~loc@@Location.Error.to_extension@@Location.Error.make~loc~sub:[](Printf.sprintf"Error: Inferred type of %s not found. You need to regenerate %s."err(get_file()))letfind_escaped_ident=find"escaped ident"letfind_injected_ident=find"injected ident"letfind_fragmentloc=matchMli.get_fragment_type(find"client value"loc)with|Somety->ty|None->assertfalseend(** Context convenience module. *)moduleContext=structtypeserver=[`Server|`Shared]typeclient=[`Client|`Shared]letof_string=function|"server"|"server.start"|"eliom.server"|"eliom.server.start"->`Server|"shared"|"shared.start"|"eliom.shared"|"eliom.shared.start"->`Shared|"client"|"client.start"|"eliom.client"|"eliom.client.start"->`Client|_->invalid_arg"Eliom ppx: Not a context"typeescape_inject=[`Escaped_valueofserver|`Injectionofclient]typet=[`Server(* [%%server ... ] *)|`Client(* [%%client ... ] *)|`Shared(* [%%shared ... ] *)|`Fragmentofserver*bool(* [%client ... ] *)|`Escaped_valueofserver(* [%shared ~%( ... ) ] *)|`Injectionofclient(* [%%client ~%( ... ) ] *)]endletdriver_args=[("-type",Arg.String(funtype_file->Mli.type_file:=Sometype_file),"FILE Load inferred types from FILE.");("-notype",Arg.Unit(fun()->Mli.type_file:=None)," Unset explicitly set path from which to load inferred types.");("-server-cmo",Arg.String(funfile->Cmo.file:=Somefile),"FILE Load inferred types from server cmo file FILE.")]let()=List.iter(fun(key,spec,doc)->Ppxlib.Driver.add_argkeyspec~doc)driver_args(** Signature of specific code of a preprocessor. *)moduletypePass=sig(** How to handle "client", "shared" and "server" sections for top level structure items. *)valshared_str:bool->structure_item->structure_itemlistvalserver_str:bool->structure_item->structure_itemlistvalclient_str:structure_item->structure_itemlist(** How to handle "client", "shared" and "server" sections for top level signature items. *)valshared_sig:signature_item->signature_itemlistvalclient_sig:signature_item->signature_itemlistvalserver_sig:signature_item->signature_itemlistvalfragment:loc:Location.t->?typ:core_type->context:Context.server->num:string->id:stringLocation.loc->unsafe:bool->expression->expression(** How to handle "[\%client ...]" and "[\%shared ...]" expr. *)valescape_inject:loc:Location.t->?ident:string->context:Context.escape_inject->id:stringLocation.loc->unsafe:bool->expression->expression(** How to handle escaped "~%ident" inside a fragment. *)valprelude:loc->structurevalpostlude:loc->structureend(** These functions try to guess if a given expression will lead to a fragment evaluation
This is not possible in general, this criteria is only syntactic
If the expression cannot have fragments, we don't need to use sections.
Consequently, this function should *never* return false positive.
*)moduleCannot_have_fragment=structletopt_forallp=functionNone->true|Somex->pxletvb_forallpl=letpx=px.pvb_exprinList.for_allplletreclongident=function|Longident.Lident_->true|Longident.Ldot(x,_)->longidentx|Longident.Lapply(_,_)->falseletrecexpressione=matche.pexp_descwith|Pexp_ident_|Pexp_constant_|Pexp_function_|Pexp_lazy_|Pexp_fun_->true|Pexp_newtype(_,e)|Pexp_asserte|Pexp_field(e,_)|Pexp_constraint(e,_)|Pexp_coerce(e,_,_)|Pexp_poly(e,_)|Pexp_try(e,_)->expressione|Pexp_ifthenelse(b,e1,e2)->expressionb&&expressione1&&opt_forallexpressione2|Pexp_sequence(e1,e2)|Pexp_setfield(e1,_,e2)->expressione1&&expressione2|Pexp_arrayl|Pexp_tuplel->List.for_allexpressionl|Pexp_record(l,e)->letpx=expression@@sndxinopt_forallexpressione&&List.for_allpl|Pexp_construct(_,e)|Pexp_variant(_,e)->opt_forallexpressione|Pexp_let(_,l,e)->vb_forallexpressionl&&expressione|Pexp_open(i,e)->module_expri.popen_expr&&expressione|Pexp_letmodule(_,me,e)->module_exprme&&expressione(* We could be more precise on those constructs *)|Pexp_object_|Pexp_while_|Pexp_for_|Pexp_match_|Pexp_pack_->false(* We can't say more using syntactic information. *)|Pexp_extension_|Pexp_send_|Pexp_new_|Pexp_setinstvar_|Pexp_override_|Pexp_apply_|_->falseandmodule_exprx=matchx.pmod_descwith|Pmod_identl->longidentl.txt|Pmod_functor_->true|Pmod_unpacke->expressione|Pmod_constraint(e,_)->module_expre|Pmod_structurel->List.for_allstructure_iteml|Pmod_apply_|_->falseandmodule_bindingm=module_exprm.pmb_exprandstructure_itemx=matchx.pstr_descwith|Pstr_type_|Pstr_typext_|Pstr_exception_|Pstr_modtype_|Pstr_class_|Pstr_class_type_->true|Pstr_eval(e,_)->expressione|Pstr_value(_,vb)->vb_forallexpressionvb|Pstr_primitive_->true|Pstr_modulemb->module_bindingmb|Pstr_recmodulembl->List.for_allmodule_bindingmbl|Pstr_openx->module_exprx.popen_expr|Pstr_includex->module_exprx.pincl_mod|_->falseend(**
Replace shared expression by the equivalent pair.
[ [%share
let x = ... %s ... in
[%client ... %x ... ]
] ]
≡
[ let x = ... s ... in
[%client ... %x ... ]
,
[%client
let x = ... %s ... in
... x ...
]
]
*)moduleShared=structletserver=(objectinheritPpxlib.Ast_traverse.mapassupermethod!expressionexpr=matchexprwith|[%expr[%client[%e?_]]]->expr|[%expr[%client.unsafe[%e?_]]]->expr|[%expr~%[%e?injection_expr]]->injection_expr|_->super#expressionexprend)#expressionletclientexpr=letcontext=ref`Topin(object(self)inheritPpxlib.Ast_traverse.mapassupermethod!expressionexpr=matchexprwith|[%expr[%client[%e?fragment_expr]]]|[%expr[%client.unsafe[%e?fragment_expr]]]->in_contextcontext`Fragmentself#expressionfragment_expr|[%expr~%[%e?injection_expr]]->(match!contextwith`Top->expr|`Fragment->injection_expr)|_->super#expressionexprend)#expressionexprletexprloc~unsafeexpr=letserver_expr=serverexprinletclient_expr=clientexprinifunsafethen[%exprEliom_shared.Value.create[%eserver_expr][%client.unsafe[%eclient_expr]]]else[%exprEliom_shared.Value.create[%eserver_expr][%client[%eclient_expr]]]endmoduleMake(Pass:Pass)=structleteliom_mappercontext=letcontext=ref(context:>Context.t)inobject(self)inheritPpxlib.Ast_traverse.mapassupermethod!expressionexpr=letloc=expr.pexp_locinletattr=expr.pexp_attributesinmatchexpr,!contextwith|{pexp_desc=Pexp_extension({txt},_)},`Clientwhenis_annotationtxt["client";"shared";"client.unsafe";"shared.unsafe"]->letside=get_extensionexprinExp.extension@@Location.Error.to_extension@@Location.Error.make~loc~sub:[](Printf.sprintf"The syntax [%%%s ...] is not allowed inside client code."side)|({pexp_desc=Pexp_extension({txt},_)},(`Fragment_|`Escaped_value_|`Injection_))whenis_annotationtxt["client";"shared";"client.unsafe";"shared.unsafe"]->letside=get_extensionexprinExp.extension@@Location.Error.to_extension@@Location.Error.make~loc~sub:[](Printf.sprintf"The syntax [%%%s ...] can not be nested."side)(* [%shared ... ] *)|({pexp_desc=Pexp_extension({txt},PStr[{pstr_desc=Pstr_eval(side_val,attr')}])},(`Server|`Shared))whenis_annotationtxt["shared";"shared.unsafe"]->letunsafe=is_annotationtxt["shared.unsafe"]inlete=Shared.exprloc~unsafeside_valinself#expression@@exp_add_attrs(attr@attr')e(* [%client ... ] *)|({pexp_desc=Pexp_extension({txt},PStr[{pstr_desc=Pstr_eval(side_val,attr)}])},((`Server|`Shared)asc))whenis_annotationtxt["client";"client.unsafe"]->Name.reset_escaped_ident();letside_val,typ=matchside_valwith|[%expr([%e?cval]:[%t?typ])]->cval,Sometyp|_->side_val,Noneinletnum=Name.fragment_numside_val.pexp_locinletid=mkloc(Name.fragment_identnum)side_val.pexp_locinletunsafe=is_annotationtxt["client.unsafe"]inin_contextcontext(`Fragment(c,unsafe))(Pass.fragment~loc?typ~context:c~num~id~unsafe%self#expression)(exp_add_attrsattrside_val)(* ~%( ... ) ] *)|[%expr~%[%e?inj]],_->(letident=matchinj.pexp_descwith|Pexp_identi->Some(String.concat"_"@@Longident.flatten_exni.txt)|_->Noneinmatch!contextwith|(`Client|`Shared)asc->letid=matchidentwith|Someid->Name.injected_identlocid|None->Name.injected_exprlocinletnew_context=`Injectioncinin_contextcontextnew_context(Pass.escape_inject~loc?ident~context:new_context~id~unsafe:false%self#expression)inj|`Fragment(c,unsafe)->letid=matchidentwith|None->Name.escaped_exprloc|Someid->Name.escaped_identlocidinletnew_context=`Escaped_valuecinin_contextcontextnew_context(Pass.escape_inject~loc?ident~context:new_context~id~unsafe%self#expression)inj|`Server->Location.raise_errorf~loc"The syntax ~%% ... is not allowed inside server code."|`Escaped_value_|`Injection_->Location.raise_errorf~loc"The syntax ~%% ... can not be nested.")|_->super#expressionexprmethod!structure_itemstr=letloc=str.pstr_locinmatchstr.pstr_descwith|Pstr_extension(({txt="server"|"shared"|"client"},_),_)->Location.raise_errorf~loc"Sections are only allowed at toplevel."|_->super#structure_itemstrmethod!signature_itemsig_=letloc=sig_.psig_locinmatchsig_.psig_descwith|Psig_extension(({txt="server"|"shared"|"client"},_),_)->Location.raise_errorf~loc"Sections are only allowed at toplevel."|_->super#signature_itemsig_end(** Toplevel translation *)(** Switch the current context when encountering [%%server] (resp. shared, client)
annotations. Call the eliom mapper and [Pass.server_str] (resp ..) on each
structure item.
*)letdispatch_strcontextstri=(* We must do this before any transformation on the structure. *)letno_fragment=Cannot_have_fragment.structure_itemstriinletf=matchcontextwith|`Server->Pass.server_strno_fragment|`Shared->Pass.shared_strno_fragment|`Client->Pass.client_strinletm=eliom_mappercontextinf@@m#structure_itemstriletdispatch_sigcontextsigi=letf=matchcontextwith|`Server->Pass.server_sig|`Shared->Pass.shared_sig|`Client->Pass.client_siginletm=eliom_mappercontextinf@@m#signature_itemsigilettoplevel_structurecontextstructs=letrecfpstr=letloc=pstr.pstr_locandmaybe_reset_injected_idents=function|`Client|`Shared->Name.reset_injected_ident()|_->()inmatchpstr.pstr_descwith|Pstr_extension(({txt},PStrstrs),_)whenis_annotationtxt["shared.start";"client.start";"server.start"]->ifstrs<>[]then[Str.extension~loc@@Location.Error.to_extension@@Location.Error.make~loc~sub:[](Printf.sprintf"The %%%%%s extension doesn't accept arguments."txt)]else(maybe_reset_injected_idents!context;context:=Context.of_stringtxt;[])|Pstr_extension(({txt},PStrstrs),_)whenis_annotationtxt["shared";"client";"server"]->letc=Context.of_stringtxtinletl=flatmap(dispatch_strc)strsinmaybe_reset_injected_identsc;l|Pstr_include{pincl_mod={pmod_desc=Pmod_structurel;pmod_attributes=[]};pincl_attributes=[]}->flatmapfl|_->dispatch_str!contextpstrinletloc={(file_positionstructs)withloc_ghost=true}in(module_hash_declarationloc::Pass.preludeloc)@flatmapfstructs@Pass.postludeloclettoplevel_signaturecontextsigs=letfpsig=letloc=psig.psig_locinmatchpsig.psig_descwith|Psig_extension(({txt},PStrstrs),_)whenis_annotationtxt["shared.start";"client.start";"server.start"]->ifstrs<>[]then[Sig.extension~loc@@Location.Error.to_extension@@Location.Error.make~loc~sub:[](Printf.sprintf"The %%%%%s extension doesn't accept arguments."txt)]else(context:=Context.of_stringtxt;[])|Psig_extension(({txt},PSigsigs),_)whenis_annotationtxt["shared";"client";"server"]->letc=Context.of_stringtxtinflatmap(dispatch_sigc)sigs|_->dispatch_sig!contextpsiginflatmapfsigsletmapper=letc=ref`ServerinobjectinheritPpxlib.Ast_traverse.mapmethod!structures=toplevel_structurecsmethod!signatures=toplevel_signaturecsendend