sewardj | abbf59d | 2005-06-25 14:42:34 +0000 | [diff] [blame] | 1 | |
| 2 | -- A program for extracting strongly connected components from a .dot |
| 3 | -- file created by auxprogs/gen-mdg. |
| 4 | |
| 5 | -- How to use: one of the following: |
| 6 | |
| 7 | -- compile to an exe: ghc -o dottoscc DotToScc.hs |
| 8 | -- and then ./dottoscc name_of_file.dot |
| 9 | |
| 10 | -- or interpret with runhugs: |
| 11 | -- runhugs DotToScc.hs name_of_file.dot |
| 12 | |
| 13 | -- or run within hugs: |
| 14 | -- hugs DotToScc.hs |
| 15 | -- Main> imain "name_of_file.dot" |
| 16 | |
| 17 | |
| 18 | module Main where |
| 19 | |
| 20 | import System |
| 21 | import List ( sort, nub ) |
| 22 | |
| 23 | usage :: IO () |
| 24 | usage = putStrLn "usage: dottoscc <name_of_file.dot>" |
| 25 | |
| 26 | main :: IO () |
| 27 | main = do args <- getArgs |
| 28 | if length args /= 1 |
| 29 | then usage |
| 30 | else imain (head args) |
| 31 | |
| 32 | imain :: String -> IO () |
| 33 | imain dot_file_name |
| 34 | = do edges <- read_dot_file dot_file_name |
| 35 | let sccs = gen_sccs edges |
| 36 | let pretty = showPrettily sccs |
| 37 | putStrLn pretty |
| 38 | where |
| 39 | showPrettily :: [[String]] -> String |
| 40 | showPrettily = unlines . concatMap showScc |
| 41 | |
| 42 | showScc elems |
| 43 | = let n = length elems |
| 44 | in |
| 45 | [""] |
| 46 | ++ (if n > 1 then [" -- " |
| 47 | ++ show n ++ " modules in cycle"] |
| 48 | else []) |
| 49 | ++ map (" " ++) elems |
| 50 | |
| 51 | |
| 52 | -- Read a .dot file and return a list of edges |
| 53 | read_dot_file :: String{-filename-} -> IO [(String,String)] |
| 54 | read_dot_file dot_file_name |
| 55 | = do bytes <- readFile dot_file_name |
| 56 | let linez = lines bytes |
| 57 | let edges = [(s,d) | Just (s,d) <- map maybe_mk_edge linez] |
| 58 | return edges |
| 59 | where |
| 60 | -- identify lines of the form "text1 -> text2" and return |
| 61 | -- text1 and text2 |
| 62 | maybe_mk_edge :: String -> Maybe (String, String) |
| 63 | maybe_mk_edge str |
| 64 | = case words str of |
| 65 | [text1, "->", text2] -> Just (text1, text2) |
| 66 | other -> Nothing |
| 67 | |
| 68 | |
| 69 | -- Take the list of edges and return a topologically sorted list of |
| 70 | -- sccs |
| 71 | gen_sccs :: [(String,String)] -> [[String]] |
| 72 | gen_sccs raw_edges |
| 73 | = let clean_edges = sort (nub raw_edges) |
| 74 | nodes = nub (concatMap (\(s,d) -> [s,d]) clean_edges) |
| 75 | ins v = [u | (u,w) <- clean_edges, v==w] |
| 76 | outs v = [w | (u,w) <- clean_edges, v==u] |
| 77 | components = map (sort.utSetToList) (deScc ins outs nodes) |
| 78 | in |
| 79 | components |
| 80 | |
| 81 | |
| 82 | -------------------------------------------------------------------- |
| 83 | -------------------------------------------------------------------- |
| 84 | -------------------------------------------------------------------- |
| 85 | |
| 86 | -- Graph-theoretic stuff that does the interesting stuff. |
| 87 | |
| 88 | -- ==========================================================-- |
| 89 | -- |
| 90 | deScc :: (Ord a) => |
| 91 | (a -> [a]) -> -- The "ins" map |
| 92 | (a -> [a]) -> -- The "outs" map |
| 93 | [a] -> -- The root vertices |
| 94 | [Set a] -- The topologically sorted components |
| 95 | |
| 96 | deScc ins outs |
| 97 | = spanning . depthFirst |
| 98 | where depthFirst = snd . deDepthFirstSearch outs (utSetEmpty, []) |
| 99 | spanning = snd . deSpanningSearch ins (utSetEmpty, []) |
| 100 | |
| 101 | |
| 102 | -- =========================================================-- |
| 103 | -- |
| 104 | deDepthFirstSearch :: (Ord a) => |
| 105 | (a -> [a]) -> -- The map, |
| 106 | (Set a, [a]) -> -- state: visited set, |
| 107 | -- current sequence of vertices |
| 108 | [a] -> -- input vertices sequence |
| 109 | (Set a, [a]) -- final state |
| 110 | |
| 111 | deDepthFirstSearch |
| 112 | = foldl . search |
| 113 | where |
| 114 | search relation (visited, sequence) vertex |
| 115 | | utSetElementOf vertex visited = (visited, sequence ) |
| 116 | | otherwise = (visited', vertex: sequence') |
| 117 | where |
| 118 | (visited', sequence') |
| 119 | = deDepthFirstSearch relation |
| 120 | (utSetUnion visited (utSetSingleton vertex), sequence) |
| 121 | (relation vertex) |
| 122 | |
| 123 | |
| 124 | -- ==========================================================-- |
| 125 | -- |
| 126 | deSpanningSearch :: (Ord a) => |
| 127 | (a -> [a]) -> -- The map |
| 128 | (Set a, [Set a]) -> -- Current state: visited set, |
| 129 | -- current sequence of vertice sets |
| 130 | [a] -> -- Input sequence of vertices |
| 131 | (Set a, [Set a]) -- Final state |
| 132 | |
| 133 | deSpanningSearch |
| 134 | = foldl . search |
| 135 | where |
| 136 | search relation (visited, utSetSequence) vertex |
| 137 | | utSetElementOf vertex visited = (visited, utSetSequence ) |
| 138 | | otherwise = (visited', utSetFromList (vertex: sequence): utSetSequence) |
| 139 | where |
| 140 | (visited', sequence) |
| 141 | = deDepthFirstSearch relation |
| 142 | (utSetUnion visited (utSetSingleton vertex), []) |
| 143 | (relation vertex) |
| 144 | |
| 145 | |
| 146 | |
| 147 | |
| 148 | |
| 149 | -------------------------------------------------------------------- |
| 150 | -------------------------------------------------------------------- |
| 151 | -------------------------------------------------------------------- |
| 152 | -- Most of this set stuff isn't needed. |
| 153 | |
| 154 | |
| 155 | -- ====================================-- |
| 156 | -- === set ===-- |
| 157 | -- ====================================-- |
| 158 | |
| 159 | data Set e = MkSet [e] |
| 160 | |
| 161 | -- ==========================================================-- |
| 162 | -- |
| 163 | unMkSet :: (Ord a) => Set a -> [a] |
| 164 | |
| 165 | unMkSet (MkSet s) = s |
| 166 | |
| 167 | |
| 168 | -- ==========================================================-- |
| 169 | -- |
| 170 | utSetEmpty :: (Ord a) => Set a |
| 171 | |
| 172 | utSetEmpty = MkSet [] |
| 173 | |
| 174 | |
| 175 | -- ==========================================================-- |
| 176 | -- |
| 177 | utSetIsEmpty :: (Ord a) => Set a -> Bool |
| 178 | |
| 179 | utSetIsEmpty (MkSet s) = s == [] |
| 180 | |
| 181 | |
| 182 | -- ==========================================================-- |
| 183 | -- |
| 184 | utSetSingleton :: (Ord a) => a -> Set a |
| 185 | |
| 186 | utSetSingleton x = MkSet [x] |
| 187 | |
| 188 | |
| 189 | -- ==========================================================-- |
| 190 | -- |
| 191 | utSetFromList :: (Ord a) => [a] -> Set a |
| 192 | |
| 193 | utSetFromList x = (MkSet . rmdup . sort) x |
| 194 | where rmdup [] = [] |
| 195 | rmdup [x] = [x] |
| 196 | rmdup (x:y:xs) | x==y = rmdup (y:xs) |
| 197 | | otherwise = x: rmdup (y:xs) |
| 198 | |
| 199 | |
| 200 | -- ==========================================================-- |
| 201 | -- |
| 202 | utSetToList :: (Ord a) => Set a -> [a] |
| 203 | |
| 204 | utSetToList (MkSet xs) = xs |
| 205 | |
| 206 | |
| 207 | |
| 208 | -- ==========================================================-- |
| 209 | -- |
| 210 | utSetUnion :: (Ord a) => Set a -> Set a -> Set a |
| 211 | |
| 212 | utSetUnion (MkSet []) (MkSet []) = (MkSet []) |
| 213 | utSetUnion (MkSet []) (MkSet (b:bs)) = (MkSet (b:bs)) |
| 214 | utSetUnion (MkSet (a:as)) (MkSet []) = (MkSet (a:as)) |
| 215 | utSetUnion (MkSet (a:as)) (MkSet (b:bs)) |
| 216 | | a < b = MkSet (a: (unMkSet (utSetUnion (MkSet as) (MkSet (b:bs))))) |
| 217 | | a == b = MkSet (a: (unMkSet (utSetUnion (MkSet as) (MkSet bs)))) |
| 218 | | a > b = MkSet (b: (unMkSet (utSetUnion (MkSet (a:as)) (MkSet bs)))) |
| 219 | |
| 220 | |
| 221 | -- ==========================================================-- |
| 222 | -- |
| 223 | utSetIntersection :: (Ord a) => Set a -> Set a -> Set a |
| 224 | |
| 225 | utSetIntersection (MkSet []) (MkSet []) = (MkSet []) |
| 226 | utSetIntersection (MkSet []) (MkSet (b:bs)) = (MkSet []) |
| 227 | utSetIntersection (MkSet (a:as)) (MkSet []) = (MkSet []) |
| 228 | utSetIntersection (MkSet (a:as)) (MkSet (b:bs)) |
| 229 | | a < b = utSetIntersection (MkSet as) (MkSet (b:bs)) |
| 230 | | a == b = MkSet (a: (unMkSet (utSetIntersection (MkSet as) (MkSet bs)))) |
| 231 | | a > b = utSetIntersection (MkSet (a:as)) (MkSet bs) |
| 232 | |
| 233 | |
| 234 | -- ==========================================================-- |
| 235 | -- |
| 236 | utSetSubtraction :: (Ord a) => Set a -> Set a -> Set a |
| 237 | |
| 238 | utSetSubtraction (MkSet []) (MkSet []) = (MkSet []) |
| 239 | utSetSubtraction (MkSet []) (MkSet (b:bs)) = (MkSet []) |
| 240 | utSetSubtraction (MkSet (a:as)) (MkSet []) = (MkSet (a:as)) |
| 241 | utSetSubtraction (MkSet (a:as)) (MkSet (b:bs)) |
| 242 | | a < b = MkSet (a: (unMkSet (utSetSubtraction (MkSet as) (MkSet (b:bs))))) |
| 243 | | a == b = utSetSubtraction (MkSet as) (MkSet bs) |
| 244 | | a > b = utSetSubtraction (MkSet (a:as)) (MkSet bs) |
| 245 | |
| 246 | |
| 247 | -- ==========================================================-- |
| 248 | -- |
| 249 | utSetElementOf :: (Ord a) => a -> Set a -> Bool |
| 250 | |
| 251 | utSetElementOf x (MkSet []) = False |
| 252 | utSetElementOf x (MkSet (y:ys)) = x==y || (x>y && utSetElementOf x (MkSet ys)) |
| 253 | |
| 254 | |
| 255 | |
| 256 | -- ==========================================================-- |
| 257 | -- |
| 258 | utSetSubsetOf :: (Ord a) => Set a -> Set a -> Bool |
| 259 | |
| 260 | utSetSubsetOf (MkSet []) (MkSet bs) = True |
| 261 | utSetSubsetOf (MkSet (a:as)) (MkSet bs) |
| 262 | = utSetElementOf a (MkSet bs) && utSetSubsetOf (MkSet as) (MkSet bs) |
| 263 | |
| 264 | |
| 265 | -- ==========================================================-- |
| 266 | -- |
| 267 | utSetUnionList :: (Ord a) => [Set a] -> Set a |
| 268 | |
| 269 | utSetUnionList setList = foldl utSetUnion utSetEmpty setList |
| 270 | |
| 271 | |