packages feed

souffle-haskell 1.1.0 → 2.0.0

raw patch · 16 files changed

+2642/−275 lines, 16 filesdep +neat-interpolationPVP ok

version bump matches the API change (PVP)

Dependencies added: neat-interpolation

API changes (from Hackage documentation)

- Language.Souffle.Class: -- <a>init</a>, which returns a handle of this type.
- Language.Souffle.Class: init :: (MonadSouffle m, Program prog) => prog -> m (Maybe (Handler m prog))
- Language.Souffle.Compiled: -- <a>init</a>, which returns a handle of this type.
- Language.Souffle.Compiled: init :: (MonadSouffle m, Program prog) => prog -> m (Maybe (Handler m prog))
- Language.Souffle.Interpreted: -- <a>init</a>, which returns a handle of this type.
- Language.Souffle.Interpreted: cleanup :: forall prog. Program prog => Handle prog -> SouffleM ()
- Language.Souffle.Interpreted: init :: (MonadSouffle m, Program prog) => prog -> m (Maybe (Handler m prog))
+ Language.Souffle.Class: --
+ Language.Souffle.Class: -- Souffle-related actions.
+ Language.Souffle.Class: -- The handle is used in all other functions of this typeclass to perform
+ Language.Souffle.Class: -- compile-time checks that a fact is only used in valid situations. For
+ Language.Souffle.Class: -- more information, see the <a>Direction</a> type.
+ Language.Souffle.Class: Input :: Direction
+ Language.Souffle.Class: InputOutput :: Direction
+ Language.Souffle.Class: Internal :: Direction
+ Language.Souffle.Class: Output :: Direction
+ Language.Souffle.Class: class Marshal a
+ Language.Souffle.Class: data Direction
+ Language.Souffle.Class: pop :: (Marshal a, Generic a, SimpleProduct a, GMarshal (Rep a), MonadPop m) => m a
+ Language.Souffle.Class: push :: (Marshal a, Generic a, SimpleProduct a, GMarshal (Rep a), MonadPush m) => a -> m ()
+ Language.Souffle.Compiled: --
+ Language.Souffle.Compiled: -- Souffle-related actions.
+ Language.Souffle.Compiled: -- The handle is used in all other functions of this typeclass to perform
+ Language.Souffle.Compiled: -- compile-time checks that a fact is only used in valid situations. For
+ Language.Souffle.Compiled: -- more information, see the <a>Direction</a> type.
+ Language.Souffle.Compiled: Input :: Direction
+ Language.Souffle.Compiled: InputOutput :: Direction
+ Language.Souffle.Compiled: Internal :: Direction
+ Language.Souffle.Compiled: Output :: Direction
+ Language.Souffle.Compiled: class MonadSouffle m => MonadSouffleFileIO m
+ Language.Souffle.Compiled: data Direction
+ Language.Souffle.Compiled: loadFiles :: MonadSouffleFileIO m => Handler m prog -> FilePath -> m ()
+ Language.Souffle.Compiled: writeFiles :: MonadSouffleFileIO m => Handler m prog -> FilePath -> m ()
+ Language.Souffle.Experimental: (.!=) :: Term ctx ty -> Term ctx ty -> Body ctx ()
+ Language.Souffle.Experimental: (.%) :: (Num ty, Integral ty) => Term ctx ty -> Term ctx ty -> Term ctx ty
+ Language.Souffle.Experimental: (.<) :: Num ty => Term ctx ty -> Term ctx ty -> Body ctx ()
+ Language.Souffle.Experimental: (.<=) :: Num ty => Term ctx ty -> Term ctx ty -> Body ctx ()
+ Language.Souffle.Experimental: (.=) :: Term ctx ty -> Term ctx ty -> Body ctx ()
+ Language.Souffle.Experimental: (.>) :: Num ty => Term ctx ty -> Term ctx ty -> Body ctx ()
+ Language.Souffle.Experimental: (.>=) :: Num ty => Term ctx ty -> Term ctx ty -> Body ctx ()
+ Language.Souffle.Experimental: (.^) :: Num ty => Term ctx ty -> Term ctx ty -> Term ctx ty
+ Language.Souffle.Experimental: (\/) :: Body ctx () -> Body ctx () -> Body ctx ()
+ Language.Souffle.Experimental: (|-) :: Head 'Relation a -> Body 'Relation () -> DSL prog 'Definition ()
+ Language.Souffle.Experimental: Definition :: UsageContext
+ Language.Souffle.Experimental: Input :: Direction
+ Language.Souffle.Experimental: InputOutput :: Direction
+ Language.Souffle.Experimental: Internal :: Direction
+ Language.Souffle.Experimental: Metadata :: StructureOpt a -> InlineOpt (FactDirection a) -> Metadata a
+ Language.Souffle.Experimental: Output :: Direction
+ Language.Souffle.Experimental: Predicate :: (forall f ctx. Fragment f ctx => Tuple ctx (Structure a) -> f ctx ()) -> Predicate a
+ Language.Souffle.Experimental: Relation :: UsageContext
+ Language.Souffle.Experimental: [Automatic] :: StructureOpt a
+ Language.Souffle.Experimental: [BTree] :: StructureOpt a
+ Language.Souffle.Experimental: [Brie] :: StructureOpt a
+ Language.Souffle.Experimental: [EqRel] :: (IsBinaryRelation a, Structure a ~ '[t, t]) => StructureOpt a
+ Language.Souffle.Experimental: [Inline] :: InlineOpt 'Internal
+ Language.Souffle.Experimental: [NoInline] :: InlineOpt d
+ Language.Souffle.Experimental: __ :: Term ctx ty
+ Language.Souffle.Experimental: band :: (Num ty, Integral ty) => Term ctx ty -> Term ctx ty -> Term ctx ty
+ Language.Souffle.Experimental: bor :: (Num ty, Integral ty) => Term ctx ty -> Term ctx ty -> Term ctx ty
+ Language.Souffle.Experimental: bxor :: (Num ty, Integral ty) => Term ctx ty -> Term ctx ty -> Term ctx ty
+ Language.Souffle.Experimental: cat :: ToString ty => Term ctx ty -> Term ctx ty -> Term ctx ty
+ Language.Souffle.Experimental: class (Fact a, SimpleProduct a) => FactMetadata a
+ Language.Souffle.Experimental: class Fragment f ctx
+ Language.Souffle.Experimental: class Num ty => SupportsArithmetic ty
+ Language.Souffle.Experimental: contains :: ToString ty => Term ctx ty -> Term ctx ty -> Body ctx ()
+ Language.Souffle.Experimental: data Body ctx a
+ Language.Souffle.Experimental: data DSL prog ctx a
+ Language.Souffle.Experimental: data Direction
+ Language.Souffle.Experimental: data Head ctx unused
+ Language.Souffle.Experimental: data InlineOpt (d :: Direction)
+ Language.Souffle.Experimental: data Metadata a
+ Language.Souffle.Experimental: data StructureOpt (a :: Type)
+ Language.Souffle.Experimental: data Term ctx ty
+ Language.Souffle.Experimental: data UsageContext
+ Language.Souffle.Experimental: embedProgram :: Program prog => prog -> DSL prog 'Definition () -> Q [Dec]
+ Language.Souffle.Experimental: factOpts :: FactMetadata a => Proxy a -> Metadata a
+ Language.Souffle.Experimental: infix 1 .!=
+ Language.Souffle.Experimental: infixl 0 |-
+ Language.Souffle.Experimental: instance (GHC.TypeLits.KnownSymbol s, Language.Souffle.Experimental.KnownSymbols symbols) => Language.Souffle.Experimental.KnownSymbols (s : symbols)
+ Language.Souffle.Experimental: instance (Language.Souffle.Experimental.KnownDLType t, Language.Souffle.Experimental.KnownDLTypes ts) => Language.Souffle.Experimental.KnownDLTypes (t : ts)
+ Language.Souffle.Experimental: instance (Language.Souffle.Experimental.SupportsArithmetic ty, GHC.Num.Num ty) => GHC.Num.Num (Language.Souffle.Experimental.Term ctx ty)
+ Language.Souffle.Experimental: instance Data.String.IsString (Language.Souffle.Experimental.Term ctx Data.Text.Internal.Lazy.Text)
+ Language.Souffle.Experimental: instance Data.String.IsString (Language.Souffle.Experimental.Term ctx Data.Text.Internal.Text)
+ Language.Souffle.Experimental: instance Data.String.IsString (Language.Souffle.Experimental.Term ctx GHC.Base.String)
+ Language.Souffle.Experimental: instance GHC.Real.Fractional (Language.Souffle.Experimental.Term ctx GHC.Types.Float)
+ Language.Souffle.Experimental: instance Language.Souffle.Experimental.Fragment Language.Souffle.Experimental.Body 'Language.Souffle.Experimental.Relation
+ Language.Souffle.Experimental: instance Language.Souffle.Experimental.Fragment Language.Souffle.Experimental.Head 'Language.Souffle.Experimental.Relation
+ Language.Souffle.Experimental: instance Language.Souffle.Experimental.KnownDLType Data.Text.Internal.Lazy.Text
+ Language.Souffle.Experimental: instance Language.Souffle.Experimental.KnownDLType Data.Text.Internal.Text
+ Language.Souffle.Experimental: instance Language.Souffle.Experimental.KnownDLType GHC.Base.String
+ Language.Souffle.Experimental: instance Language.Souffle.Experimental.KnownDLType GHC.Int.Int32
+ Language.Souffle.Experimental: instance Language.Souffle.Experimental.KnownDLType GHC.Types.Float
+ Language.Souffle.Experimental: instance Language.Souffle.Experimental.KnownDLType GHC.Word.Word32
+ Language.Souffle.Experimental: instance Language.Souffle.Experimental.KnownDLTypes '[]
+ Language.Souffle.Experimental: instance Language.Souffle.Experimental.KnownDirection 'Language.Souffle.Class.Input
+ Language.Souffle.Experimental: instance Language.Souffle.Experimental.KnownDirection 'Language.Souffle.Class.InputOutput
+ Language.Souffle.Experimental: instance Language.Souffle.Experimental.KnownDirection 'Language.Souffle.Class.Internal
+ Language.Souffle.Experimental: instance Language.Souffle.Experimental.KnownDirection 'Language.Souffle.Class.Output
+ Language.Souffle.Experimental: instance Language.Souffle.Experimental.KnownSymbols '[]
+ Language.Souffle.Experimental: instance Language.Souffle.Experimental.SupportsArithmetic GHC.Int.Int32
+ Language.Souffle.Experimental: instance Language.Souffle.Experimental.SupportsArithmetic GHC.Types.Float
+ Language.Souffle.Experimental: instance Language.Souffle.Experimental.SupportsArithmetic GHC.Word.Word32
+ Language.Souffle.Experimental: instance Language.Souffle.Experimental.ToString Data.Text.Internal.Lazy.Text
+ Language.Souffle.Experimental: instance Language.Souffle.Experimental.ToString Data.Text.Internal.Text
+ Language.Souffle.Experimental: instance Language.Souffle.Experimental.ToString GHC.Base.String
+ Language.Souffle.Experimental: instance Language.Souffle.Experimental.ToTerms '[t1, t2, t3, t4, t5, t6, t7, t8, t9, t10]
+ Language.Souffle.Experimental: instance Language.Souffle.Experimental.ToTerms '[t1, t2, t3, t4, t5, t6, t7, t8, t9]
+ Language.Souffle.Experimental: instance Language.Souffle.Experimental.ToTerms '[t1, t2, t3, t4, t5, t6, t7, t8]
+ Language.Souffle.Experimental: instance Language.Souffle.Experimental.ToTerms '[t1, t2, t3, t4, t5, t6, t7]
+ Language.Souffle.Experimental: instance Language.Souffle.Experimental.ToTerms '[t1, t2, t3, t4, t5, t6]
+ Language.Souffle.Experimental: instance Language.Souffle.Experimental.ToTerms '[t1, t2, t3, t4, t5]
+ Language.Souffle.Experimental: instance Language.Souffle.Experimental.ToTerms '[t1, t2, t3, t4]
+ Language.Souffle.Experimental: instance Language.Souffle.Experimental.ToTerms '[t1, t2, t3]
+ Language.Souffle.Experimental: instance Language.Souffle.Experimental.ToTerms '[t1, t2]
+ Language.Souffle.Experimental: instance Language.Souffle.Experimental.ToTerms '[t]
+ Language.Souffle.Experimental: instance forall k (ctx :: k). Control.Monad.Writer.Class.MonadWriter [Language.Souffle.Experimental.AST] (Language.Souffle.Experimental.Body ctx)
+ Language.Souffle.Experimental: instance forall k (ctx :: k). GHC.Base.Applicative (Language.Souffle.Experimental.Body ctx)
+ Language.Souffle.Experimental: instance forall k (ctx :: k). GHC.Base.Functor (Language.Souffle.Experimental.Body ctx)
+ Language.Souffle.Experimental: instance forall k (ctx :: k). GHC.Base.Monad (Language.Souffle.Experimental.Body ctx)
+ Language.Souffle.Experimental: instance forall k (prog :: k). Language.Souffle.Experimental.Fragment (Language.Souffle.Experimental.DSL prog) 'Language.Souffle.Experimental.Definition
+ Language.Souffle.Experimental: instance forall k1 (prog :: k1) k2 (ctx :: k2). Control.Monad.State.Class.MonadState Language.Souffle.Experimental.VarMap (Language.Souffle.Experimental.DSL prog ctx)
+ Language.Souffle.Experimental: instance forall k1 (prog :: k1) k2 (ctx :: k2). Control.Monad.Writer.Class.MonadWriter [Language.Souffle.Experimental.AST] (Language.Souffle.Experimental.DSL prog ctx)
+ Language.Souffle.Experimental: instance forall k1 (prog :: k1) k2 (ctx :: k2). GHC.Base.Applicative (Language.Souffle.Experimental.DSL prog ctx)
+ Language.Souffle.Experimental: instance forall k1 (prog :: k1) k2 (ctx :: k2). GHC.Base.Functor (Language.Souffle.Experimental.DSL prog ctx)
+ Language.Souffle.Experimental: instance forall k1 (prog :: k1) k2 (ctx :: k2). GHC.Base.Monad (Language.Souffle.Experimental.DSL prog ctx)
+ Language.Souffle.Experimental: land :: (Num ty, Integral ty) => Term ctx ty -> Term ctx ty -> Term ctx ty
+ Language.Souffle.Experimental: lor :: (Num ty, Integral ty) => Term ctx ty -> Term ctx ty -> Term ctx ty
+ Language.Souffle.Experimental: match :: ToString ty => Term ctx ty -> Term ctx ty -> Body ctx ()
+ Language.Souffle.Experimental: max' :: Num ty => Term ctx ty -> Term ctx ty -> Term ctx ty
+ Language.Souffle.Experimental: min' :: Num ty => Term ctx ty -> Term ctx ty -> Term ctx ty
+ Language.Souffle.Experimental: newtype Predicate a
+ Language.Souffle.Experimental: not' :: Body ctx a -> Body ctx ()
+ Language.Souffle.Experimental: ord :: ToString ty => Term ctx ty -> Term ctx Int32
+ Language.Souffle.Experimental: predicateFor :: forall a prog. ToPredicate prog a => DSL prog 'Definition (Predicate a)
+ Language.Souffle.Experimental: render :: Program prog => prog -> DSL prog 'Definition () -> Text
+ Language.Souffle.Experimental: renderIO :: Program prog => prog -> FilePath -> DSL prog 'Definition () -> IO ()
+ Language.Souffle.Experimental: runSouffleInterpreted :: (MonadIO m, Program prog) => prog -> DSL prog 'Definition () -> (Maybe (Handle prog) -> SouffleM a) -> m a
+ Language.Souffle.Experimental: runSouffleInterpretedWith :: (MonadIO m, Program prog) => Config -> prog -> DSL prog 'Definition () -> (Maybe (Handle prog) -> SouffleM a) -> m a
+ Language.Souffle.Experimental: strlen :: ToString ty => Term ctx ty -> Term ctx Int32
+ Language.Souffle.Experimental: substr :: ToString ty => Term ctx ty -> Term ctx Int32 -> Term ctx Int32 -> Term ctx ty
+ Language.Souffle.Experimental: to_number :: ToString ty => Term ctx ty -> Term ctx Int32
+ Language.Souffle.Experimental: to_string :: ToString ty => Term ctx Int32 -> Term ctx ty
+ Language.Souffle.Experimental: type ToPredicate prog a = (Fact a, FactMetadata a, ContainsFact prog a, SimpleProduct a, Assert (Length (Structure a) <=? 10) BigTupleError, KnownDLTypes (Structure a), KnownDirection (FactDirection a), KnownSymbols (AccessorNames a), ToTerms (Structure a))
+ Language.Souffle.Experimental: type Tuple ctx ts = TupleOf (MapType (Term ctx) ts)
+ Language.Souffle.Experimental: type VarName = Text
+ Language.Souffle.Experimental: type family NoVarsInAtom (ctx :: UsageContext) :: Constraint
+ Language.Souffle.Experimental: underscore :: Term ctx ty
+ Language.Souffle.Experimental: var :: NoVarsInAtom ctx => VarName -> DSL prog ctx' (Term ctx ty)
+ Language.Souffle.Interpreted: --
+ Language.Souffle.Interpreted: -- Souffle-related actions.
+ Language.Souffle.Interpreted: -- The handle is used in all other functions of this typeclass to perform
+ Language.Souffle.Interpreted: -- compile-time checks that a fact is only used in valid situations. For
+ Language.Souffle.Interpreted: -- more information, see the <a>Direction</a> type.
+ Language.Souffle.Interpreted: Input :: Direction
+ Language.Souffle.Interpreted: InputOutput :: Direction
+ Language.Souffle.Interpreted: Internal :: Direction
+ Language.Souffle.Interpreted: Output :: Direction
+ Language.Souffle.Interpreted: data Direction
- Language.Souffle.Class: addFact :: (MonadSouffle m, Fact a, ContainsFact prog a) => Handler m prog -> a -> m ()
+ Language.Souffle.Class: addFact :: (MonadSouffle m, Fact a, ContainsInputFact prog a) => Handler m prog -> a -> m ()
- Language.Souffle.Class: addFacts :: (MonadSouffle m, Foldable t, Fact a, ContainsFact prog a) => Handler m prog -> t a -> m ()
+ Language.Souffle.Class: addFacts :: (MonadSouffle m, Foldable t, Fact a, ContainsInputFact prog a) => Handler m prog -> t a -> m ()
- Language.Souffle.Class: class Marshal a => Fact a
+ Language.Souffle.Class: class Marshal a => Fact a where {
- Language.Souffle.Class: findFact :: (MonadSouffle m, Fact a, ContainsFact prog a, Eq a) => Handler m prog -> a -> m (Maybe a)
+ Language.Souffle.Class: findFact :: (MonadSouffle m, Fact a, ContainsOutputFact prog a, Eq a) => Handler m prog -> a -> m (Maybe a)
- Language.Souffle.Class: getFacts :: (MonadSouffle m, Fact a, ContainsFact prog a, CollectFacts m c) => Handler m prog -> m (c a)
+ Language.Souffle.Class: getFacts :: (MonadSouffle m, Fact a, ContainsOutputFact prog a, CollectFacts m c) => Handler m prog -> m (c a)
- Language.Souffle.Class: programName :: Program a => Proxy a -> String
+ Language.Souffle.Class: programName :: Program a => a -> String
- Language.Souffle.Compiled: addFact :: (MonadSouffle m, Fact a, ContainsFact prog a) => Handler m prog -> a -> m ()
+ Language.Souffle.Compiled: addFact :: (MonadSouffle m, Fact a, ContainsInputFact prog a) => Handler m prog -> a -> m ()
- Language.Souffle.Compiled: addFacts :: (MonadSouffle m, Foldable t, Fact a, ContainsFact prog a) => Handler m prog -> t a -> m ()
+ Language.Souffle.Compiled: addFacts :: (MonadSouffle m, Foldable t, Fact a, ContainsInputFact prog a) => Handler m prog -> t a -> m ()
- Language.Souffle.Compiled: class Marshal a => Fact a
+ Language.Souffle.Compiled: class Marshal a => Fact a where {
- Language.Souffle.Compiled: findFact :: (MonadSouffle m, Fact a, ContainsFact prog a, Eq a) => Handler m prog -> a -> m (Maybe a)
+ Language.Souffle.Compiled: findFact :: (MonadSouffle m, Fact a, ContainsOutputFact prog a, Eq a) => Handler m prog -> a -> m (Maybe a)
- Language.Souffle.Compiled: getFacts :: (MonadSouffle m, Fact a, ContainsFact prog a, CollectFacts m c) => Handler m prog -> m (c a)
+ Language.Souffle.Compiled: getFacts :: (MonadSouffle m, Fact a, ContainsOutputFact prog a, CollectFacts m c) => Handler m prog -> m (c a)
- Language.Souffle.Compiled: pop :: (Marshal a, Generic a, SimpleProduct a (Rep a), GMarshal (Rep a), MonadPop m) => m a
+ Language.Souffle.Compiled: pop :: (Marshal a, Generic a, SimpleProduct a, GMarshal (Rep a), MonadPop m) => m a
- Language.Souffle.Compiled: programName :: Program a => Proxy a -> String
+ Language.Souffle.Compiled: programName :: Program a => a -> String
- Language.Souffle.Compiled: push :: (Marshal a, Generic a, SimpleProduct a (Rep a), GMarshal (Rep a), MonadPush m) => a -> m ()
+ Language.Souffle.Compiled: push :: (Marshal a, Generic a, SimpleProduct a, GMarshal (Rep a), MonadPush m) => a -> m ()
- Language.Souffle.Compiled: runSouffle :: SouffleM a -> IO a
+ Language.Souffle.Compiled: runSouffle :: forall prog a. Program prog => prog -> (Maybe (Handle prog) -> SouffleM a) -> IO a
- Language.Souffle.Internal.Constraints: type family SimpleProduct (a :: Type) (f :: Type -> Type) :: Constraint
+ Language.Souffle.Internal.Constraints: type family SimpleProduct (a :: Type) :: Constraint
- Language.Souffle.Interpreted: addFact :: (MonadSouffle m, Fact a, ContainsFact prog a) => Handler m prog -> a -> m ()
+ Language.Souffle.Interpreted: addFact :: (MonadSouffle m, Fact a, ContainsInputFact prog a) => Handler m prog -> a -> m ()
- Language.Souffle.Interpreted: addFacts :: (MonadSouffle m, Foldable t, Fact a, ContainsFact prog a) => Handler m prog -> t a -> m ()
+ Language.Souffle.Interpreted: addFacts :: (MonadSouffle m, Foldable t, Fact a, ContainsInputFact prog a) => Handler m prog -> t a -> m ()
- Language.Souffle.Interpreted: class Marshal a => Fact a
+ Language.Souffle.Interpreted: class Marshal a => Fact a where {
- Language.Souffle.Interpreted: findFact :: (MonadSouffle m, Fact a, ContainsFact prog a, Eq a) => Handler m prog -> a -> m (Maybe a)
+ Language.Souffle.Interpreted: findFact :: (MonadSouffle m, Fact a, ContainsOutputFact prog a, Eq a) => Handler m prog -> a -> m (Maybe a)
- Language.Souffle.Interpreted: getFacts :: (MonadSouffle m, Fact a, ContainsFact prog a, CollectFacts m c) => Handler m prog -> m (c a)
+ Language.Souffle.Interpreted: getFacts :: (MonadSouffle m, Fact a, ContainsOutputFact prog a, CollectFacts m c) => Handler m prog -> m (c a)
- Language.Souffle.Interpreted: pop :: (Marshal a, Generic a, SimpleProduct a (Rep a), GMarshal (Rep a), MonadPop m) => m a
+ Language.Souffle.Interpreted: pop :: (Marshal a, Generic a, SimpleProduct a, GMarshal (Rep a), MonadPop m) => m a
- Language.Souffle.Interpreted: programName :: Program a => Proxy a -> String
+ Language.Souffle.Interpreted: programName :: Program a => a -> String
- Language.Souffle.Interpreted: push :: (Marshal a, Generic a, SimpleProduct a (Rep a), GMarshal (Rep a), MonadPush m) => a -> m ()
+ Language.Souffle.Interpreted: push :: (Marshal a, Generic a, SimpleProduct a, GMarshal (Rep a), MonadPush m) => a -> m ()
- Language.Souffle.Interpreted: runSouffle :: SouffleM a -> IO a
+ Language.Souffle.Interpreted: runSouffle :: Program prog => prog -> (Maybe (Handle prog) -> SouffleM a) -> IO a
- Language.Souffle.Interpreted: runSouffleWith :: Config -> SouffleM a -> IO a
+ Language.Souffle.Interpreted: runSouffleWith :: Program prog => Config -> prog -> (Maybe (Handle prog) -> SouffleM a) -> IO a
- Language.Souffle.Marshal: pop :: (Marshal a, Generic a, SimpleProduct a (Rep a), GMarshal (Rep a), MonadPop m) => m a
+ Language.Souffle.Marshal: pop :: (Marshal a, Generic a, SimpleProduct a, GMarshal (Rep a), MonadPop m) => m a
- Language.Souffle.Marshal: push :: (Marshal a, Generic a, SimpleProduct a (Rep a), GMarshal (Rep a), MonadPush m) => a -> m ()
+ Language.Souffle.Marshal: push :: (Marshal a, Generic a, SimpleProduct a, GMarshal (Rep a), MonadPush m) => a -> m ()

Files

CHANGELOG.md view
@@ -3,6 +3,33 @@ All notable changes to this project (as seen by library users) will be documented in this file. The CHANGELOG is available on [Github](https://github.com/luc-tielen/souffle-haskell.git/CHANGELOG.md). +## [2.0.0] - 2020-08-23++### Added++- The `Fact` typeclass now also requires you to specify the `FactDirection`.+  This prevents inconsistent and buggy behavior when trying to use a fact in+  an invalid way (e.g. trying to add an output-only fact).+- DSL for creating Soufflé programs directly from Haskell.+  See the docs of `Language.Souffle.Experimental` for more information.++### Changed++- souffle-haskell now supports Soufflé version 2.0.1.+- `getFacts`, `findFact`, `addFact` and `addFacts` now have stricter+  constraints in their type signatures to prevent invalid usage of facts.+- `runSouffle` for both compiled and interpreted mode and `runSouffleWith`+  for interpreted mode have updated type signatures to be able to+  automatically cleanup temporary files created while interacting with Souffle.++### Removed++- `init` function for both compiled and interpreted mode. Initialization is+  now handled by the `runSouffle*` functions. This change makes automatic+  cleanup of created files possible and prevents double initialization of+  Souffle programs.+- `cleanup` function for interpreted mode, this is handled automatically now.+ ## [1.1.0] - 2020-07-26  ### Added
README.md view
@@ -77,9 +77,11 @@ -- By making a data type an instance of Fact, we give Haskell the -- necessary information to bind to the datalog fact. instance Souffle.Fact Edge where+  type FactDirection Edge = 'Souffle.Input   factName = const "edge"  instance Souffle.Fact Reachable where+  type FactDirection Reachable = 'Souffle.Output   factName = const "reachable"  -- For simple product types, we can automatically generate the@@ -89,8 +91,7 @@   main :: IO ()-main = Souffle.runSouffle $ do-  maybeProgram <- Souffle.init Path  -- Initializes the Souffle program.+main = Souffle.runSouffle Path $ \maybeProgram -> do  -- Initializes the Souffle program.   case maybeProgram of     Nothing -> liftIO $ putStrLn "Failed to load program."     Just prog -> do@@ -182,12 +183,8 @@ change your Datalog code). However because the Souffle code is interpreted, it can't offer the same speed as in compiled mode. -The main differences with compiled mode are the following:--1. You need to import `Language.Souffle.Interpreted`-2. You need to call `Souffle.cleanup` after you no longer need the Souffle-   functionality. This will clean up the generated CSV fact files located in-   a temporary directory.+If you want to use interpreted Souffle, you need to import the+`Language.Souffle.Interpreted` module.  #### Interpreter configuration @@ -218,8 +215,7 @@ The main differences with interpreted mode are the following:  1. Compile the Datalog code with `souffle -g`.-2. Remove `Souffle.cleanup` if it is present in your code, compiled mode-   leaves no CSV artifacts.+2. Import `Language.Souffle.Compiled`  The [motivating example](#motivating-example) is a complete example for the compiled mode. 
lib/Language/Souffle/Class.hs view
@@ -14,9 +14,13 @@ --   This module also contains some helper type families for additional --   type safety and user-friendly error messages. module Language.Souffle.Class-  ( ContainsFact-  , Program(..)+  ( Program(..)   , Fact(..)+  , Marshal.Marshal(..)+  , Direction(..)+  , ContainsInputFact+  , ContainsOutputFact+  , ContainsFact   , MonadSouffle(..)   , MonadSouffleFileIO(..)   ) where@@ -35,8 +39,46 @@ import Type.Errors.Pretty  --- | A helper type family for checking if a specific Souffle `Program` contains a certain `Fact`.---   This will generate a user-friendly type error if this is not the case.+-- | A helper type family for checking if a specific Souffle `Program` contains+--   a certain `Fact`. Additionally, it also checks if the fact is marked as+--   either `Input` or `InputOutput`. This constraint will generate a+--   user-friendly type error if these conditions are not met.+type family ContainsInputFact prog fact :: Constraint where+  ContainsInputFact prog fact = (ContainsFact prog fact, IsInput fact (FactDirection fact))++-- | A helper type family for checking if a specific Souffle `Program` contains+--   a certain `Fact`. Additionally, it also checks if the fact is marked as+--   either `Output` or `InputOutput`. This constraint will generate a+--   user-friendly type error if these conditions are not met.+type family ContainsOutputFact prog fact :: Constraint where+  ContainsOutputFact prog fact = (ContainsFact prog fact, IsOutput fact (FactDirection fact))++type family IsInput (fact :: Type) (dir :: Direction) :: Constraint where+  IsInput _ 'Input = ()+  IsInput _ 'InputOutput = ()+  IsInput fact dir = TypeError+    ( "You tried to use an " <> FormatDirection dir <> " fact of type " <> fact <> " as an input."+    % "Possible solution: change the FactDirection of " <> fact+      <> " to either 'Input' or 'InputOutput'."+    )++type family IsOutput (fact :: Type) (dir :: Direction) :: Constraint where+  IsOutput _ 'Output = ()+  IsOutput _ 'InputOutput = ()+  IsOutput fact dir = TypeError+    ( "You tried to use an " <> FormatDirection dir <> " fact of type " <> fact <> " as an output."+    % "Possible solution: change the FactDirection of " <> fact+      <> " to either 'Output' or 'InputOutput'."+    )++type family FormatDirection (dir :: Direction) where+  FormatDirection 'Output = "output"+  FormatDirection 'Input = "input"+  FormatDirection 'Internal = "internal"++-- | A helper type family for checking if a specific Souffle `Program` contains+--   a certain `Fact`. This constraint will generate a user-friendly type error+--   if this is not the case. type family ContainsFact prog fact :: Constraint where   ContainsFact prog fact =     CheckContains prog (ProgramFacts prog) fact@@ -71,46 +113,55 @@    -- | Function for obtaining the name of a Datalog program.   --   This has to be the same as the name of the .dl file (minus the extension).-  ---  -- It uses a 'Proxy' to select the correct instance.-  programName :: Proxy a -> String+  programName :: a -> String  -- | A typeclass for data types representing a fact in datalog.+--+-- Example usage:+--+-- @+-- instance Fact Edge where+--   type FactDirection Edge = 'Input+--   factName = const "edge"+-- @ class Marshal.Marshal a => Fact a where+  -- | The direction or "mode" a fact can be used in.+  --   This is used to perform compile-time checks that a fact is only used+  --   in valid situations. For more information, see the 'Direction' type.+  type FactDirection a :: Direction+   -- | Function for obtaining the name of a fact   --   (has to be the same as described in the Datalog program).   --   -- It uses a 'Proxy' to select the correct instance.-  ---  -- Example usage:-  ---  -- @-  -- instance Fact Edge where-  --   factName = const "edge"-  -- @   factName :: Proxy a -> String +-- | A datatype describing which operations a certain fact supports.+--   The direction is from the datalog perspective, so that it+--   aligns with ".decl" statements in Souffle.+data Direction+  = Input+  -- ^ Fact can only be stored in Datalog (using `addFact`/`addFacts`).+  | Output+  -- ^ Fact can only be read from Datalog (using `getFacts`/`findFact`).+  | InputOutput+  -- ^ Fact supports both reading from / writing to Datalog.+  | Internal+  -- ^ Supports neither reading from / writing to Datalog. This is used for+  --   facts that are only visible inside Datalog itself.  -- | A mtl-style typeclass for Souffle-related actions. class Monad m => MonadSouffle m where   -- | Represents a handle for interacting with a Souffle program.-  --   See also `init`, which returns a handle of this type.+  --+  --   The handle is used in all other functions of this typeclass to perform+  --   Souffle-related actions.   type Handler m :: Type -> Type    -- | Helper associated type constraint that allows collecting facts from   --   Souffle in a list or vector. Only used internally.   type CollectFacts m (c :: Type -> Type) :: Constraint -  {- | Initializes a Souffle program.--     The action will return 'Nothing' if it failed to load the Souffle C++-     program or if it failed to find the Souffle interpreter (depending on-     compiled/interpreted variant).-     Otherwise it will return a handle that can be used in other functions-     in this module.-  -}-  init :: Program prog => prog -> m (Maybe (Handler m prog))-   -- | Runs the Souffle program.   run :: Handler m prog -> m () @@ -122,7 +173,7 @@    -- | Returns all facts of a program. This function makes use of type inference   --   to select the type of fact to return.-  getFacts :: (Fact a, ContainsFact prog a, CollectFacts m c)+  getFacts :: (Fact a, ContainsOutputFact prog a, CollectFacts m c)            => Handler m prog -> m (c a)    -- | Searches for a fact in a program.@@ -130,24 +181,22 @@   --   --   Conceptually equivalent to @List.find (== fact) \<$\> getFacts prog@,   --   but this operation can be implemented much faster.-  findFact :: (Fact a, ContainsFact prog a, Eq a)+  findFact :: (Fact a, ContainsOutputFact prog a, Eq a)            => Handler m prog -> a -> m (Maybe a)    -- | Adds a fact to the program.-  addFact :: (Fact a, ContainsFact prog a)+  addFact :: (Fact a, ContainsInputFact prog a)           => Handler m prog -> a -> m ()    -- | Adds multiple facts to the program. This function could be implemented   --   in terms of 'addFact', but this is done as a minor optimization.-  addFacts :: (Foldable t, Fact a, ContainsFact prog a)+  addFacts :: (Foldable t, Fact a, ContainsInputFact prog a)            => Handler m prog -> t a -> m ()  instance MonadSouffle m => MonadSouffle (ReaderT r m) where   type Handler (ReaderT r m) = Handler m   type CollectFacts (ReaderT r m) c = CollectFacts m c -  init = lift . init-  {-# INLINABLE init #-}   run = lift . run   {-# INLINABLE run #-}   setNumThreads prog = lift . setNumThreads prog@@ -167,8 +216,6 @@   type Handler (WriterT w m) = Handler m   type CollectFacts (WriterT w m) c = CollectFacts m c -  init = lift . init-  {-# INLINABLE init #-}   run = lift . run   {-# INLINABLE run #-}   setNumThreads prog = lift . setNumThreads prog@@ -188,8 +235,6 @@   type Handler (StateT s m) = Handler m   type CollectFacts (StateT s m) c = CollectFacts m c -  init = lift . init-  {-# INLINABLE init #-}   run = lift . run   {-# INLINABLE run #-}   setNumThreads prog = lift . setNumThreads prog@@ -209,8 +254,6 @@   type Handler (RWST r w s m) = Handler m   type CollectFacts (RWST r w s m) c = CollectFacts m c -  init = lift . init-  {-# INLINABLE init #-}   run = lift . run   {-# INLINABLE run #-}   setNumThreads prog = lift . setNumThreads prog@@ -230,8 +273,6 @@   type Handler (ExceptT e m) = Handler m   type CollectFacts (ExceptT e m) c = CollectFacts m c -  init = lift . init-  {-# INLINABLE init #-}   run = lift . run   {-# INLINABLE run #-}   setNumThreads prog = lift . setNumThreads prog
lib/Language/Souffle/Compiled.hs view
@@ -14,9 +14,13 @@   ( Program(..)   , Fact(..)   , Marshal(..)+  , Direction(..)+  , ContainsInputFact+  , ContainsOutputFact   , Handle   , SouffleM   , MonadSouffle(..)+  , MonadSouffleFileIO(..)   , runSouffle   ) where @@ -45,11 +49,26 @@ newtype Handle prog = Handle (ForeignPtr Internal.Souffle)  -- | A monad for executing Souffle-related actions in.-newtype SouffleM a-  = SouffleM-  { runSouffle :: IO a  -- ^ Returns the underlying IO action.-  } deriving ( Functor, Applicative, Monad, MonadIO ) via IO+newtype SouffleM a = SouffleM (IO a)+  deriving ( Functor, Applicative, Monad, MonadIO ) via IO +{- | Initializes and runs a Souffle program.++     The 2nd argument is passed in a handle after initialization of the+     Souffle program. The handle will contain 'Nothing' if it failed to+     load the Souffle C++ program. In the successful case it will contain+     a handle that can be used for performing Souffle related actions+     using the other functions in this module.+-}+runSouffle :: forall prog a. Program prog+           => prog -> (Maybe (Handle prog) -> SouffleM a) -> IO a+runSouffle prog action =+  let progName = programName prog+      (SouffleM result) = do+        handle <- fmap Handle <$> liftIO (Internal.init progName)+        action handle+   in result+ type Tuple = Ptr Internal.Tuple  -- | A monad used solely for marshalling and unmarshalling@@ -153,13 +172,6 @@   type Handler SouffleM = Handle   type CollectFacts SouffleM c = Collect c -  init :: forall prog. Program prog-       => prog -> SouffleM (Maybe (Handle prog))-  init _ =-    let progName = programName (Proxy :: Proxy prog)-    in SouffleM $ fmap Handle <$> Internal.init progName-  {-# INLINABLE init #-}-   run (Handle prog) = SouffleM $ Internal.run prog   {-# INLINABLE run #-} @@ -171,7 +183,7 @@     SouffleM $ Internal.getNumThreads prog   {-# INLINABLE getNumThreads #-} -  addFact :: forall a prog. (Fact a, ContainsFact prog a)+  addFact :: forall a prog. (Fact a, ContainsInputFact prog a)           => Handle prog -> a -> SouffleM ()   addFact (Handle prog) fact = liftIO $ do     let relationName = factName (Proxy :: Proxy a)@@ -179,7 +191,7 @@     addFact' relation fact   {-# INLINABLE addFact #-} -  addFacts :: forall t a prog . (Foldable t, Fact a, ContainsFact prog a)+  addFacts :: forall t a prog . (Foldable t, Fact a, ContainsInputFact prog a)            => Handle prog -> t a -> SouffleM ()   addFacts (Handle prog) facts = liftIO $ do     let relationName = factName (Proxy :: Proxy a)@@ -187,7 +199,7 @@     traverse_ (addFact' relation) facts   {-# INLINABLE addFacts #-} -  getFacts :: forall a c prog. (Fact a, ContainsFact prog a, Collect c)+  getFacts :: forall a c prog. (Fact a, ContainsOutputFact prog a, Collect c)            => Handle prog -> SouffleM (c a)   getFacts (Handle prog) = SouffleM $ do     let relationName = factName (Proxy :: Proxy a)@@ -196,7 +208,7 @@     Internal.getRelationIterator relation >>= collect factCount   {-# INLINABLE getFacts #-} -  findFact :: forall a prog. (Fact a, ContainsFact prog a)+  findFact :: forall a prog. (Fact a, ContainsOutputFact prog a)            => Handle prog -> a -> SouffleM (Maybe a)   findFact (Handle prog) fact = SouffleM $ do     let relationName = factName (Proxy :: Proxy a)
+ lib/Language/Souffle/Experimental.hs view
@@ -0,0 +1,1098 @@+{-# LANGUAGE GADTs, RankNTypes, TypeFamilies, DataKinds, TypeOperators, ConstraintKinds #-}+{-# LANGUAGE UndecidableInstances, UndecidableSuperClasses, FlexibleContexts #-}+{-# LANGUAGE MultiParamTypeClasses, FlexibleInstances, DerivingVia, ScopedTypeVariables #-}+{-# LANGUAGE PolyKinds #-}+{-# OPTIONS_GHC -Wno-redundant-constraints #-}++{-| This module provides an experimental DSL for generating Souffle Datalog code,+    directly from Haskell.++    The module is meant to be imported unqualified, unlike the rest of this+    library. This allows for a syntax that is very close to the corresponding+    Datalog syntax you would normally write.++    The functions and operators provided by this module follow a naming scheme:++    - If there is no clash with something imported via Prelude, the+      function or operator is named exactly the same as in Souffle.+    - If there is a clash for functions, an apostrophe is appended+      (e.g. "max" in Datalog is 'max'' in Haskell).+    - Most operators (besides those from the Num typeclass) start with a "."+      (e.g. '.^' is the "^"  operator in Datalog)++    The DSL makes heavy use of Haskell's typesystem to avoid+    many kinds of errors. This being said, not everything can be checked at+    compile-time (for example performing comparisons on ungrounded variables+    can't be checked). For this reason you should regularly write the+    Datalog code to a file while prototyping your algorithm and check it using+    the Souffle executable for errors.++    A large subset of the Souffle language is covered, with some exceptions+    such as "$", aggregates, ... There are no special functions for supporting+    components either, but this is automatically possible by making use of+    polymorphism in Haskell.++    Here's an example snippet of Haskell code that can generate Datalog code:++    @+    -- Assuming we have 2 types of facts named Edge and Reachable:+    data Edge = Edge String String+    data Reachable = Reachable String String++    program = do+      Predicate edge <- predicateFor \@Edge+      Predicate reachable <- predicateFor \@Reachable+      a <- var "a"+      b <- var "b"+      c <- var "c"+      reachable(a, b) |- edge(a, b)+      reachable(a, b) |- do+        edge(a, c)+        reachable(c, b)+    @++    When rendered to a file (using 'renderIO'), this generates the following+    Souffle code:++    @+    .decl edge(t1: symbol, t2: symbol)+    .input edge+    .decl reachable(t1: symbol, t2: symbol)+    .output reachable+    reachable(a, b) :-+      edge(a, b)+    reachable(a, b) :- do+      edge(a, c)+      reachable(c, b)+    @++    For more examples, take a look at the <https://github.com/luc-tielen/souffle-haskell/blob/2c24e1e169da269c45fc192ab5efd4ff2196114b/tests/Test/Language/Souffle/ExperimentalSpec.hs tests>.+-}+module Language.Souffle.Experimental+  ( -- * DSL-related types and functions+    -- ** Types+    Predicate(..)+  , Fragment+  , Tuple+  , DSL+  , Head+  , Body+  , Term+  , VarName+  , UsageContext(..)+  , Direction(..)+  , ToPredicate+  , FactMetadata(..)+  , Metadata(..)+  , StructureOpt(..)+  , InlineOpt(..)+  -- ** Basic building blocks+  , predicateFor+  , var+  , __+  , underscore+  , (|-)+  , (\/)+  , not'+  -- ** Souffle operators+  , (.<)+  , (.<=)+  , (.>)+  , (.>=)+  , (.=)+  , (.!=)+  , (.^)+  , (.%)+  , band+  , bor+  , bxor+  , lor+  , land+  -- ** Souffle functions+  , max'+  , min'+  , cat+  , contains+  , match+  , ord+  , strlen+  , substr+  , to_number+  , to_string+  -- * Functions for running a Datalog DSL fragment / AST directly.+  , runSouffleInterpretedWith+  , runSouffleInterpreted+  , embedProgram+  -- * Rendering functions+  , render+  , renderIO+  -- * Helper type families useful in some situations+  , Structure+  , NoVarsInAtom+  , SupportsArithmetic+  ) where++import Control.Monad.Reader+import Control.Monad.State+import Control.Monad.Writer+import Data.Int+import Data.Kind+import Data.List.NonEmpty (NonEmpty(..), toList)+import Data.Map ( Map )+import qualified Data.Map as Map+import Data.Maybe (fromMaybe, catMaybes, mapMaybe)+import Data.Proxy+import Data.String+import qualified Data.Text as T+import qualified Data.Text.IO as TIO+import qualified Data.Text.Lazy as TL+import Data.Word+import GHC.Generics+import GHC.TypeLits+import Language.Haskell.TH.Syntax (qRunIO, qAddForeignFilePath, Q, Dec, ForeignSrcLang(..))+import Language.Souffle.Class ( Program(..), Fact(..), ContainsFact, Direction(..) )+import Language.Souffle.Internal.Constraints (SimpleProduct)+import qualified Language.Souffle.Interpreted as I+import System.Directory+import System.FilePath+import System.IO.Temp+import System.Process+import Text.Printf (printf)+import Type.Errors.Pretty+++-- | A datatype that contains a function for generating Datalog AST fragments+--   that can be glued together using other functions in this module.+--+--   The rank-N type allows using the inner function in multiple places to+--   generate different parts of the AST. This is one of the key things+--   that allows writing Haskell code in a very smilar way to the Datalog code.+--+--   The inner function uses the 'Structure' of a type to compute what the+--   shape of the input tuple for the predicate should be. For example, if a+--   fact has a data constructor containing a Float and a String,+--   the resulting tuple will be of type ('Term' ctx Float, 'Term' ctx String).+--+--   Currently, only facts with up to 10 fields are supported. If you need more+--   fields, please file an issue on+--   <https://github.com/luc-tielen/souffle-haskell/issues Github>.+newtype Predicate a+  = Predicate (forall f ctx. Fragment f ctx => Tuple ctx (Structure a) -> f ctx ())++type VarMap = Map VarName Int++-- | The main monad in which Datalog AST fragments are combined together+--   using other functions in this module.+--+--   - The "prog" type variable is used for performing many compile time checks.+--     This variable is filled (automatically) with a type that implements the+--     'Program' typeclass.+--   - The "ctx" type variable is the context in which a DSL fragment is used.+--     For more information, see 'UsageContext'.+--   - The "a" type variable is the value contained inside+--     (just like other monads).+newtype DSL prog ctx a = DSL (StateT VarMap (Writer [AST]) a)+  deriving (Functor, Applicative, Monad, MonadWriter [AST], MonadState VarMap)+  via (StateT VarMap (Writer [AST]))++addDefinition :: AST -> DSL prog 'Definition ()+addDefinition dl = tell [dl]++-- | This function runs the DSL fragment directly using the souffle interpreter+--   executable.+--+--   It does this by saving the fragment to a temporary file right before+--   running the souffle interpreter. All created files are automatically+--   cleaned up after the souffle related actions have been executed. If this is+--   not your intended behavior, see 'runSouffleInterpretedWith' which allows+--   passing in different interpreter settings.+runSouffleInterpreted+  :: (MonadIO m, Program prog)+  => prog+  -> DSL prog 'Definition ()+  -> (Maybe (I.Handle prog) -> I.SouffleM a)+  -> m a+runSouffleInterpreted program dsl f = liftIO $ do+  tmpDir <- getCanonicalTemporaryDirectory+  souffleHsDir <- createTempDirectory tmpDir "souffle-haskell"+  defaultCfg <- I.defaultConfig+  let cfg = defaultCfg { I.cfgDatalogDir = souffleHsDir+                       , I.cfgFactDir = Just souffleHsDir+                       , I.cfgOutputDir = Just souffleHsDir+                       }+  runSouffleInterpretedWith cfg program dsl f <* removeDirectoryRecursive souffleHsDir++-- | This function runs the DSL fragment directly using the souffle interpreter+--   executable.+--+--   It does this by saving the fragment to a file in the directory specified by+--   the 'I.cfgDatalogDir' field in the interpreter settings. Depending on the+--   chosen settings, the fact and output files may not be automatically cleaned+--   up after running the souffle interpreter. See 'I.runSouffleWith' for more+--   information on automatic cleanup.+runSouffleInterpretedWith+  :: (MonadIO m, Program prog)+  => I.Config+  -> prog+  -> DSL prog 'Definition ()+  -> (Maybe (I.Handle prog) -> I.SouffleM a)+  -> m a+runSouffleInterpretedWith config program dsl f = liftIO $ do+  let progName = programName program+      datalogFile = I.cfgDatalogDir config </> progName <.> "dl"+  renderIO program datalogFile dsl+  I.runSouffleWith config program f++-- | Embeds a Datalog program from a DSL fragment directly in a Haskell file.+--+--   Note that due to TemplateHaskell staging restrictions, this function must+--   be used in a different module than the module where 'Program' and 'Fact'+--   instances are defined.+embedProgram :: Program prog => prog -> DSL prog 'Definition () -> Q [Dec]+embedProgram program dsl = do+  cppFile <- qRunIO $ do+    tmpDir <- getCanonicalTemporaryDirectory+    souffleHsDir <- createTempDirectory tmpDir "souffle-haskell"+    let progName = programName program+        datalogFile = souffleHsDir </> progName <.> "dl"+        cppFile = souffleHsDir </> progName <.> "cpp"+    renderIO program datalogFile dsl+    callCommand $ printf "souffle -g %s %s" cppFile datalogFile+    pure cppFile+  qAddForeignFilePath LangCxx cppFile+  pure []++runDSL :: Program prog => prog -> DSL prog 'Definition a -> DL+runDSL _ (DSL a) = Statements $ mapMaybe simplify $ execWriter (evalStateT a mempty) where+  simplify = \case+    Declare' name dir fields opts -> pure $ Declare name dir fields opts+    Rule' name terms body -> Rule name terms <$> simplify body+    Atom' name terms -> pure $ Atom name terms+    And' exprs -> case mapMaybe simplify exprs of+      [] -> Nothing+      exprs' -> pure $ foldl1 And exprs'+    Or' exprs -> case mapMaybe simplify exprs of+      [] -> Nothing+      exprs' -> pure $ foldl1 Or exprs'+    Not' expr -> Not <$> simplify expr+    Constrain' e -> pure $ Constrain e++-- | Generates a unique variable, using the name argument as a hint.+--+--   The type of the variable is determined the first predicate it is used in.+--   The 'NoVarsInAtom' constraint generates a user-friendly type error if the+--   generated variable is used inside a relation (which is not valid in+--   Datalog).+--+--   Note: If a variable is created but not used using this function, you will+--   get a compile-time error because it can't deduce the constraint.+var :: NoVarsInAtom ctx => VarName -> DSL prog ctx' (Term ctx ty)+var name = do+  count <- fromMaybe 0 <$> gets (Map.lookup name)+  modify $ Map.insert name (count + 1)+  let varName = if count == 0 then name else name <> "_" <> T.pack (show count)+  pure $ VarTerm varName++-- | Data type representing the head of a relation+--   (the part before ":-" in a Datalog relation).+--+--   - The "ctx" type variable is the context in which this type is used.+--     For this type, this will always be 'Relation'. The variable is there to+--     perform some compile-time checks.+--   - The "unused" type variable is unused and only there so the type has the+--     same kind as 'Body' and 'DSL'.+--+--   See also '|-'.+data Head ctx unused+  = Head Name (NonEmpty SimpleTerm)++-- | Data type representing the body of a relation+--   (what follows after ":-" in a Datalog relation).+--+--   By being a monad, it supports do-notation which allows for a syntax+--   that is quite close to Datalog.+--+--   - The "ctx" type variable is the context in which this type is used.+--     For this type, this will always be 'Relation'. The variable is there to+--     perform some compile-time checks.+--   - The "a" type variable is the value contained inside+--     (just like other monads).+--+--   See also '|-'.+newtype Body ctx a = Body (Writer [AST] a)+  deriving (Functor, Applicative, Monad, MonadWriter [AST])+  via (Writer [AST])++-- | Creates a fragment that is the logical disjunction (OR) of 2 sub-fragments.+--   This corresponds with ";" in Datalog.+(\/) :: Body ctx () -> Body ctx () -> Body ctx ()+body1 \/ body2 = do+  let rules1 = And' $ runBody body1+      rules2 = And' $ runBody body2+  tell [Or' [rules1, rules2]]++-- | Creates a fragment that is the logical negation of a sub-fragment.+--   This is equivalent to "!" in Datalog. (But this operator can't be used+--   in Haskell since it only allows unary negation as a prefix operator.)+not' :: Body ctx a -> Body ctx ()+not' body = do+  let rules = And' $ runBody body+  tell [Not' rules]++runBody :: Body ctx a -> [AST]+runBody (Body m) = execWriter m++data TypeInfo (a :: k) (ts :: [Type]) = TypeInfo++-- | Constraint that makes sure a type can be converted to a predicate function.+--   It gives a user-friendly error in case any of the sub-constraints+--   are not met.+type ToPredicate prog a =+  ( Fact a+  , FactMetadata a+  , ContainsFact prog a+  , SimpleProduct a+  , Assert (Length (Structure a) <=? 10) BigTupleError+  , KnownDLTypes (Structure a)+  , KnownDirection (FactDirection a)+  , KnownSymbols (AccessorNames a)+  , ToTerms (Structure a)+  )++-- | A typeclass for optionally configuring extra settings+--   (for performance reasons).++--   Since it contains no required functions, it is possible to derive this+--   typeclass automatically (this gives you the default behavior):+--+--   @+--   data Edge = Edge String String+--     deriving (Generic, Marshal, FactMetadata)+--   @+class (Fact a, SimpleProduct a) => FactMetadata a where+  -- | An optional function for configuring fact metadata.+  --+  --   By default no extra options are configured.+  --   For more information, see the 'Metadata' type.+  factOpts :: Proxy a -> Metadata a+  factOpts = const $ Metadata Automatic NoInline++-- | A data type that allows for finetuning of fact settings+--   (for performance reasons).+data Metadata a+  = Metadata (StructureOpt a) (InlineOpt (FactDirection a))++-- | Datatype describing the way a fact is stored inside Datalog.+--   A different choice of storage type can lead to an improvement in+--   performance (potentially).+--+--   For more information, see this+--   <https://souffle-lang.github.io/tuning#datastructure link> and this+--   <https://souffle-lang.github.io/relations link>.+data StructureOpt (a :: Type) where+  -- | Automatically choose the underlying storage for a relation.+  --   This is the storage type that is used by default.+  --+  --   For Souffle, it will choose a direct btree for facts with arity <= 6.+  --   For larger facts, it will use an indirect btree.+  Automatic :: StructureOpt a+  -- | Uses a direct btree structure.+  BTree :: StructureOpt a+  -- | Uses a brie structure. This can improve performance in some cases and is+  --   more memory efficient for particularly large relations.+  Brie :: StructureOpt a+  -- | A high performance datastructure optimised specifically for equivalence+  --   relations. This is only valid for binary facts with 2 fields of the+  --   same type.+  EqRel :: (IsBinaryRelation a, Structure a ~ '[t, t]) => StructureOpt a++type IsBinaryRelation a =+  Assert (Length (Structure a) == 2)+         ("Equivalence relations are only allowed with binary relations" <> ".")++-- | Datatype indicating if we should inline a fact or not.+data InlineOpt (d :: Direction) where+  -- | Inlines the fact, only possible for internal facts.+  Inline :: InlineOpt 'Internal+  -- | Does not inline the fact.+  NoInline :: InlineOpt d++-- | Generates a function for a type that implements 'Fact' and is a+--   'SimpleProduct'. The predicate function takes the same amount of arguments+--   as the original fact type. Calling the function with a tuple of arguments,+--   creates fragments of datalog code that can be glued together using other+--   functions in this module.+--+--   Note: You need to specify for which fact you want to return a predicate+--   for using TypeApplications.+predicateFor :: forall a prog. ToPredicate prog a => DSL prog 'Definition (Predicate a)+predicateFor = do+  let typeInfo = TypeInfo :: TypeInfo a (Structure a)+      p = Proxy :: Proxy a+      name = T.pack $ factName p+      accNames = fromMaybe genericNames $ accessorNames p+      opts = toSimpleMetadata $ factOpts p+      genericNames = map (("t" <>) . T.pack . show) [1..]+      tys = getTypes (Proxy :: Proxy (Structure a))+      direction = getDirection (Proxy :: Proxy (FactDirection a))+      fields = zipWith FieldData tys accNames+      definition = Declare' name direction fields opts+  addDefinition definition+  pure $ Predicate $ toFragment typeInfo name++toSimpleMetadata :: Metadata a -> SimpleMetadata+toSimpleMetadata (Metadata struct inline) =+  let structOpt = case struct of+        Automatic -> AutomaticLayout+        BTree -> BTreeLayout+        Brie -> BrieLayout+        EqRel -> EqRelLayout+      inlineOpt = case inline of+        Inline -> DoInline+        NoInline -> DoNotInline+  in SimpleMetadata structOpt inlineOpt++class KnownDirection a where+  getDirection :: Proxy a -> Direction+instance KnownDirection 'Input where getDirection = const Input+instance KnownDirection 'Output where getDirection = const Output+instance KnownDirection 'InputOutput where getDirection = const InputOutput+instance KnownDirection 'Internal where getDirection = const Internal++-- | Turnstile operator from Datalog, used in relations.+--+--   This is used for creating a DSL fragment that contains a relation.+--   NOTE: |- is used instead of :- due to limitations of the Haskell syntax.+(|-) :: Head 'Relation a -> Body 'Relation () -> DSL prog 'Definition ()+Head name terms |- body =+  let rules = runBody body+      relation = Rule' name terms (And' rules)+  in addDefinition relation++infixl 0 |-++-- | A typeclass used for generating AST fragments of Datalog code.+--   The generated fragments can be further glued together using the+--   various functions in this module.+class Fragment f ctx where+  toFragment :: ToTerms ts => TypeInfo a ts -> Name -> Tuple ctx ts -> f ctx ()++instance Fragment Head 'Relation where+  toFragment typeInfo name terms =+    let terms' = toTerms (Proxy :: Proxy 'Relation) typeInfo terms+     in Head name terms'++instance Fragment Body 'Relation where+  toFragment typeInfo name terms =+    let terms' = toTerms (Proxy :: Proxy 'Relation) typeInfo terms+    in tell [Atom' name terms']++instance Fragment (DSL prog) 'Definition where+  toFragment typeInfo name terms =+    let terms' = toTerms (Proxy :: Proxy 'Definition) typeInfo terms+     in addDefinition $ Atom' name terms'+++data RenderMode = Nested | TopLevel++-- | Renders a DSL fragment to the corresponding Datalog code and writes it to+--   a file.+renderIO :: Program prog => prog -> FilePath -> DSL prog 'Definition () -> IO ()+renderIO prog path = TIO.writeFile path . render prog++-- | Renders a DSL fragment to the corresponding Datalog code.+render :: Program prog => prog -> DSL prog 'Definition () -> T.Text+render prog = flip runReader TopLevel . f . runDSL prog where+  f = \case+    Statements stmts ->+      T.unlines <$> traverse f stmts+    Declare name dir fields metadata ->+      let fieldPairs = map renderField fields+          renderedFactOpts = renderMetadata metadata+          renderedOpts = if T.null renderedFactOpts then "" else " " <> renderedFactOpts+       in pure $ T.intercalate "\n" $ catMaybes+        [ Just $ ".decl " <> name <> "(" <> T.intercalate ", " fieldPairs <> ")" <> renderedOpts+        , renderDir name dir+        ]+    Atom name terms -> do+      let rendered = name <> "(" <> renderTerms (toList terms) <> ")"+      end <- maybeDot+      pure $ rendered <> end+    Rule name terms body -> do+      body' <- f body+      let rendered =+            name <> "(" <> renderTerms (toList terms) <> ") :-\n" <>+            T.intercalate "\n" (map indent $ T.lines body')+      pure rendered+    And e1 e2 -> do+      txt <- nested $ do+        txt1 <- f e1+        txt2 <- f e2+        pure $ txt1 <> ",\n" <> txt2+      end <- maybeDot+      pure $ txt <> end+    Or e1 e2 -> do+      txt <- nested $ do+        txt1 <- f e1+        txt2 <- f e2+        pure $ txt1 <> ";\n" <> txt2+      end <- maybeDot+      case end of+        "." -> pure $ txt <> end+        _ -> pure $ "(" <> txt <> ")"+    Not e -> do+      let maybeAddParens txt = case e of+            And _ _ -> "(" <> txt <> ")"+            _ -> txt+      txt <- maybeAddParens <$> nested (f e)+      end <- maybeDot+      case end of+        "." -> pure $ "!" <> txt <> end+        _ -> pure $ "!" <> txt+    Constrain t -> do+      let t' = renderTerm t+      end <- maybeDot+      case end of+        "." -> pure $ t' <> "."+        _ -> pure t'+  indent = ("  " <>)+  nested = local (const Nested)+  maybeDot = ask >>= \case+    TopLevel -> pure "."+    Nested -> pure mempty++renderDir :: VarName -> Direction -> Maybe T.Text+renderDir name = \case+  Input -> Just $ ".input " <> name+  Output -> Just $ ".output " <> name+  InputOutput -> Just $ T.intercalate "\n"+                      $ catMaybes [renderDir name Input, renderDir name Output]+  Internal -> Nothing++renderField :: FieldData -> T.Text+renderField (FieldData ty accName) =+  let txt = case ty of+        DLNumber -> ": number"+        DLUnsigned -> ": unsigned"+        DLFloat -> ": float"+        DLString -> ": symbol"+   in accName <> txt++renderMetadata :: SimpleMetadata -> T.Text+renderMetadata (SimpleMetadata struct inline) =+  let structTxt = case struct of+        AutomaticLayout -> Nothing+        BTreeLayout -> Just "btree"+        BrieLayout -> Just "brie"+        EqRelLayout -> Just "eqrel"+      inlineTxt = case inline of+        DoInline -> Just "inline"+        DoNotInline -> Nothing+  in T.intercalate " " $ catMaybes [structTxt, inlineTxt]++renderTerms :: [SimpleTerm] -> T.Text+renderTerms = T.intercalate ", " . map renderTerm++renderTerm :: SimpleTerm -> T.Text+renderTerm = \case+  I x -> T.pack $ show x+  U x -> T.pack $ show x+  F x -> T.pack $ printf "%f" x+  S s -> "\"" <> T.pack s <> "\""+  V v -> v+  Underscore -> "_"++  BinOp' op t1 t2 -> renderTerm t1 <> " " <> renderBinOp op <> " " <> renderTerm t2+  UnaryOp' op t1 -> renderUnaryOp op <> renderTerm t1+  Func' name ts -> renderFunc name <> "(" <> renderTerms (toList ts) <> ")"+  where+    renderFunc = \case+      Max -> "max"+      Min -> "min"+      Cat -> "cat"+      Contains -> "contains"+      Match -> "match"+      Ord -> "ord"+      StrLen -> "strlen"+      Substr -> "substr"+      ToNumber -> "to_number"+      ToString -> "to_string"+    renderBinOp = \case+      Plus -> "+"+      Mul -> "*"+      Subtract -> "-"+      Div -> "/"+      Pow -> "^"+      Rem -> "%"+      BinaryAnd -> "band"+      BinaryOr -> "bor"+      BinaryXor -> "bxor"+      LogicalAnd -> "land"+      LogicalOr -> "lor"+      LessThan -> "<"+      LessThanOrEqual -> "<="+      GreaterThan -> ">"+      GreaterThanOrEqual -> ">="+      IsEqual -> "="+      IsNotEqual -> "!="+    renderUnaryOp Negate = "-"+++type Name = T.Text++-- | Type representing a variable name in Datalog.+type VarName = T.Text++type AccessorName = T.Text++data DLType+  = DLNumber+  | DLUnsigned+  | DLFloat+  | DLString++data FieldData = FieldData DLType AccessorName++-- | A type level tag describing in which context a DSL fragment is used.+--   This is only used on the type level and helps catch some semantic errors+--   at compile time.+data UsageContext+  = Definition+  -- ^ A DSL fragment is used in a top level definition.+  | Relation+  -- ^ A DSL fragment is used inside a relation (either head or body of a relation).++-- | A type family used for generating a user-friendly type error in case+--   you use a variable in a DSL fragment where it is not allowed+--   (outside of relations).+type family NoVarsInAtom (ctx :: UsageContext) :: Constraint where+  NoVarsInAtom ctx = Assert (ctx == 'Relation) NoVarsInAtomError++type NoVarsInAtomError =+  ( "You tried to use a variable in a top level fact, which is not supported in Souffle."+  % "Possible solutions:"+  % "  - Move the fact inside a rule body."+  % "  - Replace the variable in the fact with a string, number, unsigned or float constant."+  )++-- | Data type for representing Datalog terms.+--+--   All constructors are hidden, but with the `Num`, 'Fractional' and+--   `IsString` instances it is possible to create terms using Haskell syntax+--   for literals. For non-literal values, smart constructors are provided.+--   (See for example 'underscore' / '__'.)+data Term ctx ty where+  -- NOTE: type family is used here instead of "Term 'Relation ty";+  -- this allows giving a better type error in some situations.+  VarTerm :: NoVarsInAtom ctx => VarName -> Term ctx ty+  UnderscoreTerm :: Term ctx ty+  NumberTerm :: Int32 -> Term ctx Int32+  UnsignedTerm :: Word32 -> Term ctx Word32+  FloatTerm :: Float -> Term ctx Float+  StringTerm :: ToString ty => ty -> Term ctx ty++  UnaryOp :: Num ty => Op1 -> Term ctx ty -> Term ctx ty+  BinOp :: Num ty => Op2 -> Term ctx ty -> Term ctx ty -> Term ctx ty+  Func :: FuncName -> NonEmpty SimpleTerm -> Term ctx ty2++data Op2+  = Plus+  | Mul+  | Subtract+  | Div+  | Pow+  | Rem+  | BinaryAnd+  | BinaryOr+  | BinaryXor+  | LogicalAnd+  | LogicalOr+  | LessThan+  | LessThanOrEqual+  | GreaterThan+  | GreaterThanOrEqual+  | IsEqual+  | IsNotEqual++data Op1 = Negate++data FuncName+  = Max+  | Min+  | Cat+  | Contains+  | Match+  | Ord+  | StrLen+  | Substr+  | ToNumber+  | ToString+++-- | Term representing a wildcard ("_") in Datalog.+underscore :: Term ctx ty+underscore = UnderscoreTerm++-- | Term representing a wildcard ("_") in Datalog. Note that in the DSL this+--   is with 2 underscores. (Single underscore is reserved for typed holes!)+__ :: Term ctx ty+__ = underscore++class ToString a where+  toString :: a -> String++instance ToString String where toString = id+instance ToString T.Text where toString = T.unpack+instance ToString TL.Text where toString = TL.unpack++instance IsString (Term ctx String) where fromString = StringTerm+instance IsString (Term ctx T.Text) where fromString = StringTerm . T.pack+instance IsString (Term ctx TL.Text) where fromString = StringTerm . TL.pack++-- | A helper typeclass, mainly used for avoiding a lot of boilerplate+--   in the 'Num' instance for 'Term'.+class Num ty => SupportsArithmetic ty where+  fromInteger' :: Integer -> Term ctx ty++instance SupportsArithmetic Int32 where+  fromInteger' = NumberTerm . fromInteger+instance SupportsArithmetic Word32 where+  fromInteger' = UnsignedTerm . fromInteger+instance SupportsArithmetic Float where+  fromInteger' = FloatTerm . fromInteger++instance (SupportsArithmetic ty, Num ty) => Num (Term ctx ty) where+  fromInteger = fromInteger'+  (+) = BinOp Plus+  (*) = BinOp Mul+  (-) = BinOp Subtract+  negate = UnaryOp Negate+  abs = error "'abs' is not supported for Souffle terms"+  signum = error "'signum' is not supported for Souffle terms"++instance Fractional (Term ctx Float) where+  fromRational = FloatTerm . fromRational+  (/) = BinOp Div++-- | Exponentiation operator ("^" in Datalog).+(.^) :: Num ty => Term ctx ty -> Term ctx ty -> Term ctx ty+(.^) = BinOp Pow++-- | Remainder operator ("%" in Datalog).+(.%) :: (Num ty, Integral ty) => Term ctx ty -> Term ctx ty -> Term ctx ty+(.%) = BinOp Rem++-- | Creates a less than constraint (a < b), for use in the body of a relation.+(.<) :: Num ty => Term ctx ty -> Term ctx ty -> Body ctx ()+(.<) = addConstraint LessThan+infix 1 .<++-- | Creates a less than or equal constraint (a <= b), for use in the body of+--   a relation.+(.<=) :: Num ty => Term ctx ty -> Term ctx ty -> Body ctx ()+(.<=) = addConstraint LessThanOrEqual+infix 1 .<=++-- | Creates a greater than constraint (a > b), for use in the body of a relation.+(.>) :: Num ty => Term ctx ty -> Term ctx ty -> Body ctx ()+(.>) = addConstraint GreaterThan+infix 1 .>++-- | Creates a greater than or equal constraint (a >= b), for use in the body of+--   a relation.+(.>=) :: Num ty => Term ctx ty -> Term ctx ty -> Body ctx ()+(.>=) = addConstraint GreaterThanOrEqual+infix 1 .>=++-- | Creates a constraint that 2 terms should be equal to each other (a = b),+--   for use in the body of a relation.+(.=) :: Term ctx ty -> Term ctx ty -> Body ctx ()+(.=) = addConstraint IsEqual+infix 1 .=++-- | Creates a constraint that 2 terms should not be equal to each other+--   (a != b), for use in the body of a relation.+(.!=) :: Term ctx ty -> Term ctx ty -> Body ctx ()+(.!=) = addConstraint IsNotEqual+infix 1 .!=++addConstraint :: Op2 -> Term ctx ty -> Term ctx ty -> Body ctx ()+addConstraint op e1 e2 =+  let expr = BinOp' op (toTerm e1) (toTerm e2)+   in tell [Constrain' expr]++-- | Binary AND operator.+band :: (Num ty, Integral ty) => Term ctx ty -> Term ctx ty -> Term ctx ty+band = BinOp BinaryAnd++-- | Binary OR operator.+bor :: (Num ty, Integral ty) => Term ctx ty -> Term ctx ty -> Term ctx ty+bor = BinOp BinaryOr++-- | Binary XOR operator.+bxor :: (Num ty, Integral ty) => Term ctx ty -> Term ctx ty -> Term ctx ty+bxor = BinOp BinaryXor++-- | Logical AND operator.+land :: (Num ty, Integral ty) => Term ctx ty -> Term ctx ty -> Term ctx ty+land = BinOp LogicalAnd++-- | Logical OR operator.+lor :: (Num ty, Integral ty) => Term ctx ty -> Term ctx ty -> Term ctx ty+lor = BinOp LogicalOr++-- | "max" function.+max' :: Num ty => Term ctx ty -> Term ctx ty -> Term ctx ty+max' = func2 Max++-- | "min" function.+min' :: Num ty => Term ctx ty -> Term ctx ty -> Term ctx ty+min' = func2 Min++-- | "cat" function (string concatenation).+cat :: ToString ty => Term ctx ty -> Term ctx ty -> Term ctx ty+cat = func2 Cat++-- | "contains" predicate, checks if 2nd string contains the first.+contains :: ToString ty => Term ctx ty -> Term ctx ty -> Body ctx ()+contains a b =+  let expr = toTerm $ func2 Contains a b+   in tell [Constrain' expr]++-- | "match" predicate, checks if a wildcard string matches a given string.+match :: ToString ty => Term ctx ty -> Term ctx ty -> Body ctx ()+match p s =+  let expr = toTerm $ func2 Match p s+  in tell [Constrain' expr]++-- | "ord" function.+ord :: ToString ty => Term ctx ty -> Term ctx Int32+ord = func1 Ord++-- | "strlen" function.+strlen :: ToString ty => Term ctx ty -> Term ctx Int32+strlen = func1 StrLen++-- | "substr" function.+substr :: ToString ty => Term ctx ty -> Term ctx Int32 -> Term ctx Int32 -> Term ctx ty+substr a b c = Func Substr $ toTerm a :| [toTerm b, toTerm c]++-- | "to_number" function.+to_number :: ToString ty => Term ctx ty -> Term ctx Int32+to_number = func1 ToNumber++-- | "to_string" function.+to_string :: ToString ty => Term ctx Int32 -> Term ctx ty+to_string = func1 ToString++func1 :: FuncName -> Term ctx ty -> Term ctx ty2+func1 name a = Func name $ toTerm a :| []++func2 :: FuncName -> Term ctx ty -> Term ctx ty -> Term ctx ty2+func2 name a b = Func name $ toTerm a :| [toTerm b]++data SimpleTerm+  = V VarName+  | I Int32+  | U Word32+  | F Float+  | S String+  | Underscore++  | BinOp' Op2 SimpleTerm SimpleTerm+  | UnaryOp' Op1 SimpleTerm+  | Func' FuncName (NonEmpty SimpleTerm)++data SimpleMetadata = SimpleMetadata StructureOption InlineOption++data StructureOption+  = AutomaticLayout+  | BTreeLayout+  | BrieLayout+  | EqRelLayout++data InlineOption+  = DoInline+  | DoNotInline++data AST+  = Declare' VarName Direction [FieldData] SimpleMetadata+  | Rule' Name (NonEmpty SimpleTerm) AST+  | Atom' Name (NonEmpty SimpleTerm)+  | And' [AST]+  | Or' [AST]+  | Not' AST+  | Constrain' SimpleTerm++data DL+  = Statements [DL]+  | Declare VarName Direction [FieldData] SimpleMetadata+  | Rule Name (NonEmpty SimpleTerm) DL+  | Atom Name (NonEmpty SimpleTerm)+  | And DL DL+  | Or DL DL+  | Not DL+  | Constrain SimpleTerm+++class KnownDLTypes (ts :: [Type]) where+  getTypes :: Proxy ts -> [DLType]++instance KnownDLTypes '[] where+  getTypes _ = []++instance (KnownDLType t, KnownDLTypes ts) => KnownDLTypes (t ': ts) where+  getTypes _ = getType (Proxy :: Proxy t) : getTypes (Proxy :: Proxy ts)++class KnownDLType t where+  getType :: Proxy t -> DLType++instance KnownDLType Int32 where getType = const DLNumber+instance KnownDLType Word32 where getType = const DLUnsigned+instance KnownDLType Float where getType = const DLFloat+instance KnownDLType String where getType = const DLString+instance KnownDLType T.Text where getType = const DLString+instance KnownDLType TL.Text where getType = const DLString++type family AccessorNames a :: [Symbol] where+  AccessorNames a = GetAccessorNames (Rep a)++type family GetAccessorNames (f :: Type -> Type) :: [Symbol] where+  GetAccessorNames (a :*: b) = GetAccessorNames a ++ GetAccessorNames b+  GetAccessorNames (C1 ('MetaCons _ _ 'False) _) = '[]+  GetAccessorNames (S1 ('MetaSel ('Just name) _ _ _) a) = '[name] ++ GetAccessorNames a+  GetAccessorNames (M1 _ _ a) = GetAccessorNames a+  GetAccessorNames (K1 _ _) = '[]++class KnownSymbols (symbols :: [Symbol]) where+  toStrings :: Proxy symbols -> [String]++instance KnownSymbols '[] where+  toStrings = const []++instance (KnownSymbol s, KnownSymbols symbols) => KnownSymbols (s ': symbols) where+  toStrings _ =+    let sym = symbolVal (Proxy :: Proxy s)+        symbols =  toStrings (Proxy :: Proxy symbols)+     in sym : symbols++accessorNames :: forall a. KnownSymbols (AccessorNames a) => Proxy a -> Maybe [T.Text]+accessorNames _ = case toStrings (Proxy :: Proxy (AccessorNames a)) of+  [] -> Nothing+  names -> Just $ T.pack <$> names++-- | A type synonym for a tuple consisting of Datalog 'Term's.+--   Only tuples containing up to 10 elements are currently supported.+type Tuple ctx ts = TupleOf (MapType (Term ctx) ts)++class ToTerms (ts :: [Type]) where+  toTerms :: Proxy ctx -> TypeInfo a ts -> Tuple ctx ts -> NonEmpty SimpleTerm++instance ToTerms '[t] where+  toTerms _ _ a =+    toTerm a :| []++instance ToTerms '[t1, t2] where+  toTerms _ _ (a, b) =+    toTerm a :| [toTerm b]++instance ToTerms '[t1, t2, t3] where+  toTerms _ _ (a, b, c) =+    toTerm a :| [toTerm b, toTerm c]++instance ToTerms '[t1, t2, t3, t4] where+  toTerms _ _ (a, b, c, d) =+    toTerm a :| [toTerm b, toTerm c, toTerm d]++instance ToTerms '[t1, t2, t3, t4, t5] where+  toTerms _ _ (a, b, c, d, e) =+    toTerm a :| [toTerm b, toTerm c, toTerm d, toTerm e]++instance ToTerms '[t1, t2, t3, t4, t5, t6] where+  toTerms _ _ (a, b, c, d, e, f) =+    toTerm a :| [toTerm b, toTerm c, toTerm d, toTerm e, toTerm f]++instance ToTerms '[t1, t2, t3, t4, t5, t6, t7] where+  toTerms _ _ (a, b, c, d, e, f, g) =+    toTerm a :| [toTerm b, toTerm c, toTerm d, toTerm e, toTerm f, toTerm g]++instance ToTerms '[t1, t2, t3, t4, t5, t6, t7, t8] where+  toTerms _ _ (a, b, c, d, e, f, g, h) =+    toTerm a :| [toTerm b, toTerm c, toTerm d, toTerm e, toTerm f, toTerm g, toTerm h]++instance ToTerms '[t1, t2, t3, t4, t5, t6, t7, t8, t9] where+  toTerms _ _ (a, b, c, d, e, f, g, h, i) =+    toTerm a :| [toTerm b, toTerm c, toTerm d, toTerm e, toTerm f, toTerm g, toTerm h, toTerm i]++instance ToTerms '[t1, t2, t3, t4, t5, t6, t7, t8, t9, t10] where+  toTerms _ _ (a, b, c, d, e, f, g, h, i, j) =+    toTerm a :| [ toTerm b, toTerm c, toTerm d, toTerm e, toTerm f+                , toTerm g, toTerm h, toTerm i, toTerm j+                ]++toTerm :: Term ctx t -> SimpleTerm+toTerm = \case+  VarTerm v -> V v+  StringTerm s -> S $ toString s+  NumberTerm x -> I x+  UnsignedTerm x -> U x+  FloatTerm x -> F x+  UnderscoreTerm -> Underscore++  BinOp op t1 t2 -> BinOp' op (toTerm t1) (toTerm t2)+  UnaryOp op t1 -> UnaryOp' op (toTerm t1)+  Func name ts -> Func' name ts+++-- Helper functions / type families / ...++type family MapType (f :: Type -> Type) (ts :: [Type]) :: [Type] where+  MapType _ '[] = '[]+  MapType f (t ': ts) = f t ': MapType f ts++type family Assert (c :: Bool) (msg :: ErrorMessage) :: Constraint where+  Assert 'True _ = ()+  Assert 'False msg = TypeError msg++type family (a :: k) == (b :: k) :: Bool where+  a == a = 'True+  _ == _ = 'False++type family Length (xs :: [Type]) :: Nat where+  Length '[] = 0+  Length (_ ': xs) = 1 + Length xs++-- | A helper type family for computing the list of types used in a data type.+--   (The type family assumes a data type with a single data constructor.)+type family Structure a :: [Type] where+  Structure a = Collect (Rep a)++type family Collect (a :: Type -> Type) where+  Collect (a :*: b) = Collect a ++ Collect b+  Collect (M1 _ _ a) = Collect a+  Collect (K1 _ ty) = '[ty]++type family a ++ b = c where+  '[] ++ b = b+  a ++ '[] = a+  (a ': b) ++ c = a ': (b ++ c)++type family TupleOf (ts :: [Type]) = t where+  TupleOf '[t] = t+  TupleOf '[t1, t2] = (t1, t2)+  TupleOf '[t1, t2, t3] = (t1, t2, t3)+  TupleOf '[t1, t2, t3, t4] = (t1, t2, t3, t4)+  TupleOf '[t1, t2, t3, t4, t5] = (t1, t2, t3, t4, t5)+  TupleOf '[t1, t2, t3, t4, t5, t6] = (t1, t2, t3, t4, t5, t6)+  TupleOf '[t1, t2, t3, t4, t5, t6, t7] = (t1, t2, t3, t4, t5, t6, t7)+  TupleOf '[t1, t2, t3, t4, t5, t6, t7, t8] = (t1, t2, t3, t4, t5, t6, t7, t8)+  TupleOf '[t1, t2, t3, t4, t5, t6, t7, t8, t9] = (t1, t2, t3, t4, t5, t6, t7, t8, t9)+  TupleOf '[t1, t2, t3, t4, t5, t6, t7, t8, t9, t10] = (t1, t2, t3, t4, t5, t6, t7, t8, t9, t10)+  TupleOf _ = TypeError BigTupleError++type BigTupleError =+  ( "The DSL only supports facts/tuples consisting of up to 10 elements."+  % "If you need more arguments, please submit an issue on Github "+  <> "(https://github.com/luc-tielen/souffle-haskell/issues)"+  )+
lib/Language/Souffle/Internal/Constraints.hs view
@@ -21,13 +21,11 @@ --   the 'Language.Souffle.Marshal.Marshal' typeclass. -- --   The __a__ type parameter is the original type, used when displaying the type error.---   The __f__ type parameter should be equal to 'Rep a', used for analyzing the---   structure of the data type. -- --   A type error is returned if the passed in type is not a simple product type---   consisting of only simple types like Int32, String and Text.-type family SimpleProduct (a :: Type) (f :: Type -> Type) :: Constraint where-  SimpleProduct a f = (ProductLike a f, OnlySimpleFields a f)+--   consisting of only "simple" types like Int32, Word32, Float, String and Text.+type family SimpleProduct (a :: Type) :: Constraint where+  SimpleProduct a = (ProductLike a (Rep a), OnlySimpleFields a (Rep a))  type family ProductLike (t :: Type) (f :: Type -> Type) :: Constraint where   ProductLike t (_ :*: b) = ProductLike t b
lib/Language/Souffle/Interpreted.hs view
@@ -1,5 +1,5 @@ {-# OPTIONS_GHC -Wno-redundant-constraints #-}-{-# LANGUAGE FlexibleInstances, TypeFamilies, DerivingVia, InstanceSigs #-}+{-# LANGUAGE FlexibleInstances, TypeFamilies, DerivingVia, InstanceSigs, UndecidableInstances #-}  -- | This module provides an implementation for the `MonadSouffle` typeclass --   defined in "Language.Souffle.Class".@@ -13,6 +13,9 @@   ( Program(..)   , Fact(..)   , Marshal(..)+  , Direction(..)+  , ContainsInputFact+  , ContainsOutputFact   , Config(..)   , Handle   , SouffleM@@ -20,7 +23,6 @@   , runSouffle   , runSouffleWith   , defaultConfig-  , cleanup   , souffleStdOut   , souffleStdErr   ) where@@ -69,7 +71,7 @@ --   - __cfgFactDir__: The directory where the initial input fact file(s) can be found --   if present. If Nothing, then a temporary directory will be used, during the --   souffle session.---   - __cfgOutputDir__: The directory where the output facts file(s) are created.+--   - __cfgOutputDir__: The directory where the output fact file(s) are created. --   If Nothing, it will be part of the temporary directory. data Config   = Config@@ -100,18 +102,65 @@   pure $ Config (fromMaybe "." dlDir) souffleBin Nothing Nothing {-# INLINABLE defaultConfig #-} --- | Returns an IO action that will run the Souffle interpreter with---   default settings (see `defaultConfig`).-runSouffle :: SouffleM a -> IO a-runSouffle m = do+{- | Initializes and runs a Souffle program with default settings.++     The 2nd argument is passed in a handle after initialization of the+     Souffle program. The handle will contain 'Nothing' if it failed to+     locate the souffle interpreter executable or if it failed to find the+     souffle program file. In the successful case it will contain a handle+     that can be used for performing Souffle related actions using the other+     functions in this module.+-}+runSouffle :: Program prog => prog -> (Maybe (Handle prog) -> SouffleM a) -> IO a+runSouffle program m = do   cfg <- defaultConfig-  runSouffleWith cfg m+  runSouffleWith cfg program m {-# INLINABLE runSouffle #-} --- | Returns an IO action that will run the Souffle interpreter with---   the given interpreter settings.-runSouffleWith :: Config -> SouffleM a -> IO a-runSouffleWith cfg (SouffleM m) = runReaderT m cfg+{- | Initializes and runs a Souffle program with the given interpreter settings.++     The 3rd argument is passed in a handle after initialization of the+     Souffle program. The handle will contain 'Nothing' if it failed to+     locate the souffle interpreter executable or if it failed to find the+     souffle program file. In the successful case it will contain a handle+     that can be used for performing Souffle related actions using the other+     functions in this module.++     If the config settings do not specify a fact or output dir,+     temporary directories will be created for storing files in. These+     directories will also be automatically cleaned up at the end of+     this function.+-}+runSouffleWith+  :: Program prog => Config -> prog -> (Maybe (Handle prog) -> SouffleM a) -> IO a+runSouffleWith cfg program f = bracket initialize maybeCleanup $ \handle -> do+  let (SouffleM action) = f handle+  runReaderT action cfg+  where+    initialize = datalogProgramFile program (cfgDatalogDir cfg) >>= \case+      Nothing -> pure Nothing+      Just datalogExecutable -> do+        tmpDir <- getCanonicalTemporaryDirectory+        souffleTempDir <- createTempDirectory tmpDir "souffle-haskell"+        let factDir = fromMaybe (souffleTempDir </> "fact") $ cfgFactDir cfg+            outDir = fromMaybe (souffleTempDir </> "out") $ cfgOutputDir cfg+        traverse_ (createDirectoryIfMissing True) [factDir, outDir]+        forM mSouffleBin $ \souffleBin ->+          Handle+            <$> (newIORef $ HandleData+                  { soufflePath = souffleBin+                  , tmpDirPath  = souffleTempDir+                  , factPath    = factDir+                  , outputPath  = outDir+                  , datalogExec = datalogExecutable+                  , noOfThreads = 1+                  })+            <*> newIORef Nothing+            <*> newIORef Nothing+    maybeCleanup = maybe mempty $ \h -> do+      handle <- readIORef $ handleData h+      removeDirectoryRecursive $ tmpDirPath handle+    mSouffleBin = cfgSouffleBin cfg {-# INLINABLE runSouffleWith #-}  -- | A datatype representing a handle to a datalog program.@@ -128,7 +177,7 @@ --   is stored. data HandleData = HandleData   { soufflePath :: FilePath-  , basePath    :: FilePath+  , tmpDirPath  :: FilePath   , factPath    :: FilePath   , outputPath  :: FilePath   , datalogExec :: FilePath@@ -206,34 +255,6 @@   type Handler SouffleM = Handle   type CollectFacts SouffleM c = Collect c -  init :: forall prog. Program prog => prog -> SouffleM (Maybe (Handle prog))-  init prg = SouffleM $ datalogProgramFile prg >>= \case-    Nothing -> pure Nothing-    Just datalogExecutable -> do-      souffleTempDir <- liftIO $ do-        tmpDir <- getCanonicalTemporaryDirectory-        createTempDirectory tmpDir "souffle-haskell"--      factDir <- fromMaybe (souffleTempDir </> "fact") <$> asks cfgFactDir-      outDir <- fromMaybe (souffleTempDir </> "out") <$> asks cfgOutputDir-      liftIO $ do-        createDirectoryIfMissing True factDir-        createDirectoryIfMissing True outDir-      mSouffleBin <- asks cfgSouffleBin-      liftIO $ forM mSouffleBin $ \souffleBin ->-        Handle-          <$> (newIORef $ HandleData-                { soufflePath = souffleBin-                , basePath    = souffleTempDir-                , factPath    = factDir-                , outputPath  = outDir-                , datalogExec = datalogExecutable-                , noOfThreads = 1-                })-          <*> newIORef Nothing-          <*> newIORef Nothing-  {-# INLINABLE init #-}-   run (Handle refHandleData refHandleStdOut refHandleStdErr) = liftIO $ do     handle <- readIORef refHandleData     -- Invoke the souffle binary using parameters, supposing that the facts@@ -277,7 +298,7 @@     noOfThreads <$> readIORef (handleData handle)   {-# INLINABLE getNumThreads #-} -  getFacts :: forall a c prog. (Marshal a, Fact a, ContainsFact prog a, Collect c)+  getFacts :: forall a c prog. (Marshal a, Fact a, ContainsOutputFact prog a, Collect c)            => Handle prog -> SouffleM (c a)   getFacts h = liftIO $ do     handle <- readIORef $ handleData h@@ -287,14 +308,14 @@     pure $! facts  -- force facts before running to avoid issues with lazy IO   {-# INLINABLE getFacts #-} -  findFact :: (Fact a, ContainsFact prog a, Eq a)+  findFact :: (Fact a, ContainsOutputFact prog a, Eq a)            => Handle prog -> a -> SouffleM (Maybe a)   findFact prog fact = do     facts :: [a] <- getFacts prog     pure $ find (== fact) facts   {-# INLINABLE findFact #-} -  addFact :: forall a prog. (Fact a, ContainsFact prog a, Marshal a)+  addFact :: forall a prog. (Fact a, ContainsInputFact prog a, Marshal a)           => Handle prog -> a -> SouffleM ()   addFact h fact = liftIO $ do     handle <- readIORef $ handleData h@@ -304,7 +325,7 @@     appendFile factFile $ intercalate "\t" line ++ "\n"   {-# INLINABLE addFact #-} -  addFacts :: forall a prog f. (Fact a, ContainsFact prog a, Marshal a, Foldable f)+  addFacts :: forall a prog f. (Fact a, ContainsInputFact prog a, Marshal a, Foldable f)            => Handle prog -> f a -> SouffleM ()   addFacts h facts = liftIO $ do     handle <- readIORef $ handleData h@@ -314,11 +335,10 @@     traverse_ (\line -> appendFile factFile (intercalate "\t" line ++ "\n")) factLines   {-# INLINABLE addFacts #-} -datalogProgramFile :: forall prog. Program prog => prog -> ReaderT Config IO (Maybe FilePath)-datalogProgramFile _ = do-  dir <- asks cfgDatalogDir-  let dlFile = dir </> programName (Proxy :: Proxy prog) <.> "dl"-  liftIO $ doesFileExist dlFile >>= \case+datalogProgramFile :: forall prog. Program prog => prog -> FilePath -> IO (Maybe FilePath)+datalogProgramFile prog datalogDir = do+  let dlFile = datalogDir </> programName prog <.> "dl"+  doesFileExist dlFile >>= \case     False -> pure Nothing     True -> pure $ Just dlFile {-# INLINABLE datalogProgramFile #-}@@ -343,15 +363,6 @@     -- deepseq needed to avoid issues with lazy IO     pure $ contents `deepseq` (map (splitOn '\t') . lines) contents {-# INLINABLE readCSVFile #-}---- | Cleans up the temporary directory that this library has written files to.---   This functionality is only provided for the interpreted version since the---   compiled version directly (de-)serializes data via the C++ API.-cleanup :: forall prog. Program prog => Handle prog -> SouffleM ()-cleanup h  = liftIO $ do-  handle <- readIORef $ handleData h-  traverse_ removeDirectoryRecursive [factPath handle, outputPath handle, basePath handle]-{-# INLINABLE cleanup #-}  -- | Returns the handle of stdout from the souffle interpreter. souffleStdOut :: forall prog. Program prog => Handle prog -> SouffleM (Maybe T.Text)
lib/Language/Souffle/Marshal.hs view
@@ -77,10 +77,10 @@   pop :: MonadPop m => m a    default push-    :: (Generic a, C.SimpleProduct a (Rep a), GMarshal (Rep a), MonadPush m)+    :: (Generic a, C.SimpleProduct a, GMarshal (Rep a), MonadPush m)     => a -> m ()   default pop-    :: (Generic a, C.SimpleProduct a (Rep a), GMarshal (Rep a), MonadPop m)+    :: (Generic a, C.SimpleProduct a, GMarshal (Rep a), MonadPop m)     => m a   push a = gpush (from a)   {-# INLINABLE push #-}
scripts/import_souffle_headers.hs view
@@ -1,4 +1,4 @@-{-# LANGUAGE DataKinds, TypeFamilies, DeriveGeneric, DeriveAnyClass #-}+{-# LANGUAGE DataKinds, TypeFamilies, DeriveGeneric, DeriveAnyClass, TypeApplications #-}  module Main ( main ) where @@ -20,33 +20,72 @@ import qualified Text.Megaparsec as P import qualified Text.Megaparsec.Char as P import qualified Language.Souffle.Interpreted as Souffle+import Language.Souffle.Experimental   data Includes = Includes FilePath FilePath-  deriving (Eq, Show, Generic, Souffle.Marshal)+  deriving (Eq, Show, Generic, Souffle.Marshal, FactMetadata) +data TransitivelyIncludes = TransitivelyIncludes FilePath FilePath+  deriving (Eq, Show, Generic, Souffle.Marshal, FactMetadata)+ newtype TopLevelInclude = TopLevelInclude FilePath-  deriving (Eq, Show, Generic, Souffle.Marshal)+  deriving (Eq, Show, Generic, Souffle.Marshal, FactMetadata)  newtype RequiredInclude = RequiredInclude FilePath-  deriving (Eq, Show, Generic, Souffle.Marshal)+  deriving (Eq, Show, Generic, Souffle.Marshal, FactMetadata)  data Handle = Handle  instance Souffle.Program Handle where-  type ProgramFacts Handle = [TopLevelInclude, Includes, RequiredInclude]+  type ProgramFacts Handle =+    [ TopLevelInclude+    , Includes+    , TransitivelyIncludes+    , RequiredInclude+    ]   programName = const "required_include"  instance Souffle.Fact Includes where+  type FactDirection Includes = 'Souffle.Input   factName = const "includes" +instance Souffle.Fact TransitivelyIncludes where+  type FactDirection TransitivelyIncludes = 'Souffle.Internal+  factName = const "transitively_includes"+ instance Souffle.Fact TopLevelInclude where+  type FactDirection TopLevelInclude = 'Souffle.Input   factName = const "top_level_include"  instance Souffle.Fact RequiredInclude where+  type FactDirection RequiredInclude = 'Souffle.Output   factName = const "required_include" +dlProgram :: DSL Handle 'Definition ()+dlProgram = do+  Predicate includes <- predicateFor @Includes+  Predicate transitivelyIncludes <- predicateFor @TransitivelyIncludes+  Predicate topLevelInclude <- predicateFor @TopLevelInclude+  Predicate requiredInclude <- predicateFor @RequiredInclude +  file1 <- var "file1"+  file2 <- var "file2"+  file3 <- var "file3"++  requiredInclude(file1) |-+    topLevelInclude(file1)+  requiredInclude(file1) |- do+    topLevelInclude(file2)+    transitivelyIncludes(file2, file1)++  transitivelyIncludes(file1, file2) |-+    includes(file1, file2)+  transitivelyIncludes(file1, file2) |- do+    includes(file1, file3)+    transitivelyIncludes(file3, file2)++ run :: String -> IO () run = callCommand @@ -114,18 +153,15 @@  computeRequiredIncludes :: [Includes] -> IO [FilePath] computeRequiredIncludes includes = do-  cfg <- Souffle.defaultConfig-  let config = cfg { Souffle.cfgDatalogDir = "./scripts" }-  requiredIncludes <- Souffle.runSouffleWith config $-    Souffle.init Handle >>= \case-      Nothing -> error "Failed to load Souffle program. Aborting."-      Just prog -> do-        Souffle.addFacts prog [ TopLevelInclude "souffle/src/SouffleInterface.h"-                              , TopLevelInclude "souffle/src/CompiledSouffle.h"-                              ]-        Souffle.addFacts prog includes-        Souffle.run prog-        Souffle.getFacts prog+  requiredIncludes <- runSouffleInterpreted Handle dlProgram $ \case+    Nothing -> error "Failed to load Souffle program. Aborting."+    Just prog -> do+      Souffle.addFacts prog [ TopLevelInclude "souffle/src/SouffleInterface.h"+                            , TopLevelInclude "souffle/src/CompiledSouffle.h"+                            ]+      Souffle.addFacts prog includes+      Souffle.run prog+      Souffle.getFacts prog   pure $ map (\(RequiredInclude include) -> include) requiredIncludes  copyHeader :: FilePath -> IO FilePath
souffle-haskell.cabal view
@@ -4,10 +4,10 @@ -- -- see: https://github.com/sol/hpack ----- hash: 0e669f6fe266f6ab6d74d38303f2f74ccf8b6264c4aec9aeb972e5e76b38d236+-- hash: d1e5d51d922f3a246fbf5e312f90e05c4ad621f11985cb4fbe1de6ac504bd730  name:           souffle-haskell-version:        1.1.0+version:        2.0.0 synopsis:       Souffle Datalog bindings for Haskell description:    Souffle Datalog bindings for Haskell. category:       Logic Programming, Foreign Binding, Bindings@@ -79,6 +79,7 @@   exposed-modules:       Language.Souffle.Class       Language.Souffle.Compiled+      Language.Souffle.Experimental       Language.Souffle.Internal       Language.Souffle.Internal.Bindings       Language.Souffle.Internal.Constraints@@ -92,6 +93,7 @@       lib   default-extensions: OverloadedStrings LambdaCase ScopedTypeVariables   ghc-options: -Wall -Weverything -Wno-safe -Wno-unsafe -Wno-implicit-prelude -Wno-missed-specializations -Wno-all-missed-specializations -Wno-missing-import-lists -Wno-type-defaults -Wno-missing-local-signatures -Wno-monomorphism-restriction -Wno-missing-deriving-strategies -optP-Wno-nonportable-include-path -fhide-source-paths -fno-show-valid-hole-fits -fno-sort-valid-hole-fits+  cpp-options: -std=c++17   cxx-options: -std=c++17 -Wall   include-dirs:       cbits@@ -143,6 +145,7 @@   build-depends:       array <=1.0     , base >=4.12 && <5+    , containers >=0.6.2.1 && <1     , deepseq >=1.4.4 && <2     , directory >=1.3.3 && <2     , filepath >=1.4.2 && <2@@ -166,6 +169,7 @@       scripts   default-extensions: OverloadedStrings LambdaCase ScopedTypeVariables   ghc-options: -Wall -Weverything -Wno-safe -Wno-unsafe -Wno-implicit-prelude -Wno-missed-specializations -Wno-all-missed-specializations -Wno-missing-import-lists -Wno-type-defaults -Wno-missing-local-signatures -Wno-monomorphism-restriction -Wno-missing-deriving-strategies -optP-Wno-nonportable-include-path -fhide-source-paths -fno-show-valid-hole-fits -fno-sort-valid-hole-fits+  cpp-options: -std=c++17   cxx-options: -std=c++17   include-dirs:       cbits@@ -236,6 +240,9 @@   main-is: test.hs   other-modules:       Test.Language.Souffle.CompiledSpec+      Test.Language.Souffle.Experimental.Fixtures+      Test.Language.Souffle.Experimental.FixturesCompiled+      Test.Language.Souffle.ExperimentalSpec       Test.Language.Souffle.InterpretedSpec       Test.Language.Souffle.MarshalSpec       Paths_souffle_haskell@@ -243,6 +250,7 @@       tests   default-extensions: OverloadedStrings LambdaCase ScopedTypeVariables   ghc-options: -Wall -Weverything -Wno-safe -Wno-unsafe -Wno-implicit-prelude -Wno-missed-specializations -Wno-all-missed-specializations -Wno-missing-import-lists -Wno-type-defaults -Wno-missing-local-signatures -Wno-monomorphism-restriction -Wno-missing-deriving-strategies -optP-Wno-nonportable-include-path -fhide-source-paths -fno-show-valid-hole-fits -fno-sort-valid-hole-fits+  cpp-options: -std=c++17 -D__EMBEDDED_SOUFFLE__   cxx-options: -std=c++17 -D__EMBEDDED_SOUFFLE__   include-dirs:       cbits@@ -295,6 +303,7 @@   build-depends:       array <=1.0     , base >=4.12 && <5+    , containers >=0.6.2.1 && <1     , deepseq >=1.4.4 && <2     , directory >=1.3.3 && <2     , filepath >=1.4.2 && <2@@ -302,6 +311,7 @@     , hspec >=2.6.1 && <3.0.0     , hspec-hedgehog ==0.*     , mtl >=2.0 && <3+    , neat-interpolation ==0.*     , process >=1.6 && <2     , souffle-haskell     , template-haskell >=2 && <3
tests/Test/Language/Souffle/CompiledSpec.hs view
@@ -24,9 +24,11 @@   programName = const "path"  instance Souffle.Fact Edge where+  type FactDirection Edge = 'Souffle.InputOutput   factName = const "edge"  instance Souffle.Fact Reachable where+  type FactDirection Reachable = 'Souffle.Output   factName = const "reachable"  instance Souffle.Marshal Edge@@ -43,18 +45,18 @@ spec :: Spec spec = describe "Souffle API" $ parallel $ do   describe "init" $ parallel $ do-    it "returns nothing if it cannot load a souffle program" $ do-      prog <- Souffle.runSouffle (Souffle.init BadPath)+    it "returns nothing in case it cannot load a souffle program" $ do+      prog <- Souffle.runSouffle BadPath pure       isJust prog `shouldBe` False -    it "returns just the program if it can load a souffle program" $ do-      prog <- Souffle.runSouffle (Souffle.init Path)+    it "returns just the program in case it can load a souffle program" $ do+      prog <- Souffle.runSouffle Path pure       isJust prog `shouldBe` True    describe "getFacts" $ parallel $ do     it "can retrieve facts as a list" $ do-      (edges, reachables) <- Souffle.runSouffle $ do-        prog <- fromJust <$> Souffle.init Path+      (edges, reachables) <- Souffle.runSouffle Path $ \handle -> do+        let prog = fromJust handle         Souffle.run prog         es <- Souffle.getFacts prog         rs <- Souffle.getFacts prog@@ -63,8 +65,8 @@       reachables `shouldBe` [Reachable "b" "c", Reachable "a" "c", Reachable "a" "b"]      it "can retrieve facts as a vector" $ do-      (edges, reachables) <- Souffle.runSouffle $ do-        prog <- fromJust <$> Souffle.init Path+      (edges, reachables) <- Souffle.runSouffle Path $ \handle -> do+        let prog = fromJust handle         Souffle.run prog         es <- Souffle.getFacts prog         rs <- Souffle.getFacts prog@@ -73,8 +75,8 @@       reachables `shouldBe` V.fromList [Reachable "a" "b", Reachable "a" "c", Reachable "b" "c"]      it "can retrieve facts as an array" $ do-      (edges, reachables) <- Souffle.runSouffle $ do-        prog <- fromJust <$> Souffle.init Path+      (edges, reachables) <- Souffle.runSouffle Path $ \handle -> do+        let prog = fromJust handle         Souffle.run prog         es <- Souffle.getFacts prog         rs <- Souffle.getFacts prog@@ -82,16 +84,16 @@       edges `shouldBe` A.listArray (0 :: Int, 1) [Edge "a" "b", Edge "b" "c"]       reachables `shouldBe` A.listArray (0 :: Int, 2) [Reachable "a" "b", Reachable "a" "c", Reachable "b" "c"] -    it "returns no facts if program hasn't run yet" $ do-      edges <- Souffle.runSouffle $ do-        prog <- fromJust <$> Souffle.init Path+    it "returns no facts in case program hasn't run yet" $ do+      edges <- Souffle.runSouffle Path $ \handle -> do+        let prog = fromJust handle         Souffle.getFacts prog       edges `shouldBe` ([] :: [Edge]) -  describe "addFact" $ parallel $ do+  describe "addFact" $ parallel $     it "adds a fact" $ do-      (edgesBefore, edgesAfter) <- Souffle.runSouffle $ do-        prog <- fromJust <$> Souffle.init Path+      (edgesBefore, edgesAfter) <- Souffle.runSouffle Path $ \handle -> do+        let prog = fromJust handle         Souffle.run prog         es1 <- Souffle.getFacts prog         Souffle.addFact prog $ Edge "e" "f"@@ -101,20 +103,10 @@       edgesBefore `shouldBe` [Edge "b" "c", Edge "a" "b"]       edgesAfter `shouldBe` [Edge "e" "f", Edge "b" "c", Edge "a" "b"] -    -- NOTE: this is different compared to interpreted version (bug in Souffle?)-    it "can add a fact even if it is marked as output" $ do-      reachables <- Souffle.runSouffle $ do-        prog <- fromJust <$> Souffle.init Path-        Souffle.addFact prog $ Reachable "e" "f"-        Souffle.run prog-        Souffle.getFacts prog-      reachables `shouldBe` [ Reachable "e" "f", Reachable "b" "c"-                            , Reachable "a" "c", Reachable "a" "b" ]-   describe "addFacts" $ parallel $     it "can add multiple facts at once" $ do-      (edgesBefore, edgesAfter) <- Souffle.runSouffle $ do-        prog <- fromJust <$> Souffle.init Path+      (edgesBefore, edgesAfter) <- Souffle.runSouffle Path $ \handle -> do+        let prog = fromJust handle         Souffle.run prog         es1 <- Souffle.getFacts prog         Souffle.addFacts prog [Edge "e" "f", Edge "f" "g"]@@ -126,16 +118,16 @@    describe "run" $ parallel $ do     it "is OK to run a program multiple times" $ do-      edges <- Souffle.runSouffle $ do-        prog <- fromJust <$> Souffle.init Path+      edges <- Souffle.runSouffle Path $ \handle -> do+        let prog = fromJust handle         Souffle.run prog         Souffle.run prog         Souffle.getFacts prog       edges `shouldBe` [Edge "b" "c", Edge "a" "b"]      it "discovers new facts after running with new facts" $ do-      (reachablesBefore, reachablesAfter) <- Souffle.runSouffle $ do-        prog <- fromJust <$> Souffle.init Path+      (reachablesBefore, reachablesAfter) <- Souffle.runSouffle Path $ \handle -> do+        let prog = fromJust handle         Souffle.run prog         rs1 <- Souffle.getFacts prog         Souffle.addFacts prog [Edge "e" "f", Edge "f" "g"]@@ -148,8 +140,8 @@    describe "configuring number of cores" $ parallel $     it "is possible to configure number of cores" $ do-      results <- Souffle.runSouffle $ do-        prog <- fromJust <$> Souffle.init Path+      results <- Souffle.runSouffle Path $ \handle -> do+        let prog = fromJust handle         numCpus1 <- Souffle.getNumThreads prog         Souffle.setNumThreads prog 4         numCpus2 <- Souffle.getNumThreads prog@@ -159,9 +151,9 @@       results `shouldBe` (1, 4, 2)    describe "findFact" $ parallel $ do-    it "returns Nothing if no matching fact was found" $ do-      (edge, reachable) <- Souffle.runSouffle $ do-        prog <- fromJust <$> Souffle.init Path+    it "returns Nothing in case no matching fact was found" $ do+      (edge, reachable) <- Souffle.runSouffle Path $ \handle -> do+        let prog = fromJust handle         Souffle.run prog         e <- Souffle.findFact prog $ Edge "c" "d"         r <- Souffle.findFact prog $ Reachable "d" "e"@@ -169,9 +161,9 @@       edge `shouldBe` Nothing       reachable `shouldBe` Nothing -    it "returns Just the fact if matching fact was found" $ do-      (edge, reachable) <- Souffle.runSouffle $ do-        prog <- fromJust <$> Souffle.init Path+    it "returns Just the fact in case matching fact was found" $ do+      (edge, reachable) <- Souffle.runSouffle Path $ \handle -> do+        let prog = fromJust handle         Souffle.run prog         e <- Souffle.findFact prog $ Edge "a" "b"         r <- Souffle.findFact prog $ Reachable "a" "c"
+ tests/Test/Language/Souffle/Experimental/Fixtures.hs view
@@ -0,0 +1,31 @@++{-# LANGUAGE DataKinds, TypeFamilies, DeriveGeneric, DeriveAnyClass #-}++module Test.Language.Souffle.Experimental.Fixtures+  ( module Test.Language.Souffle.Experimental.Fixtures+  ) where++import GHC.Generics+import Language.Souffle.Class+import Language.Souffle.Experimental++data CompiledProgram = CompiledProgram++instance Program CompiledProgram where+  type ProgramFacts CompiledProgram = [Edge, Reachable]+  programName = const "compiledprogram"++data Edge = Edge String String+  deriving (Generic, Marshal, FactMetadata)++data Reachable = Reachable String String+  deriving (Eq, Show, Generic, Marshal, FactMetadata)++instance Fact Edge where+  type FactDirection Edge = 'Input+  factName = const "edge"++instance Fact Reachable where+  type FactDirection Reachable = 'Output+  factName = const "reachable"+
+ tests/Test/Language/Souffle/Experimental/FixturesCompiled.hs view
@@ -0,0 +1,21 @@++{-# LANGUAGE TypeApplications, TemplateHaskell #-}+module Test.Language.Souffle.Experimental.FixturesCompiled () where++-- NOTE: this module can't be grouped together with "Fixtures"+-- due to TemplateHaskell staging restriction.++import Test.Language.Souffle.Experimental.Fixtures+import Language.Souffle.Experimental++$(embedProgram CompiledProgram $ do+  Predicate edge <- predicateFor @Edge+  Predicate reachable <- predicateFor @Reachable+  a <- var "a"+  b <- var "b"+  c <- var "c"+  reachable(a, b) |- edge(a, b)+  reachable(a, b) |- do+    edge(a, c)+    reachable(c, b)+ )
+ tests/Test/Language/Souffle/ExperimentalSpec.hs view
@@ -0,0 +1,1117 @@++{-# LANGUAGE DeriveGeneric, DeriveAnyClass, TypeApplications, QuasiQuotes, TypeOperators #-}+{-# LANGUAGE DataKinds, TypeFamilies #-}++module Test.Language.Souffle.ExperimentalSpec+  ( module Test.Language.Souffle.ExperimentalSpec+  ) where++import qualified Test.Language.Souffle.Experimental.Fixtures as F+import Test.Hspec+import GHC.Generics+import Data.Int+import Data.Word+import Data.Maybe (fromJust)+import qualified Data.Text as T+import qualified Data.Text.Lazy as TL+import System.IO.Temp+import Language.Souffle.Experimental+import Language.Souffle.Class+import Language.Souffle.Interpreted as I+import Language.Souffle.Compiled as C+import NeatInterpolation+++data Point = Point { x :: Int32, y :: Int32 }+  deriving (Generic, Marshal, FactMetadata)++newtype IntFact = IntFact Int32+  deriving (Generic, Marshal, FactMetadata)++newtype UnsignedFact = UnsignedFact Word32+  deriving (Generic, Marshal, FactMetadata)++newtype FloatFact = FloatFact Float+  deriving (Generic, Marshal, FactMetadata)++data TextFact = TextFact T.Text TL.Text+  deriving (Generic, Marshal, FactMetadata)++data Triple = Triple String Int32 String+  deriving (Generic, Marshal, FactMetadata)++newtype Vertex = Vertex String+  deriving (Generic, Marshal, FactMetadata)++data Edge = Edge String String+  deriving (Generic, Marshal, FactMetadata)++data Reachable = Reachable String String+  deriving (Eq, Show, Generic, Marshal, FactMetadata)++newtype BTreeFact = BTreeFact Int32+  deriving (Generic, Marshal)++newtype BrieFact = BrieFact Int32+  deriving (Generic, Marshal)++data EqRelFact = EqRelFact Int32 Int32+  deriving (Generic, Marshal)++data DSLProgram = DSLProgram++instance Program DSLProgram where+  type ProgramFacts DSLProgram =+    [ Point+    , IntFact+    , FloatFact+    , UnsignedFact+    , TextFact+    , BTreeFact+    , BrieFact+    , EqRelFact+    , Triple+    , Vertex+    , Edge+    , Reachable+    ]+  programName = const "dslprogram"++instance Fact Point where+  type FactDirection Point = 'Input+  factName = const "point"+instance Fact IntFact where+  type FactDirection IntFact = 'Input+  factName = const "intfact"+instance Fact FloatFact where+  type FactDirection FloatFact = 'Input+  factName = const "floatfact"+instance Fact UnsignedFact where+  type FactDirection UnsignedFact = 'Input+  factName = const "unsignedfact"+instance Fact TextFact where+  type FactDirection TextFact = 'InputOutput+  factName = const "textfact"+instance Fact Triple where+  type FactDirection Triple = 'Internal+  factName = const "triple"+instance Fact Vertex where+  type FactDirection Vertex = 'Output+  factName = const "vertex"+instance Fact Edge where+  type FactDirection Edge = 'Input+  factName = const "edge"+instance Fact Reachable where+  type FactDirection Reachable = 'Output+  factName = const "reachable"+instance Fact BTreeFact where+  type FactDirection BTreeFact = 'Internal+  factName = const "btreefact"+instance Fact BrieFact where+  type FactDirection BrieFact = 'Internal+  factName = const "briefact"+instance Fact EqRelFact where+  type FactDirection EqRelFact = 'Input+  factName = const "eqrelfact"+instance FactMetadata BTreeFact where+  factOpts = const $ Metadata BTree NoInline+instance FactMetadata BrieFact where+  factOpts = const $ Metadata Brie Inline+instance FactMetadata EqRelFact where+  factOpts = const $ Metadata EqRel NoInline++spec :: Spec+spec = describe "Souffle DSL" $ parallel $ do+  describe "code generation" $ parallel $ do+    let prog ==> txt =+          let rendered = T.strip $ render DSLProgram prog+              expected = T.strip txt+          in rendered `shouldBe` expected++    it "can render an empty program" $+      render DSLProgram (pure ()) `shouldBe` ""++    it "can render a program with an input type definition" $ do+      let prog = do+            Predicate _ <- predicateFor @Edge+            pure ()+      prog ==> [text|+        .decl edge(t1: symbol, t2: symbol)+        .input edge+        |]++    it "can render a program with an output type definition" $ do+      let prog = do+            Predicate _ <- predicateFor @Reachable+            pure ()+      prog ==> [text|+        .decl reachable(t1: symbol, t2: symbol)+        .output reachable+        |]++    it "can render a program with type declared both as in- and output" $ do+      let prog = do+            Predicate _ <- predicateFor @TextFact+            pure ()+      prog ==> [text|+        .decl textfact(t1: symbol, t2: symbol)+        .input textfact+        .output textfact+        |]++    it "can render a program with type declared and only used internally" $ do+      let prog = do+            Predicate _ <- predicateFor @Triple+            pure ()+      prog ==> [text|+        .decl triple(t1: symbol, t2: number, t3: symbol)+        |]++    it "renders type declaration based on type info" $ do+      let prog = do+            Predicate _ <- predicateFor @IntFact+            Predicate _ <- predicateFor @UnsignedFact+            Predicate _ <- predicateFor @FloatFact+            Predicate _ <- predicateFor @Triple+            Predicate _ <- predicateFor @BTreeFact+            Predicate _ <- predicateFor @BrieFact+            Predicate _ <- predicateFor @EqRelFact+            pure ()+      prog ==> [text|+        .decl intfact(t1: number)+        .input intfact+        .decl unsignedfact(t1: unsigned)+        .input unsignedfact+        .decl floatfact(t1: float)+        .input floatfact+        .decl triple(t1: symbol, t2: number, t3: symbol)+        .decl btreefact(t1: number) btree+        .decl briefact(t1: number) brie inline+        .decl eqrelfact(t1: number, t2: number) eqrel+        .input eqrelfact+        |]++    it "uses record accessors as attribute names in type declaration if provided" $ do+      let prog = do+            Predicate _ <- predicateFor @Point+            pure ()+      prog ==> [text|+        .decl point(x: number, y: number)+        .input point+        |]++    it "can render facts" $ do+      let prog = do+            Predicate edge <- predicateFor @Edge+            Predicate triple <- predicateFor @Triple+            Predicate txt <- predicateFor @TextFact+            Predicate unsigned <- predicateFor @UnsignedFact+            Predicate float <- predicateFor @FloatFact+            edge("a", "b")+            triple("cde", 1000, "fgh")+            txt("ijk", "lmn")+            unsigned(42)+            float(42.42)+            float(0.01)+      prog ==> [text|+        .decl edge(t1: symbol, t2: symbol)+        .input edge+        .decl triple(t1: symbol, t2: number, t3: symbol)+        .decl textfact(t1: symbol, t2: symbol)+        .input textfact+        .output textfact+        .decl unsignedfact(t1: unsigned)+        .input unsignedfact+        .decl floatfact(t1: float)+        .input floatfact+        edge("a", "b").+        triple("cde", 1000, "fgh").+        textfact("ijk", "lmn").+        unsignedfact(42).+        floatfact(42.42).+        floatfact(0.01).+        |]++    it "can render a relation with a single rule" $ do+      let prog = do+            Predicate edge <- predicateFor @Edge+            Predicate reachable <- predicateFor @Reachable+            a <- var "a"+            b <- var "b"+            reachable(a, b) |- edge(a, b)+      prog ==> [text|+        .decl edge(t1: symbol, t2: symbol)+        .input edge+        .decl reachable(t1: symbol, t2: symbol)+        .output reachable+        reachable(a, b) :-+          edge(a, b).+        |]++    it "can render a relation with multiple rules" $ do+      let prog = do+            Predicate edge <- predicateFor @Edge+            Predicate reachable <- predicateFor @Reachable+            a <- var "a"+            b <- var "b"+            reachable(a, b) |- do+              edge(a, a)+              edge(b, b)+              edge(a, b)+      prog ==> [text|+        .decl edge(t1: symbol, t2: symbol)+        .input edge+        .decl reachable(t1: symbol, t2: symbol)+        .output reachable+        reachable(a, b) :-+          edge(a, a),+          edge(b, b),+          edge(a, b).+        |]++    it "can render a relation containing a wildcard" $ do+      let prog = do+            Predicate edge <- predicateFor @Edge+            Predicate vertex <- predicateFor @Vertex+            a <- var "a"+            vertex(a) |- do+              edge(a, __)+              edge(__, a)+      prog ==> [text|+        .decl edge(t1: symbol, t2: symbol)+        .input edge+        .decl vertex(t1: symbol)+        .output vertex+        vertex(a) :-+          edge(a, _),+          edge(_, a).+        |]++    it "can render a relation with a logical or in the rule block" $ do+      let prog = do+            Predicate edge <- predicateFor @Edge+            Predicate reachable <- predicateFor @Reachable+            a <- var "a"+            b <- var "b"+            reachable(a, b) |- do+              let rules1 = do+                    edge(a, a)+                    edge(b, b)+                  rules2 = do+                    edge(a, b)+                    edge(b, a)+              rules1 \/ rules2+      prog ==> [text|+        .decl edge(t1: symbol, t2: symbol)+        .input edge+        .decl reachable(t1: symbol, t2: symbol)+        .output reachable+        reachable(a, b) :-+          edge(a, a),+          edge(b, b);+          edge(a, b),+          edge(b, a).+        |]++    it "can render a relation with multiple clauses" $ do+      let prog = do+            Predicate edge <- predicateFor @Edge+            Predicate reachable <- predicateFor @Reachable+            a <- var "a"+            b <- var "b"+            c <- var "c"+            reachable(a, b) |- edge(a, b)+            reachable(a, b) |- do+              edge(a, c)+              reachable(c, b)+      prog ==> [text|+        .decl edge(t1: symbol, t2: symbol)+        .input edge+        .decl reachable(t1: symbol, t2: symbol)+        .output reachable+        reachable(a, b) :-+          edge(a, b).+        reachable(a, b) :-+          edge(a, c),+          reachable(c, b).+        |]++    it "can render a mix of and- and or- clauses correctly" $ do+      let prog = do+            Predicate edge <- predicateFor @Edge+            Predicate reachable <- predicateFor @Reachable+            a <- var "a"+            b <- var "b"+            c <- var "c"+            reachable(a, b) |- do+              edge(a, c) \/ edge(a, b)+              reachable(c, b)+      prog ==> [text|+        .decl edge(t1: symbol, t2: symbol)+        .input edge+        .decl reachable(t1: symbol, t2: symbol)+        .output reachable+        reachable(a, b) :-+          (edge(a, c);+          edge(a, b)),+          reachable(c, b).+        |]++    it "discards empty alternative blocks" $ do+      let prog = do+            Predicate edge <- predicateFor @Edge+            Predicate reachable <- predicateFor @Reachable+            a <- var "a"+            b <- var "b"+            c <- var "c"+            reachable(a, b) |- do+              pure () \/ edge(a, c)+              reachable(c, b)+            reachable(a, b) |- do+              edge(a,c) \/ pure ()+              reachable(c, b)+            reachable(a, b) |- do+              pure () \/ do+                edge(a, c)+                reachable(c, b)+      prog ==> [text|+        .decl edge(t1: symbol, t2: symbol)+        .input edge+        .decl reachable(t1: symbol, t2: symbol)+        .output reachable+        reachable(a, b) :-+          edge(a, c),+          reachable(c, b).+        reachable(a, b) :-+          edge(a, c),+          reachable(c, b).+        reachable(a, b) :-+          edge(a, c),+          reachable(c, b).+        |]++    it "discards rules with empty rule blocks completely" $ do+      let prog = do+            Predicate reachable <- predicateFor @Reachable+            a <- var "a"+            b <- var "b"+            reachable(a, b) |-+              pure ()+            reachable(a, b) |- do+              pure () \/ pure ()+              pure ()+      prog ==> [text|+        .decl reachable(t1: symbol, t2: symbol)+        .output reachable+        |]++    it "discards empty negations" $ do+      let prog = do+            Predicate reachable <- predicateFor @Reachable+            a <- var "a"+            b <- var "b"+            reachable(a, b) |- do+              not' $ pure ()+      prog ==> [text|+        .decl reachable(t1: symbol, t2: symbol)+        .output reachable+        |]++    it "allows generically describing predicate relations" $ do+      -- NOTE: type signature not required, but it results in more clear type errors+      -- and can serve as documentation.+      let transitive :: forall prog p1 p2 t. Structure p1 ~ Structure p2+                      => Structure p1 ~ '[t, t]+                      => Predicate p1 -> Predicate p2 -> DSL prog 'Definition ()+          transitive (Predicate p1) (Predicate p2) = do+            a <- var "a"+            b <- var "b"+            c <- var "c"+            p1(a, b) |- p2(a, b)+            p1(a, b) |- do+              p2(a, c)+              p1(c, b)+          prog = do+            edge <- predicateFor @Edge+            reachable <- predicateFor @Reachable+            transitive reachable edge+      prog ==> [text|+        .decl edge(t1: symbol, t2: symbol)+        .input edge+        .decl reachable(t1: symbol, t2: symbol)+        .output reachable+        reachable(a, b) :-+          edge(a, b).+        reachable(a, b) :-+          edge(a, c),+          reachable(c, b).+        |]++    it "can render logical negation in rule block" $ do+      let prog = do+            Predicate edge <- predicateFor @Edge+            Predicate triple <- predicateFor @Triple+            a <- var "a"+            b <- var "b"+            c <- var "c"+            triple(a, b, c) |- do+              not' $ edge(a,c)+            triple(a, b, c) |- do+              not' $ do+                edge(a,a)+                edge(c,c)+            triple(a, b, c) |- do+              not' $ edge(a,a) \/ edge(c,c)+            triple(a, b, c) |- do+              not' $ not' $ edge(a,a)+      prog ==> [text|+        .decl edge(t1: symbol, t2: symbol)+        .input edge+        .decl triple(t1: symbol, t2: number, t3: symbol)+        triple(a, b, c) :-+          !edge(a, c).+        triple(a, b, c) :-+          !(edge(a, a),+          edge(c, c)).+        triple(a, b, c) :-+          !(edge(a, a);+          edge(c, c)).+        triple(a, b, c) :-+          !!edge(a, a).+        |]++    it "generates unique var names to avoid name collisions" $ do+      let prog = do+            Predicate edge <- predicateFor @Edge+            Predicate reachable <- predicateFor @Reachable+            a <- var "a"+            a' <- var "a"+            reachable(a, a') |- edge(a, a')+      prog ==> [text|+        .decl edge(t1: symbol, t2: symbol)+        .input edge+        .decl reachable(t1: symbol, t2: symbol)+        .output reachable+        reachable(a, a_1) :-+          edge(a, a_1).+        |]++    describe "operators" $ parallel $ do+      -- TODO: check for number, unsigned, float; check underscore is not allowed+      describe "arithmetic" $ parallel $ do+        it "supports +" $ do+          let prog = do+                Predicate int <- predicateFor @IntFact+                Predicate unsigned <- predicateFor @UnsignedFact+                Predicate float <- predicateFor @FloatFact+                int(10 + 32)+                int(10 + 80 + 10)+                unsigned(10 + 32)+                float(10.12 + 31.88)+          prog ==> [text|+            .decl intfact(t1: number)+            .input intfact+            .decl unsignedfact(t1: unsigned)+            .input unsignedfact+            .decl floatfact(t1: float)+            .input floatfact+            intfact(10 + 32).+            intfact(10 + 80 + 10).+            unsignedfact(10 + 32).+            floatfact(10.12 + 31.88).+            |]++        it "supports *" $ do+          let prog = do+                Predicate int <- predicateFor @IntFact+                Predicate unsigned <- predicateFor @UnsignedFact+                Predicate float <- predicateFor @FloatFact+                int(10 * 32)+                int(10 * 80 * 10)+                unsigned(10 * 32)+                float(10.12 * 31.88)+          prog ==> [text|+            .decl intfact(t1: number)+            .input intfact+            .decl unsignedfact(t1: unsigned)+            .input unsignedfact+            .decl floatfact(t1: float)+            .input floatfact+            intfact(10 * 32).+            intfact(10 * 80 * 10).+            unsignedfact(10 * 32).+            floatfact(10.12 * 31.88).+            |]++        it "supports binary -" $ do+          let prog = do+                Predicate int <- predicateFor @IntFact+                Predicate unsigned <- predicateFor @UnsignedFact+                Predicate float <- predicateFor @FloatFact+                int(10 - 32)+                int(10 - 80 - 10)+                unsigned(10 - 32)+                float(10.12 - 31.88)+          prog ==> [text|+            .decl intfact(t1: number)+            .input intfact+            .decl unsignedfact(t1: unsigned)+            .input unsignedfact+            .decl floatfact(t1: float)+            .input floatfact+            intfact(10 - 32).+            intfact(10 - 80 - 10).+            unsignedfact(10 - 32).+            floatfact(10.12 - 31.88).+            |]++        it "supports unary -" $ do+          let prog = do+                Predicate int <- predicateFor @IntFact+                Predicate float <- predicateFor @FloatFact+                int(-42)+                int(-100)+                float(-13.37)+          prog ==> [text|+            .decl intfact(t1: number)+            .input intfact+            .decl floatfact(t1: float)+            .input floatfact+            intfact(-42).+            intfact(-100).+            floatfact(-13.37).+            |]++        it "supports /" $ do+          let prog = do+                Predicate float <- predicateFor @FloatFact+                float(13.37 / 0.01)+          prog ==> [text|+            .decl floatfact(t1: float)+            .input floatfact+            floatfact(13.37 / 0.01).+            |]++        it "supports ^" $ do+          let prog = do+                Predicate int <- predicateFor @IntFact+                Predicate unsigned <- predicateFor @UnsignedFact+                Predicate float <- predicateFor @FloatFact+                int(10 .^ 32)+                int(10 .^ 80 .^ 10)+                unsigned(10 .^ 32)+                float(42.42 .^ 2)+          prog ==> [text|+            .decl intfact(t1: number)+            .input intfact+            .decl unsignedfact(t1: unsigned)+            .input unsignedfact+            .decl floatfact(t1: float)+            .input floatfact+            intfact(10 ^ 32).+            intfact(10 ^ 80 ^ 10).+            unsignedfact(10 ^ 32).+            floatfact(42.42 ^ 2.0).+            |]++        it "supports %" $ do+          let prog = do+                Predicate int <- predicateFor @IntFact+                Predicate unsigned <- predicateFor @UnsignedFact+                int(10 .% 32)+                int(10 .% 80 .% 10)+                unsigned(10 .% 32)+          prog ==> [text|+            .decl intfact(t1: number)+            .input intfact+            .decl unsignedfact(t1: unsigned)+            .input unsignedfact+            intfact(10 % 32).+            intfact(10 % 80 % 10).+            unsignedfact(10 % 32).+            |]++      describe "logical operators" $ parallel $ do+        it "supports band" $ do+          let prog = do+                Predicate int <- predicateFor @IntFact+                Predicate unsigned <- predicateFor @UnsignedFact+                int(10 `band` 32)+                int(10 `band` 80 `band` 10)+                unsigned(10 `band` 32)+          prog ==> [text|+            .decl intfact(t1: number)+            .input intfact+            .decl unsignedfact(t1: unsigned)+            .input unsignedfact+            intfact(10 band 32).+            intfact(10 band 80 band 10).+            unsignedfact(10 band 32).+            |]++        it "supports bor" $ do+          let prog = do+                Predicate int <- predicateFor @IntFact+                Predicate unsigned <- predicateFor @UnsignedFact+                int(10 `bor` 32)+                int(10 `bor` 80 `bor` 10)+                unsigned(10 `bor` 32)+          prog ==> [text|+            .decl intfact(t1: number)+            .input intfact+            .decl unsignedfact(t1: unsigned)+            .input unsignedfact+            intfact(10 bor 32).+            intfact(10 bor 80 bor 10).+            unsignedfact(10 bor 32).+            |]++        it "supports bxor" $ do+          let prog = do+                Predicate int <- predicateFor @IntFact+                Predicate unsigned <- predicateFor @UnsignedFact+                int(10 `bxor` 32)+                int(10 `bxor` 80 `bxor` 10)+                unsigned(10 `bxor` 32)+          prog ==> [text|+            .decl intfact(t1: number)+            .input intfact+            .decl unsignedfact(t1: unsigned)+            .input unsignedfact+            intfact(10 bxor 32).+            intfact(10 bxor 80 bxor 10).+            unsignedfact(10 bxor 32).+            |]++        it "supports land" $ do+          let prog = do+                Predicate int <- predicateFor @IntFact+                Predicate unsigned <- predicateFor @UnsignedFact+                int(10 `land` 32)+                int(10 `land` 80 `land` 10)+                unsigned(10 `land` 32)+          prog ==> [text|+            .decl intfact(t1: number)+            .input intfact+            .decl unsignedfact(t1: unsigned)+            .input unsignedfact+            intfact(10 land 32).+            intfact(10 land 80 land 10).+            unsignedfact(10 land 32).+            |]++        it "supports lor" $ do+          let prog = do+                Predicate int <- predicateFor @IntFact+                Predicate unsigned <- predicateFor @UnsignedFact+                int(10 `lor` 32)+                int(10 `lor` 80 `lor` 10)+                unsigned(10 `lor` 32)+          prog ==> [text|+            .decl intfact(t1: number)+            .input intfact+            .decl unsignedfact(t1: unsigned)+            .input unsignedfact+            intfact(10 lor 32).+            intfact(10 lor 80 lor 10).+            unsignedfact(10 lor 32).+            |]++      describe "comparisons and equality, inequality" $ parallel $ do+        -- NOTE: the following generated programs are not correct+        -- since vars are not grounded (but is done to keep tests succinct)+        it "supports <" $ do+          let prog = do+                Predicate int <- predicateFor @IntFact+                Predicate unsigned <- predicateFor @UnsignedFact+                Predicate float <- predicateFor @FloatFact+                a <- var "a"+                b <- var "b"+                c <- var "c"+                int(a) |- a .< 10+                unsigned(b) |- b .< 10+                float(c) |- c .< 10.1+          prog ==> [text|+            .decl intfact(t1: number)+            .input intfact+            .decl unsignedfact(t1: unsigned)+            .input unsignedfact+            .decl floatfact(t1: float)+            .input floatfact+            intfact(a) :-+              a < 10.+            unsignedfact(b) :-+              b < 10.+            floatfact(c) :-+              c < 10.1.+            |]++        it "supports <=" $ do+          let prog = do+                Predicate int <- predicateFor @IntFact+                Predicate unsigned <- predicateFor @UnsignedFact+                Predicate float <- predicateFor @FloatFact+                a <- var "a"+                b <- var "b"+                c <- var "c"+                int(a) |- a .<= 10+                unsigned(b) |- b .<= 10+                float(c) |- c .<= 10.1+          prog ==> [text|+            .decl intfact(t1: number)+            .input intfact+            .decl unsignedfact(t1: unsigned)+            .input unsignedfact+            .decl floatfact(t1: float)+            .input floatfact+            intfact(a) :-+              a <= 10.+            unsignedfact(b) :-+              b <= 10.+            floatfact(c) :-+              c <= 10.1.+            |]++        it "supports >" $ do+          let prog = do+                Predicate int <- predicateFor @IntFact+                Predicate unsigned <- predicateFor @UnsignedFact+                Predicate float <- predicateFor @FloatFact+                a <- var "a"+                b <- var "b"+                c <- var "c"+                int(a) |- a .> 10+                unsigned(b) |- b .> 10+                float(c) |- c .> 10.1+          prog ==> [text|+            .decl intfact(t1: number)+            .input intfact+            .decl unsignedfact(t1: unsigned)+            .input unsignedfact+            .decl floatfact(t1: float)+            .input floatfact+            intfact(a) :-+              a > 10.+            unsignedfact(b) :-+              b > 10.+            floatfact(c) :-+              c > 10.1.+            |]++        it "supports >=" $ do+          let prog = do+                Predicate int <- predicateFor @IntFact+                Predicate unsigned <- predicateFor @UnsignedFact+                Predicate float <- predicateFor @FloatFact+                a <- var "a"+                b <- var "b"+                c <- var "c"+                int(a) |- a .>= 10+                unsigned(b) |- b .>= 10+                float(c) |- c .>= 10.1+          prog ==> [text|+            .decl intfact(t1: number)+            .input intfact+            .decl unsignedfact(t1: unsigned)+            .input unsignedfact+            .decl floatfact(t1: float)+            .input floatfact+            intfact(a) :-+              a >= 10.+            unsignedfact(b) :-+              b >= 10.+            floatfact(c) :-+              c >= 10.1.+            |]++        it "supports =" $ do+          let prog = do+                Predicate int <- predicateFor @IntFact+                Predicate unsigned <- predicateFor @UnsignedFact+                Predicate float <- predicateFor @FloatFact+                Predicate vertex <- predicateFor @Vertex+                a <- var "a"+                b <- var "b"+                c <- var "c"+                d <- var "d"+                int(a) |- a .= 10+                unsigned(b) |- b .= 10+                float(c) |- c .= 10.1+                vertex(d) |- d .= "abc"+          prog ==> [text|+            .decl intfact(t1: number)+            .input intfact+            .decl unsignedfact(t1: unsigned)+            .input unsignedfact+            .decl floatfact(t1: float)+            .input floatfact+            .decl vertex(t1: symbol)+            .output vertex+            intfact(a) :-+              a = 10.+            unsignedfact(b) :-+              b = 10.+            floatfact(c) :-+              c = 10.1.+            vertex(d) :-+              d = "abc".+            |]++        it "supports !=" $ do+          let prog = do+                Predicate int <- predicateFor @IntFact+                Predicate unsigned <- predicateFor @UnsignedFact+                Predicate float <- predicateFor @FloatFact+                Predicate vertex <- predicateFor @Vertex+                a <- var "a"+                b <- var "b"+                c <- var "c"+                d <- var "d"+                int(a) |- a .!= 10+                unsigned(b) |- b .!= 10+                float(c) |- c .!= 10.1+                vertex(d) |- d .!= "abc"+          prog ==> [text|+            .decl intfact(t1: number)+            .input intfact+            .decl unsignedfact(t1: unsigned)+            .input unsignedfact+            .decl floatfact(t1: float)+            .input floatfact+            .decl vertex(t1: symbol)+            .output vertex+            intfact(a) :-+              a != 10.+            unsignedfact(b) :-+              b != 10.+            floatfact(c) :-+              c != 10.1.+            vertex(d) :-+              d != "abc".+            |]++    describe "functors" $ parallel $ do+      it "supports max" $ do+        let prog = do+              Predicate int <- predicateFor @IntFact+              Predicate unsigned <- predicateFor @UnsignedFact+              Predicate float <- predicateFor @FloatFact+              int(max' 10 32)+              int(max' (max' 10 80) 10)+              unsigned(max' 10 32)+              float(max' 42.42 2)+        prog ==> [text|+          .decl intfact(t1: number)+          .input intfact+          .decl unsignedfact(t1: unsigned)+          .input unsignedfact+          .decl floatfact(t1: float)+          .input floatfact+          intfact(max(10, 32)).+          intfact(max(max(10, 80), 10)).+          unsignedfact(max(10, 32)).+          floatfact(max(42.42, 2.0)).+          |]++      it "supports min" $ do+        let prog = do+              Predicate int <- predicateFor @IntFact+              Predicate unsigned <- predicateFor @UnsignedFact+              Predicate float <- predicateFor @FloatFact+              int(min' 10 32)+              int(min' (min' 10 80) 10)+              unsigned(min' 10 32)+              float(min' 42.42 2)+        prog ==> [text|+          .decl intfact(t1: number)+          .input intfact+          .decl unsignedfact(t1: unsigned)+          .input unsignedfact+          .decl floatfact(t1: float)+          .input floatfact+          intfact(min(10, 32)).+          intfact(min(min(10, 80), 10)).+          unsignedfact(min(10, 32)).+          floatfact(min(42.42, 2.0)).+          |]++      it "supports cat" $ do+        let prog = do+              Predicate vertex <- predicateFor @Vertex+              vertex(cat "abc" "def")+              vertex(cat "abc" $ cat "def" "ghi")+        prog ==> [text|+          .decl vertex(t1: symbol)+          .output vertex+          vertex(cat("abc", "def")).+          vertex(cat("abc", cat("def", "ghi"))).+          |]++      it "supports contains" $ do+        let prog = do+              Predicate vertex <- predicateFor @Vertex+              Predicate int <- predicateFor @IntFact+              a <- var "a"+              b <- var "b"+              int(0) |- do+                vertex(a)+                vertex(b)+                contains a b+        prog ==> [text|+          .decl vertex(t1: symbol)+          .output vertex+          .decl intfact(t1: number)+          .input intfact+          intfact(0) :-+            vertex(a),+            vertex(b),+            contains(a, b).+          |]++      it "supports match" $ do+        let prog = do+              Predicate vertex <- predicateFor @Vertex+              Predicate int <- predicateFor @IntFact+              a <- var "a"+              int(0) |- do+                vertex(a)+                match "*.a" a+        prog ==> [text|+          .decl vertex(t1: symbol)+          .output vertex+          .decl intfact(t1: number)+          .input intfact+          intfact(0) :-+            vertex(a),+            match("*.a", a).+          |]++      it "supports ord" $ do+        let prog = do+              Predicate vertex <- predicateFor @Vertex+              Predicate int <- predicateFor @IntFact+              a <- var "a"+              int(ord a) |-+                vertex(a)+        prog ==> [text|+          .decl vertex(t1: symbol)+          .output vertex+          .decl intfact(t1: number)+          .input intfact+          intfact(ord(a)) :-+            vertex(a).+          |]++      it "supports strlen" $ do+        let prog = do+              Predicate vertex <- predicateFor @Vertex+              Predicate int <- predicateFor @IntFact+              a <- var "a"+              int(strlen a) |-+                vertex(a)+        prog ==> [text|+          .decl vertex(t1: symbol)+          .output vertex+          .decl intfact(t1: number)+          .input intfact+          intfact(strlen(a)) :-+            vertex(a).+          |]++      it "supports substr" $ do+        let prog = do+              Predicate vertex <- predicateFor @Vertex+              a <- var "a"+              vertex(a) |-+                a .= substr "Hello" 1 3+        prog ==> [text|+          .decl vertex(t1: symbol)+          .output vertex+          vertex(a) :-+            a = substr("Hello", 1, 3).+          |]+++      it "supports to_number" $ do+        let prog = do+              Predicate vertex <- predicateFor @Vertex+              Predicate int <- predicateFor @IntFact+              a <- var "a"+              int(to_number a) |-+                vertex(a)+        prog ==> [text|+          .decl vertex(t1: symbol)+          .output vertex+          .decl intfact(t1: number)+          .input intfact+          intfact(to_number(a)) :-+            vertex(a).+          |]++      it "supports to_string" $ do+        let prog = do+              Predicate vertex <- predicateFor @Vertex+              Predicate int <- predicateFor @IntFact+              a <- var "a"+              vertex(to_string a) |-+                int(a)+        prog ==> [text|+          .decl vertex(t1: symbol)+          .output vertex+          .decl intfact(t1: number)+          .input intfact+          vertex(to_string(a)) :-+            intfact(a).+          |]++  describe "running DSL code directly " $ parallel $ do+    it "can run DSL with default config in interpreted mode" $ do+      let ast = do+            Predicate edge <- predicateFor @Edge+            Predicate reachable <- predicateFor @Reachable+            a <- var "a"+            b <- var "b"+            c <- var "c"+            reachable(a, b) |- edge(a, b)+            reachable(a, b) |- do+              edge(a, c)+              reachable(c, b)+          action handle = do+            let prog = fromJust handle+            I.addFacts prog [Edge "a" "b", Edge "b" "c"]+            I.run prog+            I.getFacts prog+      rs <- runSouffleInterpreted DSLProgram ast action+      rs `shouldBe` [Reachable "a" "b", Reachable "a" "c", Reachable "b" "c"]++    it "can run DSL with modified config in interpreted mode" $ do+      tmpDir <- getCanonicalTemporaryDirectory+      souffleHsDir <- createTempDirectory tmpDir "souffle-haskell"+      cfg <- I.defaultConfig+      let config = cfg { I.cfgDatalogDir = souffleHsDir }+          ast = do+            Predicate edge <- predicateFor @Edge+            Predicate reachable <- predicateFor @Reachable+            a <- var "a"+            b <- var "b"+            c <- var "c"+            reachable(a, b) |- edge(a, b)+            reachable(a, b) |- do+              edge(a, c)+              reachable(c, b)+          action handle = do+            let prog = fromJust handle+            I.addFacts prog [Edge "a" "b", Edge "b" "c"]+            I.run prog+            I.getFacts prog+      rs <- runSouffleInterpretedWith config DSLProgram ast action+      rs `shouldBe` [Reachable "a" "b", Reachable "a" "c", Reachable "b" "c"]++    it "can run DSL in compiled mode" $ do+      rs <- C.runSouffle F.CompiledProgram $ \handle -> do+        let prog = fromJust handle+        C.addFacts prog [F.Edge "a" "b", F.Edge "b" "c"]+        C.run prog+        C.getFacts prog+      rs `shouldBe` [F.Reachable "b" "c", F.Reachable "a" "c", F.Reachable "a" "b"]+
tests/Test/Language/Souffle/InterpretedSpec.hs view
@@ -7,7 +7,6 @@  import Test.Hspec import GHC.Generics-import Control.Monad (join) import Data.Maybe import Control.Monad.IO.Class (liftIO) import System.Directory@@ -30,9 +29,11 @@   deriving (Eq, Show, Generic)  instance Souffle.Fact Edge where+  type FactDirection Edge = 'Souffle.InputOutput   factName = const "edge"  instance Souffle.Fact Reachable where+  type FactDirection Reachable = 'Souffle.Output   factName = const "reachable"  instance Souffle.Marshal Edge@@ -58,115 +59,91 @@ spec :: Spec spec = describe "Souffle API" $ parallel $ do   describe "init" $ parallel $ do-    it "returns nothing if it cannot load a souffle program" $ do-      prog <- Souffle.runSouffle (Souffle.init BadPath)+    it "returns nothing in case it cannot load a souffle program" $ do+      prog <- Souffle.runSouffle BadPath pure       isJust prog `shouldBe` False -    it "returns just the program if it can load a souffle program" $ do-      prog <- Souffle.runSouffle $ do-        handle <- fromJust <$> Souffle.init Path-        Souffle.cleanup handle-        pure $ Just handle+    it "returns just the program in case it can load a souffle program" $ do+      prog <- Souffle.runSouffle Path pure       isJust prog `shouldBe` True    describe "getFacts" $ parallel $ do     it "can retrieve facts as a list" $ do-      (edges, reachables) <- Souffle.runSouffle $ do-        prog <- fromJust <$> Souffle.init PathNoInput+      (edges, reachables) <- Souffle.runSouffle PathNoInput $ \handle -> do+        let prog = fromJust handle         Souffle.run prog         es <- Souffle.getFacts prog         rs <- Souffle.getFacts prog-        Souffle.cleanup prog         pure (es , rs)       edges `shouldBe` [Edge "a" "b", Edge "b" "c"]       reachables `shouldBe` [Reachable "a" "b", Reachable "a" "c", Reachable "b" "c"]      it "can retrieve facts as a vector" $ do-      (edges, reachables) <- Souffle.runSouffle $ do-        prog <- fromJust <$> Souffle.init PathNoInput+      (edges, reachables) <- Souffle.runSouffle PathNoInput $ \handle -> do+        let prog = fromJust handle         Souffle.run prog         es <- Souffle.getFacts prog         rs <- Souffle.getFacts prog-        Souffle.cleanup prog         pure (es , rs)       edges `shouldBe` V.fromList [Edge "a" "b", Edge "b" "c"]       reachables `shouldBe` V.fromList [Reachable "a" "b", Reachable "a" "c", Reachable "b" "c"]      it "can retrieve facts as an array" $ do-      (edges, reachables) <- Souffle.runSouffle $ do-        prog <- fromJust <$> Souffle.init PathNoInput+      (edges, reachables) <- Souffle.runSouffle PathNoInput $ \handle -> do+        let prog = fromJust handle         Souffle.run prog         es <- Souffle.getFacts prog         rs <- Souffle.getFacts prog-        Souffle.cleanup prog         pure (es , rs)       edges `shouldBe` A.listArray (0 :: Int, 1) [Edge "a" "b", Edge "b" "c"]       reachables `shouldBe` A.listArray (0 :: Int, 2) [Reachable "a" "b", Reachable "a" "c", Reachable "b" "c"] -    it "returns no facts if program hasn't run yet" $ do-      edges <- Souffle.runSouffle $ do-        prog <- fromJust <$> Souffle.init Path-        results <- Souffle.getFacts prog-        Souffle.cleanup prog-        pure results+    it "returns no facts in case program hasn't run yet" $ do+      edges <- Souffle.runSouffle Path $ Souffle.getFacts . fromJust       edges `shouldBe` ([] :: [Edge])      it "can retrieve facts from custom output directory" $ do       cfg <- Souffle.defaultConfig       tmp <- getTestTemporaryDirectory-      (edges, reachables) <- Souffle.runSouffleWith (cfg { Souffle.cfgOutputDir = Just tmp }) $ do-        prog <- fromJust <$> Souffle.init PathNoInput+      let cfg' = cfg { Souffle.cfgOutputDir = Just tmp }+      (edges, reachables) <- Souffle.runSouffleWith cfg' PathNoInput $ \handle -> do+        let prog = fromJust handle         Souffle.run prog         es <- Souffle.getFacts prog         rs <- Souffle.getFacts prog-        Souffle.cleanup prog         pure (es , rs)       edges `shouldBe` V.fromList [Edge "a" "b", Edge "b" "c"]       reachables `shouldBe` V.fromList [Reachable "a" "b", Reachable "a" "c", Reachable "b" "c"]       outputDirExist <- doesDirectoryExist tmp-      outputDirExist `shouldBe` False+      outputDirExist `shouldBe` True  -- should not be automatically cleaned up    describe "addFact" $ parallel $ do     it "adds a fact" $ do-      edges <- Souffle.runSouffle $ do-        prog <- fromJust <$> Souffle.init Path+      edges <- Souffle.runSouffle Path $ \handle -> do+        let prog = fromJust handle         Souffle.addFact prog $ Edge "e" "f"         Souffle.run prog-        edges <- Souffle.getFacts prog-        Souffle.cleanup prog-        pure edges+        Souffle.getFacts prog       edges `shouldBe` [Edge "a" "b", Edge "b" "c", Edge "e" "f"] -    -- NOTE: this is different compared to compiled version (bug in Souffle?)-    it "can not add a fact if it is not marked as input" $ do-      reachables <- Souffle.runSouffle $ do-        prog <- fromJust <$> Souffle.init PathNoInput-        Souffle.addFact prog $ Reachable "e" "f"-        Souffle.run prog-        reachables <- Souffle.getFacts prog-        Souffle.cleanup prog-        pure reachables-      reachables `shouldBe`-        [ Reachable "a" "b", Reachable "a" "c", Reachable "b" "c" ]-     it "adds a fact to a custom input directory" $ do       cfg <- Souffle.defaultConfig       tmp <- getTestTemporaryDirectory-      join $ Souffle.runSouffleWith (cfg { Souffle.cfgFactDir = Just tmp }) $ do -- Souffle-        prog <- fromJust <$> Souffle.init Path+      let cfg' = cfg { Souffle.cfgFactDir = Just tmp }+      Souffle.runSouffleWith cfg' Path $ \handle -> do+        let prog = fromJust handle         Souffle.addFact prog $ Edge "e" "f"         Souffle.run prog         edges <- Souffle.getFacts prog-        entries <- liftIO $ listDirectory tmp-        Souffle.cleanup prog-        pure $ do -- IO+        liftIO $ do+          entries <- listDirectory tmp           edges `shouldBe` [Edge "a" "b", Edge "b" "c", Edge "e" "f"]           length entries `shouldNotBe` 0    describe "addFacts" $ parallel $     it "can add multiple facts at once" $ do-      edges <- Souffle.runSouffle $ do-        prog <- fromJust <$> Souffle.init Path+      edges <- Souffle.runSouffle Path $ \handle -> do+        let prog = fromJust handle         Souffle.addFacts prog [Edge "e" "f", Edge "f" "g"]         Souffle.run prog         Souffle.getFacts prog@@ -174,25 +151,22 @@    describe "run" $ parallel $ do     it "is OK to run a program multiple times" $ do-      edges <- Souffle.runSouffle $ do-        prog <- fromJust <$> Souffle.init PathNoInput+      edges <- Souffle.runSouffle PathNoInput $ \handle -> do+        let prog = fromJust handle         Souffle.run prog         Souffle.run prog-        facts <- Souffle.getFacts prog-        Souffle.cleanup prog-        pure facts+        Souffle.getFacts prog       edges `shouldBe` [Reachable "a" "b", Reachable "a" "c", Reachable "b" "c"]      it "discovers new facts after running with new facts" $ do-      (reachablesBefore, reachablesAfter) <- Souffle.runSouffle $ do-        prog <- fromJust <$> Souffle.init Path+      (reachablesBefore, reachablesAfter) <- Souffle.runSouffle Path $ \handle -> do+        let prog = fromJust handle         Souffle.addFacts prog [Edge "c" "d"]         Souffle.run prog         rs1 <- Souffle.getFacts prog         Souffle.addFacts prog [Edge "b" "e"]         Souffle.run prog         rs2 <- Souffle.getFacts prog-        Souffle.cleanup prog         pure (rs1, rs2)       reachablesBefore `shouldBe`         [ Reachable "a" "b", Reachable "a" "c", Reachable "a" "d"@@ -203,20 +177,19 @@         , Reachable "b" "e", Reachable "c" "d" ]      it "saves stdout and stderr output after run" $ do-      (stdout, stderr) <- Souffle.runSouffle $ do-        prog <- fromJust <$> Souffle.init PathNoInput+      (stdout, stderr) <- Souffle.runSouffle PathNoInput $ \handle -> do+        let prog = fromJust handle         Souffle.run prog         out <- Souffle.souffleStdOut prog         err <- Souffle.souffleStdErr prog-        Souffle.cleanup prog         pure (out, err)       stdout `shouldBe` Just ""       stderr `shouldBe` Just ""    describe "configuring number of cores" $ parallel $     it "is possible to configure number of cores" $ do-      results <- Souffle.runSouffle $ do-        prog <- fromJust <$> Souffle.init Path+      results <- Souffle.runSouffle Path $ \handle -> do+        let prog = fromJust handle         numCpus1 <- Souffle.getNumThreads prog         Souffle.setNumThreads prog 4         numCpus2 <- Souffle.getNumThreads prog@@ -226,9 +199,9 @@       results `shouldBe` (1, 4, 2)    describe "findFact" $ parallel $ do-    it "returns Nothing if no matching fact was found" $ do-      (edge, reachable) <- Souffle.runSouffle $ do-        prog <- fromJust <$> Souffle.init PathNoInput+    it "returns Nothing in case no matching fact was found" $ do+      (edge, reachable) <- Souffle.runSouffle PathNoInput $ \handle -> do+        let prog = fromJust handle         Souffle.run prog         e <- Souffle.findFact prog $ Edge "c" "d"         r <- Souffle.findFact prog $ Reachable "d" "e"@@ -236,9 +209,9 @@       edge `shouldBe` Nothing       reachable `shouldBe` Nothing -    it "returns Just the fact if matching fact was found" $ do-      (edge, reachable) <- Souffle.runSouffle $ do-        prog <- fromJust <$> Souffle.init PathNoInput+    it "returns Just the fact in case matching fact was found" $ do+      (edge, reachable) <- Souffle.runSouffle PathNoInput $ \handle -> do+        let prog = fromJust handle         Souffle.run prog         e <- Souffle.findFact prog $ Edge "a" "b"         r <- Souffle.findFact prog $ Reachable "a" "c"
tests/Test/Language/Souffle/MarshalSpec.hs view
@@ -100,21 +100,27 @@   deriving (Eq, Show, Generic)  instance Souffle.Fact StringFact where+  type FactDirection StringFact = 'Souffle.InputOutput   factName = const "string_fact"  instance Souffle.Fact TextFact where+  type FactDirection TextFact = 'Souffle.InputOutput   factName = const "string_fact"  instance Souffle.Fact LazyTextFact where+  type FactDirection LazyTextFact = 'Souffle.InputOutput   factName = const "string_fact"  instance Souffle.Fact Int32Fact where+  type FactDirection Int32Fact = 'Souffle.InputOutput   factName = const "number_fact"  instance Souffle.Fact Word32Fact where+  type FactDirection Word32Fact = 'Souffle.InputOutput   factName = const "unsigned_fact"  instance Souffle.Fact FloatFact where+  type FactDirection FloatFact = 'Souffle.InputOutput   factName = const "float_fact"  instance Souffle.Marshal StringFact@@ -125,12 +131,14 @@ instance Souffle.Marshal FloatFact  instance Souffle.Program RoundTrip where-  type ProgramFacts RoundTrip = +  type ProgramFacts RoundTrip =     [StringFact, TextFact, LazyTextFact, Int32Fact, Word32Fact, FloatFact]   programName = const "round_trip"  type RoundTripAction-  = forall a. (Souffle.Fact a, Souffle.ContainsFact RoundTrip a)+  = forall a. Souffle.Fact a+  => Souffle.ContainsInputFact RoundTrip a+  => Souffle.ContainsOutputFact RoundTrip a   => a -> PropertyT IO a  spec :: Spec@@ -173,7 +181,6 @@             fact' <- run fact             fact === fact' -          {- TODO: enable this test once souffle floating point conversions are fixed           it "can serialize and deserialize Float values" $ hedgehog $ do             let epsilon = 1e-6                 fmin = -1e9@@ -182,20 +189,17 @@             let fact = FloatFact x             FloatFact x' <- run fact             (abs (x' - x) < epsilon) === True-          -}      describe "interpreted mode" $ parallel $-      roundTripTests $ \fact -> liftIO $ Interpreted.runSouffle $ do-        handle <- fromJust <$> Interpreted.init RoundTrip-        Interpreted.addFact handle fact-        Interpreted.run handle-        fact' <- Prelude.head <$> Interpreted.getFacts handle-        Interpreted.cleanup handle-        pure fact'+      roundTripTests $ \fact -> liftIO $ Interpreted.runSouffle RoundTrip $ \handle -> do+        let prog = fromJust handle+        Interpreted.addFact prog fact+        Interpreted.run prog+        Prelude.head <$> Interpreted.getFacts prog      describe "compiled mode" $ parallel $-      roundTripTests $ \fact -> liftIO $ Compiled.runSouffle $ do-        handle <- fromJust <$> Compiled.init RoundTrip-        Compiled.addFact handle fact-        Compiled.run handle-        Prelude.head <$> Compiled.getFacts handle+      roundTripTests $ \fact -> liftIO $ Compiled.runSouffle RoundTrip $ \handle -> do+        let prog = fromJust handle+        Compiled.addFact prog fact+        Compiled.run prog+        Prelude.head <$> Compiled.getFacts prog