123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296(****************************************************************************)(* *)(* 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/>. *)(* *)(****************************************************************************)(** Lattice of partial maps.
Sets of partial maps M ∈ ℘(𝕂 ⇀ 𝕍) from concrete keys set 𝕂 to
concrete values set 𝕍 are abstracted as a partial map ℳ ∈ (𝕂⇀𝒱)
that binds concrete keys to abstract values.
*)openMopsa_utilsopenBot_topopenCore.Allletdebugfmt=Debug.debug~channel:"framework.lattices.partial_map"fmtmodulePMap=MapExtPolytype('k,'v)map=('k,'v)PMap.twith_bot_topmoduletypeKEY=sigtypetvalcompare:t->t->intvalprint:Print.printer->t->unitendmoduleMake(Key:KEY)(Value:LATTICE)=struct(** Abstraction of a set of partial maps from [Key.t] to ['a].*)typet=(Key.t,Value.t)mapletbottom:t=BOTlettop:t=TOPletis_bottom(a:t):bool=matchawith|BOT->true|TOP->false|Nbtm->falseletempty:t=Nbt(PMap.empty~compare:Key.compare)letsubset(a1:t)(a2:t):bool=ifa1==a2thentrueelsematcha1,a2with|BOT,_->true|_,BOT->false|_,TOP->true|TOP,_->false|Nbtm1,Nbtm2->PMap.for_all2zo(fun_v1->false)(fun_v2->true)(fun_v1v2->Value.subsetv1v2)m1m2(** Inclusion test. *)letjoin(a1:t)(a2:t):t=ifa1==a2thena1elsematcha1,a2with|BOT,x|x,BOT->x|TOP,_|_,TOP->TOP|Nbtm1,Nbtm2->Nbt(PMap.map2zo(fun_v1->v1)(fun_v2->v2)(fun_v1v2->Value.joinv1v2)m1m2)(** Join two sets of partial maps. *)letwidenctx(a1:t)(a2:t):t=ifa1==a2thena1elsematcha1,a2with|BOT,x|x,BOT->x|TOP,x|x,TOP->TOP|Nbtm1,Nbtm2->Nbt(PMap.map2zo(fun_v1->v1)(fun_v2->v2)(fun_v1v2->Value.widenctxv1v2)m1m2)(** Widening (naive). *)letmeet(a1:t)(a2:t):t=ifa1==a2thena1elsematcha1,a2with|BOT,x|x,BOT->BOT|TOP,x|x,TOP->x|Nbtm1,Nbtm2->tryNbt(PMap.fold2zo(funk_acc->PMap.removekacc)(funk_acc->PMap.removekacc)(funkv1v2acc->letv=Value.meetv1v2inifValue.is_bottomvthenraiseBot.Found_BOTelsePMap.addkvacc)m1m2m1)withBot.Found_BOT->bottom(** Meet. *)letprintprinter(a:t):unit=matchawith|BOT->pp_stringprinter"⊥"|TOP->pp_stringprinter"⊤"|NbtmwhenPMap.is_emptym->pp_stringprinter"∅"|Nbtm->pp_mapKey.printValue.printprinter(PMap.bindingsm)~mopen:""~mbind:"⇀"~msep:","~mclose:""(** Printing. *)letfind(k:Key.t)(a:t):'a=matchawith|BOT->Value.bottom|TOP->Value.top|Nbtm->PMap.findkmletfind_opt(k:Key.t)(a:t):'aoption=matchawith|BOT->SomeValue.bottom|TOP->SomeValue.top|Nbtm->PMap.find_optkmletremove(k:Key.t)(a:t):t=matchawith|BOT->BOT|TOP->TOP|Nbtm->Nbt(PMap.removekm)letadd(k:Key.t)(v:'a)(a:t):t=ifValue.is_bottomvthenBOTelsematchawith|BOT->BOT|TOP->TOP|Nbtm->Nbt(PMap.addkvm)letrename(k:Key.t)(k':Key.t)(a:t):t=letv=findkainleta=removekainaddk'valetsingleton(k:Key.t)(v:'a):t=addkvemptyletfilter(f:Key.t->'a->bool)(a:t):t=matchawith|BOT->BOT|TOP->TOP|Nbtm->Nbt(PMap.filterfm)letpartition(f:Key.t->'a->bool)(a:t):t*t=matchawith|BOT->BOT,BOT|TOP->TOP,TOP|Nbtm->leta,b=PMap.partitionfminNbta,Nbtbletiter(f:Key.t->'a->unit)(a:t):unit=matchawith|BOT->()|TOP->raiseTop.Found_TOP|Nbtm->PMap.iterfmletfold(f:Key.t->'a->'b->'b)(a:t)(x:'b):'b=matchawith|BOT->x|TOP->raiseTop.Found_TOP|Nbtm->PMap.foldfmxletfold2of1f2fabacc=matcha,bwith|BOT,_|_,BOT->acc|TOP,_|_,TOP->raiseTop.Found_TOP|Nbtm1,Nbtm2->PMap.fold2of1f2fm1m2accletfold2zof1f2fabacc=matcha,bwith|BOT,_|_,BOT->acc|TOP,_|_,TOP->raiseTop.Found_TOP|Nbtm1,Nbtm2->PMap.fold2zof1f2fm1m2accletmem(x:Key.t)(a:t):bool=matchawith|BOT->false|TOP->true|Nbtm->PMap.memxmletcanonize(a:t):t=ifis_bottomathenBOTelsealetmap(f:Value.t->Value.t)(a:t):t=matchawith|BOT->BOT|TOP->TOP|Nbtm->Nbt(PMap.mapfm)|>canonizeletmapi(f:Key.t->Value.t->Value.t)(a:t):t=matchawith|BOT->BOT|TOP->TOP|Nbtm->Nbt(PMap.mapifm)|>canonizeletmap_p(f:Key.t*Value.t->Key.t*'a)(a:t):(Key.t,'a)map=matchawith|BOT->BOT|TOP->TOP|Nbtm->Nbt(PMap.fold(funkvacc->letk',v'=f(k,v)inPMap.addk'v'acc)m(PMap.empty~compare:Key.compare))|>canonizeletbindings(a:t):(Key.t*'a)list=matchawith|BOT->[]|TOP->raiseTop.Found_TOP|Nbtm->PMap.bindingsmletfor_all(f:Key.t->'a->bool)(a:t):bool=matchawith|BOT->true|TOP->raiseTop.Found_TOP|Nbtm->PMap.for_allfmletexists(f:Key.t->'a->bool)(a:t):bool=matchawith|BOT->false|TOP->raiseTop.Found_TOP|Nbtm->PMap.existsfmletmax_binding(a:t):(Key.t*'a)option=matchawith|BOT->None|TOP->None|Nbtm->Some(PMap.max_bindingm)letcardinal(a:t):int=matchawith|BOT->0|TOP->raiseTop.Found_TOP|Nbtm->PMap.cardinalmletmap2zof1f2f(a1:t)(a2:t):t=ifa1==a2thena1elsematcha1,a2with|BOT,x|x,BOT->x|TOP,x|x,TOP->TOP|Nbtm1,Nbtm2->Nbt(PMap.map2zof1f2fm1m2)end