packages feed

Yogurt 0.3 → 0.4

raw patch · 7 files changed

+221/−111 lines, 7 filesdep +sybdep ~basePVP ok

version bump matches the API change (PVP)

Dependencies added: syb

Dependency ranges changed: base

API changes (from Hackage documentation)

- Network.Yogurt.Engine: connect :: String -> Int -> Mud () -> IO ()
- Network.Yogurt.Engine: runMud :: MVar MudState -> Mud a -> IO a
+ Network.Yogurt: version :: Version
+ Network.Yogurt.Mud: liftIO :: (MonadIO m) => forall a. IO a -> m a
+ Network.Yogurt.Mud: runMud :: MVar MudState -> RunMud
+ Network.Yogurt.Readline: connect :: String -> Int -> Mud () -> IO ()
+ Network.Yogurt.Session: Session :: String -> Int -> (Reload -> Mud ()) -> Session
+ Network.Yogurt.Session: data Session
+ Network.Yogurt.Session: hostName :: Session -> String
+ Network.Yogurt.Session: instance Typeable Session
+ Network.Yogurt.Session: mudProgram :: Session -> Reload -> Mud ()
+ Network.Yogurt.Session: portNumber :: Session -> Int
+ Network.Yogurt.Session: session :: Session
+ Network.Yogurt.Session: type Reload = Mud ()
+ Network.Yogurt.Utils: triggerOneOf :: [(Pattern, Mud ())] -> Mud ()

Files

