packages feed

haskus-utils 1.4 → 1.5

raw patch · 16 files changed

+1113/−499 lines, 16 filesdep +doctestdep +freedep −extradep −file-embeddep ~containersPVP ok

version bump matches the API change (PVP)

Dependencies added: doctest, free

Dependencies removed: extra, file-embed

Dependency ranges changed: containers

API changes (from Hackage documentation)

- Haskus.Utils.Parser: Choice :: Choice a
- Haskus.Utils.Parser: EndOfInput :: ParseError
- Haskus.Utils.Parser: SyntaxError :: ParseError
- Haskus.Utils.Parser: choice :: forall m fs zs. (Monad m, HFoldl (Choice ParseError) (Flow m '[ParseError]) fs (Flow m zs)) => HList fs -> Flow m zs
- Haskus.Utils.Parser: choice' :: forall a m fs zs. (Monad m, HFoldl (Choice a) (Flow m '[a]) fs (Flow m zs)) => HList fs -> Flow m zs
- Haskus.Utils.Parser: data Choice a
- Haskus.Utils.Parser: data ParseError
- Haskus.Utils.Parser: instance (x Data.Type.Equality.~ Haskus.Utils.Variant.OldFlow.Flow m xs, y Data.Type.Equality.~ Haskus.Utils.Variant.OldFlow.Flow m ys, z Data.Type.Equality.~ Haskus.Utils.Variant.OldFlow.Flow m zs, a Haskus.Utils.Variant.:< xs, Haskus.Utils.Variant.LiftVariant ys zs, Haskus.Utils.Variant.LiftVariant (Haskus.Utils.Types.List.Remove a xs) zs, zs Data.Type.Equality.~ Haskus.Utils.Types.List.Union (Haskus.Utils.Types.List.Remove a xs) ys, GHC.Base.Monad m) => Haskus.Utils.HList.Apply (Haskus.Utils.Parser.Choice a) (x, y) z
- Haskus.Utils.Parser: instance GHC.Classes.Eq Haskus.Utils.Parser.ParseError
- Haskus.Utils.Parser: instance GHC.Show.Show Haskus.Utils.Parser.ParseError
- Haskus.Utils.Parser: many :: (zs ~ Remove ParseError xs, Monad m, ParseError :< xs) => Flow m xs -> Flow m '[[V zs]]
- Haskus.Utils.Parser: manyAtLeast :: (zs ~ Remove ParseError xs, Monad m, ParseError :< xs) => Word -> Flow m xs -> Flow m '[[V zs], ParseError]
- Haskus.Utils.Parser: manyAtMost :: (zs ~ Remove ParseError xs, Monad m, ParseError :< xs) => Word -> Flow m xs -> Flow m '[[V zs]]
- Haskus.Utils.Parser: manyAtMost' :: (zs ~ Remove ParseError xs, Monad m, ParseError :< xs) => Word -> Flow m xs -> m [V zs]
- Haskus.Utils.Parser: manyAtMost'' :: ('[x] ~ Remove ParseError xs, Monad m, ParseError :< xs) => Word -> Flow m xs -> m [x]
- Haskus.Utils.Parser: manyBounded :: forall zs xs m. (zs ~ Remove ParseError xs, Monad m, ParseError :<? xs) => Maybe Word -> Maybe Word -> Flow m xs -> Flow m '[[V zs], ParseError]
- Haskus.Utils.Parser: manyTill :: (zs ~ Remove ParseError xs, zs' ~ Remove ParseError ys, Monad m, ParseError :<? xs, ParseError :< ys) => Flow m xs -> Flow m ys -> Flow m '[([V zs], V zs'), ParseError]
- Haskus.Utils.Parser: manyTill' :: (zs ~ Remove ParseError xs, Monad m, ParseError :<? xs, ParseError :< ys) => Flow m xs -> Flow m ys -> Flow m '[[V zs], ParseError]
- Haskus.Utils.Solver: constraintReduce :: (Ord p, Eq p, Eq e) => PredOracle p -> Constraint e p -> Constraint e p
- Haskus.Utils.Solver: instance (GHC.Classes.Ord p, GHC.Classes.Eq e, GHC.Classes.Eq a, GHC.Classes.Eq p) => Haskus.Utils.Solver.Predicated (Haskus.Utils.Solver.Rule e p a)
- Haskus.Utils.Solver: instance GHC.Classes.Eq p => GHC.Classes.Eq (Haskus.Utils.Solver.Constraint e p)
- Haskus.Utils.Solver: instance GHC.Classes.Ord p => GHC.Classes.Ord (Haskus.Utils.Solver.Constraint e p)
- Haskus.Utils.Solver: instance GHC.Show.Show p => GHC.Show.Show (Haskus.Utils.Solver.Constraint e p)
- Haskus.Utils.Solver: mergeRules :: Rule e p a -> Rule e p b -> Rule e p (a, b)
- Haskus.Utils.Solver: orderedNonTerminal :: [(Constraint e p, Rule e p a)] -> Rule e p a
- Haskus.Utils.Solver: simplifyConstraint :: Constraint e p -> Constraint e p
+ Haskus.Utils.Dynamic: [DynEq] :: forall a. (Eq a, Ord a) => TypeRep a -> a -> DynEq
+ Haskus.Utils.Dynamic: data DynEq
+ Haskus.Utils.Dynamic: fromDynEq :: Typeable a => DynEq -> a -> a
+ Haskus.Utils.Dynamic: fromDynEqMaybe :: forall a. Typeable a => DynEq -> Maybe a
+ Haskus.Utils.Dynamic: instance GHC.Classes.Eq Haskus.Utils.Dynamic.DynEq
+ Haskus.Utils.Dynamic: instance GHC.Classes.Ord Haskus.Utils.Dynamic.DynEq
+ Haskus.Utils.Dynamic: toDynEq :: (Typeable a, Eq a, Ord a) => a -> DynEq
+ Haskus.Utils.Flow: (<.<) :: (b -> c) -> (a -> b) -> a -> c
+ Haskus.Utils.Flow: (>.>) :: (a -> b) -> (b -> c) -> a -> c
+ Haskus.Utils.Flow: forLoop :: a -> (a -> Bool) -> (a -> a) -> acc -> (acc -> a -> acc) -> acc
+ Haskus.Utils.Flow: forLoopM_ :: Monad m => a -> (a -> Bool) -> (a -> a) -> (a -> m ()) -> m ()
+ Haskus.Utils.Flow: infixl 9 >.>
+ Haskus.Utils.Flow: infixr 9 <.<
+ Haskus.Utils.Flow: intersperseM_ :: Monad m => m () -> [a] -> (a -> m ()) -> m ()
+ Haskus.Utils.Maths: gcds :: Integral a => [a] -> a
+ Haskus.Utils.Maths: lcms :: Integral a => [a] -> a
+ Haskus.Utils.MonadFlow: CachedMonadFlow :: [MonadTree m a] -> (forall b. m b -> m b) -> CachedMonadFlow m a
+ Haskus.Utils.MonadFlow: MonadEmit :: a -> e -> MonadFlowF m a e
+ Haskus.Utils.MonadFlow: MonadRead :: m v -> (v -> e) -> MonadFlowF m a e
+ Haskus.Utils.MonadFlow: MonadWith :: m v -> (v -> MonadFlow m a ()) -> e -> MonadFlowF m a e
+ Haskus.Utils.MonadFlow: [cachedContext] :: CachedMonadFlow m a -> forall b. m b -> m b
+ Haskus.Utils.MonadFlow: [cachedTree] :: CachedMonadFlow m a -> [MonadTree m a]
+ Haskus.Utils.MonadFlow: cacheMonadFlow :: Monad m => (forall b. m b -> m b) -> MonadFlow m a r -> m (CachedMonadFlow m a)
+ Haskus.Utils.MonadFlow: cacheMonadFlowPure :: (forall b. m b -> m b) -> MonadFlow m a r -> CachedMonadFlow m a
+ Haskus.Utils.MonadFlow: data CachedMonadFlow m a
+ Haskus.Utils.MonadFlow: data MonadFlowF m a e
+ Haskus.Utils.MonadFlow: emitM :: a -> MonadFlow m a ()
+ Haskus.Utils.MonadFlow: instance GHC.Base.Functor (Haskus.Utils.MonadFlow.CachedMonadFlow m)
+ Haskus.Utils.MonadFlow: instance GHC.Base.Functor (Haskus.Utils.MonadFlow.MonadFlowF m a)
+ Haskus.Utils.MonadFlow: monadFlowToMonadTree :: MonadFlow m a r -> [MonadTree m a]
+ Haskus.Utils.MonadFlow: runM :: forall m v a. Eq v => m v -> MonadFlow m a v
+ Haskus.Utils.MonadFlow: runMonadFlow :: Monad m => MonadFlow m a r -> m (r, [a])
+ Haskus.Utils.MonadFlow: type MonadFlow m a r = Free (MonadFlowF m a) r
+ Haskus.Utils.MonadFlow: updateCachedMonadFlow :: Monad m => CachedMonadFlow m a -> m (CachedMonadFlow m a)
+ Haskus.Utils.MonadFlow: updateCachedMonadFlowMaybe :: Monad m => CachedMonadFlow m a -> m (Maybe (CachedMonadFlow m a))
+ Haskus.Utils.MonadFlow: withM :: Eq v => m v -> (v -> MonadFlow m a ()) -> MonadFlow m a ()
+ Haskus.Utils.MonadStream: MonadStream :: MonadVarNE m s (n (MonadStream m n a)) -> MonadStream m n a
+ Haskus.Utils.MonadStream: PureStream :: a -> n (MonadStream m n a) -> MonadStream m n a
+ Haskus.Utils.MonadStream: data MonadStream m n a
+ Haskus.Utils.MonadStream: instance GHC.Base.Functor n => GHC.Base.Functor (Haskus.Utils.MonadStream.MonadStream m n)
+ Haskus.Utils.MonadStream: showMonadStream :: (Foldable n, Show a, Eq (n (MonadStream m n a)), Monoid (n (MonadStream m n a))) => MonadStream m n a -> String
+ Haskus.Utils.MonadStream: showMonadStreams :: (Foldable n, Show a, Eq (n (MonadStream m n a)), Monoid (n (MonadStream m n a))) => n (MonadStream m n a) -> String
+ Haskus.Utils.MonadStream: type MonadList m a = MonadStream m Maybe a
+ Haskus.Utils.MonadStream: type MonadTree m a = MonadStream m [] a
+ Haskus.Utils.MonadStream: updateMonadStream :: (Monad m, Traversable n) => MonadStream m n a -> m (MonadStream m n a)
+ Haskus.Utils.MonadStream: updateMonadStreamMaybe :: (Monad m, Traversable n) => MonadStream m n a -> m (Maybe (MonadStream m n a))
+ Haskus.Utils.MonadStream: updateMonadStreams :: (Monad m, Traversable n) => n (MonadStream m n a) -> m (n (MonadStream m n a))
+ Haskus.Utils.MonadStream: updateMonadStreamsMaybe :: (Monad m, Traversable n) => n (MonadStream m n a) -> m (Maybe (n (MonadStream m n a)))
+ Haskus.Utils.MonadVar: CachedMonadVar :: a -> !s -> !m s -> (s -> a) -> MonadVar m s a
+ Haskus.Utils.MonadVar: MonadVar :: !m s -> (s -> a) -> MonadVar m s a
+ Haskus.Utils.MonadVar: MonadVarNE :: a -> !Maybe s -> !m s -> (s -> a) -> MonadVarNE m s a
+ Haskus.Utils.MonadVar: data MonadVar m s a
+ Haskus.Utils.MonadVar: data MonadVarNE m s a
+ Haskus.Utils.MonadVar: instance GHC.Base.Functor (Haskus.Utils.MonadVar.MonadVar m s)
+ Haskus.Utils.MonadVar: instance GHC.Base.Functor (Haskus.Utils.MonadVar.MonadVarNE m s)
+ Haskus.Utils.MonadVar: updateMonadVar :: (Monad m, Eq s) => MonadVar m s a -> m (MonadVar m s a)
+ Haskus.Utils.MonadVar: updateMonadVarForce :: (Monad m, Eq s) => MonadVar m s a -> m (MonadVar m s a)
+ Haskus.Utils.MonadVar: updateMonadVarMaybe :: (Monad m, Eq s) => MonadVar m s a -> m (Maybe (MonadVar m s a))
+ Haskus.Utils.MonadVar: updateMonadVarNE :: (Monad m, Eq s) => MonadVarNE m s a -> m (MonadVarNE m s a)
+ Haskus.Utils.MonadVar: updateMonadVarNEMaybe :: (Monad m, Eq s) => MonadVarNE m s a -> m (Maybe (MonadVarNE m s a))
+ Haskus.Utils.STM: cloneTChan :: () => TChan a -> STM (TChan a)
+ Haskus.Utils.STM: swapTVarIO :: MonadIO m => TVar a -> a -> m a
+ Haskus.Utils.STM: writeTVarIO :: MonadIO m => TVar a -> a -> m ()
+ Haskus.Utils.STM.SnapVar: data SnapContext
+ Haskus.Utils.STM.SnapVar: data SnapVar a
+ Haskus.Utils.STM.SnapVar: modifySnapVar :: SnapVar a -> (a -> a) -> STM a
+ Haskus.Utils.STM.SnapVar: modifySnapVarIO :: MonadIO m => SnapVar a -> (a -> a) -> m a
+ Haskus.Utils.STM.SnapVar: newSnapContext :: STM SnapContext
+ Haskus.Utils.STM.SnapVar: newSnapContextIO :: MonadIO m => m SnapContext
+ Haskus.Utils.STM.SnapVar: newSnapVar :: SnapContext -> a -> STM (SnapVar a)
+ Haskus.Utils.STM.SnapVar: newSnapVarIO :: MonadIO m => SnapContext -> a -> m (SnapVar a)
+ Haskus.Utils.STM.SnapVar: readSnapVar :: SnapVar a -> STM a
+ Haskus.Utils.STM.SnapVar: readSnapVarIO :: MonadIO m => SnapVar a -> m a
+ Haskus.Utils.STM.SnapVar: readSnapshot :: SnapVar a -> STM a
+ Haskus.Utils.STM.SnapVar: readSnapshotIO :: MonadIO m => SnapVar a -> m a
+ Haskus.Utils.STM.SnapVar: withSnapshot :: MonadIO m => SnapContext -> m r -> m r
+ Haskus.Utils.STM.SnapVar: writeSnapVar :: SnapVar a -> a -> STM ()
+ Haskus.Utils.STM.SnapVar: writeSnapVarIO :: MonadIO m => SnapVar a -> a -> m ()
+ Haskus.Utils.Solver: CErr :: Either String e -> Constraint e p
+ Haskus.Utils.Solver: InvalidPred :: PredState
+ Haskus.Utils.Solver: IsValid :: p -> Constraint e p
+ Haskus.Utils.Solver: OrderedNonTerminal :: [(Constraint e p, Rule e p a)] -> Rule e p a
+ Haskus.Utils.Solver: constraintOptimize :: Constraint e p -> Constraint e p
+ Haskus.Utils.Solver: constraintSimplify :: (Ord p, Eq p, Eq e) => PredOracle p -> Constraint e p -> Constraint e p
+ Haskus.Utils.Solver: instance (GHC.Classes.Eq p, GHC.Classes.Eq e) => GHC.Classes.Eq (Haskus.Utils.Solver.Constraint e p)
+ Haskus.Utils.Solver: instance (GHC.Classes.Ord a, GHC.Classes.Ord p, GHC.Classes.Eq e, GHC.Classes.Eq a, GHC.Classes.Eq p) => Haskus.Utils.Solver.Predicated (Haskus.Utils.Solver.Rule e p a)
+ Haskus.Utils.Solver: instance (GHC.Classes.Ord p, GHC.Classes.Ord e) => GHC.Classes.Ord (Haskus.Utils.Solver.Constraint e p)
+ Haskus.Utils.Solver: instance (GHC.Show.Show p, GHC.Show.Show e) => GHC.Show.Show (Haskus.Utils.Solver.Constraint e p)
+ Haskus.Utils.Solver: instance (Haskus.Utils.Solver.Predicated x, Haskus.Utils.Solver.Predicated y, Haskus.Utils.Solver.PredErr x Data.Type.Equality.~ Haskus.Utils.Solver.PredErr y, Haskus.Utils.Solver.Pred x Data.Type.Equality.~ Haskus.Utils.Solver.Pred y) => Haskus.Utils.Solver.Predicated (x, y)
+ Haskus.Utils.Solver: oracleUnion :: Ord p => PredOracle p -> PredOracle p -> PredOracle p
+ Haskus.Utils.Solver: predAdd :: Ord p => [(p, PredState)] -> PredOracle p -> PredOracle p
+ Haskus.Utils.Solver: predIsInvalid :: Ord p => PredOracle p -> p -> Bool
+ Haskus.Utils.Solver: ruleSimplify :: (Ord p, Eq e) => PredOracle p -> Rule e p a -> Rule e p a
+ Haskus.Utils.Solver: simplifyPredicates :: Predicated a => PredOracle (Pred a) -> a -> a
+ Haskus.Utils.TimedValue: TimedValue :: t -> a -> TimedValue t a
+ Haskus.Utils.TimedValue: data TimedValue t a
+ Haskus.Utils.TimedValue: instance GHC.Classes.Eq t => GHC.Classes.Eq (Haskus.Utils.TimedValue.TimedValue t a)
+ Haskus.Utils.TimedValue: instance GHC.Classes.Ord t => GHC.Classes.Ord (Haskus.Utils.TimedValue.TimedValue t a)
- Haskus.Utils.Solver: class Predicated a where {
+ Haskus.Utils.Solver: class (Ord (Pred a), Ord (PredTerm a)) => Predicated a where {
- Haskus.Utils.Solver: createPredicateTable :: (Ord (Pred a), Eq (Pred a), Eq a, Predicated a, Predicated a, Pred a ~ Pred a) => a -> (PredOracle (Pred a) -> Bool) -> Bool -> Either (PredTerm a) [(PredOracle (Pred a), PredTerm a)]
+ Haskus.Utils.Solver: createPredicateTable :: (Ord (Pred a), Eq (Pred a), Eq a, Predicated a, Predicated a, Pred a ~ Pred a) => a -> (PredOracle (Pred a) -> Bool) -> Either (PredTerm a) [(PredOracle (Pred a), PredTerm a)]
- Haskus.Utils.Solver: getPredicates :: Predicated a => a -> [Pred a]
+ Haskus.Utils.Solver: getPredicates :: Predicated a => a -> Set (Pred a)
- Haskus.Utils.Solver: getTerminals :: Predicated a => a -> [PredTerm a]
+ Haskus.Utils.Solver: getTerminals :: Predicated a => a -> Set (PredTerm a)

Files

haskus-utils.cabal view
@@ -1,5 +1,5 @@ name:                haskus-utils-version:             1.4+version:             1.5 synopsis:            Haskus utility modules license:             BSD3 license-file:        LICENSE@@ -21,11 +21,16 @@ library   exposed-modules:     Haskus.Utils.Solver-    Haskus.Utils.Parser     Haskus.Utils.HArray     Haskus.Utils.MultiState+    Haskus.Utils.Dynamic     Haskus.Utils.Embed     Haskus.Utils.Flow+    Haskus.Utils.Maths+    Haskus.Utils.MonadVar+    Haskus.Utils.MonadFlow+    Haskus.Utils.MonadStream+    Haskus.Utils.TimedValue     Haskus.Utils.STM     Haskus.Utils.STM.TEq     Haskus.Utils.STM.TMap@@ -34,6 +39,7 @@     Haskus.Utils.STM.TTree     Haskus.Utils.STM.Future     Haskus.Utils.STM.TGraph+    Haskus.Utils.STM.SnapVar    other-modules: @@ -50,11 +56,9 @@       ,  transformers              >= 0.4       ,  mtl                       >= 2.2       ,  template-haskell          >= 2.10-      ,  file-embed                >= 0.0.10-      ,  extra                     >= 1.4       ,  hashable                  >= 1.2+      ,  free -  build-tools:    ghc-options:          -Wall   default-language:     Haskell2010   hs-source-dirs:       src/lib@@ -75,3 +79,5 @@       ,  haskus-utils       ,  tasty                   >= 0.11       ,  tasty-quickcheck        >= 0.8+      ,  doctest+      ,  containers
+ src/lib/Haskus/Utils/Dynamic.hs view
@@ -0,0 +1,55 @@+{-# LANGUAGE ScopedTypeVariables #-}+{-# LANGUAGE GADTs #-}++module Haskus.Utils.Dynamic+   ( -- * Dynamic+     module Data.Dynamic+   -- * Dynamic with equality+   , DynEq (..)+   , toDynEq+   , fromDynEq+   , fromDynEqMaybe+   )+where++import Data.Dynamic+import Type.Reflection++-- | Dynamic type with Eq and Ord instance+--+-- Can be used as Map keys for instance+data DynEq where+   DynEq :: forall a. (Eq a, Ord a) => TypeRep a -> a -> DynEq++instance Eq DynEq where+   (DynEq tra a) == (DynEq trb b) = case tra `eqTypeRep` trb of+      Nothing    -> False+      Just HRefl -> a == b++instance Ord DynEq where+   compare (DynEq tra a) (DynEq trb b) = case tra `eqTypeRep` trb of+      Nothing    -> compare (SomeTypeRep tra) (SomeTypeRep trb)+      Just HRefl -> compare a b++-- | Create a DynEq value+--+-- >>> toDynEq (10 :: Int) == toDynEq (12 :: Int)+-- False+-- >>> toDynEq (10 :: Int) <= toDynEq (12 :: Int)+-- True+-- >>> toDynEq (10 :: Int) /= toDynEq "Test"+-- True+toDynEq :: (Typeable a, Eq a, Ord a) => a -> DynEq+toDynEq a = DynEq typeRep a++-- | Get a value from a DynEq or the default one if the type doesn't match+fromDynEq :: Typeable a => DynEq -> a -> a+fromDynEq (DynEq tr a) def = case tr `eqTypeRep` typeOf def of+   Nothing    -> def+   Just HRefl -> a++-- | Get a value from a DynEq if the type matches+fromDynEqMaybe :: forall a. Typeable a => DynEq -> Maybe a+fromDynEqMaybe (DynEq tr a) = case tr `eqTypeRep` (typeRep :: TypeRep a) of+   Nothing    -> Nothing+   Just HRefl -> Just a
src/lib/Haskus/Utils/Embed.hs view
@@ -1,7 +1,6 @@ -- | Embed data into the executable binary module Haskus.Utils.Embed    ( embedBytes-   , module Data.FileEmbed    -- | Raw text quasiquoter    , raw    , rawQ@@ -10,59 +9,57 @@  import Language.Haskell.TH import Language.Haskell.TH.Quote-import Data.FileEmbed import Data.Word  -- | Embed bytes in a C array, return an Addr# embedBytes :: [Word8] -> Q Exp embedBytes bs = return $ LitE (StringPrimL bs) +----------------------------------------------------------------------+-- Raw text quasiquoter (adapted from raw-strings-qq package (BSD3)+----------------------------------------------------------------------  -{-| Adapted from the raw-strings-qq package (BSD3)--A quasiquoter for raw string literals - that is, string literals that don't-recognise the standard escape sequences (such as @\'\\n\'@). Basically, they-make your code more readable by freeing you from the responsibility to escape-backslashes. They are useful when working with regular expressions, DOS/Windows-paths and markup languages (such as XML).--Don't forget the @LANGUAGE QuasiQuotes@ pragma if you're using this-module in your code.--Usage:--@-    ghci> :set -XQuasiQuotes-    ghci> import Text.RawString.QQ-    ghci> let s = [raw|\\w+\@[a-zA-Z_]+?\\.[a-zA-Z]{2,3}|]-    ghci> s-    \"\\\\w+\@[a-zA-Z_]+?\\\\.[a-zA-Z]{2,3}\"-    ghci> [raw|C:\\Windows\\SYSTEM|] ++ [raw|\\user32.dll|]-    \"C:\\\\Windows\\\\SYSTEM\\\\user32.dll\"-@--Multiline raw string literals are also supported:--@-    multiline :: String-    multiline = [raw|\<HTML\>-    \<HEAD\>-    \<TITLE\>Auto-generated html formated source\</TITLE\>-    \<META HTTP-EQUIV=\"Content-Type\" CONTENT=\"text/html; charset=windows-1252\"\>-    \</HEAD\>-    \<BODY LINK=\"#0000ff\" VLINK=\"#800080\" BGCOLOR=\"#ffffff\"\>-    \<P\> \</P\>-    \<PRE\>|]-@--Caveat: since the @\"|]\"@ character sequence is used to terminate the-quasiquotation, you can't use it inside the raw string literal. Use 'rawQ' if you-want to embed that character sequence inside the raw string.--}+-- |+--+-- A quasiquoter for raw string literals - that is, string literals that don't+-- recognise the standard escape sequences (such as @\'\\n\'@). Basically, they+-- make your code more readable by freeing you from the responsibility to escape+-- backslashes. They are useful when working with regular expressions, DOS/Windows+-- paths and markup languages (such as XML).+--+-- Don't forget the @LANGUAGE QuasiQuotes@ pragma if you're using this+-- module in your code.+--+-- Usage:+--+-- > :set -XQuasiQuotes+-- > import Haskus.Utils.Embed+-- > let s = [raw|\\w+\@[a-zA-Z_]+?\\.[a-zA-Z]{2,3}|]+-- > s+-- \"\\\\w+\@[a-zA-Z_]+?\\\\.[a-zA-Z]{2,3}\"+-- > [raw|C:\\Windows\\SYSTEM|] ++ [raw|\\user32.dll|]+-- \"C:\\\\Windows\\\\SYSTEM\\\\user32.dll\"+--+-- Multiline raw string literals are also supported:+--+-- @+--     multiline :: String+--     multiline = [raw|\<HTML\>+--     \<HEAD\>+--     \<TITLE\>Auto-generated html formated source\</TITLE\>+--     \<META HTTP-EQUIV=\"Content-Type\" CONTENT=\"text/html; charset=windows-1252\"\>+--     \</HEAD\>+--     \<BODY LINK=\"#0000ff\" VLINK=\"#800080\" BGCOLOR=\"#ffffff\"\>+--     \<P\> \</P\>+--     \<PRE\>|]+-- @+--+-- Caveat: since the @\"|]\"@ character sequence is used to terminate the+-- quasiquotation, you can't use it inside the raw string literal. Use 'rawQ' if you+-- want to embed that character sequence inside the raw string. raw :: QuasiQuoter raw = QuasiQuoter {-    -- Extracted from dead-simple-json.     quoteExp  = return . LitE . StringL . normaliseNewlines,      quotePat  = \_ -> fail "illegal raw string QuasiQuote \@@ -73,21 +70,22 @@                            \(allowed as expression only, used as a declaration)" } -{-| A variant of 'raw' that interprets the @\"|~]\"@ sequence as @\"|]\"@,-@\"|~~]\"@ as @\"|~]\"@ and, in general, @\"|~^n]\"@ as @\"|~^(n-1)]\"@-for n >= 1.--Usage:--@-    ghci> [rawQ||~]|~]|]-    \"|]|]\"-    ghci> [rawQ||~~]|]-    \"|~]\"-    ghci> [rawQ||~~~~]|]-    \"|~~~]\"-@--}+-- | A variant of 'raw' that interprets the @\"|~]\"@ sequence as @\"|]\"@,+-- @\"|~~]\"@ as @\"|~]\"@ and, in general, @\"|~^n]\"@ as @\"|~^(n-1)]\"@+-- for n >= 1.+--+-- Usage:+--+--+-- > [rawQ||~]|~]|]+-- \"|]|]\"+--+-- > [rawQ||~~]|]+-- \"|~]\"+--+-- > [rawQ||~~~~]|]+-- \"|~~~]\"+-- rawQ :: QuasiQuoter rawQ = QuasiQuoter {     quoteExp  = return . LitE . StringL . escape_rQ . normaliseNewlines,
src/lib/Haskus/Utils/Flow.hs view
@@ -1,8 +1,12 @@+{-# LANGUAGE BangPatterns #-}+ -- | Control-flow module Haskus.Utils.Flow    ( MonadIO (..)    , MonadInIO (..)    -- * Basic operators+   , (>.>)+   , (<.<)    , (|>)    , (<|)    , (||>)@@ -34,62 +38,156 @@    , (>=>)    , loopM    , whileM+   , intersperseM_+   , forLoopM_+   , forLoop    -- * Variant based operators-   , module Haskus.Utils.Variant.Flow+   , module Haskus.Utils.Variant.Excepts    -- * Monad transformers    , lift    ) where  import Haskus.Utils.Variant-import Haskus.Utils.Variant.Flow+import Haskus.Utils.Variant.Excepts import Haskus.Utils.Monad import Haskus.Utils.Maybe  import Control.Monad.Trans.Class (lift) +-- $setup+-- >>> :set -XDataKinds+-- >>> :set -XTypeApplications+-- >>> :set -XFlexibleContexts+-- >>> :set -XTypeFamilies++-- | Compose functions+--+-- >>> (+1) >.> (*7) <| 1+-- 14+(>.>) :: (a -> b) -> (b -> c) -> a -> c+f >.> g = \x -> g (f x)++infixl 9 >.>++-- | Compose functions+--+-- >>> (+1) <.< (*7) <| 1+-- 8+(<.<) :: (b -> c) -> (a -> b) -> a -> c+f <.< g = \x -> f (g x)++infixr 9 <.<++ -- | Apply a function+--+-- >>> 5 |> (*2)+-- 10 (|>) :: a -> (a -> b) -> b-{-# INLINE (|>) #-}+{-# INLINABLE (|>) #-} x |> f = f x  infixl 0 |>  -- | Apply a function+--+-- >>> (*2) <| 5+-- 10 (<|) :: (a -> b) -> a -> b-{-# INLINE (<|) #-}+{-# INLINABLE (<|) #-} f <| x = f x  infixr 0 <|  -- | Apply a function in a Functor+--+-- >>> Just 5 ||> (*2)+-- Just 10 (||>) :: Functor f => f a -> (a -> b) -> f b-{-# INLINE (||>) #-}+{-# INLINABLE (||>) #-} x ||> f = fmap f x  infixl 0 ||>  -- | Apply a function in a Functor+--+-- >>> (*2) <|| Just 5+-- Just 10 (<||) :: Functor f => (a -> b) -> f a -> f b-{-# INLINE (<||) #-}+{-# INLINABLE (<||) #-} f <|| x = fmap f x  infixr 0 <||  -- | Apply a function in a Functor+--+-- >>> Just [5] |||> (*2)+-- Just [10] (|||>) :: (Functor f, Functor g) => f (g a) -> (a -> b) -> f (g b)-{-# INLINE (|||>) #-}+{-# INLINABLE (|||>) #-} x |||> f = fmap (fmap f) x  infixl 0 |||>  -- | Apply a function in a Functor+--+-- >>> (*2) <||| Just [5]+-- Just [10]+-- (<|||) :: (Functor f, Functor g) => (a -> b) -> f (g a) -> f (g b)-{-# INLINE (<|||) #-}+{-# INLINABLE (<|||) #-} f <||| x = fmap (fmap f) x  infixr 0 <|||  -- | Composition of catMaybes and forM+-- +-- >>> let f x = if x > 3 then putStrLn "OK" >> return (Just x) else return Nothing+-- >>> forMaybeM [0..5] f+-- OK+-- OK+-- [4,5] forMaybeM :: Monad m => [a] -> (a -> m (Maybe b)) -> m [b] forMaybeM xs f = catMaybes <|| forM xs f++-- | forM_ with interspersed action+--+-- >>> intersperseM_ (putStr ", ") ["1","2","3","4"] putStr+-- 1, 2, 3, 4+intersperseM_ :: Monad m => m () -> [a] -> (a -> m ()) -> m ()+intersperseM_ f as g = go as+   where+      go []     = pure ()+      go [x]    = g x+      go (x:xs) = g x >> f >> go xs++-- | Fast for-loop in a Monad (more efficient than forM_ [0..n] for instance).+--+-- >>> forLoopM_ (0::Word) (<5) (+1) print+-- 0+-- 1+-- 2+-- 3+-- 4+forLoopM_ :: (Monad m) => a -> (a -> Bool) -> (a -> a) -> (a -> m ()) -> m ()+{-# INLINABLE forLoopM_ #-}+forLoopM_ start cond inc f = go start+   where+      go !x | cond x    = f x >> go (inc x)+            | otherwise = return ()+++-- | Fast fort-loop with an accumulated result+--+-- >>> let f acc n = acc ++ (if n == 0 then "" else ", ") ++ show n+-- >>> forLoop (0::Word) (<5) (+1) "" f+-- "0, 1, 2, 3, 4"+forLoop :: a -> (a -> Bool) -> (a -> a) -> acc -> (acc -> a -> acc) -> acc+{-# INLINABLE forLoop #-}+forLoop start cond inc acc0 f = go acc0 start+   where+      go acc !x+         | cond x    = let acc' = f acc x+                       in acc' `seq` go acc' (inc x)+         | otherwise = acc
+ src/lib/Haskus/Utils/Maths.hs view
@@ -0,0 +1,24 @@+module Haskus.Utils.Maths+   ( gcds+   , lcms+   )+where++-- | Return the GCD of a list of integrals+--+-- >>> gcds [2,4,8]+-- 2+gcds :: Integral a => [a] -> a+gcds []     = 1+gcds [0]    = 1+gcds [x]    = x+gcds (x:xs) = foldr gcd x xs++-- | Return the LCM of a list of integrals+--+-- >>> lcms [2,3,5]+-- 30+lcms :: Integral a => [a] -> a+lcms []     = 0+lcms [x]    = x+lcms (x:xs) = foldr lcm x xs
+ src/lib/Haskus/Utils/MonadFlow.hs view
@@ -0,0 +1,117 @@+{-# LANGUAGE ExistentialQuantification #-}+{-# LANGUAGE LambdaCase #-}+{-# LANGUAGE BlockArguments #-}+{-# LANGUAGE RankNTypes #-}+{-# LANGUAGE DeriveFunctor #-}++-- | IO control-flow with cache+module Haskus.Utils.MonadFlow+   ( MonadFlowF (..)+   , MonadFlow+   , runMonadFlow+   , runM+   , withM+   , emitM+   -- * Cached control flow+   , CachedMonadFlow (..)+   , cacheMonadFlow+   , cacheMonadFlowPure+   , updateCachedMonadFlow+   , updateCachedMonadFlowMaybe+   , monadFlowToMonadTree+   )+where++import Haskus.Utils.Flow+import Haskus.Utils.MonadVar+import Haskus.Utils.MonadStream+import Control.Monad.Free++-- | MonadFlow Functor+data MonadFlowF m a e+   = MonadEmit a e                                               -- emit a pure value+   | forall v. Eq v => MonadRead (m v) (v -> e)                  -- read a monadic value and put it in the current scope+   | forall v. Eq v => MonadWith (m v) (v -> MonadFlow m a ()) e -- open a new scope and read a monadic value in it++type MonadFlow m a r = Free (MonadFlowF m a) r++instance Functor (MonadFlowF m a) where+   fmap f = \case+      MonadEmit a e   -> MonadEmit a (f e)+      MonadRead v g   -> MonadRead v (f . g)+      MonadWith v k e -> MonadWith v k (f e)++-- | Run an MonadFlow+runMonadFlow :: Monad m => MonadFlow m a r -> m (r,[a])+runMonadFlow = \case+   Free (MonadWith io f t) -> do+      val <- io+      (_,r1)  <- runMonadFlow (f val)+      (k2,r2) <- runMonadFlow t+      pure (k2, r1 <> r2)+   Free (MonadRead io f)  -> do+      val <- io+      runMonadFlow (f val)+   Free (MonadEmit a t)   -> do+      (k,as) <- runMonadFlow t+      pure (k,a:as)+   Pure k              ->+      pure (k,[])+++-- | Emit a pure value+emitM :: a -> MonadFlow m a ()+emitM a = liftF (MonadEmit a ())++-- | Get a variable in IO+--+-- Use `withM` to clearly limit the variable scope+runM :: forall m v a. (Eq v) => m v -> MonadFlow m a v+runM f = liftF (MonadRead f id)++-- | Read and use an IO variable in a delimited scope+withM :: Eq v => m v -> (v -> MonadFlow m a ()) -> MonadFlow m a ()+withM f g = liftF (MonadWith f g ())++------------------------------------------------+-- Cached control-flow+------------------------------------------------++-- | Cached control-flow+data CachedMonadFlow m a = CachedMonadFlow+   { cachedTree    :: [MonadTree m a]      -- ^ Cached control-flow as an MonadTree+   , cachedContext :: forall b. m b -> m b -- ^ Monadic context when performing an update (e.g. withSnapshot ctx)+   }+   deriving (Functor)++-- | Create a cache from an MonadFlow.+--+-- Execute the MonadFlow once to get cached values+cacheMonadFlow :: Monad m => (forall b. m b -> m b) -> MonadFlow m a r -> m (CachedMonadFlow m a)+cacheMonadFlow ctx cflow = updateCachedMonadFlow (cacheMonadFlowPure ctx cflow)++-- | Create a cache from an MonadFlow.+--+-- This is the pure version: IO dependent nodes may not have any cached value+cacheMonadFlowPure :: (forall b. m b -> m b) -> MonadFlow m a r -> CachedMonadFlow m a+cacheMonadFlowPure ctx f = (CachedMonadFlow (monadFlowToMonadTree f) ctx)++-- | Update a cached MonadFlow+updateCachedMonadFlow :: Monad m => CachedMonadFlow m a -> m (CachedMonadFlow m a)+updateCachedMonadFlow (CachedMonadFlow trees withCtx) = do+   trees' <- withCtx (forM trees updateMonadStream)+   pure (CachedMonadFlow trees' withCtx)++-- | Update a cached MonadFlow+updateCachedMonadFlowMaybe :: Monad m => CachedMonadFlow m a -> m (Maybe (CachedMonadFlow m a))+updateCachedMonadFlowMaybe (CachedMonadFlow trees withCtx) =+   withCtx (updateMonadStreamsMaybe trees)+   |||> (\ts -> CachedMonadFlow ts withCtx)++monadFlowToMonadTree :: MonadFlow m a r -> [MonadTree m a]+monadFlowToMonadTree = \case+   Free (MonadRead io f)   -> [ MonadStream (MonadVarNE [] Nothing io (monadFlowToMonadTree . f)) ]+   Free (MonadWith io f c) -> MonadStream (MonadVarNE [] Nothing io (monadFlowToMonadTree . f)):monadFlowToMonadTree c+   Free (MonadEmit a t)    -> PureStream a []:monadFlowToMonadTree t+   Pure _                  -> []+
+ src/lib/Haskus/Utils/MonadStream.hs view
@@ -0,0 +1,117 @@+{-# LANGUAGE LambdaCase #-}+{-# LANGUAGE DeriveFunctor #-}+{-# LANGUAGE ExistentialQuantification #-}+{-# LANGUAGE StandaloneDeriving #-}+{-# LANGUAGE BlockArguments #-}++-- | Monadic tree with cache+module Haskus.Utils.MonadStream+   ( MonadStream (..)+   , MonadTree+   , MonadList+   , showMonadStream+   , showMonadStreams+   , updateMonadStream+   , updateMonadStreamMaybe+   , updateMonadStreamsMaybe+   , updateMonadStreams+   )+where++import Haskus.Utils.Monad+import Haskus.Utils.MonadVar+import Haskus.Utils.Flow+import Haskus.Utils.Maybe++-- | Monadic stream with cache+--+-- Both the structure and the values may be monadically dependent. The last+-- monadic values read can be stored in a cache.+data MonadStream m n a+   = PureStream a (n (MonadStream m n a))                                   -- ^ Pure stream+   | forall s. Eq s => MonadStream (MonadVarNE m s (n (MonadStream m n a))) -- ^ Monadic stream++deriving instance Functor n => Functor (MonadStream m n)++-- | Monadic rose tree+type MonadTree m a = MonadStream m [] a++-- | Monadic list+type MonadList m a = MonadStream m Maybe a++-- | Pretty-show an MonadStream+showMonadStream ::+   ( Foldable n+   , Show a+   , Eq (n (MonadStream m n a))+   , Monoid (n (MonadStream m n a))+   ) => MonadStream m n a -> String+showMonadStream = go 0+   where+      indent n c      = replicate (2*n) ' ' <> c+      showNode n a ts = indent n "- " <> show a <> "\n" <> concatMap (go (n+1)) ts+      go n = \case+         PureStream a ts                 -> showNode n a ts+         MonadStream (MonadVarNE ts _ _ _)+            | ts == mempty -> indent n "{}\n"+            | otherwise    -> indent n "{\n" <> concatMap (go (n+1)) ts <> indent n "}\n"++-- | Pretty-show some MonadStreams+showMonadStreams ::+   ( Foldable n+   , Show a+   , Eq (n (MonadStream m n a))+   , Monoid (n (MonadStream m n a))+   ) => n (MonadStream m n a) -> String+showMonadStreams = concatMap showMonadStream++-- | Update a MonadStream recursively. Reuse cached values when possible+updateMonadStream ::+   ( Monad m+   , Traversable n+   ) => MonadStream m n a -> m (MonadStream m n a)+updateMonadStream t = updateMonadStreamMaybe t+   ||> fromMaybe t++-- | Update a MonadStream recursively. Reuse cached values when possible+updateMonadStreamMaybe ::+   ( Monad m+   , Traversable n+   ) => MonadStream m n a -> m (Maybe (MonadStream m n a))+updateMonadStreamMaybe = go False+   where+      go False (PureStream a ts) = PureStream a <||| updateMonadStreamsMaybe ts+      go True  (PureStream a ts) = Just <|| PureStream a <|| updateMonadStreams ts+      go True (MonadStream dv) = do+            (MonadVarNE ts' ms' io f) <- updateMonadVarNE dv+            ts'' <- updateMonadStreams ts'+            pure (Just (MonadStream (MonadVarNE ts'' ms' io f)))+      go False (MonadStream dv@(MonadVarNE ts ms io f)) = do+            mcdv <- updateMonadVarNEMaybe dv+            case mcdv of+               Nothing -> updateMonadStreamsMaybe ts+                          |||> (\ts' -> MonadStream (MonadVarNE ts' ms io f))+               Just (MonadVarNE ts' ms' _ _) -> do+                  ts'' <- updateMonadStreams ts'+                  pure (Just (MonadStream (MonadVarNE ts'' ms' io f)))++-- | Update a MonadStream forest recursively. Reuse cached values when possible+updateMonadStreamsMaybe ::+   ( Monad m+   , Traversable n+   ) => n (MonadStream m n a) -> m (Maybe (n (MonadStream m n a)))+updateMonadStreamsMaybe ns = do+   ns' <- forM ns \n -> do+      mu <- updateMonadStreamMaybe n+      pure (n,mu)+   if all (isNothing . snd) ns'+      then pure Nothing+      else pure (Just (fmap fst ns'))++-- | Update a MonadStream forest recursively+updateMonadStreams ::+   ( Monad m+   , Traversable n+   ) => n (MonadStream m n a) -> m (n (MonadStream m n a))+updateMonadStreams ns = updateMonadStreamsMaybe ns+   ||> fromMaybe ns
+ src/lib/Haskus/Utils/MonadVar.hs view
@@ -0,0 +1,90 @@+{-# LANGUAGE LambdaCase #-}+{-# LANGUAGE DeriveFunctor #-}+{-# LANGUAGE ExistentialQuantification #-}+{-# LANGUAGE StandaloneDeriving #-}+{-# LANGUAGE BlockArguments #-}++-- | Monadic variable with cache+module Haskus.Utils.MonadVar+   ( MonadVar (..)+   , updateMonadVarForce+   , updateMonadVarMaybe+   , updateMonadVar+   -- * Non-empty+   , MonadVarNE (..)+   , updateMonadVarNEMaybe+   , updateMonadVarNE+   )+where++import Haskus.Utils.Flow+import Haskus.Utils.Maybe++-- | A value that can be read with IO. The last read can be cached in it too.+--+-- We store both the read value (type s) and a pure modifier function (s -> a).+-- By doing this we can easily compare a read value to the cached one without+-- performing extra computations. The functor instance compose with the modifier+-- function.+--+-- The supposedly costly modifier function is applied lazily+data MonadVar m s a+   = MonadVar !(m s) (s -> a)            -- ^ IO accessor + modifier function+   | CachedMonadVar a !s !(m s) (s -> a) -- ^ Additional cached transformed and read values.+   deriving (Functor)++-- | Check if the MonadVar cache needs to be updated.+--+-- Invariably produce an MonadVar with cached values or Nothing if the old one+-- hasn't changed.+updateMonadVarMaybe :: (Monad m, Eq s) => MonadVar m s a -> m (Maybe (MonadVar m s a))+updateMonadVarMaybe dv@(MonadVar {}) = Just <|| updateMonadVarForce dv+updateMonadVarMaybe (CachedMonadVar _ s io f) = do+   s' <- io+   if s == s'+      then pure Nothing+      else pure <| Just <| CachedMonadVar (f s') s' io f++-- | Check if the MonadVar cache needs to be updated. Return the updated MonadVar.+--+-- Invariably produce an MonadVar with cached values.+updateMonadVar :: (Monad m, Eq s) => MonadVar m s a -> m (MonadVar m s a)+updateMonadVar dv = fromMaybe dv <|| updateMonadVarMaybe dv++-- | Update an MonadVar without comparing to the cache even if it is available.+--+-- Invariably produce an MonadVar with cached values.+updateMonadVarForce :: (Monad m, Eq s) => MonadVar m s a -> m (MonadVar m s a)+updateMonadVarForce (CachedMonadVar _ _ io f) = do+   s <- io+   pure (CachedMonadVar (f s) s io f)+updateMonadVarForce (MonadVar io f) = do+   s <- io+   pure (CachedMonadVar (f s) s io f)++++----------------------------------+-- Non-empty MonadVar+----------------------------------+++-- Non-empty MonadVar+--+-- The value may be set purely if the source is Nothing+data MonadVarNE m s a+   = MonadVarNE a !(Maybe s) !(m s) (s -> a) -- ^ Additional cached transformed and read values.+   deriving (Functor)++-- | Check if the MonadVarNE cache needs to be updated.+updateMonadVarNEMaybe :: (Monad m, Eq s) => MonadVarNE m s a -> m (Maybe (MonadVarNE m s a))+updateMonadVarNEMaybe (MonadVarNE _ ms io f) = do+   s' <- io+   pure case ms of+      Just s | s == s' -> Nothing+      _                -> Just <| MonadVarNE (f s') (Just s') io f++-- | Check if the MonadVarNE cache needs to be updated. Return the updated+-- MonadVarNE+updateMonadVarNE :: (Monad m, Eq s) => MonadVarNE m s a -> m (MonadVarNE m s a)+updateMonadVarNE dv = fromMaybe dv <|| updateMonadVarNEMaybe dv
− src/lib/Haskus/Utils/Parser.hs
@@ -1,190 +0,0 @@-{-# LANGUAGE DataKinds #-}-{-# LANGUAGE ExistentialQuantification #-}-{-# LANGUAGE FlexibleInstances #-}-{-# LANGUAGE MultiParamTypeClasses #-}-{-# LANGUAGE TypeFamilies #-}-{-# LANGUAGE TypeApplications #-}-{-# LANGUAGE UndecidableInstances #-}-{-# LANGUAGE FlexibleContexts #-}-{-# LANGUAGE ScopedTypeVariables #-}-{-# LANGUAGE AllowAmbiguousTypes #-}-{-# LANGUAGE TypeOperators #-}---- | Tools to write parsers using Flows-module Haskus.Utils.Parser-   ( ParseError (..)-   , Choice (..)-   , choice-   , choice'-   , manyBounded-   , manyAtMost-   , manyAtMost'-   , manyAtMost''-   , many-   , manyAtLeast-   , manyTill-   , manyTill'-   )-where--import Prelude hiding (min,max)-import Haskus.Utils.HList-import Haskus.Utils.Types.List-import Haskus.Utils.Variant.OldFlow-import Haskus.Utils.Variant----- A parser is a Flow function that can either:---    - return a parsed value or a semantic error---    - fail with a ParseError:---       - not enough input---       - syntax error------- | Parser error-data ParseError-   = SyntaxError-   | EndOfInput-   deriving (Show,Eq)---- We can define combinators between parsers--data Choice a = Choice--instance forall x y z xs ys zs m a.-      ( x ~ Flow m xs-      , y ~ Flow m ys-      , z ~ Flow m zs-      , a :< xs-      , LiftVariant ys zs-      , LiftVariant (Remove a xs) zs-      , zs ~ Union (Remove a xs) ys-      , Monad m-      ) => Apply (Choice a) (x,y) z-   where-      apply _ (x,y) = x >%~|> \(_ :: a) -> y---- | Try to apply the actions in the list in order, until one of them succeeds.--- Returns the value of the succeeding action, or the value of the last one.--- Failures are detected with values of type "ParseError".-choice :: forall m fs zs.-   ( Monad m-   , HFoldl (Choice ParseError) (Flow m '[ParseError]) fs (Flow m zs)-   ) => HList fs -> Flow m zs-choice = choice' @ParseError---- | Try to apply the actions in the list in order, until one of them succeeds.--- Returns the value of the succeeding action, or the value of the last one.--- Failures are detected with values of type "a".-choice' :: forall a m fs zs.-   ( Monad m-   , HFoldl (Choice a) (Flow m '[a]) fs (Flow m zs)-   ) => HList fs -> Flow m zs-choice' = hFoldl (Choice :: Choice a) (flowSingle undefined :: Flow m '[a])---- | Apply the action zero or more times (until a ParseError result is--- returned)-many ::-   ( zs ~ Remove ParseError xs-   , Monad m-   , ParseError :< xs-   ) => Flow m xs -> Flow m '[[V zs]]-many f = manyBounded Nothing Nothing f-            >%~^> \(_ :: ParseError) -> flowSingle []---- | Apply the action zero or more times (up to max) until a ParseError result--- is returned-manyAtMost ::-   ( zs ~ Remove ParseError xs-   , Monad m-   , ParseError :< xs-   ) => Word -> Flow m xs -> Flow m '[[V zs]]-manyAtMost max f = manyBounded Nothing (Just max) f-                     >%~^> \(_ :: ParseError) -> flowSingle []---- | Apply the action zero or more times (up to max) until a ParseError result--- is returned-manyAtMost' ::-   ( zs ~ Remove ParseError xs-   , Monad m-   , ParseError :< xs-   ) => Word -> Flow m xs -> m [V zs]-manyAtMost' max f = variantToValue <$> manyAtMost max f---- | Apply the action zero or more times (up to max) until a ParseError result--- is returned-manyAtMost'' ::-   ( '[x] ~ Remove ParseError xs-   , Monad m-   , ParseError :< xs-   ) => Word -> Flow m xs -> m [x]-manyAtMost'' max f = fmap variantToValue <$> manyAtMost' max f---- | Apply the action at least n times or more times (until a ParseError--- result is returned)-manyAtLeast ::-   ( zs ~ Remove ParseError xs-   , Monad m-   , ParseError :< xs-   ) => Word -> Flow m xs -> Flow m '[[V zs],ParseError]-manyAtLeast min = manyBounded (Just min) Nothing---- | Apply the first action zero or more times until the second succeeds.--- If the first action fails, the whole operation fails.------ Return both the list of first values and the ending value-manyTill ::-   ( zs ~ Remove ParseError xs-   , zs' ~ Remove ParseError ys-   , Monad m-   , ParseError :<? xs-   , ParseError :< ys-   ) => Flow m xs -> Flow m ys -> Flow m '[([V zs],V zs'),ParseError]-manyTill f g = go []-   where-      go xs = do-         v <- g-         case popVariant v of-            Right EndOfInput  -> flowSet EndOfInput-            Right SyntaxError -> do-               u <- f-               case popVariantMaybe u of-                  Right (e :: ParseError) -> flowSet e-                  Left x                  -> go (x:xs)-            Left x            -> flowSet (reverse xs,x)---- | Apply the first action zero or more times until the second succeeds.--- If the first action fails, the whole operation fails.------ Return only the list of first values-manyTill' ::-   ( zs ~ Remove ParseError xs-   , Monad m-   , ParseError :<? xs-   , ParseError :< ys-   ) => Flow m xs -> Flow m ys -> Flow m '[[V zs],ParseError]-manyTill' f g = manyTill f g >.-.> fst---- | Apply the given action at least 'min' times and at most 'max' time------ On failure, fails.-manyBounded :: forall zs xs m.-   ( zs ~ Remove ParseError xs-   , Monad m-   , ParseError :<? xs-   ) => Maybe Word -> Maybe Word -> Flow m xs -> Flow m '[[V zs],ParseError]-manyBounded _ (Just 0) _   = flowSet ([] :: [V zs])-manyBounded (Just 0) max f = manyBounded Nothing max f-manyBounded min max f      = do-   v <- f-   case popVariantMaybe v of-      Right (e :: ParseError) -> case min of-         Just n | n > 0 -> flowSet e-         _              -> flowSet ([] :: [V zs])-      Left x           -> do-         let minus1 = fmap (\k -> k - 1)-         xs <- manyBounded (minus1 min) (minus1 max) f-         case variantToEither xs of-            Left (e :: ParseError) -> flowSet e-            Right xs'              -> flowSet (x : xs')-
src/lib/Haskus/Utils/STM.hs view
@@ -8,9 +8,11 @@    , newTVarIO    , readTVarIO    , S.writeTVar+   , writeTVarIO    , S.readTVar    , S.newTVar    , S.swapTVar+   , swapTVarIO    , S.modifyTVar    , S.modifyTVar'    -- ** TMVar@@ -32,6 +34,7 @@    , S.newBroadcastTChan    , S.writeTChan    , S.dupTChan+   , S.cloneTChan    , S.readTChan    ) where@@ -47,6 +50,14 @@ -- | Read a TVar in an IO monad readTVarIO :: MonadIO m => TVar a -> m a readTVarIO = liftIO . S.readTVarIO++-- | Write a TVar in an IO monad+writeTVarIO :: MonadIO m => TVar a -> a -> m ()+writeTVarIO v a = atomically (S.writeTVar v a)++-- | Swap a TVar in an IO monad+swapTVarIO :: MonadIO m => TVar a -> a -> m a+swapTVarIO v a = atomically (S.swapTVar v a)  -- | Create a broadcast channel newBroadcastTChanIO :: MonadIO m => m (TChan a)
src/lib/Haskus/Utils/STM/Future.hs view
@@ -30,7 +30,9 @@  -- | `newFuture` in `IO` newFutureIO :: MonadIO m => m (Future a, FutureSource a)-newFutureIO = atomically newFuture+newFutureIO = do+   m <- liftIO newEmptyTMVarIO+   return (Future m, FutureSource m)  -- | Set a future setFuture :: a -> FutureSource a -> STM ()
+ src/lib/Haskus/Utils/STM/SnapVar.hs view
@@ -0,0 +1,165 @@+-- | Snapshotable STM variables+--+-- A `SnapVar` is like a `TVar` except that they have a context which can+-- enable a snapshot mode. When in snapshot mode, the current values are not+-- erased by writes. Instead each SnapVar can have 2 values: the value at the+-- time of the snapshot and its current value.+--+-- There can be only a single snapshot at a time. When the snapshot mode is+-- exited, the variables only keep the current value alive, not the snapshot+-- one.+--+module Haskus.Utils.STM.SnapVar+   ( SnapVar+   , SnapContext+   , newSnapContextIO+   , newSnapContext+   , newSnapVarIO+   , newSnapVar+   , writeSnapVar+   , writeSnapVarIO+   , readSnapVar+   , readSnapVarIO+   , modifySnapVar+   , modifySnapVarIO+   -- * Snapshot+   , withSnapshot+   , readSnapshot+   , readSnapshotIO+   )+where++import Haskus.Utils.STM+import Haskus.Utils.Monad++-- | A snapshot variable+data SnapVar a = SnapVar+   { snapContext   :: !SnapContext      -- ^ Snapshot context+   , snapValue     :: !(TVar a)         -- ^ Snapshot value (during snapshot) or current value (otherwise)+   , snapNextValue :: !(TVar (Maybe a)) -- ^ Next value (during snapshot)+   }++-- | Snapshot context+data SnapContext = SnapContext+   { snapContextState    :: !(TVar SnapState) -- ^ Snapshot state+   , snapContextUpdaters :: !(TVar [STM ()])  -- ^ Variable updaters (on snapshot exit)+   }++-- | Snapshot state+data SnapState+   = NoSnapshot   -- ^ No snapshot active+   | Snapshot     -- ^ Snapshot active+   | SnapshotExit -- ^ Exiting the snapshot+++-- | Create a new snapshot context+newSnapContextIO :: MonadIO m => m SnapContext+newSnapContextIO = SnapContext <$> newTVarIO NoSnapshot <*> newTVarIO []++-- | Create a new snapshot context+newSnapContext :: STM SnapContext+newSnapContext = SnapContext <$> newTVar NoSnapshot <*> newTVar []++-- | Create a new SnapVar+newSnapVarIO :: MonadIO m => SnapContext -> a -> m (SnapVar a)+newSnapVarIO ctx v = SnapVar <$> return ctx <*> newTVarIO v <*> newTVarIO Nothing++-- | Create a new SnapVar+newSnapVar :: SnapContext -> a -> STM (SnapVar a)+newSnapVar ctx v = SnapVar <$> return ctx <*> newTVar v <*> newTVar Nothing++-- | Write a SnapVar+writeSnapVar :: SnapVar a -> a -> STM ()+writeSnapVar v a = do+   state <- readTVar (snapContextState (snapContext v))+   case state of+      NoSnapshot   -> writeTVar (snapValue v) a+      SnapshotExit -> do+         writeTVar (snapValue v) a+         -- update next-value too to be sure that no updater will erase our+         -- value and that a future read will be correct+         writeTVar (snapNextValue v) Nothing+      Snapshot     -> do+         -- write into next-value and get old value+         mv <- swapTVar (snapNextValue v) (Just a)+         -- install value updater on exit (if not already done)+         case mv of+            Just _  -> return () -- value updater already installed+            Nothing -> modifyTVar (snapContextUpdaters (snapContext v)) (updateSnapVar v:)++-- | SnapVar updater (on snapshot exit, for variable written during the snapshot)+updateSnapVar :: SnapVar a -> STM ()+updateSnapVar v = do+   -- read fresh value+   nv <- readTVar (snapNextValue v)+   writeTVar (snapNextValue v) Nothing+   case nv of+      Just val -> writeTVar (snapValue v) val+      Nothing  -> return () -- nothing to do, a writer has already done this before us++-- | Write a SnapVar+writeSnapVarIO :: MonadIO m => SnapVar a -> a -> m ()+writeSnapVarIO v a = atomically (writeSnapVar v a)++-- | Read a SnapVar+readSnapVar :: SnapVar a -> STM a+readSnapVar v = do+   state <- readTVar (snapContextState (snapContext v))+   case state of+      NoSnapshot -> readTVar (snapValue v)+      _          -> do+         -- in the SnapshotExit case we could update the value here if+         -- necessary, but an updater will eventually do it so we don't bother+         mv <- readTVar (snapNextValue v)+         case mv of+            Just a  -> return a+            Nothing -> readTVar (snapValue v)++-- | Read a SnapVar+readSnapVarIO :: MonadIO m => SnapVar a -> m a+readSnapVarIO v = atomically (readSnapVar v)++-- | Modify a SnapVar+modifySnapVar :: SnapVar a -> (a -> a) -> STM a+modifySnapVar v f = do+   old <- readSnapVar v+   writeSnapVar v (f old)+   return old++-- | Modify a SnapVar+modifySnapVarIO :: MonadIO m => SnapVar a -> (a -> a) -> m a+modifySnapVarIO v f = atomically (modifySnapVar v f)++-- | Read the snapshot value of the variable.+--+-- Must be used in the context of a `withSnapshot`+readSnapshotIO :: MonadIO m => SnapVar a -> m a+readSnapshotIO v = readTVarIO (snapValue v)++-- | Read the snapshot value of the variable.+--+-- Must be used in the context of a `withSnapshot`+readSnapshot :: SnapVar a -> STM a+readSnapshot v = readTVar (snapValue v)++-- | Use a snapshot+withSnapshot :: MonadIO m => SnapContext -> m r -> m r+withSnapshot ctx action = do+   -- enable snapshot+   old <- swapTVarIO (snapContextState ctx) Snapshot+   case old of+      NoSnapshot -> return ()+      _          -> error "withSnapshot: invalid snapshot state"++   +   -- use the snapshot+   r <- action++   -- finalize snapshot +   writeTVarIO (snapContextState ctx) SnapshotExit -- in SnapshotExit state, no new updaters can be added to the list+   updaters <- readTVarIO (snapContextUpdaters ctx)+   forM_ updaters atomically -- run updaters one by one instead of all at once to have small transactions++   writeTVarIO (snapContextState ctx) NoSnapshot++   return r
src/lib/Haskus/Utils/Solver.hs view
@@ -15,19 +15,21 @@    , makeOracle    , oraclePredicates    , emptyOracle+   , oracleUnion    , predIsSet    , predIsUnset    , predIsUndef+   , predIsInvalid    , predIs    , predState+   , predAdd    -- * Constraint    , Constraint (..)-   , simplifyConstraint-   , constraintReduce+   , constraintOptimize+   , constraintSimplify    -- * Rule    , Rule (..)-   , orderedNonTerminal-   , mergeRules+   , ruleSimplify    , evalsTo    , MatchResult (..)    -- * Predicated data@@ -45,11 +47,18 @@ import Haskus.Utils.Map.Strict (Map) import qualified Haskus.Utils.Map.Strict as Map -import Data.Bits import Control.Arrow (first,second)+import Data.Set (Set)+import qualified Data.Set as Set -import Prelude hiding (pred)+import Prelude hiding (pred,length) +-- $setup+-- >>> :set -XDataKinds+-- >>> :set -XTypeApplications+-- >>> :set -XFlexibleContexts+-- >>> :set -XTypeFamilies+ ------------------------------------------------------- -- Constraint -------------------------------------------------------@@ -59,6 +68,7 @@    = SetPred       -- ^ Set predicate    | UnsetPred     -- ^ Unset predicate    | UndefPred     -- ^ Undefined predicate+   | InvalidPred   -- ^ Invalid predicate (can't be used)    deriving (Show,Eq,Ord)  -- | Predicate oracle@@ -76,6 +86,10 @@ predIsUndef :: Ord p => PredOracle p -> p -> Bool predIsUndef oracle p = predIs oracle p UndefPred +-- | Ask an oracle if a predicate is invalid+predIsInvalid :: Ord p => PredOracle p -> p -> Bool+predIsInvalid oracle p = predIs oracle p InvalidPred+ -- | Check the state of a predicate predIs :: Ord p => PredOracle p -> p -> PredState -> Bool predIs oracle p s = predState oracle p == s@@ -90,10 +104,21 @@ makeOracle :: Ord p => [(p,PredState)] -> PredOracle p makeOracle = Map.fromList --- | Get a list of predicates from an oracle+-- | Get a list of valid and defined predicates from an oracle oraclePredicates :: Ord p => PredOracle p -> [(p,PredState)] oraclePredicates = filter (\(_,s) -> s /= UndefPred) . Map.toList +-- | Combine two oracles+-- TODO: check that there is no contradiction+oracleUnion :: Ord p => PredOracle p -> PredOracle p -> PredOracle p+oracleUnion = Map.union++-- | Add predicates to an oracle+-- TODO: check that there is no contradiction+predAdd :: Ord p => [(p,PredState)] -> PredOracle p -> PredOracle p+predAdd cs = oracleUnion (makeOracle cs)++ -- | Oracle that always answer Undef emptyOracle :: PredOracle p emptyOracle = Map.empty@@ -102,50 +127,82 @@ -- Constraint ------------------------------------------------------- +-- | A constraint is a boolean expression+--+-- `p` is the predicate type data Constraint e p-   = Predicate p-   | Not (Constraint e p)-   | And [Constraint e p]-   | Or  [Constraint e p]-   | Xor [Constraint e p]-   | CBool Bool+   = Predicate p            -- ^ Predicate value+   | IsValid p              -- ^ Is the predicate valid+   | Not (Constraint e p)   -- ^ Logic not+   | And [Constraint e p]   -- ^ Logic and+   | Or  [Constraint e p]   -- ^ Logic or+   | Xor [Constraint e p]   -- ^ Logic xor+   | CBool Bool             -- ^ Constant+   | CErr (Either String e) -- ^ Error    deriving (Show,Eq,Ord)  instance Functor (Constraint e) where    fmap f (Predicate p)  = Predicate (f p)+   fmap f (IsValid   p)  = IsValid (f p)    fmap _ (CBool b)      = CBool b    fmap f (Not c)        = Not (fmap f c)    fmap f (And cs)       = And (fmap (fmap f) cs)    fmap f (Or cs)        = Or (fmap (fmap f) cs)    fmap f (Xor cs)       = Xor (fmap (fmap f) cs)+   fmap _ (CErr e)       = CErr e  -- | Reduce a constraint-constraintReduce :: (Ord p, Eq p, Eq e) => PredOracle p -> Constraint e p -> Constraint e p-constraintReduce oracle c = case simplifyConstraint c of+--+-- >>> data P = A | B deriving (Show,Eq,Ord)+-- >>> let c = And [IsValid A, Predicate B]+--+-- >>> let oracle = makeOracle [(A,InvalidPred),(B,SetPred)]+-- >>> constraintSimplify oracle c+-- CBool False+--+-- >>> let oracle = makeOracle [(A,SetPred),(B,SetPred)]+-- >>> constraintSimplify oracle c+-- CBool True+--+-- >>> let oracle = makeOracle [(A,SetPred),(B,UnsetPred)]+-- >>> constraintSimplify oracle c+-- CBool False+constraintSimplify :: (Ord p, Eq p, Eq e) => PredOracle p -> Constraint e p -> Constraint e p+constraintSimplify oracle c = case constraintOptimize c of+   CErr e       -> CErr e+   IsValid p    -> case predState oracle p of+                     UndefPred   -> IsValid p+                     InvalidPred -> CBool False+                     SetPred     -> CBool True+                     UnsetPred   -> CBool True    Predicate p  -> case predState oracle p of-                      UndefPred -> Predicate p-                      SetPred   -> CBool True-                      UnsetPred -> CBool False-   Not c'       -> case constraintReduce oracle c' of+                      UndefPred   -> Predicate p+                      InvalidPred -> CErr (Left "Invalid predicate")+                      SetPred     -> CBool True+                      UnsetPred   -> CBool False+   Not c'       -> case constraintSimplify oracle c' of                       CBool v -> CBool (not v)+                      CErr e  -> CErr e                       c''     -> Not c''-   And cs       -> case fmap (constraintReduce oracle) cs of-                      []                                     -> error "Empty And constraint"+   And cs       -> case fmap (constraintSimplify oracle) cs of+                      []                                     -> CErr (Left "Empty And constraint")                       cs' | all (constraintIsBool True)  cs' -> CBool True                       cs' | any (constraintIsBool False) cs' -> CBool False+                      cs' | all constraintIsError cs'        -> CErr (Left "And expression only contains Error constraints")                       cs' -> case filter (not . constraintIsBool True) cs' of                         [c'] -> c'                         cs'' -> And cs''-   Or cs        -> case fmap (constraintReduce oracle) cs of-                      []                                      -> error "Empty Or constraint"+   Or cs        -> case filter (not . constraintIsError) <| fmap (constraintSimplify oracle) cs of+                      []                                      -> CErr (Left "Empty Or constraint")                       cs' | all (constraintIsBool False)  cs' -> CBool False                       cs' | any (constraintIsBool True)   cs' -> CBool True                       cs' -> case filter (not . constraintIsBool False) cs' of                         [c'] -> c'                         cs'' -> Or cs''-   Xor cs       -> case fmap (constraintReduce oracle) cs of-                      []  -> error "Empty Xor constraint"-                      cs' -> simplifyConstraint (Xor cs')+   Xor cs       -> case fmap (constraintSimplify oracle) cs of+                      cs' | any constraintIsError cs' -> CErr (Left "Xor expression contains Error constraint")+                      []                              -> CErr (Left "Empty Xor constraint")+                      cs'                             -> constraintOptimize (Xor cs')    c'@(CBool _) -> c'  -- | Check that a constraint is evaluated to a given boolean value@@ -153,102 +210,87 @@ constraintIsBool v (CBool v') = v == v' constraintIsBool _ _          = False +-- | Check that a constraint is evaluated to an error+constraintIsError :: Constraint e p -> Bool+constraintIsError (CErr _) = True+constraintIsError _        = False+ -- | Get predicates used in a constraint-getConstraintPredicates :: Constraint e p -> [p]+getConstraintPredicates :: Ord p => Constraint e p -> Set p getConstraintPredicates = \case-   Predicate p  -> [p]+   CErr _       -> Set.empty+   IsValid   p  -> Set.singleton p+   Predicate p  -> Set.singleton p    Not c        -> getConstraintPredicates c-   And cs       -> concatMap getConstraintPredicates cs-   Or  cs       -> concatMap getConstraintPredicates cs-   Xor cs       -> concatMap getConstraintPredicates cs-   CBool _      -> []+   And cs       -> Set.unions $ fmap getConstraintPredicates cs+   Or  cs       -> Set.unions $ fmap getConstraintPredicates cs+   Xor cs       -> Set.unions $ fmap getConstraintPredicates cs+   CBool _      -> Set.empty  -- | Get constraint terminals-getConstraintTerminals :: Constraint e p -> [Bool]+getConstraintTerminals :: Constraint e p -> Set Bool getConstraintTerminals = \case-   Predicate _  -> [True,False]-   CBool v      -> [v]-   Not c        -> fmap not (getConstraintTerminals c)+   CErr _       -> Set.empty+   IsValid   _  -> tf+   Predicate _  -> tf+   CBool v      -> Set.singleton v+   Not c        -> Set.map not (getConstraintTerminals c)    And cs       -> let cs' = fmap getConstraintTerminals cs-                   in if | null cs                -> []-                         | any (False `elem`) cs' -> [False]-                         | all (sing True)    cs' -> [True]-                         | otherwise              -> [True,False]+                   in if | null cs                         -> Set.empty+                         | any (False `elem`) cs'          -> Set.singleton False+                         | all (== Set.singleton True) cs' -> Set.singleton True+                         | otherwise                       -> tf    Or  cs       -> let cs' = fmap getConstraintTerminals cs-                   in if | null cs                -> []-                         | any (True `elem`) cs'  -> [True]-                         | all (sing False)   cs' -> [False]-                         | otherwise              -> [True,False]-   Xor cs       -> let cs' = fmap getConstraintTerminals cs-                   in if | null cs                -> []+                   in if | null cs                          -> Set.empty+                         | any (True `elem`) cs'            -> Set.singleton True+                         | all (== Set.singleton False) cs' -> Set.singleton False+                         | otherwise                        -> tf+   Xor cs       -> let cs' = fmap (Set.toList . getConstraintTerminals) cs+                   in if | null cs                -> Set.empty                          | otherwise              -> xo False cs'    where-      xo t     []           = [t]+      tf = Set.fromList [True,False]++      xo t     []           = Set.singleton t       xo False ([True]:xs)  = xo True xs-      xo True  ([True]:_)   = [False]+      xo True  ([True]:_)   = Set.singleton False       xo False ([False]:xs) = xo False xs       xo True  ([False]:xs) = xo True xs-      xo _     ([]:_)       = []-      xo _     _            = [True,False]--      sing v [v'] = v == v'-      sing _ _    = False------------------------------------------------------------- Rule----------------------------------------------------------data Rule e p a-   = Terminal a-   | NonTerminal [(Constraint e p, Rule e p a)]-   | Fail e-   deriving (Show,Eq,Ord)--instance Functor (Rule e p) where-   fmap f (Terminal a)     = Terminal (f a)-   fmap f (NonTerminal xs) = NonTerminal (fmap (second (fmap f)) xs)-   fmap _ (Fail e)         = Fail e----- | NonTerminal whose constraints are evaluated in order------ Earlier constraints must be proven false for the next ones to be considered-orderedNonTerminal :: [(Constraint e p, Rule e p a)] -> Rule e p a-orderedNonTerminal = NonTerminal . go []-   where-      go _  []          = []-      go [] ((c,r):xs)  = (simplifyConstraint c,r) : go [c] xs-      go cs ((c,r):xs)  = (simplifyConstraint (And (c:fmap Not cs)),r) : go (c:cs) xs+      xo _     ([]:_)       = Set.empty+      xo _     _            = tf --- | Simplify a constraint-simplifyConstraint :: Constraint e p -> Constraint e p-simplifyConstraint x = case x of+-- | Optimize/simplify a constraint+constraintOptimize :: Constraint e p -> Constraint e p+constraintOptimize x = case x of+   CErr _            -> x+   Not (CErr e)      -> CErr e+   IsValid _         -> x    Predicate _       -> x    CBool _           -> x+   Not (IsValid _)   -> x    Not (Predicate _) -> x    Not (CBool v)     -> CBool (not v)-   Not (Not c)       -> simplifyConstraint c-   Not (Or cs)       -> simplifyConstraint (And (fmap Not cs))-   Not (And cs)      -> simplifyConstraint (Or (fmap Not cs))-   Not (Xor cs)      -> case simplifyConstraint (Xor cs) of+   Not (Not c)       -> constraintOptimize c+   Not (Or cs)       -> constraintOptimize (And (fmap Not cs))+   Not (And cs)      -> constraintOptimize (Or (fmap Not cs))+   Not (Xor cs)      -> case constraintOptimize (Xor cs) of                            Xor cs' -> Not (Xor cs')-                           r       -> simplifyConstraint (Not r)-   And [c]           -> simplifyConstraint c-   Or  [c]           -> simplifyConstraint c-   Xor [c]           -> let c' = simplifyConstraint c+                           r       -> constraintOptimize (Not r)+   And [c]           -> constraintOptimize c+   Or  [c]           -> constraintOptimize c+   Xor [c]           -> let c' = constraintOptimize c                         in if | constraintIsBool True c'  -> CBool True                               | constraintIsBool False c' -> CBool False                               | otherwise                 -> c'-   And cs            -> let cs' = fmap simplifyConstraint cs+   And cs            -> let cs' = fmap constraintOptimize cs                         in if | any (constraintIsBool False) cs' -> CBool False                               | all (constraintIsBool True)  cs' -> CBool True                               | otherwise                        -> And cs'-   Or cs             -> let cs' = fmap simplifyConstraint cs+   Or cs             -> let cs' = fmap constraintOptimize cs                         in if | any (constraintIsBool True) cs'  -> CBool True                               | all (constraintIsBool False) cs' -> CBool False                               | otherwise                        -> Or cs'-   Xor cs            -> let cs'        = fmap simplifyConstraint cs+   Xor cs            -> let cs'        = fmap constraintOptimize cs                             countTrue  = length (filter (constraintIsBool True) cs')                             countFalse = length (filter (constraintIsBool False) cs')                             countAll   = length cs'@@ -257,46 +299,78 @@                               | countAll == countFalse                               -> CBool False                               | otherwise                                            -> Xor cs' --- | Merge two rules together-mergeRules :: Rule e p a -> Rule e p b -> Rule e p (a,b)-mergeRules = go++-------------------------------------------------------+-- Rule+-------------------------------------------------------++-- | A rule can produce some "a"s (one or more if it diverges), depending on the+-- constraints.+data Rule e p a+   = Terminal a+   | OrderedNonTerminal [(Constraint e p, Rule e p a)]+   | NonTerminal [(Constraint e p, Rule e p a)]+   | Fail e+   deriving (Show,Eq,Ord)++instance Functor (Rule e p) where+   fmap f (Terminal a)            = Terminal (f a)+   fmap f (NonTerminal xs)        = NonTerminal (fmap (second (fmap f)) xs)+   fmap f (OrderedNonTerminal xs) = OrderedNonTerminal (fmap (second (fmap f)) xs)+   fmap _ (Fail e)                = Fail e+++-- | Simplify a rule given an oracle+ruleSimplify ::+   ( Ord p, Eq e+   ) => PredOracle p -> Rule e p a -> Rule e p a+ruleSimplify oracle r = case r of+   Terminal a            -> Terminal a+   Fail e                -> Fail e+   OrderedNonTerminal rs -> OrderedNonTerminal (simplifyNonTerminal rs)+   NonTerminal rs        -> NonTerminal (concatMap foldNonTerminal (simplifyNonTerminal rs))    where-      go (Fail e)           _                = Fail e-      go _                  (Fail e)         = Fail e-      go (Terminal a)       (Terminal b)     = Terminal (a,b)-      go (Terminal a)       (NonTerminal bs) = NonTerminal (fl (Terminal a) bs)-      go (NonTerminal as)   (Terminal b)     = NonTerminal (fr (Terminal b) as)-      go (NonTerminal as)   b                = NonTerminal (fr b            as)+      -- Simplify non-terminal rule constraints. Remove rules whose constraint is False+      simplifyNonTerminal xs = xs+         -- reduce constraints+         |> fmap (first (constraintSimplify oracle))+         -- recursively simplify nested rules+         |> fmap (second (ruleSimplify oracle))+         -- filter non matching rules+         |> filter (not . constraintIsBool False . fst) -      fl x = fmap (second (x `mergeRules`))-      fr x = fmap (second (`mergeRules` x))+      -- non terminal sub-rules whose constraints are True can be folded into the+      -- upper non-terminal rule. We rely on this to perform rule reduction.+      foldNonTerminal (c, NonTerminal rs)+         | constraintIsBool True c = rs+      foldNonTerminal x = [x]   -- | Reduce a rule ruleReduce :: forall e p a.    ( Ord p, Eq e, Eq p, Eq a) => PredOracle p -> Rule e p a -> MatchResult e (Rule e p a) a-ruleReduce oracle r = case r of-   Terminal a     -> Match a-   Fail e         -> MatchFail [e]+ruleReduce oracle r = case ruleSimplify oracle r of+   Terminal a            -> Match a+   Fail e                -> MatchFail [e]+   NonTerminal []        -> NoMatch+   OrderedNonTerminal [] -> NoMatch+   OrderedNonTerminal ((c,x):xs)+      | constraintIsBool True c  -> ruleReduce oracle x+      | constraintIsBool False c -> ruleReduce oracle (OrderedNonTerminal xs)+      | otherwise                -> DontMatch (OrderedNonTerminal ((c,x):xs))    NonTerminal rs ->        let-         rs' :: [(Constraint e p, Rule e p a)]-         rs' = rs-               -- reduce constraints-               |> fmap (first (constraintReduce oracle))-               -- filter non matching rules-               |> filter (not . constraintIsBool False . fst)--         (matchingRules,mayMatchRules) = partition (constraintIsBool True . fst) rs'+         (matchingRules,mayMatchRules) = partition (constraintIsBool True . fst) rs          matchingResults               = nub $ fmap snd $ matchingRules  -         (failingResults,terminalResults,nonTerminalResults) = go [] [] [] matchingResults+         (failingResults,terminalResults,hasNonTerminalResults) = go [] [] False matchingResults          go fr tr ntr = \case-            []                 -> (fr,tr,ntr)-            (Fail x:xs)        -> go (x:fr) tr ntr xs-            (Terminal x:xs)    -> go fr (x:tr) ntr xs-            (NonTerminal x:xs) -> go fr tr (x:ntr) xs+            []                        -> (fr,tr,ntr)+            (Fail x:xs)               -> go (x:fr) tr ntr  xs+            (Terminal x:xs)           -> go fr (x:tr) ntr  xs+            (NonTerminal _:xs)        -> go fr tr     True xs+            (OrderedNonTerminal _:xs) -> go fr tr     True xs           divergence = case terminalResults of             -- results are already "nub"ed.@@ -304,39 +378,32 @@             (_:_:_) -> True             _       -> False       in-      case rs' of-         []                                 -> NoMatch-         _  | not (null failingResults)     -> MatchFail failingResults-            | divergence                    -> MatchDiverge (fmap Terminal terminalResults)-            | not (null nonTerminalResults) ->-               -- fold matching nested NonTerminals-               ruleReduce oracle-                  <| NonTerminal -                  <| (fmap (\x -> (CBool True, Terminal x)) terminalResults-                      ++ mayMatchRules-                      ++ concat nonTerminalResults)--            | otherwise                     ->-               case (matchingResults,mayMatchRules) of-                  ([Terminal a], [])    -> Match a-                  _                     -> DontMatch (NonTerminal rs')+      if | not (null failingResults)     -> MatchFail failingResults+         | divergence                    -> MatchDiverge (fmap Terminal terminalResults)+         | hasNonTerminalResults         -> DontMatch (NonTerminal rs)+         | otherwise                     ->+            case (terminalResults,mayMatchRules) of+               ([a], []) -> Match a+               _         -> DontMatch (NonTerminal rs)   -- | Get possible resulting terminals-getRuleTerminals :: Rule e p a -> [a]-getRuleTerminals (Fail _)         = []-getRuleTerminals (Terminal a)     = [a]-getRuleTerminals (NonTerminal xs) = concatMap (getRuleTerminals . snd) xs+getRuleTerminals :: Ord a => Rule e p a -> Set a+getRuleTerminals (Fail _)                = Set.empty+getRuleTerminals (Terminal a)            = Set.singleton a+getRuleTerminals (NonTerminal xs)        = Set.unions (fmap (getRuleTerminals . snd) xs)+getRuleTerminals (OrderedNonTerminal xs) = Set.unions (fmap (getRuleTerminals . snd) xs)  -- | Get predicates used in a rule-getRulePredicates :: Eq p => Rule e p a -> [p]-getRulePredicates (Fail _)         = []-getRulePredicates (Terminal _)     = []-getRulePredicates (NonTerminal xs) = nub $ concatMap (\(x,y) -> getConstraintPredicates x ++ getRulePredicates y) xs+getRulePredicates :: (Eq p,Ord p) => Rule e p a -> Set p+getRulePredicates (Fail _)                = Set.empty+getRulePredicates (Terminal _)            = Set.empty+getRulePredicates (NonTerminal xs)        = Set.unions $ fmap (\(x,y) -> getConstraintPredicates x `Set.union` getRulePredicates y) xs+getRulePredicates (OrderedNonTerminal xs) = Set.unions $ fmap (\(x,y) -> getConstraintPredicates x `Set.union` getRulePredicates y) xs  -- | Constraint checking that a predicated value evaluates to some terminal evalsTo :: (Ord (Pred a), Eq a, Eq (PredTerm a), Eq (Pred a), Predicated a) => a -> PredTerm a -> Constraint e (Pred a)-evalsTo s a = case createPredicateTable s (const True) True of+evalsTo s a = case createPredicateTable s (const True) of    Left x   -> CBool (x == a)    Right xs -> orConstraints <| fmap andPredicates                              <| fmap oraclePredicates@@ -346,17 +413,17 @@    where        andPredicates []  = CBool True-      andPredicates [x] = makePred x-      andPredicates xs  = And (fmap makePred xs)+      andPredicates xs  = And (concatMap makePred xs)        orConstraints []  = CBool True       orConstraints [x] = x       orConstraints xs  = Or xs -      makePred (p, UnsetPred) = Not (Predicate p)-      makePred (p, SetPred)   = Predicate p-      makePred (_, UndefPred) = undefined -- shouldn't be possible given we use-                                          -- get the predicates from the oracle itself+      makePred (p, UnsetPred)   = [IsValid p, Not (Predicate p)]+      makePred (p, SetPred)     = [IsValid p, Predicate p]+      makePred (p, InvalidPred) = [Not (IsValid p)]+      makePred (_, UndefPred)   = undefined -- shouldn't be possible given we use+                                            -- get the predicates from the oracle itself   -------------------------------------------------------@@ -410,7 +477,7 @@ --                               , getPredicates b --                               ] -- @-class Predicated a where+class (Ord (Pred a), Ord (PredTerm a)) => Predicated a where    -- | Error type    type PredErr a :: * @@ -426,19 +493,23 @@    -- | Reduce predicates    reducePredicates :: PredOracle (Pred a) -> a -> MatchResult (PredErr a) a (PredTerm a) +   -- | Simplify predicates+   simplifyPredicates :: PredOracle (Pred a) -> a -> a+    -- | Get possible resulting terminals-   getTerminals :: a -> [PredTerm a]+   getTerminals :: a -> Set (PredTerm a)     -- | Get used predicates-   getPredicates :: a -> [Pred a]+   getPredicates :: a -> Set (Pred a)  -instance (Ord p, Eq e, Eq a, Eq p) => Predicated (Rule e p a) where+instance (Ord a, Ord p, Eq e, Eq a, Eq p) => Predicated (Rule e p a) where    type PredErr  (Rule e p a) = e    type Pred     (Rule e p a) = p    type PredTerm (Rule e p a) = a -   reducePredicates = ruleReduce+   reducePredicates   = ruleReduce+   simplifyPredicates = ruleSimplify    liftTerminal     = Terminal    getTerminals     = getRuleTerminals    getPredicates    = getRulePredicates@@ -448,15 +519,42 @@    type Pred     (Constraint e p) = p    type PredTerm (Constraint e p) = Bool -   reducePredicates oracle c = case constraintReduce oracle c of+   reducePredicates oracle c = case constraintSimplify oracle c of       CBool v -> Match v       c'      -> DontMatch c' +   simplifyPredicates oracle c = constraintSimplify oracle c+    liftTerminal     = CBool    getTerminals     = getConstraintTerminals    getPredicates    = getConstraintPredicates +instance forall x y.+   ( Predicated x+   , Predicated y+   , PredErr x ~ PredErr y+   , Pred x ~ Pred y+   ) => Predicated (x,y)+   where+   type PredErr  (x,y) = PredErr x+   type Pred     (x,y) = Pred x+   type PredTerm (x,y) = (PredTerm x, PredTerm y) +   reducePredicates oracle (x,y) =+      initP (,) (,)+         |> (`applyP` reducePredicates oracle x)+         |> (`applyP` reducePredicates oracle y)+         |> resultP++   simplifyPredicates oracle (x,y) = (simplifyPredicates oracle x, simplifyPredicates oracle y)++   liftTerminal (x,y)  = (liftTerminal x, liftTerminal y)+   getTerminals (x,y)  = Set.fromList+                           [ (x',y') | x' <- Set.toList (getTerminals x)+                                     , y' <- Set.toList (getTerminals y)+                           ]+   getPredicates (x,y) = Set.union (getPredicates x) (getPredicates y)+ -- | Reduction result data MatchResult e nt t    = NoMatch@@ -517,8 +615,8 @@    , Predicated a    , Predicated a    , Pred a ~ Pred a-   ) => a -> (PredOracle (Pred a) -> Bool) -> Bool -> Either (PredTerm a) [(PredOracle (Pred a),PredTerm a)]-createPredicateTable s oracleChecker fullTable =+   ) => a -> (PredOracle (Pred a) -> Bool) -> Either (PredTerm a) [(PredOracle (Pred a),PredTerm a)]+createPredicateTable s oracleChecker =    -- we first check if the predicated value reduces to a terminal without any    -- additional oracle    case reducePredicates emptyOracle s of@@ -531,25 +629,11 @@        oracles = filter oracleChecker (fmap makeOracle predSets) -      preds        = sort (getPredicates s)--      predSets-         | fullTable = makeFullSets preds-         | otherwise = makeSets     preds [] +      preds = Set.toList (getPredicates (simplifyPredicates emptyOracle s)) -      makeFullSets ps  = fmap (makeFullSet ps) ([0..2^(length ps)-1] :: [Word])-      makeFullSet ps n = fmap (setB n) (ps `zip` [0..])-      setB n (p,i)     = if testBit n i-         then (p,SetPred)-         else (p,UnsetPred)+      predSets = makeSets preds [[]]  +      -- make predicate sets (each predicate is either Set, Unset or Undef)       makeSets []     os  = os-      makeSets (p:ps) os = let ns = [(p,SetPred),(p,UnsetPred)]-                           in makeSets ps $ concat-                                 [ [ [n] | n <- ns ]-                                 , [(n:o) | o <- os, n <- ns]-                                 , os-                                 ]---+      makeSets (p:ps) os = let ns = [(p,SetPred),(p,UnsetPred),(p,UndefPred)]+                           in makeSets ps [(n:o) | o <- os, n <- ns]
+ src/lib/Haskus/Utils/TimedValue.hs view
@@ -0,0 +1,21 @@+-- | Mutable value with associated last write time+module Haskus.Utils.TimedValue+   ( TimedValue (..)+   )+where++-- | Value with Eq/Ord instances uniquely based on time field indicating the+-- time the value was last written.+--+-- This can be used with IOVar/IOTree which use Eq instances to detect value+-- changes. It can be useful for values that we don't want to structurally+-- compare (because it is too costly or because we can't)+--+-- `t` should be SystemTime (fast to query monotonic clock)+data TimedValue t a = TimedValue t a++instance Eq t => Eq (TimedValue t a) where+   TimedValue t1 _ == TimedValue t2 _ = t1 == t2++instance Ord t => Ord (TimedValue t a) where+   compare (TimedValue t1 _) (TimedValue t2 _) = compare t1 t2
src/tests/Haskus/Tests/Utils/Solver.hs view
@@ -13,11 +13,11 @@ import Test.Tasty import Test.Tasty.QuickCheck as QC import Data.List+import qualified Data.Set as Set  import Haskus.Utils.Solver import Haskus.Utils.Flow - data Predi    = PredA    | PredB@@ -65,57 +65,60 @@          |> (`applyP` reducePredicates oracle b)          |> resultP -   getTerminals (PD as bs) = [ PD a b | a <- getTerminals as-                                      , b <- getTerminals bs-                             ]+   simplifyPredicates oracle (PD a b) =+      PD (simplifyPredicates oracle a)+         (simplifyPredicates oracle b) -   getPredicates (PD a b) = concat-                              [ getPredicates a-                              , getPredicates b+   getTerminals (PD as bs) = Set.fromList+                              [ PD a b+                              | a <- Set.toList (getTerminals as)+                              , b <- Set.toList (getTerminals bs)                               ] +   getPredicates (PD a b) = Set.union (getPredicates a) (getPredicates b)+ testsSolver :: TestTree testsSolver = testGroup "Solver" $    [ testProperty "Constraint reduce: CBool True"-         (constraintReduce oracleAll (CBool True) == (CBool True :: C))+         (constraintSimplify oracleAll (CBool True) == (CBool True :: C))    , testProperty "Constraint reduce: CBool False"-         (constraintReduce oracleAll (CBool False) == (CBool False :: C))+         (constraintSimplify oracleAll (CBool False) == (CBool False :: C))    , testProperty "Constraint reduce: Not False"-         (constraintReduce oracleAll (Not (CBool False)) == (CBool True :: C))+         (constraintSimplify oracleAll (Not (CBool False)) == (CBool True :: C))    , testProperty "Constraint reduce: Not True"-         (constraintReduce oracleAll (Not (CBool True)) == (CBool False :: C))+         (constraintSimplify oracleAll (Not (CBool True)) == (CBool False :: C))    , testProperty "Constraint reduce: And [True,True]"-         (constraintReduce oracleAll (And [CBool True,CBool True]) == (CBool True :: C))+         (constraintSimplify oracleAll (And [CBool True,CBool True]) == (CBool True :: C))    , testProperty "Constraint reduce: And [True,False]"-         (constraintReduce oracleAll (And [CBool True,CBool False]) == (CBool False :: C))+         (constraintSimplify oracleAll (And [CBool True,CBool False]) == (CBool False :: C))    , testProperty "Constraint reduce: Or [True,True]"-         (constraintReduce oracleAll (Or [CBool True,CBool True]) == (CBool True :: C))+         (constraintSimplify oracleAll (Or [CBool True,CBool True]) == (CBool True :: C))    , testProperty "Constraint reduce: Or [True,False]"-         (constraintReduce oracleAll (Or [CBool True,CBool False]) == (CBool True :: C))+         (constraintSimplify oracleAll (Or [CBool True,CBool False]) == (CBool True :: C))    , testProperty "Constraint reduce: Or [False,False]"-         (constraintReduce oracleAll (Or [CBool False,CBool False]) == (CBool False :: C))+         (constraintSimplify oracleAll (Or [CBool False,CBool False]) == (CBool False :: C))     , testProperty "Constraint reduce: Xor [True,False,True]"-         (constraintReduce oracleAll (Xor [CBool True,CBool False,CBool True]) == (CBool False :: C))+         (constraintSimplify oracleAll (Xor [CBool True,CBool False,CBool True]) == (CBool False :: C))    , testProperty "Constraint reduce: Xor [True,False,False]"-         (constraintReduce oracleAll (Xor [CBool True,CBool False,CBool False]) == (CBool True :: C))+         (constraintSimplify oracleAll (Xor [CBool True,CBool False,CBool False]) == (CBool True :: C))     , testProperty "Constraint reduce: Not (Xor [True,False,False])"-         (constraintReduce oracleAll (Not (Xor [CBool True,CBool False,CBool False])) == (CBool False :: C))+         (constraintSimplify oracleAll (Not (Xor [CBool True,CBool False,CBool False])) == (CBool False :: C))    , testProperty "Constraint reduce: Not (Xor [False,False,False])"-         (constraintReduce oracleAll (Not (Xor [CBool False,CBool False,CBool False])) == (CBool True :: C))+         (constraintSimplify oracleAll (Not (Xor [CBool False,CBool False,CBool False])) == (CBool True :: C))     , testProperty "Constraint reduce: matching oracle"-         (constraintReduce oracleA (Predicate PredA) == (CBool True :: C))+         (constraintSimplify oracleA (Predicate PredA) == (CBool True :: C))    , testProperty "Constraint reduce: non matching oracle"-         (constraintReduce oracleB (Predicate PredA) == (CBool False :: C))+         (constraintSimplify oracleB (Predicate PredA) == (CBool False :: C))     , testProperty "Constraint reduce: evalsTo 0"-         (constraintReduce oracleAll (simpleRule `evalsTo` 0) == (CBool False :: C))+         (constraintSimplify oracleAll (simpleRule `evalsTo` 0) == (CBool False :: C))    , testProperty "Constraint reduce: evalsTo 1"-         (constraintReduce oracleAll (simpleRule `evalsTo` 1) == (CBool True :: C))+         (constraintSimplify oracleAll (simpleRule `evalsTo` 1) == (CBool True :: C))    , testProperty "Constraint reduce: evals to D"-         (constraintReduce oracleA (d1 `evalsTo` PD 0 "Test") == (CBool True :: C))+         (constraintSimplify oracleA (d1 `evalsTo` PD 0 "Test") == (CBool True :: C))     , testProperty "Evals to: Terminal 0"          (((Terminal 0 :: R Int NT) `evalsTo` 0) == (CBool True :: C))@@ -146,27 +149,27 @@          )     , testProperty "Ordered non terminal 0"-         (case reducePredicates oracleAB (orderedNonTerminal [(Predicate PredA, Terminal 0 :: R Int NT)+         (case reducePredicates oracleAB (OrderedNonTerminal [(Predicate PredA, Terminal 0 :: R Int NT)                                                              ,(Predicate PredB, Terminal 1)                                                              ]) of             Match 0 -> True             _       -> False          )    , testProperty "Ordered non terminal 1"-         (case reducePredicates oracleAB (orderedNonTerminal [(Predicate PredB, Terminal 1 :: R Int NT)+         (case reducePredicates oracleAB (OrderedNonTerminal [(Predicate PredB, Terminal 1 :: R Int NT)                                                              ,(Predicate PredA, Terminal 0)                                                              ]) of             Match 1 -> True             _       -> False          )    , testProperty "Get predicates: flat"-         (sort (getPredicates d1) == sort [PredA,PredC,PredD,PredE])+         (getPredicates d1 == Set.fromList [PredA,PredC,PredD,PredE])     , testProperty "Get predicates: nested"-         (sort (getPredicates d2) == sort [PredA,PredB,PredC,PredD])+         (getPredicates d2 == Set.fromList [PredA,PredB,PredC,PredD])     , testProperty "Create predicate table: flat non terminal"-         (case createPredicateTable d1 (const True) False of+         (case createPredicateTable d1 (const True) of             Left _   -> False             Right xs -> sort (fmap (oraclePredicates . fst) xs) == sort                            [ [(PredA, SetPred)  , (PredC, UnsetPred), (PredD, UnsetPred), (PredE, UnsetPred)]@@ -175,37 +178,24 @@                            ]          )    , testProperty "Create predicate table: nested non terminal"-         (case createPredicateTable d2 (const True) False of+         (case createPredicateTable d2 (const True) of             Left _   -> False             Right xs -> sort (fmap (oraclePredicates . fst) xs) == sort-                  [ [(PredA,SetPred),(PredB,SetPred),(PredC,UnsetPred),(PredD,UnsetPred)]-                  , [(PredA,SetPred),(PredB,UnsetPred),(PredC,SetPred),(PredD,SetPred)]-                  , [(PredA,SetPred),(PredB,UnsetPred),(PredC,SetPred),(PredD,UnsetPred)]-                  , [(PredA,SetPred),(PredB,UnsetPred),(PredC,UnsetPred),(PredD,SetPred)]-                  , [(PredA,SetPred),(PredB,UnsetPred),(PredC,UnsetPred),(PredD,UnsetPred)]-                  , [(PredA,UnsetPred),(PredB,SetPred),(PredC,SetPred),(PredD,UnsetPred)]-                  , [(PredA,UnsetPred),(PredB,SetPred),(PredC,UnsetPred),(PredD,SetPred)]-                  , [(PredA,SetPred),(PredB,UnsetPred),(PredD,SetPred)]-                  , [(PredA,SetPred),(PredB,UnsetPred),(PredD,UnsetPred)]-                  , [(PredA,SetPred),(PredB,UnsetPred),(PredC,SetPred)]-                  , [(PredA,SetPred),(PredB,UnsetPred),(PredC,UnsetPred)]-                  , [(PredA,SetPred),(PredB,UnsetPred)]+                  [[(PredA,SetPred),(PredB,SetPred),(PredC,UnsetPred),(PredD,UnsetPred)]+                  ,[(PredA,SetPred),(PredB,UnsetPred)]+                  ,[(PredA,SetPred),(PredB,UnsetPred),(PredC,SetPred)]+                  ,[(PredA,SetPred),(PredB,UnsetPred),(PredC,SetPred),(PredD,SetPred)]+                  ,[(PredA,SetPred),(PredB,UnsetPred),(PredC,SetPred),(PredD,UnsetPred)]+                  ,[(PredA,SetPred),(PredB,UnsetPred),(PredC,UnsetPred)]+                  ,[(PredA,SetPred),(PredB,UnsetPred),(PredC,UnsetPred),(PredD,SetPred)]+                  ,[(PredA,SetPred),(PredB,UnsetPred),(PredC,UnsetPred),(PredD,UnsetPred)]+                  ,[(PredA,SetPred),(PredB,UnsetPred),(PredD,SetPred)]+                  ,[(PredA,SetPred),(PredB,UnsetPred),(PredD,UnsetPred)]+                  ,[(PredA,UnsetPred),(PredB,SetPred),(PredC,SetPred),(PredD,UnsetPred)]+                  ,[(PredA,UnsetPred),(PredB,SetPred),(PredC,UnsetPred),(PredD,SetPred)]                   ]          ) -   , testProperty "Create full predicate table: nested non terminal"-         (case createPredicateTable d2 (const True) True of-            Left _   -> False-            Right xs -> sort (fmap (oraclePredicates . fst) xs) == sort-                  [ [(PredA,SetPred),(PredB,UnsetPred),(PredC,SetPred),(PredD,SetPred)]-                  , [(PredA,UnsetPred),(PredB,SetPred),(PredC,UnsetPred),(PredD,SetPred)]-                  , [(PredA,SetPred),(PredB,UnsetPred),(PredC,UnsetPred),(PredD,SetPred)]-                  , [(PredA,UnsetPred),(PredB,SetPred),(PredC,SetPred),(PredD,UnsetPred)]-                  , [(PredA,SetPred),(PredB,UnsetPred),(PredC,SetPred),(PredD,UnsetPred)]-                  , [(PredA,SetPred),(PredB,SetPred),(PredC,UnsetPred),(PredD,UnsetPred)]-                  , [(PredA,SetPred),(PredB,UnsetPred),(PredC,UnsetPred),(PredD,UnsetPred)]-                  ]-         )    ]     where
src/tests/Main.hs view
@@ -1,5 +1,31 @@+{-# LANGUAGE LambdaCase #-}+ import Haskus.Tests.Utils import Test.Tasty+import Test.DocTest +import Control.Exception+import System.Exit++ main :: IO ()-main = defaultMain testsUtils+main = wrapTests+   [ title "TASTY"   $ defaultMain testsUtils+   , title "DOCTEST" $ doctest ["src/lib/"]+   ]++title :: String -> IO () -> IO ()+title s m = do+   putStrLn ""+   putStrLn (replicate 30 '=')+   putStrLn s+   putStrLn (replicate 30 '=')+   m++wrap :: IO () -> IO Bool+wrap m = (m >> return True) `catch` (\e -> return (e == ExitSuccess))++wrapTests :: [IO ()] -> IO ()+wrapTests ts = (and <$> traverse wrap ts) >>= \case+   True  -> title "SUMMARY" exitSuccess+   False -> title "SUMMARY" exitFailure