module Camfort.Transformation.EquivalenceElim
( refactorEquivalences
) where
import Data.List
import qualified Data.Map as M
import Data.Generics.Uniplate.Operations
import Control.Monad.State.Lazy
import qualified Language.Fortran.AST as F
import qualified Language.Fortran.Analysis.Types as FAT (analyseTypes, TypeEnv)
import qualified Language.Fortran.Util.Position as FU
import qualified Language.Fortran.Analysis.Renaming as FAR
import qualified Language.Fortran.Analysis as FA
import Camfort.Helpers
import Camfort.Helpers.Syntax
import Camfort.Analysis.Annotations
import Camfort.Transformation.DeadCode
type A1 = FA.Analysis Annotation
type RmEqState = ([[F.Expression A1]], Int, Report)
refactorEquivalences :: F.ProgramFile A -> (Report, F.ProgramFile A)
refactorEquivalences pf = do
let pf' = FAR.analyseRenames . FA.initAnalysis $ pf
let (pf'', typeEnv) = FAT.analyseTypes pf'
pf''' <- refactoring typeEnv pf''
deadCode True (fmap FA.prevAnnotation pf''')
where
refactoring :: FAT.TypeEnv -> F.ProgramFile A1 -> (Report, F.ProgramFile A1)
refactoring tenv pf = (report, pf')
where
(pf', (_, _, report)) = runState equiv ([], 0, "")
equiv = do pf' <- transformBiM perBlockRmEquiv pf
descendBiM (addCopysPerBlockGroup tenv) pf'
addCopysPerBlockGroup :: FAT.TypeEnv -> [F.Block A1] -> State RmEqState [F.Block A1]
addCopysPerBlockGroup tenv blocks = do
blockss <- mapM (addCopysPerBlock tenv) blocks
return $ concat blockss
addCopysPerBlock :: FAT.TypeEnv -> F.Block A1 -> State RmEqState [F.Block A1]
addCopysPerBlock tenv x@(F.BlStatement _ _ _
(F.StExpressionAssign a sp@(FU.SrcSpan s1 _) dstE _))
| not (pRefactored $ FA.prevAnnotation a) = do
eqs <- equivalentsToExpr dstE
if length eqs <= 1
then return [x]
else do
(equivs, n, r) <- get
let eqs' = deleteBy (\x y -> af x == af y) dstE eqs
let pos = afterAligned sp
let copies = map (mkCopy tenv pos dstE) eqs'
let (FU.Position _ c l) = s1
let reportF i = show (l + i) ++ ":" ++ show c
++ ": added copy due to refactored equivalence\n"
let report n = concatMap reportF [n..(n + length copies 1)]
put (equivs, n + length eqs', r ++ report n)
return $ x : copies
addCopysPerBlock tenv x = do
x' <- descendBiM (addCopysPerBlockGroup tenv) x
return [x']
equalTypes tenv e e' = do
v1 <- extractVariable e
v2 <- extractVariable e'
t1 <- M.lookup v1 tenv
t2 <- M.lookup v2 tenv
if t1 == t2 then Just t1 else Nothing
mkCopy :: FAT.TypeEnv
-> FU.Position
-> F.Expression A1 -> F.Expression A1 -> F.Block A1
mkCopy tenv pos srcE dstE = FA.initAnalysis $
F.BlStatement a sp Nothing $
case equalTypes tenv srcE dstE of
Nothing -> F.StExpressionAssign a sp dstE' call
where
call = F.ExpFunctionCall a sp transf argst
transf = F.ExpValue a sp (F.ValVariable "transfer")
argst = Just (F.AList a sp args)
args = map (F.Argument a sp Nothing) [srcE', dstE']
Just _ -> F.StExpressionAssign a sp dstE' srcE'
where
sp = FU.SrcSpan (toCol0 pos) (toCol0 pos)
a = unitAnnotation { refactored = Just pos, newNode = True }
dstE' = FA.stripAnalysis dstE
srcE' = FA.stripAnalysis srcE
perBlockRmEquiv :: F.Block A1 -> State RmEqState (F.Block A1)
perBlockRmEquiv = transformBiM perStatementRmEquiv
perStatementRmEquiv :: F.Statement A1 -> State RmEqState (F.Statement A1)
perStatementRmEquiv (F.StEquivalence a sp@(FU.SrcSpan spL _) equivs) = do
(ess, n, r) <- get
let report = r ++ show spL ++ ": removed equivalence \n"
put (((map F.aStrip) . F.aStrip $ equivs) ++ ess, n 1, r ++ report)
let a' = onPrev (\ap -> ap {refactored = Just spL, deleteNode = True}) a
return (F.StEquivalence a' (deleteLine sp) equivs)
perStatementRmEquiv f = return f
equivalentsToExpr :: F.Expression A1 -> State RmEqState [F.Expression A1]
equivalentsToExpr x = do
(equivs, _, _) <- get
return (inGroup x equivs)
where
inGroup _ [] = []
inGroup x (xs:xss) =
if AnnotationFree x `elem` map AnnotationFree xs
then xs
else inGroup x xss