123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475(* Copyright (C) 2018 Gabriel Radanne <drupyog@zoho.com>
*
* This library 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, either version 3 of the License, or (at your
* option) any later version, with the OCaml static compilation exception.
*
* This library 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 library. If not, see <http://www.gnu.org/licenses/>.
*)openMigrate_parsetreeopenAst_404letocaml_version=Versions.ocaml_404moduleAC=Ast_convenience_404moduleA=Ast_helpermoduleLoc=Locationletinternal_error~loc=Loc.raise_errorf~loc"Internal error@."letmk_gens=letc=ref0infun()->incrc;Printf.sprintf"%s%d"s!cmoduleTyre=structletmk~locs=AC.evar~loc("Tyre."^s)letmkf~locsl=A.Exp.apply~loc(mk~locs)lletconv~locto_from_t=mkf~loc"conv"[Nolabel,to_;Nolabel,from_;Nolabel,t]letbin~locsab=mkf~locs[Nolabel,a;Nolabel,b]endmoduleRe=structletmk~locs=AC.evar~loc("Re."^s)letmkf~locsl=A.Exp.apply~loc(mk~locs)lletmkfl~locsl=mkf~locs[Nolabel,AC.list~locl]end(** Utilities for captures *)type('a,'b)capture=|No|Namedof'a|Unnamedof'bletreccapturee=letopenRegexpinmatche.Loc.txtwith|Code_->No|Seql->letcs=List.mapcapturelinletl=List.filter(functionNo->false|_->true)csinbeginmatchlwith|[]->No|[c]->c|_->Unnamed()end|Altl->ifList.exists(funx->capturex=No)lthenNoelseUnnamed()|Optt->capturet|Repeat(_,t)->capturet|Nongreedyt->capturet|Capture_->Unnamed()|Capture_as(s,_)->Nameds|Call_->Unnamed()letcapture_singleton=function|No->No|Unnamed()->Unnamed1|Nameds->Named[s](** Simplification of regexps *)letflatten_seq=letrecfe=matche.Loc.txtwith|Regexp.Seql->flattenl|_->[e]andflattenl=List.flatten@@List.mapflinflattenletflatten_alt=letrecfe=matche.Loc.txtwith|Regexp.Altl->flattenl|_->[e]andflattenl=List.flatten@@List.mapflinflattenletextract_re_list~locl=letis_re=function{Loc.txt=Regexp.Code_;_}->true|_->falseinletget=function{Loc.txt=Regexp.Coder;_}->r|_->internal_error~locinifList.for_allis_relthenSome(List.mapgetl)elseNoneletcollapse_ungrouped_seq~locl=letmkseq=function|[]->[]|rl->[Loc.mkloc(Regexp.Code(Re.mkfl"seq"~loc@@List.revrl))loc]inletrecauxacc=function|[]->mkseqacc|{Loc.txt=Regexp.Coder;_}::l->aux(r::acc)l|h::t->mkseqacc@h::aux[]tinmatchaux[]lwith|[]->Regexp.Code(Re.mk~loc"epsilon")|[x]->x.txt|l->Seqlletreccollapse_ungrouped(t:stringRegexp.t)=letloc=t.Loc.locinlete:_Regexp.node=matcht.Loc.txtwith|Regexp.Codee->letf=AC.evar~loc"Re.Perl.re"inlets=A.Exp.constant~loc(A.Const.stringe)inCode(A.Exp.apply~locf[Nolabel,s])|Calllid->Calllid|Capturet->Capture(collapse_ungroupedt)|Capture_as(s,t)->Capture_as(s,collapse_ungroupedt)|Seql->letl=flatten_seq@@List.mapcollapse_ungroupedlincollapse_ungrouped_seq~locl|Altl->letl=flatten_alt@@List.mapcollapse_ungroupedlinbeginmatchextract_re_list~loclwith|Somer->Code(Re.mkfl"alt"~locr)|None->Altlend|Optt->beginmatchcollapse_ungroupedtwith|{Loc.txt=Coder;_}->Code(Re.mkf~loc"opt"[Nolabel,r])|t->Opttend|Repeat({Loc.txt=(i,j);_}asij,t)->beginmatchcollapse_ungroupedtwith|{Loc.txt=Coder;_}->leti=A.Exp.constant(A.Const.inti)inletj=matchjwith|None->AC.constr"None"[]|Somej->AC.constr"Some"[A.Exp.constant(A.Const.intj)]inCode(Re.mkf~loc"repn"[Nolabel,r;Nolabel,i;Nolabel,j])|t->Repeat(ij,t)end|Nongreedyt->beginmatchcollapse_ungroupedtwith|{Loc.txt=Coder;_}->Code(Re.mkf~loc"non_greedy"[Nolabel,r])|t->NongreedytendinLoc.mklocelocletsimplify=collapse_ungrouped(** Converters to/from nested tuples *)letrecmake_nested_tuple_pat~locids=matchidswith|[]->internal_error~loc|[v]->AC.pvar~locv|v::ids->letpat=make_nested_tuple_pat~locidsinA.Pat.tuple~loc[AC.pvar~locv;pat]letrecmake_nested_tuple_expr~locexprs=matchexprswith|[]->internal_error~loc|[e]->e|e::exprs->lettuples=make_nested_tuple_expr~locexprsinA.Exp.tuple~loc[e;tuples]letmake_object_expr~locexprmeths=letrecfexprmeths=matchexpr,methswith|[],[]->[]|expr::exprs,meth::meths->letdecls=fexprsmethsinletdecl=A.Cf.method_~locmethPublic(Cfk_concrete(Fresh,expr))indecl::decls|_,_->internal_error~locinA.Exp.object_~loc(A.Cstr.mk(A.Pat.any~loc())@@fexprmeths)letmake_conv_of_nested_tuple~loc~make_pat~make_expr~idstyre_expr=letfun_to=lettuple_pat=make_nested_tuple_pat~locidsinletlids=List.map(AC.evar~loc)idsinletexpr=make_expr~loclidsinA.Exp.fun_~locNolabelNonetuple_patexprinletfun_from=letobj_pat,subexprs=make_pat~loc()inletexpr=make_nested_tuple_expr~locsubexprsinA.Exp.fun_~locNolabelNoneobj_patexprinTyre.conv~locfun_tofun_fromtyre_exprletmake_conv_object~locmethstyre_expr=letobj_var="v"inletgen=mk_genobj_varinletids=List.init(List.lengthmeths)(fun_->gen())inletmake_expr~loclids=make_object_expr~loclidsmethsinletmake_pat~loc()=letobj=AC.evar~locobj_varinletobj_pat=AC.pvar~locobj_varinletmethsends=List.map(funm->A.Exp.send~locobjm.Loc.txt)methsinobj_pat,methsendsinmake_conv_of_nested_tuple~loc~ids~make_expr~make_pattyre_exprletmake_conv_tuple~locntyre_expr=letgen=mk_gen"v"inletids=List.initn(fun_->gen())inletmake_expr~locl=A.Exp.tuple~loclinletmake_pat~loc()=letplids=List.map(AC.pvar~loc)idsinletelids=List.map(AC.evar~loc)idsinletptuple=A.Pat.tuple~locplidsinptuple,elidsinmake_conv_of_nested_tuple~loc~ids~make_expr~make_pattyre_expr(** Converters to/from nested either types *)letppolys~locx=A.Pat.(variant~locs(Somex))letepolys~locx=A.Exp.(variant~locs(Somex))letmake_nested_either_constr~loc~length~mknx=letrecnested_rights~locnexpr=ifn=0thenexprelsemk"Right"~loc(nested_rights~loc(n-1)expr)inifn=length-1thennested_rights~locnxelsenested_rights~locn(mk"Left"~locx)letmake_match_from_nested~locmk_exprs=letlength=List.lengthmk_exprsinletmake_casenmk_expr=letid="v"inA.Exp.case(make_nested_either_constr~loc~length~mk:ppolyn@@AC.pvar~locid)(mk_expr@@AC.evar~locid)inA.Exp.function_~loc@@List.mapimake_casemk_exprsletmake_match_to_nested~locmk_pats=letlength=List.lengthmk_patsinletmake_casenmk_pat=letid="v"inA.Exp.case(mk_pat@@AC.pvar~locid)(make_nested_either_constr~loc~length~mk:epolyn@@AC.evar~locid)inA.Exp.function_~loc@@List.mapimake_casemk_patsletmake_conv_sum~loccapturestyre_expr=letname_from_capturei=function|No->Loc.raise_errorf~loc"All alternatives branches must have a capturing group."|Unnamed_->Location.mkloc("Alt"^string_of_inti)loc|Nameds->sinletbranchnames=List.mapiname_from_capturecapturesinletfun_to=letexpr_branchs=List.map(fun{Loc.loc;txt}->epoly~loctxt)branchnamesinmake_match_from_nested~locexpr_branchsinletfun_from=letpat_branchs=List.map(fun{Loc.loc;txt}->ppoly~loctxt)branchnamesinmake_match_to_nested~locpat_branchsinTyre.conv~locfun_tofun_fromtyre_expr(** Alternatives *)letrecalt_to_expr~loc=function|[]->internal_error~loc|[e]->e|(e)::exprs->letexprs=alt_to_expr~locexprsinTyre.bin~loc"alt"eexprsletalt_to_conv~loccapturesexprs=letalt_expr=alt_to_expr~locexprsinmake_conv_sum~loccapturesalt_expr(** Sequences *)letrecseq_to_expr~loc=function|[]->internal_error~loc|[capture,e]->capture_singletoncapture,e|(capture,e)::exprs->letcaptures,exprs=seq_to_expr~locexprsinletcaptures,(<&>)=matchcapture,captureswith|c,No->capture_singletonc,Tyre.bin~loc"suffix"|No,c->c,Tyre.bin~loc"prefix"|Unnamed(),Unnamedi->Unnamed(i+1),Tyre.bin~loc"seq"|Nameds,Namedl->Named(s::l),Tyre.bin~loc"seq"|Unnamed_,Named_|Named_,Unnamed_->Loc.raise_errorf~loc"The same sequence must not mix unnamed and named capture groups@."incaptures,e<&>exprsletseq_to_conv~locl=letseq_capture,seq_expr=seq_to_expr~loclinmatchseq_capturewith|No->(* This case should not happen: If simplification was run,
sequence of ungrouped regex would have been collapsed. *)internal_error~loc|Unnamed0|Named[]->internal_error~loc(* No. *)|Unnamed1|Unnamed2|Named[_]->seq_expr|Unnamedi->make_conv_tuple~lociseq_expr|Namedl->make_conv_object~loclseq_expr(** Put everything together *)letrecexpr_of_regex(t:_Regexp.t)=letloc=t.Loc.locinmatcht.Loc.txtwith|Regexp.Coder->Tyre.mkf~loc"regex"[Nolabel,r]|Seql->letseq_itemre=capturere,expr_of_regexreinseq_to_conv~loc@@List.mapseq_iteml|Altl->letexprs=List.mapexpr_of_regexlinletcaptures=List.mapcapturelinalt_to_conv~loccapturesexprs|Optt->Tyre.mkf~loc"opt"[Nolabel,expr_of_regext]|Repeat({Loc.txt=(0,None);_},t)->Tyre.mkf~loc"rep"[Nolabel,expr_of_regext]|Repeat({Loc.txt=(1,None);_},t)->Tyre.mkf~loc"rep1"[Nolabel,expr_of_regext]|Repeat({loc;_},_)->Loc.raise_errorf~loc"Repetitions other than + and * are not implemented."|Nongreedyt->Tyre.mkf~loc"non_greedy"[Nolabel,expr_of_regext]|Capturet->expr_of_regext|Capture_as(_,t)->expr_of_regext|Calllid->A.Exp.identlidletadjust_position~locdelim=let(+~)posi=Lexing.{poswithpos_cnum=pos.pos_cnum+i}inmatchdelimwith|None->loc.Loc.loc_start+~1|Somes->loc.Loc.loc_start+~(String.lengths+2)letexpr_of_string~locsdelim=letpos=adjust_position~locdeliminexpr_of_regex@@simplify@@Regexp.parse_exn~possletrecregexp_of_patternpat=letopenParsetreeinletloc=pat.ppat_locinletre=matchpat.ppat_descwith|Ppat_constant(Pconst_string(s,delim))->letpos=adjust_position~locdelimin(Regexp.parse_exn~poss).txt|Ppat_alias(pat,s)->Regexp.(Capture_as(s,regexp_of_patternpat))|Ppat_or(pat1,pat2)->Regexp.(Alt[regexp_of_patternpat1;regexp_of_patternpat2])|Ppat_any->Regexp.Code".*"|Ppat_varid->Regexp.(Capture_as(id,{loc;txt=Code".*"}))|_->Loc.raise_errorf~loc"This pattern is not a valid tyre pattern."inLoc.mklocrelocletexpr_of_patternpat=letre=simplify@@regexp_of_patternpatinmatchre.txtwith|Seql->letf_itemre=capturere,expr_of_regexreinletcapture_seq,expr=seq_to_expr~loc:re.loc@@List.mapf_itemlincapture_seq,expr|_->capture_singleton(capturere),expr_of_regexreletexpr_of_function~locl=leterr_on_guard=function|None->()|Somee->Loc.raise_errorf~loc:e.Parsetree.pexp_loc"Tyre patterns can not have guards."inletroute_of_case{Parsetree.pc_rhs;pc_guard;pc_lhs}=err_on_guardpc_guard;letloc=pc_lhs.ppat_locinletcapture,re=expr_of_patternpc_lhsinletpvar_of_lid{Loc.loc;txt}=AC.pvar~loctxtinletarg=matchcapturewith|Named[]|Unnamed0->internal_error~loc|No|Unnamed_->A.Pat.any~loc()|Named[lid]->pvar_of_lidlid|Namedl->make_nested_tuple_pat~loc@@List.map(fun{Loc.txt;_}->txt)linlete=AC.func~loc[arg,pc_rhs]inAC.constr~loc"Tyre.Route"[re;e]inletl=List.maproute_of_caselinTyre.mkf~loc"route"[Nolabel,AC.list~locl]openAst_mapperletexprmappere_ext=letopenParsetreeinmatche_ext.pexp_descwith|Pexp_extension({txt="tyre";_},PStr[{pstr_desc=Pstr_eval(e,_);_}])->letloc=e.pexp_locin(matche.pexp_descwith|Pexp_constant(Pconst_string(s,delim))->expr_of_string~locsdelim|Pexp_functionl->expr_of_function~locl|_->Loc.raise_errorf~loc"[%%tyre] is only allowed on constant strings and functions.")|_->default_mapper.exprmappere_extlet()=Driver.register~name:"ppx_regexp.tyre"ocaml_version(fun_config_cookies->{default_mapperwithexpr})