module Language.Fortran.Analysis.Renaming
( analyseRenames, rename, extractNameMap, renameAndStrip, unrename, underRenaming, NameMap )
where
import Debug.Trace
import Language.Fortran.AST hiding (fromList)
import Language.Fortran.Util.Position
import Language.Fortran.Analysis
import Language.Fortran.Analysis.Types
import Prelude hiding (lookup)
import Data.Maybe (maybe, fromMaybe)
import qualified Data.List as L
import Data.Map (findWithDefault, insert, union, empty, lookup, member, Map, fromList)
import qualified Data.Map as M
import Control.Monad.State.Strict
import Control.Monad
import Data.Generics.Uniplate.Data
import Data.Generics.Uniplate.Operations
import Data.Data
import Data.Tuple
type ModuleMap = Map ProgramUnitName ModEnv
type NameMap = Map String String
type Renamer a = State RenameState a
data RenameState = RenameState { scopeStack :: [String]
, uniqNums :: [Int]
, environ :: [ModEnv]
, nameMap :: NameMap
, moduleMap :: ModuleMap }
deriving (Show, Eq)
type RenamerFunc t = t -> Renamer t
analyseRenames :: Data a => ProgramFile (Analysis a) -> ProgramFile (Analysis a)
analyseRenames (ProgramFile cm_pus bs) = ProgramFile cm_pus' bs
where
cm_pus' = zip (map fst cm_pus) pus'
(Just pus', _) = runRenamer (skimProgramUnits pus >> renameSubPUs (Just pus)) renameState0
pus = map snd cm_pus
rename :: Data a => ProgramFile (Analysis a) -> (NameMap, ProgramFile (Analysis a))
rename pf = (extractNameMap pf, trPU fPU (trE fE pf))
where
trE :: Data a => (Expression a -> Expression a) -> ProgramFile a -> ProgramFile a
trE = transformBi
fE :: Data a => Expression (Analysis a) -> Expression (Analysis a)
fE (ExpValue a s (ValVariable v)) = ExpValue a s . ValVariable $ fromMaybe v (uniqueName a)
fE x = x
trPU :: Data a => (ProgramUnit a -> ProgramUnit a) -> ProgramFile a -> ProgramFile a
trPU = transformBi
fPU :: Data a => ProgramUnit (Analysis a) -> ProgramUnit (Analysis a)
fPU (PUFunction a s ty r n args res b subs) =
PUFunction a s ty r (fromMaybe n (uniqueName a)) args res b subs
fPU (PUSubroutine a s r n args b subs) =
PUSubroutine a s r (fromMaybe n (uniqueName a)) args b subs
fPU x = x
extractNameMap :: Data a => ProgramFile (Analysis a) -> NameMap
extractNameMap pf = eMap `union` puMap
where
eMap = fromList [ (un, n) | ExpValue (Analysis { uniqueName = Just un }) _ (ValVariable n) <- uniE pf ]
puMap = fromList [ (un, n) | pu <- uniPU pf, Named un <- [puName pu], Named n <- [getName pu], n /= un ]
uniE :: Data a => ProgramFile a -> [Expression a]
uniE = universeBi
uniPU :: Data a => ProgramFile a -> [ProgramUnit a]
uniPU = universeBi
renameAndStrip :: Data a => ProgramFile (Analysis a) -> (NameMap, ProgramFile a)
renameAndStrip pf = fmap stripAnalysis (rename pf)
unrename :: Data a => (NameMap, ProgramFile a) -> ProgramFile a
unrename (nm, pf) = trPU fPU . trV fV $ pf
where
trV :: Data a => (Value a -> Value a) -> ProgramFile a -> ProgramFile a
trV = transformBi
fV :: Data a => Value a -> Value a
fV (ValVariable v) = ValVariable $ fromMaybe v (v `lookup` nm)
fV x = x
trPU :: Data a => (ProgramUnit a -> ProgramUnit a) -> ProgramFile a -> ProgramFile a
trPU = transformBi
fPU :: Data a => ProgramUnit a -> ProgramUnit a
fPU (PUFunction a s ty r n args res b subs) = PUFunction a s ty r (fromMaybe n (n `lookup` nm)) args res b subs
fPU x = x
underRenaming :: (Data a, Data b) => (ProgramFile (Analysis a) -> b) -> ProgramFile a -> b
underRenaming f pf = tryUnrename `descendBi` f pf'
where
(renameMap, pf') = rename . analyseRenames . initAnalysis $ pf
tryUnrename n = n `fromMaybe` lookup n renameMap
programUnit :: Data a => RenamerFunc (ProgramUnit (Analysis a))
programUnit (PUModule a s name blocks m_contains) = do
env0 <- initialEnv blocks
pushScope name env0
blocks' <- mapM renameDeclDecls blocks
m_contains' <- renameSubPUs m_contains
env <- getEnv
addModEnv name env
let a' = a { moduleEnv = Just env }
popScope
return (PUModule a' s name blocks' m_contains')
programUnit (PUFunction a s ty rec name args res blocks m_contains) = do
Just name' <- getFromEnv name
blocks1 <- mapM renameEntryPointDecl blocks
env0 <- initialEnv blocks1
pushScope name env0
blocks2 <- mapM renameEntryPointResultDecl blocks1
res' <- mapM renameGenericDecls res
args' <- mapM renameGenericDecls args
blocks3 <- mapM renameDeclDecls blocks2
m_contains' <- renameSubPUs m_contains
blocks4 <- mapM renameBlock blocks3
popScope
return . setUniqueName name' $ PUFunction a s ty rec name args' res' blocks4 m_contains'
programUnit (PUSubroutine a s rec name args blocks m_contains) = do
Just name' <- getFromEnv name
blocks1 <- mapM renameEntryPointDecl blocks
env0 <- initialEnv blocks1
pushScope name env0
args' <- mapM renameGenericDecls args
blocks2 <- mapM renameDeclDecls blocks1
m_contains' <- renameSubPUs m_contains
blocks3 <- mapM renameBlock blocks2
popScope
return . setUniqueName name' $ PUSubroutine a s rec name args' blocks3 m_contains'
programUnit (PUMain a s n blocks m_contains) = do
env0 <- initialEnv blocks
pushScope (fromMaybe "_main" n) env0
blocks' <- mapM renameDeclDecls blocks
m_contains' <- renameSubPUs m_contains
blocks'' <- mapM renameBlock blocks'
popScope
return (PUMain a s n blocks'' m_contains')
programUnit pu = return pu
declarator :: Data a => RenamerFunc (Declarator (Analysis a))
declarator = renameGenericDecls
expression :: Data a => RenamerFunc (Expression (Analysis a))
expression = renameExp
renameState0 = RenameState { scopeStack = []
, uniqNums = [1..]
, environ = [empty]
, nameMap = empty
, moduleMap = empty }
runRenamer m = runState m
getUniqNum :: Renamer Int
getUniqNum = do
uniqNum <- gets (head . uniqNums)
modify $ \ s -> s { uniqNums = drop 1 (uniqNums s) }
return uniqNum
uniquify :: String -> String -> Renamer String
uniquify scope var = do
n <- getUniqNum
return $ scope ++ "_" ++ var ++ show n
isModule (PUModule {}) = True; isModule _ = False
isUseStatement (BlStatement _ _ _ (StUse _ _ (ExpValue _ _ (ValVariable _)) _)) = True
isUseStatement _ = False
isUseID (UseID {}) = True; isUseID _ = False
initialEnv :: Data a => [Block (Analysis a)] -> Renamer ModEnv
initialEnv blocks = do
let uses = takeWhile isUseStatement blocks
fmap M.unions . forM uses $ \ use -> case use of
(BlStatement _ _ _ (StUse _ _ (ExpValue _ _ (ValVariable m)) Nothing)) -> do
mMap <- gets moduleMap
return $ fromMaybe empty (Named m `lookup` mMap)
(BlStatement _ _ _ (StUse _ _ (ExpValue _ _ (ValVariable m)) (Just onlyAList)))
| only <- aStrip onlyAList, all isUseID only -> do
mMap <- gets moduleMap
let env = fromMaybe empty (Named m `lookup` mMap)
let onlyNames = map (\ (UseID _ _ v) -> varName v) only
return $ M.filterWithKey (\ k _ -> k `elem` onlyNames) env
_ -> trace "WARNING: USE renaming not supported (yet)" $ return empty
getScope :: Renamer String
getScope = gets (head . scopeStack)
getScopes :: Renamer String
getScopes = gets (L.intercalate "_" . reverse . scopeStack)
pushScope :: String -> ModEnv -> Renamer ()
pushScope name env0 = modify $ \ s -> s { scopeStack = name : scopeStack s
, environ = env0 : environ s }
popScope :: Renamer ()
popScope = modify $ \ s -> s { scopeStack = drop 1 $ scopeStack s
, environ = drop 1 $ environ s }
addModEnv :: String -> ModEnv -> Renamer ()
addModEnv name env = modify $ \ s -> s { moduleMap = insert (Named name) env (moduleMap s) }
getEnv :: Renamer ModEnv
getEnv = gets (head . environ)
getEnvs :: Renamer ModEnv
getEnvs = M.unionsWith (curry fst) `fmap` gets environ
getFromEnv :: String -> Renamer (Maybe String)
getFromEnv v = ((fst `fmap`) . lookup v) `fmap` getEnv
getFromEnvs :: String -> Renamer (Maybe String)
getFromEnvs v = ((fst `fmap`) . lookup v) `fmap` getEnvs
getFromEnvsWithType :: String -> Renamer (Maybe (String, NameType))
getFromEnvsWithType v = lookup v `fmap` getEnvs
getFromEnvsIfSubprogram :: String -> Renamer (Maybe String)
getFromEnvsIfSubprogram v = do
mEntry <- getFromEnvsWithType v
case mEntry of
Just (v', NTSubprogram) -> return $ Just v'
Just (_, NTVariable) -> getFromEnv v
_ -> return $ Nothing
addToEnv :: String -> String -> NameType -> Renamer ()
addToEnv v v' nt = modify $ \ s -> s { environ = insert v (v', nt) (head (environ s)) : drop 1 (environ s) }
addUnique :: String -> NameType -> Renamer String
addUnique v nt = do
v' <- flip uniquify v =<< getScopes
addToEnv v v' nt
return v'
addUnique_ :: String -> NameType -> Renamer ()
addUnique_ v nt = addUnique v nt >> return ()
maybeAddUnique :: String -> NameType -> Renamer String
maybeAddUnique v nt = maybe (addUnique v nt) return =<< getFromEnvsIfSubprogram v
setUniqueName :: (Annotated f, Data a) => String -> f (Analysis a) -> f (Analysis a)
setUniqueName un x
| a@(Analysis { uniqueName = Nothing }) <- getAnnotation x = setAnnotation (a { uniqueName = Just un }) x
| otherwise = x
renameSubPUs :: Data a => RenamerFunc (Maybe [ProgramUnit (Analysis a)])
renameSubPUs Nothing = return Nothing
renameSubPUs (Just pus) = skimProgramUnits pus >> Just `fmap` (mapM programUnit pus)
skimProgramUnits :: Data a => [ProgramUnit (Analysis a)] -> Renamer ()
skimProgramUnits pus = forM_ pus $ \ pu -> case pu of
PUModule _ _ name _ _ -> addToEnv name name NTSubprogram
PUFunction _ _ _ _ name _ _ _ _ -> addUnique_ name NTSubprogram
PUSubroutine _ _ _ name _ _ _ -> addUnique_ name NTSubprogram
PUMain _ _ (Just name) _ _ -> addToEnv name name NTSubprogram
_ -> return ()
renameGenericDecls :: (Data a, Data (f (Analysis a))) => RenamerFunc (f (Analysis a))
renameGenericDecls = trans renameExpDecl
where
trans :: (Data a, Data (f (Analysis a))) => RenamerFunc (Expression (Analysis a)) -> RenamerFunc (f (Analysis a))
trans = transformBiM
renameExpDecl :: Data a => RenamerFunc (Expression (Analysis a))
renameExpDecl e@(ExpValue _ _ (ValVariable v)) = flip setUniqueName e `fmap` maybeAddUnique v NTVariable
renameExpDecl e = return e
renameDeclDecls :: (Data a, Data (f (Analysis a))) => RenamerFunc (f (Analysis a))
renameDeclDecls = trans declarator
where
trans :: (Data a, Data (f (Analysis a))) => RenamerFunc (Declarator (Analysis a)) -> RenamerFunc (f (Analysis a))
trans = transformBiM
renameEntryPointDecl :: Data a => RenamerFunc (Block (Analysis a))
renameEntryPointDecl (BlStatement a s l (StEntry a' s' v mArgs mRes)) = do
v' <- renameExpDecl v
return (BlStatement a s l (StEntry a' s' v' mArgs mRes))
renameEntryPointDecl b = return b
renameEntryPointResultDecl :: Data a => RenamerFunc (Block (Analysis a))
renameEntryPointResultDecl (BlStatement a s l (StEntry a' s' v mArgs (Just res))) = do
res' <- renameExpDecl res
return (BlStatement a s l (StEntry a' s' v mArgs (Just res')))
renameEntryPointResultDecl b = return b
renameExp :: Data a => RenamerFunc (Expression (Analysis a))
renameExp e@(ExpValue _ _ (ValVariable v)) = maybe e (flip setUniqueName e) `fmap` getFromEnvs v
renameExp e = return e
renameBlock :: Data a => RenamerFunc (Block (Analysis a))
renameBlock = trans expression
where
trans :: Data a => RenamerFunc (Expression a) -> RenamerFunc (Block a)
trans = transformBiM