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     = forall a. Data a => ProgramUnit a -> [Statement a]
allPUS ProgramUnit (Analysis a)
pu
      symTable1 :: SymbolTable
symTable1 = forall a.
Data a =>
ProgramUnit (Analysis a) -> SymbolTable -> SymbolTable
undefineDummyArguments ProgramUnit (Analysis a)
pu forall b c a. (b -> c) -> (a -> b) -> a -> c
. forall a. Data a => ProgramUnit (Analysis a) -> SymbolTable
collectSymbols forall a b. (a -> b) -> a -> b
$ ProgramUnit (Analysis a)
pu
      mbs1 :: StorageTable
mbs1      = SymbolTable -> StorageTable
allocateMemoryBlocks SymbolTable
symTable1
  in  (SymbolTable
symTable1, StorageTable
mbs1)
        forall a b. a -> (a -> b) -> b
& forall a.
Data a =>
[Statement (Analysis a)] -> ProgramUnitModel -> ProgramUnitModel
processStorageClass [Statement (Analysis a)]
stmts
        forall a b. a -> (a -> b) -> b
& forall a.
Data a =>
ProgramUnit (Analysis a) -> ProgramUnitModel -> ProgramUnitModel
processCommon ProgramUnit (Analysis a)
pu
        forall a b. a -> (a -> b) -> b
& 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) = forall (t :: * -> *) b a.
Foldable t =>
(b -> a -> b) -> b -> t a -> b
foldl' forall {a}.
Data a =>
ProgramFileModel -> ProgramUnit (Analysis a) -> ProgramFileModel
handler 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 = forall k a. Ord k => k -> a -> Map k a -> Map k a
M.insert (forall a. ProgramUnit (Analysis a) -> ProgramUnitName
puName ProgramUnit (Analysis a)
pu) (forall a. Data a => ProgramUnit (Analysis a) -> ProgramUnitModel
programUnitModel ProgramUnit (Analysis a)
pu) ProgramFileModel
m