initial DigestedQuery code with tests

This commit is contained in:
Felix Dilke 2026-02-11 17:11:19 +00:00
parent a9ff3b4a5f
commit 42c992428a
7 changed files with 106 additions and 15 deletions

View File

@ -81,6 +81,7 @@ test-suite haskell-exps-test
Test.Datalog.DatalogParserSpec Test.Datalog.DatalogParserSpec
Test.Datalog.InMemoryDBSpec Test.Datalog.InMemoryDBSpec
Test.Datalog.NaiveQESpec Test.Datalog.NaiveQESpec
Test.Datalog.DigestedQuerySpec
Test.Utility.UtilitySpec Test.Utility.UtilitySpec
library langfeatures library langfeatures
@ -96,6 +97,7 @@ library langfeatures
Datalog.DatalogDB Datalog.DatalogDB
Datalog.NaiveQE Datalog.NaiveQE
Datalog.QueryEngine Datalog.QueryEngine
Datalog.DigestedQuery
Utility.Utility Utility.Utility
executable haskell-experiments executable haskell-experiments

View File

@ -0,0 +1,67 @@
module Datalog.DigestedQuery where
import Data.Void
import Data.Text (Text)
import qualified Data.Text as T
import Text.Megaparsec
import Text.Megaparsec.Char
import qualified Text.Megaparsec.Char.Lexer as L
import Control.Monad (void)
import Data.Functor (($>))
import Datalog.DatalogDB (Relation, Constant, DatalogDBException (..))
import Datalog.DatalogParser
import Control.Exception (throw)
import GHC.TypeError (ErrorMessage(Text))
import Data.List (nub, elemIndex)
data DigestedQuery = DigestedQuery {
allBoundVariables :: [Text],
numSoughtVariables :: Int,
conditions :: [DigestedQueryCondition]
} deriving (Show, Eq, Ord)
data DigestedQueryCondition = DigestedQueryCondition {
__relation :: Text,
_entries :: [DigestedQueryEntry]
} deriving (Show, Eq, Ord)
data DigestedQueryEntry =
DigestedQueryEntryConstant Constant |
DigestedQueryEntryVariable Int
deriving (Show, Eq, Ord)
digestQuery :: Text -> DigestedQuery
digestQuery queryText =
case parseDatalog queryText of
Right (Query variables literals) -> digestQuerySub variables literals
Right statement0 -> throw $ NonQueryException "cannot digest non-query" statement0
Left ex -> throw ex
digestQuerySub :: [Text] -> [Literal] -> DigestedQuery
digestQuerySub variables literals =
DigestedQuery {
allBoundVariables = allBoundVariables,
numSoughtVariables =
if (null variables) then (length allBoundVariables) else (length variables),
conditions = extractCondition <$> literals
} where
allBoundVariables = nub $ variables ++ extractedVariables
extractedVariables :: [Text] =
nub $ concatMap extractVariablesSub literals
extractVariablesSub :: Literal -> [Text]
extractVariablesSub literal =
concatMap extractVariablesSubSub (arguments literal)
extractVariablesSubSub :: Term -> [Text]
extractVariablesSubSub (Var name) = [name]
extractVariablesSubSub _ = []
extractCondition :: Literal -> DigestedQueryCondition
extractCondition literal = DigestedQueryCondition {
__relation = predName literal,
_entries = extractEntry <$> (arguments literal)
}
extractEntry :: Term -> DigestedQueryEntry
extractEntry (Var varName) = case elemIndex varName allBoundVariables of
Just index -> DigestedQueryEntryVariable index
Nothing -> throw $ VariableLookupException varName allBoundVariables
extractEntry constant = DigestedQueryEntryConstant constant

View File

@ -65,9 +65,3 @@ withFacts =
withFactsAndRules :: [Text] -> [Text] -> InMemoryDB withFactsAndRules :: [Text] -> [Text] -> InMemoryDB
withFactsAndRules facts = foldr (addRule . extractRule) (withFacts facts) withFactsAndRules facts = foldr (addRule . extractRule) (withFacts facts)
-- query :: forall db . (DatalogDB db) => db -> 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

View File

