123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771(** The [Bindlib] library provides support for free and bound variables in the
OCaml language. The main application is the representation of types with a
binding structure (e.g., abstract syntax trees).
@author Christophe Raffalli
@author Rodolphe Lepigre
@version 6.0.0 *)(* Counter for generating fresh variable keys (i.e., unique identifiers). *)let((reset_counter:unit->unit),(fresh_key:unit->int))=letc=ref(-1)in((fun()->c:=-1),(fun()->incrc;!c))(* FIXME remove in recent enough OCaml. *)moduleInt=structtypet=intletcompare=(-)endmoduleIMap=Map.Make(Int)moduleSMap=Map.Make(String)(* Environment **************************************************************)(* The [Bindlib] library uses environments to store values associated to bound
variables upon substitution. We rely on the [Obj] module to store variables
with potentially different types in a single array.
Uses of the [Env] module by [Bindlib] are safe because every single cell of
every environment is only used at a single, fixed type. Moreover, there can
be no problem with unboxed floating-point arrays because it is not possible
to extend the [float] type itself with variables. *)moduleEnv:sig(* Type of an environment. *)typet(* [create next_free size] creates a new empty environment of with the given
[size], and with its next free cell at index [next_free]. *)valcreate:int->int->t(* [set env i e] sets the value stored at position [i] of [env] to [e]. *)valset:t->int->'a->unit(* [get i env] gets the value stored at position [i] in [env]. *)valget:int->t->'a(* [blit s t n] copies the [n] first elements of [s] to [t]. *)valblit:t->t->int->unit(* [copy env] make a copy of environment [env]. *)valcopy:t->t(* [get_next_free env] returns the index of the next free cell in [env]. *)valget_next_free:t->int(* [set_next_free env i] sets the next free cell index of [env] to [i]. *)valset_next_free:t->int->unitend=structtypet={tab:Obj.tarray;mutablenext_free:int}letcreatenext_freesize={tab=Array.makesize(Obj.repr());next_free}letsetenvie=Array.setenv.tabi(Obj.repre)letgetienv=Obj.obj(Array.getenv.tabi)letblitsrcdstlen=Array.blitsrc.tab0dst.tab0lenletcopyenv={tab=Array.copyenv.tab;next_free=env.next_free}letget_next_freeenv=env.next_freeletset_next_freeenvn=env.next_free<-nend(* Closure representation ***************************************************)(* In the internals of [Bindlib], variables are identified by a unique integer
key. Closures are then formed by mapping each free variable to a slot in an
environment (of type [Env.t]), but not directly. The mapping is established
using a map (of type [varpos]) which associates variable keys to indices in
the environment. *)typevarpos=intIMap.ttype'aclosure=varpos->Env.t->'aletmap_closure_auxfa=funenv->f(aenv)letmap_closure:('a->'b)->'aclosure->'bclosure=funfclavs->map_closure_auxf(clavs)letapp_closure_auxfa=funenv->fenvaletapp_closure:('a->'b)closure->'a->'bclosure=funclfavs->app_closure_aux(clfvs)aletapply_closure_auxfa=funenv->fenv(aenv)let(<*>):('a->'b)closure->'aclosure->'bclosure=funclfclavs->apply_closure_aux(clfvs)(clavs)(* Box and variable representation ******************************************)(* At the core of [Bindlib] is the type ['a box] which represents an object of
type ['a] whose free variables are available for binding. Intuitively, this
is represented as the combination of an environment and a closure as in the
[Env(vs,n,t)]. The [Box(e)] constructor's only role is to optimise the case
where an object is known to be closed.
In the [Env(vs,n,t)] constructor, the list [vs] contains all free variables
in scope (sorted by their keys), the integer [n] counts the number of bound
variables that have a reserved slot in the environment, and finally, [t] is
a representation of the objects (with variables) using a closure.
Note that the closure [t] is intended to receive the environment as second
argument, and the position of the free variables of [vs] in the environment
as a first argument.
Important remark: to be able to efficiently implement substitution, we make
sure that the [varpos] map (given as first argument to closures) is used at
most once for each variable, even if a variable appears many times. *)type'abox=|Boxof'a(* Closed object. *)|Envofany_varlist*int*'aclosure(* Open object with environment. *)and'avar={var_key:int;(* Unique identifier. *)var_name:string;(* Name as a free variable. *)var_mkfree:'avar->'a;(* Function to build a term. *)var_box:'abox;(* Variable as a boxed object. *)}(* Variable of any type (using an existential). In principle, this constructor
can be unboxed since it has a single constructor. However, this only works
starting from OCaml 4.11.0. The annotation is erased for prior versions. *)andany_var=V:'avar->any_var[@@unboxed]letname_ofx=x.var_namelethash_varx=Hashtbl.hash(`HVar,x.var_key)letbox_varx=x.var_boxletcompare_varsxy=y.var_key-x.var_keyleteq_varsxy=x.var_key=y.var_keyletuid_ofx=x.var_keytype'amvar='avararrayletnames_ofxs=Array.mapname_ofxsletuids_ofxs=Array.mapuid_ofxs(* The [merge_uniq] and [remove] functions manipulate variables lists found in
boxed object of the form [Env(_,_,_)]. They rely on the fact that such list
is always sorted in the order of variable keys, and has no duplicates. *)letmerge_uniq:any_varlist->any_varlist->any_varlist=letrecmerge_uniqaccl1l2=match(l1,l2)with|([],_)->List.rev_appendaccl2|(_,[])->List.rev_appendaccl1|((V(x)asvx)::xs,(V(y)asvy)::ys)->ifx.var_key=y.var_keythenmerge_uniq(vx::acc)xsyselseifx.var_key<y.var_keythenmerge_uniq(vx::acc)xsl2else(* x.var_key > y.var_key*)merge_uniq(vy::acc)l1ysinmerge_uniq[]letremove:'avar->any_varlist->any_varlist=fun{var_key;_}->letrecremoveacc=function|(V(x)asv)::lwhenx.var_key<var_key->remove(v::acc)l|(V(x))::lwhenx.var_key=var_key->List.rev_appendaccl|_->raiseNot_foundinremove[](* Function [minimize vs n cl] constructs a minimal closure equivalent to [cl]
and only containing variables of [vs]. Additionally, the function builds an
environment with [n] extra slots. *)letminimize_aux_prefixsizent=funenv->letnew_env=Env.createsize(size+n)inEnv.blitenvnew_envsize;tnew_envletminimize_auxtabnt=funenv->letsize=Array.lengthtabinletnew_env=Env.createsize(size+n)inArray.iteri(funix->Env.setnew_envi(Env.getxenv))tab;tnew_envletminimize:any_varlist->int->'aclosure->'aclosure=funvsnt->ifn=0thentelsefunvp->letsize=List.lengthvsinlettab=Array.makesize0inletprefix=reftrueinletf(new_vp,i)(V(var))=letj=IMap.findvar.var_keyvpinifi<>jthenprefix:=false;tab.(i)<-j;(IMap.addvar.var_keyinew_vp,i+1)inlet(new_vp,_)=List.fold_leftf(IMap.empty,0)vsinlett=tnew_vpinif!prefixthenminimize_aux_prefixsizentelseminimize_auxtabntletbox:'a->'abox=funt->Box(t)letapply_box:('a->'b)box->'abox->'bbox=funfa->match(f,a)with|(Box(f),Box(a))->Box(fa)|(Box(f),Env(va,na,ta))->Env(va,na,map_closurefta)|(Env(vf,nf,tf),Box(a))->Env(vf,nf,app_closuretfa)|(Env(vf,nf,tf),Env(va,na,ta))->Env(merge_uniqvfva,0,minimizevfnftf<*>minimizevanata)letoccur:'avar->'bbox->bool=funvb->matchbwith|Box(_)->false|Env(vs,_,_)->List.exists(fun(V(x))->x.var_key=v.var_key)vsletis_closed:'abox->bool=funb->matchbwithBox(_)->true|Env(_,_,_)->falseletbox_apply:('a->'b)->'abox->'bbox=funfa->matchawith|Box(a)->Box(fa)|Env(vs,na,ta)->Env(vs,na,map_closurefta)letbox_apply2ftatb=apply_box(box_applyfta)tbletbox_apply3ftatbtc=apply_box(box_apply2ftatb)tcletbox_apply4ftatbtctd=apply_box(box_apply3ftatbtc)tdletbox_pairxy=box_apply2(funxy->(x,y))xyletbox_triplexyz=box_apply3(funxyz->(x,y,z))xyzletbox_opto=matchowith|None->boxNone|Some(e)->box_apply(fune->Some(e))eletdummy_box:'abox=letfail_=failwith"Invalid use of dummy_box"inEnv([],0,fail)letunboxb=matchbwithBox(t)->t|Env(vs,nb,t)->letnbvs=List.lengthvsinletenv=Env.createnbvs(nbvs+nb)inletcur=ref0inletfnvp(V(x))=leti=!curinincrcur;Env.setenvi(x.var_mkfreex);IMap.addx.var_keyivpint(List.fold_leftfnIMap.emptyvs)env(* Functorial interface for boxing functions ********************************)(* We use the following type and functions for combining several boxed objects
into one. In particular, we combine all lists of variables [vs] that appear
in the encountered [Env(vs,n,_)] constructors. We also remember the largest
[n] encountered in such constructors, and whether we encountered only [Box]
constructors (i.e., closed objects).
The type [data] is used to represent these three pieces of information. The
function [gather_data data b] combines data gathered so far (in [data]) and
new data from a boxed object [b]. Note that [no_data] plays the role of the
neutral element of [gather_data]. *)typedata=bool*any_varlist*intletgather_data:data->'abox->data=fundatab->match(b,data)with|(Box(_),_)->data|(Env(vs,n,_),(_,ws,m))->(false,merge_uniqwsvs,maxmn)letno_data:data=(true,[],0)(* Function [box_to_closure b] constructs a closure from boxed object [b]. The
information about variables and reserved slots (contained in boxed objects)
is lost in the process, and it must thus be tracked independently (with the
use of [gather_data]) to reconstruct a boxed object in the end. *)letbox_to_closure:'abox->'aclosure=funb->matchbwith|Box(t)->fun__->t|Env(vs,n,t)->minimizevsntmoduletypeMap=sigtype'atvalmap:('a->'b)->'at->'btendmoduleLift(M:Map)=structletlift_box_auxm=funenv->M.map(funo->oenv)mletlift_box:'aboxM.t->'aM.tbox=funm->letdata=refno_datainletfnb=data:=gather_data!datab;box_to_closurebinletm=M.mapfnminletauxvp=letm=M.map(funo->ovp)minlift_box_auxminmatch!datawith|(true,_,_)->Box(auxIMap.empty(Env.create00))|(_,vs,n)->Env(vs,n,minimizevsnaux)endmoduletypeMap2=sigtype('a,'b)tvalmap:('a->'b)->('c->'d)->('a,'c)t->('b,'d)tendmoduleLift2(M:Map2)=structletlift_box_auxm=funenv->M.map(funo->oenv)(funo->oenv)mletlift_box:('abox,'bbox)M.t->('a,'b)M.tbox=funm->letdata=refno_datainletfnb=data:=gather_data!datab;box_to_closurebinletm=M.mapfnfnminletauxvp=letm=M.map(funo->ovp)(funo->ovp)minlift_box_auxminmatch!datawith|(true,_,_)->Box(auxIMap.empty(Env.create00))|(_,vs,n)->Env(vs,n,minimizevsnaux)endmoduleLift_list=Lift(structtype'at='alistletmap=List.mapend)letbox_list=Lift_list.lift_boxmoduleLift_rev_list=Lift(structtype'at='alistletmap=List.rev_mapend)letbox_rev_list=Lift_rev_list.lift_boxmoduleLift_array=Lift(structtype'at='aarrayletmap=Array.mapend)letbox_array=Lift_array.lift_box(* Representation of binders ************************************************)type('a,'b)binder={b_name:string;(* Preferred name for the bound variable. *)b_bind:bool;(* Indicates whether the variable occurs. *)b_rank:int;(* Number of remaining free variables. *)b_mkfree:'avar->'a;(* Injection of variables into domain. *)b_value:'a->'b;(* Substitution function. *)}letbinder_nameb=b.b_nameletsubstbx=b.b_valuexletbinder_occurb=b.b_bindletbinder_constantb=notb.b_bindletbinder_closedb=b.b_rank=0letbinder_rankb=b.b_rankletbinder_composebf={bwithb_value=(funx->f(b.b_valuex))}type('a,'b)mbinder={mb_names:stringarray;(* Preferred names for the bound variables. *)mb_binds:boolarray;(* Indicates whether the variables occur. *)mb_rank:int;(* Number of remaining free variables. *)mb_mkfree:'avar->'a;(* Injection of variables into domain. *)mb_value:'aarray->'b;(* Substitution function. *)}letmbinder_arityb=Array.lengthb.mb_namesletmbinder_namesb=b.mb_namesletmsubstbxs=b.mb_valuexsletmbinder_occursb=b.mb_bindsletmbinder_constantb=not(Array.fold_left(||)falseb.mb_binds)letmbinder_closedb=b.mb_rank=0letmbinder_rankb=b.mb_rankletmbinder_composebf={bwithmb_value=(funx->f(b.mb_valuex))}letraw_binderb_nameb_bindb_rankb_mkfreeb_value={b_name;b_bind;b_rank;b_mkfree;b_value}letraw_mbindermb_namesmb_bindsmb_rankmb_mkfreemb_value={mb_names;mb_binds;mb_rank;mb_mkfree;mb_value}letbind_applybarg=box_apply2substbargletmbind_applybargs=box_apply2msubstbargs(* Variable creation ********************************************************)letbuild_var_auxkeyvp=Env.get(IMap.findkeyvp)letbuild_varvar_keyvar_mkfreename=letrecx=letvar_box=Env([V(x)],0,build_var_auxvar_key)in{var_key;var_name=name;var_mkfree;var_box}inxletnew_varmkfreename=build_var(fresh_key())mkfreenameletnew_mvarmkfreenames=Array.map(funn->new_varmkfreen)namesletcopy_varxmkfree=build_varx.var_keymkfreeletunbindb=letx=new_varb.b_mkfree(binder_nameb)in(x,substb(b.b_mkfreex))letunbind2b1b2=letx=new_varb1.b_mkfree(binder_nameb1)inletv=b1.b_mkfreexin(x,substb1v,substb2v)leteq_bindereqfg=f==g||let(_,t,u)=unbind2fgineqtuletunmbindb=letx=new_mvarb.mb_mkfree(mbinder_namesb)in(x,msubstb(Array.mapb.mb_mkfreex))letunmbind2b1b2=ifmbinder_arityb1<>mbinder_arityb2theninvalid_arg"Arity mismatch in unmbind2";letxs=new_mvarb1.mb_mkfree(mbinder_namesb1)inletvs=Array.mapb1.mb_mkfreexsin(xs,msubstb1vs,msubstb2vs)leteq_mbindereqfg=f==g||(mbinder_arityf=mbinder_arityg&&let(_,t,u)=unmbind2fgineqtu)(* Implementation of [bind_var] *********************************************)letbuild_binderxb_rankb_bindb_value={b_name=x.var_name;b_rank;b_bind;b_value;b_mkfree=x.var_mkfree}letbind_var_aux1nt=funarg->letenv=Env.create1(n+1)inEnv.setenv0arg;tenvletbind_var_aux2rankt=funenvarg->letnext=Env.get_next_freeenvinifnext=rankthenbeginEnv.set_next_freeenv(next+1);Env.setenvnextarg;tenvendelsebeginletenv=Env.copyenvinEnv.set_next_freeenv(rank+1);fori=rank+1tonext-1doEnv.setenvi0done;Env.setenvrankarg;tenvendletbind_var_aux3xrankt=funenv->letvalue=bind_var_aux2ranktenvinbuild_binderxranktruevalueletbind_var_aux4t=funenv_->tenvletbind_var_aux5xrankt=funenv->letvalue=bind_var_aux4tenvinbuild_binderxrankfalsevalueletbind_var:'avar->'bbox->('a,'b)binderbox=funxb->matchbwith|Box(t)->Box(build_binderx0false(fun_->t))|Env(vs,n,t)->trymatchvswith|[V(y)]->ifx.var_key<>y.var_keythenraiseNot_found;(* The variable to bind is the last one. *)letr=0inlett=t(IMap.singletonx.var_keyr)inletvalue=bind_var_aux1ntinBox(build_binderx0truevalue)|_->letvs=removexvsin(* General case. *)letclvp=letrank=List.lengthvsinletr=rankinlett=t(IMap.addx.var_keyrvp)inbind_var_aux3xranktinEnv(vs,n+1,cl)withNot_found->(* The variable does not occur. *)letvaluevp=lett=tvpinletrank=List.lengthvsinbind_var_aux5xranktinEnv(vs,n,value)letbox_binderfb=ifbinder_closedbthenboxbelselet(x,t)=unbindbinbind_varx(ft)(* Implementation of [bind_mvar] ********************************************)letcheck_arity:'amvar->'aarray->unit=funxsargs->ifArray.(lengthxs<>lengthargs)theninvalid_arg"Bad arity in msubst"letbind_mvar_aux0xst=funargs->check_arityxsargs;tletbind_mvar_aux1mxsmb_bindst=funargs->check_arityxsargs;letv=Env.create0minletpos=ref0infori=0toArray.lengthxs-1doifmb_binds.(i)thenbeginEnv.setv!posargs.(i);incrpos;enddone;Env.set_next_freev!pos;tvletbind_mvar_aux2xst=funenvargs->check_arityxsargs;tenvletbind_mvar_aux3xstmb_namesmb_rankmb_bindsmb_mkfree=funenv->letmb_value=bind_mvar_aux2xstenvin{mb_names;mb_rank;mb_binds;mb_value;mb_mkfree}letbind_mvar_aux4xstmb_rankmb_binds=funenvargs->check_arityxsargs;letnext=Env.get_next_freeenvinletcur_pos=refmb_rankinifnext=mb_rankthenbeginfori=0toArray.lengthxs-1doifmb_binds.(i)thenbeginEnv.setenv!cur_posargs.(i);incrcur_pos;enddone;Env.set_next_freeenv!cur_pos;tenvendelsebeginletenv=Env.copyenvinfori=0toArray.lengthxs-1doifmb_binds.(i)thenbeginEnv.setenv!cur_posargs.(i);incrcur_pos;enddone;Env.set_next_freeenv!cur_pos;fori=!cur_postonext-1doEnv.setenvi0done;tenvendletbind_mvar_aux5xstmb_namesmb_rankmb_bindsmb_mkfree=funenv->letmb_value=bind_mvar_aux4xstmb_rankmb_bindsenvin{mb_names;mb_rank;mb_binds;mb_value;mb_mkfree}letbind_mvar:'amvar->'bbox->('a,'b)mbinderbox=funxsb->letmb_mkfree=ifArray.lengthxs>0thenxs.(0).var_mkfreeelse(fun_->assertfalse)inmatchbwith|Box(t)->letmb_binds=Array.map(fun_->false)xsinletmb_names=Array.mapname_ofxsinletmb_value=bind_mvar_aux0xstinBox({mb_names;mb_rank=0;mb_binds;mb_value;mb_mkfree})|Env(vs,n,t)->letkeys=Array.map(fun_->0)xsinletvss=Array.map(fun_->vs)xsinlet(vs,m)=letvs=refvsinletm=refninfori=Array.lengthxs-1downto0doletv=xs.(i)inbegintryvs:=removev!vs;incrm;keys.(i)<-v.var_keywithNot_found->keys.(i)<--1end;vss.(i)<-!vs(*NOTE: store each vs, for good renaming *)done;(!vs,!m)inifvs=[]then(* All the free variables become bound. *)letmb_names=Array.map(fun_->"")xsinletcur_pos=ref0inletvp=refIMap.emptyinletfikey=mb_names.(i)<-xs.(i).var_name;ifkey>=0thenbeginvp:=IMap.addkey!cur_pos!vp;incrcur_pos;trueendelsefalseinletmb_binds=Array.mapifkeysinlett=t!vpinletmb_value=bind_mvar_aux1mxsmb_bindstinBox({mb_names;mb_binds;mb_rank=0;mb_value;mb_mkfree})elseifm=nthen(* None of the variables occur. *)letclvp=letmb_rank=List.lengthvsinletmb_binds=Array.map(fun_->false)xsinletfnx=x.var_nameinletmb_names=Array.mapfnxsinlett=tvpinbind_mvar_aux3xstmb_namesmb_rankmb_bindsmb_mkfreeinEnv(vs,n,cl)else(* General case. *)letclvp=letmb_names=Array.map(fun_->"")xsinletmb_rank=List.lengthvsinletcur_pos=refmb_rankinletvp=refvpinletfikey=mb_names.(i)<-xs.(i).var_name;ifkey>=0then(vp:=IMap.addkey!cur_pos!vp;incrcur_pos;true)elsefalseinletmb_binds=Array.mapifkeysinlett=t!vpinbind_mvar_aux5xstmb_namesmb_rankmb_bindsmb_mkfreeinEnv(vs,m,cl)letbox_mbinderfb=ifb.mb_rank=0thenboxbelselet(xs,t)=unmbindbinbind_mvarxs(ft)(* Functorial interface for renaming policies *******************************)moduletypeRenaming=sigtypectxtvalempty_ctxt:ctxtvalreset_context_for_closed_terms:boolvalskip_constant_binders:boolvalconstant_binder_name:stringoptionvalnew_name:string->ctxt->string*ctxtvalreserve_name:string->ctxt->ctxtendmoduleCtxt(R:Renaming)=structincludeRletfree_varsb=matchbwith|Box(_)->empty_ctxt|Env(vs,_,_)->letaddctxt(V(x))=reserve_name(name_ofx)ctxtinList.fold_leftaddempty_ctxtvsletnew_var_inctxtmkfreename=let(fresh_name,ctxt)=new_namenamectxtin(new_varmkfreefresh_name,ctxt)letnew_mvar_inctxtmkfreenames=letctxt=refctxtinletfname=let(v,new_ctxt)=new_var_in!ctxtmkfreenameinctxt:=new_ctxt;vin(Array.mapfnames,!ctxt)letreset_ctxtclosedctxt=ifreset_context_for_closed_terms&&closedthenempty_ctxtelsectxtletunbind_varconstantmkfreenamectxt=match(constant,skip_constant_binders,constant_binder_name)with|(true,_,Some(s))->(new_varmkfrees,ctxt)|(true,true,None)->(fst(new_var_inctxtmkfreename),ctxt)|_->new_var_inctxtmkfreenameletunbind_inctxtb=letctxt=reset_ctxt(binder_closedb)ctxtinlet(x,ctxt)=letconstant=binder_constantbinunbind_varconstantb.b_mkfree(binder_nameb)ctxtin(x,substb(b.b_mkfreex),ctxt)letunbind2_inctxtb1b2=letctxt=reset_ctxt(binder_closedb1&&binder_closedb2)ctxtinlet(x,ctxt)=letconstant=binder_constantb1&&binder_constantb2inunbind_varconstantb1.b_mkfree(binder_nameb1)ctxtinletv=b1.b_mkfreexin(x,substb1v,substb2v,ctxt)letunmbind_inctxtb=letctxt=reset_ctxt(mbinder_closedb)ctxtinletctxt_ref=refctxtinletxs=letfniname=letconstant=notb.mb_binds.(i)inlet(x,ctxt)=unbind_varconstantb.mb_mkfreename!ctxt_refinctxt_ref:=ctxt;xinArray.mapifnb.mb_namesin(xs,msubstb(Array.mapb.mb_mkfreexs),!ctxt_ref)letunmbind2_inctxtb1b2=letctxt=reset_ctxt(mbinder_closedb1&&mbinder_closedb2)ctxtinletctxt_ref=refctxtinletxs=letfniname=letconstant=notb1.mb_binds.(i)&¬b1.mb_binds.(i)inlet(x,ctxt)=unbind_varconstantb1.mb_mkfreename!ctxt_refinctxt_ref:=ctxt;xinArray.mapifnb1.mb_namesinletvs=Array.mapb1.mb_mkfreexsin(xs,msubstb1vs,msubstb2vs,!ctxt_ref)end(* Default renaming policy (functor instantiation) **************************)includeCtxt(struct(* Our renaming policy views variable names as a pair of a “prefix” and of a
decimal “suffix”. For example, ["xyz42"] corresponds to [("xyz", 42)]. If
a name does not have a decimal suffix, then the suffix value [0] is used.
This means that ["xyz"] corresponds to [("xyz", 0)]. Moreover, all zeroes
leading the decimal suffix are considered to be part of the prefix. Thus,
name ["xyz0042"] corresponds to [("xyz00", 42)], and ["xyz0"] corresponds
to [("xyz0", 0)].
Following the above convention, one way to represent contexts would be to
have a map from prefixes to sets of suffixes. We could then check whether
a name is in the context by checking whether its prefix is already mapped
to a set containing its suffix. However, this is not what we do.
Instead, we over-approximate the set of names contained in a context, and
only store a map from prefixes to the largest used suffix. So, when there
is a mapping from ["xyz"] to [42] in the context, this means that all the
names of the form [("xyz", i)] are taken for [i <= 42]. *)typectxt=intSMap.tletempty_ctxt=SMap.emptyletreset_context_for_closed_terms=falseletskip_constant_binders=falseletconstant_binder_name=Noneletsplit_name:string->string*int=funname->letlen=String.lengthnamein(* [i] is the index of the first first character of the suffix. *)leti=letis_digitc='0'<=c&&c<='9'inletfirst_digit=refleninletfirst_non_0=refleninwhile!first_digit>0&&is_digitname.[!first_digit-1]dodecrfirst_digit;ifname.[!first_digit]<>'0'thenfirst_non_0:=!first_digitdone;!first_non_0inifi=lenthen(name,0)else(String.subname0i,int_of_string(String.subnamei(len-i)))letget_suffix:string->int->ctxt->int*ctxt=funnamesuffixctxt->letn=trySMap.findnamectxtwithNot_found->-1inletsuffix=ifsuffix>nthensuffixelsen+1in(suffix,SMap.addnamesuffixctxt)letmerge_name:string->int->string=funprefixsuffix->ifsuffix>0thenprefix^string_of_intsuffixelseprefixletnew_name:string->ctxt->string*ctxt=funnamectxt->let(prefix,suffix)=split_namenameinlet(suffix,ctxt)=get_suffixprefixsuffixctxtin(merge_nameprefixsuffix,ctxt)letreserve_name:string->ctxt->ctxt=funnamectxt->let(prefix,suffix)=split_namenameintryletn=SMap.findprefixctxtinifsuffix<=nthenctxtelseSMap.addprefixsuffixctxtwithNot_found->SMap.addprefixsuffixctxtend)