HaRe-0.6: StrategyLib-4.0-beta/examples/haskell/HsChaseImports.hs
module HsChaseImports where
-------------------------------------------------------------------------------
-- This module implements import chasing for Haskell.
-------------------------------------------------------------------------------
import HsIOWrap(parseWrap')
import StrategyLib
import HsModuleCollection
import Language.Haskell.Syntax
import Language.Haskell.Parser
import Language.Haskell.Pretty
import SyntaxTermInstances
import IO
import System
import qualified Literate
import Monad
import Control.Monad.Identity
-------------------------------------------------------------------------------
hsIOwrapChasing transform
= do [pth,m,d] <- getArgs
errLn "Import chasing..."
errLn ("Start from: "++m)
errLn ("Search path: "++pth)
errLn ("Writing results in:"++d)
let dirs = breakPath pth
ms <- hsChaseFrom dirs m
ms' <- transform ms
mapM_ (writeModule d) ms'
writeModule :: String -> (ModuleName,[ModuleName],HsModule) -> IO ()
writeModule d (n,is,m)
= do let sout = (prettyPrint m)++"\n"
let fout = d++"/"++n++".hs"
errLn ("Writing module "++n++" to file "++fout)
writeFile fout sout
-- Break path argument into directory names
breakPath :: String -> [String]
breakPath x = case break (==':') x of
(p,(_:pp)) -> p: breakPath pp
(p,[]) -> [p]
-- Start chasing from a single module.
hsChaseFrom dirs m
= hsChaseWith dirs [m] [] [] onModule onMissing
where
onModule n i m a = return $ a++[(n,i,m)]
onMissing _ = return
-- General import chase algorithm
hsChaseWith :: [FilePath] -> [ModuleName] -> [ModuleName] -> a
-> (ModuleName -> [ModuleName] -> HsModule -> a -> IO a)
-> (ModuleName -> a -> IO a)
-> IO a
hsChaseWith dirs todo done accu onModule onMissing
= chase todo done accu
where
chase [] done accu
= do errLn "Import chasing complete."
return accu
chase (m:ms) done accu
| m `elem` done = chase ms done accu
| otherwise = processFile `mplus` skipFile
where
processFile
= do sin <- readHsFile dirs m
case (parseWrap' (fromLiterate sin)) of
Left pin
-> let is = getImports pin
in do accu' <- onModule m is pin accu
chase (ms++is) (m:done) accu'
Right msg
-> do errLn ("Failed to parse "++m++": "++msg)
accu' <- onMissing m accu
chase ms (m:done) accu'
skipFile
= do errLn ("Did not find module "++m++": skipping.")
accu' <- onMissing m accu
chase ms (m:done) accu'
--- Extraction of imports ----------------------------------------------------
getImports :: HsModule -> [String]
getImports m
= runIdentity (applyTU (full_tdTU worker) m)
where
worker :: TU [String] Identity
worker = adhocTU (constTU []) (\i -> return [getName i])
getName :: HsImportDecl -> String
getName (HsImportDecl _ (Module mid) _ _ _) = mid
--- I/O Auxilliaries ---------------------------------------------------------
errLn str = hPutStrLn stderr str
readHsFile dirs basename
= do errLn ("Starting search for: "++basename)
readFileSearching dirs basename ["hs","lhs"]
-- Search for a file in given directories with alternative extensions
readFileSearching dirs basename exts
= do results <- mapM tryReadFile fnames
case dropWhile hasFailed results of
((Right (fc,fn)):_)
-> errLn ("Read file: "++fn) >> return fc
_ -> errLn ("Could not find file: "++basename) >> mzero
where
fnames = [d++'/':basename++'.':e | d <- dirs, e <- exts]
hasFailed (Left _) = True
hasFailed _ = False
tryReadFile fn = try ( readFile fn >>= \fc -> return (fc,fn) )
--handle literate scripts -----------------------------------------------------
fromLiterate str
= fromLit (isLit str) str
isLit :: String -> Bool
isLit = any isLitLine . lines
where
isLitLine ('>':_) = True
isLitLine _ = False
-- Rest is taken from DrIFT
-- NB we don't do the latex-style literate scripts currently.
fromLit True txt = case Literate.process txt of
([],s) -> s
(e,_) -> error e
fromLit False txt = txt
toLit True = unlines . map (\l -> '>':l) . lines
toLit False = id
isLiterate :: String -> Bool
isLiterate = any ((=='>'). head) . words
-------------------------------------------------------------------------------