module Camfort.Functionality where
import System.Console.GetOpt
import System.Directory
import System.Environment
import System.IO
import Data.Monoid
import Data.Generics.Uniplate.Operations
import Camfort.Analysis.Annotations
import Camfort.Analysis.Types
import Camfort.Analysis.Loops
import Camfort.Analysis.LVA
import Camfort.Analysis.Syntax
import Camfort.Transformation.DeadCode
import Camfort.Transformation.CommonBlockElim
import Camfort.Transformation.CommonBlockElimToCalls
import Camfort.Transformation.EquivalenceElim
import Camfort.Transformation.DerivedTypeIntro
import qualified Camfort.Specification.Units as LU
import Camfort.Specification.Units.Environment
import Camfort.Specification.Units.Solve
import Camfort.Helpers
import Camfort.Output
import Camfort.Input
import Data.Data
import Data.List (foldl', nub, (\\), elemIndices, intersperse, intercalate)
import qualified Data.ByteString.Char8 as B
import Data.Text.Encoding (encodeUtf8, decodeUtf8With)
import Data.Text.Encoding.Error (replace)
import qualified Language.Fortran.Parser.Any as FP
import qualified Language.Fortran.AST as F
import Language.Fortran.Analysis.Renaming
(renameAndStrip, analyseRenames, unrename, NameMap)
import Language.Fortran.Analysis(initAnalysis)
import qualified Camfort.Specification.Stencils as Stencils
data Flag = Version
| Input String
| Output String
| Solver Solver
| Excludes String
| Literals AssumeLiterals
| StencilInferMode Stencils.InferMode
| Debug deriving (Data, Show)
type Options = [Flag]
instance Default String where
defaultValue = ""
getExcludes :: Options -> String
getExcludes xs = getOption xs
typeStructuring inSrc excludes outSrc _ = do
putStrLn $ "Introducing derived data types in " ++ show inSrc ++ "\n"
doRefactor typeStruct inSrc excludes outSrc
ast d _ f _ = do
(_, _, p) <- readParseSrcFile (d ++ "/" ++ f)
putStrLn $ show p
asts inSrc excludes _ _ = do
putStrLn $ "Do a basic analysis and output the HTML files "
++ "with AST information for " ++ show inSrc ++ "\n"
let astAnalysis = (map numberStmts) . map (fmap (const unitAnnotation))
doAnalysis astAnalysis inSrc excludes
countVarDecls inSrc excludes _ _ = do
putStrLn $ "Counting variable declarations in " ++ show inSrc ++ "\n"
doAnalysisSummary countVariableDeclarations inSrc excludes
loops inSrc excludes _ _ = do
putStrLn $ "Analysing loops for " ++ show inSrc ++ "\n"
doAnalysis loopAnalyse inSrc excludes
lvaA inSrc excludes _ _ = do
putStrLn $ "Analysing loops for " ++ show inSrc ++ "\n"
doAnalysis lva inSrc excludes
dead inSrc excludes outSrc _ = do
putStrLn $ "Eliminating dead code in " ++ show inSrc ++ "\n"
doRefactor ((mapM (deadCode False))) inSrc excludes outSrc
commonToArgs inSrc excludes outSrc _ = do
putStrLn $ "Refactoring common blocks in " ++ show inSrc ++ "\n"
doRefactor (commonElimToCalls inSrc) inSrc excludes outSrc
common inSrc excludes outSrc _ = do
putStrLn $ "Refactoring common blocks in " ++ show inSrc ++ "\n"
doRefactor (commonElimToModules inSrc) inSrc excludes outSrc
equivalences inSrc excludes outSrc _ = do
putStrLn $ "Refactoring equivalences blocks in " ++ show inSrc ++ "\n"
doRefactor (mapM refactorEquivalences) inSrc excludes outSrc
unitsCheck inSrc excludes outSrc opt = do
putStrLn $ "Checking units for " ++ show inSrc ++ "\n"
let ?solver = getOption opt :: Solver
in let ?assumeLiterals = getOption opt :: AssumeLiterals
in doAnalysisReportForpar (mapM LU.checkUnits) inSrc excludes outSrc
unitsInfer inSrc excludes outSrc opt = do
putStrLn $ "Inferring units for " ++ show inSrc ++ "\n"
let ?solver = getOption opt :: Solver
in let ?assumeLiterals = getOption opt :: AssumeLiterals
in doAnalysisReportForpar (mapM LU.inferUnits) inSrc excludes outSrc
unitsSynth inSrc excludes outSrc opt = do
putStrLn $ "Synthesising units for " ++ show inSrc ++ "\n"
let ?solver = getOption opt :: Solver
in let ?assumeLiterals = getOption opt :: AssumeLiterals
in doRefactorForpar (mapM LU.synthesiseUnits) inSrc excludes outSrc
unitsCriticals inSrc excludes outSrc opt = do
putStrLn $ "Infering critical variables for units inference in directory "
++ show inSrc ++ "\n"
let ?solver = getOption opt :: Solver
in let ?assumeLiterals = getOption opt :: AssumeLiterals
in doAnalysisReportForpar (mapM LU.inferCriticalVariables)
inSrc excludes outSrc
stencilsCheck inSrc excludes _ _ = do
putStrLn $ "Checking stencil specs for " ++ show inSrc ++ "\n"
doAnalysisSummaryForpar (\f p -> (Stencils.check f p, p)) inSrc excludes Nothing
stencilsInfer inSrc excludes outSrc opt = do
putStrLn $ "Infering stencil specs for " ++ show inSrc ++ "\n"
doAnalysisSummaryForpar (Stencils.infer (getOption opt)) inSrc excludes (Just outSrc)
stencilsSynth inSrc excludes outSrc opt = do
putStrLn $ "Synthesising stencil specs for " ++ show inSrc ++ "\n"
doRefactorForpar (Stencils.synth (getOption opt)) inSrc excludes outSrc
stencilsVarFlowCycles inSrc excludes _ _ = do
putStrLn $ "Inferring var flow cycles for " ++ show inSrc ++ "\n"
let flowAnalysis = intercalate ", " . map show . Stencils.findVarFlowCycles
doAnalysisSummaryForpar (\_ p -> (flowAnalysis p , p)) inSrc excludes Nothing
doRefactorForpar :: ([(Filename, F.ProgramFile A)]
-> (String, [(Filename, F.ProgramFile Annotation)]))
-> FileOrDir -> [Filename] -> FileOrDir -> IO ()
doRefactorForpar rFun inSrc excludes outSrc = do
if excludes /= [] && excludes /= [""]
then putStrLn $ "Excluding " ++ (concat $ intersperse "," excludes)
++ " from " ++ inSrc ++ "/"
else return ()
ps <- readForparseSrcDir inSrc excludes
let (report, ps') = rFun (map (\(f, inp, ast) -> (f, ast)) ps)
putStrLn report
let outputs = mkOutputFileForpar ps ps'
outputFiles inSrc outSrc outputs
where snd3 (a, b, c) = b
mkOutputFileForpar :: [(Filename, SourceText, a)]
-> [(Filename, F.ProgramFile Annotation)]
-> [(Filename, SourceText, F.ProgramFile Annotation)]
mkOutputFileForpar ps ps' = zip3 (map fst ps') (map snd3 ps) (map snd ps')
where
snd3 (a, b, c) = b
doAnalysisReportForpar :: ([(Filename, F.ProgramFile A)] -> (String, t1))
-> FileOrDir -> [Filename] -> t -> IO ()
doAnalysisReportForpar rFun inSrc excludes outSrc = do
if excludes /= [] && excludes /= [""]
then putStrLn $ "Excluding " ++ (concat $ intersperse "," excludes)
++ " from " ++ inSrc ++ "/"
else return ()
ps <- readForparseSrcDir inSrc excludes
let (report, ps') = rFun (map (\(f, inp, ast) -> (f, ast)) ps)
putStrLn report
readForparseSrcDir :: FileOrDir -> [Filename]
-> IO [(Filename, SourceText, F.ProgramFile A)]
readForparseSrcDir 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 readForparseSrcFile files
readForparseSrcFile :: Filename -> IO (Filename, SourceText, F.ProgramFile A)
readForparseSrcFile f = do
inp <- flexReadFile f
let ast = FP.fortranParser inp f
return $ (f, inp, fmap (const unitAnnotation) ast)
doAnalysisSummaryForpar :: (Monoid s, Show' s) => (Filename -> F.ProgramFile A -> (s, F.ProgramFile A))
-> FileOrDir -> [Filename] -> Maybe FileOrDir -> IO ()
doAnalysisSummaryForpar aFun inSrc excludes outSrc = do
if excludes /= [] && excludes /= [""]
then putStrLn $ "Excluding " ++ (concat $ intersperse "," excludes)
++ " from " ++ inSrc ++ "/"
else return ()
ps <- readForparseSrcDir inSrc excludes
let (out, ps') = callAndSummarise aFun ps
putStrLn "Output of the analysis:"
putStrLn . show' $ out
callAndSummarise aFun ps = do
foldl' (\(n, pss) (f, _, ps) -> let (n', ps') = aFun f ps
in (n `mappend` n', ps' : pss)) (mempty, []) ps
flexReadFile :: String -> IO B.ByteString
flexReadFile = fmap (encodeUtf8 . decodeUtf8With (replace ' ')) . B.readFile