packages feed

fortran-vars-0.1.0: src/Language/Fortran/Vars/MemoryLocation.hs

module Language.Fortran.Vars.MemoryLocation
  ( getLocation
  , generateLinearizedIndexRange
  , getStartLocation
  )
where

import           Data.Data                      ( Data )
import           Data.List                      ( foldl' )
import qualified Data.Map                      as M
import           Language.Fortran.Analysis      ( Analysis
                                                , srcName
                                                )
import           Language.Fortran.AST           ( AList(..)
                                                , Expression(..)
                                                , Index(..)
                                                , Name
                                                , Value(..)
                                                )

import           Language.Fortran.Vars.Eval
                                                ( eval )
import           Language.Fortran.Vars.Kind
                                                ( toInt
                                                , getTypeKind
                                                )
import           Language.Fortran.Vars.Range
                                                ( Range )
import           Language.Fortran.Vars.Types
                                                ( SymbolTableEntry(..)
                                                , Type(..)
                                                , SemType(..)
                                                , Location
                                                , Offset
                                                , SymbolTable
                                                )


isIxSingle :: Index a -> Bool
isIxSingle IxSingle{} = True
isIxSingle IxRange{}  = False

linearizedIndex :: [Int] -> [(Int, Int)] -> Int
linearizedIndex indices dimensions =
  -- The normalized index starts at 0
  let normalizedIndices =
          zipWith (curry (\(i, (lower, _)) -> i - lower)) indices dimensions
      dimSizes    = map (\(lower, upper) -> upper - lower + 1) dimensions
      leadingDims = calcLeadingDimensions dimSizes
  in  foldl' (\acc (i, ld) -> acc + i * ld)
             0
             (zip normalizedIndices leadingDims)
 where
  calcLeadingDimensions :: [Int] -> [Int]
  calcLeadingDimensions []       = []
  calcLeadingDimensions dimSizes = calc [1] dimSizes
   where
    calc _   []      = []
    calc acc [_    ] = reverse acc
    calc acc (h : t) = calc ((head acc * h) : acc) t

-- | Given only single indices return the 'Range' in memory that
-- these indices point to.
generateLinearizedIndexRange :: [Int] -> Int -> [(Int, Int)] -> Int -> Range
generateLinearizedIndexRange intIndices start dims kind =
  let offset = linearizedIndex intIndices dims * kind
  in  (start + offset, start + offset + kind - 1)

findBlockOffset :: SymbolTable -> Name -> Offset -> Location
findBlockOffset symTable symbol offset = case M.lookup symbol symTable of
  Just (SVariable _ (blockName, start)) -> (blockName, start + offset)
  Just entry -> error $ symbol ++ " is not a variable - " ++ show entry
  Nothing ->
    error
      $  "Unable to find location for symbol "
      ++ symbol
      ++ " at offset "
      ++ show offset

calculateOffset :: Data a => SymbolTable -> Name -> [Index (Analysis a)] -> Int
-- array index c(2,4)
calculateOffset symTable symbol indices@(IxSingle{} : _) =
  let Just entry = M.lookup symbol symTable
  in  case entry of
        SVariable (TArray ty (Just dims)) _ ->
          let ixSingles    = takeWhile isIxSingle indices
              Just kind    = getTypeKind ty
              arrayIndices = map toIndices ixSingles
                 where
                  toIndices (IxSingle _ _ _ expr) = toInt $ eval symTable expr
                  toIndices _ = error "toIndices: unexpected input"
          in  linearizedIndex arrayIndices dims * kind
        _ ->
          error "Only array-typed VariableEntries are expected at this point"
-- substring c(:5)
calculateOffset _ _ (IxRange _ _ Nothing _ _ : _) = 0
-- substring c(5:)
calculateOffset symTable _ (IxRange _ _ (Just lowerIndex) _ _ : _) =
  toInt (eval symTable lowerIndex) - 1
calculateOffset _ _ _ = error "calculateOffset: invalid index"

-- | Given a 'SymbolTable' and some 'Expression' (which is assumed to have been predetermined
-- to be of some variable type), return the 'Location' that the variable in question will be
-- located in memory
getLocation :: Data a => SymbolTable -> Expression (Analysis a) -> Location
-- variable
getLocation symTable e@(ExpValue _ _ (ValVariable _)) =
  findBlockOffset symTable (srcName e) 0
-- array index c(2,4)
-- substring c(5:10)
getLocation symTable (ExpSubscript _ _ e@ExpValue{} (AList _ _ indices)) =
  let symbol = srcName e
      offset = calculateOffset symTable symbol indices
  in  findBlockOffset symTable symbol offset
-- array index and substring c(2,4)(1:20)
getLocation symTable (ExpSubscript _ _ (ExpSubscript _ _ e@ExpValue{} (AList _ _ indices)) (AList _ _ subs))
  = let symbol = srcName e
        offset =
            calculateOffset symTable symbol indices
              + calculateOffset symTable symbol subs
    in  findBlockOffset symTable symbol offset
-- array within common block with dimensions declaration: common /block/ a, b(10)
getLocation symTable (ExpFunctionCall _ _ e@ExpValue{} _) =
  findBlockOffset symTable (srcName e) 0
getLocation _ _ = error "getLocation : Not a variable expression"

-- | Given a 'SymbolTable' and some 'Expression' (which is assumed to have been
-- predetermined to be of some variable type), return the start 'Location' that
-- the variable in question will be located in memory.
-- Start 'Location' is the begining of greater data structure that a variable
-- belongs to e.g. start 'Location' of c(20) is 'Location' of c
getStartLocation :: Data a => SymbolTable -> Expression (Analysis a) -> Location
-- variable
getStartLocation symTable e@(ExpValue _ _ (ValVariable _)) =
  findBlockOffset symTable (srcName e) 0
-- dimensions specification within COMMON c(10,20)
getStartLocation symTable (ExpSubscript _ _ e@ExpValue{} _) =
  findBlockOffset symTable (srcName e) 0
-- dimensions specification within COMMON c(10,20)
--   (vars with nonstandard kind declared after COMMON block)
getStartLocation symTable (ExpFunctionCall _ _ e@ExpValue{} _) =
  findBlockOffset symTable (srcName e) 0
getStartLocation _ _ = error "getStartLocation : Not a variable expression"