123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288(************************************************************************)(* * The Coq Proof Assistant / The Coq Development Team *)(* v * Copyright INRIA, CNRS and contributors *)(* <O___,, * (see version control and CREDITS file for authors & dates) *)(* \VV/ **************************************************************)(* // * This file is distributed under the terms of the *)(* * GNU Lesser General Public License Version 2.1 *)(* * (see LICENSE file for the text of the license) *)(************************************************************************)(* Created by Bruno Barras for Coq V7.0, Mar 2001 *)(* Support for explicit substitutions *)openUtil(*********************)(* Lifting *)(*********************)(* Explicit lifts and basic operations *)(* Invariant to preserve in this module: no lift contains two consecutive
[ELSHFT] nor two consecutive [ELLFT]. *)(* Terminology comes from substitution calculi (see e.g. Hardin et al.).
That is, what is called a lift in Coq is made of what is called in
substitution calculi a shift (the shift to add) and of what is
called a lift (the threshold above which to apply the shift), which
can be iterated as represented in the type [lift] *)typelift=|ELID|ELSHFToflift*int(* ELSHFT(l,n) == lift of n, then apply lift l *)|ELLFTofint*lift(* ELLFT(n,l) == apply l to de Bruijn > n *)(* i.e under n binders *)letel_id=ELID(* compose a relocation of magnitude n *)letel_shft_recn=function|ELSHFT(el,k)->ELSHFT(el,k+n)|el->ELSHFT(el,n)letel_shftnel=ifInt.equaln0thenelelseel_shft_recnel(* cross n binders *)letel_liftn_recn=function|ELID->ELID|ELLFT(k,el)->ELLFT(n+k,el)|el->ELLFT(n,el)letel_liftnnel=ifInt.equaln0thenelelseel_liftn_recnelletel_liftel=el_liftn_rec1el(* relocation of de Bruijn n in an explicit lift *)letrecreloc_reln=function|ELID->n|ELLFT(k,el)->ifn<=kthennelse(reloc_rel(n-k)el)+k|ELSHFT(el,k)->(reloc_rel(n+k)el)letrecis_lift_id=function|ELID->true|ELSHFT(e,n)->Int.equaln0&&is_lift_ide|ELLFT(_,e)->is_lift_ide(*********************)(* Substitutions *)(*********************)(* Variant of skewed lists enriched w.r.t. a monoid. See the Range module.
In addition to the indexed data, every node contains a monoid element, in our
case, integers. It corresponds to the number of partial shifts to apply when
reaching this subtree. The total shift is obtained by summing all the partial
shifts encountered in the tree traversal. For efficiency, we also cache the
sum of partial shifts of the whole subtree as the last argument of the [Node]
constructor.
A more intuitive but inefficient representation of this data structure would
be a list of terms interspeded with shifts, as in
type 'a subst = NIL | CONS of 'a or_var * 'a subst | SHIFT of 'a subst
On this inefficient representation, the typing rules would be:
· ⊢ NIL : ·
Γ ⊢ σ : Δ and Γ ⊢ t : A{σ} implies Γ ⊢ CONS (t, σ) : Δ, A
Γ ⊢ σ : Δ implies Γ, A ⊢ SHIFT σ : Δ
The efficient representation is isomorphic to this naive variant, except that
shifts are grouped together, and we use skewed lists instead of lists.
*)typeshf=intletcmpnm=n+mletidn=0type'aor_var=Argof'a|Varofinttype'atree=|Leafofshf*'aor_var|Nodeofshf*'aor_var*'atree*'atree*shf(*
Invariants:
- All trees are complete.
- Define get_shift inductively as [get_shift (Leaf (w, _)) := w] and
[get_shift (Node (w, _, t1, t2, _)) := w + t1 + t2] then for every tree
of the form Node (_, _, t1, t2, sub), we must have
sub = get_shift t1 + get_shift t2.
In the naive semantics:
Leaf (w, x) := SHIFT^w (CONS (x, NIL))
Node (w, x, t1, t2, _) := SHIFT^w (CONS (x, t1 @ t2))
*)type'asubs=Nilofshf*int|Consofint*'atree*'asubs(*
In the naive semantics mentioned above, we have the following.
Nil (w, n) stands for SHIFT^w (ID n) where ID n is a compact form of identity
substitution, defined inductively as
ID 0 := NIL
ID (S n) := CONS (Var 1, SHIFT (ID n))
Cons (h, t, s) stands for (t @ s) and h is the total number of values in the
tree t. In particular, it is always of the form 2^n - 1 for some n.
*)(* Returns the number of shifts contained in the whole tree. *)leteval=function|Leaf(w,_)->w|Node(w1,_,_,_,w2)->cmpw1w2letleafx=Leaf(idn,x)letnodext1t2=Node(idn,x,t1,t2,cmp(evalt1)(evalt2))letrectree_gethwti=matchtwith|Leaf(w',x)->letw=cmpww'inifi=0thenw,Inlxelseassertfalse|Node(w',x,t1,t2,_)->letw=cmpww'inifi=0thenw,Inlxelseleth=h/2inifi<=hthentree_gethwt1(i-1)elsetree_geth(cmpw(evalt1))t2(i-h-1)letrecgetwli=matchlwith|Nil(w',n)->letw=cmpww'inifi<nthenw,Inl(Var(i+1))elsen+w,Inr(i-n)(* FIXME: double check *)|Cons(h,t,rem)->ifi<hthentree_gethwtielseget(cmp(evalt)w)rem(i-h)letgetli=getidnlilettree_writew=function|Leaf(w',x)->Leaf(cmpww',x)|Node(w',x,t1,t2,wt)->Node(cmpww',x,t1,t2,wt)letwritewl=matchlwith|Nil(w',n)->Nil(cmpww',n)|Cons(h,t,rem)->Cons(h,tree_writewt,rem)letconsxl=matchlwith|Cons(h1,t1,Cons(h2,t2,rem))->ifInt.equalh1h2thenCons(1+h1+h2,nodext1t2,rem)elseCons(1,leafx,l)|_->Cons(1,leafx,l)letexpand_relns=letk,v=gets(n-1)inmatchvwith|Inl(Argv)->Inl(k,v)|Inl(Vari)->Inr(k+i,None)|Inri->Inr(k+i+1,Some(i+1))letis_subs_id=function|Nil(w,_)->Int.equalw0|Cons(_,_,_)->falseletsubs_consvs=cons(Argv)sletrecpush_varsis=ifInt.equali0thenselsepush_vars(predi)(cons(Vari)s)letsubs_liftnns=ifInt.equaln0thenselsematchswith|Nil(0,m)->Nil(0,m+n)(* Preserve identity substitutions *)|Nil_|Cons_->lets=writensinpush_varsnsletsubs_lifts=matchswith|Nil(0,m)->Nil(0,m+1)(* Preserve identity substitutions *)|Nil_|Cons_->cons(Var1)(write1s)letsubs_idn=Nil(0,n)letsubs_shft(n,s)=writens(* pop is the n-ary tailrec variant of a function whose typing rules would be
given as follows. Assume Γ ⊢ e : Δ, A, then
- Γ := Ξ, A, Ω for some Ξ and Ω with |Ω| := fst (pop e)
- Ξ ⊢ snd (pop e) : Δ
*)letrecpopnie=ifInt.equaln0theni,eelsematchewith|ELID->i,e|ELLFT(k,e)->ifk<=nthenpop(n-k)ieelsei,ELLFT(k-n,e)|ELSHFT(e,k)->popn(i+k)eletapplymke=function|Vari->Var(reloc_relie)|Argv->Arg(mkev)letrectree_mapmke=function|Leaf(w,x)->let(n,e)=popw0einLeaf(w+n,applymkex),e|Node(w,x,t1,t2,_)->let(n,e)=popw0einletx=applymkexinlett1,e=tree_mapmket1inlett2,e=tree_mapmket2inNode(w+n,x,t1,t2,cmp(evalt1)(evalt2)),eletreclift_iden=matchewith|ELID->Nil(0,n)|ELSHFT(e,k)->writek(lift_iden)|ELLFT(k,e)->ifk<=nthensubs_liftnk(lift_ide(n-k))elseassertfalseletreclift_substmkes=matchswith|Nil(w,m)->let(n,e)=popw0einwrite(w+n)(lift_idem)|Cons(h,t,rem)->lett,e=tree_mapmketinletrem=lift_substmkereminCons(h,t,rem)moduleInternal=structtype'aor_rel=RELofint|VALofint*'aletto_relshift=function|Vari->REL(i+shift)|Argv->VAL(shift,v)letrecget_tree_substshiftaccu=function|Leaf(w,x)->to_rel(shift+w)x::accu|Node(w,x,l,r,_)->letaccu=get_tree_subst(shift+w+evall)accurinletaccu=get_tree_subst(shift+w)acculinto_rel(shift+w)x::acculetrecget_substshiftaccu=function|Nil(w,n)->List.initn(funi->REL(w+i+shift+1))|Cons(_,t,s)->letaccu=get_subst(shift+evalt)accusinget_tree_substshiftaccutletrecget_shiftaccu=function|Nil(w,n)->accu+w+n|Cons(_,t,s)->get_shift(evalt+accu)sletrepr(s:'asubs)=letshift=get_shift0sinletsubs=get_subst0[]sinsubs,shiftend