module Language.C.Analysis.DefTable (
IdentEntry, identOfTyDecl,
TagEntry, TagFwdDecl(..),
DefTable(..),
emptyDefTable,
globalDefs,
inFileScope,
enterFunctionScope,leaveFunctionScope,enterBlockScope,leaveBlockScope,
enterMemberDecl,leaveMemberDecl,
DeclarationStatus(..),declStatusDescr,
defineTypeDef, defineGlobalIdent, defineScopedIdent, defineScopedIdentWhen,
declareTag,defineTag,defineLabel,lookupIdent,
lookupTag,lookupLabel,lookupIdentInner,lookupTagInner,
insertType, lookupType,
mergeDefTable
)
where
import Language.C.Data
import Language.C.Analysis.NameSpaceMap
import Language.C.Analysis.SemRep
import Control.Applicative ((<|>))
import Data.Map (Map)
import qualified Data.Map as Map
import Data.IntMap (IntMap, union)
import qualified Data.IntMap as IntMap
import Data.Generics
type IdentEntry = Either TypeDef IdentDecl
identOfTyDecl :: IdentEntry -> Ident
identOfTyDecl = either identOfTypeDef declIdent
data TagFwdDecl = CompDecl CompTypeRef
| EnumDecl EnumTypeRef
instance HasSUERef TagFwdDecl where
sueRef (CompDecl ctr) = sueRef ctr
sueRef (EnumDecl etr) = sueRef etr
instance CNode TagFwdDecl where
nodeInfo (CompDecl ctr) = nodeInfo ctr
nodeInfo (EnumDecl etr) = nodeInfo etr
type TagEntry = Either TagFwdDecl TagDef
data DefTable = DefTable
{
identDecls :: NameSpaceMap Ident IdentEntry,
tagDecls :: NameSpaceMap SUERef TagEntry,
labelDefs :: NameSpaceMap Ident Ident,
memberDecls :: NameSpaceMap Ident MemberDecl,
refTable :: IntMap Name,
typeTable :: IntMap Type
}
emptyDefTable :: DefTable
emptyDefTable = DefTable nameSpaceMap nameSpaceMap nameSpaceMap nameSpaceMap IntMap.empty IntMap.empty
globalDefs :: DefTable -> GlobalDecls
globalDefs deftbl = Map.foldWithKey insertDecl (GlobalDecls e gtags e) (globalNames $ identDecls deftbl)
where
e = Map.empty
(_fwd_decls,gtags) = Map.mapEither id $ globalNames (tagDecls deftbl)
insertDecl ident (Left tydef) ds = ds { gTypeDefs = Map.insert ident tydef (gTypeDefs ds)}
insertDecl ident (Right obj) ds = ds { gObjs = Map.insert ident obj (gObjs ds) }
inFileScope :: DefTable -> Bool
inFileScope dt = not (hasLocalNames (identDecls dt) || hasLocalNames (labelDefs dt))
leaveScope_ :: (Ord k) => NameSpaceMap k a -> NameSpaceMap k a
leaveScope_ = fst . leaveScope
enterLocalScope :: DefTable -> DefTable
enterLocalScope deftbl = deftbl {
identDecls = enterNewScope (identDecls deftbl),
tagDecls = enterNewScope (tagDecls deftbl)
}
leaveLocalScope :: DefTable -> DefTable
leaveLocalScope deftbl = deftbl {
identDecls = leaveScope_ (identDecls deftbl),
tagDecls = leaveScope_ (tagDecls deftbl)
}
enterFunctionScope :: DefTable -> DefTable
enterFunctionScope deftbl = enterLocalScope $ deftbl { labelDefs = enterNewScope (labelDefs deftbl) }
leaveFunctionScope :: DefTable -> DefTable
leaveFunctionScope deftbl = leaveLocalScope $ deftbl { labelDefs = leaveScope_ (labelDefs deftbl) }
enterBlockScope :: DefTable -> DefTable
enterBlockScope deftbl = enterLocalScope $ deftbl { labelDefs = enterNewScope (labelDefs deftbl) }
leaveBlockScope :: DefTable -> DefTable
leaveBlockScope deftbl = leaveLocalScope $ deftbl { labelDefs = leaveScope_ (labelDefs deftbl) }
enterMemberDecl :: DefTable -> DefTable
enterMemberDecl deftbl = deftbl { memberDecls = enterNewScope (memberDecls deftbl) }
leaveMemberDecl :: DefTable -> ([MemberDecl], DefTable)
leaveMemberDecl deftbl =
let (decls',members) = leaveScope (memberDecls deftbl)
in (,) (map snd members)
(deftbl { memberDecls = decls' })
data DeclarationStatus t =
NewDecl
| Redeclared t
| KeepDef t
| Shadowed t
| KindMismatch t
deriving (Data,Typeable)
declStatusDescr :: DeclarationStatus t -> String
declStatusDescr NewDecl = "new"
declStatusDescr (Redeclared _) = "redeclared"
declStatusDescr (KeepDef _) = "keep old"
declStatusDescr (Shadowed _) = "shadowed"
declStatusDescr (KindMismatch _) = "kind mismatch"
compatIdentEntry :: IdentEntry -> IdentEntry -> Bool
compatIdentEntry (Left _tydef) = either (const True) (const False)
compatIdentEntry (Right def) = either (const False) $
\other_def -> case (def,other_def) of
(EnumeratorDef _, EnumeratorDef _) -> True
(EnumeratorDef _, _) -> True
(_, EnumeratorDef _) -> True
(_,_) -> True
data TagEntryKind = CompKind CompTyKind | EnumKind
deriving (Eq,Ord)
instance Show TagEntryKind where
show (CompKind ctk) = show ctk
show EnumKind = "enum"
tagKind :: TagEntry -> TagEntryKind
tagKind (Left (CompDecl cd)) = CompKind (compTag cd)
tagKind (Left (EnumDecl _)) = EnumKind
tagKind (Right (CompDef cd)) = CompKind (compTag cd)
tagKind (Right (EnumDef _)) = EnumKind
compatTagEntry :: TagEntry -> TagEntry -> Bool
compatTagEntry te1 te2 = tagKind te1 == tagKind te2
defRedeclStatus :: (t -> t -> Bool) -> t -> Maybe t -> DeclarationStatus t
defRedeclStatus sameKind def oldDecl =
case oldDecl of
Just def' | def `sameKind` def' -> Redeclared def'
| otherwise -> KindMismatch def'
Nothing -> NewDecl
defRedeclStatusLocal :: (Ord k) =>
(t -> t -> Bool) -> k -> t -> Maybe t -> NameSpaceMap k t -> DeclarationStatus t
defRedeclStatusLocal sameKind ident def oldDecl nsm =
case defRedeclStatus sameKind def oldDecl of
NewDecl -> case lookupName nsm ident of
Just shadowed -> Shadowed shadowed
Nothing -> NewDecl
redecl -> redecl
defineTypeDef :: Ident -> TypeDef -> DefTable -> (DeclarationStatus IdentEntry, DefTable)
defineTypeDef ident tydef deftbl =
(defRedeclStatus compatIdentEntry (Left tydef) oldDecl, deftbl { identDecls = decls' })
where
(decls', oldDecl) = defLocal (identDecls deftbl) ident (Left tydef)
defineGlobalIdent :: Ident -> IdentDecl -> DefTable -> (DeclarationStatus IdentEntry, DefTable)
defineGlobalIdent ident def deftbl =
(defRedeclStatus compatIdentEntry (Right def) oldDecl, deftbl { identDecls = decls' })
where
(decls',oldDecl) = defGlobal (identDecls deftbl) ident (Right def)
defineScopedIdent :: Ident -> IdentDecl -> DefTable -> (DeclarationStatus IdentEntry, DefTable)
defineScopedIdent = defineScopedIdentWhen (const True)
defineScopedIdentWhen :: (IdentDecl -> Bool) -> Ident -> IdentDecl -> DefTable ->
(DeclarationStatus IdentEntry, DefTable)
defineScopedIdentWhen override_def ident def deftbl
= (redecl_status, deftbl { identDecls = decls' })
where
new_def = Right def
old_decls = identDecls deftbl
old_decl_opt = lookupInnermostScope old_decls ident
(decls',redecl_status) | (Just old_decl) <- old_decl_opt, not (old_decl `compatIdentEntry` new_def)
= (new_decls, KindMismatch old_decl)
| maybe True doOverride old_decl_opt
= (new_decls, redeclStatus' old_decl_opt)
| otherwise
= (old_decls, maybe NewDecl KeepDef old_decl_opt)
new_decls = fst (defLocal old_decls ident new_def)
doOverride (Left _) = False
doOverride (Right old_def) = (override_def old_def)
redeclStatus' overriden_decl = defRedeclStatusLocal compatIdentEntry ident new_def overriden_decl old_decls
declareTag :: SUERef -> TagFwdDecl -> DefTable -> (DeclarationStatus TagEntry, DefTable)
declareTag sueref decl deftbl =
case lookupTag sueref deftbl of
Nothing -> (NewDecl, deftbl { tagDecls = fst $ defLocal (tagDecls deftbl) sueref (Left decl) })
Just old_def | tagKind old_def == tagKind (Left decl) -> (KeepDef old_def, deftbl)
| otherwise -> (KindMismatch old_def, deftbl)
defineTag :: SUERef -> TagDef -> DefTable -> (DeclarationStatus TagEntry, DefTable)
defineTag sueref def deftbl =
(redeclStatus, deftbl { tagDecls = decls'})
where
(decls',olddecl) = defLocal (tagDecls deftbl) sueref (Right def)
redeclStatus =
case olddecl of
Just fwd_decl@(Left decl) | tagKind fwd_decl == tagKind (Right def) -> NewDecl
| otherwise -> KindMismatch fwd_decl
_ -> defRedeclStatusLocal compatTagEntry sueref (Right def) olddecl (tagDecls deftbl)
defineLabel :: Ident -> DefTable -> (DeclarationStatus Ident, DefTable)
defineLabel ident deftbl =
let (labels',old_label) = defLocal (labelDefs deftbl) ident ident
in (maybe NewDecl Redeclared old_label, deftbl { labelDefs = labels' })
lookupIdent :: Ident -> DefTable -> Maybe IdentEntry
lookupIdent ident deftbl = lookupName (identDecls deftbl) ident
lookupTag :: SUERef -> DefTable -> Maybe TagEntry
lookupTag sue_ref deftbl = lookupName (tagDecls deftbl) sue_ref
lookupLabel :: Ident -> DefTable -> Maybe Ident
lookupLabel ident deftbl = lookupName (labelDefs deftbl) ident
lookupIdentInner :: Ident -> DefTable -> Maybe IdentEntry
lookupIdentInner ident deftbl = lookupInnermostScope (identDecls deftbl) ident
lookupTagInner :: SUERef -> DefTable -> Maybe TagEntry
lookupTagInner sue_ref deftbl = lookupInnermostScope (tagDecls deftbl) sue_ref
insertType :: DefTable -> Name -> Type -> DefTable
insertType dt n t = dt { typeTable = IntMap.insert (nameId n) t (typeTable dt) }
lookupType :: DefTable -> Name -> Maybe Type
lookupType dt n = IntMap.lookup (nameId n) (typeTable dt)
mergeDefTable :: DefTable -> DefTable -> DefTable
mergeDefTable (DefTable i1 t1 l1 m1 r1 tt1) (DefTable i2 t2 l2 m2 r2 tt2) =
DefTable
(mergeNameSpace i1 i2)
(mergeNameSpace t1 t2)
(mergeNameSpace l1 l2)
(mergeNameSpace m1 m2)
(union r1 r2)
(union tt1 tt2)