Compare commits

..

8 Commits

14 changed files with 155 additions and 132 deletions

View File

@ -51,21 +51,18 @@ extra-doc-files: CHANGELOG.md
-- Extra source files to be distributed with the package, such as examples, or a tutorial module.
-- extra-source-files:
common commonSettings
-- Base language which the package is written in.
default-language: Haskell2010
default-extensions:
OverloadedStrings
common warnings
ghc-options: -Wall
test-suite haskell-exps-test
-- Import common warning flags.
import: warnings
-- Base language which the package is written in.
default-language: Haskell2010
-- Modules included in this executable, other than Main.
-- other-modules:
-- LANGUAGE extensions used by modules in this package.
-- other-extensions:
import: warnings, commonSettings
-- The interface type and version of the test suite.
type: exitcode-stdio-1.0
@ -77,15 +74,16 @@ test-suite haskell-exps-test
main-is: Main.hs
-- Test dependencies.
build-depends: base, containers, megaparsec, hspec, langfeatures
other-modules: Test.OlogsSpec,
Test.SimpleParserSpec,
Test.ArithmeticParserSpec,
Test.Datalog.DatalogParserSpec,
build-depends: base, containers, megaparsec, hspec, langfeatures, text
other-modules: Test.OlogsSpec
Test.SimpleParserSpec
Test.ArithmeticParserSpec
Test.Datalog.DatalogParserSpec
Test.Datalog.InMemoryDBSpec
Test.Datalog.NaiveQESpec
library langfeatures
default-language: Haskell2010
import: warnings, commonSettings
build-depends: base, containers, megaparsec, parser-combinators, text
hs-source-dirs: src
exposed-modules: Ologs
@ -95,13 +93,13 @@ library langfeatures
Datalog.InMemoryDB
Datalog.Rules
Datalog.DatalogDB
ghc-options: -Wall
default-extensions:
OverloadedStrings
Datalog.NaiveQE
Datalog.QueryEngine
executable haskell-experiments
default-language: Haskell2010
import: warnings, commonSettings
build-depends: base, containers
main-is: Main.hs
hs-source-dirs: src

Binary file not shown.

After

Width:  |  Height:  |  Size: 101 KiB

View File

