packages feed

Pugs-6.2.13.20110925: src/Pugs/Class.hs

{-# OPTIONS_GHC -fglasgow-exts -fparr -fallow-undecidable-instances -fallow-incoherent-instances #-}

{-|
    Class meta-model.  (object meta-meta-model)

>   Learn now the lore of Living Creatures!
>   First name the four, the free peoples:
>   Eldest of all, the elf-children;
>   Dwarf the delver, dark are his houses;
>   Ent the earthborn, old as mountains;
>   Man the mortal, master of horses...
-}

module Pugs.Class
    ( module Pugs.Class
    , module Pugs.AST.Eval
    , module MO.Run
    , module MO.Compile
    , module MO.Compile.Class
    , module MO.Util
    , module Control.Monad.Fix
    ) where
import MO.Run hiding (__)
import MO.Compile
import MO.Compile.Class
import MO.Util hiding (traceM, traceShow)
import Pugs.Internals
import Pugs.AST.Eval
import Control.Monad.Fix
import qualified StringTable.AtomMap as AtomMap
import qualified Data.Typeable as Typeable
import Data.Sequence (Seq)
import qualified Data.Sequence as Seq
import qualified Data.Foldable as F

type Val = Invocant Eval
type Call = MethodInvocation Eval

class (Show a, Typeable a, Ord a) => Boxable a where
    mkVal :: a -> Val
    mkVal x = MkInvocant x (class_interface (classOf x))

    coerceVal :: Val -> Eval a
    coerceVal (MkInvocant x _) = case Typeable.cast x of
        Just y -> return y
        _      -> fail $ "Cannot coerce from " ++ (show $ typeOf x) ++ " to " ++ (show $ typeOf (undefined :: a))

    instanceMethods :: [(ID, MethodPrim a)]
    instanceMethods = []

    classOf :: a -> PureClass
    classOf _ = mkPureClass (classNameOf (undefined :: a)) (instanceMethods :: [(ID, MethodPrim a)])

    classNameOf :: a -> String
    classNameOf _ = takeTypeName "" . reverse . show . typeOf $ (undefined :: a)
        -- Here we intuit "Str" from "Pugs.Val.Str.PureStr".
        where
        takeTypeName acc [] = acc
        takeTypeName acc (x:xs)
            | isLower x = takeTypeName (x:acc) xs
            | otherwise = x:acc

type MethodPrim a = (a -> Seq Val -> Eval Val)

class Boxable b => MethodPrimable a b | a -> b where 
    asPrim :: a -> MethodPrim b

instance Boxable a => MethodPrimable Val a where
    asPrim v _ _ = return v

instance Boxable a => MethodPrimable Call a where
    asPrim f x _ = ivDispatch (mkVal x) f

-- Auto-generate pure instances from Eval instances
instance MethodPrimable (a -> b -> Eval z) a => MethodPrimable (a -> b -> z) a where
    asPrim f = asPrim ((\x args -> return (f x args)) :: (a -> b -> Eval z))

instance MethodPrimable (a -> b -> c -> Eval z) a => MethodPrimable (a -> b -> c -> z) a where
    asPrim f = asPrim ((\x y args -> return (f x y args)) :: (a -> b -> c -> Eval z))

instance (Boxable a, Boxable z) => MethodPrimable (a -> z) a where
    asPrim f x _ = return (mkVal (f x))

instance (Boxable a, Boxable z) => MethodPrimable (a -> Eval z) a where
    asPrim f x _ = fmap mkVal (f x)

instance (Boxable a, Boxable z) => MethodPrimable (a -> Val -> Eval z) a where
    asPrim f x args = fmap mkVal (f x (args `Seq.index` 0))

instance (Boxable a, Boxable z) => MethodPrimable (a -> Seq Val -> Eval z) a where
    asPrim f x args = fmap mkVal (f x args)

instance (Boxable a, Boxable z) => MethodPrimable (a -> [Val] -> Eval z) a where
    asPrim f x args = fmap mkVal (f x (cast args))

instance (Boxable a, Boxable b, Boxable z) => MethodPrimable (a -> [b] -> Eval z) a where
    asPrim f x args = do
        args' <- mapM coerceVal (cast args)
        fmap mkVal (f x args')

instance (Boxable a, Boxable b, Boxable z) => MethodPrimable (a -> b -> Eval z) a where
    asPrim f x args = do
        y <- coerceVal (args `Seq.index` 0)
        fmap mkVal (f x y)

instance (Boxable a, Boxable b, Boxable c, Boxable z) => MethodPrimable (a -> b -> c -> Eval z) a where
    asPrim f x args = do
        y <- coerceVal (args `Seq.index` 0)
        z <- coerceVal (args `Seq.index` 1)
        fmap mkVal (f x y z)

(...) :: MethodPrimable a b => String -> a -> (ID, MethodPrim b)
(...) x y = (_cast x, asPrim y)

(!!!) :: Boxable b => String -> (a -> Eval b) -> (ID, a -> Eval Val)
(!!!) x y = (_cast x, mkValM . y)

mkValM :: Boxable a => Eval a -> Eval Val
mkValM x = do
    x' <- x
    return $ MkInvocant x' (class_interface (classOf x'))

mkBoxClass :: Boxable a => String -> [(ID, MethodPrim a)] -> PureClass
mkBoxClass cls methods = newMOClass MkMOClass
    { moc_parents         = []
    , moc_roles           = []
    , moc_attributes      = []
    , moc_public_methods  = newCollection' methodName $ map mkBoxMethod methods
    , moc_private_methods = newCollection []
    , moc_name            = _cast cls
    }



-- | Variant of @mkBoxClass@ making use of the fixed-point combinator
-- to tye in its "self", and, that adds the standard HOW and WHICH methods.
-- mkPureClass :: (Boxable a) => String -> [(ID, MethodPrim a)] -> PureClass
mkPureClass :: Boxable a => String -> [(ID, MethodPrim a)] -> PureClass
mkPureClass cls methods = fix . (mkBoxClass cls .) $ \self -> flip (++) methods
    [ ""        ... mkVal self
    , "ITEM"    ... id
    , "LIST"    ... id
    ]

raiseWhatError :: String -> a
raiseWhatError = error

mkBoxMethod :: forall a. Boxable a => (ID, MethodPrim a) -> AnyMethod Eval
mkBoxMethod (meth, fun) = MkMethod $ MkSimpleMethod
    { sm_name       = meth
    , sm_definition = MkMethodCompiled $ \args -> do
        inv  <- fromInvocant args :: Eval a
        fun inv $ F.foldr mappend mempty (fmap f_positionals (c_feeds args))
    }

type PureClass = MOClass Eval

instance (Show a, Typeable a, Ord a) => Boxable (Maybe a)

instance Boxable a => Boxable [a]
instance Boxable a => Boxable (Seq a)

instance Boxable ID
instance Boxable PureClass where
    classOf _ = _PureClass

_PureClass :: PureClass
_PureClass = mkPureClass "Class"
    [ "methods"     ... ((filter (/= nullID) . map methodName . all_methods) :: PureClass -> [ID])
    ]

instance ((:>:) Call) String where
    cast = (`MkMethodInvocation` CaptSub{ c_feeds = mempty }) . _cast

instance ((:>:) Call) ByteString where
    cast = (`MkMethodInvocation` CaptSub{ c_feeds = mempty }) . cast

instance ((:>:) Call (ByteString, [Val], AtomMap Val)) where
    cast (meth, pos, named) = MkMethodInvocation (cast meth) CaptSub
        { c_feeds = Seq.singleton $ MkFeed (Seq.fromList pos) (AtomMap.map Seq.singleton named)}