2026-01-21 11:24:30 +00:00
|
|
|
{-# LANGUAGE OverloadedStrings #-}
|
|
|
|
|
{-# LANGUAGE DuplicateRecordFields #-}
|
|
|
|
|
{-# LANGUAGE OverloadedRecordDot #-}
|
|
|
|
|
{-# HLINT ignore "Use const" #-}
|
|
|
|
|
{-# HLINT ignore "Unused LANGUAGE pragma" #-}
|
|
|
|
|
{-# HLINT ignore "Avoid lambda" #-}
|
|
|
|
|
{-# LANGUAGE ScopedTypeVariables #-}
|
|
|
|
|
{-# LANGUAGE NoFieldSelectors #-}
|
|
|
|
|
{-# OPTIONS_GHC -Wno-unrecognised-pragmas #-}
|
|
|
|
|
{-# LANGUAGE TypeApplications #-}
|
2026-01-22 14:25:09 +00:00
|
|
|
{-# LANGUAGE ImportQualifiedPost #-}
|
2026-01-21 11:24:30 +00:00
|
|
|
|
|
|
|
|
module Test.Datalog.NaiveDatabaseSpec where
|
|
|
|
|
|
|
|
|
|
import Test.Hspec
|
|
|
|
|
import Datalog.NaiveDatabase
|
2026-01-21 17:11:13 +00:00
|
|
|
import qualified Datalog.NaiveDatabase as NaiveDatabase
|
2026-01-22 14:25:09 +00:00
|
|
|
import Data.Set qualified as Set
|
|
|
|
|
import Datalog.DatalogParser
|
|
|
|
|
import qualified Data.Map as Map
|
2026-01-21 11:24:30 +00:00
|
|
|
|
|
|
|
|
spec :: Spec
|
|
|
|
|
spec = do
|
|
|
|
|
describe "dummy test" $ do
|
|
|
|
|
it "..." $ do
|
|
|
|
|
1 `shouldBe` (1 :: Int)
|
2026-01-22 14:25:09 +00:00
|
|
|
it "can ingest facts into relations & a universe" $ do
|
|
|
|
|
let db = NaiveDatabase.withFacts
|
|
|
|
|
[ "parent(\"alice\", \"bob\")."
|
|
|
|
|
, "parent(\"bob\", \"carol\")." ]
|
|
|
|
|
constants db `shouldBe`
|
|
|
|
|
(Set.fromList $ Sym <$> ["alice", "bob", "carol"])
|
|
|
|
|
relations db `shouldBe`
|
|
|
|
|
Map.fromList [
|
|
|
|
|
("parent",
|
2026-01-26 12:20:19 +00:00
|
|
|
Relation "parent" 2 (Set.fromList ([Sym <$> ["bob", "carol"]])) [] )
|
2026-01-22 14:25:09 +00:00
|
|
|
]
|
2026-01-22 17:33:49 +00:00
|
|
|
it "can ingest facts and rules" $ do
|
|
|
|
|
let db = NaiveDatabase.withFactsAndRules
|
|
|
|
|
[ "parent(\"alice\", \"bob\")."
|
|
|
|
|
, "parent(\"bob\", \"carol\")." ]
|
|
|
|
|
[ "ancestor(X,Y) :- parent(X,Y)."
|
|
|
|
|
, "ancestor(X,Y) :- parent(X,Z), ancestor(Z,Y)." ]
|
2026-01-26 12:25:44 +00:00
|
|
|
let parentRelation = Relation {
|
|
|
|
|
_name = "parent",
|
|
|
|
|
_arity = 2,
|
|
|
|
|
_tuples = Set.fromList [
|
|
|
|
|
[Var "X",Var "Z"]
|
|
|
|
|
],
|
|
|
|
|
_rules = []
|
|
|
|
|
}
|
|
|
|
|
let ancestorRelation = Relation {
|
|
|
|
|
_name = "ancestor",
|
|
|
|
|
_arity = 2,
|
|
|
|
|
_tuples = Set.fromList [
|
|
|
|
|
[Var "Z",Var "Y"]
|
|
|
|
|
],
|
|
|
|
|
_rules = []
|
|
|
|
|
}
|
2026-01-22 17:33:49 +00:00
|
|
|
constants db `shouldBe`
|
|
|
|
|
(Set.fromList $ Sym <$> ["alice", "bob", "carol"])
|
|
|
|
|
relations db `shouldBe`
|
|
|
|
|
Map.fromList [
|
2026-01-23 18:12:47 +00:00
|
|
|
("ancestor",
|
|
|
|
|
Relation {
|
|
|
|
|
_arity = 2,
|
2026-01-26 12:20:19 +00:00
|
|
|
_name = "ancestor",
|
2026-01-23 18:12:47 +00:00
|
|
|
_tuples = Set.fromList [
|
|
|
|
|
[Var "X", Var "Y"]
|
|
|
|
|
],
|
|
|
|
|
_rules = [
|
|
|
|
|
RelationRule {
|
2026-01-26 12:07:41 +00:00
|
|
|
headVariables = [ "X", "Y", "Z" ],
|
2026-01-23 18:12:47 +00:00
|
|
|
body = [
|
|
|
|
|
(
|
2026-01-26 12:25:44 +00:00
|
|
|
parentRelation, [
|
2026-01-23 18:12:47 +00:00
|
|
|
RuleElementVariable "X",
|
|
|
|
|
RuleElementVariable "Z"
|
|
|
|
|
]
|
|
|
|
|
),(
|
2026-01-26 12:25:44 +00:00
|
|
|
ancestorRelation,[
|
2026-01-23 18:12:47 +00:00
|
|
|
RuleElementVariable "Z",
|
|
|
|
|
RuleElementVariable "Y"
|
|
|
|
|
]
|
|
|
|
|
)
|
|
|
|
|
]
|
|
|
|
|
}
|
|
|
|
|
]
|
|
|
|
|
}
|
|
|
|
|
),
|
2026-01-26 12:25:44 +00:00
|
|
|
("parent", parentRelation )
|
2026-01-23 18:12:47 +00:00
|
|
|
]
|
2026-01-22 14:25:09 +00:00
|
|
|
|
|
|
|
|
it "can do basic queries" $ do
|
2026-01-21 18:01:06 +00:00
|
|
|
let db = NaiveDatabase.withFacts
|
|
|
|
|
[ "parent(\"alice\", \"bob\")."
|
|
|
|
|
, "parent(\"bob\", \"carol\")." ]
|
2026-01-22 14:25:09 +00:00
|
|
|
query db "?- parent(alice,X)." `shouldBe` "#NYI" -- ideally, 'bob'
|