fortran-vars-0.3.0: src/Language/Fortran/Vars/StructureTable.hs
{-# LANGUAGE ScopedTypeVariables #-}
module Language.Fortran.Vars.StructureTable
( StructureTable
, StructureTableEntry(..)
, collectStructures
, lookupField
, hasEntry
, programStructureTables
)
where
import Data.Data ( Data )
import Data.Maybe ( mapMaybe )
import qualified Data.Map as M
import Data.List ( foldl' )
import Language.Fortran.Analysis ( Analysis
, puName
)
import Language.Fortran.AST ( Statement(..)
, StructureItem(..)
, UnionMap(..)
, Expression(..)
, Value(..)
, ProgramUnit(..)
, ProgramFile
, TypeSpec(..)
, Declarator(..)
, DeclaratorType(..)
, aStrip
)
import Language.Fortran.Extras
( allPUS
, allPU
)
import Language.Fortran.Vars.SymbolTable
( collectSymbols )
import Language.Fortran.Vars.Types
( SymbolTable
, StructureTableEntry(..)
, Structure
, StructureTable
, ProgramStructureTables
, Type(..)
, SemType(..)
, TypeError(..)
)
import Language.Fortran.Vars.Utils
( typeSpecToArrayType
, typeSpecToScalarType
)
-- | Given a `SymbolTable` transform `StructureItem`s found in the AST into a list of
-- `StructureTableEntry`s
itemToEntry
:: SymbolTable -> StructureItem (Analysis a) -> [StructureTableEntry]
itemToEntry st (StructFields _ _ ty _ decls) =
mapMaybe (handleDeclarator st ty) (aStrip decls)
itemToEntry st (StructUnion _ _ l) = [UnionEntry $ handleUnion st <$> aStrip l]
itemToEntry _ StructStructure{} = []
-- TODO take into account length, should override default typespecs
-- | Given the 'TypeSpec' and 'Declarator' found in a field entry create a
-- 'StructureTableEntry'
handleDeclarator
:: SymbolTable
-> TypeSpec (Analysis a)
-> Declarator (Analysis a)
-> Maybe StructureTableEntry
handleDeclarator st ty (Declarator _ _ expr ScalarDecl _ _) =
let scalarTy = typeSpecToScalarType st ty
in expToName expr >>= \name -> Just $ FieldEntry name scalarTy
handleDeclarator st ty (Declarator _ _ expr (ArrayDecl dims) _ _) =
let arrayty = typeSpecToArrayType st (aStrip dims) ty
in expToName expr >>= \name -> Just $ FieldEntry name arrayty
-- | Transform a `UnionMap` in an AST to `StructureTableEntry`s
handleUnion :: SymbolTable -> UnionMap (Analysis a) -> [StructureTableEntry]
handleUnion st (UnionMap _ _ si) = concatMap (itemToEntry st) $ aStrip si
-- | Given an `Expression` maybe get the name
expToName :: Expression a -> Maybe String
expToName (ExpValue _ _ (ValVariable name)) = Just name
expToName _ = Nothing
-- | Collect structures defined in a `ProgramUnit` and return a `StructureTable`
collectStructures
:: Data a => SymbolTable -> ProgramUnit (Analysis a) -> StructureTable
collectStructures st pu =
M.foldlWithKey' handler M.empty
. M.fromList
$ [ (n, aStrip s) | (StStructure _ _ (Just n) s) <- allPUS pu ]
where
handler structTable name entry =
M.insert name (concatMap (itemToEntry st) entry) structTable
-- | Look up reference on a type to return another `Type`
lookupField :: StructureTable -> Type -> String -> Either TypeError Type
lookupField structTable ty ref = case ty of
TCustom tyName -> case M.lookup tyName structTable of
Just struct -> hasEntry ref struct
Nothing -> Left . UnknownField $ ref <> " not a field of " <> tyName
ty' -> Left . UnknownField $ "No fields for data type (" <> show ty' <> ")"
-- | Given a name, check that a `Structure` contains it once and return its
-- corresponding `Type`
hasEntry :: String -> Structure -> Either TypeError Type
hasEntry name struct =
let unionStructs = concat [ concat structs | (UnionEntry structs) <- struct ]
in
case
[ ty | (FieldEntry fname ty) <- struct <> unionStructs, fname == name ]
of
[ty] -> Right ty
[] -> Left . UnknownField $ name <> " is not a field"
_ ->
Left
. UnknownField
$ "Field "
<> name
<> " define multiple times for structure"
-- | Given a 'ProgramFile', generate a 'StructureTable' for each 'ProgramUnit'.
-- This can be used to check types in data reference expressions
programStructureTables
:: Data a => ProgramFile (Analysis a) -> ProgramStructureTables
programStructureTables pf = foldl' handler M.empty $ allPU pf
where
handler m pu = M.insert (puName pu) (puStructure pu) m
puStructure pu = let st = collectSymbols pu in collectStructures st pu