2026-01-21 11:24:30 +00:00
|
|
|
{-# LANGUAGE OverloadedStrings #-}
|
|
|
|
|
{-# LANGUAGE TypeApplications #-}
|
|
|
|
|
{-# OPTIONS_GHC -Wno-unrecognised-pragmas #-}
|
|
|
|
|
|
|
|
|
|
{-# HLINT ignore "Redundant flip" #-}
|
2026-01-21 17:11:13 +00:00
|
|
|
{-# LANGUAGE ImportQualifiedPost #-}
|
2026-01-21 11:24:30 +00:00
|
|
|
module Datalog.NaiveDatabase where
|
|
|
|
|
|
|
|
|
|
import Data.Map (Map)
|
|
|
|
|
import Data.Set (Set)
|
2026-01-21 17:11:13 +00:00
|
|
|
import Data.Set qualified as Set
|
|
|
|
|
import Data.Text (Text)
|
|
|
|
|
import qualified Data.Text as T
|
2026-01-22 18:00:52 +00:00
|
|
|
import Datalog.DatalogParser(parseDatalog, Literal(Literal), Statement(..), Term, Head (HeadSingle))
|
2026-01-21 17:11:13 +00:00
|
|
|
import qualified Data.Map as Map
|
|
|
|
|
import Text.Megaparsec (ParseErrorBundle)
|
|
|
|
|
import Data.Void
|
|
|
|
|
import Control.Exception (Exception)
|
|
|
|
|
import Control.Exception.Base
|
2026-01-21 11:24:30 +00:00
|
|
|
|
|
|
|
|
data Value =
|
|
|
|
|
ValueInt Int |
|
|
|
|
|
ValueSymbol String
|
|
|
|
|
|
|
|
|
|
data NaiveDatabase = NaiveDatabase {
|
2026-01-21 18:01:06 +00:00
|
|
|
relations :: Map RelationId Relation,
|
2026-01-22 10:57:11 +00:00
|
|
|
constants :: Set Constant
|
2026-01-21 17:11:13 +00:00
|
|
|
}
|
|
|
|
|
|
2026-01-22 18:00:52 +00:00
|
|
|
data BodyEntry = -- entry occurring in a head or body relation - constant or variable
|
|
|
|
|
BodyEntryConstant Constant |
|
|
|
|
|
BodyEntryVariable Text
|
|
|
|
|
deriving (Show, Eq)
|
|
|
|
|
|
|
|
|
|
data RelationRule = RelationRule {
|
|
|
|
|
headVariables :: [BodyEntry],
|
|
|
|
|
body :: [(Relation, [BodyEntry])]
|
|
|
|
|
} deriving (Show, Eq)
|
|
|
|
|
|
2026-01-21 18:01:06 +00:00
|
|
|
data Relation = Relation {
|
|
|
|
|
arity :: Int,
|
2026-01-22 18:00:52 +00:00
|
|
|
tuples :: Set [Constant],
|
|
|
|
|
rules :: [RelationRule]
|
2026-01-22 14:25:09 +00:00
|
|
|
} deriving (Show, Eq)
|
2026-01-21 18:01:06 +00:00
|
|
|
|
2026-01-22 10:57:11 +00:00
|
|
|
-- newtype RelationId = RelationId Text
|
|
|
|
|
-- deriving (Eq, Ord, Show)
|
2026-01-21 18:01:06 +00:00
|
|
|
|
2026-01-22 10:57:11 +00:00
|
|
|
-- Our constants will be the terms of the Datalog grammar - ints/variables/symbols
|
|
|
|
|
type Constant = Term
|
|
|
|
|
type RelationId = Text
|
|
|
|
|
|
|
|
|
|
-- newtype Constant = Constant Text
|
|
|
|
|
-- deriving (Eq, Ord, Show)
|
2026-01-21 18:01:06 +00:00
|
|
|
|
2026-01-21 17:11:13 +00:00
|
|
|
emptyDB :: NaiveDatabase
|
|
|
|
|
emptyDB = NaiveDatabase {
|
|
|
|
|
relations = Map.empty,
|
2026-01-22 10:57:11 +00:00
|
|
|
constants = Set.empty -- the Herbrand universe
|
2026-01-21 17:11:13 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
withFacts :: [Text] -> NaiveDatabase
|
|
|
|
|
withFacts facts =
|
|
|
|
|
foldl addFact emptyDB (extractFact <$> facts) where
|
|
|
|
|
extractFact:: Text -> Literal
|
|
|
|
|
extractFact factText =
|
|
|
|
|
case (parseDatalog factText) of
|
|
|
|
|
Right (Fact fact) -> fact
|
|
|
|
|
Right otherStatement -> throw $ NonFactException factText otherStatement
|
|
|
|
|
Left ex -> throw $ CannotParseStatementException factText ex
|
|
|
|
|
addFact :: NaiveDatabase -> Literal -> NaiveDatabase
|
2026-01-22 10:57:11 +00:00
|
|
|
addFact (NaiveDatabase relations constants) (Literal neg relationName terms) =
|
|
|
|
|
NaiveDatabase newRelations newConstants where
|
2026-01-22 14:25:09 +00:00
|
|
|
newArity = length terms
|
|
|
|
|
newRelation =
|
2026-01-22 10:57:11 +00:00
|
|
|
case Map.lookup relationName relations of
|
2026-01-22 18:00:52 +00:00
|
|
|
Nothing -> Relation (length terms) (Set.singleton terms) []
|
2026-01-22 14:25:09 +00:00
|
|
|
Just relation ->
|
|
|
|
|
if (arity relation == newArity)
|
2026-01-22 18:00:52 +00:00
|
|
|
then Relation (length terms) (Set.singleton terms) []
|
2026-01-22 14:25:09 +00:00
|
|
|
else throw $ BadArityException relationName newArity
|
|
|
|
|
newRelations = Map.insert relationName newRelation relations
|
2026-01-22 10:57:11 +00:00
|
|
|
newConstants = Set.union constants $ Set.fromList terms
|
2026-01-21 17:11:13 +00:00
|
|
|
|
2026-01-22 17:33:49 +00:00
|
|
|
withFactsAndRules :: [Text] -> [Text] -> NaiveDatabase
|
|
|
|
|
withFactsAndRules facts rules =
|
|
|
|
|
foldl addRule (withFacts facts) (extractRule <$> rules) where
|
|
|
|
|
extractRule:: Text -> (Literal, [Literal])
|
|
|
|
|
extractRule ruleText =
|
|
|
|
|
case (parseDatalog ruleText) of
|
2026-01-22 18:00:52 +00:00
|
|
|
Right (Rule (HeadSingle lhs) rhs) -> (lhs, rhs)
|
2026-01-22 17:33:49 +00:00
|
|
|
Right otherStatement -> throw $ NonRuleException ruleText otherStatement
|
|
|
|
|
Left ex -> throw $ CannotParseStatementException ruleText ex
|
2026-01-22 18:00:52 +00:00
|
|
|
addRule :: NaiveDatabase -> (Literal, [Literal]) -> NaiveDatabase
|
|
|
|
|
addRule (NaiveDatabase relations constants) ((Literal neg relationName terms), body) =
|
2026-01-22 17:33:49 +00:00
|
|
|
NaiveDatabase newRelations newConstants where
|
|
|
|
|
newRelations = relations
|
|
|
|
|
newConstants = constants
|
|
|
|
|
|
2026-01-21 17:11:13 +00:00
|
|
|
query :: NaiveDatabase -> Text -> Text
|
|
|
|
|
query db qText =
|
|
|
|
|
case (parseDatalog qText) of
|
|
|
|
|
Right (Query texts literals) -> "#NYI"
|
|
|
|
|
Right otherStatement -> throw $ NonQueryException qText otherStatement
|
|
|
|
|
Left ex -> throw $ CannotParseStatementException qText ex
|
|
|
|
|
|
|
|
|
|
data NaiveDatabaseException
|
|
|
|
|
= CannotParseStatementException Text (ParseErrorBundle Text Void) |
|
|
|
|
|
NonFactException Text Statement |
|
2026-01-22 17:33:49 +00:00
|
|
|
NonRuleException Text Statement |
|
2026-01-22 14:25:09 +00:00
|
|
|
NonQueryException Text Statement |
|
|
|
|
|
BadArityException Text Int
|
2026-01-21 17:11:13 +00:00
|
|
|
deriving (Show)
|
|
|
|
|
|
|
|
|
|
instance Exception NaiveDatabaseException
|