123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354(****************************************************************************)(* *)(* 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/>. *)(* *)(****************************************************************************)(** Pointwise lattice construction.
Lattice of partial maps 𝕂 ⇀ 𝕍 where 𝕂 is a key set
and 𝕍 is a value lattice.
Unlink Partial_map, ⊥ values is not coalescent.
Bindings that map keys to ⊥ values are not represented.
We use Maps, so 𝕂 needs to be totally ordered.
⊥ map is represented as the empty map.
⊤ map is represented as a special TOP element.
*)openMopsa_utilsopenTopopenCore.Allletdebugfmt=Debug.debug~channel:"framework.lattices.pointwise"fmtmoduletypeKEY=sigtypetvalcompare:t->t->intvalprint:printer->t->unitendmoduleMake(Key:KEY)(Value:LATTICE)=structmoduleM=MapExt.Make(Key)typent_t=Value.tM.ttypet=nt_twith_topletbottom:t=NtM.emptyletempty:t=bottomlettop:t=TOPletis_bottom(a:t):bool=top_dfl1falseM.is_emptyaletis_empty(a:t):bool=is_bottomaletis_top(a:t):bool=a=TOPletsubset(a:t)(b:t):bool=ifa==bthentrueelsetop_included(M.for_all2zo(fun__->false)(fun__->true)(fun_xy->Value.subsetxy))abletjoin(a:t)(b:t):t=ifa==bthenaelsetop_lift2(M.map2zo(fun_x->x)(fun_x->x)(fun_xy->Value.joinxy))abletwidenctx(a:t)(b:t):t=ifa==bthenaelsetop_lift2(M.map2zo(fun_x->x)(fun_x->x)(fun_xy->Value.widenctxxy))abletmeet(a:t)(b:t):t=ifa==bthenaelsetop_neutral2(M.map2zo(fun_x->x)(fun_x->x)(fun_xy->Value.meetxy))abletprintprinter(a:t):unit=matchawith|TOP->pp_stringprinter"⊤"|NtmwhenM.is_emptym->pp_stringprinter"⊥"|Ntm->pp_mapKey.printValue.printprinter(M.bindingsm)(** Returns ⊥ value if either k is not found, or a is the ⊤ map. *)letfind(k:Key.t)(a:t):Value.t=top_dfl1Value.top(funm->tryM.findkmwithNot_found->Value.bottom)aletremove(k:Key.t)(a:t):t=top_lift1(funm->M.removekm)a(* Internal function .
Call this instead of M.add to ensure that no binding to the ⊥ value
is ever added to the map.
*)letm_add(k:Key.t)(v:Value.t)(a:nt_t):nt_t=ifValue.is_bottomvthenM.removekaelseM.addkvaletadd(k:Key.t)(v:Value.t)(a:t):t=top_lift1(funm->m_addkvm)a(** Returns false of a is the ⊤ map, or k is mapped to the ⊥ value. *)letmem(k:Key.t)(a:t):bool=top_dfl1false(M.memk)aletrename(k:Key.t)(k':Key.t)(a:t):t=letv=findkainleta=removekainaddk'valetapply(k:Key.t)(f:Value.t->Value.t)(a:t):t=addk(f(findka))aletsingleton(k:Key.t)(v:Value.t):t=addkvbottomletfilter(f:Key.t->Value.t->bool)(a:t):t=top_lift1(M.filterf)a(** Raises a Top_encountered exception for the ⊤ map. *)letbindings(a:t):(Key.t*Value.t)list=M.bindings(detopa)(** Returns None for a ⊤ or ⊥ map. *)letmax_binding(a:t):(Key.t*Value.t)option=top_dfl1None(funm->trySome(M.max_bindingm)withNot_found->None)a(** Raises a Top_encountered exception for the ⊤ map. *)letcardinal(a:t):int=M.cardinal(detopa)letof_list(l:(Key.t*Value.t)list):t=Nt(M.of_listl)(** Iterator functions.
These functions do nothing for the ⊤ map, which is
equivalent here to the empty map.
*)letiter(f:Key.t->Value.t->unit)(a:t):unit=top_apply(M.iterf)()aletfold(f:Key.t->Value.t->'a->'a)(a:t)(x:'a):'a=top_dfl1x(funa->M.foldfax)aletmap(f:Value.t->Value.t)(a:t):t=top_lift1(funm->M.fold(funkvn->m_addk(fv)n)mM.empty)aletmapi(f:Key.t->Value.t->Value.t)(a:t):t=top_lift1(funm->M.fold(funkvn->m_addk(fkv)n)mM.empty)aletfor_all(f:Key.t->Value.t->bool)(a:t):bool=top_dfl1false(M.for_allf)aletexists(f:Key.t->Value.t->bool)(a:t):bool=top_dfl1false(M.existsf)a(**
Binary iterators.
If a key is bound in only one map, the function is called
with the ⊥ value as missing argument.
These functions do nothing if either map is the ⊤ map.
*)letmap2(f:Key.t->Value.t->Value.t->Value.t)(m1:t)(m2:t):t=top_lift2(funm1m2->M.fold2o(funkv1n->m_addk(fkv1Value.bottom)n)(funkv2n->m_addk(fkValue.bottomv2)n)(funkv1v2n->m_addk(fkv1v2)n)m1m2M.empty)m1m2letiter2(f:Key.t->Value.t->Value.t->unit)(m1:t)(m2:t):unit=top_apply2()()(M.iter2o(funkv1->fkv1Value.bottom)(funkv2->fkValue.bottomv2)f)m1m2letfold2(f:Key.t->Value.t->Value.t->'a->'a)(m1:t)(m2:t)(acc:'a):'a=top_apply2accacc(funm1m2->M.fold2o(funkv1acc->fkv1Value.bottomacc)(funkv2acc->fkValue.bottomv2acc)fm1m2acc)m1m2letfor_all2(f:Key.t->Value.t->Value.t->bool)(m1:t)(m2:t):bool=top_dfl2false(M.for_all2o(funkv1->fkv1Value.bottom)(funkv2->fkValue.bottomv2)f)m1m2letexists2(f:Key.t->Value.t->Value.t->bool)(m1:t)(m2:t):bool=top_dfl2false(M.exists2o(funkv1->fkv1Value.bottom)(funkv2->fkValue.bottomv2)f)m1m2letmap2z(f:Key.t->Value.t->Value.t->Value.t)(m1:t)(m2:t):t=top_lift2(funm1m2->M.fold2zo(funkv1n->m_addk(fkv1Value.bottom)n)(funkv2n->m_addk(fkValue.bottomv2)n)(funkv1v2n->m_addk(fkv1v2)n)m1m2M.empty)m1m2letiter2z(f:Key.t->Value.t->Value.t->unit)(m1:t)(m2:t):unit=top_apply2()()(M.iter2zo(funkv1->fkv1Value.bottom)(funkv2->fkValue.bottomv2)f)m1m2letfold2z(f:Key.t->Value.t->Value.t->'a->'a)(m1:t)(m2:t)(acc:'a):'a=top_apply2accacc(funm1m2->M.fold2zfm1m2acc)m1m2letfold2zo(f1:Key.t->Value.t->'a->'a)(f2:Key.t->Value.t->'a->'a)(f:Key.t->Value.t->Value.t->'a->'a)(m1:t)(m2:t)(acc:'a):'a=top_apply2accacc(funm1m2->M.fold2zof1f2fm1m2acc)m1m2letfor_all2z(f:Key.t->Value.t->Value.t->bool)(m1:t)(m2:t):bool=top_dfl2false(M.for_all2zo(funkv1->fkv1Value.bottom)(funkv2->fkValue.bottomv2)f)m1m2letexists2z(f:Key.t->Value.t->Value.t->bool)(m1:t)(m2:t):bool=top_dfl2false(M.exists2zo(funkv1->fkv1Value.bottom)(funkv2->fkValue.bottomv2)f)m1m2(** Slice iterations. *)letmap_slice(f:Key.t->Value.t->Value.t)(m:t)(lo:Key.t)(hi:Key.t):t=top_lift1(funm->M.fold_slice(funkvn->m_addk(fkv)n)mlohiM.empty)mletiter_slice(f:Key.t->Value.t->unit)(m:t)(lo:Key.t)(hi:Key.t):unit=top_apply(funm->M.iter_slicefmlohi)()mletfold_slice(f:Key.t->Value.t->'a->'a)(m:t)(lo:Key.t)(hi:Key.t)(acc:'a):'a=top_apply(funm->M.fold_slicefmlohiacc)accmletfor_all_slice(f:Key.t->Value.t->bool)(m:t)(lo:Key.t)(hi:Key.t):bool=top_dfl1false(funm->M.for_all_slicefmlohi)mletexists_slice(f:Key.t->Value.t->bool)(m:t)(lo:Key.t)(hi:Key.t):bool=top_dfl1false(funm->M.exists_slicefmlohi)mend