123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422(****************************************************************************)(* *)(* This file is part of MOPSA, a Modular Open Platform for Static Analysis. *)(* *)(* Copyright (C) 2017-2019 The MOPSA Project. *)(* *)(* 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, either version 3 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, see <http://www.gnu.org/licenses/>. *)(* *)(****************************************************************************)(** AST visitors *)openMopsa_utilsopenExpropenVaropenStmt(** Parts are the direct sub-elements of an AST node *)typeparts={exprs:exprlist;(** child expressions *)stmts:stmtlist;(** child statements *)}(** A structure of an extensible type ['a] is a tuple composed of two elements:
the parts and a builder function.
*)type'astructure=parts*(parts->'a)letleaf(x:'a):'astructure={exprs=[];stmts=[]},(fun_->x)(*==========================================================================*)(** {2 Registration} *)(*==========================================================================*)(** Information record of an AST construct with visitors *)type'avisit_info={compare:'aTypeExt.compare;print:'aTypeExt.print;visit:('a->'astructure)->'a->'astructure;}letexpr_visit_chain=ref(funexp->matchekindexpwith|E_var_->leafexp|E_constant_->leafexp|E_unop(unop,e)->{exprs=[e];stmts=[]},(funparts->{expwithekind=E_unop(unop,List.hdparts.exprs)})|E_binop(binop,e1,e2)->{exprs=[e1;e2];stmts=[]},(funparts->{expwithekind=E_binop(binop,List.hdparts.exprs,List.nthparts.exprs1)})|E_alloc_addr_->leafexp|E_addr_->leafexp|_->Exceptions.panic"expr visitor: unknown expression %a"pp_exprexp)letregister_expr_with_visitor(info:exprvisit_info):unit=register_expr_compareinfo.compare;register_expr_ppinfo.print;expr_visit_chain:=info.visit!expr_visit_chain;()letregister_expr_visitorv:unit=expr_visit_chain:=v!expr_visit_chain;()letstmt_visit_chain:(stmt->stmtstructure)ref=ref(funstmt->matchskindstmtwith|S_program_->Exceptions.panic"visitor of S_program not supported"|S_assign(lhs,rhs)->{exprs=[lhs;rhs];stmts=[]},(function|{exprs=[lhs;rhs]}->{stmtwithskind=S_assign(lhs,rhs)}|_->assertfalse)|S_assumecond->{exprs=[cond];stmts=[]},(function|{exprs=[cond]}->{stmtwithskind=S_assume(cond)}|_->assertfalse)|S_rename(e,e')->{exprs=[e;e'];stmts=[]},(function|{exprs=[e;e']}->{stmtwithskind=S_rename(e,e')}|_->assertfalse)|S_add(e)->{exprs=[e];stmts=[]},(function|{exprs=[e]}->{stmtwithskind=S_add(e)}|_->assertfalse)|S_remove(e)->{exprs=[e];stmts=[]},(function|{exprs=[e]}->{stmtwithskind=S_remove(e)}|_->assertfalse)|S_invalidate(e)->{exprs=[e];stmts=[]},(function|{exprs=[e]}->{stmtwithskind=S_invalidate(e)}|_->assertfalse)|S_forget(e)->{exprs=[e];stmts=[]},(function|{exprs=[e]}->{stmtwithskind=S_forget(e)}|_->assertfalse)|S_project(el)->{exprs=el;stmts=[]},(function|{exprs}->{stmtwithskind=S_project(exprs)})|S_expand(e,el)->{exprs=e::el;stmts=[]},(function|{exprs=e::el}->{stmtwithskind=S_expand(e,el)}|_->assertfalse)|S_fold(e,el)->{exprs=e::el;stmts=[]},(function|{exprs=e::el}->{stmtwithskind=S_fold(e,el)}|_->assertfalse)|S_block(sl,vl)->{exprs=[];stmts=sl},(funparts->{stmtwithskind=S_block(parts.stmts,vl)})|S_breakpoint_->leafstmt|_->Exceptions.panic"stmt_visit_chain: unknown statement")letregister_stmt_with_visitor(info:stmtvisit_info):unit=register_stmt_compareinfo.compare;register_stmt_ppinfo.print;stmt_visit_chain:=info.visit!stmt_visit_chain;()letregister_stmt_visitorv:unit=stmt_visit_chain:=v!stmt_visit_chain;()letstructure_of_expr(expr:expr):exprstructure=!expr_visit_chainexprletstructure_of_stmt(stmt:stmt):stmtstructure=!stmt_visit_chainstmtletis_leaf_expre=letparts,_=structure_of_expreinparts.exprs=[]&&parts.stmts=[]letrecis_atomic_expre=letparts,_=structure_of_expreinparts.stmts=[]&&List.for_allis_atomic_exprparts.exprsletis_atomic_stmts=matchskindswith|S_program_->(* FIXME: as defining visitor of the program is not always
possible, we need here to give a hard-coded answer *)false|_->letparts,_=structure_of_stmtsinparts.stmts=[]&&List.for_allis_atomic_exprparts.exprs(*==========================================================================*)(** {2 Visitors} *)(*==========================================================================*)(** Visitor actions *)type'avisit_action=|Keepof'a(** Keep the result *)|VisitPartsof'a(** Continue visiting the parts of the result *)|Visitof'a(** Iterate the visitor on the result *)letfold_map_list(f:'a->'b->('a*'b))(x0:'a)(l:'blist):('a*'blist)=let(xe,l')=List.fold_left(fun(accx,accl)z->letx,z'=faccxzin(x,z'::accl))(x0,[])lin(xe,List.revl')(** [map_expr fe fs e] transforms the expression [e] into a new one,
by splitting [fe e] into its sub-parts, applying [map_expr fe fs] and
[map_stmt fe fs] on them, and finally gathering the results with
the builder of [fe e].
*)letrecmap_expr(fe:expr->exprvisit_action)(fs:stmt->stmtvisit_action)(e:expr):expr=matchfeewith|Keepe'->e'|Visite'->map_exprfefse'|VisitPartse'->letparts,builder=structure_of_expre'inletexprs'=List.map(map_exprfefs)parts.exprsandstmts'=List.map(map_stmtfefs)parts.stmtsinbuilder{exprs=exprs';stmts=stmts'}(** [map_stmt fe fs s] same as [map_expr] but on statements. *)andmap_stmt(fe:expr->exprvisit_action)(fs:stmt->stmtvisit_action)s:stmt=matchfsswith|Keeps'->s'|Visits'->map_stmtfefss'|VisitPartss'->letparts,builder=structure_of_stmts'inletexprs'=List.map(map_exprfefs)parts.exprsandstmts'=List.map(map_stmtfefs)parts.stmtsinbuilder{exprs=exprs';stmts=stmts'}(** Folding function for expressions *)letrecfold_expr(fe:'a->expr->'avisit_action)(fs:'a->stmt->'avisit_action)x0e=matchfex0ewith|Keepx1->x1|Visitx1->fold_exprfefsx1e|VisitPartsx1->letparts,_=structure_of_expreinletx2=List.fold_left(fold_exprfefs)x1parts.exprsinList.fold_left(fold_stmtfefs)x2parts.stmts(** Folding function for statements *)andfold_stmt(fe:'a->expr->'avisit_action)(fs:'a->stmt->'avisit_action)x0s=matchfsx0swith|Keepx1->x1|Visitx1->fold_stmtfefsx1s|VisitPartsx1->letparts,_=structure_of_stmtsinletx2=List.fold_left(fold_exprfefs)x1parts.exprsinList.fold_left(fold_stmtfefs)x2parts.stmtsletfold_sub_expr(fe:'a->expr->'avisit_action)(fs:'a->stmt->'avisit_action)x0e=letparts,_=structure_of_expreinletx2=List.fold_left(fold_exprfefs)x0parts.exprsinList.fold_left(fold_stmtfefs)x2parts.stmts(** Combination of map and fold for expressions *)letrecfold_map_expr(fme:'a->expr->('a*expr)visit_action)(fms:'a->stmt->('a*stmt)visit_action)(x0:'a)(expr:expr):('a*expr)=matchfmex0exprwith|Keep(x1,expr')->x1,expr'|Visit(x1,expr')->fold_map_exprfmefmsx1expr'|VisitParts(x1,expr')->letparts,builder=structure_of_exprexpr'inletx2,exprs=fold_map_list(funx0(z:expr)->fold_map_exprfmefmsx0z)x1parts.exprsinletx3,(stmts:stmtlist)=fold_map_list(funx0(z:stmt)->fold_map_stmtfmefmsx0z)x2parts.stmtsin(x3,builder{exprs;stmts})(** Combination of map and fold for statements *)andfold_map_stmt(fme:'a->expr->('a*expr)visit_action)(fms:'a->stmt->('a*stmt)visit_action)(x0:'a)(stmt:stmt):('a*stmt)=matchfmsx0stmtwith|Keep(x1,stmt')->x1,stmt'|Visit(x1,stmt')->fold_map_stmtfmefmsx1stmt'|VisitParts(x1,stmt')->letparts,builder=structure_of_stmtstmt'inletx2,exprs=fold_map_list(funx0z->fold_map_exprfmefmsx0z)x1parts.exprsinletx3,stmts=fold_map_list(funx0z->fold_map_stmtfmefmsx0z)x2parts.stmtsin(x3,builder{exprs;stmts})letrecexists_exprfefse=fee||(letparts,_=structure_of_expreinList.exists(exists_exprfefs)parts.exprs||List.exists(exists_stmtfefs)parts.stmts)andexists_stmtfefss=fss||(letparts,_=structure_of_stmtsinList.exists(exists_exprfefs)parts.exprs||List.exists(exists_stmtfefs)parts.stmts)letrecfor_all_exprfefse=fee&&(letparts,_=structure_of_expreinList.for_all(for_all_exprfefs)parts.exprs&&List.for_all(for_all_stmtfefs)parts.stmts)andfor_all_stmtfefss=fss&&(letparts,_=structure_of_stmtsinList.for_all(for_all_exprfefs)parts.exprs&&List.for_all(for_all_stmtfefs)parts.stmts)letexists_child_exprfefse=letparts,_=structure_of_expreinList.existsfeparts.exprs||List.existsfsparts.stmtsletexists_child_stmtfefss=letparts,_=structure_of_stmtsinList.existsfeparts.exprs||List.existsfsparts.stmtsletfor_all_child_exprfefse=letparts,_=structure_of_expreinList.for_allfeparts.exprs&&List.for_allfsparts.stmtsletfor_all_child_stmtfefss=letparts,_=structure_of_stmtsinList.for_allfeparts.exprs&&List.for_allfsparts.stmts(** Extract variables from an expression *)letexpr_vars(e:expr):varlist=fold_expr(funacce->matchekindewith|E_var(v,m)->Keep(v::acc)|_->VisitPartsacc)(funaccs->VisitPartsacc)[]e(** Extract variables from a statement *)letstmt_vars(s:stmt):varlist=fold_stmt(funacce->matchekindewith|E_var(v,m)->Keep(v::acc)|_->VisitPartsacc)(funaccs->VisitPartsacc)[]sletis_var_in_exprve=fold_expr(funaccee->matchekindeewith|E_var(vv,_)whencompare_varvvv=0->Keeptrue|_->VisitPartsacc)(funaccs->VisitPartsacc)falseeletis_var_in_stmtvs=fold_stmt(funaccee->matchekindeewith|E_var(vv,_)whencompare_varvvv=0->Keeptrue|_->VisitPartsacc)(funaccs->VisitPartsacc)falses