Network/Yogurt.hs view
@@ -1,10 +1,17 @@--- | Re-exports the other three modules.+-- | Provides Yogurt's version number and re-exports the other modules. module Network.Yogurt (+  version,   module Network.Yogurt.Mud,-  module Network.Yogurt.Engine,-  module Network.Yogurt.Utils+  module Network.Yogurt.Session,+  module Network.Yogurt.Utils,   ) where  import Network.Yogurt.Mud+import Network.Yogurt.Session import Network.Yogurt.Utils-import Network.Yogurt.Engine+import Data.Version+import qualified Paths_Yogurt as P++-- | The version number of this version of the library.+version :: Version+version = P.version
− Network/Yogurt/Engine.hs
@@ -1,80 +0,0 @@-{-# LANGUAGE RecursiveDo #-}--module Network.Yogurt.Engine (connect, runMud) where--import Network.Yogurt.Mud-import System.IO-import Network-import Control.Concurrent-import Control.Monad.State-import System.Console.Readline-import Network.Yogurt.IO-import Data.Char (isSpace)-import System.Process----- | @connect hostname port program@ connects to a MUD and executes the specified program. Input is read from @stdin@, and output is written to @stdout@.-connect :: String -> Int -> Mud () -> IO ()-connect host port mud = mdo-  -- Connect.-  putStrLn $ "Connecting to " ++ host ++ " port " ++ show port ++ "..."-  h <- connectTo host (PortNumber (fromIntegral port))--  -- Create shared mud state, executing initial commands.-  let out ch msg = case ch of-        Local  -> writeToTTY msg-        Remote -> do hPutStr h msg; hFlush h-  let state0 = emptyMud (runMud vState) out-  state1 <- execStateT mud state0-  vState <- newMVar state1--  -- Start child threads.-  forkIO (handleSource vState localInput Remote)-  handleSource vState (remoteInput h) Local-  -  -- Clean.-  runCommand "stty echo" >>= waitForProcess-  return ()----- Watches an input source and updates the mud state whenever a new message arrives.-handleSource :: MVar MudState ->      -- to run mud computations-                IO (Maybe String) ->  -- input source-                Destination ->        -- target destination-                IO ()-handleSource env input dest = loop where-  loop = do-    mmessage <- input-    case mmessage of-      Nothing -> return ()-      Just message -> do-        runMud env (trigger dest message)-        loop----- Local input using readline.-localInput :: IO (Maybe String)-localInput = do-  maybeLine <- readline ""-  setLineBuffer ""-  case maybeLine of-    Nothing   -> return Nothing-    Just line -> do-      when (not $ all isSpace line) (addHistory line)-      return (Just $ line ++ "\n")----- Remote input using a connection handle.-remoteInput :: Handle -> IO (Maybe String)-remoteInput h = do-  input <- maybeInput (hGetImpatientLine h 10)-  return input----- | Runs a Mud computation, executes the results (such as sending messages to the screen or the MUD) and returns the computation's result. The MVar is updated.-runMud :: MVar MudState -> Mud a -> IO a-runMud vState prog = do-  s <- takeMVar vState-  (rv, s') <- runStateT prog s-  putMVar vState s'-  return rv
Network/Yogurt/Mud.hs view
@@ -1,15 +1,20 @@-{-# OPTIONS_GHC -fglasgow-exts #-}+{-# LANGUAGE RankNTypes #-}+{-# LANGUAGE ImpredicativeTypes #-}+{-# LANGUAGE LiberalTypeSynonyms #-}  -- | The core of Yogurt, consisting of the Mud monad and all functions manipulating this monad. module Network.Yogurt.Mud (    -- * Types-  Mud, MudState, emptyMud,+  Mud, MudState,   RunMud, Output,   Hook,   Destination(..),   Pattern,   Var,+  +  -- * Running Mud computations+  emptyMud, runMud,    -- * Hooks   -- | A hook watches a channel for messages matching a specific regular expression.@@ -32,6 +37,8 @@    -- * Triggering hooks   trigger, triggerJust, io,+  +  -- * Multi-threading   liftIO, forkWithCallback    ) where@@ -47,6 +54,7 @@ import Data.Monoid (mconcat) import Data.IORef import Control.Concurrent (forkIO, ThreadId)+import Control.Concurrent.MVar   @@ -56,7 +64,7 @@ -- | The Mud monad is a state monad over IO. type Mud = StateT MudState IO --- | Run a Mud computation in IO.+-- | Run a Mud computation in IO. A common implementation of this function is @'runMud' vState@. type RunMud = forall a. Mud a -> IO a  -- | Provides a way to output messages.@@ -71,10 +79,6 @@   , mOutput   :: Output   } --- | The initial state of the Mud monad.-emptyMud :: RunMud -> Output -> MudState-emptyMud = MudState empty [0..] Nothing- -- | The abstract @Hook@ type. For every pair of hooks @(h1, h2)@: -- -- * @h1 == h2@ iff they were created by the same call to 'mkHook'.@@ -92,10 +96,10 @@   (==) = (==) `on` hId  instance Ord Hook where-  compare = flip $ mconcat [comparing hPriority, comparing hId]+  compare = flip $ mconcat [comparing hDestination, comparing hPriority, comparing hId]  instance Show Hook where-  show (Hook hid prio dest pat _) = "Hook #" ++ show hid ++ " @" ++ show prio ++ " " ++ show dest ++ " [" ++ pat ++ "]"+  show (Hook hid prio dest pat _) = show dest ++ " @" ++ show prio ++ " [" ++ pat ++ "]"  -- | The direction in which a message is going. data Destination@@ -114,7 +118,7 @@   , mAfter         :: String   } --- | Variables hold temporary, updatable, typed data.+-- | Variables hold updatable, typed data. newtype Var a = Var (IORef a)  type Id = Int@@ -132,6 +136,23 @@  updateHooks :: (IntMap Hook -> IntMap Hook) -> Mud () updateHooks f = modify $ \s -> s { hooks = f (hooks s) }++++-- Section: Running Mud computations+++-- | The initial state of the Mud monad. The 'RunMud' argument is stored in the state to make 'forkWithCallback' possible; the 'Output' argument is used by 'Mud' computations for messages leaving the engine.+emptyMud :: RunMud -> Output -> MudState+emptyMud = MudState empty [0..] Nothing++-- | Runs a 'Mud' computation, executes the results (such as sending messages to the screen or the MUD) and returns the computation's result. The 'MVar' is updated.+runMud :: MVar MudState -> RunMud+runMud vState prog = do+  s <- takeMVar vState+  (rv, s') <- runStateT prog s+  putMVar vState s'+  return rv   
+ Network/Yogurt/Readline.hs view
@@ -0,0 +1,76 @@+{-# LANGUAGE RecursiveDo #-}++-- | Provides a readline-based command-line interface for connecting to MUDs. +module Network.Yogurt.Readline (+    connect+  ) where++import Network.Yogurt.Mud+import Network.Yogurt.IO++import Control.Monad.State+import Control.Concurrent.MVar+import System.IO+import Data.Char (isSpace)+import System.Console.Readline+import Control.Concurrent+import Network+import System.Process+++-- | @connect hostname port program@ connects to a MUD and executes the specified program on connecting. Input is read from @stdin@ and output is written to @stdout@.+connect :: String -> Int -> Mud () -> IO ()+connect host port mud = mdo+  -- Connect.+  putStrLn $ "Connecting to " ++ host ++ " port " ++ show port ++ "..."+  h <- connectTo host (PortNumber (fromIntegral port))++  -- Create shared mud state, executing initial commands.+  let out ch msg = case ch of+        Local  -> writeToTTY msg+        Remote -> do hPutStr h msg; hFlush h+  let state0 = emptyMud (runMud vState) out+  state1 <- execStateT mud state0+  vState <- newMVar state1++  -- Start child threads.+  forkIO (handleSource vState localInput Remote)+  handleSource vState (remoteInput h) Local++  -- Clean.+  runCommand "stty echo" >>= waitForProcess+  return ()+++-- Watches an input source and updates the mud state whenever a new message arrives.+handleSource :: MVar MudState ->      -- to run mud computations+                IO (Maybe String) ->  -- input source+                Destination ->        -- target destination+                IO ()+handleSource env input dest = loop where+  loop = do+    mmessage <- input+    case mmessage of+      Nothing -> return ()+      Just message -> do+        runMud env (trigger dest message)+        loop+++-- Local input using readline.+localInput :: IO (Maybe String)+localInput = do+  maybeLine <- readline ""+  setLineBuffer ""+  case maybeLine of+    Nothing   -> return Nothing+    Just line -> do+      when (not $ all isSpace line) (addHistory line)+      return (Just $ line ++ "\n")+++-- Remote input using a connection handle.+remoteInput :: Handle -> IO (Maybe String)+remoteInput h = do+  input <- maybeInput (hGetImpatientLine h 10)+  return input
+ Network/Yogurt/Session.hs view
@@ -0,0 +1,51 @@+{-# LANGUAGE DeriveDataTypeable #-}++-- | +-- Sessions are used by Yogurt's standalone executable @yogurt@; see package @Yogurt-Standalone@ on hackage.+--+-- Every Yogurt file loaded by @yogurt@ should define a value of type 'Session'. For future compatibility, such a session is best defined using 'session' as starting value:+--+-- > import Network.Yogurt+-- >+-- > newmoon :: Session+-- > newmoon = session+-- >   { hostName   = "eclipse.cs.pdx.edu"+-- >   , portNumber = 7680+-- >   , mudProgram = \reload -> do+-- >       mkCommand "reload" reload+-- >   }+-- +-- A module is free to define multiple sessions, in which case you will have to tell @yogurt@ which session to load.+module Network.Yogurt.Session (+    Session(..), Reload, session+  ) where++import Network.Yogurt.Mud+import Data.Generics (Typeable)+import Control.Monad+import Data.List (elemIndices)++-- | Describes a MUD session.+data Session = Session+  { -- | The hostname to connect to.+    hostName    :: String+    -- | The port to connect to.+  , portNumber  :: Int+    -- | The initial program to run. The 'Reload' argument provides a way to+    --   reload the plugin without interrupting the MUD connection.+  , mudProgram  :: Reload -> Mud ()+  }+  deriving Typeable++-- | When executed, reloads the session from disk without interrupting the MUD connection. If the reloaded+--   session contains no errors, all hooks are uninstalled before the reloaded program is executed. Timers+--   are /not/ stopped and previous variables will still be reachable if you hang on to their handles.+type Reload = Mud ()++-- | Starting value for sessions. The default 'mudProgram' is @return ()@. There are no default values for 'hostName' and 'portNumber'.+session :: Session+session = Session+  { hostName    = error "session hostName not set"+  , portNumber  = error "session portNumber not set"+  , mudProgram  = const (return ())+  }
Network/Yogurt/Utils.hs view
@@ -1,9 +1,10 @@-{-# OPTIONS_GHC -fglasgow-exts #-}+{-# LANGUAGE RecursiveDo #-}+{-# LANGUAGE Rank2Types #-}  -- | Convenience functions on top of "Yogurt.Mud". module Network.Yogurt.Utils (-  -- * Hook and timer derivatives-  mkTrigger, mkTriggerOnce,+  -- * Hook derivatives+  mkTrigger, mkTriggerOnce, triggerOneOf,   mkAlias, mkArgAlias, mkCommand,    -- * Timers@@ -15,7 +16,8 @@   -- * Logging   Logger, startLogging, stopLogging, -  -- * Miscellaneous+  -- * Triggering multiple hooks+  -- | By default, when a message causes a hook to fire, the message is stopped and discarded unless the hook decides otherwise. These functions provide ways to give other hooks with lower priorities a chance to fire as well.   matchMore, matchMoreOn, matchMoreOn'    ) where@@ -30,7 +32,7 @@   --- Hook and timer derivatives.+-- Hook derivatives.   -- | Creates a hook that watches messages headed to the terminal. When fired, the message is passed on to the terminal and the action is executed.@@ -43,6 +45,13 @@   hook <- mkTrigger pat (act >> rmHook hook)   return hook +-- | For each pair @(pattern, action)@ a hook is installed. As soon as one of the hooks fires, the hooks are removed and the corresponding action is executed.+triggerOneOf :: [(Pattern, Mud ())] -> Mud ()+triggerOneOf pairs = mdo+  hs <- forM pairs $ \(pat, act) -> do+    mkTrigger pat (forM hs rmHook >> act)+  return ()+ -- | @mkAlias command subst@ creates a hook that watches messages headed to the remote MUD. If the message is or starts with the word @command@, the command is replaced by @subst@ before being sent to the MUD. mkAlias :: String -> String -> Mud Hook mkAlias pat subst = mkHook Remote ("^" ++ pat ++ "($| .*$)") $ do@@ -55,7 +64,7 @@   args <- fmap words (group 1)   echorln (f args) --- | Like 'mkAlias', but instead of substituting the command, a program is executed.+-- | Like 'mkAlias', but instead of substituting the command, a program is executed. The command's arguments are available as 'group' 1. mkCommand :: String -> Mud a -> Mud Hook mkCommand pat = mkHook Remote ("^" ++ pat ++ "($| .*$)") @@ -64,7 +73,7 @@ -- Section: Timers.  --- | The abstract Time type.+-- | The abstract Timer type. newtype Timer = Timer (Var Bool)  -- | Interval in milliseconds.@@ -110,13 +119,16 @@ -- Sending messages.  +withNewline :: String -> String+withNewline = (++ "\r\n")+ -- | Sends a message to the terminal, triggering hooks. receive :: String -> Mud () receive = trigger Local  -- | Sends a message appended with a newline character to the MUD, triggering hooks. sendln :: String -> Mud ()-sendln m = trigger Remote (m ++ "\n")+sendln = trigger Remote . withNewline  -- | Sends a message to the terminal, without triggering hooks. echo :: String -> Mud ()@@ -124,11 +136,11 @@  -- | Sends a message appended with a newline character to the terminal, without triggering hooks. echoln :: String -> Mud ()-echoln m = echo (m ++ "\n")+echoln = echo . withNewline  -- | Sends a message appended with a newline character to the MUD, without triggering hooks. echorln :: String -> Mud ()-echorln m = io Remote (m ++ "\n")+echorln = io Remote . withNewline  -- | Sends a bell character to the terminal. bell :: Mud ()@@ -165,7 +177,8 @@   -- | When called from a hook body, gives hooks that haven't been considered yet--- a chance to match on the currently triggering message. Useful if you want to+-- a chance to match on the currently triggering message. If no other hooks match,+-- the message is sent on to its destination. Useful if you want to -- build a hook that only has a side-effect and doesn't want to directly affect -- the other active hooks. matchMore :: Mud ()
Yogurt.cabal view
@@ -1,21 +1,43 @@ Name:           Yogurt-Version:        0.3+Version:        0.4 Synopsis:       A MUD client library-Description:    A MUD client library for Haskell. Features prioritized, regex-based hooks, variables and timers.+Description:   	Yogurt is a functional MUD client featuring prioritized, regex-based hooks, variables, timers, logging, dynamic loading of Yogurt scripts and more. For example programs, please see Yogurt's home page.+                .+                This package provides the library. To use Yogurt as a standalone executable and dynamically load and reload Yogurt scripts, please see package @Yogurt-Standalone@.+                .+                If you do not wish to install the readline-based command-line interface available in module @Network.Yogurt.Readline@, install using @cabal install Yogurt -f-readline@. + Author:         Martijn van Steenbergen Maintainer:     martijn@van.steenbergen.nl Stability:      Experimental Copyright:      Some Rights Reserved (CC) 2008-2009 Martijn van Steenbergen-Homepage:       http://martijn.van.steenbergen.nl/projects/yogurt/+Homepage:       http://code.google.com/p/yogurt-mud/ + Cabal-Version:  >= 1.2 License:        BSD3 License-file:   LICENSE Category:       Network Build-type:     Simple ++Flag readline+  Description:  Enable the readline-based command-line interface.+  Default:      True++ Library-  Build-Depends:    base, mtl, regex-posix, containers, time, old-locale, readline, network, process-  Exposed-Modules:  Network.Yogurt, Network.Yogurt.Mud, Network.Yogurt.Utils, Network.Yogurt.Engine-  Other-Modules:    Network.Yogurt.Ansi, Network.Yogurt.IO+  Exposed-Modules:  Network.Yogurt, Network.Yogurt.Mud, Network.Yogurt.Utils, Network.Yogurt.Session+  Other-Modules:    Network.Yogurt.Ansi, Paths_Yogurt+  Build-Depends:    mtl, regex-posix, containers, time, old-locale++  if impl(ghc >= 6.10)+    Build-Depends:  base >= 4, base < 5, syb+  else+    Build-Depends:  base >= 3, base < 4++  if flag(readline)+    Exposed-Modules:  Network.Yogurt.Readline+    Other-Modules:    Network.Yogurt.IO+    Build-Depends:    readline, process, network