packages feed

laborantin-hs 0.1.2.0 → 0.1.3.0

raw patch · 11 files changed

+426/−178 lines, 11 filesdep +old-locale

Dependencies added: old-locale

Files

Laborantin.hs view
@@ -1,22 +1,80 @@ -module Laborantin where+module Laborantin (+        prepare+    ,   load+    ,   remove+    ,   runAnalyze+    ,   missingParameterSets+    ,   expandParameterSets+) where  import Laborantin.Types-import Laborantin.DSL+import Laborantin.Query import Laborantin.Implementation import Control.Monad.IO.Class-import Control.Monad.Reader+import Control.Monad.Reader +import Control.Monad.State  import Control.Monad.Error import Control.Applicative import qualified Data.Set as S+import qualified Data.Map as M -execute :: (MonadIO m) => Backend m -> ScenarioDescription m -> ParameterSet -> m ()+-- | Prepare a list of execution actions for a given+-- (scenario, parameter-query, existing) ancestors.+--+-- This function returns one action per ParameterSet which is required by the+-- parameter-query and not yet present in the existing list of executions.+--+-- For instance, if the scenario has one parameter 'foo'; the query wants 'foo'+-- in [1,2,3,4]; and there is one execution with 'foo' == 2; then this function+-- returns 3 actions (for parameters 1, 3, and 4).+--+-- A user can then run these actions in sequence (or concurrently if it makes+-- sense).+prepare :: (MonadIO m)    => Backend m+                          -> TExpr Bool+                          -> [Execution m]+                          -> ScenarioDescription m+                          -> [m (Execution m)]+prepare b expr execs sc = map toAction neededParamSets+          where toAction = execute b sc+                neededParamSets = missingParameterSets sc expr existing+                                  where existing = map eParamSet execs++-- | Like matchingParameterSets but also remove existing ParameterSet given as+-- third parameter.+missingParameterSets :: ScenarioDescription m -> TExpr Bool -> [ParameterSet] -> [ParameterSet]+missingParameterSets sc expr sets = listDiff target sets+    where target = matchingParameterSets sc expr++-- | Like expandParameterSets but also filter ParameterSet to only those that+-- actually match the TExpr query.+matchingParameterSets :: ScenarioDescription m -> TExpr Bool -> [ParameterSet]+matchingParameterSets sc expr = filter matching allSets+    where matching = matchTExpr' expr sc+          allSets  = expandParameterSets sc expr ++-- | Expands parameters given a TExpr and a ScenarioDescription into a list of+-- parameter spaces (sort of cartesian product of all possible params)+expandParameterSets :: ScenarioDescription m -> TExpr Bool -> [ParameterSet]+expandParameterSets sc expr = paramSets $ expandParamSpace (sParams sc) expr++-- | Shortcut to remove from l1 the items in l2.+-- Implementation transposes objects to sets thus needs the Ord capability.+--+-- e.g. listDiff "abcd" "bcde" = "a"+listDiff :: Ord a => [a] -> [a] -> [a]+listDiff l1 l2 = S.toList (S.fromList l1 `S.difference` S.fromList l2)++-- | Executes a given scenario for a given set of parameters using a given backend.+execute :: (MonadIO m) => Backend m -> ScenarioDescription m -> ParameterSet -> m (Execution m) execute b sc prm = execution   where execution = do             (exec,final) <- bPrepareExecution b sc prm -            status <- runReaderT (runErrorT (go exec `catchError` recover exec)) (b, exec)+            status <- liftM fst $ runReaderT (runStateT (runErrorT (go exec `catchError` recover exec)) emptyEnv) (b, exec)             let exec' = either (\_ -> exec {eStatus = Failure}) (\_ -> exec {eStatus = Success}) status             bFinalizeExecution b exec' final+            return exec'             where go exec = do                          bSetup b exec                         bRun b exec@@ -24,26 +82,21 @@                         bAnalyze b exec                   recover exec err = bRecover b err exec >> throwError err -executeAnalysis :: (MonadIO m, Functor m) => Backend m -> Execution m -> m (Either AnalysisError ())-executeAnalysis b exec = do-    either rebrandError Right <$> runReaderT (runErrorT (go exec)) (b, exec)-    where go exec = bAnalyze b exec-          rebrandError (ExecutionError str) = Left $ AnalysisError str --executeExhaustive :: (MonadIO m) => Backend m -> ScenarioDescription m -> [m ()]-executeExhaustive b sc = map f $ paramSets $ sParams sc-    where f = execute b sc --executeMissing :: (MonadIO m) => Backend m -> ScenarioDescription m -> [Execution m] -> [m ()]-executeMissing b sc execs = map f $ S.toList (exhaustive `S.difference` existing)-    where successful = filter ((== Success) . eStatus) execs-          exhaustive = S.fromList $ paramSets (sParams sc)-          existing = S.fromList $ map eParamSet successful-          f = execute b sc-+-- | Loads executions of given ScenarioDescription and matching a given query+-- using a specific backend. load :: (MonadIO m) => Backend m -> [ScenarioDescription m] -> TExpr Bool -> m [Execution m] load = bLoad +-- | Remove an execution using a specific backend. remove :: (MonadIO m) => Backend m -> Execution m -> m () remove = bRemove++-- | Runs the analysis hooks.+runAnalyze :: (MonadIO m, Functor m) => Backend m -> Execution m -> m (Either AnalysisError ())+runAnalyze b exec = do+    let status = runReaderT (runStateT (runErrorT (go exec)) emptyEnv) (b, exec)+    (either rebrandError Right) . fst <$> status+    where go exec = bAnalyze b exec+          rebrandError (ExecutionError str) = Left $ AnalysisError str+
Laborantin/CLI.hs view
@@ -63,12 +63,42 @@                                       ]  -data Labor = Run        { scenarii   :: [String] , params :: [String] , continue :: Bool , matcher :: [String]} -           | Describe   { scenarii   :: [String] } -           | Find       { scenarii   :: [String] , params :: [String] , successful :: Bool, today :: Bool, matcher :: [String]} -           | Analyze    { scenarii   :: [String] , params :: [String] , successful :: Bool , today :: Bool, matcher :: [String]} -           | Rm         { scenarii   :: [String] , params :: [String] , successful :: Bool , today :: Bool, matcher :: [String]} -           | Query      { scenarii   :: [String] , params :: [String] , successful :: Bool , today :: Bool, matcher :: [String]} +data Labor = Run        { scenarii   :: [String]+                        , params :: [String]+                        , matcher :: [String]+                        }+           | Continue   { scenarii   :: [String]+                        , params :: [String]+                        , matcher :: [String]+                        , successful :: Bool+                        , today :: Bool, matcher :: [String]+                        }+           | Describe   { scenarii   :: [String]+                        }+           | Find       { scenarii   :: [String]+                        , params :: [String]+                        , successful :: Bool+                        , today :: Bool, matcher :: [String]+                        }+           | Analyze    { scenarii   :: [String]+                        , params :: [String]+                        , successful :: Bool+                        , today :: Bool, matcher :: [String]+                        }+           | Rm         { scenarii   :: [String]+                        , params :: [String]+                        , successful :: Bool+                        , today :: Bool, matcher :: [String]+                        }+           | Query      { scenarii   :: [String]+                        , params :: [String]+                        , successful :: Bool+                        , today :: Bool, matcher :: [String]+                        }+           | Params     { scenarii   :: [String]+                        , params :: [String]+                        , matcher :: [String]+                        }     deriving (Typeable, Data, Show, Eq)  instance Attributes Labor where@@ -83,12 +113,6 @@                                     , Help "Restrict a parameter, format name=type:val."                                     , ArgHelp "PARAMS"                                     ]-                    ,   continue %> [ Short "c"-                                    , Long ["continue"]-                                    , Default False-                                    , Invertible True-                                    , Help "Continue execution (skip known)"-                                    ]                     ,   successful %> [ Long ["successful"]                                     , Help "Successful only"                                     , Invertible True@@ -163,15 +187,17 @@     now <- getCurrentTime     case labor of         (Describe scii)                 -> forM_ xs' (T.putStrLn . describeScenario)-        Find {}                         -> do (execs,_) <- runEnvIO (loadMatching now)+        Find {}                         -> do execs <- runEnvIO (loadMatching now)                                               mapM_ (T.putStrLn . describeExecution) execs-        (Rm {})                         -> runSc (loadAndRemove now)-        (Run { continue = False })      -> mapM_ runSc allExecs-        (Run { continue = True })       -> do (execs,_) <- runEnvIO (loadMatching now)-                                              mapM_ runSc (remainingExecs execs)+        Rm {}                           -> runSc (loadAndRemove now)+        Run {}                          -> mapM_ runSc (targetExecs [])+        Continue {}                     -> do execs <- runEnvIO (loadMatching now)+                                              mapM_ runSc (targetExecs execs)         Analyze {}                      -> runSc (loadAndAnalyze now)         Query {}                        -> do let expr = simplifyOneBoolLevel $ query now-                                              putStrLn $ showTExpr expr+                                              print expr+        Params {}                       -> do let expr = simplifyOneBoolLevel expander +                                              print expr          where xs'           = filterDescriptions (ScenarioName $ map T.pack $ scenarii labor) xs               matcherUExprs = rights $ map parseUExpr (matcher labor)@@ -182,9 +208,9 @@               dateTExpr tst = todayToTExpr (today labor) (tst {utctDayTime = 0})               query tst     = conjunctionQueries (paramsTExpr:scenarioTExpr:statusTExpr:dateTExpr':matcherTExprs)                               where dateTExpr' = dateTExpr tst+              expander      = conjunctionQueries (paramsTExpr:scenarioTExpr:matcherTExprs)               runSc         = void . runEnvIO               loadMatching  tst = load defaultBackend xs' (query tst)               loadAndRemove  tst = loadMatching tst >>= mapM (remove defaultBackend)-              loadAndAnalyze tst = loadMatching tst >>= mapM (executeAnalysis defaultBackend)-              allExecs      = concatMap (executeExhaustive defaultBackend) xs'-              remainingExecs execs = concatMap (\sc -> executeMissing defaultBackend sc execs) xs'+              loadAndAnalyze tst = loadMatching tst >>= mapM (runAnalyze defaultBackend)+              targetExecs existing = concatMap (prepare defaultBackend expander existing) xs'
Laborantin/DSL.hs view
@@ -5,6 +5,8 @@         scenario     ,   describe     ,   parameter+    ,   require+    ,   requireTExpr     ,   dependency     ,   check     ,   resolve@@ -30,13 +32,19 @@ ) where  import qualified Data.Map as M+import Data.List (partition, nubBy)+import Laborantin import Laborantin.Types+import Laborantin.Query+import Laborantin.Query.Parse+import Laborantin.Query.Interpret import Control.Monad.State import Control.Monad.Reader import Control.Monad.Error import Control.Applicative import Data.Dynamic-import Data.Text (Text, unpack)+import Data.Text (Text, pack, unpack)+import qualified Data.Text as T  class Describable a where   changeDescription :: Text -> a -> a@@ -53,7 +61,7 @@ -- | DSL entry point to build a 'ScenarioDescription'. scenario :: Text -> State (ScenarioDescription m) () -> ScenarioDescription m scenario name f = execState f sc0-  where sc0 = SDesc name "" M.empty M.empty Nothing []+  where sc0 = SDesc name "" M.empty M.empty Nothing [] (B False)  -- | Attach a description to the 'Parameter' / 'Scnario' describe :: Describable a => Text -> State a ()@@ -69,9 +77,11 @@ -- | DSL entry point to build a 'Dependency a' within a scenario. dependency :: (Monad m) => Text -> State (Dependency m) () -> State (ScenarioDescription m) () dependency name f = modify (addDep dep)-  where addDep v sc0 = sc0 { sDeps = v:(sDeps sc0)}+  where addDep v sc0 = sc0 { sDeps = v:(sDeps sc0) }         dep = execState f dep0-              where dep0 = Dep name "" (const (return True)) (const (return ()))+              where dep0 = Dep name "" checkF solveF+                    checkF = const (return True)+                    solveF = return . fst  -- | Set verification action for the dependency check :: (Execution m -> m Bool) -> State (Dependency m) ()@@ -80,7 +90,7 @@   put $ dep0 { dCheck = f }  -- | Set resolution action for the dependency-resolve :: (Execution m -> m ()) -> State (Dependency m) ()+resolve :: ((Execution m, Backend m) -> m (Execution m)) -> State (Dependency m) () resolve f = do   dep0 <- get   put $ dep0 { dSolve = f }@@ -132,6 +142,32 @@ appendHook :: Text -> Step m () -> State (ScenarioDescription m) () appendHook name f = modify (addHook name $ Action f)   where addHook k v sc0 = sc0 { sHooks = M.insert k v (sHooks sc0) }++-- | Defines the TExpr Bool to load ancestor+requireTExpr :: (MonadIO m, Monad m) => ScenarioDescription m -> TExpr Bool -> State (ScenarioDescription m) ()+requireTExpr sc query = do+    let depName = T.concat [(sName sc),  " ~> ", (pack $ show query)]+    modify (\sc0 -> sc0 {sQuery = query})+    dependency depName $ do+        describe "auto-generated dependency for `require` statement"+        check $ \exec -> do+            let ancestors = filter ((sName sc ==) . sName . eScenario) (eAncestors exec)+            let existing = map eParamSet ancestors+            let missing = missingParameterSets sc query existing+            return (null $ missing)+        resolve $ \(exec,backend) -> do+            storedAncestors <- load backend [sc] query+            let (execAncestors, otherAncestors) = partition ((sName sc ==) . sName . eScenario) (eAncestors exec)+            let allAncestors = nubBy (samePath) (storedAncestors ++ execAncestors)+            newAncestors <- sequence $ prepare backend query allAncestors sc+            return (exec { eAncestors = newAncestors ++ allAncestors })  +            where samePath e1 e2 = ePath e1 == ePath e2++-- | Defines the TExpr Bool to load ancestor+require :: (MonadIO m, Monad m) => ScenarioDescription m -> Text -> State (ScenarioDescription m) ()+require sc txt = requireTExpr sc query+    where query = either (const deflt) (toTExpr deflt) (parseUExpr (unpack txt))+          deflt = (B True)  -- | Returns a 'Result' object for the given name. --
Laborantin/Implementation.hs view
@@ -5,8 +5,10 @@     , defaultBackend     , defaultResult     , defaultLog+    , liftIO ) where +import Control.Monad.IO.Class (liftIO) import qualified Data.Map as M import Data.Text (Text) import qualified Data.Text as T@@ -26,6 +28,7 @@ import Data.UUID import System.Directory import System.Random+import System.IO.Error import System.Log.Logger import System.Log.Handler (close) import System.Log.Handler.Simple@@ -34,11 +37,11 @@  -- | Default monad for 'defaultBackend'. --   EnvIO carries a 'DynEnv' in a state and allows you to perform IO actions.-type EnvIO = (StateT DynEnv IO)+type EnvIO = IO  -- | Execute an EnvIO action in IO.-runEnvIO :: EnvIO a -> IO (a,DynEnv)-runEnvIO m = runStateT m M.empty+runEnvIO :: IO a -> IO a+runEnvIO = id  instance ToJSON ParameterValue where     toJSON (StringParam str) = object ["type" .= ("string"::Text), "val" .= str]@@ -54,7 +57,7 @@                                                     , "params" .= params                                                     , "path" .= path                                                     , "status" .= status-                                                    , "ancestors" .= (map toJSON es)+                                                    , "ancestors" .= ancestors                                                     , "timestamps" .= tsts                                                     ]                                               where ancestors = map f es@@ -103,13 +106,13 @@                             finalizer exec                             now <- liftIO $ getCurrentTime                             let exec' = updateCompletionTime exec now-                            liftIO . putStrLn $ "execution finished\n"+                            bPrintT $ "execution finished\n"                             liftIO $ BSL.writeFile (rundir ++ "/execution.json") (encode exec')                             where rundir = ePath exec         setup             = callHooks "setup" . eScenario         run               = callHooks "run" . eScenario         teardown          = callHooks "teardown" . eScenario-        analyze exec      = liftIO (T.putStrLn $ advertise exec) >> callHooks "analyze" (eScenario exec)+        analyze exec      = callHooks "analyze" (eScenario exec)         recover err exec  = unAction (doRecover err)                             where doRecover = fromMaybe (\_ -> Action $ return ()) (sRecoveryAction $ eScenario exec)          result exec       = return . defaultResult exec@@ -126,20 +129,28 @@  advertise :: Execution m -> Text advertise exec = T.pack $ unlines [ "scenario: " ++ (show . sName . eScenario) exec-                         , "rundir: " ++ ePath exec-                         , "json-params: " ++ (C.unpack . encode . eParamSet) exec+                         , "         rundir: " ++ ePath exec+                         , "         json-params: " ++ (C.unpack . encode . eParamSet) exec                          ] +bPrint :: (MonadIO m, Show a) => a -> m ()+bPrint = liftIO . putStrLn . ("backend> " ++) . show++bPrintT :: (MonadIO m) => Text -> m ()+bPrintT = liftIO . T.putStrLn . (T.append "backend> ")+ prepareNewScenario :: ScenarioDescription EnvIO -> ParameterSet -> EnvIO (Execution EnvIO,Finalizer EnvIO) prepareNewScenario  sc params = do+    bPrint $ T.append "preparing " (sName sc)     (now,uuid) <- liftIO $ do                 now <- getCurrentTime                 id <- randomIO :: IO UUID                 return (now,id)     let rundir = intercalate "/" [T.unpack (sName sc), show uuid]-    let exec = Exec sc params rundir Running [] (now,now)-    liftIO $ print "resolving dependencies"-    resolveDependencies exec+    let newExec = Exec sc params rundir Running [] (now,now)+    bPrint "resolving dependencies"+    exec <- resolveDependencies newExec+    bPrintT $ advertise exec     handles <- liftIO $ do         createDirectoryIfMissing True rundir         BSL.writeFile (rundir ++ "/execution.json") (encode exec)@@ -147,36 +158,52 @@         h1 <- fileHandler (rundir ++ "/execution-log.txt") DEBUG         h2 <- log4jFileHandler (rundir ++ "/execution-log.xml") DEBUG         forM_ [h1,h2] (updateGlobalLogger (loggerName exec) . addHandler)-        T.putStrLn $ advertise exec         return [h1,h2]     return (exec, \_ -> liftIO $ forM_ handles close) -resolveDependencies :: Execution EnvIO -> EnvIO ()+resolveDependencies :: Execution EnvIO -> EnvIO (Execution EnvIO) resolveDependencies exec = do-    pending <- getPendingDeps exec (sDeps $ eScenario exec)+    pending <- getPendingDeps exec deps      resolveDependencies' exec [] pending+    where deps = sDeps $ eScenario exec -resolveDependencies' :: Execution EnvIO -> [Dependency EnvIO] -> [Dependency EnvIO] -> EnvIO ()-resolveDependencies' exec _ []   = return ()-resolveDependencies' exec attempted trying -  | all (\d -> elem d attempted) trying = error "cannot solve dependencies"-  | otherwise = do-    mapM (flip dSolve exec) trying-    pending <- getPendingDeps exec trying-    resolveDependencies' exec (trying ++ attempted) pending+resolveDependencies' :: Execution EnvIO -> [Dependency EnvIO] -> [Dependency EnvIO] -> EnvIO (Execution EnvIO)+resolveDependencies' exec [] []                = return exec+resolveDependencies' exec failed []            = error "cannot solve dependencies"+resolveDependencies' exec failed (dep:pending) = do+    bPrint $ "trying to solve " ++ (T.unpack $ dName dep)+    exec2 <- dSolve dep (exec, defaultBackend)+    success <- dCheck dep exec2 +    case success of+        True -> do+            bPrint $ "successfully solved " ++ (T.unpack $ dName dep)+            resolveDependencies' exec2 [] (pending ++ failed)+        False -> do+            bPrint $ "failed to solve " ++ (T.unpack $ dName dep)+            resolveDependencies' exec2 failed pending -getPendingDeps exec deps = map fst . filter (not . snd). zip deps <$> mapM (flip dCheck exec) deps+-- | Evaluates and returns, for an execution, the list of failing dependencies +--+getPendingDeps :: (Functor m, Monad m, MonadIO m) => Execution m -> [Dependency m] -> m [Dependency m]+getPendingDeps exec deps = keepFailedChecks <$> mapM checkDep deps+    where keepFailedChecks = map fst . filter (not . snd). zip deps +          checkDep dep = do+            bPrintT $ T.append "checking " (dName dep)+            dCheck dep exec   loadExisting :: [ScenarioDescription EnvIO] -> TExpr Bool -> EnvIO [Execution EnvIO] loadExisting scs qexpr = do     concat <$> mapM f scs     where f :: ScenarioDescription EnvIO -> EnvIO [Execution EnvIO]           f sc = do-            paths <- map ((name ++ "/") ++) . filter notDot <$> liftIO (getDirectoryContents name)+            paths <- map ((name ++ "/") ++) . filter notDot <$> liftIO (getDirectoryContents' name)             allExecs <- mapM (loadOne sc scs) paths             return $ filter (matchTExpr qexpr) allExecs             where notDot dirname = take 1 dirname /= "."                   name = T.unpack $ sName sc++                  getDirectoryContents' dir = catchIOError (getDirectoryContents dir)+                                                           (\e -> if isDoesNotExistError e then return [] else ioError e)  loadOne :: ScenarioDescription EnvIO -> [ScenarioDescription EnvIO] -> FilePath -> EnvIO (Execution EnvIO) loadOne sc scs path = do
Laborantin/Query.hs view
@@ -1,12 +1,13 @@ {-# LANGUAGE GADTs #-} {-# LANGUAGE OverloadedStrings #-} -module Laborantin.Query (matchTExpr, simplifyOneBoolLevel, showTExpr) where+module Laborantin.Query (matchTExpr, matchTExpr', simplifyOneBoolLevel, expandParamSpace) where  import Laborantin.Types import qualified Data.Map as M import Control.Applicative ((<$>),(<*>)) import Data.Text (Text)+import qualified Data.Map as M import qualified Data.Text as T  type Param = Maybe ParameterValue@@ -23,6 +24,10 @@ simplifyOneBoolLevel (Or a b)          = (Or (simplifyOneBoolLevel a) (simplifyOneBoolLevel b)) simplifyOneBoolLevel e                 = e +matchTExpr' :: TExpr Bool -> ScenarioDescription m -> ParameterSet -> Bool+matchTExpr' expr sc params = matchTExpr expr (Exec sc params "" Success [] (epoch, epoch)) +    where epoch = error "should not evaluated time"+ matchTExpr :: TExpr Bool -> Execution m -> Bool matchTExpr e q = match' (evalExpr q e)     where match' (Right True) = True@@ -73,55 +78,42 @@ coerceNumberParam name (Just (NumberParam r)) = Right r coerceNumberParam name _ = Left (EvalError $ "could not coerce "++ T.unpack name ++" to number") -showTExpr :: TExpr a -> String-showTExpr (N x)             = show x-showTExpr (B x)             = show x-showTExpr (S x)             = show x-showTExpr (L x)             = show x-showTExpr (T x)             = show x-showTExpr (Not x)           = "! " ++ "(" ++ showTExpr x ++ ")"-showTExpr (And e1 e2)       = "(" ++ showTExpr e1 ++ " && " ++ showTExpr e2 ++ ")"-showTExpr (Or e1 e2)        = "(" ++ showTExpr e1 ++ " || " ++ showTExpr e2 ++ ")"-showTExpr (Contains e1 e2)  = "(" ++ showTExpr e1 ++ " in " ++ showTExpr e2 ++ ")"-showTExpr (Gt e1 e2)        = "(" ++ showTExpr e1 ++ " >  " ++ showTExpr e2 ++ ")"-showTExpr (Eq e1 e2)        = "(" ++ showTExpr e1 ++ " == " ++ showTExpr e2 ++ ")"-showTExpr (Plus e1 e2)      = "(" ++ showTExpr e1 ++ " + " ++ showTExpr e2 ++ ")"-showTExpr (Times e1 e2)     = "(" ++ showTExpr e1 ++ " * " ++ showTExpr e2 ++ ")"-showTExpr ScName            = "@sc.name"-showTExpr ScStatus          = "@sc.status"-showTExpr ScTimestamp       = "@sc.timestamp"-showTExpr (ScParam key)     = "@sc.param:" ++ show key-showTExpr (SCoerce x)       = showTExpr x-showTExpr (NCoerce x)       = showTExpr x-showTExpr (SilentSCoerce x) = showTExpr x-showTExpr (SilentNCoerce x) = showTExpr x-showTExpr (TBind  str f x)  = "(" ++ str ++ " -> (" ++ showTExpr x ++ "))"--instance (Show (TExpr a)) where-    show = showTExpr+-- | Expands a ParameterSpace to all values that could match a TExpr Bool.+--+-- Currently only supports countably finite expansions of parameters. +-- That is TExpr Bool such as (@sc.param "param" > 32) are ignored.+-- Instead (@sc.param "param" in ["foo", "bar"]) gets expanded to ("param", [StringParam "foo", StringParam "bar"])+-- Supported expensions are  And / Or / Eq / Contains.+--+-- The idea is that you can generate a list of Execution to run by first+-- expanding all possible points in the ParameterSpace modified by the TExpr,+-- and then filter these possible points using a same TExpr.+--+expandParamSpace :: ParameterSpace -> TExpr Bool -> ParameterSpace+expandParamSpace params query = case query of+    (Or expr1 expr2) -> mergeParamSpaces ps1 ps2+        where ps1 = expand expr1+              ps2 = expand expr2 +    (And expr1 expr2)  -> mergeParamSpaces ps1 ps2+        where ps1 = expand expr1+              ps2 = expand expr2 +    (Eq (SCoerce (ScParam key)) expr)               -> update key (toParamValues expr)+    (Eq (NCoerce (ScParam key)) expr)               -> update key (toParamValues expr)+    (Eq (SilentSCoerce (ScParam key)) expr)         -> update key (toParamValues expr)+    (Eq (SilentNCoerce (ScParam key)) expr)         -> update key (toParamValues expr)+    (Contains (SCoerce (ScParam key)) expr)         -> update key (toParamValues expr)+    (Contains (NCoerce (ScParam key)) expr)         -> update key (toParamValues expr)+    (Contains (SilentSCoerce (ScParam key)) expr)   -> update key (toParamValues expr)+    (Contains (SilentNCoerce (ScParam key)) expr)   -> update key (toParamValues expr)+    _   -> params -showUExpr :: UExpr -> String-showUExpr (UN x) = show x-showUExpr (UB x) = show x-showUExpr (US x) = show x-showUExpr (UL x) = show x-showUExpr (UT x) = show x-showUExpr (UNot x)            = "! " ++ "(" ++ showUExpr x ++ ")"-showUExpr (UAnd e1 e2)        = "(" ++ showUExpr e1 ++ " and " ++ showUExpr e2 ++ ")"-showUExpr (UOr e1 e2)         = "(" ++ showUExpr e1 ++ " or " ++ showUExpr e2 ++ ")"-showUExpr (UContains e1 e2)   = "(" ++ showUExpr e1 ++ " in " ++ showUExpr e2 ++ ")"-showUExpr (UGt e1 e2)         = "(" ++ showUExpr e1 ++ " > " ++ showUExpr e2 ++ ")"-showUExpr (UGte  e1 e2)       = "(" ++ showUExpr e1 ++ " >= " ++ showUExpr e2 ++ ")"-showUExpr (ULt e1 e2)         = "(" ++ showUExpr e1 ++ " < " ++ showUExpr e2 ++ ")"-showUExpr (ULte e1 e2)        = "(" ++ showUExpr e1 ++ " <= " ++ showUExpr e2 ++ ")"-showUExpr (UEq e1 e2)         = "(" ++ showUExpr e1 ++ " == " ++ showUExpr e2 ++ ")"-showUExpr (UPlus e1 e2)       = "(" ++ showUExpr e1 ++ " + " ++ showUExpr e2 ++ ")"-showUExpr (UMinus e1 e2)      = "(" ++ showUExpr e1 ++ " - " ++ showUExpr e2 ++ ")"-showUExpr (UTimes e1 e2)      = "(" ++ showUExpr e1 ++ " * " ++ showUExpr e2 ++ ")"-showUExpr (UDiv  e1 e2)       = "(" ++ showUExpr e1 ++ " / " ++ showUExpr e2 ++ ")"-showUExpr UScName          = "@sc.name"-showUExpr UScStatus        = "@sc.status"-showUExpr (UScParam key)   = "@sc.param:" ++ show key+    where update = updateParam params+          expand = expandParamSpace params -instance (Show UExpr) where-    show = showUExpr+-- | Interprets a `TExpr a` into a list of ParameterValue when it makes sense (i.e.,+-- on TExpr String / TExpr Rational / TExpr [String] / TExpr [Rational] )+toParamValues :: TExpr a -> [ParameterValue]+toParamValues (N x) = [NumberParam x]+toParamValues (S x) = [StringParam x]+toParamValues (L x) = concatMap toParamValues x+toParamValues _     = []
Laborantin/Query/Interpret.hs view
@@ -26,7 +26,7 @@ data ATExpr = forall a . TExpr a ::: TTyp a  instance Show ATExpr where-    show (expr ::: ty) = showTExpr expr+    show (expr ::: ty) = show expr  toTExpr :: TExpr Bool -> UExpr -> (TExpr Bool) toTExpr expr = fromMaybe expr . toTExpr'@@ -40,6 +40,7 @@ interpret :: UExpr -> Either IError ATExpr interpret UScName       = Right (ScName ::: TTString) interpret UScStatus     = Right (ScStatus ::: TTString)+interpret UScTimestamp  = Right (ScTimestamp ::: TTUTCTime) interpret (UScParam a)  = Right (ScParam a ::: TTParam) interpret (UB a)        = Right (B a ::: TTBool) interpret (UT a)        = Right (T a ::: TTUTCTime)@@ -49,52 +50,54 @@     vals <- ((,) <$> interpret a <*> interpret b)     case vals of         (x ::: TTBool, y ::: TTBool) -> Right (And x y ::: TTBool)-        _   -> error "typing"+        _   -> error "expecting 'boolean' for And clause" interpret (UOr a b)   = do     vals <- ((,) <$> interpret a <*> interpret b)     case vals of         (x ::: TTBool, y ::: TTBool) -> Right (Or x y ::: TTBool)-        _   -> error "typing"+        _   -> error "expecting 'boolean' for Or clause" interpret (UPlus a b)   = do     vals <- ((,) <$> interpret a <*> interpret b)     case vals of         (x ::: TTNum, y ::: TTNum) -> Right (Plus x y ::: TTNum)-        _   -> error "typing"+        _   -> error "expecting 'number' for Plus operator" interpret (UMinus a b) = do     vals <- ((,) <$> interpret a <*> interpret b)     case vals of         (x ::: TTNum, y ::: TTNum) -> Right (Times x (TBind "0 - x" (\n -> N (0 - n)) y) ::: TTNum)-        _   -> error "typing"+        _   -> error "expecting 'number' for Minus operator" interpret (UTimes a b) = do     vals <- ((,) <$> interpret a <*> interpret b)     case vals of         (x ::: TTNum, y ::: TTNum) -> Right (Times x y ::: TTNum)-        _   -> error "typing"+        _   -> error "expecting 'number' for Times operator" interpret (UDiv a b) = do     vals <- ((,) <$> interpret a <*> interpret b)     case vals of         (x ::: TTNum, y ::: TTNum) -> Right (Times x (TBind "1/x" (\n -> N (1/n)) y) ::: TTNum)-        _   -> error "typing"+        _   -> error "expecting 'number' for Div operator" interpret (UEq a b) = do     vals <- ((,) <$> interpret a <*> interpret b)     case vals of-        (x ::: TTNum, y ::: TTNum)       -> Right (Eq x y ::: TTBool)-        (x ::: TTString, y ::: TTString) -> Right (Eq x y ::: TTBool)-        (x ::: TTBool, y ::: TTBool)     -> Right (Eq x y ::: TTBool)-        (x ::: TTParam, y ::: TTString)  -> Right (Eq (SCoerce x) y ::: TTBool)-        (x ::: TTString, y ::: TTParam)  -> Right (Eq x (SCoerce y) ::: TTBool)-        (x ::: TTParam, y ::: TTNum)     -> Right (Eq (NCoerce x) y ::: TTBool)-        (x ::: TTNum, y ::: TTParam)     -> Right (Eq x (NCoerce y) ::: TTBool)-        (x ::: TTParam, y ::: TTParam)   -> Right (Eq x y ::: TTBool)-        _   -> error "typing"+        (x ::: TTNum, y ::: TTNum)          -> Right (Eq x y ::: TTBool)+        (x ::: TTString, y ::: TTString)    -> Right (Eq x y ::: TTBool)+        (x ::: TTBool, y ::: TTBool)        -> Right (Eq x y ::: TTBool)+        (x ::: TTUTCTime, y ::: TTUTCTime)  -> Right (Eq x y ::: TTBool)+        (x ::: TTParam, y ::: TTString)     -> Right (Eq (SCoerce x) y ::: TTBool)+        (x ::: TTString, y ::: TTParam)     -> Right (Eq x (SCoerce y) ::: TTBool)+        (x ::: TTParam, y ::: TTNum)        -> Right (Eq (NCoerce x) y ::: TTBool)+        (x ::: TTNum, y ::: TTParam)        -> Right (Eq x (NCoerce y) ::: TTBool)+        (x ::: TTParam, y ::: TTParam)      -> Right (Eq x y ::: TTBool)+        _   -> error "type mismatch for equality check" interpret (UGt a b) = do     vals <- ((,) <$> interpret a <*> interpret b)     case vals of-        (x ::: TTNum, y ::: TTNum)       -> Right (Gt x y ::: TTBool)-        (x ::: TTParam, y ::: TTNum)     -> Right (Gt (NCoerce x) y ::: TTBool)-        (x ::: TTNum, y ::: TTParam)     -> Right (Gt x (NCoerce y) ::: TTBool)-        (x ::: TTParam, y ::: TTParam)   -> Right (Gt (NCoerce x) (NCoerce y) ::: TTBool)-        _   -> error "typing error"+        (x ::: TTNum, y ::: TTNum)          -> Right (Gt x y ::: TTBool)+        (x ::: TTUTCTime, y ::: TTUTCTime)  -> Right (Gt x y ::: TTBool)+        (x ::: TTParam, y ::: TTNum)        -> Right (Gt (NCoerce x) y ::: TTBool)+        (x ::: TTNum, y ::: TTParam)        -> Right (Gt x (NCoerce y) ::: TTBool)+        (x ::: TTParam, y ::: TTParam)      -> Right (Gt (NCoerce x) (NCoerce y) ::: TTBool)+        _   -> error "type mismatch for comparison" interpret (UGte a b) = interpret (UOr (UGt a b) (UEq a b)) interpret (ULt a b)  = interpret (UNot (UGte a b)) interpret (ULte a b) = interpret (UNot (UGt a b))@@ -102,11 +105,12 @@     val <- interpret a     case val of         (x ::: TTNum)       -> Right (Contains x (L $ ttnums b) ::: TTBool)+        (x ::: TTUTCTime)   -> Right (Contains x (L $ ttutcs b) ::: TTBool)         (x ::: TTString)    -> Right (Contains x (L $ ttstrs b) ::: TTBool)         (x ::: TTParam)     -> Right ((Or (Contains (SilentSCoerce x) (L $ ttstrs b))                                           (Contains (SilentNCoerce x) (L $ ttnums b)))                                      ::: TTBool)-        _  -> error "interpretation unsupported"+        _  -> error "interpretation unsupported for 'in'" interpret (UL as) = do     -- TODO evaluate non-heterogeneous lists     error "cannot safely evaluate list which may be heterogeneous"@@ -114,7 +118,7 @@     val <- interpret x     case val of         (x ::: TTBool) -> Right (Not x ::: TTBool)-        _   -> error "typing error"+        _  -> error "expecting 'boolean' for Not clause"  ttnums :: UExpr -> [TExpr Rational] ttnums (UL xs) = map (\(UN x) -> N x) (filter match xs)@@ -127,3 +131,9 @@     where match (US _) = True           match _      = False ttstrs _ = []++ttutcs :: UExpr -> [TExpr UTCTime]+ttutcs (UL xs) = map (\(UT x) -> T x) (filter match xs)+    where match (UT _) = True+          match _      = False+ttutcs _ = []
Laborantin/Query/Parse.hs view
@@ -3,12 +3,16 @@ module Laborantin.Query.Parse (expr, parseUExpr) where  import Laborantin.Types (UExpr (..))+import Laborantin.Query import Control.Applicative ((<$>),(<*>),(*>),(<*)) import qualified Data.Text as T import Text.Parsec import Text.Parsec.Char import Text.Parsec.Combinator-import Data.Maybe (fromJust)+import Data.Maybe+import System.Locale+import Data.Time+import Data.Time.Format  parseUExpr :: String -> Either ParseError UExpr parseUExpr = parse expr ""@@ -21,7 +25,6 @@ negated :: Parsec String u UExpr negated = UNot <$> (spaces *> char '!' *> spaces *> expr) - ary :: Parsec String u UExpr ary = UL <$> (char '[' *> (expr `sepBy` (spaces >> char ',' >> spaces)) <* char ']')       @@ -38,20 +41,38 @@ inclOp = binOp [("in",UContains), ("~>",UContains)]  literal :: Parsec String u UExpr-literal = bool <|> (UN <$> number) <|> (US . T.pack <$> quotedString)+literal = try bool <|> date <|> (UN <$> number) <|> (US . T.pack <$> quotedString) +date :: Parsec String u UExpr+date = do+    string "t:"+    str <- quotedString+    let parsed = parseTimeFormats' defaultTimeLocale fmts str+    case parsed of+        [x] -> return $ UT x+        _   -> fail "invalid time format"+    where fmts =    [ iso8601DateFormat Nothing+                    , rfc822DateFormat+                    , iso8601DateFormat (Just "%T")+                    ]++parseTimeFormats' locale fmts str = take 1 . catMaybes $ parseResults+    where parseResults = map (\fmt -> parseTime locale fmt str) fmts+ special :: Parsec String u UExpr-special = try scname <|> try scstatus <|> scparam+special = try scname <|> try scstatus <|> try sctimestamp <|> scparam -scname,scstatus,scparam :: Parsec String u UExpr+scname,scstatus,scparam,sctimestamp :: Parsec String u UExpr scname = string "@sc.name" *> return UScName scstatus = string "@sc.status" *> return UScStatus scparam = UScParam . T.pack <$> (syntax1 <|> syntax2)     where syntax1 = string "@sc.param" *> spaces *> quotedString           syntax2 = char ':' *> plainString+sctimestamp = string "@sc.timestamp" *> return UScTimestamp  quotedString :: Parsec String u String-quotedString = char '"' *> many (noneOf "\"") <* char '"'+quotedString = try (char '"' *> many (noneOf "\"") <* char '"')+               <|> (char '\'' *> many (noneOf "'") <* char '\'')  plainString :: Parsec String u String plainString = many (noneOf " ")
Laborantin/Types.hs view
@@ -1,3 +1,4 @@+{-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE ExistentialQuantification #-} {-# LANGUAGE TupleSections #-} {-# LANGUAGE FlexibleInstances #-}@@ -10,7 +11,11 @@     ,   ParameterValue (..)     ,   ParameterSpace     ,   ParameterSet+    ,   emptyScenario+    ,   emptyParameter     ,   paramSets+    ,   mergeParamSpaces+    ,   updateParam     ,   expandValue     ,   Result (..)     ,   Backend (..)@@ -24,6 +29,7 @@     ,   Step     ,   Action (..)     ,   DynEnv (..)+    ,   emptyEnv     ,   TExpr (..)     ,   UExpr (..)     ,   Dependency (..)@@ -32,12 +38,17 @@ import qualified Data.Map as M import Data.Time (UTCTime) import Control.Monad.Reader+import Control.Monad.State import Control.Monad.Error import Data.Dynamic import Data.Text (Text) import qualified Data.Text as T+import Data.List (nub)  type DynEnv = M.Map Text Dynamic+emptyEnv :: DynEnv+emptyEnv = M.empty+ type ParameterSpace = M.Map Text ParameterDescription data ExecutionError = ExecutionError String     deriving (Show)@@ -49,7 +60,7 @@ instance Error AnalysisError where   noMsg    = AnalysisError "A String Error!"   strMsg   = AnalysisError-type Step m a = ErrorT ExecutionError (ReaderT (Backend m,Execution m) m) a+type Step m a = (ErrorT ExecutionError (StateT DynEnv (ReaderT (Backend m,Execution m) m)) a)  newtype Action m = Action { unAction :: Step m () } @@ -66,14 +77,21 @@   , sHooks  :: M.Map Text (Action m)   , sRecoveryAction :: Maybe (ExecutionError -> Action m)   , sDeps   :: [Dependency m]+  , sQuery  :: TExpr Bool   } deriving (Show) +emptyScenario :: ScenarioDescription m+emptyScenario = SDesc "" "" M.empty M.empty Nothing [] noQuery+ data ParameterDescription = PDesc {     pName   :: Text   , pDesc   :: Text   , pValues :: [ParameterValue]   } deriving (Show,Eq,Ord) +emptyParameter :: ParameterDescription+emptyParameter = PDesc "" "" []+ data ParameterValue = StringParam Text    | NumberParam Rational   | Array [ParameterValue]@@ -106,7 +124,7 @@       dName     :: Text     , dDesc     :: Text     , dCheck    :: Execution m -> m Bool-    , dSolve    :: Execution m -> m ()+    , dSolve    :: (Execution m, Backend m) -> m (Execution m)     }  instance Eq (Dependency m) where@@ -129,6 +147,16 @@           f (k,desc) = concatMap (map (pName desc,) . expandValue) $ pValues desc type Finalizer m = Execution m -> m () +mergeParamSpaces :: ParameterSpace -> ParameterSpace -> ParameterSpace+mergeParamSpaces ps1 ps2 = M.mergeWithKey f id id ps1 ps2+    where f k v1 v2 = Just (v1 { pValues = values })+                        where values = nub $ (pValues v1) ++ (pValues v2)++-- | +updateParam :: ParameterSpace -> Text -> [ParameterValue] -> ParameterSpace+updateParam ps key values = M.updateWithKey f key ps+    where f k param = Just (param {pValues = values})+ data Backend m = Backend {     bName      :: Text   , bPrepareExecution  :: ScenarioDescription m -> ParameterSet -> m (Execution m,Finalizer m)@@ -177,6 +205,36 @@     SilentNCoerce     :: TExpr (Text, Maybe ParameterValue) -> TExpr Rational     TBind      :: String -> (a -> TExpr b) -> TExpr a -> TExpr b +noQuery :: TExpr Bool+noQuery = B False++showTExpr :: TExpr a -> String+showTExpr (N x)             = show x+showTExpr (B x)             = show x+showTExpr (S x)             = show x+showTExpr (L x)             = show x+showTExpr (T x)             = "t:" ++ show x+showTExpr (Not x)           = "! " ++ "(" ++ showTExpr x ++ ")"+showTExpr (And e1 e2)       = "(" ++ showTExpr e1 ++ " && " ++ showTExpr e2 ++ ")"+showTExpr (Or e1 e2)        = "(" ++ showTExpr e1 ++ " || " ++ showTExpr e2 ++ ")"+showTExpr (Contains e1 e2)  = "(" ++ showTExpr e1 ++ " in " ++ showTExpr e2 ++ ")"+showTExpr (Gt e1 e2)        = "(" ++ showTExpr e1 ++ " >  " ++ showTExpr e2 ++ ")"+showTExpr (Eq e1 e2)        = "(" ++ showTExpr e1 ++ " == " ++ showTExpr e2 ++ ")"+showTExpr (Plus e1 e2)      = "(" ++ showTExpr e1 ++ " + " ++ showTExpr e2 ++ ")"+showTExpr (Times e1 e2)     = "(" ++ showTExpr e1 ++ " * " ++ showTExpr e2 ++ ")"+showTExpr ScName            = "@sc.name"+showTExpr ScStatus          = "@sc.status"+showTExpr ScTimestamp       = "@sc.timestamp"+showTExpr (ScParam key)     = "@sc.param:" ++ show key+showTExpr (SCoerce x)       = "str!{"++(showTExpr x)++"}"+showTExpr (NCoerce x)       = "num!{"++(showTExpr )x++"}"+showTExpr (SilentSCoerce x) = "str{"++(showTExpr x)++"}"+showTExpr (SilentNCoerce x) = "num{"++(showTExpr x)++"}"+showTExpr (TBind  str f x)  = "(" ++ str ++ " -> (" ++ showTExpr x ++ "))"++instance (Show (TExpr a)) where+    show = showTExpr+ data UExpr = UN Rational     | UB Bool     | US Text@@ -197,4 +255,32 @@     | UNot UExpr     | UScName     | UScStatus+    | UScTimestamp     | UScParam     Text++showUExpr :: UExpr -> String+showUExpr (UN x) = show x+showUExpr (UB x) = show x+showUExpr (US x) = show x+showUExpr (UL x) = show x+showUExpr (UT x)              = "t:" ++ show x+showUExpr (UNot x)            = "! " ++ "(" ++ showUExpr x ++ ")"+showUExpr (UAnd e1 e2)        = "(" ++ showUExpr e1 ++ " and " ++ showUExpr e2 ++ ")"+showUExpr (UOr e1 e2)         = "(" ++ showUExpr e1 ++ " or " ++ showUExpr e2 ++ ")"+showUExpr (UContains e1 e2)   = "(" ++ showUExpr e1 ++ " in " ++ showUExpr e2 ++ ")"+showUExpr (UGt e1 e2)         = "(" ++ showUExpr e1 ++ " > " ++ showUExpr e2 ++ ")"+showUExpr (UGte  e1 e2)       = "(" ++ showUExpr e1 ++ " >= " ++ showUExpr e2 ++ ")"+showUExpr (ULt e1 e2)         = "(" ++ showUExpr e1 ++ " < " ++ showUExpr e2 ++ ")"+showUExpr (ULte e1 e2)        = "(" ++ showUExpr e1 ++ " <= " ++ showUExpr e2 ++ ")"+showUExpr (UEq e1 e2)         = "(" ++ showUExpr e1 ++ " == " ++ showUExpr e2 ++ ")"+showUExpr (UPlus e1 e2)       = "(" ++ showUExpr e1 ++ " + " ++ showUExpr e2 ++ ")"+showUExpr (UMinus e1 e2)      = "(" ++ showUExpr e1 ++ " - " ++ showUExpr e2 ++ ")"+showUExpr (UTimes e1 e2)      = "(" ++ showUExpr e1 ++ " * " ++ showUExpr e2 ++ ")"+showUExpr (UDiv  e1 e2)       = "(" ++ showUExpr e1 ++ " / " ++ showUExpr e2 ++ ")"+showUExpr UScName          = "@sc.name"+showUExpr UScStatus        = "@sc.status"+showUExpr UScTimestamp     = "@sc.timestamp"+showUExpr (UScParam key)   = "@sc.param:" ++ show key++instance (Show UExpr) where+    show = showUExpr
README.md view
@@ -112,16 +112,8 @@  # Roadmap -For version 1.2--* Syntax to parse clocktime and with --date=today/this-week etc. types of flags-* Print better debug errors on parsing messages-* Function to expand an UExpr (or a TExpr ([(ParameterName,ParameterValue)]))into a ParameterSpace-  - then need a function to extend a ParameterSpace with default values-	ScenarioDescription -> ParameterSpace -> ParameterSpace-* Add a scSelector :: TExpr Bool field to a scenario to load ancestors at prepare-time-  - "require" helper that sets a dep + selection-* exports to propose exported files using "show-exports" command+For version 0.1.4.x -# Annoyances/Bugs-* the "continue" mode doesn't do what it should anymore, don't use it for now+* [improvement] Use './results' as root dir in defaultBackend+* [improvement] Use system locale rather than default locale for time parsing+* [feature] exports to propose exported files using "show-exports" command
examples/main.hs view
@@ -3,22 +3,31 @@  module Main where  -import Laborantin-import Laborantin.Types import Laborantin.DSL+import Laborantin.Types import Laborantin.Implementation import Laborantin.CLI-import Laborantin.Query-import Control.Monad.IO.Class import qualified Data.Text as T  {-   - Example  -} +pong :: ScenarioDescription EnvIO+pong = scenario "pong" $ do+    parameter "foo" $ do+        describe "foo"+        values [str "foo"]+    setup (return ())+    run (return ())+    teardown (return ())+    recover (const (return ()))+    analyze (return ())+ ping :: ScenarioDescription EnvIO ping = scenario "ping" $ do   describe "ping to a remote server"+  require pong "@sc.param 'foo' == 'foo'"    parameter "destination" $ do     describe "a destination server (host or ip)"     values [str "example.com", str "probecraft.net", str "nonexistent"]@@ -28,10 +37,6 @@   parameter "burst-length" $ do     describe "number of back-to-back packets to send"     values [range 1 100 10] -  dependency "dummy" $ do-    describe "always true"-    check (\exec -> return True)-    resolve (\exec -> liftIO (print "resolving"))   setup $ do       setVar "hello" ("world"::String)       dbg "setting up scenario"@@ -47,4 +52,4 @@   recover $ \err -> dbg $ T.append "here we could recover from error: " (T.pack $ show err)   analyze $ liftIO . print $ "analyze action" -main = defaultMain [ping]+main = defaultMain [ping, pong]
laborantin-hs.cabal view
@@ -2,7 +2,7 @@ -- documentation, see http://haskell.org/cabal/users-guide/  name:                laborantin-hs-version:             0.1.2.0+version:             0.1.3.0 synopsis:            an experiment management framework description:              Laborantin is a framework and DSL to run and manage results from scientific@@ -10,7 +10,7 @@     run /offline/ such as benchmark and batch analytics.     .     Writing experiments with Laborantin has at least two advantages over-    rolling your own scripts.  First, Laborantain standardizes the workflow of your+    rolling your own scripts.  First, Laborantin standardizes the workflow of your     experimentations.  There is one-way to describe what a project can do, what     experiments where already run, how to delete files corresponding to a specific     experiment etc.  Second, Laborantin builds on years of experience running@@ -62,7 +62,7 @@   exposed-modules:     Laborantin, Laborantin.DSL, Laborantin.Implementation, Laborantin.Types, Laborantin.CLI, Laborantin.Query   other-modules:       Laborantin.Query.Parse, Laborantin.Query.Interpret   other-extensions:    FlexibleContexts, OverloadedStrings, TupleSections-  build-depends:       base >=4.6 && <4.7, transformers >=0.3 && <0.4, mtl >=2.1 && <2.2, containers >=0.5 && <0.6, text >=0.11 && <0.12, bytestring >=0.10 && <0.11, aeson >=0.6 && <0.7, uuid >=1.2 && <1.3, directory >=1.2 && <1.3, random >=1.0 && <1.1, hslogger >=1.2 && <1.3, cmdlib >= 0.3.5, split >= 0.2.2, time >= 1.4.0.1, parsec >= 3.1.0+  build-depends:       base >=4.6 && <4.7, transformers >=0.3 && <0.4, mtl >=2.1 && <2.2, containers >=0.5 && <0.6, text >=0.11 && <0.12, bytestring >=0.10 && <0.11, aeson >=0.6 && <0.7, uuid >=1.2 && <1.3, directory >=1.2 && <1.3, random >=1.0 && <1.1, hslogger >=1.2 && <1.3, cmdlib >= 0.3.5, split >= 0.2.2, time >= 1.4.0.1, parsec >= 3.1.0, old-locale >=1.0.0.5   -- hs-source-dirs:         default-language:    Haskell2010