{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE PatternGuards #-}
{-# LANGUAGE TupleSections #-}
{-# LANGUAGE DeriveGeneric #-}
{-# LANGUAGE DeriveDataTypeable #-}
module Language.Fortran.Util.ModFile
( modFileSuffix, ModFile, ModFiles, emptyModFile, emptyModFiles
, lookupModFileData, getLabelsModFileData, alterModFileData
, genModFile, regenModFile, encodeModFile, decodeModFile
, DeclMap, DeclContext(..), extractModuleMap, extractDeclMap
, moduleFilename, combinedDeclMap, combinedModuleMap, combinedTypeEnv
, genUniqNameToFilenameMap )
where
import Data.Data
import Data.Maybe
import Data.Generics.Uniplate.Operations
import qualified Data.Map.Strict as M
import Data.Binary
import GHC.Generics (Generic)
import qualified Data.ByteString.Char8 as B
import qualified Data.ByteString.Lazy.Char8 as LB
import qualified Language.Fortran.Util.Position as P
import qualified Language.Fortran.AST as F
import qualified Language.Fortran.Analysis as FA
import qualified Language.Fortran.Analysis.Renaming as FAR
import qualified Language.Fortran.Analysis.Types as FAT
modFileSuffix :: String
modFileSuffix = ".fsmod"
data DeclContext = DCMain | DCBlockData | DCModule F.ProgramUnitName
| DCFunction (F.ProgramUnitName, F.ProgramUnitName)
| DCSubroutine (F.ProgramUnitName, F.ProgramUnitName)
deriving (Ord, Eq, Show, Data, Typeable, Generic)
instance Binary DeclContext
type DeclMap = M.Map F.Name (DeclContext, P.SrcSpan)
data ModFile = ModFile { mfFilename :: String
, mfModuleMap :: FAR.ModuleMap
, mfDeclMap :: DeclMap
, mfTypeEnv :: FAT.TypeEnv
, mfOtherData :: M.Map String B.ByteString }
deriving (Ord, Eq, Show, Data, Typeable, Generic)
instance Binary ModFile
type ModFiles = [ModFile]
emptyModFiles :: ModFiles
emptyModFiles = []
emptyModFile :: ModFile
emptyModFile = ModFile "" M.empty M.empty M.empty M.empty
regenModFile :: forall a. Data a => F.ProgramFile (FA.Analysis a) -> ModFile -> ModFile
regenModFile pf mf = mf
{ mfModuleMap = extractModuleMap pf
, mfDeclMap = extractDeclMap pf
, mfTypeEnv = FAT.extractTypeEnv pf
, mfFilename = F.pfGetFilename pf }
genModFile :: forall a. Data a => F.ProgramFile (FA.Analysis a) -> ModFile
genModFile = flip regenModFile emptyModFile
lookupModFileData :: String -> ModFile -> Maybe B.ByteString
lookupModFileData k = M.lookup k . mfOtherData
getLabelsModFileData :: ModFile -> [String]
getLabelsModFileData = M.keys . mfOtherData
alterModFileData :: (Maybe B.ByteString -> Maybe B.ByteString) -> String -> ModFile -> ModFile
alterModFileData f k mf = mf { mfOtherData = M.alter f k . mfOtherData $ mf }
encodeModFile :: ModFile -> B.ByteString
encodeModFile = LB.toStrict . encode
decodeModFile :: Binary a => B.ByteString -> Either String a
decodeModFile bs = case decodeOrFail (LB.fromStrict bs) of
Left (_, _, s) -> Left s
Right (_, _, mf) -> Right mf
combinedModuleMap :: ModFiles -> FAR.ModuleMap
combinedModuleMap = M.unions . map mfModuleMap
combinedTypeEnv :: ModFiles -> FAT.TypeEnv
combinedTypeEnv = M.unions . map mfTypeEnv
combinedDeclMap :: ModFiles -> DeclMap
combinedDeclMap = M.unions . map mfDeclMap
moduleFilename :: ModFile -> String
moduleFilename = mfFilename
genUniqNameToFilenameMap :: ModFiles -> M.Map F.Name String
genUniqNameToFilenameMap = M.unions . map perMF
where
perMF mf = M.fromList [ (n, fname) | modEnv <- M.elems (mfModuleMap mf)
, (n, _) <- M.elems modEnv ]
where
fname = mfFilename mf
extractModuleMap :: forall a. Data a => F.ProgramFile (FA.Analysis a) -> FAR.ModuleMap
extractModuleMap pf = M.fromList [ (n, env) | pu@(F.PUModule {}) <- universeBi pf :: [F.ProgramUnit (FA.Analysis a)]
, let a = F.getAnnotation pu
, let n = F.getName pu
, env <- maybeToList (FA.moduleEnv a) ]
extractDeclMap :: forall a. Data a => F.ProgramFile (FA.Analysis a) -> DeclMap
extractDeclMap pf = M.fromList . concatMap (blockDecls . nameAndBlocks) $ universeBi pf
where
blockDecls :: (DeclContext, Maybe (F.Name, P.SrcSpan), [F.Block (FA.Analysis a)]) -> [(F.Name, (DeclContext, P.SrcSpan))]
blockDecls (dc, mret, bs)
| Nothing <- mret = map decls (universeBi bs)
| Just (ret, ss) <- mret = (ret, (dc, ss)):map decls (universeBi bs)
where
decls d = let (v, ss) = declVarName d in (v, (dc, ss))
declVarName :: F.Declarator (FA.Analysis a) -> (F.Name, P.SrcSpan)
declVarName (F.DeclVariable _ _ e _ _) = (FA.varName e, P.getSpan e)
declVarName (F.DeclArray _ _ e _ _ _) = (FA.varName e, P.getSpan e)
nameAndBlocks :: F.ProgramUnit (FA.Analysis a) -> (DeclContext, Maybe (F.Name, P.SrcSpan), [F.Block (FA.Analysis a)])
nameAndBlocks pu = case pu of
F.PUMain _ _ _ b _ -> (DCMain, Nothing, b)
F.PUModule _ _ _ b _ -> (DCModule $ FA.puName pu, Nothing, b)
F.PUSubroutine _ _ _ _ _ b _ -> (DCSubroutine (FA.puName pu, FA.puSrcName pu), Nothing, b)
F.PUFunction _ _ _ _ _ _ mret b _
| Nothing <- mret
, F.Named n <- FA.puName pu -> (DCFunction (FA.puName pu, FA.puSrcName pu), Just (n, P.getSpan pu), b)
| Just ret <- mret -> (DCFunction (FA.puName pu, FA.puSrcName pu), Just (FA.varName ret, P.getSpan ret), b)
| otherwise -> error $ "nameAndBlocks: un-named function with no return value! " ++ show (FA.puName pu) ++ " at source-span " ++ show (P.getSpan pu)
F.PUBlockData _ _ _ b -> (DCBlockData, Nothing, b)
F.PUComment {} -> (DCBlockData, Nothing, [])