{-# LANGUAGE ScopedTypeVariables, DeriveDataTypeable, StandaloneDeriving, DeriveGeneric, TupleSections #-}
module Language.Fortran.Analysis
( initAnalysis, stripAnalysis, Analysis(..)
, varName, srcName, lvVarName, lvSrcName, isNamedExpression
, genVar, puName, puSrcName, blockRhsExprs, rhsExprs
, ModEnv, NameType(..), IDType(..), ConstructType(..), BaseType(..)
, lhsExprs, isLExpr, allVars, analyseAllLhsVars, analyseAllLhsVars1, allLhsVars
, blockVarUses, blockVarDefs
, BB, BBGr
, TransFunc, TransFuncM )
where
import Language.Fortran.Util.Position (SrcSpan)
import Data.Generics.Uniplate.Data
import Data.Data
import Language.Fortran.AST
import Language.Fortran.LValue
import Data.Graph.Inductive.PatriciaTree (Gr)
import GHC.Generics (Generic)
import Text.PrettyPrint.GenericPretty
import Text.PrettyPrint
import qualified Data.Map.Strict as M
import Data.Maybe
import Data.Binary
import Language.Fortran.Intrinsics (getIntrinsicDefsUses, allIntrinsics)
import Data.Bifunctor (first)
type BB a = [Block a]
type BBGr a = Gr (BB a) ()
deriving instance (Typeable a, Typeable b) => Typeable (Gr a b)
instance (Typeable a, Typeable b) => Data (Gr a b) where
gfoldl _k z v = z v
toConstr _ = error "toConstr"
gunfold _ _ = error "gunfold"
dataTypeOf _ = mkNoRepType "Gr"
type TransFunc f g a = (f (Analysis a) -> f (Analysis a)) -> g (Analysis a) -> g (Analysis a)
type TransFuncM m f g a = (f (Analysis a) -> m (f (Analysis a))) -> g (Analysis a) -> m (g (Analysis a))
data NameType = NTSubprogram | NTVariable | NTIntrinsic deriving (Show, Eq, Ord, Data, Typeable, Generic)
instance Binary NameType
instance Out NameType
type ModEnv = M.Map String (String, NameType)
data ConstructType =
CTFunction
| CTSubroutine
| CTExternal
| CTVariable
| CTArray
| CTParameter
| CTIntrinsic
deriving (Ord, Eq, Show, Data, Typeable, Generic)
instance Out ConstructType
instance Binary ConstructType
data IDType = IDType
{ idVType :: Maybe BaseType
, idCType :: Maybe ConstructType }
deriving (Ord, Eq, Show, Data, Typeable, Generic)
instance Out IDType
instance Binary IDType
data Analysis a = Analysis
{ prevAnnotation :: a
, uniqueName :: Maybe String
, sourceName :: Maybe String
, bBlocks :: Maybe (BBGr (Analysis a))
, insLabel :: Maybe Int
, moduleEnv :: Maybe ModEnv
, idType :: Maybe IDType
, allLhsVarsAnn :: [Name]
}
deriving (Data, Show, Eq, Generic)
instance Functor Analysis where
fmap f analysis =
Analysis
{ prevAnnotation = f (prevAnnotation analysis)
, uniqueName = uniqueName analysis
, sourceName = sourceName analysis
, bBlocks = fmap (first . fmap . fmap . fmap $ f) . bBlocks $ analysis
, insLabel = insLabel analysis
, moduleEnv = moduleEnv analysis
, idType = idType analysis
, allLhsVarsAnn = allLhsVarsAnn analysis
}
instance Out (Analysis a) where
doc a = parens . text . unwords . map (uncurry (++) . fmap fromJust) . filter (isJust . snd) $
[ ("uniqueName: ", uniqueName a)
, ("sourceName: ", sourceName a)
, ("insLabel: ", fmap show (insLabel a))
, ("idType: ", fmap show (idType a)) ]
docPrec _ = doc
analysis0 a = Analysis { prevAnnotation = a
, uniqueName = Nothing
, sourceName = Nothing
, bBlocks = Nothing
, insLabel = Nothing
, moduleEnv = Nothing
, idType = Nothing
, allLhsVarsAnn = [] }
isNamedExpression :: Expression a -> Bool
isNamedExpression (ExpValue _ _ (ValVariable _)) = True
isNamedExpression (ExpValue _ _ (ValIntrinsic _)) = True
isNamedExpression _ = False
varName :: Expression (Analysis a) -> String
varName (ExpValue (Analysis { uniqueName = Just n }) _ (ValVariable {})) = n
varName (ExpValue (Analysis { sourceName = Just n }) _ (ValVariable {})) = n
varName (ExpValue _ _ (ValVariable n)) = n
varName (ExpValue (Analysis { uniqueName = Just n }) _ (ValIntrinsic {})) = n
varName (ExpValue (Analysis { sourceName = Just n }) _ (ValIntrinsic {})) = n
varName (ExpValue _ _ (ValIntrinsic n)) = n
varName _ = error "Use of varName on non-variable."
srcName :: Expression (Analysis a) -> String
srcName (ExpValue (Analysis { sourceName = Just n }) _ (ValVariable {})) = n
srcName (ExpValue _ _ (ValVariable n)) = n
srcName (ExpValue (Analysis { sourceName = Just n }) _ (ValIntrinsic {})) = n
srcName (ExpValue _ _ (ValIntrinsic n)) = n
srcName _ = error "Use of srcName on non-variable."
lvVarName :: LValue (Analysis a) -> String
lvVarName (LvSimpleVar (Analysis { uniqueName = Just n }) _ _) = n
lvVarName (LvSimpleVar (Analysis { sourceName = Just n }) _ _) = n
lvVarName (LvSimpleVar _ _ n) = n
lvVarName _ = error "Use of lvVarName on non-variable."
lvSrcName :: LValue (Analysis a) -> String
lvSrcName (LvSimpleVar (Analysis { sourceName = Just n }) _ _) = n
lvSrcName (LvSimpleVar _ _ n) = n
lvSrcName _ = error "Use of lvSrcName on a non-variable"
genVar :: Analysis a -> SrcSpan -> String -> Expression (Analysis a)
genVar a s n = ExpValue (a { uniqueName = Just n, sourceName = Just n }) s v
where
v | Just CTIntrinsic <- idCType =<< idType a = ValIntrinsic n
| otherwise = ValVariable n
puName :: ProgramUnit (Analysis a) -> ProgramUnitName
puName pu
| Just n <- uniqueName (getAnnotation pu) = Named n
| otherwise = getName pu
puSrcName :: ProgramUnit (Analysis a) -> ProgramUnitName
puSrcName pu
| Just n <- sourceName (getAnnotation pu) = Named n
| otherwise = getName pu
initAnalysis :: Functor b => b a -> b (Analysis a)
initAnalysis = fmap analysis0
stripAnalysis :: Functor b => b (Analysis a) -> b a
stripAnalysis = fmap prevAnnotation
lhsExprs :: forall a b . (Data a, Data (b a)) => b a -> [Expression a]
lhsExprs x = concatMap lhsOfStmt (universeBi x)
where
lhsOfStmt :: Statement a -> [Expression a]
lhsOfStmt (StExpressionAssign _ _ e e') = e : onExprs e'
lhsOfStmt (StCall _ _ _ (Just aexps)) = filter isLExpr argExps ++ concatMap onExprs argExps
where argExps = map extractExp . aStrip $ aexps
lhsOfStmt s = onExprs s
onExprs :: (Data a, Data (c a)) => c a -> [Expression a]
onExprs = concatMap lhsOfExp . universeBi
lhsOfExp :: Expression a -> [Expression a]
lhsOfExp (ExpFunctionCall _ _ _ (Just aexps)) = fstLvl aexps
lhsOfExp _ = []
fstLvl = filter isLExpr . map extractExp . aStrip
extractExp (Argument _ _ _ exp) = exp
rhsExprs :: (Data a, Data (b a)) => b a -> [Expression a]
rhsExprs x = concat [ blockRhsExprs b | b <- universeBi x ]
isLExpr :: Expression a -> Bool
isLExpr (ExpValue _ _ (ValVariable {})) = True
isLExpr (ExpSubscript _ _ _ _) = True
isLExpr _ = False
allVars :: forall a b. (Data a, Data (b (Analysis a))) => b (Analysis a) -> [Name]
allVars b = [ varName v | v@(ExpValue _ _ (ValVariable _)) <- uniBi b ]
where
uniBi x = universeBi x :: [Expression (Analysis a)]
analyseAllLhsVars :: forall a . Data a => ProgramFile (Analysis a) -> ProgramFile (Analysis a)
analyseAllLhsVars = (transformBi :: TransFunc Block ProgramFile a) analyseAllLhsVars1 .
(transformBi :: TransFunc Statement ProgramFile a) analyseAllLhsVars1 .
(transformBi :: TransFunc DoSpecification ProgramFile a) analyseAllLhsVars1
analyseAllLhsVars1 :: (Annotated f, Data (f (Analysis a)), Data a) => f (Analysis a) -> f (Analysis a)
analyseAllLhsVars1 x = modifyAnnotation (\ a -> a { allLhsVarsAnn = computeAllLhsVars x }) x
allLhsVars :: Data a => Block (Analysis a) -> [Name]
allLhsVars x = allLhsVarsAnn . getAnnotation $ x
allLhsVarsDoSpec :: Data a => DoSpecification (Analysis a) -> [Name]
allLhsVarsDoSpec x = computeAllLhsVars x
computeAllLhsVars :: forall a b . (Data a, Data (b (Analysis a))) => b (Analysis a) -> [Name]
computeAllLhsVars = concatMap lhsOfStmt . universeBi
where
lhsOfStmt :: Statement (Analysis a) -> [Name]
lhsOfStmt (StExpressionAssign _ _ e e') = match' e : onExprs e'
lhsOfStmt (StCall _ _ f@(ExpValue _ _ (ValIntrinsic _)) _)
| Just defs <- intrinsicDefs f = defs
lhsOfStmt (StCall _ _ _ (Just aexps)) = concatMap (match'' . extractExp) (aStrip aexps)
lhsOfStmt s = onExprs s
onExprs :: (Data (c (Analysis a))) => c (Analysis a) -> [Name]
onExprs = concatMap lhsOfExp . universeBi
lhsOfExp :: Expression (Analysis a) -> [Name]
lhsOfExp (ExpFunctionCall _ _ _ (Just aexps)) = concatMap (match . extractExp) (aStrip aexps)
lhsOfExp _ = []
extractExp (Argument _ _ _ exp) = exp
match' v@(ExpValue _ _ (ValVariable {})) = varName v
match' (ExpSubscript _ _ v@(ExpValue _ _ (ValVariable {})) _) = varName v
match' (ExpDataRef _ _ v _) = match' v
match' e = error $ "An unexpected LHS to an expression assign: " ++ show (fmap (const ()) e)
match'' v@(ExpValue _ _ (ValVariable {})) = [varName v]
match'' (ExpSubscript _ _ v@(ExpValue _ _ (ValVariable {})) _) = [varName v]
match'' (ExpDataRef _ _ v _) = match'' v
match'' e = onExprs e
match v@(ExpValue _ _ (ValVariable {})) = [varName v]
match (ExpSubscript _ _ v@(ExpValue _ _ (ValVariable {})) _) = [varName v]
match (ExpDataRef _ _ e _) = match e
match e = []
blockRhsExprs :: Data a => Block a -> [Expression a]
blockRhsExprs (BlStatement _ _ _ s) = statementRhsExprs s
blockRhsExprs (BlDo _ _ _ _ _ (Just (DoSpecification _ _ (StExpressionAssign _ _ lhs rhs) e1 e2)) _ _)
| ExpSubscript _ _ _ subs <- lhs = universeBi (rhs, e1, e2) ++ universeBi subs
| otherwise = universeBi (rhs, e1, e2)
blockRhsExprs (BlDoWhile _ _ e1 _ _ e2 _ _) = universeBi (e1, e2)
blockRhsExprs (BlIf _ _ e1 _ e2 _ _) = universeBi (e1, e2)
blockRhsExprs b = universeBi b
statementRhsExprs :: Data a => Statement a -> [Expression a]
statementRhsExprs (StExpressionAssign _ _ lhs rhs)
| ExpSubscript _ _ _ subs <- lhs = universeBi rhs ++ universeBi subs
| otherwise = universeBi rhs
statementRhsExprs (StDeclaration {}) = []
statementRhsExprs (StIfLogical _ _ _ s) = statementRhsExprs s
statementRhsExprs (StDo _ _ _ l s) = (universeBi l) ++ doSpecRhsExprs s
where doSpecRhsExprs (Just (DoSpecification _ _ s e1 e2)) =
(e1 : (universeBi e2)) ++ statementRhsExprs s
doSpecRhsExprs Nothing = []
statementRhsExprs s = universeBi s
blockVarUses :: Data a => Block (Analysis a) -> [Name]
blockVarUses (BlStatement _ _ _ (StExpressionAssign _ _ lhs rhs))
| ExpSubscript _ _ _ subs <- lhs = allVars rhs ++ concatMap allVars (aStrip subs)
| otherwise = allVars rhs
blockVarUses (BlDo _ _ _ _ _ (Just (DoSpecification _ _ (StExpressionAssign _ _ lhs rhs) e1 e2)) _ _)
| ExpSubscript _ _ _ subs <- lhs = allVars rhs ++ allVars e1 ++ maybe [] allVars e2 ++ concatMap allVars (aStrip subs)
| otherwise = allVars rhs ++ allVars e1 ++ maybe [] allVars e2
blockVarUses (BlStatement _ _ _ (StDeclaration {})) = []
blockVarUses (BlStatement _ _ _ (StCall _ _ f@(ExpValue _ _ (ValIntrinsic _)) _))
| Just uses <- intrinsicUses f = uses
blockVarUses (BlStatement _ _ _ (StCall _ _ _ (Just aexps))) = allVars aexps
blockVarUses (BlDoWhile _ _ e1 _ _ e2 _ _) = maybe [] allVars e1 ++ allVars e2
blockVarUses (BlIf _ _ e1 _ e2 _ _) = maybe [] allVars e1 ++ concatMap (maybe [] allVars) e2
blockVarUses b = allVars b
blockVarDefs :: Data a => Block (Analysis a) -> [Name]
blockVarDefs b@(BlStatement _ _ _ st) = allLhsVars b
blockVarDefs (BlDo _ _ _ _ _ (Just doSpec) _ _) = allLhsVarsDoSpec doSpec
blockVarDefs _ = []
dummyArg :: Name -> Int -> Name
dummyArg n i = n ++ "[" ++ show i ++ "]"
intrinsicDefs :: Expression (Analysis a) -> Maybe [Name]
intrinsicDefs = fmap fst . intrinsicDefsUses
intrinsicUses :: Expression (Analysis a) -> Maybe [Name]
intrinsicUses = fmap snd . intrinsicDefsUses
intrinsicDefsUses :: Expression (Analysis a) -> Maybe ([Name], [Name])
intrinsicDefsUses f = both (map (dummyArg (varName f))) <$> getIntrinsicDefsUses (srcName f) allIntrinsics
where both f (x, y) = (f x, f y)