123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778(****************************************************************************)(* *)(* 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/>. *)(* *)(****************************************************************************)(**
Abstract Syntax Tree for stub specification. Similar to the AST of
the parser, except for expressions, types and variables for which
MOPSA counterparts are used.
*)openMopsaopenUniversal.AstopenFormat(** Stub for a function *)typestub_func={stub_func_name:string;stub_func_body:sectionlist;stub_func_params:varlist;stub_func_locals:localwith_rangelist;stub_func_assigns:assignswith_rangelist;stub_func_return_type:typoption;stub_func_range:range;}(** Stub for a global directive *)andstub_directive={stub_directive_body:sectionlist;stub_directive_locals:localwith_rangelist;stub_directive_assigns:assignswith_rangelist;stub_directive_range:range;}(** {2 Stub sections} *)(** ***************** *)andsection=|S_caseofcase|S_leafofleafandleaf=|S_localoflocalwith_range|S_assumesofassumeswith_range|S_requiresofrequireswith_range|S_assignsofassignswith_range|S_ensuresofensureswith_range|S_freeoffreewith_range|S_messageofmessagewith_rangeandcase={case_label:string;case_body:leaflist;case_locals:localwith_rangelist;case_assigns:assignswith_rangelist;case_range:range;}(** {2 Leaf sections} *)(** ***************** *)andrequires=formulawith_rangeandensures=formulawith_rangeandassumes=formulawith_rangeandlocal={lvar:var;lval:local_value;}andlocal_value=|L_newofresource|L_callofexpr(** function *)*exprlist(* arguments *)andassigns={assign_target:expr;assign_offset:intervallist;}andfree=exprandmessage={message_kind:message_kind;message_body:string;}andmessage_kind=|WARN|UNSOUNDandlog_binop=Mopsa_c_stubs_parser.Cst.log_binop=|AND|OR|IMPLIESandset=|S_intervalofinterval|S_resourceofresourceandinterval=expr*exprandresource=Mopsa_c_stubs_parser.Ast.resourceandbuiltin=Mopsa_c_stubs_parser.Ast.builtin(** {2 Formulas} *)(** ************ *)andformula=|F_exprofexpr|F_binopoflog_binop*formulawith_range*formulawith_range|F_notofformulawith_range|F_forallofvar*set*formulawith_range|F_existsofvar*set*formulawith_range|F_inofexpr*set|F_otherwiseofformulawith_range*expr|F_ifofformulawith_range*formulawith_range*formulawith_rangeletcompare_assignsa1a2=Compare.paircompare_expr(Compare.list(Compare.paircompare_exprcompare_expr))(a1.assign_target,a2.assign_offset)(a2.assign_target,a2.assign_offset)letcompare_resource=Mopsa_c_stubs_parser.Ast.compare_resourceletcompare_sets1s2=matchs1,s2with|S_interval(a1,b1),S_interval(a2,b2)->Compare.compose[(fun()->compare_expra1a2);(fun()->compare_exprb1b2);]|S_resourcer1,S_resourcer2->compare_resourcer1r2|_->compares1s2letreccompare_formulaf1f2=matchf1.content,f2.contentwith|F_expre1,F_expre2->compare_expre1e2|F_binop(o1,f1,g1),F_binop(o2,f2,g2)->Compare.compose[(fun()->compareo1o2);(fun()->compare_formulaf1f2);(fun()->compare_formulag1g2);]|F_notf1,F_notf2->compare_formulaf1f2|F_forall(v1,s1,f1),F_forall(v2,s2,f2)->Compare.compose[(fun()->compare_varv1v2);(fun()->compare_sets1s2);(fun()->compare_formulaf1f2);]|F_exists(v1,s1,f1),F_exists(v2,s2,f2)->Compare.compose[(fun()->compare_varv1v2);(fun()->compare_sets1s2);(fun()->compare_formulaf1f2);]|F_in(e1,s1),F_in(e2,s2)->Compare.compose[(fun()->compare_expre1e2);(fun()->compare_sets1s2)]|x,y->comparexy(** {2 Expressions} *)(* =-=-=-=-=-=-=-= *)(** Quantifiers *)typequant=|FORALL|EXISTStypeexpr_kind+=|E_stub_callofstub_func(** called stub *)*exprlist(** arguments *)(** Call to a stubbed function *)|E_stub_return(** Returned value of a stub *)|E_stub_builtin_callofbuiltin*exprlist(** Call to a built-in function *)|E_stub_attributeofexpr*string(** Access to an attribute of a resource *)|E_stub_allocofstring(** resource *)*mode(** strong or weak *)(** Allocate a resource and translate it into an address *)|E_stub_resource_memofexpr*resource(** Filter environments in which an instance is in a resource pool *)|E_stub_primedofexpr(** Primed expressions denoting values in the post-state *)|E_stub_quantified_formulaof(quant*var*set)list(** Prefix containing the list of quantified variables *)*expr(** Quantifier-free formula *)(** Quantified formula in prenex normal form *)|E_stub_otherwiseofexpr(** condition *)*exproption(** alarm *)(** Conditions decorated with failure alarms. If the alarm is not present, the
default alarm A_stub_invalid_requirement is raised. *)|E_stub_raiseofstring(** message *)(** Raise an alarm *)|E_stub_ifofexpr(** condition *)*expr(** then *)*expr(** else *)(** Conditional stub expression *)(** {2 Statements} *)(* =-=-=-=-=-=-=- *)typestmt_kind+=|S_stub_directiveofstub_directive(** A call to a stub directive, which are stub functions called at the
initialization of the analysis. Useful to initialize variables with stub
formulas.
*)|S_stub_freeofexpr(** Release a resource *)|S_stub_requiresofexpr(** Filter the current environments that verify a condition, and raise an
alarm if the condition may be violated. *)|S_stub_prepare_all_assignsofassignslist(** Prepare primed copies of assigned objects *)|S_stub_assignsofassigns(** Declare an assigned object *)|S_stub_clean_all_assignsofassignslist(** Clean the post-state of primed copies *)(** {2 Heap addresses for resources} *)(** ******************************** *)typeaddr_kind+=|A_stub_resourceofstring(** resource address *)let()=register_addr_kind{print=(funnextfmtaddr_kind->matchaddr_kindwith|A_stub_resourceres->Format.pp_print_stringfmtres|_->nextfmtaddr_kind);compare=(funnextak1ak2->matchak1,ak2with|A_stub_resourceres1,A_stub_resourceres2->Stdlib.compareres1res2|_->nextak1ak2);}let()=Universal.Heap.Policies.register_mk_addr(fundefaultak->matchakwith|A_stub_resource_->Universal.Heap.Policies.mk_addr_stack_rangeak|_->defaultak)(** {2 Utility functions} *)(** ********************* *)letmk_stub_callstubargsrange=lett=matchstub.stub_func_return_typewith|None->T_any|Somet->tinmk_expr(E_stub_call(stub,args))~etyp:trangeletmk_stub_builtin_callbuiltinarg~etyprange=mk_expr(E_stub_builtin_call(builtin,arg))~etyprangeletmk_stub_resource_memeresrange=mk_expr(E_stub_resource_mem(e,res))~etyp:T_boolrangeletmk_stub_primederange=mk_expr(E_stub_primede)~etyp:e.etyprangeletmk_stub_directivestubrange=mk_stmt(S_stub_directivestub)rangeletmk_stub_freeerange=mk_stmt(S_stub_freee)rangeletmk_stub_prepare_all_assignsassignsrange=mk_stmt(S_stub_prepare_all_assigns(List.mapget_contentassigns))rangeletmk_stub_assignstoffsetrange=mk_stmt(S_stub_assigns{assign_target=t;assign_offset=offset})rangeletmk_stub_clean_all_assignsassignsrange=mk_stmt(S_stub_clean_all_assigns(List.mapget_contentassigns))rangeletmk_stub_requirescondrange=mk_stmt(S_stub_requirescond)rangeletmk_stub_quantified_formula?(etyp=T_bool)quantscondrange=mk_expr(E_stub_quantified_formula(quants,cond))~etyprangeletmk_stub_otherwisecondalarm?(etyp=T_bool)range=mk_expr(E_stub_otherwise(cond,alarm))range~etypletmk_stub_ifcf1f2range=mk_expr(E_stub_if(c,f1,f2))range~etyp:f1.etypletis_stub_primede=fold_expr(funaccee->matchekindeewith|E_stub_primed_->Keeptrue|_->VisitPartsacc)(funaccstmt->VisitPartsacc)falseeletfind_var_quantifiervquants=letreciter=function|[]->raiseNot_found|(q,vv,s)::tl->ifcompare_varvvv=0then(q,s)elseitertliniterquantsletfind_var_quantifier_optvquants=trySome(find_var_quantifiervquants)withNot_found->Noneletis_quantified_varvquants=matchfind_var_quantifier_optvquantswith|None->false|Some_->trueletis_forall_quantified_varvquants=matchfind_var_quantifier_optvquantswith|None->false|Some(q,_)->q=FORALLletis_exists_quantified_varvquants=matchfind_var_quantifier_optvquantswith|None->false|Some(q,_)->q=EXISTSletfind_quantified_var_intervalvquants=matchfind_var_quantifiervquantswith|(_,S_interval(lo,hi))->(lo,hi)|_->raiseNot_foundletfind_quantified_var_interval_optvquants=trySome(find_quantified_var_intervalvquants)withNot_found->Noneletnegate_stub_quantified_formulaquantscond=letquants'=List.map(fun(q,v,s)->letq'=ifq=FORALLthenEXISTSelseFORALLin(q',v,s))quantsinletconds'=mk_notcondcond.erangeinquants',conds'(** Visit expressions present in a formula *)letrecvisit_expr_in_formulavisitorf=bind_rangef@@funf->matchfwith|F_expre->F_expr(visit_exprvisitore)|F_binop(op,f1,f2)->F_binop(op,visit_expr_in_formulavisitorf1,visit_expr_in_formulavisitorf2)|F_notff->F_not(visit_expr_in_formulavisitorff)|F_forall(v,s,ff)->F_forall(v,visit_setvisitors,visit_expr_in_formulavisitorff)|F_exists(v,s,ff)->F_exists(v,visit_setvisitors,visit_expr_in_formulavisitorff)|F_in(e,s)->F_in(visit_exprvisitore,visit_setvisitors)|F_otherwise(f,e)->F_otherwise(visit_expr_in_formulavisitorf,visit_exprvisitore)|F_if(c,f1,f2)->F_if(visit_expr_in_formulavisitorc,visit_expr_in_formulavisitorf1,visit_expr_in_formulavisitorf2)andvisit_setvisitors=matchswith|S_interval(e1,e2)->S_interval(visit_exprvisitore1,visit_exprvisitore2)|S_resourcer->S_resourcerandvisit_exprvisitore=Visitor.map_exprvisitor(funstmt->Keepstmt)eletmk_stub_alloc_resource?(mode=STRONG)resrange=mk_expr(E_stub_alloc(res,mode))rangeletnegate_log_binop:log_binop->log_binop=function|AND->OR|OR->AND|IMPLIES->assertfalse(** {2 Pretty printers} *)(** =-=-=-=-=-=-=-=-=-= *)letpp_builtin=Mopsa_c_stubs_parser.Ast.pp_builtinletpp_log_binop=Mopsa_c_stubs_parser.Ast.pp_log_binopletpp_quantifierfmt=function|FORALL->pp_print_stringfmt"∀"|EXISTS->pp_print_stringfmt"∃"letrecpp_formulafmtf=matchf.contentwith|F_expre->pp_exprfmte|F_binop(op,f1,f2)->fprintffmt"(%a %a %a)"pp_formulaf1pp_log_binopoppp_formulaf2|F_notf->fprintffmt"not (%a)"pp_formulaf|F_forall(x,set,f)->fprintffmt"@[<v 2>∀ %a %a ∈ %a:@,%a@]"pp_typx.vtyppp_varxpp_setsetpp_formulaf|F_exists(x,set,f)->fprintffmt"@[<v 2>∃ %a %a ∈ %a:@,%a@]"pp_typx.vtyppp_varxpp_setsetpp_formulaf|F_in(x,set)->fprintffmt"%a ∈ %a"pp_exprxpp_setset|F_otherwise(f,e)->fprintffmt"(%a otherwise %a)"pp_formulafpp_expre|F_if(c,f1,f2)->fprintffmt"if %a then %a else %a end"pp_formulacpp_formulaf1pp_formulaf2andpp_setfmt=function|S_interval(e1,e2)->fprintffmt"[%a .. %a]"pp_expre1pp_expre2|S_resource(r)->pp_resourcefmtrandpp_intervalfmt((l,u):interval)=fprintffmt"[%a, %a]"pp_exprlpp_expruandpp_resource=Mopsa_c_stubs_parser.Ast.pp_resourceletpp_listppsepfmtl=pp_print_list~pp_sep:(funfmt()->fprintffmtsep)ppfmtlletpp_optppfmto=matchowith|None->()|Somex->ppfmtxletrecpp_localfmtlocal=letlocal=get_contentlocalinfprintffmt"local : %a %a = @[%a@];"pp_typlocal.lvar.vtyppp_varlocal.lvarpp_local_valuelocal.lvalandpp_local_valuefmtv=matchvwith|L_newresource->fprintffmt"new %a"pp_resourceresource|L_call(f,args)->fprintffmt"%a(%a)"pp_exprf(pp_listpp_expr", ")argsletpp_requiresfmtrequires=fprintffmt"requires : @[%a@];"pp_formularequires.contentletpp_assignsfmtassigns=fprintffmt"assigns : %a%a;"pp_exprassigns.content.assign_target(pp_print_list~pp_sep:(funfmt()->())pp_interval)assigns.content.assign_offsetletpp_assumesfmt(assumes:assumeswith_range)=fprintffmt"assumes : %a;"pp_formulaassumes.contentletpp_ensuresfmtensures=fprintffmt"ensures : %a;"pp_formulaensures.contentletpp_freefmtfree=fprintffmt"free : %a;"pp_exprfree.contentletpp_messagefmtmsg=matchmsg.content.message_kindwith|WARN->fprintffmt"warn: \"%s\";"msg.content.message_body|UNSOUND->fprintffmt"unsound: \"%s\";"msg.content.message_bodyletpp_leaf_sectionfmtsec=matchsecwith|S_locallocal->pp_localfmtlocal|S_assumesassumes->pp_assumesfmtassumes|S_requiresrequires->pp_requiresfmtrequires|S_assignsassigns->pp_assignsfmtassigns|S_ensuresensures->pp_ensuresfmtensures|S_freefree->pp_freefmtfree|S_messagemsg->pp_messagefmtmsgletpp_leaf_sectionsfmtsecs=fprintffmt"@[<v>";pp_print_list~pp_sep:(funfmt()->fprintffmt"@,")pp_leaf_sectionfmtsecs;fprintffmt"@]"letpp_casefmtcase=fprintffmt"@[<v 2>case \"%s\" {@,%a@]@,}"case.case_labelpp_leaf_sectionscase.case_bodyletpp_sectionfmtsec=matchsecwith|S_leafleaf->pp_leaf_sectionfmtleaf|S_casecase->pp_casefmtcaseletpp_sectionsfmtsecs=fprintffmt"@[<v>";pp_print_list~pp_sep:(funfmt()->fprintffmt"@,")pp_sectionfmtsecs;fprintffmt"@]"letpp_stub_funcfmtstub=pp_sectionsfmtstub.stub_func_bodyletpp_stub_directivefmtstub=pp_sectionsfmtstub.stub_directive_body(** {2 Registration of expressions} *)(* =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= *)let()=register_expr_with_visitor{compare=(funnexte1e2->matchekinde1,ekinde2with|E_stub_call(f1,args1),E_stub_call(f2,args2)->Compare.compose[(fun()->comparef1.stub_func_namef2.stub_func_name);(fun()->Compare.listcompare_exprargs1args2);]|E_stub_builtin_call(f1,args1),E_stub_builtin_call(f2,args2)->Compare.compose[(fun()->comparef1f2);(fun()->Compare.listcompare_exprargs1args2)]|E_stub_attribute(o1,f1),E_stub_attribute(o2,f2)->Compare.compose[(fun()->compare_expro1o2);(fun()->comparef1f2)]|E_stub_alloc(r1,m1),E_stub_alloc(r2,m2)->Compare.paircomparecompare_mode(r1,m1)(r2,m2)|E_stub_resource_mem(x1,res1),E_stub_resource_mem(x2,res2)->Compare.compose[(fun()->compare_exprx1x2);(fun()->compareres1res2);]|E_stub_primed(e1),E_stub_primed(e2)->compare_expre1e2|E_stub_quantified_formula(quants1,cond1),E_stub_quantified_formula(quants2,cond2)->Compare.pair(Compare.list(Compare.triplecomparecompare_varcompare_set))compare_expr(quants1,cond1)(quants2,cond2)|E_stub_otherwise(cond1,alarm1),E_stub_otherwise(cond2,alarm2)->Compare.paircompare_expr(Compare.optioncompare_expr)(cond1,alarm1)(cond2,alarm2)|E_stub_if(c1,fthen1,felse1),E_stub_if(c2,fthen2,felse2)->Compare.triplecompare_exprcompare_exprcompare_expr(c1,fthen1,felse1)(c2,fthen2,felse2)|E_stub_raisemsg1,E_stub_raisemsg2->String.comparemsg1msg2|_->nexte1e2);visit=(funnexte->matchekindewith|E_stub_call(f,args)->{exprs=args;stmts=[]},(function{exprs=args}->{ewithekind=E_stub_call(f,args)})|E_stub_return->leafe|E_stub_builtin_call(f,args)->{exprs=args;stmts=[]},(function{exprs}->{ewithekind=E_stub_builtin_call(f,exprs)})|E_stub_attribute(o,f)->{exprs=[o];stmts=[]},(function{exprs=[o]}->{ewithekind=E_stub_attribute(o,f)}|_->assertfalse)|E_stub_alloc_->leafe|E_stub_resource_mem(x,res)->{exprs=[x];stmts=[]},(function{exprs=[x]}->{ewithekind=E_stub_resource_mem(x,res)}|_->assertfalse)|E_stub_primed(ee)->{exprs=[ee];stmts=[]},(function{exprs=[ee]}->{ewithekind=E_stub_primed(ee)}|_->assertfalse)|E_stub_quantified_formula(quants,cond)->letrecdecompose_quantsquants=matchquantswith|[]->[]|(_,_,S_interval(hi,lo))::tl->hi::lo::decompose_quantstl|(_,_,S_resource_)::tl->decompose_quantstlinletrecrecompose_quantsquantsbounds=matchquants,boundswith|[],[]->[]|(q,v,S_interval_)::tl1,lo::hi::tl2->(q,v,S_interval(lo,hi))::recompose_quantstl1tl2|(q,v,S_resourcer)::tl1,l2->(q,v,S_resourcer)::recompose_quantstl1l2|_->assertfalsein{exprs=decompose_quantsquants@[cond];stmts=[]},(function{exprs}->letrexprs=List.revexprsinletcond=List.hdrexprsandquants=recompose_quantsquants(List.rev@@List.tlrexprs)in{ewithekind=E_stub_quantified_formula(quants,cond)})|E_stub_otherwise(cond,None)->{exprs=[cond];stmts=[]},(function{exprs}->letcond=matchexprswith[e]->e|_->assertfalsein{ewithekind=E_stub_otherwise(cond,None)})|E_stub_otherwise(cond,Somealarm)->{exprs=[cond;alarm];stmts=[]},(function{exprs}->letcond,alarm=matchexprswith[e1;e2]->e1,e2|_->assertfalsein{ewithekind=E_stub_otherwise(cond,Somealarm)})|E_stub_if(c,f1,f2)->{exprs=[c;f1;f2];stmts=[]},(function{exprs}->letc,f1,f2=matchexprswith[e0;e1;e2]->e0,e1,e2|_->assertfalsein{ewithekind=E_stub_if(c,f1,f2)})|E_stub_raisemsg->leafe|_->nexte);print=(funnextfmte->matchekindewith|E_stub_call(s,args)->fprintffmt"stub %s(%a)"s.stub_func_name(pp_listpp_expr", ")args|E_stub_return->pp_print_stringfmt"return"|E_stub_builtin_call(f,args)->fprintffmt"%a(%a)"pp_builtinf(pp_listpp_expr", ")args|E_stub_attribute(o,f)->fprintffmt"%a:%s"pp_exprof|E_stub_alloc(r,STRONG)->fprintffmt"alloc res(%s)"r|E_stub_alloc(r,WEAK)->fprintffmt"alloc res(%s):weak"r|E_stub_resource_mem(x,res)->fprintffmt"%a ∈ %a"pp_exprxpp_resourceres|E_stub_primed(ee)->fprintffmt"%a'"pp_expree|E_stub_quantified_formula(quants,cond)->fprintffmt"%a : %a"(pp_print_list~pp_sep:(funfmt()->pp_print_stringfmt", ")(funfmt(q,v,s)->fprintffmt"%a%a ∈ %a"pp_quantifierqpp_varvpp_sets))quantspp_exprcond|E_stub_otherwise(cond,None)->pp_exprfmtcond|E_stub_otherwise(cond,Somealarm)->fprintffmt"(%a otherwise %a)"pp_exprcondpp_expralarm|E_stub_if(c,f1,f2)->fprintffmt"(if %a then %a else %a end)"pp_exprcpp_exprf1pp_exprf2|E_stub_raisemsg->fprintffmt"raise(\"%s\")"msg|_->nextfmte);}(** {2 Registration of statements} *)(* =-=-=-=-=-=-=-=-=-=-=-=-=-=-=- *)let()=register_stmt_with_visitor{compare=(funnexts1s2->matchskinds1,skinds2with|S_stub_directive(stub1),S_stub_directive(stub2)->panic"compare for stubs not supported"|S_stub_free(e1),S_stub_free(e2)->compare_expre1e2|S_stub_assignsa1,S_stub_assignsa2->compare_assignsa1a2|S_stub_prepare_all_assignsa1,S_stub_prepare_all_assignsa2|S_stub_clean_all_assignsa1,S_stub_clean_all_assignsa2->Compare.listcompare_assignsa1a2|S_stub_requires(e1),S_stub_requires(e2)->compare_expre1e2|_->nexts1s2);visit=(funnexts->matchskindswith|S_stub_directive_->leafs|S_stub_freee->{exprs=[e];stmts=[]},(function{exprs=[e]}->{swithskind=S_stub_freee}|_->assertfalse)|S_stub_assignsa->letlbounds=List.mapfsta.assign_offsetinlethbounds=List.mapsnda.assign_offsetin{exprs=a.assign_target::lbounds@hbounds;stmts=[]},(function|{exprs=assign_target::bounds}->letlbounds,hbounds=ListExt.splitboundsinletassign_offset=List.combinelboundshboundsin{swithskind=S_stub_assigns{assign_target;assign_offset}}|_->assertfalse)|S_stub_prepare_all_assigns_->leafs|S_stub_clean_all_assigns_->leafs|S_stub_requirese->{exprs=[e];stmts=[]},(function{exprs=[e]}->{swithskind=S_stub_requirese}|_->assertfalse)|_->nexts);print=(funnextfmts->matchskindswith|S_stub_directive(stub)->fprintffmt"@[<v 2>/*$!@,%a@]*/"pp_sectionsstub.stub_directive_body|S_stub_freee->fprintffmt"stub-free(%a);"pp_expre|S_stub_assignsassigns->fprintffmt"assigns : %a%a;"pp_exprassigns.assign_target(pp_print_list~pp_sep:(funfmt()->())(funfmt(l,u)->fprintffmt"[%a .. %a]"pp_exprlpp_expru))assigns.assign_offset|S_stub_prepare_all_assigns_->fprintffmt"prepare assigns;"|S_stub_clean_all_assigns_->fprintffmt"clean assigns;"|S_stub_requirese->fprintffmt"requires %a;"pp_expre|_->nextfmts);}