fortran-vars-0.1.0: src/Language/Fortran/Vars/Union.hs
module Language.Fortran.Vars.Union
( union
)
where
import qualified Data.Map as M
import Language.Fortran.AST ( Name )
import Language.Fortran.Vars.Types
( SymbolTableEntry(..)
, Location
, MemoryBlock(..)
, MemoryBlockName
, ProgramUnitModel
, StorageClass(..)
, SymbolTable
)
updateVal :: MemoryBlockName -> Int -> Name -> SymbolTable -> SymbolTable
updateVal blockName diff symbol symt = case M.lookup symbol symt of
Just (SVariable ty (_, offset)) ->
let entry = SVariable ty (blockName, offset + diff)
in M.insert symbol entry symt
Just _ -> error (symbol ++ "is not a variable.")
Nothing -> symt
mergeStClass :: StorageClass -> StorageClass -> StorageClass
mergeStClass Unspecified c2 = c2
mergeStClass c1 Unspecified = c1
mergeStClass c1 c2 | c1 == c2 = c1
mergeStClass c1 c2 =
error ("Try to merge StorageClass " ++ show c1 ++ " with " ++ show c2)
-- | Given a 'ProgramUnitModel' and two different 'Location's,
-- produce a new 'ProgramUnitModel' and 'Location' that represents
-- the union of the inputs
union
:: ProgramUnitModel -> Location -> Location -> (ProgramUnitModel, Location)
union puModel loc1 loc2 | loc1 == loc2 = (puModel, loc1)
union (symTable, storageTable) location1 location2 =
let (blockName1, offset1) = location1
(blockName2, offset2) = location2
Just block1 = case M.lookup blockName1 storageTable of
Just block -> Just block
Nothing -> error ("Block doesn't exist: " ++ blockName1)
Just block2 = case M.lookup blockName2 storageTable of
Just block -> Just block
Nothing -> error ("Block doesn't exist: " ++ blockName2)
in case compare offset1 offset2 of
Prelude.GT -> mergeTo location1 location2
Prelude.LT -> mergeTo location2 location1
Prelude.EQ -> case (storageClass block1, storageClass block2) of
(Common, _ ) -> mergeTo location1 location2
(_ , Common) -> mergeTo location2 location1
_ -> if blockSize block1 >= blockSize block2
then mergeTo location1 location2
else mergeTo location2 location1
where
mergeTo :: Location -> Location -> (ProgramUnitModel, Location)
mergeTo toLocation@(toBlockName, toOffset) (fromBlockName, fromOffset) =
let
diff = toOffset - fromOffset
Just fromBlock = M.lookup fromBlockName storageTable
Just toBlock = M.lookup toBlockName storageTable
-- update toBlock variables by appending the variables of fromBlock
newVarList = variables toBlock ++ variables fromBlock
-- update toBlock size
newSize = do
to <- blockSize toBlock
from <- blockSize fromBlock
pure $ max to (from + diff)
newStClass = mergeStClass (storageClass fromBlock) (storageClass toBlock)
newBlock = MemoryBlock { blockSize = newSize
, storageClass = newStClass
, variables = newVarList
}
mbs = M.insert toBlockName newBlock storageTable
-- remove the fromBlock
mbs' = M.delete fromBlockName mbs
-- update the symbolTable for variables of fromBlock with
-- toBlock name and updated offset
symTable' =
foldr (updateVal toBlockName diff) symTable (variables fromBlock)
in
((symTable', mbs'), toLocation)