123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841(* Js_of_ocaml library
* http://www.ocsigen.org/js_of_ocaml/
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, with linking exception;
* either version 2.1 of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*)moduleOcaml_ast_mapper=Ast_mapperopenPpxlibopenStdLabelsopenAst_helperopenAsttypesopenParsetreeletnolabel=NolabelexceptionSyntax_errorofLocation.Error.tletmake_exception~loc~substr=Syntax_error(Location.Error.make~loc~substr)letraise_errorf~locfmt=Printf.ksprintf(funstr->make_exception~loc~sub:[]str|>raise)fmtletunflattenl=matchlwith|[]->None|hd::tl->Some(List.fold_left~f:(funps->Longident.Ldot(p,s))~init:(Longident.Lidenthd)tl)letrecsplit_at_dotsspos=tryletdot=String.index_fromspos'.'inString.subs~pos~len:(dot-pos)::split_at_dotss(dot+1)withNot_found->[String.subs~pos~len:(String.lengths-pos)]letparse_lids=letcomponents=split_at_dotss0inletassert_lid=String.iteri~f:(funic->matchi,cwith|0,('a'..'z'|'_')->()|0,_->assertfalse|_,('a'..'z'|'A'..'Z'|'_'|'0'..'9')->()|_->assertfalse)inletassert_uid=String.iteri~f:(funic->matchi,cwith|0,'A'..'Z'->()|0,_->assertfalse|_,('a'..'z'|'A'..'Z'|'_'|'0'..'9')->()|_->assertfalse)inletreccheck=function|[]->assertfalse|""::_->assertfalse|[s]->assert_lids|modul::rest->assert_uidmodul;checkrestincheckcomponents;matchunflattencomponentswith|None->assertfalse|Somev->vletmkloctxtloc={txt;loc}letmknoloctxt={txt;loc=Location.none}letlid?(loc=!default_loc)s=mkloc(parse_lids)locletmkloc_opt?(loc=!default_loc)x=mklocxlocletunit?loc?attrs()=Exp.construct?loc?attrs(mkloc_opt?loc(Longident.Lident"()"))Nonelettuple?loc?attrs=function|[]->unit?loc?attrs()|[x]->x|xs->Exp.tuple?loc?attrsxsletstr?loc?attrss=Exp.constant?loc?attrs(Const.strings)(** Check if an expression is an identifier and returns it.
Raise a Location.error if it's not.
*)letexp_to_string=function|{pexp_desc=Pexp_ident{txt=Longident.Lidents;_};_}->s|{pexp_desc=Pexp_construct({txt=Longident.Lidents;_},None);_}whenString.lengths>0&&s.[0]>='A'&&s.[0]<='Z'->"_"^s|{pexp_loc;_}->raise_errorf~loc:pexp_loc"Javascript methods or attributes can only be simple identifiers."lettyps=Typ.constr(lids)[](** arg1 -> arg2 -> ... -> ret *)letarrowsargsret=List.fold_rightargs~init:ret~f:(fun(l,ty)fun_->Typ.arrowltyfun_)letwrapper=refNoneletmake_str?locs=matchlocwith|None->mknolocs|Someloc->mklocsloc(* [merlin_hide] tells merlin to not look at a node, or at any of its
descendants. *)letmerlin_hide={attr_name={txt="merlin.hide";loc=Location.none};attr_payload=PStr[];attr_loc=Location.none}moduleJs:sigvaltype_:?loc:Ast_helper.loc->string->Parsetree.core_typelist->Parsetree.core_typevalunsafe:?loc:Ast_helper.loc->string->Parsetree.expressionlist->Parsetree.expressionvalfun_:?loc:Ast_helper.loc->string->Parsetree.expressionlist->Parsetree.expressionend=structletjs_dotname=match!wrapperwith|None->"Js."^name|Somem->m^".Js."^nameletjs_unsafe_dotname=js_dot("Unsafe."^name)lettype_?locsargs=Typ.constr?loc(lid?loc(js_dots))argsletapply_~where?locsargs=letargs=List.map~f:(funx->nolabel,x)argsinExp.(apply?loc(ident?loc(lid?loc(wheres)))args)letunsafe=apply_~where:js_unsafe_dotletfun_=apply_~where:js_dotendletunescapelab=iflab=""thenlabelseletlab=iflab.[0]='_'thenString.sublab~pos:1~len:(String.lengthlab-1)elselabintryleti=String.rindexlab'_'inifi=0thenraiseNot_found;String.sublab~pos:0~len:iwithNot_found->labletapp_arge=nolabel,eletinject_arge=Js.unsafe"inject"[e]letinject_argsargs=Exp.array(List.map~f:(fune->Js.unsafe"inject"[e])args)moduleArg:sigtypetvalmake:?label:arg_label->unit->tvalname:t->stringvaltyp:t->core_typevallabel:t->arg_labelvalargs:tlist->(arg_label*core_type)listend=structtypearg={label:arg_label;name:string}typet=argletcount=ref0letmake?(label=nolabel)()=letc=!countinincrcount;{label;name="t"^string_of_intc}letlabelarg=arg.labelletnamearg=arg.namelettyparg=typ(namearg)letargsl=List.map~f:(funx->labelx,typx)lendletjs_dot_t_the_first_argargs=matchargswith|[]->assertfalse|x::xs->(Arg.labelx,Js.type_"t"[Arg.typx])::Arg.argsxs(* uplift : type of the unused value - ties all types together
downlift : types of individual components (arguments and result)
*)letinvoker?(extra_types=[])upliftdownliftbodyarguments=letdefault_loc'=!default_locindefault_loc:=Location.none;letres="res"inlettyp_res=typresinlettwrap=upliftargumentstyp_resinlettfunc_args,tfunc_res=downliftargumentstyp_resin(* Build the main body *)letebody=letidentd=Exp.ident(lid(Arg.named))inletargs=List.map~f:identargumentsinbodyargsinletannotated_ebody=Exp.constraint_ebodytfunc_resin(* Build the function.
The last arguments is just used to tie all types together.
It's unused in the implementation.
{[ fun (t1 : type_of_t1) (t2 : type_of_t2) (_ : uplift_type) -> e]}
*)letlabels_and_pats=List.maparguments~f:(fund->letlabel=Arg.labeldinletpatt=Pat.var(mknoloc(Arg.named))inlabel,patt)inletmake_fun(label,pat)(label',typ)expr=assert(label'=label);Exp.fun_labelNone(Pat.constraint_pattyp)exprinletinvoker=List.fold_right2labels_and_patstfunc_args~f:make_fun~init:(make_fun(nolabel,Pat.any())(nolabel,twrap)annotated_ebody)in(* Introduce all local types:
{[ fun (type res t0 t1 ..) arg1 arg2 -> e ]}
*)letlocal_types=make_strres::List.map(extra_types@arguments)~f:(funx->make_str(Arg.namex))inletresult=List.fold_rightlocal_types~init:invoker~f:Exp.newtypeindefault_loc:=default_loc';resultletopen_tloc=Js.type_~loc"t"[Typ.object_~loc[]Open](* {[ obj##meth x y ]} generates
{[
(
fun (type res a2 a0 a1) ->
fun (a2 : a2 Js.t) ->
fun (a0 : a0) ->
fun (a1 : a1) ->
fun (_ : a2 -> a0 -> a1 -> res Js.meth) ->
(Js.Unsafe.meth_call a2 "meth"
[|(Js.Unsafe.inject a0);
(Js.Unsafe.inject a1)
|] : res)
)
(obj : < .. > Js.t)
x
y
(fun x -> x#meth)
]} *)letmethod_call~loc~apply_locobj(meth,meth_loc)args=letgloc={locwithLocation.loc_ghost=true}inletobj=letgloc={obj.pexp_locwithloc_ghost=true}inExp.constraint_~attrs:[merlin_hide]~loc:glocobj(open_tgloc)inletinvoker=invoker(funargstres->arrows(Arg.argsargs)(Js.type_"meth"[tres]))(funargstres->js_dot_t_the_first_argargs,tres)(funeargs->matcheargswith|[]->assertfalse|eobj::eargs->leteargs=inject_argseargsinJs.unsafe"meth_call"[eobj;str(unescapemeth);eargs])(Arg.make()::List.mapargs~f:(fun(label,_)->Arg.make~label()))inExp.apply~loc:apply_loc{invokerwithpexp_attributes=[merlin_hide]}((app_argobj::args)@[app_arg(Exp.fun_~loc:glocnolabelNone(Pat.var~loc:gloc(mknoloc"x"))(Exp.send~loc(Exp.ident~loc:obj.pexp_loc(lid~loc:obj.pexp_loc"x"))(make_str~loc:meth_locmeth)))])(* {[ obj##.prop ]} generates
{[
(
fun (type res a0) ->
fun (a0 : a0 Js.t) ->
fun (_ : a0 -> < get :res ;.. > Js.gen_prop) ->
(Js.Unsafe.get a0 "prop" : res)
)
(obj : < .. > Js.t)
(fun x -> x#prop)
]} *)letprop_get~locobjprop=letgloc={obj.pexp_locwithLocation.loc_ghost=true}inletobj=Exp.constraint_~loc:glocobj(open_tgloc)inletinvoker=invoker(funargstres->letloc=!default_locinarrows(Arg.argsargs)(Js.type_"gen_prop"[[%type:<get:[%ttres];..>]]))(funargstres->js_dot_t_the_first_argargs,tres)(funeargs->matcheargswith|[]|_::_::_->assertfalse|[only_arg]->Js.unsafe"get"[only_arg;str(unescapeprop)])[Arg.make()]inExp.applyinvoker[app_argobj;app_arg(Exp.fun_~loc:glocnolabelNone(Pat.var~loc:gloc(mknoloc"x"))(Exp.send~loc(Exp.ident~loc:gloc(lid~loc:gloc"x"))(make_str~locprop)))](* {[ obj##.prop := expr ]} generates
{[
(
fun (type res a1 a0) ->
fun (a1 : a1 Js.t) ->
fun (a0 : a0) ->
fun (_ : a1 -> < set :a0 -> unit ;.. > Js.gen_prop) ->
(Js.Unsafe.set a1 "prop" (Js.Unsafe.inject a0) : unit)
)
(obj : < .. > Js.t)
expr
(fun x -> x#prop)
]} *)letprop_set~loc~prop_locobjpropvalue=letgloc={obj.pexp_locwithLocation.loc_ghost=true}inletobj={(Exp.constraint_~loc:glocobj(open_tgloc))withpexp_attributes=[merlin_hide]}inletinvoker=invoker(funargs_tres->matchargswith|[obj;arg]->letloc=!default_locinassert(Arg.labelobj=nolabel);assert(Arg.labelarg=nolabel);arrows[nolabel,Arg.typobj](Js.type_"gen_prop"[[%type:<set:[%tArg.typarg]->unit;..>]])|_->assertfalse)(funargs_tres->letloc=!default_locinjs_dot_t_the_first_argargs,[%type:unit])(function|[obj;arg]->Js.unsafe"set"[obj;str(unescapeprop);inject_argarg]|_->assertfalse)[Arg.make();Arg.make()]inExp.applyinvoker[app_argobj;app_argvalue;app_arg(Exp.fun_~loc:{locwithloc_ghost=true}nolabelNone(Pat.var~loc:gloc(mknoloc"x"))(Exp.send~loc:prop_loc(Exp.ident~loc:obj.pexp_loc(lid~loc:gloc"x"))(make_str~locprop)))](* {[ new%js constr x y ]} generates
{[
(
fun (type res a2 a0 a1) ->
fun (a2 : (a0 -> a1 -> res Js.t) Js.constr) ->
fun (a0 : a0) ->
fun (a1 : a1) ->
fun (_ : unit) ->
(Js.Unsafe.new_obj a2
[|(Js.Unsafe.inject a0);
(Js.Unsafe.inject a1)
|] : res Js.t)
)
constr x y ()
]}
*)(** Instantiation of a class, used by new%js. *)letnew_objectconstrargs=letinvoker=invoker(fun_args_tres->letloc=!default_locin[%type:unit])(funargstres->lettres=Js.type_"t"[tres]inmatchargswith|[]->assertfalse|unit::args->assert(Arg.labelunit=nolabel);letargs=Arg.argsargsin(nolabel,Js.type_"constr"[arrowsargstres])::args,tres)(function|constr::args->Js.unsafe"new_obj"[constr;inject_argsargs]|_->assertfalse)(Arg.make()::List.mapargs~f:(fun(label,_)->Arg.make~label()))inletgloc={constr.locwithloc_ghost=true}inExp.applyinvoker((app_arg(Exp.ident~loc:constr.locconstr)::args)@[app_arg(unit~loc:gloc())])moduleS=Map.Make(String)(** We remove Pexp_poly as it should never be in the parsetree except after a method call.
*)letformat_methbody=matchbody.pexp_descwith|Pexp_poly(e,_)->e|_->body(** Ensure basic sanity rules about fields of a literal object:
- No duplicated declaration
- Only relevant declarations (val and method, for now).
*)moduleProp_kind=structtypet=[`Readonly|`Writeonly|`Readwrite|`Optdef]letprop_typeconstrty=letconstr=matchconstrwith|`Readonly->"readonly_prop"|`Writeonly->"writeonly_prop"|`Readwrite->"prop"|`Optdef->"optdef_prop"inJs.type_constr[ty]letwrap_arg_typeconstrty=matchconstrwith|`Readonly|`Writeonly|`Readwrite->ty|`Optdef->Js.type_"optdef"[ty]endtypefield_desc=|MethofstringAsttypes.loc*Asttypes.private_flag*Asttypes.override_flag*Parsetree.expression*Arg.tlist|ValofstringAsttypes.loc*Prop_kind.t*Asttypes.override_flag*Parsetree.expressionletfilter_mapfl=letl=List.fold_leftl~init:[]~f:(funaccx->matchfxwith|Somex->x::acc|None->acc)inList.revlletpreprocess_literal_objectmappperfields:[`Fieldsoffield_desclist|`Errorof_]=letcheck_nameidnames=lettxt=unescapeid.txtinifS.memtxtnamesthenletid'=S.findtxtnamesin(* We point out both definitions in locations (more convenient for the user). *)letdetailsid=ifid.txt<>txtthenPrintf.sprintf" (normalized to %S)"txtelse""inletsub=[id'.loc,Printf.sprintf"Duplicated val or method %S%s."id'.txt(detailsid')]inmake_exception~loc:id.loc~sub(Printf.sprintf"Duplicated val or method %S%s."id.txt(detailsid))|>raiseelseS.addtxtidnamesinletdrop_prefix~prefixs=letprefix_len=String.lengthprefixinifString.lengths>prefix_len&&String.subs~pos:0~len:prefix_len=prefixthentrue,String.subs~pos:prefix_len~len:(String.lengths-prefix_len)elsefalse,sinletparse_attributex=matchdrop_prefix~prefix:"jsoo."xwith|_,"optdef"->Some`Optdef|_,"writeonly"->Some`Writeonly|_,"readonly"->Some`Readonly|_,"readwrite"->Some`Readwrite|false,_->None|true,_->Some(`Unkownx)inletjsoo_attributes=filter_map(fun{attr_name={txt;_};attr_payload=_;attr_loc=_}->parse_attributetxt)inletf(names,fields)exp=matchexp.pcf_descwith|Pcf_val(id,mut,Cfk_concrete(bang,body))->letnames=check_nameidnamesinletbody=mappperbodyinletkind=matchmut,jsoo_attributesexp.pcf_attributeswith|Immutable,[]->`Readonly|Mutable,[]->`Readwrite|Immutable,[`Readonly]->`Readonly|(Immutable|Mutable),[`Optdef]->`Optdef|(Immutable|Mutable),[`Writeonly]->`Writeonly|(Immutable|Mutable),[`Readwrite]->`Readwrite|(Immutable|Mutable),[`Unkowns]->raise_errorf~loc:exp.pcf_loc"Unkown jsoo attribute ([@@%s])."s|Mutable,[`Readonly]->raise_errorf~loc:exp.pcf_loc"A mutable field cannot be readonly."|_,_::_::_->raise_errorf~loc:exp.pcf_loc"Too many attributes."innames,Val(id,kind,bang,body)::fields|Pcf_method(id,priv,Cfk_concrete(bang,body))->letnames=check_nameidnamesinletbody=format_meth(mappperbody)inletreccreate_meth_tyexp=matchexp.pexp_descwith|Pexp_fun(label,_,_,body)->Arg.make~label()::create_meth_tybody|_->[]inletfun_ty=create_meth_tybodyinnames,Meth(id,priv,bang,body,fun_ty)::fields|_->raise_errorf~loc:exp.pcf_loc"This field is not valid inside a js literal object."intry`Fields(List.rev(snd(List.fold_leftfields~init:(S.empty,[])~f)))withSyntax_errorerror->`Error(Location.Error.to_extensionerror)(* {[ object%js (self)
val readonlyprop = e1
val prop = e2
method meth x = e3
end ]} generates
{[
(
fun (type res a6 a7 a8 a9) ->
fun (a7 : a7) ->
fun (a8 : a8) ->
fun (a9 : res Js.t -> a6 -> a9) ->
fun
(_ :
res Js.t ->
a7 Js.readonly_prop ->
a8 Js.prop ->
(res Js.t -> a6 -> a9 Js.meth) ->
res)
->
(Js.Unsafe.obj
[|("readonlyprop", (Js.Unsafe.inject a7));
("prop", (Js.Unsafe.inject a8));
("meth", (Js.Unsafe.inject (Js.wrap_meth_callback a9)))
|] : res Js.t)
)
e1
e2
(fun self -> fun x -> e3)
(fun self read_only_prop prop meth ->
object
method read_only_prop = read_only_prop
method prop = prop
method meth = meth self
end)
]} *)letliteral_objectself_id(fields:field_desclist)=letname=function|Val(id,_,_,_)->id|Meth(id,_,_,_,_)->idinletbody=function|Val(_,_,_,body)->body|Meth(_,_,_,body,_)->Exp.fun_~loc:{body.pexp_locwithloc_ghost=true}NolabelNoneself_idbodyinletextra_types=List.concat(List.mapfields~f:(function|Val_->[]|Meth(_,_,_,_,l)->l))inletinvoker=invoker~extra_types(funargstres->letargs=List.map2fieldsargs~f:(funfdesc->letret_ty=Arg.typdescinletlabel=Arg.labeldescinmatchfwith|Val(_,constr,_,_)->label,Prop_kind.prop_typeconstrret_ty|Meth(_,_,_,_,args)->(label,arrows((nolabel,Js.type_"t"[tres])::Arg.argsargs)(Js.type_"meth"[ret_ty])))inarrows((nolabel,Js.type_"t"[tres])::args)tres)(funargstres->letargs=List.map2fieldsargs~f:(funfdesc->letret_ty=Arg.typdescinletlabel=Arg.labeldescinmatchfwith|Val(_,constr,_,_)->label,Prop_kind.wrap_arg_typeconstrret_ty|Meth(_,_,_,_,args)->label,arrows((nolabel,Js.type_"t"[tres])::Arg.argsargs)ret_ty)inargs,Js.type_"t"[tres])(funargs->Js.unsafe"obj"[Exp.array(List.map2fieldsargs~f:(funfarg->tuple[str(unescape(namef).txt);inject_arg(matchfwith|Val_->arg|Meth_->Js.fun_"wrap_meth_callback"[arg])]))])(List.mapfields~f:(function|Val_->Arg.make()|Meth(_,_,_,_,_fun_ty)->Arg.make()))inletself="self"inletgloc={!default_locwithLocation.loc_ghost=true}inletfake_object=Exp.object_{pcstr_self=Pat.any~loc:gloc();pcstr_fields=List.mapfields~f:(funf->letloc=(namef).locinletapplye=matchfwith|Val_->e|Meth_->Exp.applye[nolabel,Exp.ident(lid~loc:Location.noneself)]in{pcf_loc=loc;pcf_attributes=[];pcf_desc=Pcf_method(namef,Public,Cfk_concrete(Fresh,apply(Exp.ident~loc(lid~loc:Location.none(namef).txt))))})}inExp.applyinvoker(List.mapfields~f:(funf->app_arg(bodyf))@[app_arg{(List.fold_right(self::List.mapfields~f:(funf->(namef).txt))~init:fake_object~f:(funnamefun_->Exp.fun_~loc:glocnolabelNone(Pat.var~loc:gloc(mknolocname))fun_))withpexp_attributes=[merlin_hide]}])lettransform=object(self)inheritAst_traverse.mapassupermethod!expressionexpr=letprev_default_loc=!default_locindefault_loc:=expr.pexp_loc;let{pexp_attributes;_}=exprinletnew_expr=matchexprwith(* obj##.var *)|[%expr[%e?obj]##.[%e?meth]]->letobj=self#expressionobjinletprop=exp_to_stringmethinletnew_expr=prop_get~loc:meth.pexp_locobjpropinself#expression{new_exprwithpexp_attributes}(* obj##.var := value *)|[%expr[%e?[%expr[%e?obj]##.[%e?meth]]asprop]:=[%e?value]]->letobj=self#expressionobjinletvalue=self#expressionvalueinletprop_loc=prop.pexp_locinletprop=exp_to_stringmethinletnew_expr=prop_set~loc:meth.pexp_loc~prop_locobjpropvalueinself#expression{new_exprwithpexp_attributes}(* obj##(meth arg1 arg2) .. *)|[%expr[%e?obj]##[%e?{pexp_desc=Pexp_apply(meth,args);_}]]->letmeth_str=exp_to_stringmethinletobj=self#expressionobjinletargs=List.mapargs~f:(fun(s,e)->s,self#expressione)inletnew_expr=letloc=(* The method call "obj ## meth" node doesn't really exist. *){expr.pexp_locwithloc_ghost=true}inmethod_call~loc~apply_loc:expr.pexp_locobj(meth_str,meth.pexp_loc)argsinself#expression{new_exprwithpexp_attributes}(* obj##meth arg1 arg2 .. *)|{pexp_desc=Pexp_apply(([%expr[%e?obj]##[%e?meth]]asprop),args);pexp_loc;_}->letmeth_str=exp_to_stringmethinletobj=self#expressionobjinletargs=List.mapargs~f:(fun(s,e)->s,self#expressione)inletnew_expr=method_call~loc:prop.pexp_loc~apply_loc:pexp_locobj(meth_str,meth.pexp_loc)argsinself#expression{new_exprwithpexp_attributes}(* obj##meth *)|[%expr[%e?obj]##[%e?meth]]asexpr->letobj=self#expressionobjinletmeth_str=exp_to_stringmethinletnew_expr=method_call~loc:expr.pexp_loc~apply_loc:expr.pexp_locobj(meth_str,meth.pexp_loc)[]inself#expression{new_exprwithpexp_attributes}(* new%js constr] *)|[%expr[%js[%e?{pexp_desc=Pexp_newconstr;_}]]]->letnew_expr=new_objectconstr[]inself#expression{new_exprwithpexp_attributes}(* new%js constr arg1 arg2 ..)] *)|{pexp_desc=Pexp_apply([%expr[%js[%e?{pexp_desc=Pexp_newconstr;_}]]],args);_}->letargs=List.mapargs~f:(fun(s,e)->s,self#expressione)inletnew_expr=new_objectconstrargsinself#expression{new_exprwithpexp_attributes}(* object%js ... end *)|[%expr[%js[%e?{pexp_desc=Pexp_objectclass_struct;_}]]]->letfields=preprocess_literal_objectself#expressionclass_struct.pcstr_fieldsinletnew_expr=matchfieldswith|`Fieldsfields->literal_objectclass_struct.pcstr_selffields|`Errore->Exp.extensioneinself#expression{new_exprwithpexp_attributes}|_->super#expressionexprindefault_loc:=prev_default_loc;new_exprendlet()=Driver.register_transformation"ppx_js"~impl:transform#structureletmapper=letexpr_exp=Ppxlib_ast.Selected_ast.of_ocamlExpressionexp|>transform#expression|>Ppxlib_ast.Selected_ast.to_ocamlExpressionin{Ocaml_ast_mapper.default_mapperwithexpr}