Initial commit with basic datalog parser and abstract syntax
This commit is contained in:
commit
9dd48b46af
6
.gitignore
vendored
Normal file
6
.gitignore
vendored
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
tags
|
||||||
|
dist/
|
||||||
|
dist-newstyle/
|
||||||
|
result
|
||||||
|
result-*
|
||||||
|
/.direnv/
|
||||||
5
CHANGELOG.md
Normal file
5
CHANGELOG.md
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
# Revision history for toy-datalog
|
||||||
|
|
||||||
|
## 0.1.0.0 -- YYYY-mm-dd
|
||||||
|
|
||||||
|
* First version. Released on an unsuspecting world.
|
||||||
26
LICENSE
Normal file
26
LICENSE
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
Copyright (c) 2026, Cale Gibbard
|
||||||
|
All rights reserved.
|
||||||
|
|
||||||
|
Redistribution and use in source and binary forms, with or without
|
||||||
|
modification, are permitted provided that the following conditions are
|
||||||
|
met:
|
||||||
|
|
||||||
|
1. Redistributions of source code must retain the above copyright
|
||||||
|
notice, this list of conditions and the following disclaimer.
|
||||||
|
|
||||||
|
2. Redistributions in binary form must reproduce the above copyright
|
||||||
|
notice, this list of conditions and the following disclaimer in the
|
||||||
|
documentation and/or other materials provided with the
|
||||||
|
distribution.
|
||||||
|
|
||||||
|
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||||
|
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||||
|
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||||
|
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||||
|
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||||
|
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||||
|
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||||
|
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||||
|
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||||
|
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||||
|
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
8
app/Main.hs
Normal file
8
app/Main.hs
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
module Main where
|
||||||
|
|
||||||
|
import qualified MyLib (someFunc)
|
||||||
|
|
||||||
|
main :: IO ()
|
||||||
|
main = do
|
||||||
|
putStrLn "Hello, Haskell!"
|
||||||
|
MyLib.someFunc
|
||||||
8
src/Datalog.hs
Normal file
8
src/Datalog.hs
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
module Datalog
|
||||||
|
( module Datalog.Syntax
|
||||||
|
, module Datalog.Parser
|
||||||
|
)
|
||||||
|
where
|
||||||
|
|
||||||
|
import Datalog.Syntax
|
||||||
|
import Datalog.Parser
|
||||||
86
src/Datalog/Parser.hs
Normal file
86
src/Datalog/Parser.hs
Normal file
@ -0,0 +1,86 @@
|
|||||||
|
module Datalog.Parser
|
||||||
|
( parseTerm
|
||||||
|
, parseAtom
|
||||||
|
, parseRule
|
||||||
|
, parseProgram
|
||||||
|
)
|
||||||
|
where
|
||||||
|
|
||||||
|
import Datalog.Syntax
|
||||||
|
import Data.Text (Text)
|
||||||
|
import Data.Text qualified as T
|
||||||
|
import Data.Void (Void)
|
||||||
|
import Text.Megaparsec
|
||||||
|
import Text.Megaparsec.Char
|
||||||
|
import Text.Megaparsec.Char.Lexer qualified as L
|
||||||
|
|
||||||
|
type Parser = Parsec Void Text
|
||||||
|
|
||||||
|
lexeme :: (MonadParsec e Text m) => m a -> m a
|
||||||
|
lexeme = L.lexeme whitespace
|
||||||
|
|
||||||
|
symbol :: (MonadParsec e Text m) => Text -> m Text
|
||||||
|
symbol = L.symbol whitespace
|
||||||
|
|
||||||
|
whitespace :: (MonadParsec e Text m) => m ()
|
||||||
|
whitespace = L.space
|
||||||
|
space1
|
||||||
|
(L.skipLineComment "--")
|
||||||
|
(L.skipBlockComment "{-" "-}")
|
||||||
|
|
||||||
|
parens :: (MonadParsec e Text m) => m a -> m a
|
||||||
|
parens = between (symbol "(") (symbol ")")
|
||||||
|
|
||||||
|
comma, period :: (MonadParsec e Text m) => m ()
|
||||||
|
comma = () <$ symbol ","
|
||||||
|
period = () <$ symbol "."
|
||||||
|
|
||||||
|
{-
|
||||||
|
data Term = Con Ident | Var Ident
|
||||||
|
deriving (Eq, Ord, Show)
|
||||||
|
-}
|
||||||
|
|
||||||
|
parseCon :: (MonadParsec e Text m) => m Term
|
||||||
|
parseCon = Con . T.pack <$> lexeme (liftA2 (:) lowerChar (many alphaNumChar))
|
||||||
|
|
||||||
|
parseVar :: (MonadParsec e Text m) => m Term
|
||||||
|
parseVar = Var . T.pack <$> lexeme (liftA2 (:) upperChar (many alphaNumChar))
|
||||||
|
|
||||||
|
parseTerm :: Parser Term
|
||||||
|
parseTerm = parseCon <|> parseVar
|
||||||
|
|
||||||
|
{-
|
||||||
|
data Atom = Atom Ident [Term]
|
||||||
|
deriving (Eq, Ord, Show)
|
||||||
|
-}
|
||||||
|
|
||||||
|
parseAtom :: Parser Atom
|
||||||
|
parseAtom = do
|
||||||
|
rel <- T.pack <$> lexeme (liftA2 (:) lowerChar (many alphaNumChar))
|
||||||
|
args <- parens (parseTerm `sepBy` comma)
|
||||||
|
return (Atom rel args)
|
||||||
|
|
||||||
|
{-
|
||||||
|
data Rule = Atom :- [Atom]
|
||||||
|
deriving (Eq, Ord, Show)
|
||||||
|
-}
|
||||||
|
|
||||||
|
parseFact :: Parser Rule
|
||||||
|
parseFact = do
|
||||||
|
headAtom <- parseAtom
|
||||||
|
period
|
||||||
|
return (headAtom :- [])
|
||||||
|
|
||||||
|
parseRule :: Parser Rule
|
||||||
|
parseRule = try parseFact <|> do
|
||||||
|
headAtom <- parseAtom <* symbol ":-"
|
||||||
|
bodyAtoms <- parseAtom `sepBy` comma
|
||||||
|
period
|
||||||
|
return (headAtom :- bodyAtoms)
|
||||||
|
|
||||||
|
{-
|
||||||
|
data Program = Program [Rule]
|
||||||
|
-}
|
||||||
|
|
||||||
|
parseProgram :: Parser Program
|
||||||
|
parseProgram = Program <$> many parseRule
|
||||||
17
src/Datalog/Syntax.hs
Normal file
17
src/Datalog/Syntax.hs
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
module Datalog.Syntax where
|
||||||
|
|
||||||
|
import Data.Text (Text)
|
||||||
|
|
||||||
|
type Ident = Text
|
||||||
|
|
||||||
|
data Term = Con Ident | Var Ident
|
||||||
|
deriving (Eq, Ord, Show)
|
||||||
|
|
||||||
|
data Atom = Atom Ident [Term]
|
||||||
|
deriving (Eq, Ord, Show)
|
||||||
|
|
||||||
|
data Rule = Atom :- [Atom]
|
||||||
|
deriving (Eq, Ord, Show)
|
||||||
|
|
||||||
|
data Program = Program [Rule]
|
||||||
|
deriving (Eq, Ord, Show)
|
||||||
27
test/Main.hs
Normal file
27
test/Main.hs
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
module Main (main) where
|
||||||
|
|
||||||
|
import Datalog
|
||||||
|
import Data.Text qualified as T
|
||||||
|
import Data.Text.IO qualified as T
|
||||||
|
import Text.Megaparsec qualified as M
|
||||||
|
|
||||||
|
parserGoldenTest :: String -> IO Bool
|
||||||
|
parserGoldenTest prefix = do
|
||||||
|
let inFile = "test/golden/" <> prefix <> ".dl"
|
||||||
|
outFile = "test/golden/" <> prefix <> ".show"
|
||||||
|
inp <- T.readFile inFile
|
||||||
|
out <- T.readFile outFile
|
||||||
|
result <- case M.parse parseProgram inFile inp of
|
||||||
|
Left e -> do
|
||||||
|
print e
|
||||||
|
return False
|
||||||
|
Right out' -> do
|
||||||
|
return (T.strip out == T.pack (show out'))
|
||||||
|
let resultS = if result then "passed" else "failed"
|
||||||
|
putStrLn $ unwords ["Parser test", prefix, resultS]
|
||||||
|
return result
|
||||||
|
|
||||||
|
main :: IO ()
|
||||||
|
main = do
|
||||||
|
True <- parserGoldenTest "ancestor"
|
||||||
|
return ()
|
||||||
5
test/golden/ancestor.dl
Normal file
5
test/golden/ancestor.dl
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
parent(xerces, brooke).
|
||||||
|
parent(brooke, damocles).
|
||||||
|
|
||||||
|
ancestor(X, Y) :- parent(X, Y).
|
||||||
|
ancestor(X, Y) :- parent(X, Z), ancestor(Z, Y).
|
||||||
1
test/golden/ancestor.show
Normal file
1
test/golden/ancestor.show
Normal file
@ -0,0 +1 @@
|
|||||||
|
Program [Atom "parent" [Con "xerces",Con "brooke"] :- [],Atom "parent" [Con "brooke",Con "damocles"] :- [],Atom "ancestor" [Var "X",Var "Y"] :- [Atom "parent" [Var "X",Var "Y"]],Atom "ancestor" [Var "X",Var "Y"] :- [Atom "parent" [Var "X",Var "Z"],Atom "ancestor" [Var "Z",Var "Y"]]]
|
||||||
54
toy-datalog.cabal
Normal file
54
toy-datalog.cabal
Normal file
@ -0,0 +1,54 @@
|
|||||||
|
cabal-version: 3.0
|
||||||
|
name: toy-datalog
|
||||||
|
version: 0.1.0.0
|
||||||
|
-- synopsis:
|
||||||
|
-- description:
|
||||||
|
license: BSD-2-Clause
|
||||||
|
license-file: LICENSE
|
||||||
|
author: Cale Gibbard
|
||||||
|
maintainer: cale.gibbard@obsidian.systems
|
||||||
|
category: Database
|
||||||
|
build-type: Simple
|
||||||
|
extra-doc-files: CHANGELOG.md
|
||||||
|
|
||||||
|
common common
|
||||||
|
ghc-options: -Wall
|
||||||
|
default-extensions:
|
||||||
|
ImportQualifiedPost,
|
||||||
|
OverloadedStrings,
|
||||||
|
FlexibleContexts
|
||||||
|
|
||||||
|
library
|
||||||
|
import: common
|
||||||
|
exposed-modules:
|
||||||
|
Datalog
|
||||||
|
Datalog.Parser
|
||||||
|
Datalog.Syntax
|
||||||
|
build-depends:
|
||||||
|
base,
|
||||||
|
text,
|
||||||
|
megaparsec
|
||||||
|
hs-source-dirs: src
|
||||||
|
default-language: Haskell2010
|
||||||
|
|
||||||
|
executable toy-datalog
|
||||||
|
import: common
|
||||||
|
main-is: Main.hs
|
||||||
|
build-depends:
|
||||||
|
base,
|
||||||
|
toy-datalog
|
||||||
|
hs-source-dirs: app
|
||||||
|
default-language: Haskell2010
|
||||||
|
|
||||||
|
test-suite toy-datalog-test
|
||||||
|
import: common
|
||||||
|
default-language: Haskell2010
|
||||||
|
type: exitcode-stdio-1.0
|
||||||
|
hs-source-dirs: test
|
||||||
|
main-is: Main.hs
|
||||||
|
|
||||||
|
build-depends:
|
||||||
|
base,
|
||||||
|
toy-datalog,
|
||||||
|
megaparsec,
|
||||||
|
text
|
||||||
Loading…
x
Reference in New Issue
Block a user