module Language.C.Analysis.TravMonad (
MonadName(..),
MonadSymtab(..),
MonadCError(..),
MonadTrav(..),
handleTagDecl, handleTagDef, handleEnumeratorDef, handleTypeDef,
handleObjectDef,handleFunDef,handleVarDecl,handleParamDecl,
handleAsmBlock,
enterPrototypeScope,leavePrototypeScope,
enterFunctionScope,leaveFunctionScope,
enterBlockScope,leaveBlockScope,
lookupTypeDef, lookupObject,
createSUERef,
hadHardErrors,handleTravError,throwOnLeft,
astError, warn,
Trav,
runTrav,runTrav_,
TravState,initTravState,withExtDeclHandler,modifyUserState,userState,
getUserState,
TravOptions(..),modifyOptions,
travErrors,
CLanguage(..),
mapMaybeM,maybeM,mapSndM,concatMapM,
)
where
import Language.C.Data
import Language.C.Data.Ident
import Language.C.Data.RList as RList
import Language.C.Syntax
import Language.C.Analysis.Builtins
import Language.C.Analysis.SemError
import Language.C.Analysis.SemRep
import Language.C.Analysis.DefTable hiding (enterBlockScope,leaveBlockScope,
enterFunctionScope,leaveFunctionScope)
import qualified Language.C.Analysis.DefTable as ST
import Data.IntMap (insert, lookup)
import Data.Maybe
import Control.Applicative (Applicative(..))
import Control.Monad (liftM, ap)
import Prelude hiding (lookup)
class (Monad m) => MonadName m where
genName :: m Name
class (Monad m) => MonadSymtab m where
getDefTable :: m DefTable
withDefTable :: (DefTable -> (a, DefTable)) -> m a
class (Monad m) => MonadCError m where
throwTravError :: Error e => e -> m a
catchTravError :: m a -> (CError -> m a) -> m a
recordError :: Error e => e -> m ()
getErrors :: m [CError]
class (MonadName m, MonadSymtab m, MonadCError m) => MonadTrav m where
handleDecl :: DeclEvent -> m ()
checkRedef :: (MonadCError m, CNode t, CNode t1) => String -> t -> (DeclarationStatus t1) -> m ()
checkRedef subject new_decl redecl_status =
case redecl_status of
NewDecl -> return ()
Redeclared old_def -> throwTravError $
redefinition LevelError subject DuplicateDef (nodeInfo new_decl) (nodeInfo old_def)
KindMismatch old_def -> throwTravError $
redefinition LevelError subject DiffKindRedecl (nodeInfo new_decl) (nodeInfo old_def)
Shadowed _old_def -> return ()
KeepDef _old_def -> return ()
handleTagDecl :: (MonadCError m, MonadSymtab m) => TagFwdDecl -> m ()
handleTagDecl decl = do
redecl <- withDefTable $ declareTag (sueRef decl) decl
checkRedef (show $ sueRef decl) decl redecl
handleTagDef :: (MonadTrav m) => TagDef -> m ()
handleTagDef def = do
redecl <- withDefTable $ defineTag (sueRef def) def
checkRedef (show $ sueRef def) def redecl
handleDecl (TagEvent def)
handleEnumeratorDef :: (MonadCError m, MonadSymtab m) => Enumerator -> m ()
handleEnumeratorDef enumerator = do
let ident = declIdent enumerator
redecl <- withDefTable $ defineScopedIdent ident (EnumeratorDef enumerator)
checkRedef (show ident) ident redecl
return ()
handleTypeDef :: (MonadTrav m) => TypeDef -> m ()
handleTypeDef typeDef@(TypeDef ident _ _ _) = do
redecl <- withDefTable $ defineTypeDef ident typeDef
checkRedef (show ident) typeDef redecl
handleDecl (TypeDefEvent typeDef)
return ()
handleAsmBlock :: (MonadTrav m) => AsmBlock -> m ()
handleAsmBlock asm = handleDecl (AsmEvent asm)
redefErr :: (MonadCError m, CNode old, CNode new) =>
Ident -> ErrorLevel -> new -> old -> RedefKind -> m ()
redefErr name lvl new old kind =
throwTravError $ redefinition lvl (show name) kind (nodeInfo new) (nodeInfo old)
checkIdentTyRedef :: (MonadCError m) => IdentEntry -> (DeclarationStatus IdentEntry) -> m ()
checkIdentTyRedef (Right decl) status = checkVarRedef decl status
checkIdentTyRedef (Left tydef) (KindMismatch old_def) =
redefErr (identOfTypeDef tydef) LevelError tydef old_def DiffKindRedecl
checkIdentTyRedef (Left tydef) (Redeclared old_def) =
redefErr (identOfTypeDef tydef) LevelError tydef old_def DuplicateDef
checkIdentTyRedef (Left _tydef) _ = return ()
checkVarRedef :: (MonadCError m) => IdentDecl -> (DeclarationStatus IdentEntry) -> m ()
checkVarRedef def redecl =
case redecl of
KindMismatch old_def -> redefVarErr old_def DiffKindRedecl
KeepDef (Right old_def) | not (agreeOnLinkage def old_def) -> linkageErr def old_def
| otherwise -> throwOnLeft $ checkCompatibleTypes new_ty (declType old_def)
Redeclared (Right old_def) | not (agreeOnLinkage def old_def) -> linkageErr def old_def
| not(canBeOverwritten old_def) -> redefVarErr old_def DuplicateDef
| otherwise -> throwOnLeft $ checkCompatibleTypes new_ty (declType old_def)
_ -> return ()
where
redefVarErr old_def kind = redefErr (declIdent def) LevelError def old_def kind
linkageErr def old_def =
case (declLinkage def, declLinkage old_def) of
(NoLinkage, _) -> redefErr (declIdent def) LevelError def old_def NoLinkageOld
otherwise -> redefErr (declIdent def) LevelError def old_def DisagreeLinkage
new_ty = declType def
canBeOverwritten (Declaration _) = True
canBeOverwritten (ObjectDef od) = isTentative od
canBeOverwritten _ = False
agreeOnLinkage def old_def
| declStorage old_def == FunLinkage InternalLinkage = True
| not (hasLinkage $ declStorage def) || not (hasLinkage $ declStorage old_def) = False
| (declLinkage def) /= (declLinkage old_def) = False
| otherwise = True
handleVarDecl :: (MonadTrav m) => Bool -> Decl -> m ()
handleVarDecl is_local decl = do
def <- enterDecl decl (const False)
handleDecl ((if is_local then LocalEvent else DeclEvent) def)
handleParamDecl :: (MonadTrav m) => ParamDecl -> m ()
handleParamDecl pd@(AbstractParamDecl _ _) = handleDecl (ParamEvent pd)
handleParamDecl pd@(ParamDecl vardecl node) = do
let def = ObjectDef (ObjDef vardecl Nothing node)
redecl <- withDefTable $ defineScopedIdent (declIdent def) def
checkVarRedef def redecl
handleDecl (ParamEvent pd)
enterDecl :: (MonadCError m, MonadSymtab m) => Decl -> (IdentDecl -> Bool) -> m IdentDecl
enterDecl decl cond = do
let def = Declaration decl
redecl <- withDefTable $
defineScopedIdentWhen cond (declIdent def) def
checkVarRedef def redecl
return def
handleFunDef :: (MonadTrav m) => Ident -> FunDef -> m ()
handleFunDef ident fun_def = do
let def = FunctionDef fun_def
redecl <- withDefTable $
defineScopedIdentWhen isDeclaration ident def
checkVarRedef def redecl
handleDecl (DeclEvent def)
isDeclaration :: IdentDecl -> Bool
isDeclaration (Declaration _) = True
isDeclaration _ = False
checkCompatibleTypes :: Type -> Type -> Either TypeMismatch ()
checkCompatibleTypes _ _ = Right ()
handleObjectDef :: (MonadTrav m) => Bool -> Ident -> ObjDef -> m ()
handleObjectDef local ident obj_def = do
let def = ObjectDef obj_def
redecl <- withDefTable $
defineScopedIdentWhen (\old -> shouldOverride def old) ident def
checkVarRedef def redecl
handleDecl ((if local then LocalEvent else DeclEvent) def)
where
isTentativeDef (ObjectDef object_def) = isTentative object_def
isTentativeDef _ = False
shouldOverride def old | isDeclaration old = True
| not (isTentativeDef def) = True
| isTentativeDef old = True
| otherwise = False
updDefTable :: (MonadSymtab m) => (DefTable -> DefTable) -> m ()
updDefTable f = withDefTable (\st -> ((),f st))
enterPrototypeScope :: (MonadSymtab m) => m ()
enterPrototypeScope = updDefTable (ST.enterBlockScope)
leavePrototypeScope :: (MonadSymtab m) => m ()
leavePrototypeScope = updDefTable (ST.leaveBlockScope)
enterFunctionScope :: (MonadSymtab m) => m ()
enterFunctionScope = updDefTable (ST.enterFunctionScope)
leaveFunctionScope :: (MonadSymtab m) => m ()
leaveFunctionScope = updDefTable (ST.leaveFunctionScope)
enterBlockScope :: (MonadSymtab m) => m ()
enterBlockScope = updDefTable (ST.enterBlockScope)
leaveBlockScope :: (MonadSymtab m) => m ()
leaveBlockScope = updDefTable (ST.leaveBlockScope)
lookupTypeDef :: (MonadCError m, MonadSymtab m) => Ident -> m Type
lookupTypeDef ident =
getDefTable >>= \symt ->
case lookupIdent ident symt of
Nothing ->
astError (nodeInfo ident) $ "unbound typeDef: " ++ identToString ident
Just (Left (TypeDef def_ident ty _ _)) -> addRef ident def_ident >> return ty
Just (Right d) -> astError (nodeInfo ident) (wrongKindErrMsg d)
where
wrongKindErrMsg d = "wrong kind of object: expected typedef but found "++ (objKindDescr d)
++ " (for identifier `" ++ identToString ident ++ "')"
lookupObject :: (MonadCError m, MonadSymtab m) => Ident -> m (Maybe IdentDecl)
lookupObject ident = do
old_decl <- liftM (lookupIdent ident) getDefTable
mapMaybeM old_decl $ \obj ->
case obj of
Right objdef -> addRef ident objdef >> return objdef
Left _tydef -> astError (nodeInfo ident) (mismatchErr "lookupObject" "an object" "a typeDef")
addRef :: (MonadCError m, MonadSymtab m, CNode u, CNode d) => u -> d -> m ()
addRef use def =
case (nodeInfo use, nodeInfo def) of
(NodeInfo _ _ useName, NodeInfo _ _ defName) ->
withDefTable
(\dt ->
((),
dt { refTable = insert (nameId useName) defName (refTable dt) }
)
)
(_, _) -> return ()
mismatchErr :: String -> String -> String -> String
mismatchErr ctx expect found = ctx ++ ": Expected " ++ expect ++ ", but found: " ++ found
createSUERef :: (MonadCError m, MonadSymtab m) => NodeInfo -> Maybe Ident -> m SUERef
createSUERef _node_info (Just ident) = return$ NamedRef ident
createSUERef node_info Nothing | (Just name) <- nameOfNode node_info = return $ AnonymousRef name
| otherwise = astError node_info "struct/union/enum definition without unique name"
handleTravError :: (MonadCError m) => m a -> m (Maybe a)
handleTravError a = liftM Just a `catchTravError` (\e -> recordError e >> return Nothing)
hadHardErrors :: [CError] -> Bool
hadHardErrors = (not . null . filter isHardError)
astError :: (MonadCError m) => NodeInfo -> String -> m a
astError node msg = throwTravError $ invalidAST node msg
throwOnLeft :: (MonadCError m, Error e) => Either e a -> m a
throwOnLeft (Left err) = throwTravError err
throwOnLeft (Right v) = return v
warn :: (Error e, MonadCError m) => e -> m ()
warn err = recordError (changeErrorLevel err LevelWarn)
newtype Trav s a = Trav { unTrav :: TravState s -> Either CError (a, TravState s) }
modify :: (TravState s -> TravState s) -> Trav s ()
modify f = Trav (\s -> Right ((),f s))
gets :: (TravState s -> a) -> Trav s a
gets f = Trav (\s -> Right (f s, s))
get :: Trav s (TravState s)
get = Trav (\s -> Right (s,s))
put :: TravState s -> Trav s ()
put s = Trav (\_ -> Right ((),s))
runTrav :: forall s a. s -> Trav s a -> Either [CError] (a, TravState s)
runTrav state traversal =
case unTrav action (initTravState state) of
Left trav_err -> Left [trav_err]
Right (v, ts) | hadHardErrors (travErrors ts) -> Left (travErrors ts)
| otherwise -> Right (v,ts)
where
action = do withDefTable (const ((), builtins))
traversal
runTrav_ :: Trav () a -> Either [CError] (a,[CError])
runTrav_ t = fmap fst . runTrav () $
do r <- t
es <- getErrors
return (r,es)
withExtDeclHandler :: Trav s a -> (DeclEvent -> Trav s ()) -> Trav s a
withExtDeclHandler action handler =
do modify $ \st -> st { doHandleExtDecl = handler }
action
instance Functor (Trav s) where
fmap = liftM
instance Applicative (Trav s) where
pure = return
(<*>) = ap
instance Monad (Trav s) where
return x = Trav (\s -> Right (x,s))
m >>= k = Trav (\s -> case unTrav m s of
Right (x,s1) -> unTrav (k x) s1
Left e -> Left e)
instance MonadName (Trav s) where
genName = generateName
instance MonadSymtab (Trav s) where
getDefTable = gets symbolTable
withDefTable f = do
ts <- get
let (r,symt') = f (symbolTable ts)
put $ ts { symbolTable = symt' }
return r
instance MonadCError (Trav s) where
throwTravError e = Trav (\_ -> Left (toError e))
catchTravError a handler = Trav (\s -> case unTrav a s of
Left e -> unTrav (handler e) s
Right r -> Right r)
recordError e = modify $ \st -> st { rerrors = (rerrors st) `snoc` toError e }
getErrors = gets (RList.reverse . rerrors)
instance MonadTrav (Trav s) where
handleDecl d = ($ d) =<< gets doHandleExtDecl
data CLanguage = C89 | C99 | GNU89 | GNU99
data TravOptions =
TravOptions {
language :: CLanguage
}
data TravState s =
TravState {
symbolTable :: DefTable,
rerrors :: RList CError,
nameGenerator :: [Name],
doHandleExtDecl :: (DeclEvent -> Trav s ()),
userState :: s,
options :: TravOptions
}
travErrors :: TravState s -> [CError]
travErrors = RList.reverse . rerrors
initTravState :: s -> TravState s
initTravState userst =
TravState {
symbolTable = emptyDefTable,
rerrors = RList.empty,
nameGenerator = newNameSupply,
doHandleExtDecl = const (return ()),
userState = userst,
options = TravOptions { language = C99 }
}
modifyUserState :: (s -> s) -> Trav s ()
modifyUserState f = modify $ \ts -> ts { userState = f (userState ts) }
getUserState :: Trav s s
getUserState = userState `liftM` get
modifyOptions :: (TravOptions -> TravOptions) -> Trav s ()
modifyOptions f = modify $ \ts -> ts { options = f (options ts) }
generateName :: Trav s Name
generateName =
get >>= \ts ->
do let (new_name : gen') = nameGenerator ts
put $ ts { nameGenerator = gen'}
return new_name
mapMaybeM :: (Monad m) => (Maybe a) -> (a -> m b) -> m (Maybe b)
mapMaybeM m f = maybe (return Nothing) (liftM Just . f) m
maybeM :: (Monad m) => (Maybe a) -> (a -> m ()) -> m ()
maybeM m f = maybe (return ()) f m
mapSndM :: (Monad m) => (b -> m c) -> (a,b) -> m (a,c)
mapSndM f (a,b) = liftM ((,) a) (f b)
concatMapM :: (Monad m) => (a -> m [b]) -> [a] -> m [b]
concatMapM f = liftM concat . mapM f