module Berp.Base.StdTypes.Type (typeClass, newType) where
import Data.List (delete)
import Control.Monad.Trans (liftIO)
import Berp.Base.SemanticTypes (Object (..), Procedure)
import Berp.Base.Monad (constantIO)
import Berp.Base.Identity (newIdentity)
import Berp.Base.Attributes (mkAttributes)
import Berp.Base.Object (typeOf)
import Berp.Base.Prims (primitive, callMethod, returningProcedure)
import Berp.Base.StdNames (mroName, initName)
import Berp.Base.StdTypes.Object (object)
import Berp.Base.StdTypes.Dictionary (emptyDictionary)
import Berp.Base.StdTypes.ObjectBase (objectBase)
import Berp.Base.StdTypes.String (string)
import Berp.Base.StdTypes.Tuple (tuple)
typeClass :: Object
typeClass = constantIO $ do
identity <- newIdentity
dict <- attributes
return $
Type
{ object_identity = identity
, object_type = typeClass
, object_dict = dict
, object_bases = objectBase
, object_constructor = returningProcedure (\args -> liftIO $ newType args)
, object_type_name = string "type"
, object_mro = tuple [typeClass, object]
}
newType :: [Object] -> IO Object
newType args
| [obj] <- args = return $ typeOf obj
| [name, bases, dict] <- args = do
identity <- newIdentity
let theType =
Type
{ object_identity = identity
, object_type = typeClass
, object_dict = dict
, object_bases = bases
, object_constructor = returningProcedure $ instantiate theType
, object_type_name = name
, object_mro = tuple $ mro theType $ getTupleElements bases
}
return theType
| otherwise = fail "type() takes 1 or 3 arguments"
getTupleElements :: Object -> [Object]
getTupleElements (Tuple { object_tuple = objs }) = objs
getTupleElements _other = error "bases of object is not a tuple"
instantiate :: Object -> Procedure
instantiate objectType args = do
identity <- liftIO $ newIdentity
dict <- liftIO $ emptyDictionary
let object =
Object
{ object_identity = identity
, object_type = objectType
, object_dict = dict
}
callMethod object initName args
return object
attributes :: IO Object
attributes =
mkAttributes [ (mroName, primitive 1 mroMethod) ]
mroMethod :: Procedure
mroMethod (obj:_) = return $ object_mro obj
mroMethod _other = error "mro called with wrong number of arguments"
mro :: Object -> [Object] -> [Object]
mro klass bases
= klass : merge (map getMro bases ++ [bases])
where
getMro :: Object -> [Object]
getMro (Type { object_mro = obj }) = getTupleElements obj
getMro _other = error "Fatal error: object's base is not a type"
merge :: [[Object]] -> [Object]
merge seqs =
mergeWork [] $ nonEmptySeqs seqs
where
mergeWork acc seqs
| null seqs = reverse acc
| candidate:_ <- findCandidate seqs
= mergeWork (candidate:acc) $
nonEmptySeqs $ removeCandidate candidate seqs
| otherwise
= error "Cannot create a consistent method resolution"
findCandidate :: [[Object]] -> [Object]
findCandidate seqs =
[ candidate | candidate <- map head seqs,
all (candidate `notElem`) (map tail seqs) ]
removeCandidate :: Object -> [[Object]] -> [[Object]]
removeCandidate candidate = map (delete candidate)
nonEmptySeqs :: [[Object]] -> [[Object]]
nonEmptySeqs = filter (not . null)