packages feed

ideas-0.7: src/Service/DomainReasoner.hs

{-# LANGUAGE MultiParamTypeClasses, TypeSynonymInstances #-}
-----------------------------------------------------------------------------
-- Copyright 2010, Open Universiteit Nederland. This file is distributed 
-- under the terms of the GNU General Public License. For more information, 
-- see the file "LICENSE.txt", which is included in the distribution.
-----------------------------------------------------------------------------
-- |
-- Maintainer  :  bastiaan.heeren@ou.nl
-- Stability   :  provisional
-- Portability :  portable (depends on ghc)
--
-----------------------------------------------------------------------------
module Service.DomainReasoner 
   ( -- * Domain Reasoner data type
     DomainReasoner, runDomainReasoner, runWithCurrent
   , liftEither, MonadIO(..), catchError 
     -- * Update functions
   , addPackages, addPackage, addPkgService
   , addServices, addService, addTestSuite
   , setVersion, setFullVersion
     -- * Accessor functions
   , getPackages, getExercises, getServices
   , getVersion, getFullVersion, getTestSuite
   , findPackage, findService
   ) where

import Common.Library
import Common.TestSuite
import Common.Utils (Some(..))
import Control.Monad.Error
import Control.Monad.State
import Data.Maybe
import Service.Types
import Service.ExercisePackage

-----------------------------------------------------------------------
-- Domain Reasoner data type

newtype DomainReasoner a = DR { unDR :: ErrorT String (StateT Content IO) a }

data Content = Content
   { packages    :: [Some ExercisePackage]
   , services    :: [Some ExercisePackage] -> [Service]
   , testSuite   :: TestSuite
   , version     :: String
   , fullVersion :: Maybe String
   }
   
noContent :: Content
noContent = Content [] (const []) (return ()) [] Nothing

runDomainReasoner :: DomainReasoner a -> IO a
runDomainReasoner m = do
   result <- evalStateT (runErrorT (unDR m)) noContent
   case result of
      Left msg -> fail msg
      Right a  -> return a

-- | Returns a run function, based on the current state, inside the monad
runWithCurrent :: DomainReasoner (DomainReasoner a -> IO a)
runWithCurrent =
   get >>= \st -> return (runDomainReasoner . (put st >>))

liftEither :: Either String a -> DomainReasoner a
liftEither = either fail return

-----------------------------------------------------------------------
-- Instance declarations

instance Monad DomainReasoner where
   return a   = DR (return a)
   DR m >>= f = DR (m >>= unDR . f)
   fail s     = DR (fail s)

instance MonadError String DomainReasoner where
   throwError     = fail
   catchError m f = DR (unDR m `catchError` (unDR . f))

instance MonadPlus DomainReasoner where
   mzero       = DR mzero
   a `mplus` b = DR (unDR a `mplus` unDR b)

instance MonadState Content DomainReasoner where
   get   = DR get
   put s = DR (put s)

instance MonadIO DomainReasoner where
   liftIO m = DR (liftIO m)

-----------------------------------------------------------------------
-- Update functions

addPackages :: [Some ExercisePackage] -> DomainReasoner ()
addPackages xs = modify $ \c -> c { packages = xs ++ packages c }

addPackage :: Some ExercisePackage -> DomainReasoner ()
addPackage pkg = addPackages [pkg]

addPkgService :: ([Some ExercisePackage] -> Service) -> DomainReasoner ()
addPkgService f = modify $ \c -> 
   c { services = \xs -> f xs : services c xs }

addServices :: [Service] -> DomainReasoner ()
addServices = mapM_ (addPkgService . const)

addService :: Service -> DomainReasoner ()
addService s = addServices [s]

addTestSuite :: TestSuite -> DomainReasoner ()
addTestSuite m = modify $ \c -> c { testSuite = testSuite c >> m }

setVersion :: String -> DomainReasoner ()
setVersion s = modify $ \c -> c { version = s }

setFullVersion :: String -> DomainReasoner ()
setFullVersion s = modify $ \c -> c  { fullVersion = Just s }

-----------------------------------------------------------------------
-- Accessor functions

getPackages :: DomainReasoner [Some ExercisePackage]
getPackages = gets packages

getExercises :: DomainReasoner [Some Exercise]
getExercises = gets (map (\(Some pkg) -> Some (exercise pkg)) . packages)

getServices :: DomainReasoner [Service]
getServices = gets (\c -> services c (packages c))

getVersion :: DomainReasoner String
getVersion = gets version

getFullVersion :: DomainReasoner String
getFullVersion = gets fullVersion >>= maybe getVersion return

getTestSuite :: DomainReasoner TestSuite
getTestSuite = gets testSuite

findPackage :: Id -> DomainReasoner (Some ExercisePackage)
findPackage i = do
   pkgs <- getPackages 
   case [ a | a@(Some pkg) <- pkgs, getId pkg == resolveId i ] of
      [this] -> return this
      _      -> fail $ "Package " ++ show i ++ " not found"
      
findService :: String -> DomainReasoner Service
findService txt = do
   srvs <- getServices
   case filter ((==txt) . showId) srvs of
      [hd] -> return hd
      []   -> fail $ "No service " ++ txt
      _    -> fail $ "Ambiguous service " ++ txt

-----------------------------------------------------------------------
-- Identifier aliases (temporary)

resolveId :: Id -> Id
resolveId i = fromMaybe i (lookup i table)
 where
   table = map (newId *** newId)
      [ ("math.coverup",             "algebra.equations.coverup")
      , ("math.lineq",               "algebra.equations.linear")
      , ("math.lineq-mixed",         "algebra.equations.linear.mixed")
      , ("math.quadreq",             "algebra.equations.quadratic")         
      , ("math.quadreq-no-abc",      "algebra.equations.quadratic.no-abc")    
      , ("math.quadreq-with-approx", "algebra.equations.quadratic.approximate")
      , ("math.higherdegree",        "algebra.equations.polynomial")
      , ("math.rationaleq",          "algebra.equations.rational")
      , ("math.linineq",             "algebra.inequalities.linear")
      , ("math.quadrineq",           "algebra.inequalities.quadratic")
      , ("math.ineqhigherdegree",    "algebra.inequalities.polynomial")
      , ("math.factor",              "algebra.manipulation.polynomial.factor")
      , ("math.simplifyrational",    "algebra.manipulation.rational.simplify")
      , ("math.simplifypower",       "algebra.manipulation.exponents.simplify")
      , ("math.nonnegexp",           "algebra.manipulation.exponents.nonnegative")
      , ("math.powerof",             "algebra.manipulation.exponents.powerof")
      , ("math.derivative",          "calculus.differentiation")
      , ("math.fraction",            "arithmetic.fractions")
      , ("math.calcpower",           "arithmetic.exponents")
      , ("linalg.gaussianelim",      "linearalgebra.gaussianelim")
      , ("linalg.gramschmidt",       "linearalgebra.gramschmidt")
      , ("linalg.linsystem",         "linearalgebra.linsystem")
      , ("linalg.systemwithmatrix",  "linearalgebra.systemwithmatrix")
      , ("logic.dnf",                "logic.propositional.dnf")
      , ("logic.dnf-unicode",        "logic.propositional.dnf.unicode")
      , ("relationalg.cnf",          "relationalgebra.cnf")
      -- MathDox compatibility
      , ("gaussianelimination"        , "linearalgebra.gaussianelim")
      , ("gramschmidt"                , "linearalgebra.gramschmidt")
      , ("solvelinearsystem"          , "linearalgebra.linsystem")
      , ("solvelinearsystemwithmatrix", "linearalgebra.systemwithmatrix")
      ]