@@ -736,7 +736,87 @@ substitute name idx replacement = substitute' idx
736736 where
737737 go = substitute' index
738738
739- -- | Increase the index of all bound variables matching the given variable name
739+ {- | Rewrite the De Bruijn index of every reference to @namespace@ that is free
740+ with respect to @minIndex@, using @adjust minIndex index@. Binders for other
741+ names are transparent; a binder for @namespace@ raises @minIndex@ by one (see
742+ Note [Sequential scoping of Let bindings] for the @Let@ case). This is the
743+ shared traversal behind 'shift' (which makes room for a new binder) and
744+ 'unshift' (which closes the gap left by a removed one); keeping both on one
745+ traversal stops them from drifting apart.
746+ -}
747+ overFreeIndex
748+ ∷ (Index → Index → Index )
749+ -- ^ Given the current @minIndex@ and a reference's index, the new index
750+ → Name
751+ -- ^ The variable name to match (a.k.a. the namespace)
752+ → Index
753+ -- ^ The minimum bound at or above which references are considered free
754+ → RawExp ann
755+ → RawExp ann
756+ overFreeIndex adjust namespace = go
757+ where
758+ go minIndex expression =
759+ case expression of
760+ Ref ann (Local name) index
761+ | name == namespace →
762+ Ref ann (Local name) (adjust minIndex index)
763+ Abs ann argument body →
764+ Abs ann argument (go minIndex' body)
765+ where
766+ minIndex'
767+ | paramName argument == Just namespace = minIndex + 1
768+ | otherwise = minIndex
769+ -- See Note [Sequential scoping of Let bindings]
770+ Let ann binds body →
771+ Let ann binds' body'
772+ where
773+ (bodyMinIndex, binds') = mapAccumL withGrouping minIndex binds
774+ body' = go bodyMinIndex body
775+ withGrouping minIdx grouping =
776+ case grouping of
777+ Standalone (annotation, boundName, expr) →
778+ ( if boundName == namespace then minIdx + 1 else minIdx
779+ , Standalone (annotation, boundName, go minIdx expr)
780+ )
781+ RecursiveGroup recBinds →
782+ ( minIdx'
783+ , RecursiveGroup $
784+ recBinds <&> \ (nameAnn, boundName, expr) →
785+ (nameAnn, boundName, go minIdx' expr)
786+ )
787+ where
788+ minIdx' =
789+ minIdx
790+ + fromIntegral
791+ (length (filter (== namespace) (bindingNames grouping)))
792+ App ann argument function →
793+ App ann (go minIndex argument) (go minIndex function)
794+ LiteralArray ann as →
795+ LiteralArray ann (go minIndex <$> as)
796+ LiteralObject ann props →
797+ LiteralObject ann (go minIndex <<$>> props)
798+ ReflectCtor ann a →
799+ ReflectCtor ann (go minIndex a)
800+ DataArgumentByIndex ann idx a →
801+ DataArgumentByIndex ann idx (go minIndex a)
802+ Eq ann a b →
803+ Eq ann (go minIndex a) (go minIndex b)
804+ ArrayLength ann a →
805+ ArrayLength ann (go minIndex a)
806+ ArrayIndex ann a indx →
807+ ArrayIndex ann (go minIndex a) indx
808+ ObjectProp ann a prop →
809+ ObjectProp ann (go minIndex a) prop
810+ ObjectUpdate ann a patches →
811+ ObjectUpdate ann (go minIndex a) (go minIndex <<$>> patches)
812+ IfThenElse ann p th el →
813+ IfThenElse ann (go minIndex p) (go minIndex th) (go minIndex el)
814+ _ → expression
815+
816+ {- | Increase the index of all references to the given name bound at or above
817+ @minIndex@. Used to make room when a new binder for that name is introduced,
818+ e.g. when substituting a term under a λ that shadows the name.
819+ -}
740820shift
741821 ∷ Int
742822 -- ^ The amount to shift by
@@ -747,72 +827,29 @@ shift
747827 → RawExp ann
748828 -- ^ The expression to shift
749829 → RawExp ann
750- shift offset namespace minIndex expression =
751- case expression of
752- Ref ann (Local name) index →
753- Ref ann (Local name) $
754- index
755- + if name == namespace && minIndex <= index
756- then fromIntegral offset
757- else 0
758- Abs ann argument body →
759- Abs ann argument (shift offset namespace minIndex' body)
760- where
761- minIndex'
762- | paramName argument == Just namespace = minIndex + 1
763- | otherwise = minIndex
764- -- See Note [Sequential scoping of Let bindings]
765- Let ann binds body →
766- Let ann binds' body'
767- where
768- (bodyMinIndex, binds') = mapAccumL withGrouping minIndex binds
769- body' = shift offset namespace bodyMinIndex body
770- withGrouping minIdx grouping =
771- case grouping of
772- Standalone (annotation, boundName, expr) →
773- ( if boundName == namespace then minIdx + 1 else minIdx
774- , Standalone
775- ( annotation
776- , boundName
777- , shift offset namespace minIdx expr
778- )
779- )
780- RecursiveGroup recBinds →
781- ( minIdx'
782- , RecursiveGroup $
783- recBinds <&> \ (nameAnn, boundName, expr) →
784- (nameAnn, boundName, shift offset namespace minIdx' expr)
785- )
786- where
787- minIdx' =
788- minIdx
789- + fromIntegral
790- (length (filter (== namespace) (bindingNames grouping)))
791- App ann argument function →
792- App ann (go argument) (go function)
793- LiteralArray ann as →
794- LiteralArray ann (go <$> as)
795- LiteralObject ann props →
796- LiteralObject ann (go <<$>> props)
797- ReflectCtor ann a →
798- ReflectCtor ann (go a)
799- DataArgumentByIndex ann idx a →
800- DataArgumentByIndex ann idx (go a)
801- Eq ann a b →
802- Eq ann (go a) (go b)
803- ArrayLength ann a →
804- ArrayLength ann (go a)
805- ArrayIndex ann a indx →
806- ArrayIndex ann (go a) indx
807- ObjectProp ann a prop →
808- ObjectProp ann (go a) prop
809- ObjectUpdate ann a patches →
810- ObjectUpdate ann (go a) (go <<$>> patches)
811- IfThenElse ann p th el →
812- IfThenElse ann (go p) (go th) (go el)
813- _ → expression
814- where
815- go = shift offset namespace minIndex
830+ shift offset =
831+ overFreeIndex \ minIndex index →
832+ if minIndex <= index then index + fromIntegral offset else index
833+
834+ {- | Decrease by one the index of references to the given name bound strictly
835+ above @minIndex@: the inverse of @shift 1@, to be applied after a binder for
836+ the name is removed (e.g. by beta reduction) so that references which pointed
837+ past that binder are lowered back into place. References at exactly @minIndex@
838+ are the removed binder itself and have already been consumed by the
839+ accompanying substitution, so the strict @minIndex < index@ guard both leaves
840+ genuine inner references untouched and keeps the 'Natural' index from
841+ underflowing.
842+ -}
843+ unshift
844+ ∷ Name
845+ -- ^ The variable name to match (a.k.a. the namespace)
846+ → Index
847+ -- ^ References bound strictly above this bound are lowered
848+ → RawExp ann
849+ → RawExp ann
850+ unshift =
851+ overFreeIndex \ minIndex index →
852+ if minIndex < index then index - 1 else index
816853
817854$ (makePrisms ''AlgebraicType)
818855$ (makePrisms ''Parameter)
0 commit comments