{-# LANGUAGE CPP, FlexibleInstances #-} -- | The Zipper is a data structure which maintains a location in -- a tree, and allows O(1) movement and local changes -- (to be more precise, in our case it is O(k) where k is the number -- of children of the node at question; typically this is a very small number). -- module Data.Generics.Fixplate.Zipper where -------------------------------------------------------------------------------- import Prelude hiding (foldl,foldr,mapM,mapM_,concat,concatMap) import Data.Foldable import Data.Traversable () import Data.Maybe import Text.Show () import Text.Read import Data.Generics.Fixplate.Base import Data.Generics.Fixplate.Open import Data.Generics.Fixplate.Misc -------------------------------------------------------------------------------- -- * Types -- | A context node. type Node f = Either (Mu f) (Path f) -- | The context or path type. The invariant we must respect is that there is exactly -- one child with the 'Right' constructor. data Path f = Top | Path { unPath :: f (Node f) } -- | The zipper type itself, which encodes a locations in thre tree @Mu f@. data Loc f = Loc { focus :: Mu f , path :: Path f } -------------------------------------------------------------------------------- instance EqF f => Eq (Path f) where Top == Top = True Path p1 == Path p2 = equalF p1 p2 _ == _ = False instance EqF f => Eq (Loc f) where Loc f1 p1 == Loc f2 p2 = f1 == f2 && p1 == p2 instance ShowF f => Show (Path f) where showsPrec d Top = showString "Top" showsPrec d (Path xs) = showParen (d>10) $ showString "Path " . showsPrecF 11 xs instance ShowF f => Show (Loc f) where showsPrec d (Loc foc path) = showParen (d>10) $ showString "Loc " . showsPrec 11 foc . showChar ' ' . showsPrec 11 path instance ReadF f => Read (Path f) where #ifdef __GLASGOW_HASKELL__ readPrec = parens $ (do { Ident "Top" <- lexP ; return Top }) +++ (prec app_prec $ do { Ident "Path" <- lexP ; p <- step readPrecF ; return (Path p) }) #else readsPrec d r = readParen (d > app_prec) (\r -> [ (Top, s) | ("Top", s) <- lex r]) r ++ (\r -> [ (Path p, t) | ("Path", s) <- lex r , (f,t) <- readsPrecF (app_prec+1) s]) r #endif instance ReadF f => Read (Loc f) where #ifdef __GLASGOW_HASKELL__ readPrec = parens $ (prec app_prec $ do { Ident "Loc" <- lexP ; f <- step readPrec ; p <- step readPrec ; return (Loc f p) }) #else readsPrec d r = readParen (d > app_prec) (\r -> [ (Loc f p, u) | ("Loc", s) <- lex r , (f,t) <- readsPrec (app_prec+1) s , (p,u) <- readsPrec (app_prec+1) t]) r #endif -------------------------------------------------------------------------------- -- * Converting to and from zippers -- | Creates a zipper from a tree, with the focus at the root. root :: Mu f -> Loc f root t = Loc t Top -- | Restores a tree from a zipper. defocus :: Traversable f => Loc f -> Mu f defocus (Loc foc path) = go foc path where go t Top = t go t (Path xs) = go (Fix s) path' where (Just path', s) = mapAccumL h Nothing xs h old (Left y) = (old , y) h _ (Right p) = (Just p , t) -- | We attribute all nodes with a zipper focused at that location. locations :: Traversable f => Mu f -> Attr f (Loc f) locations tree = go (root tree) tree where go loc (Fix t) = Fix (Ann loc t') where t' = enumerateWith_ (\j x -> go (unsafeMoveDown j loc) x) t -- | The list of all locations. locationsList :: Traversable f => Mu f -> [Loc f] locationsList = toList . Attrib . locations -- | The zipper version of 'forget'. locForget :: Functor f => Loc (Ann f a) -> Loc f locForget (Loc foc path) = Loc (forget foc) (go path) where go :: Functor f => Path (Ann f a) -> Path f go Top = Top go (Path (Ann _ nodes)) = Path (fmap h nodes) h :: Functor f => Node (Ann f a) -> Node f h (Left t) = Left (forget t) h (Right p) = Right (go p) -------------------------------------------------------------------------------- -- * Manipulating the subtree at focus -- | Extracts the subtree at focus. Synonym of 'focus'. extract :: Loc f -> Mu f extract = focus -- | Replaces the subtree at focus. replace :: Mu f -> Loc f -> Loc f replace new loc = loc { focus = new } -- | Modifies the subtree at focus. modify :: (Mu f -> Mu f) -> Loc f -> Loc f modify h loc = replace (h (focus loc)) loc -------------------------------------------------------------------------------- -- * Safe movements -- | Moves down to the child with the given index. -- The leftmost children has index @0@. moveDown :: Traversable f => Int -> Loc f -> Maybe (Loc f) moveDown pos (Loc foc path) = new where new = case mfoc' of Nothing -> Nothing Just foc' -> Just $ Loc foc' (Path nodes') ((mfoc',_),nodes') = mapAccumL g (Nothing,0) (unFix foc) g (old,j) x = if j==pos then ((Just x , j+1), Right path ) else ((old , j+1), Left x ) -- | Moves down to the leftmost child. moveDownL :: Traversable f => Loc f -> Maybe (Loc f) moveDownL (Loc foc path) = new where new = case mfoc' of Nothing -> Nothing Just foc' -> Just $ Loc foc' (Path nodes') (mfoc',nodes') = mapAccumL g Nothing (unFix foc) g old x = case old of Nothing -> (Just x , Right path ) _ -> (old , Left x ) -- | Moves down to the rightmost child. moveDownR :: Traversable f => Loc f -> Maybe (Loc f) moveDownR (Loc foc path) = new where new = case mfoc' of Nothing -> Nothing Just foc' -> Just $ Loc foc' (Path nodes') (mfoc',nodes') = mapAccumR g Nothing (unFix foc) g old x = case old of Nothing -> (Just x , Right path ) _ -> (old , Left x ) -------------------------------------------------------------------------------- -- | Moves up. moveUp :: Traversable f => Loc f -> Maybe (Loc f) moveUp (Loc foc path) = case path of Top -> Nothing Path nodes -> case mpath of Nothing -> error "moveUp: shouldn't happen" Just path' -> Just $ case path' of Path nodes' -> Loc (Fix foc') (Path nodes') Top -> Loc (Fix foc') Top where (mpath,foc') = mapAccumL g Nothing nodes g old ei = case ei of Right p -> (Just p , foc) Left x -> (old , x ) -------------------------------------------------------------------------------- moveRight :: Traversable f => Loc f -> Maybe (Loc f) moveRight (Loc foc path) = case path of Top -> Nothing Path nodes -> case two of Two foc' -> Just $ Loc foc' (Path nodes') _ -> Nothing where (two,nodes') = mapAccumL g Empty nodes g old ei = case ei of Right p -> (One p , Left foc ) Left x -> case old of One p -> (Two x , Right p ) _ -> (old , ei ) moveLeft :: Traversable f => Loc f -> Maybe (Loc f) moveLeft (Loc foc path) = case path of Top -> Nothing Path nodes -> case two of Two foc' -> Just $ Loc foc' (Path nodes') _ -> Nothing where (two,nodes') = mapAccumR g Empty nodes g old ei = case ei of Right p -> (One p , Left foc ) Left x -> case old of One p -> (Two x , Right p ) _ -> (old , ei ) -------------------------------------------------------------------------------- -- * Testing for borders -- | Checks whether we are at the top (root). isTop :: Loc f -> Bool isTop (Loc _ p) = case p of { Top -> True ; _ -> False } -- | Checks whether we cannot move down. isBottom :: Traversable f => Loc f -> Bool isBottom = isNothing . moveDownL isLeftmost :: Traversable f => Loc f -> Bool isLeftmost = isNothing . moveLeft isRightmost :: Traversable f => Loc f -> Bool isRightmost = isNothing . moveRight -------------------------------------------------------------------------------- -- * Location queries -- | Gives back the index of the given location among the children of its parent. -- Indexing starts from zero. In case of root node (no parent), we also return zero. horizontalPos :: Foldable f => Loc f -> Int horizontalPos (Loc _ path) = case path of Top -> 0 Path nodes -> case mpos of Right pos -> pos Left _ -> error "horizontalPos: shouldn't happen" where mpos = foldl g (Left 0) nodes g old ei = case old of Right _ -> old Left j -> case ei of Left _ -> Left (j+1) Right _ -> Right j -- | We return the full path from the root as a sequence of child indices. -- This means that -- -- > loc == foldl (flip unsafeMoveDown) (moveTop loc) (fullPathDown loc) -- fullPathDown :: Foldable f => Loc f -> [Int] fullPathDown = reverse . fullPathUp -- | The following equations hold for 'fullPathUp' and 'fullPathDown': -- -- > fullPathUp == reverse . fullPathDown -- > loc == foldr unsafeMoveDown (moveTop loc) (fullPathUp loc) -- fullPathUp :: Foldable f => Loc f -> [Int] fullPathUp (Loc _ pth) = go pth where go path = case path of Top -> [] Path nodes -> case mpos of Right (pos,parent) -> pos : go parent Left _ -> error "fullPathUp: shouldn't happen" where mpos = foldl g (Left 0) nodes g old ei = case old of Right _ -> old Left j -> case ei of Left _ -> Left (j+1) Right p -> Right (j,p) -------------------------------------------------------------------------------- -- * Compound movements -- | Moves to the top, by repeatedly moving up. moveTop :: Traversable f => Loc f -> Loc f moveTop = tillNothing moveUp -- | Moves left until it can. -- It should be faster than repeated left steps. leftmost :: Traversable f => Loc f -> Loc f leftmost orig@(Loc foc path) = case path of Top -> orig Path nodes -> case both of Both {} -> Loc foc' (Path nodes') _ -> error "leftmost: shouldn't happen" where -- this tricky implementation uses lazyness -- so that we only need a single traversal (foc',pnew) = case both of { Both f p -> (f,p) ; _ -> error "leftmost: shouldn't happen" } (both,nodes') = mapAccumL g None nodes g old ei = case old of None -> case ei of Left x -> (First x , Right pnew) Right p -> (Both foc p , ei ) -- we are already at the leftmost position First f -> case ei of Left x -> (old , ei ) Right p -> (Both f p , Left foc ) Both {} -> (old, ei) -- | Moves right until it can. -- It should be faster than repeated right steps. rightmost :: Traversable f => Loc f -> Loc f rightmost orig@(Loc foc path) = case path of Top -> orig Path nodes -> case both of Both {} -> Loc foc' (Path nodes') _ -> error "rightmost: shouldn't happen" where -- this tricky implementation uses lazyness -- so that we only need a single traversal (foc',pnew) = case both of { Both f p -> (f,p) ; _ -> error "rightmost: shouldn't happen" } (both,nodes') = mapAccumR g None nodes g old ei = case old of None -> case ei of Left x -> (First x , Right pnew) Right p -> (Both foc p , ei ) -- we are already at the rightmost position First f -> case ei of Left x -> (old , ei ) Right p -> (Both f p , Left foc ) Both {} -> (old, ei) -------------------------------------------------------------------------------- -- * Unsafe movements unsafeMoveDown :: Traversable f => Int -> Loc f -> Loc f unsafeMoveDown i = unsafe (moveDown i) "unsafeMoveDown: cannot move down" unsafeMoveDownL :: Traversable f => Loc f -> Loc f unsafeMoveDownR :: Traversable f => Loc f -> Loc f unsafeMoveUp :: Traversable f => Loc f -> Loc f unsafeMoveDownL = unsafe moveDownL "unsafeMoveDownL: cannot move down" unsafeMoveDownR = unsafe moveDownR "unsafeMoveDownR: cannot move down" unsafeMoveUp = unsafe moveUp "unsafeMoveUp: cannot move up" unsafeMoveLeft, unsafeMoveRight :: Traversable f => Loc f -> Loc f unsafeMoveLeft = unsafe moveLeft "unsafeMoveLeft: cannot move left" unsafeMoveRight = unsafe moveRight "unsafeMoveRight: cannot move right" --------------------------------------------------------------------------------