cabal-db-0.1.12: Src/Graph.hs
{-# LANGUAGE BangPatterns #-}
{-# LANGUAGE FlexibleContexts #-}
module Graph
( GraphSt
, GraphIndex
, graphResolve
, graphInsertDep
, withGraph
, graphLoop
) where
import Control.Applicative
import qualified Data.Map as M
import Control.Monad.State
-- represent the element in the graph
type GraphIndex = Int
data GraphSt a = GraphSt
{ nextIndex :: !GraphIndex
, indexTable :: M.Map a GraphIndex
, depsTable :: M.Map GraphIndex [GraphIndex]
} deriving (Show,Eq)
-- | return the graph index of a 'a' object.
-- if it doesn't exist, it create one
graphResolve :: (Monad m, Ord a) => a -> StateT (GraphSt a) m GraphIndex
graphResolve pn = get >>= addOrGet
where addOrGet st = maybe (add st) return $ M.lookup pn (indexTable st)
add st = put (st { nextIndex = ni+1, indexTable = M.insert pn ni (indexTable st) }) >> return ni
where ni = nextIndex st
graphIsProcessed :: (Functor f, Monad f) => GraphIndex -> StateT (GraphSt a) f Bool
graphIsProcessed pn = M.member pn <$> gets depsTable
graphInsertDep :: Monad m => GraphIndex -> GraphIndex -> StateT (GraphSt a) m ()
graphInsertDep i j = modifyDepsTable i appendOrCreateList
where appendOrCreateList Nothing = Just [j]
appendOrCreateList (Just z) = Just (j:z)
modifyDepsTable k f = modify (\st -> st { depsTable = M.alter f k (depsTable st) })
withGraph :: (Functor m, Monad m)
=> StateT (GraphSt a) m ()
-> m (M.Map a GraphIndex, M.Map GraphIndex [GraphIndex])
withGraph f = (\st -> (indexTable st, depsTable st))
<$> execStateT f (GraphSt 1 M.empty M.empty)
graphLoop :: Ord a
=> (a -> IO [a])
-> a
-> StateT (GraphSt a) IO ()
graphLoop getEdges = loop (0 :: Int)
where loop !depth pn
| depth > 1000 = error "internal error: infinite loop detected"
| otherwise = do
pni <- graphResolve pn
processed <- graphIsProcessed pni
--liftIO $ putStrLn ("loop: " ++ show pn ++ " processed: " ++ show processed ++ " depth: " ++ show depth)
unless processed $ do
depNames <- filter (/= pn) <$> liftIO (getEdges pn)
mapM_ (loop (depth+1)) depNames
mapM_ (graphResolve >=> graphInsertDep pni) depNames