123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119openMigrate_parsetree.Ast_408openParsetreeopenAst_mapperopenAst_helperopenLocation(* Removes all occurences of the character '_' to make the 'parse'
function of the module being used more resilient *)letremove__s=letnb_occ=ref0inString.iter(function'_'->incrnb_occ|_->())s;lets'=Bytes.make(String.lengths-!nb_occ)' 'inletnb_cur=ref0inString.iteri(funi->function|'_'->incrnb_cur|c->Bytes.sets'(i-!nb_cur)c)s;Bytes.to_strings'(* given a module name [Mn] and a function name [fn], builds the identifier
[Mn.fn] *)letidfnameloc={pexp_desc=Pexp_ident{txt=Lidentfname;loc};pexp_loc_stack=[];pexp_loc=loc;pexp_attributes=[];}(* given an ast fragment representing a string 'c', builds the ast
fragment for '(fname c)' *)letparsecfnameloc=letc=remove__cinletname=idfnamelocinExp.apply~loc:locname[Nolabel,Exp.constant(Pconst_string(c,None))](* replaces litterals [lit] by [fname "lit"] *)letparse_mappermodefname=letreplaceconstloc=matchconst,modewith|Pconst_integer(c,None),("parse.all"|"parse.int")|Pconst_float(c,None),("parse.all"|"parse.float")->parsecfnameloc|c,_->Exp.constant~loccinlethandlemapper=function|{pexp_desc=Pexp_constantx;pexp_loc;_}->replacexpexp_loc|x->default_mapper.exprmapperxin{default_mapperwithexpr=handle}(* can we handle the given attribute? *)letcheck_attra=matcha.attr_name.txtwith|"parse.int"|"parse.float"|"parse.all"->true|_->false(* given the payload of an attribute, computes the name of the
function to be used for the parsing of litterals *)letget_fnamepayloadloc=matchpayloadwith|PStr[]->"of_string"|PStr[{pstr_desc=Pstr_eval({pexp_desc=Pexp_apply({pexp_desc=Pexp_ident{txt=Lident"using";_};_},[(Nolabel,{pexp_desc=Pexp_ident{txt=Lidentfname;_};_})]);_},_);_}]->fname|_->Format.printf"%a\n%!"print_locloc;failwith"wrong payload for parse attribute. Should be \"using ident\""(* get the name of the parsing utility using the payload, and removes
from the attributes list the one that was used. We keep the others
potential attributes to not interfere with other PPXs. Raises
Not_found if no [@parse] attribute was found. *)letdeal_with_attrattrs=letattr=List.findcheck_attrattrsinletfname=get_fnameattr.attr_payloadattr.attr_locinletofs=parse_mapperattr.attr_name.txtfnameinofs,List.filter((<>)attr)attrs(* when a [let open[parse.int] M in e] is met,
rewrites [e] using parse_mapper *)letopen_wide_mapper=lethandle_exprmapperexpr=matchexpr.pexp_descwith|Pexp_open(op,exp)->(tryletmapper,attrs=deal_with_attrexpr.pexp_attributesinletexp'=mapper.exprmapperexpinletope=Pexp_open(op,exp')in{exprwithpexp_desc=ope;pexp_attributes=attrs}withNot_found->expr)|_->default_mapper.exprmapperexprinlethandle_strmapper=(* if one str_it is an open[@parse...], then the next str_it will
be mapped using parse_mapper. Otherwise it keeps using the same mapper *)letrecaux(res,map)=function|[]->List.revres|({pstr_desc=Pstr_openopd;_}ash)::tl->(tryletmap',attrs=deal_with_attropd.popen_attributesinletopd'={opdwithpopen_attributes=attrs}inleth'={hwithpstr_desc=Pstr_openopd'}inaux(h'::res,map')tlwithNot_found->aux(h::res,map)tl)|h::tl->leth'=map.structure_item{mapwithexpr=handle_expr}h|>map.structure_itemmapinaux(h'::res,map)tlinaux([],mapper)in{default_mapperwithexpr=handle_expr;structure=handle_str}let()=letopenMigrate_parsetreeinDriver.register~name:"ppx_openwide"~args:[]Versions.ocaml_408(fun_config_cookies->open_wide_mapper)