1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606(* Yoann Padioleau
*
* Copyright (C) 2019 r2c
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public License
* version 2.1 as published by the Free Software Foundation, with the
* special exception on linking described in file license.txt.
*
* 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 file
* license.txt for more details.
*)openCommon(* A is the pattern, and B the concrete source code. For now
* we both use the same module but they may differ later
* as the expressivity of the pattern language grows.
*
* subtle: use 'b' for to report errors, 'a' is the sgrep pattern and it
* has no file information usually.
*)moduleA=Ast_genericmoduleB=Ast_genericmoduleMV=Metavars_genericmoduleAst=Ast_genericmoduleLib=Lib_ast(*****************************************************************************)(* Prelude *)(*****************************************************************************)(* AST generic vs AST generic code matcher.
*
* This module allows to match some AST elements against other AST elements in
* a flexible way, providing a kind of grep but at a syntactical level.
*
* Most of the boilerplate code was generated by
* $ pfff/meta/gen_code -matcher_gen_all
* using OCaml pad-style reflection (see commons/ocaml.ml) on
* h_program-lang/ast_generic.ml.
*
* See pfff/matcher/fuzzy_vs_fuzzy.ml for another approach.
*
* There are four main features allowing a "pattern" to match some "code":
* - metavariables can match anything
* - '...' can match any sequence
* - simple constructs match complex constructs having more details
* (e.g., the absence of attribute in a pattern will still match functions
* having many attributes), "less-is-more"
* - we do not care about differences in spaces/indentations/comments.
* we work at the AST-level.
*)(*****************************************************************************)(* Globals *)(*****************************************************************************)letverbose=reffalse(*****************************************************************************)(* Helpers *)(*****************************************************************************)letpr2,_pr2_once=Common2.mk_pr2_wrappersverbose(*****************************************************************************)(* Types *)(*****************************************************************************)(* ------------------------------------------------------------------------*)(* Combinators history *)(* ------------------------------------------------------------------------*)(*
* version0:
* type ('a, 'b) matcher = 'a -> 'b -> bool
*
* This just lets you know if you matched something.
*
* version1:
* type ('a, 'b) matcher = 'a -> 'b -> unit -> ('a, 'b) option
*
* The Maybe monad.
*
* version2:
* type ('a, 'b) matcher = 'a -> 'b -> binding -> binding list
*
* Why not returning a binding option ? because we need sometimes
* to return multiple possible bindings for one matching code.
* For instance with the pattern do 'f(..., X, ...)', X could be binded
* to different parts of the code.
*
* Note that the empty list means a match failure.
*
* version3:
* type ('a, 'b) matcher = 'a -> 'b -> tin -> ('a,'b) tout
*
* version4: back to simpler
* type ('a, 'b) matcher = 'a -> 'b -> tin -> tout
*)(* tin is for 'type in' and tout for 'type out' *)typetin=MV.metavars_bindingtypetout=MV.metavars_bindinglist(* A matcher is something taking an element A and an element B
* (for this module A will be the AST of the pattern and B
* the AST of the program we want to match over), then some environment
* information tin, and it will return something (tout) that will
* represent a match between element A and B.
*)type('a,'b)matcher='a->'b->tin->tout(* The >>= combinator below allow you to configure the matching process
* anyway you want. Essentially this combinator takes a matcher,
* another matcher, and returns a matcher that combine the 2
* matcher arguments.
*
* In the case of a simple boolean matcher, you just need to write:
*
* let (>>=) m1 m2 = fun tin ->
* match m1 tin with
* | None -> None
* | Some x ->
* m2 x tin
*)let((>>=):(tin->tout)->(unit->(tin->tout))->(tin->tout))=funm1m2->funtin->(* let's get a list of possible environment match (could be
* the empty list when it didn't match, playing the role None
* had before)
*)letxs=m1tinin(* try m2 on each possible returned bindings *)letxxs=xs|>List.map(funbinding->m2()binding)inList.flattenxxs(* the disjunctive combinator *)let((>||>):(tin->tout)->(tin->tout)->(tin->tout))=funm1m2->funtin->(* CHOICE
let xs = m1 tin in
if null xs
then m2 tin
else xs
*)(* opti? use set instead of list *)m1tin@m2tin(* The classical monad combinators *)let(return:tin->tout)=funtin->[tin]let(fail:tin->tout)=fun_tin->[](* ------------------------------------------------------------------------*)(* Environment *)(* ------------------------------------------------------------------------*)(* pre: both 'a' and 'b' contains only regular code; there are no
* metavariables inside them.
*)letequal_ast_binded_codeab=matcha,bwith|Ast.E_,Ast.E_|Ast.N_,Ast.N_|Ast.S_,Ast.S_->(* Note that because we want to retain the position information
* of the matched code in the environment (e.g. for the -pvar
* sgrep command line argument), we can not just use the
* generic '=' OCaml operator as 'a' and 'b' may represent
* the same code but they will contain leaves in their AST
* with different position information. So before doing
* the comparison we just need to remove/abstract-away
* the line number information in each ASTs.
*)leta=Lib.abstract_position_info_anyainletb=Lib.abstract_position_info_anybina=*=b|_,_->falseletcheck_and_add_metavar_binding((mvar:MV.mvar),valu)=funtin->matchCommon2.assoc_optmvartinwith|Somevalu'->(* Should we use generic_vs_generic itself for comparing the code?
* Hmmm, we can't because it leads to a circular dependencies.
* Moreover here we know both valu and valu' are regular code,
* not patterns, so we can just use the generic '=' of OCaml.
*)ifequal_ast_binded_codevaluvalu'thenSometinelseNone|None->(* first time the metavar is binded, just add it to the environment *)Some(Common2.insert_assoc(mvar,valu)tin)let(envf:(MV.mvarAst.wrap,Ast.any)matcher)=fun(mvar,_imvar)any->funtin->matchcheck_and_add_metavar_binding(mvar,any)tinwith|None->pr2(spf"envf: fail, %s"mvar);failtin|Somenew_binding->pr2(spf"envf: success, %s"mvar);returnnew_bindingletempty_environment()=[](*****************************************************************************)(* Boilerplate code, "AST Generic vs AST Generic" *)(*****************************************************************************)letreturn()=returnletfail()=fail(* ---------------------------------------------------------------------- *)(* stdlib: option, list, ref, either, bool *)(* ---------------------------------------------------------------------- *)let(m_option:('a,'b)matcher->('aoption,'boption)matcher)=funfab->matcha,bwith|None,None->return()|Somexa,Somexb->fxaxb>>=(fun()->return())|None,_|Some_,_->fail()let(m_ref:('a,'b)matcher->('aref,'bref)matcher)=funfab->matcha,bwith{contents=xa},{contents=xb}->fxaxb>>=(fun()->return())letrecm_listfab=matcha,bwith|[],[]->return()|xa::aas,xb::bbs->fxaxb>>=(fun()->m_listfaasbbs>>=(fun()->return()))|[],_|_::_,_->fail()letm_boolab=ifa=bthenreturn()elsefail()letm_stringab=ifa=$=bthenreturn()elsefail()letm_other_xxxab=matcha,bwith|a,bwhena=*=b->return()|_->fail()(* ---------------------------------------------------------------------- *)(* Token *)(* ---------------------------------------------------------------------- *)(* we do not care about position! or differences in space/indent/comment!
* so we can just 'return ()'
*)letm_info_a_b=return()letm_tokab=m_infoabletm_wrapfab=matcha,bwith((xaa,ainfo),(xbb,binfo))->fxaaxbb>>=(fun()->m_infoainfobinfo>>=(fun()->return()))(* ---------------------------------------------------------------------- *)(* Name *)(* ---------------------------------------------------------------------- *)letm_identab=(* metavariable! *)matcha,bwith|(str,tok),bwhenMV.is_metavar_namestr->envf(str,tok)(B.Idb)(* general case *)|(a,b)->(m_wrapm_string)abletm_dotted_nameab=matcha,bwith(* TODO: [$X] should match any list *)(a,b)->(m_listm_ident)abletm_qualified_nameab=matcha,bwith(a,b)->m_dotted_nameabletm_module_nameab=matcha,bwith|A.FileName(a1),B.FileName(b1)->(m_wrapm_string)a1b1>>=(fun()->return())|A.DottedName(a1),B.DottedName(b1)->m_dotted_namea1b1>>=(fun()->return())|A.FileName_,_|A.DottedName_,_->fail()letm_resolved_nameab=matcha,bwith|A.Local,B.Local->return()|A.Param,B.Param->return()|A.Global(a1),B.Global(b1)->m_qualified_namea1b1>>=(fun()->return())|A.ImportedModule(a1),B.ImportedModule(b1)->m_qualified_namea1b1>>=(fun()->return())|A.NotResolved,B.NotResolved->return()|A.Macro,B.Macro->return()|A.EnumConstant,B.EnumConstant->return()|A.Local,_|A.Param,_|A.Global_,_|A.NotResolved,_|A.Macro,_|A.EnumConstant,_|A.ImportedModule_,_->fail()(* start of recursive need *)letrecm_nameab=matcha,bwith|(a1,a2),(b1,b2)->m_identa1b1>>=(fun()->m_id_infoa2b2>>=(fun()->return()))(* ------------------------------------------------------------------------- *)(* Expression *)(* ------------------------------------------------------------------------- *)andm_exprab=matcha,bwith(* $X should not match an IdSpecial otherwise $X(...) could match
* a+b because this is transformed in a Call(IdSpecial Plus, ...)
*)|A.Name((str,_tok),_id_info),B.IdSpecial_whenMV.is_metavar_namestr->fail()(* metavariable! *)|A.Name((str,tok),_id_info),e2whenMV.is_metavar_namestr->envf(str,tok)(B.E(e2))|A.L(a1),B.L(b1)->m_literala1b1>>=(fun()->return())|A.Container(a1,a2),B.Container(b1,b2)->m_container_operatora1b1>>=(fun()->(m_listm_expr)a2b2>>=(fun()->return()))|A.Tuple(a1),B.Tuple(b1)->(m_listm_expr)a1b1>>=(fun()->return())|A.Record(a1),B.Record(b1)->(m_listm_field)a1b1>>=(fun()->return())|A.Constructor(a1,a2),B.Constructor(b1,b2)->m_namea1b1>>=(fun()->(m_listm_expr)a2b2>>=(fun()->return()))|A.Lambda(a1),B.Lambda(b1)->m_function_definitiona1b1>>=(fun()->return())|A.AnonClass(a1),B.AnonClass(b1)->m_class_definitiona1b1>>=(fun()->return())|A.Nop,B.Nop->return()|A.Name(a1),B.Name(b1)->m_namea1b1>>=(fun()->return())|A.IdSpecial(a1),B.IdSpecial(b1)->m_wrapm_speciala1b1>>=(fun()->return())|A.Call(a1,a2),B.Call(b1,b2)->m_expra1b1>>=(fun()->m_argumentsa2b2>>=(fun()->return()))|A.Xml(a1),B.Xml(b1)->m_xmla1b1>>=(fun()->return())|A.Assign(a1,a2),B.Assign(b1,b2)->m_expra1b1>>=(fun()->m_expra2b2>>=(fun()->return()))|A.AssignOp(a1,a2,a3),B.AssignOp(b1,b2,b3)->m_expra1b1>>=(fun()->m_wrapm_arithmetic_operatora2b2>>=(fun()->m_expra3b3>>=(fun()->return())))|A.LetPattern(a1,a2),B.LetPattern(b1,b2)->m_patterna1b1>>=(fun()->m_expra2b2>>=(fun()->return()))|A.ObjAccess(a1,a2),B.ObjAccess(b1,b2)->m_expra1b1>>=(fun()->m_identa2b2>>=(fun()->return()))|A.ArrayAccess(a1,a2),B.ArrayAccess(b1,b2)->m_expra1b1>>=(fun()->m_expra2b2>>=(fun()->return()))|A.Conditional(a1,a2,a3),B.Conditional(b1,b2,b3)->m_expra1b1>>=(fun()->m_expra2b2>>=(fun()->m_expra3b3>>=(fun()->return())))|A.MatchPattern(a1,a2),B.MatchPattern(b1,b2)->m_expra1b1>>=(fun()->(m_listm_action)a2b2>>=(fun()->return()))|A.Yield(a1),B.Yield(b1)->m_expra1b1>>=(fun()->return())|A.Await(a1),B.Await(b1)->m_expra1b1>>=(fun()->return())|A.Cast(a1,a2),B.Cast(b1,b2)->m_type_a1b1>>=(fun()->m_expra2b2>>=(fun()->return()))|A.Seq(a1),B.Seq(b1)->(m_listm_expr)a1b1>>=(fun()->return())|A.Ref(a1),B.Ref(b1)->m_expra1b1>>=(fun()->return())|A.DeRef(a1),B.DeRef(b1)->m_expra1b1>>=(fun()->return())|A.Ellipses(a1),B.Ellipses(b1)->m_toka1b1>>=(fun()->return())|A.OtherExpr(a1,a2),B.OtherExpr(b1,b2)->m_other_expr_operatora1b1>>=(fun()->(m_listm_any)a2b2>>=(fun()->return()))|A.L_,_|A.Container_,_|A.Tuple_,_|A.Record_,_|A.Constructor_,_|A.Lambda_,_|A.AnonClass_,_|A.Nop,_|A.Name_,_|A.IdSpecial_,_|A.Call_,_|A.Xml_,_|A.Assign_,_|A.AssignOp_,_|A.LetPattern_,_|A.ObjAccess_,_|A.ArrayAccess_,_|A.Conditional_,_|A.MatchPattern_,_|A.Yield_,_|A.Await_,_|A.Cast_,_|A.Seq_,_|A.Ref_,_|A.DeRef_,_|A.Ellipses_,_|A.OtherExpr_,_->fail()andm_literalab=matcha,bwith(* '...' on string *)|A.String("...",a),B.String(_s,b)->m_infoab>>=(fun()->return())(* regexp matching *)|A.String(name,info_name),B.String(sb,info_sb)whenname=~"^=~/\\(.*\\)/$"->lets=Common.matched1namein(* TODO
let rex = Pcre.regexp s in
if Pcre.pmatch ~rex sb
*)ifsb=~sthenm_infoinfo_nameinfo_sb>>=(fun()->return())elsefail()|A.String(a1),B.String(b1)->(m_wrapm_string)a1b1>>=(fun()->return())|A.Unit(a1),B.Unit(b1)->m_toka1b1>>=(fun()->return())|A.Bool(a1),B.Bool(b1)->(m_wrapm_bool)a1b1>>=(fun()->return())|A.Int(a1),B.Int(b1)->(m_wrapm_string)a1b1>>=(fun()->return())|A.Float(a1),B.Float(b1)->(m_wrapm_string)a1b1>>=(fun()->return())|A.Char(a1),B.Char(b1)->(m_wrapm_string)a1b1>>=(fun()->return())|A.Regexp(a1),B.Regexp(b1)->(m_wrapm_string)a1b1>>=(fun()->return())|A.Null(a1),B.Null(b1)->m_toka1b1>>=(fun()->return())|A.Undefined(a1),B.Undefined(b1)->m_toka1b1>>=(fun()->return())|A.Unit_,_|A.Bool_,_|A.Int_,_|A.Float_,_|A.Char_,_|A.String_,_|A.Regexp_,_|A.Null_,_|A.Undefined_,_->fail()andm_actionab=matcha,bwith|(a1,a2),(b1,b2)->m_patterna1b1>>=(fun()->m_expra2b2>>=(fun()->return()))andm_arithmetic_operatorab=matcha,bwith|_whena=*=b->return()|_->fail()andm_specialab=matcha,bwith|A.This,B.This->return()|A.Super,B.Super->return()|A.Self,B.Self->return()|A.Parent,B.Parent->return()|A.Eval,B.Eval->return()|A.Typeof,B.Typeof->return()|A.Instanceof,B.Instanceof->return()|A.Sizeof,B.Sizeof->return()|A.New,B.New->return()|A.Concat,B.Concat->return()|A.Spread,B.Spread->return()|A.ArithOp(a1),B.ArithOp(b1)->m_arithmetic_operatora1b1>>=(fun()->return())|A.IncrDecr(a1,a2),B.IncrDecr(b1,b2)->m_boola1b1>>=(fun()->m_boola2b2>>=(fun()->return()))|A.This,_|A.Super,_|A.Self,_|A.Parent,_|A.Eval,_|A.Typeof,_|A.Instanceof,_|A.Sizeof,_|A.New,_|A.Concat,_|A.Spread,_|A.ArithOp_,_|A.IncrDecr_,_->fail()andm_id_infoab=matcha,bwith{A.id_qualifier=a1;id_typeargs=a2;id_resolved=_a3;id_type=a4;},{B.id_qualifier=b1;id_typeargs=b2;id_resolved=_b3;id_type=b4;}->(m_optionm_dotted_name)a1b1>>=(fun()->(m_optionm_type_arguments)a2b2>>=(fun()->(* TODO:
* right now doing import flask in a file means every reference
* to flask.xxx will be tagged with a ImportedEntity, but
* sgrep pattern might use flask.xxx without this tag, which prevents
* matching, hence the comment for now. We need to correctly resolve
* names and always compare with the resolved_name instead of the
* name used in the code (which can be an alias)
*)(* (m_ref m_resolved_name) a3 b3 >>= (fun () -> *)(m_ref(m_optionm_type_))a4b4>>=(fun()->return())))andm_container_operatorab=matcha,bwith|A.Array,B.Array->return()|A.List,B.List->return()|A.Set,B.Set->return()|A.Dict,B.Dict->return()|A.Array,_|A.List,_|A.Set,_|A.Dict,_->fail()andm_other_expr_operator=m_other_xxxandm_xmlab=matcha,bwith(a,b)->(m_listm_any)ab(*---------------------------------------------------------------------------*)(* Arguments list iso *)(*---------------------------------------------------------------------------*)andm_argumentsab=matcha,bwith(a,b)->(m_list__m_argument)abandm_list__m_argument(xsa:A.argumentlist)(xsb:A.argumentlist)=matchxsa,xsbwith|[],[]->return()(* '...', can also match no argument *)|[A.Arg(A.Ellipses_i)],[]->return()|A.Arg(A.Ellipsesi)::xsa,xb::xsb->(* can match nothing *)(m_list__m_argumentxsa(xb::xsb))>||>(* can match one *)(m_list__m_argumentxsaxsb)>||>(* can match more *)(m_list__m_argument((A.Arg(A.Ellipsesi))::xsa)xsb)|A.ArgKwd((s,_tok)asida,ea)::xsa,xsb->(trylet(before,there,after)=xsb|>Common2.split_when(function|A.ArgKwd((s2,_),_)whens=$=s2->true|_->false)in(matchtherewith|A.ArgKwd(idb,eb)->m_identidaidb>>=(fun()->m_expreaeb>>=(fun()->m_list__m_argumentxsa(before@after)>>=(fun()->return())))|_->raiseImpossible)withNot_found->fail())(* the general case *)|xa::aas,xb::bbs->m_argumentxaxb>>=(fun()->m_list__m_argumentaasbbs>>=(fun()->return()))|[],_|_::_,_->fail()andm_argumentab=matcha,bwith|A.Arg(a1),B.Arg(b1)->m_expra1b1>>=(fun()->return())|A.ArgType(a1),B.ArgType(b1)->m_type_a1b1>>=(fun()->return())(* TODO: iso on keyword argument, keyword is optional in pattern *)|A.ArgKwd(a1,a2),B.ArgKwd(b1,b2)->m_identa1b1>>=(fun()->m_expra2b2>>=(fun()->return()))|A.ArgOther(a1,a2),B.ArgOther(b1,b2)->m_other_argument_operatora1b1>>=(fun()->(m_listm_any)a2b2>>=(fun()->return()))|A.Arg_,_|A.ArgKwd_,_|A.ArgType_,_|A.ArgOther_,_->fail()andm_other_argument_operator=m_other_xxx(* ------------------------------------------------------------------------- *)(* Type *)(* ------------------------------------------------------------------------- *)andm_type_ab=matcha,bwith|A.TyBuiltin(a1),B.TyBuiltin(b1)->(m_wrapm_string)a1b1>>=(fun()->return())|A.TyFun(a1,a2),B.TyFun(b1,b2)->(m_listm_type_)a1b1>>=(fun()->m_type_a2b2>>=(fun()->return()))|A.TyApply(a1,a2),B.TyApply(b1,b2)->m_namea1b1>>=(fun()->m_type_argumentsa2b2>>=(fun()->return()))|A.TyVar(a1),B.TyVar(b1)->m_identa1b1>>=(fun()->return())|A.TyArray(a1,a2),B.TyArray(b1,b2)->(m_optionm_expr)a1b1>>=(fun()->m_type_a2b2>>=(fun()->return()))|A.TyPointer(a1),B.TyPointer(b1)->m_type_a1b1>>=(fun()->return())|A.TyTuple(a1),B.TyTuple(b1)->(m_listm_type_)a1b1>>=(fun()->return())|A.TyQuestion(a1),B.TyQuestion(b1)->m_type_a1b1>>=(fun()->return())|A.OtherType(a1,a2),B.OtherType(b1,b2)->m_other_type_operatora1b1>>=(fun()->(m_listm_any)a2b2>>=(fun()->return()))|A.TyBuiltin_,_|A.TyFun_,_|A.TyApply_,_|A.TyVar_,_|A.TyArray_,_|A.TyPointer_,_|A.TyTuple_,_|A.TyQuestion_,_|A.OtherType_,_->fail()andm_type_argumentsab=matcha,bwith(a,b)->(m_listm_type_argument)abandm_type_argumentab=matcha,bwith|A.TypeArg(a1),B.TypeArg(b1)->m_type_a1b1>>=(fun()->return())|A.OtherTypeArg(a1,a2),B.OtherTypeArg(b1,b2)->m_other_type_argument_operatora1b1>>=(fun()->(m_listm_any)a2b2>>=(fun()->return()))|A.TypeArg_,_|A.OtherTypeArg_,_->fail()andm_other_type_operator=m_other_xxxandm_other_type_argument_operator=m_other_xxx(* ------------------------------------------------------------------------- *)(* Attribute *)(* ------------------------------------------------------------------------- *)(* TODO: should sort attributes and allow subset *)andm_attributeab=matcha,bwith|A.Recursive,B.Recursive->return()|A.MutuallyRecursive,B.MutuallyRecursive->return()|A.Static,B.Static->return()|A.Volatile,B.Volatile->return()|A.Extern,B.Extern->return()|A.Public,B.Public->return()|A.Private,B.Private->return()|A.Protected,B.Protected->return()|A.Abstract,B.Abstract->return()|A.Final,B.Final->return()|A.Var,B.Var->return()|A.Let,B.Let->return()|A.Const,B.Const->return()|A.Mutable,B.Mutable->return()|A.Generator,B.Generator->return()|A.Async,B.Async->return()|A.Ctor,B.Ctor->return()|A.Dtor,B.Dtor->return()|A.Getter,B.Getter->return()|A.Setter,B.Setter->return()|A.Variadic,B.Variadic->return()|A.NamedAttr(a1,a2),B.NamedAttr(b1,b2)->m_identa1b1>>=(fun()->(m_listm_any)a2b2>>=(fun()->return()))|A.OtherAttribute(a1,a2),B.OtherAttribute(b1,b2)->m_other_attribute_operatora1b1>>=(fun()->(m_listm_any)a2b2>>=(fun()->return()))|A.Recursive,_|A.MutuallyRecursive,_|A.Static,_|A.Volatile,_|A.Extern,_|A.Public,_|A.Private,_|A.Protected,_|A.Abstract,_|A.Final,_|A.Var,_|A.Let,_|A.Const,_|A.Mutable,_|A.Generator,_|A.Async,_|A.Ctor,_|A.Dtor,_|A.Getter,_|A.Setter,_|A.Variadic,_|A.NamedAttr_,_|A.OtherAttribute_,_->fail()andm_other_attribute_operator=m_other_xxx(* ------------------------------------------------------------------------- *)(* Statement *)(* ------------------------------------------------------------------------- *)andm_stmtab=matcha,bwith(* metavariable! *)|A.ExprStmt(A.Name((str,tok),_id_info)),bwhenMV.is_metavar_namestr->envf(str,tok)(B.Sb)(* '...' can to match any statememt *)|A.ExprStmt(A.Ellipses_i),_b->return()|A.ExprStmt(a1),B.ExprStmt(b1)->m_expra1b1>>=(fun()->return())|A.DefStmt(a1),B.DefStmt(b1)->m_definitiona1b1>>=(fun()->return())|A.DirectiveStmt(a1),B.DirectiveStmt(b1)->m_directivea1b1>>=(fun()->return())(* TODO: ... should also allow a subset of stmts *)|A.Block(a1),B.Block(b1)->(m_listm_stmt)a1b1>>=(fun()->return())|A.If(a1,a2,a3),B.If(b1,b2,b3)->m_expra1b1>>=(fun()->m_stmta2b2>>=(fun()->m_stmta3b3>>=(fun()->return())))|A.While(a1,a2),B.While(b1,b2)->m_expra1b1>>=(fun()->m_stmta2b2>>=(fun()->return()))|A.DoWhile(a1,a2),B.DoWhile(b1,b2)->m_stmta1b1>>=(fun()->m_expra2b2>>=(fun()->return()))|A.For(a1,a2),B.For(b1,b2)->m_for_headera1b1>>=(fun()->m_stmta2b2>>=(fun()->return()))|A.Switch(a1,a2),B.Switch(b1,b2)->m_expra1b1>>=(fun()->(m_listm_case_and_body)a2b2>>=(fun()->return()))|A.Return(a1),B.Return(b1)->m_expra1b1>>=(fun()->return())|A.Continue(a1),B.Continue(b1)->(m_optionm_expr)a1b1>>=(fun()->return())|A.Break(a1),B.Break(b1)->(m_optionm_expr)a1b1>>=(fun()->return())|A.Label(a1,a2),B.Label(b1,b2)->m_labela1b1>>=(fun()->m_stmta2b2>>=(fun()->return()))|A.Goto(a1),B.Goto(b1)->m_labela1b1>>=(fun()->return())|A.Throw(a1),B.Throw(b1)->m_expra1b1>>=(fun()->return())|A.Try(a1,a2,a3),B.Try(b1,b2,b3)->m_stmta1b1>>=(fun()->(m_listm_catch)a2b2>>=(fun()->(m_optionm_finally)a3b3>>=(fun()->return())))|A.Assert(a1,a2),B.Assert(b1,b2)->m_expra1b1>>=(fun()->(m_optionm_expr)a2b2>>=(fun()->return()))|A.OtherStmt(a1,a2),B.OtherStmt(b1,b2)->m_other_stmt_operatora1b1>>=(fun()->(m_listm_any)a2b2>>=(fun()->return()))|A.ExprStmt_,_|A.DefStmt_,_|A.DirectiveStmt_,_|A.Block_,_|A.If_,_|A.While_,_|A.DoWhile_,_|A.For_,_|A.Switch_,_|A.Return_,_|A.Continue_,_|A.Break_,_|A.Label_,_|A.Goto_,_|A.Throw_,_|A.Try_,_|A.Assert_,_|A.OtherStmt_,_->fail()andm_for_headerab=matcha,bwith|A.ForClassic(a1,a2,a3),B.ForClassic(b1,b2,b3)->(m_listm_for_var_or_expr)a1b1>>=(fun()->m_expra2b2>>=(fun()->m_expra3b3>>=(fun()->return())))|A.ForEach(a1,a2),B.ForEach(b1,b2)->m_patterna1b1>>=(fun()->m_expra2b2>>=(fun()->return()))|A.ForClassic_,_|A.ForEach_,_->fail()andm_for_var_or_exprab=matcha,bwith|A.ForInitVar(a1,a2),B.ForInitVar(b1,b2)->m_entitya1b1>>=(fun()->m_variable_definitiona2b2>>=(fun()->return()))|A.ForInitExpr(a1),B.ForInitExpr(b1)->m_expra1b1>>=(fun()->return())|A.ForInitVar_,_|A.ForInitExpr_,_->fail()andm_labelab=matcha,bwith(a,b)->m_identabandm_catchab=matcha,bwith|(a1,a2),(b1,b2)->m_patterna1b1>>=(fun()->m_stmta2b2>>=(fun()->return()))andm_finallyab=matcha,bwith(a,b)->m_stmtabandm_case_and_bodyab=matcha,bwith|(a1,a2),(b1,b2)->(m_listm_case)a1b1>>=(fun()->m_stmta2b2>>=(fun()->return()))andm_caseab=matcha,bwith|A.Case(a1),B.Case(b1)->m_expra1b1>>=(fun()->return())|A.Default,B.Default->return()|A.Case_,_|A.Default,_->fail()andm_other_stmt_operator=m_other_xxx(* ------------------------------------------------------------------------- *)(* Pattern *)(* ------------------------------------------------------------------------- *)andm_patternab=matcha,bwith|A.PatVar(a1),B.PatVar(b1)->m_identa1b1>>=(fun()->return())|A.PatLiteral(a1),B.PatLiteral(b1)->m_literala1b1>>=(fun()->return())|A.PatConstructor(a1,a2),B.PatConstructor(b1,b2)->m_namea1b1>>=(fun()->(m_listm_pattern)a2b2>>=(fun()->return()))|A.PatTuple(a1),B.PatTuple(b1)->(m_listm_pattern)a1b1>>=(fun()->return())|A.PatList(a1),B.PatList(b1)->(m_listm_pattern)a1b1>>=(fun()->return())|A.PatRecord(a1),B.PatRecord(b1)->(m_listm_field_pattern)a1b1>>=(fun()->return())|A.PatKeyVal(a1,a2),B.PatKeyVal(b1,b2)->m_patterna1b1>>=(fun()->m_patterna2b2>>=(fun()->return()))|A.PatUnderscore(a1),B.PatUnderscore(b1)->m_toka1b1>>=(fun()->return())|A.PatDisj(a1,a2),B.PatDisj(b1,b2)->m_patterna1b1>>=(fun()->m_patterna2b2>>=(fun()->return()))|A.PatAs(a1,a2),B.PatAs(b1,b2)->m_patterna1b1>>=(fun()->m_identa2b2>>=(fun()->return()))|A.PatTyped(a1,a2),B.PatTyped(b1,b2)->m_patterna1b1>>=(fun()->m_type_a2b2>>=(fun()->return()))|A.PatWhen(a1,a2),B.PatWhen(b1,b2)->m_patterna1b1>>=(fun()->m_expra2b2>>=(fun()->return()))|A.OtherPat(a1,a2),B.OtherPat(b1,b2)->m_other_pattern_operatora1b1>>=(fun()->(m_listm_any)a2b2>>=(fun()->return()))|A.PatVar_,_|A.PatLiteral_,_|A.PatConstructor_,_|A.PatTuple_,_|A.PatList_,_|A.PatRecord_,_|A.PatKeyVal_,_|A.PatUnderscore_,_|A.PatDisj_,_|A.PatWhen_,_|A.PatAs_,_|A.PatTyped_,_|A.OtherPat_,_->fail()andm_field_patternab=matcha,bwith|(a1,a2),(b1,b2)->m_namea1b1>>=(fun()->m_patterna2b2>>=(fun()->return()))andm_other_pattern_operator=m_other_xxx(* ------------------------------------------------------------------------- *)(* Definitions *)(* ------------------------------------------------------------------------- *)andm_definitionab=matcha,bwith|(a1,a2),(b1,b2)->m_entitya1b1>>=(fun()->m_definition_kinda2b2>>=(fun()->return()))andm_entityab=matcha,bwith{A.name=a1;attrs=a2;type_=a3;tparams=a4;},{B.name=b1;attrs=b2;type_=b3;tparams=b4;}->m_identa1b1>>=(fun()->(m_listm_attribute)a2b2>>=(fun()->(m_optionm_type_)a3b3>>=(fun()->(m_listm_type_parameter)a4b4>>=(fun()->return()))))andm_definition_kindab=matcha,bwith|A.FuncDef(a1),B.FuncDef(b1)->m_function_definitiona1b1>>=(fun()->return())|A.VarDef(a1),B.VarDef(b1)->m_variable_definitiona1b1>>=(fun()->return())|A.ClassDef(a1),B.ClassDef(b1)->m_class_definitiona1b1>>=(fun()->return())|A.TypeDef(a1),B.TypeDef(b1)->m_type_definitiona1b1>>=(fun()->return())|A.ModuleDef(a1),B.ModuleDef(b1)->m_module_definitiona1b1>>=(fun()->return())|A.MacroDef(a1),B.MacroDef(b1)->m_macro_definitiona1b1>>=(fun()->return())|A.Signature(a1),B.Signature(b1)->m_type_a1b1>>=(fun()->return())|A.FuncDef_,_|A.VarDef_,_|A.ClassDef_,_|A.TypeDef_,_|A.ModuleDef_,_|A.MacroDef_,_|A.Signature_,_->fail()andm_type_parameter_constraintab=matcha,bwith|A.Extends(a1),B.Extends(b1)->m_type_a1b1>>=(fun()->return())andm_type_parameter_constraintsab=matcha,bwith(a,b)->(m_listm_type_parameter_constraint)abandm_type_parameterab=matcha,bwith|(a1,a2),(b1,b2)->m_identa1b1>>=(fun()->m_type_parameter_constraintsa2b2>>=(fun()->return()))(* ------------------------------------------------------------------------- *)(* Function (or method) definition *)(* ------------------------------------------------------------------------- *)andm_function_definitionab=matcha,bwith{A.fparams=a1;frettype=a2;fbody=a3;},{B.fparams=b1;frettype=b2;fbody=b3;}->m_parametersa1b1>>=(fun()->(m_optionm_type_)a2b2>>=(fun()->m_stmta3b3>>=(fun()->return())))andm_parametersab=matcha,bwith(a,b)->(m_listm_parameter)abandm_parameterab=matcha,bwith|A.ParamClassic(a1),B.ParamClassic(b1)->m_parameter_classica1b1>>=(fun()->return())|A.ParamPattern(a1),B.ParamPattern(b1)->m_patterna1b1>>=(fun()->return())|A.OtherParam(a1,a2),B.OtherParam(b1,b2)->m_other_parameter_operatora1b1>>=(fun()->(m_listm_any)a2b2>>=(fun()->return()))|A.ParamClassic_,_|A.ParamPattern_,_|A.OtherParam_,_->fail()andm_parameter_classicab=matcha,bwith{A.pname=a1;pdefault=a2;ptype=a3;pattrs=a4;},{B.pname=b1;pdefault=b2;ptype=b3;pattrs=b4;}->m_identa1b1>>=(fun()->(m_optionm_expr)a2b2>>=(fun()->(m_optionm_type_)a3b3>>=(fun()->(m_listm_attribute)a4b4>>=(fun()->return()))))andm_other_parameter_operator=m_other_xxx(* ------------------------------------------------------------------------- *)(* Variable definition *)(* ------------------------------------------------------------------------- *)andm_variable_definitionab=matcha,bwith{A.vinit=a1;vtype=a2;},{B.vinit=b1;vtype=b2;}->(m_optionm_expr)a1b1>>=(fun()->(m_optionm_type_)a2b2>>=(fun()->return()))(* ------------------------------------------------------------------------- *)(* Field definition and use *)(* ------------------------------------------------------------------------- *)andm_fieldab=matcha,bwith|A.FieldVar(a1,a2),B.FieldVar(b1,b2)->m_entitya1b1>>=(fun()->m_variable_definitiona2b2>>=(fun()->return()))|A.FieldMethod(a1,a2),B.FieldMethod(b1,b2)->m_entitya1b1>>=(fun()->m_function_definitiona2b2>>=(fun()->return()))|A.FieldDynamic(a1,a2,a3),B.FieldDynamic(b1,b2,b3)->m_expra1b1>>=(fun()->(m_listm_attribute)a2b2>>=(fun()->m_expra3b3>>=(fun()->return())))|A.FieldSpread(a1),B.FieldSpread(b1)->m_expra1b1>>=(fun()->return())|A.FieldStmt(a1),B.FieldStmt(b1)->m_stmta1b1>>=(fun()->return())|A.FieldVar_,_|A.FieldMethod_,_|A.FieldDynamic_,_|A.FieldSpread_,_|A.FieldStmt_,_->fail()(* ------------------------------------------------------------------------- *)(* Type definition *)(* ------------------------------------------------------------------------- *)andm_type_definitionab=matcha,bwith{A.tbody=a1;},{B.tbody=b1;}->m_type_definition_kinda1b1>>=(fun()->return())andm_type_definition_kindab=matcha,bwith|A.OrType(a1),B.OrType(b1)->(m_listm_or_type)a1b1>>=(fun()->return())|A.AndType(a1),B.AndType(b1)->(m_listm_field)a1b1>>=(fun()->return())|A.AliasType(a1),B.AliasType(b1)->m_type_a1b1>>=(fun()->return())|A.Exception(a1,a2),B.Exception(b1,b2)->m_identa1b1>>=(fun()->(m_listm_type_)a2b2>>=(fun()->return()))|A.OtherTypeKind(a1,a2),B.OtherTypeKind(b1,b2)->m_other_type_kind_operatora1b1>>=(fun()->(m_listm_any)a2b2>>=(fun()->return()))|A.OrType_,_|A.AndType_,_|A.AliasType_,_|A.Exception_,_|A.OtherTypeKind_,_->fail()andm_or_typeab=matcha,bwith|A.OrConstructor(a1,a2),B.OrConstructor(b1,b2)->(m_ident)a1b1>>=(fun()->(m_listm_type_)a2b2>>=(fun()->return()))|A.OrEnum(a1,a2),B.OrEnum(b1,b2)->m_identa1b1>>=(fun()->(m_expr)a2b2>>=(fun()->return()))|A.OrUnion(a1,a2),B.OrUnion(b1,b2)->m_identa1b1>>=(fun()->(m_type_)a2b2>>=(fun()->return()))|A.OtherOr(a1,a2),B.OtherOr(b1,b2)->m_other_or_type_element_operatora1b1>>=(fun()->(m_listm_any)a2b2>>=(fun()->return()))|A.OrConstructor_,_|A.OrEnum_,_|A.OrUnion_,_|A.OtherOr_,_->fail()andm_other_type_kind_operator=m_other_xxxandm_other_or_type_element_operator=m_other_xxx(* ------------------------------------------------------------------------- *)(* Class definition *)(* ------------------------------------------------------------------------- *)andm_class_definitionab=matcha,bwith{A.ckind=a1;cextends=a2;cimplements=a3;cbody=a4;},{B.ckind=b1;cextends=b2;cimplements=b3;cbody=b4;}->m_class_kinda1b1>>=(fun()->(m_listm_type_)a2b2>>=(fun()->(m_listm_type_)a3b3>>=(fun()->(m_listm_field)a4b4>>=(fun()->return()))))andm_class_kindab=matcha,bwith|A.Class,B.Class->return()|A.Interface,B.Interface->return()|A.Trait,B.Trait->return()|A.Class,_|A.Interface,_|A.Trait,_->fail()(* ------------------------------------------------------------------------- *)(* Module definition *)(* ------------------------------------------------------------------------- *)andm_module_definitionab=matcha,bwith{A.mbody=a1;},{B.mbody=b1;}->m_module_definition_kinda1b1>>=(fun()->return())andm_module_definition_kindab=matcha,bwith|A.ModuleAlias(a1),B.ModuleAlias(b1)->m_namea1b1>>=(fun()->return())|A.ModuleStruct(a1,a2),B.ModuleStruct(b1,b2)->(m_optionm_dotted_name)a1b1>>=(fun()->(m_listm_item)a2b2>>=(fun()->return()))|A.OtherModule(a1,a2),B.OtherModule(b1,b2)->m_other_module_operatora1b1>>=(fun()->(m_listm_any)a2b2>>=(fun()->return()))|A.ModuleAlias_,_|A.ModuleStruct_,_|A.OtherModule_,_->fail()andm_other_module_operator=m_other_xxx(* ------------------------------------------------------------------------- *)(* Macro definition *)(* ------------------------------------------------------------------------- *)andm_macro_definitionab=matcha,bwith{A.macroparams=a1;macrobody=a2;},{B.macroparams=b1;macrobody=b2;}->(m_listm_ident)a1b1>>=(fun()->(m_listm_any)a2b2>>=(fun()->return()))(* ------------------------------------------------------------------------- *)(* Directives (Module import/export, macros) *)(* ------------------------------------------------------------------------- *)andm_directiveab=matcha,bwith|A.Import(a1,a2),B.Import(b1,b2)->m_module_namea1b1>>=(fun()->(m_listm_alias)a2b2>>=(fun()->return()))|A.ImportAll(a1,a2),B.ImportAll(b1,b2)->m_module_namea1b1>>=(fun()->(m_optionm_ident)a2b2>>=(fun()->return()))|A.OtherDirective(a1,a2),B.OtherDirective(b1,b2)->m_other_directive_operatora1b1>>=(fun()->(m_listm_any)a2b2>>=(fun()->return()))|A.Import_,_|A.ImportAll_,_|A.OtherDirective_,_->fail()andm_aliasab=matcha,bwith|(a1,a2),(b1,b2)->m_identa1b1>>=(fun()->(m_optionm_ident)a2b2>>=(fun()->return()))andm_other_directive_operator=m_other_xxx(* ------------------------------------------------------------------------- *)(* Toplevel *)(* ------------------------------------------------------------------------- *)andm_itemab=matcha,bwith|A.IStmt(a1),B.IStmt(b1)->m_stmta1b1>>=(fun()->return())|A.IDef(a1),B.IDef(b1)->m_definitiona1b1>>=(fun()->return())|A.IDir(a1),B.IDir(b1)->m_directivea1b1>>=(fun()->return())|A.IStmt_,_|A.IDef_,_|A.IDir_,_->fail()andm_programab=matcha,bwith(a,b)->(m_listm_item)ab(* ------------------------------------------------------------------------- *)(* Any *)(* ------------------------------------------------------------------------- *)andm_anyab=matcha,bwith|A.N(a1),B.N(b1)->m_namea1b1>>=(fun()->return())|A.Di(a1),B.Di(b1)->m_dotted_namea1b1>>=(fun()->return())|A.En(a1),B.En(b1)->m_entitya1b1>>=(fun()->return())|A.E(a1),B.E(b1)->m_expra1b1>>=(fun()->return())|A.S(a1),B.S(b1)->m_stmta1b1>>=(fun()->return())|A.T(a1),B.T(b1)->m_type_a1b1>>=(fun()->return())|A.P(a1),B.P(b1)->m_patterna1b1>>=(fun()->return())|A.Def(a1),B.Def(b1)->m_definitiona1b1>>=(fun()->return())|A.Dir(a1),B.Dir(b1)->m_directivea1b1>>=(fun()->return())|A.I(a1),B.I(b1)->m_itema1b1>>=(fun()->return())|A.Pa(a1),B.Pa(b1)->m_parametera1b1>>=(fun()->return())|A.Ar(a1),B.Ar(b1)->m_argumenta1b1>>=(fun()->return())|A.At(a1),B.At(b1)->m_attributea1b1>>=(fun()->return())|A.Dk(a1),B.Dk(b1)->m_definition_kinda1b1>>=(fun()->return())|A.Pr(a1),B.Pr(b1)->m_programa1b1>>=(fun()->return())|A.Id(a1),B.Id(b1)->m_identa1b1>>=(fun()->return())|A.Id_,_|A.N_,_|A.Di_,_|A.En_,_|A.E_,_|A.S_,_|A.T_,_|A.P_,_|A.Def_,_|A.Dir_,_|A.I_,_|A.Pa_,_|A.Ar_,_|A.At_,_|A.Dk_,_|A.Pr_,_->fail()