module Camfort.Input where
import qualified Data.ByteString.Char8 as B
import qualified Language.Fortran.Parser as Fortran
import Language.Fortran.PreProcess
import Language.Fortran
import Data.Monoid
import Data.Generics.Uniplate.Operations
import Camfort.Analysis.Annotations
import Language.Haskell.ParseMonad
import qualified Language.Haskell.Syntax as LHS
import System.Directory
import Camfort.Helpers
import Camfort.Output
import Camfort.Traverse
import Data.Data
import Data.List (nub, (\\), elemIndices, intersperse)
class Default t where
defaultValue :: t
getOption :: forall t opt . (Data opt, Data t, Default opt) => [t] -> opt
getOption [] = defaultValue
getOption (x : xs) =
case universeBi x :: [opt] of
[] -> getOption xs
(opt : _) -> opt
doAnalysis :: (Program A -> Program Annotation)
-> FileOrDir -> [Filename] -> IO ()
doAnalysis aFun src excludes = do
if excludes /= [] && excludes /= [""]
then putStrLn $ "Excluding " ++ (concat $ intersperse "," excludes)
++ " from " ++ src ++ "/"
else return ()
ps <- readParseSrcDir src excludes
let inFiles = map Fortran.fst3 ps
let outFiles = filter (\f -> not ((take (length $ src ++ "out") f) == (src ++ "out"))) inFiles
let asts' = map (\(f, _, ps) -> aFun ps) ps
outputAnalysisFiles src asts' outFiles
doAnalysisSummary :: (Monoid s, Show s)
=> (Program A -> s) -> FileOrDir -> [Filename] -> IO ()
doAnalysisSummary aFun d excludes = do
if excludes /= [] && excludes /= [""]
then putStrLn $ "Excluding " ++ (concat $ intersperse "," excludes)
++ " from " ++ d ++ "/"
else return ()
ps <- readParseSrcDir d excludes
let inFiles = map Fortran.fst3 ps
putStrLn "Output of the analysis:"
putStrLn $ show $ Prelude.foldl (\n (f, _, ps) -> n `mappend` (aFun ps)) mempty ps
doAnalysisReport :: ([(Filename, Program A)] -> (String, t1))
-> FileOrDir -> [Filename] -> t -> IO ()
doAnalysisReport rFun inSrc excludes outSrc = do
if excludes /= [] && excludes /= [""]
then putStrLn $ "Excluding " ++ (concat $ intersperse "," excludes)
++ " from " ++ inSrc ++ "/"
else return ()
ps <- readParseSrcDir inSrc excludes
putStr "\n"
let (report, ps') = rFun (map (\(f, inp, ast) -> (f, ast)) ps)
putStrLn report
doAnalysisReport' :: ([(Filename, Program A)] -> (String, t1))
-> FileOrDir -> [Filename] -> t -> IO ()
doAnalysisReport' rFun inSrc excludes outSrc = do
if excludes /= [] && excludes /= [""]
then putStrLn $ "Excluding " ++ (concat $ intersperse "," excludes)
++ " from " ++ inSrc ++ "/"
else return ()
ps <- readParseSrcDir inSrc excludes
putStr "\n"
let (report, ps') = rFun (map (\(a, b, c) -> (a, c)) ps)
putStrLn report
doRefactor :: ([(Filename, Program A)]
-> (String, [(Filename, Program Annotation)]))
-> FileOrDir -> [Filename] -> FileOrDir -> IO ()
doRefactor rFun inSrc excludes outSrc = do
if excludes /= [] && excludes /= [""]
then putStrLn $ "Excluding " ++ (concat $ intersperse "," excludes)
++ " from " ++ inSrc ++ "/"
else return ()
ps <- readParseSrcDir inSrc excludes
let (report, ps') = rFun (map (\(f, inp, ast) -> (f, ast)) ps)
putStrLn report
let outFiles = map fst ps'
let outData = zip3 outFiles (map (B.pack . Fortran.snd3) ps) (map snd ps')
outputFiles inSrc outSrc outData
readParseSrcDir :: FileOrDir -> [Filename] -> IO [(Filename, String, Program A)]
readParseSrcDir inp excludes = do
isdir <- isDirectory inp
files <- if isdir then do
files <- rGetDirContents inp
return $ (map (\y -> inp ++ "/" ++ y) files) \\ excludes
else return [inp]
mapM readParseSrcFile files
rGetDirContents :: FileOrDir -> IO [String]
rGetDirContents d = do
ds <- getDirectoryContents d
ds' <- return $ ds \\ [".", ".."]
rec ds'
where
rec [] = return $ []
rec (x:xs) = do xs' <- rec xs
g <- doesDirectoryExist (d ++ "/" ++ x)
if g then
do x' <- rGetDirContents (d ++ "/" ++ x)
return $ (map (\y -> x ++ "/" ++ y) x') ++ xs'
else if isFortran x
then return $ x : xs'
else return $ xs'
isFortran x = elem (fileExt x) [".f", ".f90", ".f77", ".cmn", ".inc"]
readParseSrcFile :: Filename -> IO (Filename, String, Program A)
readParseSrcFile f = do
putStrLn f
inp <- readFile f
ast <- parse f
return $ (f, inp, map (fmap (const unitAnnotation)) ast)
parse :: Filename -> IO (Program ())
parse f =
let mode = ParseMode { parseFilename = f }
selectedParser = case (fileExt f) of
".cmn" -> Fortran.include_parser
".inc" -> Fortran.include_parser
_ -> Fortran.parser
in do inp <- readFile f
case runParserWithMode mode selectedParser (' ' : pre_process inp) of
(ParseOk p) -> return $ p
(ParseFailed l e) -> error e
fileExt x = let ix = elemIndices '.' x
in if (length ix == 0) then ""
else Prelude.drop (Prelude.last ix) x