@ -4,17 +4,19 @@ import Test.Hspec
import qualified Test.OlogsSpec as Ologs import qualified Test.OlogsSpec as Ologs
import qualified Test.SimpleParserSpec as SimpleParserSpec import qualified Test.SimpleParserSpec as SimpleParserSpec
import qualified Test.ArithmeticParserSpec as ArithmeticParserSpec import qualified Test.ArithmeticParserSpec as ArithmeticParserSpec
import qualified Test.Utility.UtilitySpec as UtilitySpec
import qualified Test.Datalog.DatalogParserSpec as DatalogParserSpec import qualified Test.Datalog.DatalogParserSpec as DatalogParserSpec
import qualified Test.Datalog.InMemoryDBSpec as InMemoryDBSpec import qualified Test.Datalog.InMemoryDBSpec as InMemoryDBSpec
import qualified Test.Datalog.NaiveQESpec as NaiveQESpec import qualified Test.Datalog.NaiveQESpec as NaiveQESpec
import qualified Test.Utility.UtilitySpec as UtilitySpec import qualified Test.Datalog.DigestedQuerySpec as DigestedQuerySpec
main :: IO () main :: IO ()
main = hspec $ do main = hspec $ do
describe "Utility" UtilitySpec.spec
describe "Ologs" Ologs.spec describe "Ologs" Ologs.spec
describe "SimpleParser" SimpleParserSpec.spec describe "SimpleParser" SimpleParserSpec.spec
describe "ArithmeticParser" ArithmeticParserSpec.spec describe "ArithmeticParser" ArithmeticParserSpec.spec
describe "DatalogParser" DatalogParserSpec.spec describe "DatalogParser" DatalogParserSpec.spec
describe "InMemoryDB" InMemoryDBSpec.spec describe "InMemoryDB" InMemoryDBSpec.spec
describe "NaiveQE" NaiveQESpec.spec describe "NaiveQE" NaiveQESpec.spec
describe "Utility" UtilitySpec.spec describe "DigestedQuery" DigestedQuerySpec.spec

View File

@ -0,0 +1,33 @@
{-# LANGUAGE DuplicateRecordFields #-}
{-# LANGUAGE OverloadedRecordDot #-}
{-# HLINT ignore "Use const" #-}
{-# HLINT ignore "Unused LANGUAGE pragma" #-}
{-# HLINT ignore "Avoid lambda" #-}
{-# LANGUAGE ScopedTypeVariables #-}
{-# OPTIONS_GHC -Wno-unrecognised-pragmas #-}
{-# LANGUAGE TypeApplications #-}
module Test.Datalog.DigestedQuerySpec where
import Test.Hspec
import Datalog.DatalogParser
import Datalog.DigestedQuery (DigestedQuery(..), DigestedQueryCondition(..), DigestedQueryEntry(..))
import Datalog.DigestedQuery (digestQuery)
spec :: Spec
spec = do
describe "DigestedQuery" $ do
it "can digest basic queries" $ do
digestQuery "?- parent(alice,X)." `shouldBe` DigestedQuery {
allBoundVariables = ["X"],
numSoughtVariables = 1,
conditions = [
DigestedQueryCondition {
__relation = "parent",
_entries = [
DigestedQueryEntryConstant $ Sym "alice",
DigestedQueryEntryVariable 0
]
}
]
}

View File

@ -181,10 +181,6 @@ spec = do
(<<$>>) :: (Functor f1, Functor f2) => (a -> b) -> f1 (f2 a) -> f1 (f2 b) (<<$>>) :: (Functor f1, Functor f2) => (a -> b) -> f1 (f2 a) -> f1 (f2 b)
(<<$>>) = fmap fmap fmap (<<$>>) = fmap fmap fmap
-- (<<<$>>>) :: (Functor f1, Functor f2, Functor f3) => f1 (f2 (a -> b)) -> f1 (f2 (f3 a -> f3 b))
-- (<<<$>>>) :: Functor f => (a1 -> b) -> (a2 -> a1) -> f a2 -> f b
-- (<<<$>>>) :: (Functor f1, Functor f2) => (a1 -> a2 -> b) -> f1 a1 -> f1 (f2 a2 -> f2 b)
-- (<<<$>>>) :: (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)) (<<<$>>>) :: (Functor f1, Functor f2, Functor f3) => (a -> b) -> f1 (f2 (f3 a)) -> f1 (f2 (f3 b))
(<<<$>>>) = fmap fmap fmap fmap fmap fmap fmap fmap (<<<$>>>) = fmap fmap fmap fmap fmap fmap fmap fmap

View File

@ -25,9 +25,6 @@ import Datalog.NaiveQE
spec :: Spec spec :: Spec
spec = do spec = do
describe "NaiveQESpec" do describe "NaiveQESpec" do
it "..." $ do
1 `shouldBe` 1
it "can do basic queries" do it "can do basic queries" do
let db :: InMemoryDB = let db :: InMemoryDB =
InMemoryDB.withFacts InMemoryDB.withFacts