on master help sign in
refactor Tutorial D interpreter to make use of client library

-this should cut down on the common code between D interpreters -the interpreter used as a library consumer is an important test of the API coverage of the client library

commitda528b7b42a6cc805c018c9faa7392890db58aef authorAgentM <agentm@themactionfaction.com> dateMon 20 Jul 2015, 20:11 (11 years ago) parent870aada
8 files changed+140−170
ProjectM36/Base.hs+2−1 ProjectM36/RelationalExpression.hs+2−1 TutorialD/Interpreter.hs+49−47 TutorialD/Interpreter/Base.hs−4 TutorialD/Interpreter/TransactionGraphOperator.hs−1 TutorialD/tutd.hs+9−33 project-m36.cabal+3−2 test/TutorialD/Interpreter.hs+75−81
ProjectM36/Base.hs+2−1
@@ -265,4 +265,5 @@ instance Binary AttributeNames
265265data PersistenceStrategy = NoPersistence | --no filesystem persistence/memory-only database
266266 MinimalPersistence FilePath --fsync off
267267 --CrashSafePersistence FilePath --full fsync- not yet implemented
268− deriving (Show, Read)
268+ deriving (Show, Read)
269+
ProjectM36/RelationalExpression.hs+2−1
@@ -521,4 +521,5 @@ intAtomFuncLessThan _ _= BoolAtom False
521521
522522boolAtomNot :: [Atom] -> Atom
523523boolAtomNot (BoolAtom bool:_) = BoolAtom $ not bool
524−boolAtomNot _ = BoolAtom False
524+boolAtomNot _ = BoolAtom False
525+
TutorialD/Interpreter.hs+49−47
@@ -7,16 +7,13 @@ import TutorialD.Interpreter.TransactionGraphOperator
77import ProjectM36.Base
88import ProjectM36.Relation.Show.Term
99import ProjectM36.TransactionGraph
10+import ProjectM36.Client
1011import Text.Parsec
1112import Text.Parsec.String
1213import Control.Monad.State
1314import System.Console.Haskeline
14−import System.IO
1515import System.Directory (getHomeDirectory)
16−import qualified Data.UUID as U
17−import Data.UUID.V4 (nextRandom)
1816import qualified Data.Text as T
19−import qualified Data.Text.IO as TIO
2017import Control.Applicative ((<*))
2118
2219{-
@@ -43,56 +40,61 @@ promptText (DisconnectedTransaction parentUUID _) graph = "TutorialD (" `T.appen
4340 Right parentTrans -> case headNameForTransaction parentTrans graph of
4441 Nothing -> T.pack (show $ transactionUUID parentTrans)
4542 Just headName -> headName
43+
44+parseTutorialD :: Connection -> String -> Either ParseError ParsedOperation
45+parseTutorialD (InProcessConnection _ _) inputString = parse interpreterParserP "" inputString
4646
47−interpret :: U.UUID -> DisconnectedTransaction -> TransactionGraph -> String -> (TutorialDOperatorResult, DisconnectedTransaction, TransactionGraph, TransactionGraphWasUpdated)
48−interpret freshUUID discon@(DisconnectedTransaction parentUUID context) graph inputString = case parse interpreterParserP "" inputString of
49− Left err -> (DisplayErrorResult (T.pack $ show err), discon, graph, False)
50− Right (RODatabaseContextOp execOp) -> (evalRODatabaseContextOp context execOp, discon, graph, False)
51− Right (DatabaseExprOp execOp) -> case evalDatabaseExpr True context execOp of
52− Left err -> (DisplayErrorResult $ T.pack (show err), discon, graph, False)
53− Right context' -> (QuietSuccessResult, DisconnectedTransaction parentUUID context', graph, False)
54− Right (GraphOp execOp) -> case evalGraphOp freshUUID discon graph execOp of
55− Left err -> (DisplayErrorResult $ T.pack (show err), discon, graph, False)
56− Right (discon', graph') -> (QuietSuccessResult, discon', graph', True)
57− Right (ROGraphOp execOp) -> case evalROGraphOp discon graph execOp of
58− Left err -> (DisplayErrorResult $ T.pack (show err), discon, graph, False)
59− Right rel -> (DisplayResult $ showRelation rel, discon, graph, False)
60−
47+--execute the operation and display result
48+evalTutorialD :: Connection -> ParsedOperation -> IO (TutorialDOperatorResult)
49+evalTutorialD conn expr = case expr of
50+
51+ --this does not pass through the ProjectM36.Client library because the operations
52+ --are specific to the interpreter, though some operations may be of general use in the future
53+ (RODatabaseContextOp execOp) -> do
54+ (DisconnectedTransaction _ context) <- disconnectedTransaction conn
55+ return $ evalRODatabaseContextOp context execOp
56+
57+ (DatabaseExprOp execOp) -> do
58+ maybeErr <- executeDatabaseContextExpr conn execOp
59+ case maybeErr of
60+ Just err -> barf err
61+ Nothing -> return $ QuietSuccessResult
62+
63+ (GraphOp execOp) -> do
64+ maybeErr <- executeGraphExpr conn execOp
65+ case maybeErr of
66+ Just err -> barf err
67+ Nothing -> return $ QuietSuccessResult
68+
69+ (ROGraphOp execOp) -> do
70+ discon <- disconnectedTransaction conn
71+ graph <- transactionGraph conn
72+ case evalROGraphOp discon graph execOp of
73+ Left err -> barf err
74+ Right rel -> return $ DisplayResult $ showRelation rel
75+
76+ where
77+ barf err = return $ DisplayErrorResult (T.pack (show err))
78+
6179data InterpreterConfig = InterpreterConfig {
6280 persistenceStrategy :: PersistenceStrategy
6381 }
6482
65−reprLoop :: InterpreterConfig -> DisconnectedTransaction -> TransactionGraph -> IO ()
66−reprLoop config currentTransaction graph = do
83+reprLoop :: InterpreterConfig -> Connection -> IO ()
84+reprLoop config conn = do
6785 homeDirectory <- getHomeDirectory
6886 let settings = defaultSettings {historyFile = Just (homeDirectory ++ "/.tutd_history")}
69−
70− maybeLine <- runInputT settings $ getInputLine (T.unpack (promptText currentTransaction graph))
71−
72− let roloop = reprLoop config currentTransaction graph
87+ discon <- disconnectedTransaction conn
88+ graph <- transactionGraph conn
89+ maybeLine <- runInputT settings $ getInputLine (T.unpack (promptText discon graph))
90+ let roloop = reprLoop config conn
7391 case maybeLine of
7492 Nothing -> return ()
7593 Just line -> do
76− newUUID <- nextRandom
77− let (opResult, disconTrans, updatedGraph, graphWasUpdated) = interpret newUUID currentTransaction graph line
78− when graphWasUpdated $ processPersistence (persistenceStrategy config) updatedGraph
79− case (opResult, disconTrans, updatedGraph) of
80− (QuitResult, _, _) -> return ()
81− (DisplayErrorResult err, _, _) -> TIO.hPutStrLn stderr ("ERROR: " `T.append` err) >> roloop
82− (DisplayIOResult outio, updatedTrans', updatedGraph') -> do
83− outio
84− reprLoop config updatedTrans' updatedGraph'
85− (DisplayResult out, updatedTrans', updatedGraph') -> do
86− TIO.putStrLn out
87− --assume that the DisplayResult type implies that the transaction graph was not updated
88− reprLoop config updatedTrans' updatedGraph'
89− (QuietSuccessResult, updatedTrans', updatedGraph') -> do
90− --save the current transaction graph
91− TIO.putStrLn "Done."
92− reprLoop config updatedTrans' updatedGraph'
93−
94−--used by :dumpGraph
95−{-
96−displayTransactionGraph :: TransactionGraph -> String
97−displayTransactionGraph (TransactionGraph _ transSet) = L.intercalate "\n" $ S.foldr (\(Transaction tUUID pUUID _ ) acc -> acc ++ [show tUUID ++ " " ++ show pUUID]) [] transSet
98−-}
94+ case parseTutorialD conn line of
95+ Left err -> do
96+ displayOpResult $ DisplayErrorResult (T.pack (show err))
97+ roloop
98+ Right parsed -> do
99+ evald <- evalTutorialD conn parsed
100+ displayOpResult evald
TutorialD/Interpreter/Base.hs−4
@@ -10,7 +10,6 @@ import qualified Data.List as L
1010import qualified Data.Vector as V
1111import qualified Data.Text.IO as TIO
1212import System.IO
13−import ProjectM36.TransactionGraph.Persist
1413
1514lexer :: Token.TokenParser ()
1615lexer = Token.makeTokenParser tutD
@@ -78,6 +77,3 @@ displayOpResult (DisplayIOResult ioout) = ioout
7877displayOpResult (DisplayErrorResult err) = TIO.hPutStrLn stderr ("ERR: " `T.append` err)
7978displayOpResult QuietSuccessResult = return ()
8079
81−processPersistence :: PersistenceStrategy -> TransactionGraph -> IO ()
82−processPersistence NoPersistence _ = return ()
83−processPersistence (MinimalPersistence dbdir) graph = transactionGraphPersist dbdir graph
TutorialD/Interpreter/TransactionGraphOperator.hs−1
@@ -6,7 +6,6 @@ import Text.Parsec.String
66import qualified Data.UUID as U
77import qualified Data.Text as T
88import ProjectM36.TransactionGraph
9−import ProjectM36.Relation.Show.Term
109
1110jumpToHeadP :: Parser TransactionGraphOperator
1211jumpToHeadP = do
TutorialD/tutd.hs+9−33
@@ -1,14 +1,9 @@
11{-# LANGUAGE OverloadedStrings #-}
22import TutorialD.Interpreter
3−import ProjectM36.RelationalExpression
4−import ProjectM36.TransactionGraph
5−import ProjectM36.TransactionGraph.Persist
6−import ProjectM36.Transaction
73import ProjectM36.Base
4+import ProjectM36.Client
85import System.IO
96import Options.Applicative
10−import Data.UUID.V4 (nextRandom)
11−import qualified Data.Text as T
127import System.Exit
138
149{-
@@ -33,37 +28,18 @@ parsePersistenceStrategy = do
3328
3429
3530
36−configSetup :: InterpreterConfig -> IO (TransactionGraph)
37−configSetup config = do
38− let failHard msg = do
39− hPutStrLn stderr msg
40− exitFailure
41− firstGraphUUID <- nextRandom
42− let bootstrapGraph = bootstrapTransactionGraph firstGraphUUID dateExamples
43− case persistenceStrategy config of
44− --create date examples graph for now- probably should be empty context in the future
45− NoPersistence -> do { putStrLn "Running without filesystem persistence."; return bootstrapGraph}
46− MinimalPersistence dbdir -> do
47− err <- setupDatabaseDir dbdir bootstrapGraph
48− case err of
49− Just err' -> failHard ("Failed to configure database: " ++ show err')
50− Nothing -> do
51− graph <- transactionGraphLoad dbdir emptyTransactionGraph
52− case graph of
53− Left err' -> failHard ("Failed to load graph: " ++ show err')
54− Right graph' -> return graph'
55−
5631opts :: ParserInfo InterpreterConfig
5732opts = info parseArgs idm
5833
5934main :: IO ()
6035main = do
6136 interpreterConfig <- execParser opts
62− let currentHeadName = "master"
63− freshGraph <- configSetup interpreterConfig
64− case transactionForHead currentHeadName freshGraph of
65− Nothing -> hPutStrLn stderr $ "Failed to find head transaction for " ++ T.unpack currentHeadName ++ "."
66− Just headTransaction -> do
67− let currentTransaction = newDisconnectedTransaction (transactionUUID headTransaction) (transactionContext headTransaction)
68− reprLoop interpreterConfig currentTransaction freshGraph
37+ dbconn <- connectProjectM36 (InProcessConnectionInfo (persistenceStrategy interpreterConfig))
38+ case dbconn of
39+ Left err -> do
40+ hPutStrLn stderr ("Failed to create database connection: " ++ show err)
41+ exitFailure
42+ Right conn -> do
43+ _ <- reprLoop interpreterConfig conn
44+ return ()
6945
project-m36.cabal+3−2
@@ -7,7 +7,7 @@ Category: Relational Algebra
77Cabal-Version: >= 1.2
88
99Library
10− Build-Depends: base,mtl,containers,unordered-containers,parsec,hashable,haskeline,directory,uuid,cassava,text,bytestring,deepseq,deepseq-generics,vector,parallel,monad-parallel,transformers,gnuplot,binary,filepath,directory,vector-binary-instances,unix,temporary
10+ Build-Depends: base,mtl,containers,unordered-containers,parsec,hashable,haskeline,directory,uuid,cassava,text,bytestring,deepseq,deepseq-generics,vector,parallel,monad-parallel,transformers,gnuplot,binary,filepath,directory,vector-binary-instances,unix,temporary,stm
1111 Exposed-Modules: ProjectM36.Error,
1212 ProjectM36.Transaction,
1313 ProjectM36.TransactionGraph,
@@ -27,7 +27,8 @@ Library
2727 ProjectM36.StaticOptimizer,
2828 ProjectM36.Relation.Show.Term,
2929 ProjectM36.Relation.Show.CSV,
30− ProjectM36.Relation.Show.Gnuplot
30+ ProjectM36.Relation.Show.Gnuplot,
31+ ProjectM36.Client
3132 GHC-Options: -Wall -rtsopts
3233
3334Executable tutd
test/TutorialD/Interpreter.hs+75−81
@@ -1,6 +1,7 @@
11{-# LANGUAGE OverloadedStrings #-}
22import TutorialD.Interpreter.DatabaseExpr
33import TutorialD.Interpreter
4+import TutorialD.Interpreter.Base
45import Test.HUnit
56import ProjectM36.RelationalExpression
67import ProjectM36.Relation
@@ -8,16 +9,14 @@ import ProjectM36.Tuple
89import ProjectM36.TupleSet
910import ProjectM36.Error
1011import ProjectM36.Base
11−import ProjectM36.Transaction
1212import ProjectM36.TransactionGraph
13+import ProjectM36.Client
1314import qualified ProjectM36.Attribute as A
1415import qualified Data.HashSet as HS
1516import qualified Data.Map as M
16−import qualified Data.Set as S
1717import System.Exit
18−import Data.UUID.V4 (nextRandom)
19−import Data.UUID (nil)
20−import Data.Either (isRight)
18+import Data.Maybe (isJust)
19+import System.IO
2120import qualified Data.Vector as V
2221
2322--urgent: add group and ungroup tests- I missed the group relation type bug
@@ -105,91 +104,78 @@ assertTutdEqual databaseContext expected tutd = assertEqual tutd expected interp
105104
106105transactionGraphBasicTest :: Test
107106transactionGraphBasicTest = TestCase $ do
108− (_, graph) <- dateExamplesGraph
109− assertEqual "validate bootstrapped graph" (validateGraph graph) Nothing
107+ dbconn <- dateExamplesConnection
108+ graph <- transactionGraph dbconn
109+ assertEqual "validate bootstrapped graph" (validateGraph graph) Nothing
110110
111111--add a new transaction to the graph, validate it is in the graph
112112transactionGraphAddCommitTest :: Test
113113transactionGraphAddCommitTest = TestCase $ do
114− ((DisconnectedTransaction firstUUID context), origGraph) <- dateExamplesGraph
115− freshUUID <- nextRandom
116− case interpretDatabaseExpr context "x:=S" of
117− Left err -> assertFailure (show err)
118− Right newContext -> do
119− let discon = newDisconnectedTransaction firstUUID newContext
120− let addTrans = addDisconnectedTransaction freshUUID "master" discon origGraph
121− case addTrans of
122− Left err -> assertFailure (show err)
123− Right (newTrans, graph) -> do
124− assertBool "transaction in graph" (isRight $ transactionForUUID freshUUID graph)
125− assertEqual "validate fresh commit with deleted S" (validateGraph graph) Nothing
126− assertEqual "ensure S was deleted in newContext" (M.lookup "x" (relationVariables (transactionContext newTrans))) (Just suppliersRel)
114+ dbconn <- dateExamplesConnection
115+ case parseTutorialD dbconn "x:=S" of
116+ Left err -> assertFailure (show err)
117+ Right parsed -> do
118+ result <- evalTutorialD dbconn parsed
119+ case result of
120+ QuitResult -> assertFailure "quit?"
121+ DisplayResult _ -> assertFailure "display?"
122+ DisplayIOResult _ -> assertFailure "displayIO?"
123+ DisplayErrorResult err -> assertFailure (show err)
124+ QuietSuccessResult -> do
125+ commit dbconn >>= maybeFail
126+ (DisconnectedTransaction _ context) <- disconnectedTransaction dbconn
127+ assertEqual "ensure x was added" (M.lookup "x" (relationVariables context)) (Just suppliersRel)
127128
128129transactionRollbackTest :: Test
129130transactionRollbackTest = TestCase $ do
130− ((DisconnectedTransaction firstUUID origContext), graph) <- dateExamplesGraph
131− freshUUID <- nextRandom
132− case transactionForUUID firstUUID graph of
133− Left err -> assertFailure (show err)
134− Right _ -> do
135− case interpretDatabaseExpr origContext "x:=S" of
136− Left err -> assertFailure (show err)
137− Right newContext -> do
138− let discon = newDisconnectedTransaction firstUUID newContext
139− let (_, (DisconnectedTransaction _ newContext2), newGraph, _) = interpret freshUUID discon graph ":rollback"
140− assertEqual "validate context" (M.lookup "x" (relationVariables newContext2)) Nothing
141− assertEqual "validate graph" graph newGraph
131+ dbconn <- dateExamplesConnection
132+ graph <- transactionGraph dbconn
133+ maybeErr <- executeDatabaseContextExpr dbconn (Assign "x" (RelationVariable "S"))
134+ case maybeErr of
135+ Just err -> assertFailure (show err)
136+ Nothing -> do
137+ rollback dbconn >>= maybeFail
138+ (DisconnectedTransaction _ context') <- disconnectedTransaction dbconn
139+ graph' <- transactionGraph dbconn
140+ assertEqual "validate context" Nothing (M.lookup "x" (relationVariables context'))
141+ assertEqual "validate graph" graph graph'
142142
143143--commit a new transaction with "x" relation, jump to first transaction, verify that "x" is not present
144144transactionJumpTest :: Test
145145transactionJumpTest = TestCase $ do
146− ((DisconnectedTransaction firstUUID origContext), origGraph) <- dateExamplesGraph
147− freshUUID <- nextRandom
148− case transactionForUUID firstUUID origGraph of
149− Left err -> assertFailure (show err)
150− Right _ -> do
151− case interpretDatabaseExpr origContext "x:=S" of
152− Left err -> assertFailure (show err)
153− Right newContext -> do
154− --modify the second transaction
155− case interpretDatabaseExpr newContext "x:=S" of
156− Left err-> assertFailure (show err)
157− Right newContext2 -> do
158− --add the transaction
159− let discon = newDisconnectedTransaction firstUUID newContext2
160− let addTrans = addDisconnectedTransaction freshUUID "master" discon origGraph
161− case addTrans of
162− Left err -> assertFailure (show err)
163− Right (_, graph) -> do
164− --jump to the first transaction
165− let (_, (DisconnectedTransaction parentUUID newContext3), newGraph, _) = interpret freshUUID discon graph (":jump " ++ show firstUUID)
166− assertEqual "validate discon" parentUUID firstUUID
167− assertEqual "validate discon2" Nothing (M.lookup "x" (relationVariables newContext3))
168− assertEqual "validate graph" transactionUUIDs (S.map transactionUUID (transactionsForGraph newGraph))
169− where
170− transactionUUIDs = S.fromList [firstUUID, freshUUID]
146+ dbconn <- dateExamplesConnection
147+ (DisconnectedTransaction firstUUID _) <- disconnectedTransaction dbconn
148+ maybeErr <- executeDatabaseContextExpr dbconn (Assign "x" (RelationVariable "S"))
149+ case maybeErr of
150+ Just err -> assertFailure (show err)
151+ Nothing -> do
152+ commit dbconn >>= maybeFail
153+ --perform the jump
154+ maybeErr2 <- executeGraphExpr dbconn (JumpToTransaction firstUUID)
155+ case maybeErr2 of
156+ Just err -> assertFailure (show err)
157+ Nothing -> do
158+ --check that the disconnected transaction does not include "x"
159+ (DisconnectedTransaction _ context') <- disconnectedTransaction dbconn
160+ assertEqual "ensure x is not present" Nothing (M.lookup "x" (relationVariables context'))
161+
162+maybeFail :: (Show a) => Maybe a -> IO ()
163+maybeFail (Just err) = assertFailure (show err)
164+maybeFail Nothing = return ()
171165
172166--branch from the first transaction and verify that there are two heads
173167transactionBranchTest :: Test
174168transactionBranchTest = TestCase $ do
175− (origDiscon@(DisconnectedTransaction firstUUID origContext), origGraph) <- dateExamplesGraph
176− freshUUID1 <- nextRandom
177− freshUUID2 <- nextRandom
178− case transactionForUUID firstUUID origGraph of
179− Left err -> assertFailure (show err)
180− Right _ -> do
181− let disconMaster = newDisconnectedTransaction firstUUID origContext
182− let addTransMaster = addDisconnectedTransaction freshUUID1 "master" disconMaster origGraph
183− --add a second transaction to the "master" branch
184− case addTransMaster of
185− Left err -> assertFailure (show err)
186− Right (_, graph) -> do
187− --add a third transaction to the "test" branch
188− let (_, _, newGraph, _) = interpret freshUUID2 origDiscon graph ":branch test"
189− assertEqual "verify test head" freshUUID2 $ maybeTransUUID (transactionForHead "test" newGraph)
190− assertEqual "verify master head" freshUUID1 $ maybeTransUUID (transactionForHead "master" newGraph)
191− where
192− maybeTransUUID t = maybe nil transactionUUID t
169+ dbconn <- dateExamplesConnection
170+ mapM_ (\x -> x >>= maybeFail) [executeGraphExpr dbconn (Branch "test"),
171+ executeDatabaseContextExpr dbconn (Assign "x" (RelationVariable "S")),
172+ commit dbconn,
173+ executeGraphExpr dbconn (JumpToHead "master"),
174+ executeDatabaseContextExpr dbconn (Assign "y" (RelationVariable "S"))
175+ ]
176+ graph <- transactionGraph dbconn
177+ assertBool "master branch exists" $ isJust (transactionForHead "master" graph)
178+ assertBool "test branch exists" $ isJust (transactionForHead "test" graph)
193179
194180simpleJoinTest :: Test
195181simpleJoinTest = TestCase $ assertTutdEqual dateExamples joinedRel "x:=S join SP"
@@ -214,10 +200,18 @@ simpleJoinTest = TestCase $ assertTutdEqual dateExamples joinedRel "x:=S join SP
214200 [StringAtom "London", IntAtom 200, StringAtom "P2", StringAtom "S4", StringAtom "Clark", IntAtom 20]
215201 ]
216202
217−dateExamplesGraph :: IO (DisconnectedTransaction, TransactionGraph)
218−dateExamplesGraph = do
219− firstTransactionUUID <- nextRandom
220− let graph = bootstrapTransactionGraph firstTransactionUUID dateExamples
221− let discon = newDisconnectedTransaction firstTransactionUUID dateExamples
222− return (discon, graph)
203+dateExamplesConnection :: IO (Connection)
204+dateExamplesConnection = do
205+ dbconn <- connectProjectM36 (InProcessConnectionInfo NoPersistence)
206+ case dbconn of
207+ Left err -> do
208+ hPutStrLn stderr (show err)
209+ exitFailure
210+ Right conn -> do
211+ mapM_ (\(rvName,rvRel) -> executeDatabaseContextExpr conn (Assign rvName (ExistingRelation rvRel))) (M.toList (relationVariables dateExamples))
212+ mapM_ (\(idName,incDep) -> executeDatabaseContextExpr conn (AddInclusionDependency idName incDep)) (M.toList (inclusionDependencies dateExamples))
213+ --skipping atom functions for now- there are no atom function manipulation operators yet
214+ commit conn >>= maybeFail
215+ return conn
216+
223217