module Language.Fortran.Vars
  ( programUnitModel
  , programFileModel
  )
where

import           Language.Fortran.Extras
                                                ( allPUS )
import           Data.Data                      ( Data )
import           Data.Function                  ( (&) )
import           Data.List                      ( foldl' )
import qualified Data.Map                      as M
import           Language.Fortran.Analysis      ( Analysis
                                                , puName
                                                )
import           Language.Fortran.AST           ( ProgramFile(..)
                                                , ProgramUnit(..)
                                                )

import           Language.Fortran.Vars.Dummy
                                                ( undefineDummyArguments )
import           Language.Fortran.Vars.Equivalence
                                                ( processEquivalence )
import           Language.Fortran.Vars.Memory
                                                ( allocateMemoryBlocks
                                                , processCommon
                                                )
import           Language.Fortran.Vars.StorageClass
                                                ( processStorageClass )
import           Language.Fortran.Vars.SymbolTable
                                                ( collectSymbols )
import           Language.Fortran.Vars.Types
                                                ( ProgramFileModel
                                                , ProgramUnitModel
                                                )

-- | Given a 'ProgramUnit', generate a 'ProgramUnitModel' that contains not only a
-- 'SymbolTable' for the non-intrinsic symbols in the unit, but also a
-- 'Language.Fortran.Vars.Types.StorageTable' that determines the locations
-- that non-constant, non-parameter variables will be allocated
programUnitModel :: Data a => ProgramUnit (Analysis a) -> ProgramUnitModel
programUnitModel :: forall a. Data a => ProgramUnit (Analysis a) -> ProgramUnitModel
programUnitModel ProgramUnit (Analysis a)
pu =
  let stmts :: [Statement (Analysis a)]
stmts     = ProgramUnit (Analysis a) -> [Statement (Analysis a)]
forall a. Data a => ProgramUnit a -> [Statement a]
allPUS ProgramUnit (Analysis a)
pu
      symTable1 :: SymbolTable
symTable1 = ProgramUnit (Analysis a) -> SymbolTable -> SymbolTable
forall a.
Data a =>
ProgramUnit (Analysis a) -> SymbolTable -> SymbolTable
undefineDummyArguments ProgramUnit (Analysis a)
pu (SymbolTable -> SymbolTable)
-> (ProgramUnit (Analysis a) -> SymbolTable)
-> ProgramUnit (Analysis a)
-> SymbolTable
forall b c a. (b -> c) -> (a -> b) -> a -> c
. ProgramUnit (Analysis a) -> SymbolTable
forall a. Data a => ProgramUnit (Analysis a) -> SymbolTable
collectSymbols (ProgramUnit (Analysis a) -> SymbolTable)
-> ProgramUnit (Analysis a) -> SymbolTable
forall a b. (a -> b) -> a -> b
$ ProgramUnit (Analysis a)
pu
      mbs1 :: StorageTable
mbs1      = SymbolTable -> StorageTable
allocateMemoryBlocks SymbolTable
symTable1
  in  (SymbolTable
symTable1, StorageTable
mbs1)
        ProgramUnitModel
-> (ProgramUnitModel -> ProgramUnitModel) -> ProgramUnitModel
forall a b. a -> (a -> b) -> b
& [Statement (Analysis a)] -> ProgramUnitModel -> ProgramUnitModel
forall a.
Data a =>
[Statement (Analysis a)] -> ProgramUnitModel -> ProgramUnitModel
processStorageClass [Statement (Analysis a)]
stmts
        ProgramUnitModel
-> (ProgramUnitModel -> ProgramUnitModel) -> ProgramUnitModel
forall a b. a -> (a -> b) -> b
& ProgramUnit (Analysis a) -> ProgramUnitModel -> ProgramUnitModel
forall a.
Data a =>
ProgramUnit (Analysis a) -> ProgramUnitModel -> ProgramUnitModel
processCommon ProgramUnit (Analysis a)
pu
        ProgramUnitModel
-> (ProgramUnitModel -> ProgramUnitModel) -> ProgramUnitModel
forall a b. a -> (a -> b) -> b
& [Statement (Analysis a)] -> ProgramUnitModel -> ProgramUnitModel
forall a.
Data a =>
[Statement (Analysis a)] -> ProgramUnitModel -> ProgramUnitModel
processEquivalence [Statement (Analysis a)]
stmts

-- | Given a 'ProgramFile', generate a 'ProgramFileModel' for each 'ProgramUnit' in
-- the file that contains not only a 'SymbolTable' for the non-intrinsic symbols in
-- the unit, but also a 'Language.Fortran.Vars.Types.StorageTable' that
-- determines the locations that non-constant, non-parameter variables will be allocated
programFileModel :: Data a => ProgramFile (Analysis a) -> ProgramFileModel
programFileModel :: forall a. Data a => ProgramFile (Analysis a) -> ProgramFileModel
programFileModel (ProgramFile MetaInfo
_ [ProgramUnit (Analysis a)]
pus) = (ProgramFileModel -> ProgramUnit (Analysis a) -> ProgramFileModel)
-> ProgramFileModel
-> [ProgramUnit (Analysis a)]
-> ProgramFileModel
forall (t :: * -> *) b a.
Foldable t =>
(b -> a -> b) -> b -> t a -> b
foldl' ProgramFileModel -> ProgramUnit (Analysis a) -> ProgramFileModel
forall {a}.
Data a =>
ProgramFileModel -> ProgramUnit (Analysis a) -> ProgramFileModel
handler ProgramFileModel
forall k a. Map k a
M.empty [ProgramUnit (Analysis a)]
pus
  where handler :: ProgramFileModel -> ProgramUnit (Analysis a) -> ProgramFileModel
handler ProgramFileModel
m ProgramUnit (Analysis a)
pu = ProgramUnitName
-> ProgramUnitModel -> ProgramFileModel -> ProgramFileModel
forall k a. Ord k => k -> a -> Map k a -> Map k a
M.insert (ProgramUnit (Analysis a) -> ProgramUnitName
forall a. ProgramUnit (Analysis a) -> ProgramUnitName
puName ProgramUnit (Analysis a)
pu) (ProgramUnit (Analysis a) -> ProgramUnitModel
forall a. Data a => ProgramUnit (Analysis a) -> ProgramUnitModel
programUnitModel ProgramUnit (Analysis a)
pu) ProgramFileModel
m