packages feed

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      

-------------------------------------------------------------------------------