| |
| -- A program for extracting strongly connected components from a .dot |
| -- file created by auxprogs/gen-mdg. |
| |
| -- How to use: one of the following: |
| |
| -- compile to an exe: ghc -o dottoscc DotToScc.hs |
| -- and then ./dottoscc name_of_file.dot |
| |
| -- or interpret with runhugs: |
| -- runhugs DotToScc.hs name_of_file.dot |
| |
| -- or run within hugs: |
| -- hugs DotToScc.hs |
| -- Main> imain "name_of_file.dot" |
| |
| |
| module Main where |
| |
| import System |
| import List ( sort, nub ) |
| |
| usage :: IO () |
| usage = putStrLn "usage: dottoscc <name_of_file.dot>" |
| |
| main :: IO () |
| main = do args <- getArgs |
| if length args /= 1 |
| then usage |
| else imain (head args) |
| |
| imain :: String -> IO () |
| imain dot_file_name |
| = do edges <- read_dot_file dot_file_name |
| let sccs = gen_sccs edges |
| let pretty = showPrettily sccs |
| putStrLn pretty |
| where |
| showPrettily :: [[String]] -> String |
| showPrettily = unlines . concatMap showScc |
| |
| showScc elems |
| = let n = length elems |
| in |
| [""] |
| ++ (if n > 1 then [" -- " |
| ++ show n ++ " modules in cycle"] |
| else []) |
| ++ map (" " ++) elems |
| |
| |
| -- Read a .dot file and return a list of edges |
| read_dot_file :: String{-filename-} -> IO [(String,String)] |
| read_dot_file dot_file_name |
| = do bytes <- readFile dot_file_name |
| let linez = lines bytes |
| let edges = [(s,d) | Just (s,d) <- map maybe_mk_edge linez] |
| return edges |
| where |
| -- identify lines of the form "text1 -> text2" and return |
| -- text1 and text2 |
| maybe_mk_edge :: String -> Maybe (String, String) |
| maybe_mk_edge str |
| = case words str of |
| [text1, "->", text2] -> Just (text1, text2) |
| other -> Nothing |
| |
| |
| -- Take the list of edges and return a topologically sorted list of |
| -- sccs |
| gen_sccs :: [(String,String)] -> [[String]] |
| gen_sccs raw_edges |
| = let clean_edges = sort (nub raw_edges) |
| nodes = nub (concatMap (\(s,d) -> [s,d]) clean_edges) |
| ins v = [u | (u,w) <- clean_edges, v==w] |
| outs v = [w | (u,w) <- clean_edges, v==u] |
| components = map (sort.utSetToList) (deScc ins outs nodes) |
| in |
| components |
| |
| |
| -------------------------------------------------------------------- |
| -------------------------------------------------------------------- |
| -------------------------------------------------------------------- |
| |
| -- Graph-theoretic stuff that does the interesting stuff. |
| |
| -- ==========================================================-- |
| -- |
| deScc :: (Ord a) => |
| (a -> [a]) -> -- The "ins" map |
| (a -> [a]) -> -- The "outs" map |
| [a] -> -- The root vertices |
| [Set a] -- The topologically sorted components |
| |
| deScc ins outs |
| = spanning . depthFirst |
| where depthFirst = snd . deDepthFirstSearch outs (utSetEmpty, []) |
| spanning = snd . deSpanningSearch ins (utSetEmpty, []) |
| |
| |
| -- =========================================================-- |
| -- |
| deDepthFirstSearch :: (Ord a) => |
| (a -> [a]) -> -- The map, |
| (Set a, [a]) -> -- state: visited set, |
| -- current sequence of vertices |
| [a] -> -- input vertices sequence |
| (Set a, [a]) -- final state |
| |
| deDepthFirstSearch |
| = foldl . search |
| where |
| search relation (visited, sequence) vertex |
| | utSetElementOf vertex visited = (visited, sequence ) |
| | otherwise = (visited', vertex: sequence') |
| where |
| (visited', sequence') |
| = deDepthFirstSearch relation |
| (utSetUnion visited (utSetSingleton vertex), sequence) |
| (relation vertex) |
| |
| |
| -- ==========================================================-- |
| -- |
| deSpanningSearch :: (Ord a) => |
| (a -> [a]) -> -- The map |
| (Set a, [Set a]) -> -- Current state: visited set, |
| -- current sequence of vertice sets |
| [a] -> -- Input sequence of vertices |
| (Set a, [Set a]) -- Final state |
| |
| deSpanningSearch |
| = foldl . search |
| where |
| search relation (visited, utSetSequence) vertex |
| | utSetElementOf vertex visited = (visited, utSetSequence ) |
| | otherwise = (visited', utSetFromList (vertex: sequence): utSetSequence) |
| where |
| (visited', sequence) |
| = deDepthFirstSearch relation |
| (utSetUnion visited (utSetSingleton vertex), []) |
| (relation vertex) |
| |
| |
| |
| |
| |
| -------------------------------------------------------------------- |
| -------------------------------------------------------------------- |
| -------------------------------------------------------------------- |
| -- Most of this set stuff isn't needed. |
| |
| |
| -- ====================================-- |
| -- === set ===-- |
| -- ====================================-- |
| |
| data Set e = MkSet [e] |
| |
| -- ==========================================================-- |
| -- |
| unMkSet :: (Ord a) => Set a -> [a] |
| |
| unMkSet (MkSet s) = s |
| |
| |
| -- ==========================================================-- |
| -- |
| utSetEmpty :: (Ord a) => Set a |
| |
| utSetEmpty = MkSet [] |
| |
| |
| -- ==========================================================-- |
| -- |
| utSetIsEmpty :: (Ord a) => Set a -> Bool |
| |
| utSetIsEmpty (MkSet s) = s == [] |
| |
| |
| -- ==========================================================-- |
| -- |
| utSetSingleton :: (Ord a) => a -> Set a |
| |
| utSetSingleton x = MkSet [x] |
| |
| |
| -- ==========================================================-- |
| -- |
| utSetFromList :: (Ord a) => [a] -> Set a |
| |
| utSetFromList x = (MkSet . rmdup . sort) x |
| where rmdup [] = [] |
| rmdup [x] = [x] |
| rmdup (x:y:xs) | x==y = rmdup (y:xs) |
| | otherwise = x: rmdup (y:xs) |
| |
| |
| -- ==========================================================-- |
| -- |
| utSetToList :: (Ord a) => Set a -> [a] |
| |
| utSetToList (MkSet xs) = xs |
| |
| |
| |
| -- ==========================================================-- |
| -- |
| utSetUnion :: (Ord a) => Set a -> Set a -> Set a |
| |
| utSetUnion (MkSet []) (MkSet []) = (MkSet []) |
| utSetUnion (MkSet []) (MkSet (b:bs)) = (MkSet (b:bs)) |
| utSetUnion (MkSet (a:as)) (MkSet []) = (MkSet (a:as)) |
| utSetUnion (MkSet (a:as)) (MkSet (b:bs)) |
| | a < b = MkSet (a: (unMkSet (utSetUnion (MkSet as) (MkSet (b:bs))))) |
| | a == b = MkSet (a: (unMkSet (utSetUnion (MkSet as) (MkSet bs)))) |
| | a > b = MkSet (b: (unMkSet (utSetUnion (MkSet (a:as)) (MkSet bs)))) |
| |
| |
| -- ==========================================================-- |
| -- |
| utSetIntersection :: (Ord a) => Set a -> Set a -> Set a |
| |
| utSetIntersection (MkSet []) (MkSet []) = (MkSet []) |
| utSetIntersection (MkSet []) (MkSet (b:bs)) = (MkSet []) |
| utSetIntersection (MkSet (a:as)) (MkSet []) = (MkSet []) |
| utSetIntersection (MkSet (a:as)) (MkSet (b:bs)) |
| | a < b = utSetIntersection (MkSet as) (MkSet (b:bs)) |
| | a == b = MkSet (a: (unMkSet (utSetIntersection (MkSet as) (MkSet bs)))) |
| | a > b = utSetIntersection (MkSet (a:as)) (MkSet bs) |
| |
| |
| -- ==========================================================-- |
| -- |
| utSetSubtraction :: (Ord a) => Set a -> Set a -> Set a |
| |
| utSetSubtraction (MkSet []) (MkSet []) = (MkSet []) |
| utSetSubtraction (MkSet []) (MkSet (b:bs)) = (MkSet []) |
| utSetSubtraction (MkSet (a:as)) (MkSet []) = (MkSet (a:as)) |
| utSetSubtraction (MkSet (a:as)) (MkSet (b:bs)) |
| | a < b = MkSet (a: (unMkSet (utSetSubtraction (MkSet as) (MkSet (b:bs))))) |
| | a == b = utSetSubtraction (MkSet as) (MkSet bs) |
| | a > b = utSetSubtraction (MkSet (a:as)) (MkSet bs) |
| |
| |
| -- ==========================================================-- |
| -- |
| utSetElementOf :: (Ord a) => a -> Set a -> Bool |
| |
| utSetElementOf x (MkSet []) = False |
| utSetElementOf x (MkSet (y:ys)) = x==y || (x>y && utSetElementOf x (MkSet ys)) |
| |
| |
| |
| -- ==========================================================-- |
| -- |
| utSetSubsetOf :: (Ord a) => Set a -> Set a -> Bool |
| |
| utSetSubsetOf (MkSet []) (MkSet bs) = True |
| utSetSubsetOf (MkSet (a:as)) (MkSet bs) |
| = utSetElementOf a (MkSet bs) && utSetSubsetOf (MkSet as) (MkSet bs) |
| |
| |
| -- ==========================================================-- |
| -- |
| utSetUnionList :: (Ord a) => [Set a] -> Set a |
| |
| utSetUnionList setList = foldl utSetUnion utSetEmpty setList |
| |
| |