forked from GitHub/gf-core
258 lines
6.7 KiB
Haskell
258 lines
6.7 KiB
Haskell
----------------------------------------------------------------------
|
|
-- |
|
|
-- Module : Zipper
|
|
-- Maintainer : AR
|
|
-- Stability : (stable)
|
|
-- Portability : (portable)
|
|
--
|
|
-- > CVS $Date: 2005/06/11 20:27:05 $
|
|
-- > CVS $Author: aarne $
|
|
-- > CVS $Revision: 1.9 $
|
|
--
|
|
-- Gérard Huet's zipper (JFP 7 (1997)). AR 10\/8\/2001
|
|
-----------------------------------------------------------------------------
|
|
|
|
module GF.Data.Zipper (-- * types
|
|
Tr(..),
|
|
Path(..),
|
|
Loc(..),
|
|
-- * basic (original) functions
|
|
leaf,
|
|
goLeft, goRight, goUp, goDown,
|
|
changeLoc,
|
|
changeNode,
|
|
forgetNode,
|
|
-- * added sequential representation
|
|
goAhead,
|
|
goBack,
|
|
-- ** n-ary versions
|
|
goAheadN,
|
|
goBackN,
|
|
-- * added mappings between locations and trees
|
|
loc2tree,
|
|
loc2treeMarked,
|
|
tree2loc,
|
|
goRoot,
|
|
goLast,
|
|
goPosition,
|
|
getPosition,
|
|
keepPosition,
|
|
-- * added some utilities
|
|
traverseCollect,
|
|
scanTree,
|
|
mapTr,
|
|
mapTrM,
|
|
mapPath,
|
|
mapPathM,
|
|
mapLoc,
|
|
mapLocM,
|
|
foldTr,
|
|
foldTrM,
|
|
mapSubtrees,
|
|
mapSubtreesM,
|
|
changeRoot,
|
|
nthSubtree,
|
|
arityTree
|
|
) where
|
|
|
|
import GF.Data.Operations
|
|
|
|
newtype Tr a = Tr (a,[Tr a]) deriving (Show,Eq)
|
|
|
|
data Path a =
|
|
Top
|
|
| Node ([Tr a], (Path a, a), [Tr a])
|
|
deriving Show
|
|
|
|
leaf :: a -> Tr a
|
|
leaf a = Tr (a,[])
|
|
|
|
newtype Loc a = Loc (Tr a, Path a) deriving Show
|
|
|
|
goLeft, goRight, goUp, goDown :: Loc a -> Err (Loc a)
|
|
goLeft (Loc (t,p)) = case p of
|
|
Top -> Bad "left of top"
|
|
Node (l:left, upv, right) -> return $ Loc (l, Node (left,upv,t:right))
|
|
Node _ -> Bad "left of first"
|
|
goRight (Loc (t,p)) = case p of
|
|
Top -> Bad "right of top"
|
|
Node (left, upv, r:right) -> return $ Loc (r, Node (t:left,upv,right))
|
|
Node _ -> Bad "right of first"
|
|
goUp (Loc (t,p)) = case p of
|
|
Top -> Bad "up of top"
|
|
Node (left, (up,v), right) ->
|
|
return $ Loc (Tr (v, reverse left ++ (t:right)), up)
|
|
goDown (Loc (t,p)) = case t of
|
|
Tr (v,(t1:trees)) -> return $ Loc (t1,Node ([],(p,v),trees))
|
|
_ -> Bad "down of empty"
|
|
|
|
changeLoc :: Loc a -> Tr a -> Err (Loc a)
|
|
changeLoc (Loc (_,p)) t = return $ Loc (t,p)
|
|
|
|
changeNode :: (a -> a) -> Loc a -> Loc a
|
|
changeNode f (Loc (Tr (n,ts),p)) = Loc (Tr (f n, ts),p)
|
|
|
|
forgetNode :: Loc a -> Err (Loc a)
|
|
forgetNode (Loc (Tr (n,[t]),p)) = return $ Loc (t,p)
|
|
forgetNode _ = Bad $ "not a one-branch tree"
|
|
|
|
-- added sequential representation
|
|
|
|
-- | a successor function
|
|
goAhead :: Loc a -> Err (Loc a)
|
|
goAhead s@(Loc (t,p)) = case (t,p) of
|
|
(Tr (_,_:_),Node (_,_,_:_)) -> goDown s
|
|
(Tr (_,[]), _) -> upsRight s
|
|
(_, _) -> goDown s
|
|
where
|
|
upsRight t = case goRight t of
|
|
Ok t' -> return t'
|
|
Bad _ -> goUp t >>= upsRight
|
|
|
|
-- | a predecessor function
|
|
goBack :: Loc a -> Err (Loc a)
|
|
goBack s@(Loc (t,p)) = case goLeft s of
|
|
Ok s' -> downRight s'
|
|
_ -> goUp s
|
|
where
|
|
downRight s = case goDown s of
|
|
Ok s' -> case goRight s' of
|
|
Ok s'' -> downRight s''
|
|
_ -> downRight s'
|
|
_ -> return s
|
|
|
|
-- n-ary versions
|
|
|
|
goAheadN :: Int -> Loc a -> Err (Loc a)
|
|
goAheadN i st
|
|
| i < 1 = return st
|
|
| otherwise = goAhead st >>= goAheadN (i-1)
|
|
|
|
goBackN :: Int -> Loc a -> Err (Loc a)
|
|
goBackN i st
|
|
| i < 1 = return st
|
|
| otherwise = goBack st >>= goBackN (i-1)
|
|
|
|
-- added mappings between locations and trees
|
|
|
|
loc2tree :: Loc a -> Tr a
|
|
loc2tree (Loc (t,p)) = case p of
|
|
Top -> t
|
|
Node (left,(p',v),right) ->
|
|
loc2tree (Loc (Tr (v, reverse left ++ (t : right)),p'))
|
|
|
|
loc2treeMarked :: Loc a -> Tr (a, Bool)
|
|
loc2treeMarked (Loc (Tr (a,ts),p)) =
|
|
loc2tree (Loc (Tr (mark a, map (mapTr nomark) ts), mapPath nomark p))
|
|
where
|
|
(mark, nomark) = (\a -> (a,True), \a -> (a, False))
|
|
|
|
tree2loc :: Tr a -> Loc a
|
|
tree2loc t = Loc (t,Top)
|
|
|
|
goRoot :: Loc a -> Loc a
|
|
goRoot = tree2loc . loc2tree
|
|
|
|
goLast :: Loc a -> Err (Loc a)
|
|
goLast = rep goAhead where
|
|
rep f s = err (const (return s)) (rep f) (f s)
|
|
|
|
goPosition :: [Int] -> Loc a -> Err (Loc a)
|
|
goPosition p = go p . goRoot where
|
|
go [] s = return s
|
|
go (p:ps) s = goDown s >>= apply p goRight >>= go ps
|
|
|
|
getPosition :: Loc a -> [Int]
|
|
getPosition = reverse . getp where
|
|
getp (Loc (t,p)) = case p of
|
|
Top -> []
|
|
Node (left,(p',v),_) -> length left : getp (Loc (Tr (v, []),p'))
|
|
|
|
keepPosition :: (Loc a -> Err (Loc a)) -> (Loc a -> Err (Loc a))
|
|
keepPosition f s = do
|
|
let p = getPosition s
|
|
s' <- f s
|
|
goPosition p s'
|
|
|
|
apply :: Monad m => Int -> (a -> m a) -> a -> m a
|
|
apply n f a = case n of
|
|
0 -> return a
|
|
_ -> f a >>= apply (n-1) f
|
|
|
|
-- added some utilities
|
|
|
|
traverseCollect :: Path a -> [a]
|
|
traverseCollect p = reverse $ case p of
|
|
Top -> []
|
|
Node (_, (p',v), _) -> v : traverseCollect p'
|
|
|
|
scanTree :: Tr a -> [a]
|
|
scanTree (Tr (a,ts)) = a : concatMap scanTree ts
|
|
|
|
mapTr :: (a -> b) -> Tr a -> Tr b
|
|
mapTr f (Tr (x,ts)) = Tr (f x, map (mapTr f) ts)
|
|
|
|
mapTrM :: Monad m => (a -> m b) -> Tr a -> m (Tr b)
|
|
mapTrM f (Tr (x,ts)) = do
|
|
fx <- f x
|
|
fts <- mapM (mapTrM f) ts
|
|
return $ Tr (fx,fts)
|
|
|
|
mapPath :: (a -> b) -> Path a -> Path b
|
|
mapPath f p = case p of
|
|
Node (ts1, (p,v), ts2) ->
|
|
Node (map (mapTr f) ts1, (mapPath f p, f v), map (mapTr f) ts2)
|
|
Top -> Top
|
|
|
|
mapPathM :: Monad m => (a -> m b) -> Path a -> m (Path b)
|
|
mapPathM f p = case p of
|
|
Node (ts1, (p,v), ts2) -> do
|
|
ts1' <- mapM (mapTrM f) ts1
|
|
p' <- mapPathM f p
|
|
v' <- f v
|
|
ts2' <- mapM (mapTrM f) ts2
|
|
return $ Node (ts1', (p',v'), ts2')
|
|
Top -> return Top
|
|
|
|
mapLoc :: (a -> b) -> Loc a -> Loc b
|
|
mapLoc f (Loc (t,p)) = Loc (mapTr f t, mapPath f p)
|
|
|
|
mapLocM :: Monad m => (a -> m b) -> Loc a -> m (Loc b)
|
|
mapLocM f (Loc (t,p)) = do
|
|
t' <- mapTrM f t
|
|
p' <- mapPathM f p
|
|
return $ (Loc (t',p'))
|
|
|
|
foldTr :: (a -> [b] -> b) -> Tr a -> b
|
|
foldTr f (Tr (x,ts)) = f x (map (foldTr f) ts)
|
|
|
|
foldTrM :: Monad m => (a -> [b] -> m b) -> Tr a -> m b
|
|
foldTrM f (Tr (x,ts)) = do
|
|
fts <- mapM (foldTrM f) ts
|
|
f x fts
|
|
|
|
mapSubtrees :: (Tr a -> Tr a) -> Tr a -> Tr a
|
|
mapSubtrees f t = let Tr (x,ts) = f t in Tr (x, map (mapSubtrees f) ts)
|
|
|
|
mapSubtreesM :: Monad m => (Tr a -> m (Tr a)) -> Tr a -> m (Tr a)
|
|
mapSubtreesM f t = do
|
|
Tr (x,ts) <- f t
|
|
ts' <- mapM (mapSubtreesM f) ts
|
|
return $ Tr (x, ts')
|
|
|
|
-- | change the root without moving the pointer
|
|
changeRoot :: (a -> a) -> Loc a -> Loc a
|
|
changeRoot f loc = case loc of
|
|
Loc (Tr (a,ts),Top) -> Loc (Tr (f a,ts),Top)
|
|
Loc (t, Node (left,pv,right)) -> Loc (t, Node (left,chPath pv,right))
|
|
where
|
|
chPath pv = case pv of
|
|
(Top,a) -> (Top, f a)
|
|
(Node (left,pv,right),v) -> (Node (left, chPath pv,right),v)
|
|
|
|
nthSubtree :: Int -> Tr a -> Err (Tr a)
|
|
nthSubtree n (Tr (a,ts)) = ts !? n
|
|
|
|
arityTree :: Tr a -> Int
|
|
arityTree (Tr (_,ts)) = length ts
|