@ -1,5 +1,3 @@
{-# LANGUAGE OverloadedStrings #-}
module ArithmeticParser where
import Text.Megaparsec

View File

@ -1,8 +1,8 @@
{-# HLINT ignore "Redundant flip" #-}
{-# LANGUAGE ImportQualifiedPost #-}
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE ScopedTypeVariables #-}
{-# OPTIONS_GHC -Wno-unrecognised-pragmas #-}
{-# LANGUAGE BlockArguments #-}
module Datalog.DatalogDB where
@ -79,3 +79,34 @@ addFact (Literal neg relationName terms) db =
newRelation = lookupRelationArity relationName db newArity (Set.singleton terms)
extraConstants = Set.fromList terms
-- the world isn't quite ready for these, as the 'update relation' pattern doesn't fir - maybe use a lens?
-- lookupRelation00 :: DatalogDB db =>
-- Text -> db -> Int -> (Relation -> Relation) -> db
-- lookupRelation00 relationName db newArity update =
-- insertRelation db (update newRelation)
-- where
-- newRelation = case lookupRelation db relationName of
-- Nothing -> Relation relationName newArity Set.empty []
-- Just relation ->
-- if _arity relation == newArity then
-- relation
-- else throw $ BadArityException relationName newArity
-- lookupRelation000 :: DatalogDB db =>
-- Text -> db -> Int -> Set [Constant] -> (Relation -> Relation) -> db
-- lookupRelation000 relationName db newArity tuples update =
-- lookupRelation00 relationName db newArity \relation ->
-- update relation {
-- _tuples = Set.union tuples $ _tuples relation
-- }
-- lookupRelationArity0 :: DatalogDB db => Text -> db -> Int -> (Relation -> Relation) -> db
-- lookupRelationArity0 relationName db newArity update =
-- insertRelation db (update newRelation)
-- where
-- newRelation = case lookupRelation db relationName of
-- Nothing -> Relation relationName newArity Set.empty []
-- Just relation ->
-- if _arity relation == newArity then relation
-- else throw $ BadArityException relationName newArity

View File

@ -1,5 +1,3 @@
{-# LANGUAGE OverloadedStrings #-}
module Datalog.DatalogParser where
import Data.Void

View File

@ -1,6 +1,5 @@
{-# HLINT ignore "Redundant flip" #-}
{-# LANGUAGE ImportQualifiedPost #-}
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE ScopedTypeVariables #-}
{-# OPTIONS_GHC -Wno-unrecognised-pragmas #-}
{-# LANGUAGE InstanceSigs #-}
@ -23,7 +22,6 @@ data InMemoryDB = InMemoryDB
, constants :: Set Constant
} deriving (Show, Eq)
instance DatalogDB InMemoryDB where
emptyDB :: InMemoryDB
emptyDB = InMemoryDB
@ -47,26 +45,6 @@ instance DatalogDB InMemoryDB where
constants = Set.union newConstants (constants db)
}
lookupRelation00 :: DatalogDB db =>
Text -> db -> Int -> (Relation -> Relation) -> db
lookupRelation00 relationName db newArity update =
insertRelation db (update newRelation)
where
newRelation = case lookupRelation db relationName of
Nothing -> Relation relationName newArity Set.empty []
Just relation ->
if _arity relation == newArity then
relation
else throw $ BadArityException relationName newArity
lookupRelation000 :: DatalogDB db =>
Text -> db -> Int -> Set [Constant] -> (Relation -> Relation) -> db
lookupRelation000 relationName db newArity tuples update =
lookupRelation00 relationName db newArity \relation ->
update relation {
_tuples = Set.union tuples $ _tuples relation
}
withFacts :: DatalogDB db => [Text] -> db
withFacts =
foldr (addFact . extractFact) emptyDB

View File

@ -0,0 +1,35 @@
{-# HLINT ignore "Redundant flip" #-}
-- {-# LANGUAGE ImportQualifiedPost #-}
-- {-# LANGUAGE InstanceSigs #-}
{-# LANGUAGE ScopedTypeVariables #-}
{-# OPTIONS_GHC -Wno-unrecognised-pragmas #-}
{-# LANGUAGE BlockArguments #-}
{-# HLINT ignore "Redundant flip" #-}
{-# LANGUAGE ImportQualifiedPost #-}
{-# LANGUAGE ScopedTypeVariables #-}
{-# OPTIONS_GHC -Wno-unrecognised-pragmas #-}
{-# LANGUAGE InstanceSigs #-}
{-# LANGUAGE BlockArguments #-}
module Datalog.NaiveQE where
import Datalog.QueryEngine
import Data.Text
import Datalog.DatalogDB
import Datalog.DatalogParser
import Control.Exception
data NaiveQE = NaiveQE
{
} deriving (Show, Eq)
instance QueryEngine NaiveQE where
queryEngine :: (DatalogDB db) => db -> NaiveQE
queryEngine db = NaiveQE { }
query :: NaiveQE -> Text -> Text
query qe queryText =
case parseDatalog queryText of
Right (Query texts literals) -> "#NYI"
Right otherStatement -> throw $ NonQueryException queryText otherStatement
Left ex -> throw $ CannotParseStatementException queryText ex

View File

@ -0,0 +1,16 @@
{-# HLINT ignore "Redundant flip" #-}
-- {-# LANGUAGE ImportQualifiedPost #-}
-- {-# LANGUAGE InstanceSigs #-}
{-# LANGUAGE ScopedTypeVariables #-}
{-# OPTIONS_GHC -Wno-unrecognised-pragmas #-}
{-# LANGUAGE BlockArguments #-}
module Datalog.QueryEngine where
import Datalog.DatalogDB
import Data.Text
class QueryEngine qe where
queryEngine :: (DatalogDB db) => db -> qe
query :: qe -> Text -> Text

View File

@ -1,6 +1,5 @@
{-# HLINT ignore "Redundant flip" #-}
{-# LANGUAGE ImportQualifiedPost #-}
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE ScopedTypeVariables #-}
{-# OPTIONS_GHC -Wno-unrecognised-pragmas #-}
@ -29,7 +28,7 @@ extractRule ruleText =
appendRule :: Relation -> RelationRule -> Relation
appendRule relation rule =
relation {
_rules = rule : (_rules relation)
_rules = rule : _rules relation
}
toRuleBodyElement :: BodyConstraint -> RuleBodyElement
@ -70,7 +69,7 @@ data (DatalogDB db) => RuleContext db = RuleContext
}
digestHead :: (DatalogDB db) => db -> Relation -> Literal -> RuleContext db
digestHead db relation (Literal neg relationName terms) =
digestHead db relation (Literal _ _ terms) =
RuleContext
{ __relation = relation
, _variableNames = variableNames
@ -80,17 +79,17 @@ digestHead db relation (Literal neg relationName terms) =
}
where
variableNames = nub $ extractVariableNames terms
entries' = nub $ (headTermToElement variableNames) <$> terms
entries' = nub $ headTermToElement variableNames <$> terms
extraConstants = Set.fromList $ mapMaybe extractConstant entries' where
extractConstant :: RuleElement -> Maybe Constant
extractConstant (RuleElementConstant constant) = Just constant
extractConstant _ = Nothing
digestBody :: (DatalogDB db) => Literal -> RuleContext db -> RuleContext db
digestBody (Literal neg subRelationName subTerms) context =
digestBody (Literal _ subRelationName subTerms) context =
context {
_variableNames = variableNames
, _bodyConstraints = newConstraint : constraints
, _db = insertRelation (addConstants db constants') subRelation
, _db = insertRelation (addConstants db (Set.fromList extraConstants)) subRelation
}
where
db = _db context
@ -101,7 +100,6 @@ digestBody (Literal neg subRelationName subTerms) context =
constantFromTerm :: Term -> Maybe Constant
constantFromTerm (Var _) = Nothing
constantFromTerm constant = Just constant
constants' = Set.fromList extraConstants
constraints = _bodyConstraints context
newConstraint = BodyConstraint subRelation subRuleElements where
subRuleElements = toRuleElement <$> subTerms

View File

@ -6,6 +6,7 @@ import qualified Test.SimpleParserSpec as SimpleParserSpec
import qualified Test.ArithmeticParserSpec as ArithmeticParserSpec
import qualified Test.Datalog.DatalogParserSpec as DatalogParserSpec
import qualified Test.Datalog.InMemoryDBSpec as InMemoryDBSpec
import qualified Test.Datalog.NaiveQESpec as NaiveQESpec
main :: IO ()
main = hspec $ do
@ -14,4 +15,5 @@ main = hspec $ do
describe "ArithmeticParser" ArithmeticParserSpec.spec
describe "DatalogParser" DatalogParserSpec.spec
describe "InMemoryDB" InMemoryDBSpec.spec
describe "NaiveQE" NaiveQESpec.spec

View File

@ -1,4 +1,3 @@
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE DuplicateRecordFields #-}
{-# LANGUAGE OverloadedRecordDot #-}
{-# HLINT ignore "Use const" #-}

View File

@ -1,4 +1,3 @@
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE DuplicateRecordFields #-}
{-# LANGUAGE OverloadedRecordDot #-}
{-# HLINT ignore "Use const" #-}

View File

@ -2,7 +2,6 @@
{-# LANGUAGE DuplicateRecordFields #-}
{-# LANGUAGE ImportQualifiedPost #-}
{-# LANGUAGE OverloadedRecordDot #-}
{-# LANGUAGE OverloadedStrings #-}
{-# HLINT ignore "Use const" #-}
{-# HLINT ignore "Unused LANGUAGE pragma" #-}
{-# HLINT ignore "Avoid lambda" #-}
@ -21,6 +20,7 @@ import Datalog.InMemoryDB
import Datalog.InMemoryDB qualified as InMemoryDB
import Test.Hspec
import Datalog.DatalogDB
import Data.Text
spec :: Spec
spec = do
@ -55,36 +55,20 @@ spec = do
, _arity = 2
, _tuples =
Set.fromList $
map (Sym <$>) [["alice", "bob"], ["bob", "carol"]]
Sym <<$>> [["alice", "bob"], ["bob", "carol"]]
, _rules = []
}
ancestorRule = RelationRule
{ headVariables = ["X", "Y", "Z"]
, bodyElements =
[ RuleBodyElement
{ _subRelationId = "parent"
, _ruleElements =
[ RuleElementVariable 0
, RuleElementVariable 2
]
}
, RuleBodyElement
{ _subRelationId = "ancestor"
, _ruleElements =
[ RuleElementVariable 2
, RuleElementVariable 1
]
}
[ ruleBody "parent" [0, 2]
, ruleBody "ancestor" [2, 1]
]
}
ancestorRule2 = RelationRule
{ headVariables = ["X", "Y"]
, bodyElements =
[ RuleBodyElement
{ _subRelationId = "parent"
, _ruleElements = [RuleElementVariable 0, RuleElementVariable 1]
}
]
[ ruleBody "parent" [0, 1] ]
}
ancestorRelation =
Relation
@ -149,30 +133,6 @@ spec = do
constants db
`shouldBe` Set.empty
it "can ingest facts and rules with duplicate head entries" do
let db =
InMemoryDB.withFactsAndRules
[]
["equivalent(Q,Q) :- ."]
rule1 =
RelationRule
{ headVariables = ["Q"]
, bodyElements = []
}
equivalentRelation =
Relation
{ _arity = 2
, _name = "equivalent"
, _tuples = Set.empty
, _rules = [rule1]
}
relations db
`shouldBe` Map.singleton "equivalent" equivalentRelation
constants db
`shouldBe` Set.empty
it "can ingest a theory of equivalence relations" do
let db =
InMemoryDB.withFactsAndRules
@ -190,33 +150,14 @@ spec = do
RelationRule
{ headVariables = ["R", "Q"]
, bodyElements =
[ RuleBodyElement
{ _subRelationId = "equivalent"
, _ruleElements =
[ RuleElementVariable 1
, RuleElementVariable 0
]
}
]
[ ruleBody "equivalent" [1, 0] ]
}
rule3 =
RelationRule
{ headVariables = ["Q", "S", "R"]
, bodyElements =
[ RuleBodyElement
{ _subRelationId = "equivalent"
, _ruleElements =
[ RuleElementVariable 0
, RuleElementVariable 2
]
}
, RuleBodyElement
{ _subRelationId = "equivalent"
, _ruleElements =
[ RuleElementVariable 2
, RuleElementVariable 1
]
}
[ ruleBody "equivalent" [0, 2]
, ruleBody "equivalent" [2, 1]
]
}
equivalentRelation =
@ -249,3 +190,11 @@ spec = do
-- (<<<$>>>) :: (Functor f1, Functor f2, Functor f3) => f1 (a -> b) -> f1 (f2 (f3 a) -> f2 (f3 b))
(<<<$>>>) :: (Functor f1, Functor f2, Functor f3) => (a -> b) -> f1 (f2 (f3 a)) -> f1 (f2 (f3 b))
(<<<$>>>) = fmap fmap fmap fmap fmap fmap fmap fmap
ruleBody :: Text -> [Int] -> RuleBodyElement
ruleBody subRelationId indices =
RuleBodyElement
{ _subRelationId = subRelationId
, _ruleElements =
RuleElementVariable <$> indices
}

View File

@ -0,0 +1,22 @@
{-# LANGUAGE BlockArguments #-}
{-# LANGUAGE DuplicateRecordFields #-}
{-# LANGUAGE ImportQualifiedPost #-}
{-# LANGUAGE OverloadedRecordDot #-}
{-# HLINT ignore "Use const" #-}
{-# HLINT ignore "Unused LANGUAGE pragma" #-}
{-# HLINT ignore "Avoid lambda" #-}
{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE TypeApplications #-}
{-# LANGUAGE NoFieldSelectors #-}
{-# LANGUAGE NoMonomorphismRestriction #-}
{-# OPTIONS_GHC -Wno-unrecognised-pragmas #-}
module Test.Datalog.NaiveQESpec where
import Test.Hspec
spec :: Spec
spec = do
describe "NaiveQESpec" do
it "..." $ do
1 `shouldBe` 1