{-# LANGUAGE OverloadedStrings, DeriveDataTypeable #-} module MOO.Verb ( Verb(..) , ObjSpec(..) , PrepSpec(..) , initVerb , obj2string , string2obj , objMatch , prep2string , string2prep , prepMatch , prepPhrases , verbNameMatch ) where import Control.Applicative ((<$>), (<*>)) import Data.Typeable (Typeable) import Database.VCache (VCacheable(put, get)) import MOO.AST import {-# SOURCE #-} MOO.Object (nothing) import MOO.Types import qualified MOO.String as Str data Verb = Verb { verbNames :: StrT , verbProgram :: Program , verbOwner :: ObjId , verbPermR :: Bool , verbPermW :: Bool , verbPermX :: Bool , verbPermD :: Bool , verbDirectObject :: ObjSpec , verbPreposition :: PrepSpec , verbIndirectObject :: ObjSpec } deriving Typeable instance VCacheable Verb where put verb = do put $ verbNames verb put $ verbProgram verb put $ verbOwner verb put $ verbPermR verb put $ verbPermW verb put $ verbPermX verb put $ verbPermD verb put $ verbDirectObject verb put $ verbPreposition verb put $ verbIndirectObject verb get = Verb <$> get <*> get <*> get <*> get <*> get <*> get <*> get <*> get <*> get <*> get instance Sizeable Verb where storageBytes verb = storageBytes (verbNames verb) + storageBytes (verbProgram verb) * 2 + storageBytes (verbOwner verb) + storageBytes (verbPermR verb) + storageBytes (verbPermW verb) + storageBytes (verbPermX verb) + storageBytes (verbPermD verb) + storageBytes (verbDirectObject verb) + storageBytes (verbPreposition verb) + storageBytes (verbIndirectObject verb) initVerb = Verb { verbNames = "" , verbProgram = Program [] , verbOwner = nothing , verbPermR = False , verbPermW = False , verbPermX = False , verbPermD = False , verbDirectObject = ObjNone , verbPreposition = PrepNone , verbIndirectObject = ObjNone } -- | Argument (direct/indirect object) specifier data ObjSpec = ObjNone -- ^ none | ObjAny -- ^ any | ObjThis -- ^ this deriving (Enum, Bounded, Typeable) instance VCacheable ObjSpec where put = put . fromEnum get = toEnum <$> get instance Sizeable ObjSpec where storageBytes _ = storageBytes () obj2string :: ObjSpec -> StrT obj2string ObjNone = "none" obj2string ObjAny = "any" obj2string ObjThis = "this" string2obj :: StrT -> Maybe ObjSpec string2obj = flip lookup $ map mkAssoc [minBound ..] where mkAssoc :: ObjSpec -> (StrT, ObjSpec) mkAssoc objSpec = (obj2string objSpec, objSpec) objMatch :: ObjId -> ObjSpec -> ObjId -> Bool objMatch _ ObjNone oid = oid == nothing objMatch _ ObjAny _ = True objMatch this ObjThis oid = oid == this -- | Preposition specifier data PrepSpec = PrepAny -- ^ any | PrepNone -- ^ none | PrepWithUsing -- ^ with\/using | PrepAtTo -- ^ at\/to | PrepInfrontof -- ^ in front of | PrepInInsideInto -- ^ in\/inside\/into | PrepOntopofOnOntoUpon -- ^ on top of\/on\/onto\/upon | PrepOutofFrominsideFrom -- ^ out of\/from inside\/from | PrepOver -- ^ over | PrepThrough -- ^ through | PrepUnderUnderneathBeneath -- ^ under\/underneath\/beneath | PrepBehind -- ^ behind | PrepBeside -- ^ beside | PrepForAbout -- ^ for\/about | PrepIs -- ^ is | PrepAs -- ^ as | PrepOffofOff -- ^ off of\/off deriving (Enum, Bounded, Eq, Typeable) instance VCacheable PrepSpec where put = put . fromEnum get = toEnum <$> get instance Sizeable PrepSpec where storageBytes _ = storageBytes () prep2string :: PrepSpec -> StrT prep2string PrepAny = "any" prep2string PrepNone = "none" prep2string PrepWithUsing = "with/using" prep2string PrepAtTo = "at/to" prep2string PrepInfrontof = "in front of" prep2string PrepInInsideInto = "in/inside/into" prep2string PrepOntopofOnOntoUpon = "on top of/on/onto/upon" prep2string PrepOutofFrominsideFrom = "out of/from inside/from" prep2string PrepOver = "over" prep2string PrepThrough = "through" prep2string PrepUnderUnderneathBeneath = "under/underneath/beneath" prep2string PrepBehind = "behind" prep2string PrepBeside = "beside" prep2string PrepForAbout = "for/about" prep2string PrepIs = "is" prep2string PrepAs = "as" prep2string PrepOffofOff = "off of/off" string2prep :: StrT -> Maybe PrepSpec string2prep = flip lookup $ concatMap mkAssoc [minBound ..] where mkAssoc :: PrepSpec -> [(StrT, PrepSpec)] mkAssoc prepSpec = [ (prep, prepSpec) | prep <- Str.splitOn "/" $ prep2string prepSpec ] ++ [ (Str.fromString $ show index, prepSpec) | let index = fromEnum prepSpec - fromEnum (succ PrepNone) , index >= 0 ] prepMatch :: PrepSpec -> PrepSpec -> Bool prepMatch PrepAny _ = True prepMatch vp cp = vp == cp prepPhrases :: [(PrepSpec, [StrT])] prepPhrases = [ (prepSpec, Str.words prepPhrase) | prepSpec <- [succ PrepNone ..] , prepPhrase <- Str.splitOn "/" $ prep2string prepSpec ] -- | Does the given verb name match any of the given aliases? Each alias may -- use @*@ to separate required and optional text to match. verbNameMatch :: StrT -> [StrT] -> Bool verbNameMatch name = any matchName where matchName :: StrT -> Bool matchName vname | post == "" = name == vname | post == "*" = preName == pre | otherwise = preName == pre && postName `Str.isPrefixOf` Str.tail post where (pre, post) = Str.breakOn "*" vname (preName, postName) = Str.splitAt (Str.length pre) name