packages feed

hoop-0.3.0.0: src/Language/MSH/StateDecl.hs

--------------------------------------------------------------------------------

module Language.MSH.StateDecl (
    module Language.MSH.MethodTable,

    StateMod(..),
    StateObjCtr(..),
    StateMemberDecl(..),
    StateDecl(..),

    isBaseClass,
    isAbstractClass,
    isFinalClass,
    ctrsForClass,

    declByParent
) where

--------------------------------------------------------------------------------

import Prelude hiding ((<>))
import qualified Data.Map as M

import Language.Haskell.TH
import Language.Haskell.TH.Syntax
import qualified Language.Haskell.TH.Ppr as Ppr

import Language.MSH.MethodTable
import Language.MSH.Pretty

--------------------------------------------------------------------------------

-- | Enumerates class modifiers.
data StateMod = Abstract | Final deriving Show

-- | Enumerates object constructor types.
data StateObjCtr = DataCtr | StartCtr | MiddleCtr | EndCtr deriving Show

-- | Used to represent fields in state classes.
data StateMemberDecl = StateDataDecl {
    -- | The name of the field.
    stateDataName   :: String,
    -- | The (optional) expression which specifies the default value.
    stateDataExpr   :: Maybe String,
    -- | The type of the field.
    stateDataType   :: String
} deriving Show

instance Pretty StateMemberDecl where
    pp (StateDataDecl{..}) =
        text stateDataName <+>
        optionally stateDataExpr (\e -> text "=" <+> text e) <+>
        text "::" <+>
        text stateDataType

-- | Represents a state class definition.
data StateDecl = StateDecl {
    -- | The class modifier, if any.
    stateMod      :: Maybe StateMod,
    -- | The name of the class.
    stateName     :: String,
    -- | The list of type parameters.
    stateParams   :: [String],
    -- | The name of the parent class, if any.
    stateParentN  :: Maybe String,
    -- | The type argument for the parent class.
    stateParentPs :: [String],
    -- | The parent class, once resolved.
    stateParent   :: Maybe StateDecl,
    -- | The field declarations.
    stateData     :: [StateMemberDecl],
    -- | The method signatures and definitions.
    stateBody     :: [Dec],
    -- | The method table for this class, once computed.
    stateMethods  :: MethodTable
}

instance Pretty StateDecl where
    pp (StateDecl{..}) =
        text "CLASS" <+> parens (text stateName) <> colon $$ nest 4 (vcat [
            optionally stateParentN (\p -> text "PARENT:" <+> text p <+> hsep (map text stateParentPs)),
            optionally stateParent (\_ -> text "PARENT RESOLVED"),
            optionally stateMod (\m -> text "MODIFIER:" <+> text (show m)),
            text "PARAMS:" <+> hsep (map text stateParams),
            text "DATA" <> colon $$ nest 4 (vcat (map pp stateData)),
            text "BODY (TH AST)" <> colon $$ nest 4 (vcat (map (text . Ppr.pprint) stateBody)),
            text "METHOD TABLE" <> colon $$ nest 4 (ppMethodTable stateMethods)
        ])

instance Show StateDecl where
    show = render . pp

--------------------------------------------------------------------------------

-- | `isBaseClass` @class determines whether @class is a base class.
isBaseClass :: StateDecl -> Bool
isBaseClass (StateDecl { stateParentN = Nothing } ) = True
isBaseClass _ = False

-- | `isAbstractClass` @class determines whether @class is abstract.
isAbstractClass :: StateDecl -> Bool
isAbstractClass (StateDecl { stateMod = Just Abstract }) = True
isAbstractClass _ = False

-- | `isFinalClass` @class determines whether @class is final.
isFinalClass :: StateDecl -> Bool
isFinalClass (StateDecl { stateMod = Just Final }) = True
isFinalClass _ = False

-- | `declByParent' @name @class determines whether a method named @name
-- was declared by a parent of @class.
declByParent :: Name -> StateDecl -> Bool
declByParent _ (StateDecl { stateParent = Nothing })  = False
declByParent n (StateDecl { stateParent = (Just p) }) =
    M.member (nameBase n) (methods $ stateMethods p) || declByParent n p

-- | `ctrsForClass` @dec returns a list of object states for the state class
--   described by @dec.
ctrsForClass :: StateDecl -> [StateObjCtr]
ctrsForClass (StateDecl { stateParentN = p, stateMod = m }) = case p of
    Nothing -> case m of
        Nothing         -> [DataCtr, StartCtr]
        (Just Abstract) -> [StartCtr]
        (Just Final)    -> [DataCtr]
    (Just _) -> case m of
        Nothing         -> [DataCtr, StartCtr, MiddleCtr, EndCtr]
        (Just Abstract) -> [StartCtr, MiddleCtr]
        (Just Final)    -> [DataCtr, EndCtr]