| 1 | {-# OPTIONS_GHC -fglasgow-exts -fparr -fallow-undecidable-instances -fallow-incoherent-instances #-} |
|---|
| 2 | |
|---|
| 3 | {-| |
|---|
| 4 | Class meta-model. (object meta-meta-model) |
|---|
| 5 | |
|---|
| 6 | > Learn now the lore of Living Creatures! |
|---|
| 7 | > First name the four, the free peoples: |
|---|
| 8 | > Eldest of all, the elf-children; |
|---|
| 9 | > Dwarf the delver, dark are his houses; |
|---|
| 10 | > Ent the earthborn, old as mountains; |
|---|
| 11 | > Man the mortal, master of horses... |
|---|
| 12 | -} |
|---|
| 13 | |
|---|
| 14 | module Pugs.Class |
|---|
| 15 | ( module Pugs.Class |
|---|
| 16 | , module Pugs.AST.Eval |
|---|
| 17 | , module MO.Run |
|---|
| 18 | , module MO.Compile |
|---|
| 19 | , module MO.Compile.Class |
|---|
| 20 | , module MO.Util |
|---|
| 21 | , module Control.Monad.Fix |
|---|
| 22 | ) where |
|---|
| 23 | import MO.Run hiding (__) |
|---|
| 24 | import MO.Compile |
|---|
| 25 | import MO.Compile.Class |
|---|
| 26 | import MO.Util hiding (traceM, traceShow) |
|---|
| 27 | import Pugs.Internals |
|---|
| 28 | import Pugs.AST.Eval |
|---|
| 29 | import Control.Monad.Fix |
|---|
| 30 | import qualified StringTable.AtomMap as AtomMap |
|---|
| 31 | import qualified Data.Typeable as Typeable |
|---|
| 32 | |
|---|
| 33 | type Val = Invocant Eval |
|---|
| 34 | type Call = MethodInvocation Eval |
|---|
| 35 | |
|---|
| 36 | class (Show a, Typeable a, Ord a) => Boxable a where |
|---|
| 37 | mkVal :: a -> Val |
|---|
| 38 | mkVal x = MkInvocant x (class_interface (classOf x)) |
|---|
| 39 | |
|---|
| 40 | coerceVal :: Val -> Eval a |
|---|
| 41 | coerceVal (MkInvocant x _) = case Typeable.cast x of |
|---|
| 42 | Just y -> return y |
|---|
| 43 | _ -> fail $ "Cannot coerce from " ++ (show $ typeOf x) ++ " to " ++ (show $ typeOf (undefined :: a)) |
|---|
| 44 | |
|---|
| 45 | instanceMethods :: [(ID, MethodPrim a)] |
|---|
| 46 | instanceMethods = [] |
|---|
| 47 | |
|---|
| 48 | classOf :: a -> PureClass |
|---|
| 49 | classOf _ = mkPureClass (classNameOf (undefined :: a)) (instanceMethods :: [(ID, MethodPrim a)]) |
|---|
| 50 | |
|---|
| 51 | classNameOf :: a -> String |
|---|
| 52 | classNameOf _ = takeTypeName "" . reverse . show . typeOf $ (undefined :: a) |
|---|
| 53 | -- Here we intuit "Str" from "Pugs.Val.Str.PureStr". |
|---|
| 54 | where |
|---|
| 55 | takeTypeName acc [] = acc |
|---|
| 56 | takeTypeName acc (x:xs) |
|---|
| 57 | | isLower x = takeTypeName (x:acc) xs |
|---|
| 58 | | otherwise = x:acc |
|---|
| 59 | |
|---|
| 60 | type MethodPrim a = (a -> [:Val:] -> Eval Val) |
|---|
| 61 | |
|---|
| 62 | class Boxable b => MethodPrimable a b | a -> b where |
|---|
| 63 | asPrim :: a -> MethodPrim b |
|---|
| 64 | |
|---|
| 65 | instance Boxable a => MethodPrimable Val a where |
|---|
| 66 | asPrim v _ _ = return v |
|---|
| 67 | |
|---|
| 68 | instance Boxable a => MethodPrimable Call a where |
|---|
| 69 | asPrim f x _ = ivDispatch (mkVal x) f |
|---|
| 70 | |
|---|
| 71 | -- Auto-generate pure instances from Eval instances |
|---|
| 72 | instance MethodPrimable (a -> b -> Eval z) a => MethodPrimable (a -> b -> z) a where |
|---|
| 73 | asPrim f = asPrim ((\x args -> return (f x args)) :: (a -> b -> Eval z)) |
|---|
| 74 | |
|---|
| 75 | instance MethodPrimable (a -> b -> c -> Eval z) a => MethodPrimable (a -> b -> c -> z) a where |
|---|
| 76 | asPrim f = asPrim ((\x y args -> return (f x y args)) :: (a -> b -> c -> Eval z)) |
|---|
| 77 | |
|---|
| 78 | instance (Boxable a, Boxable z) => MethodPrimable (a -> z) a where |
|---|
| 79 | asPrim f x _ = return (mkVal (f x)) |
|---|
| 80 | |
|---|
| 81 | instance (Boxable a, Boxable z) => MethodPrimable (a -> Eval z) a where |
|---|
| 82 | asPrim f x _ = fmap mkVal (f x) |
|---|
| 83 | |
|---|
| 84 | instance (Boxable a, Boxable z) => MethodPrimable (a -> Val -> Eval z) a where |
|---|
| 85 | asPrim f x args = fmap mkVal (f x (args !: 0)) |
|---|
| 86 | |
|---|
| 87 | instance (Boxable a, Boxable z) => MethodPrimable (a -> [:Val:] -> Eval z) a where |
|---|
| 88 | asPrim f x args = fmap mkVal (f x args) |
|---|
| 89 | |
|---|
| 90 | instance (Boxable a, Boxable z) => MethodPrimable (a -> [Val] -> Eval z) a where |
|---|
| 91 | asPrim f x args = fmap mkVal (f x (cast args)) |
|---|
| 92 | |
|---|
| 93 | instance (Boxable a, Boxable b, Boxable z) => MethodPrimable (a -> [b] -> Eval z) a where |
|---|
| 94 | asPrim f x args = do |
|---|
| 95 | args' <- mapM coerceVal (cast args) |
|---|
| 96 | fmap mkVal (f x args') |
|---|
| 97 | |
|---|
| 98 | instance (Boxable a, Boxable b, Boxable z) => MethodPrimable (a -> b -> Eval z) a where |
|---|
| 99 | asPrim f x args = do |
|---|
| 100 | y <- coerceVal (args !: 0) |
|---|
| 101 | fmap mkVal (f x y) |
|---|
| 102 | |
|---|
| 103 | instance (Boxable a, Boxable b, Boxable c, Boxable z) => MethodPrimable (a -> b -> c -> Eval z) a where |
|---|
| 104 | asPrim f x args = do |
|---|
| 105 | y <- coerceVal (args !: 0) |
|---|
| 106 | z <- coerceVal (args !: 1) |
|---|
| 107 | fmap mkVal (f x y z) |
|---|
| 108 | |
|---|
| 109 | (...) :: MethodPrimable a b => String -> a -> (ID, MethodPrim b) |
|---|
| 110 | (...) x y = (_cast x, asPrim y) |
|---|
| 111 | |
|---|
| 112 | (!!!) :: Boxable b => String -> (a -> Eval b) -> (ID, a -> Eval Val) |
|---|
| 113 | (!!!) x y = (_cast x, mkValM . y) |
|---|
| 114 | |
|---|
| 115 | mkValM :: Boxable a => Eval a -> Eval Val |
|---|
| 116 | mkValM x = do |
|---|
| 117 | x' <- x |
|---|
| 118 | return $ MkInvocant x' (class_interface (classOf x')) |
|---|
| 119 | |
|---|
| 120 | mkBoxClass :: Boxable a => String -> [(ID, MethodPrim a)] -> PureClass |
|---|
| 121 | mkBoxClass cls methods = newMOClass MkMOClass |
|---|
| 122 | { moc_parents = [] |
|---|
| 123 | , moc_roles = [] |
|---|
| 124 | , moc_attributes = [] |
|---|
| 125 | , moc_public_methods = newCollection' methodName $ map mkBoxMethod methods |
|---|
| 126 | , moc_private_methods = newCollection [] |
|---|
| 127 | , moc_name = _cast cls |
|---|
| 128 | } |
|---|
| 129 | |
|---|
| 130 | |
|---|
| 131 | |
|---|
| 132 | -- | Variant of @mkBoxClass@ making use of the fixed-point combinator |
|---|
| 133 | -- to tye in its "self", and, that adds the standard HOW and WHICH methods. |
|---|
| 134 | -- mkPureClass :: (Boxable a) => String -> [(ID, MethodPrim a)] -> PureClass |
|---|
| 135 | mkPureClass :: Boxable a => String -> [(ID, MethodPrim a)] -> PureClass |
|---|
| 136 | mkPureClass cls methods = fix . (mkBoxClass cls .) $ \self -> flip (++) methods |
|---|
| 137 | [ "" ... mkVal self |
|---|
| 138 | , "ITEM" ... id |
|---|
| 139 | , "LIST" ... id |
|---|
| 140 | ] |
|---|
| 141 | |
|---|
| 142 | raiseWhatError :: String -> a |
|---|
| 143 | raiseWhatError = error |
|---|
| 144 | |
|---|
| 145 | mkBoxMethod :: forall a. Boxable a => (ID, MethodPrim a) -> AnyMethod Eval |
|---|
| 146 | mkBoxMethod (meth, fun) = MkMethod $ MkSimpleMethod |
|---|
| 147 | { sm_name = meth |
|---|
| 148 | , sm_definition = MkMethodCompiled $ \args -> do |
|---|
| 149 | inv <- fromInvocant args :: Eval a |
|---|
| 150 | fun inv $ concatMapP f_positionals (c_feeds args) |
|---|
| 151 | } |
|---|
| 152 | |
|---|
| 153 | type PureClass = MOClass Eval |
|---|
| 154 | |
|---|
| 155 | instance (Show a, Typeable a, Ord a) => Boxable (Maybe a) |
|---|
| 156 | |
|---|
| 157 | instance Boxable a => Boxable [a] |
|---|
| 158 | instance Boxable a => Boxable [:a:] |
|---|
| 159 | |
|---|
| 160 | instance Boxable ID |
|---|
| 161 | instance Boxable PureClass where |
|---|
| 162 | classOf _ = _PureClass |
|---|
| 163 | |
|---|
| 164 | _PureClass :: PureClass |
|---|
| 165 | _PureClass = mkPureClass "Class" |
|---|
| 166 | [ "methods" ... ((filter (/= nullID) . map methodName . all_methods) :: PureClass -> [ID]) |
|---|
| 167 | ] |
|---|
| 168 | |
|---|
| 169 | instance ((:>:) Call) String where |
|---|
| 170 | cast = (`MkMethodInvocation` CaptSub{ c_feeds = [::] }) . _cast |
|---|
| 171 | |
|---|
| 172 | instance ((:>:) Call) ByteString where |
|---|
| 173 | cast = (`MkMethodInvocation` CaptSub{ c_feeds = [::] }) . cast |
|---|
| 174 | |
|---|
| 175 | instance ((:>:) Call (ByteString, [Val], AtomMap Val)) where |
|---|
| 176 | cast (meth, pos, named) = MkMethodInvocation (cast meth) CaptSub |
|---|
| 177 | { c_feeds = [: MkFeed (toP pos) (AtomMap.map (\x -> [:x:]) named) :]} |
|---|