2module TutorialDInterpreter where
10import RelationStaticOptimizer
11import RelationalTransaction
12import RelationalTransactionShow
14import Text.Parsec.String
15import Text.Parsec.Expr
16import Text.Parsec.Language
17import qualified Text.Parsec.Token as Token
18import qualified Data.Set as S
19import qualified Data.HashSet as HS
20import qualified Data.Map as M
21import qualified Data.List as L
22import Control.Applicative (liftA, (<*), (*>))
23import Control.Monad.State
24import System.Console.Haskeline
26import System.Directory (getHomeDirectory)
27import qualified Data.UUID as U
28import Data.UUID.V4 (nextRandom)
31List of Tutorial D operators:
32show relation expression type :: relexpr -> context -> opresult
33show relvar :: relvar name -> context -> opresult
35show graph :: graph -> DisconnectedTransaction -> opresult
36show constraints :: context -> opresult
37jump to head :: head name -> graph -> DisconnectedTransaction (graph unchanged)
38jump to UUID :: uuid -> graph -> DisconnectedTransaction (graph unchanged)
39branch :: uuid -> branch name -> graph -> (DisconnectedTransation, graph)
40commit :: uuid -> DisconnectedTransaction -> graph -> (DisconnectedTransaction, graph)
41rollback :: DisconnectedTransaction -> graph -> (DisconnectedTransaction, graph)
45lexer :: Token.TokenParser ()
46lexer = Token.makeTokenParser tutD
47 where tutD = emptyDef {
48 Token.reservedOpNames = ["join", "where", "union", "group", "ungroup"],
49 Token.reservedNames = [],
50 Token.identStart = letter <|> char '_',
51 Token.identLetter = alphaNum <|> char '_' <|> char '#'} -- # needed for Date examples
53parens = Token.parens lexer
54reservedOp = Token.reservedOp lexer
55reserved = Token.reserved lexer
56braces = Token.braces lexer
57identifier = Token.identifier lexer
58comma = Token.comma lexer
59semi = Token.semi lexer
63 uuidStr <- many (alphaNum <|> char '-')
64 case U.fromString uuidStr of
65 Nothing -> fail "Invalid uuid string"
66 Just uuid -> return uuid
69attributeList :: Parser [AttributeName]
70attributeList = sepBy identifier comma
72makeRelation :: Parser RelationalExpr
75 attrs <- (try makeAttributes <|> return M.empty)
76 if not (M.null attrs) then do
77 return $ MakeStaticRelation attrs emptyTupleSet
79 tuples <- braces (sepBy tupleP comma)
80 return $ MakeStaticRelation (tupleAttributes (head tuples)) (HS.fromList tuples)
82--used in relation creation
83makeAttributes :: Parser Attributes
85 attrList <- braces (sepBy attributeAndType comma)
86 return $ M.fromList $ map toAttributeAssocList attrList
88 toAttributeAssocList attr@(Attribute attrName _) = (attrName, attr)
90attributeAndType :: Parser Attribute
92 attrName <- identifier
93 attrTypeName <- identifier
94 --convert type name into type
95 case tutDTypeToAtomType attrTypeName of
96 Just t -> return $ Attribute attrName t
97 Nothing -> fail (attrTypeName ++ " is not a valid type name.")
99tupleP :: Parser RelationTuple
102 attrValues <- braces (sepBy tupleAtomP comma)
103 return $ RelationTuple (M.fromList attrValues)
105tupleAtomP :: Parser (AttributeName, Atom)
107 attributeName <- identifier
108 attrType <- identifier
109 atom <- parens (stringAtomP <|> intAtomP)
110 case tutDTypeToAtomType attrType of
111 Nothing -> fail (attrType ++ " is not a valid type name.")
112 Just typeA -> if typeA == atomTypeForAtom atom then
113 return $ (attributeName, atom)
115 fail "type mismatch in tuple generation"
117--convert Tutorial D type to AtomType
118tutDTypeToAtomType :: String -> Maybe AtomType
119tutDTypeToAtomType tutDType = case tutDType of
120 "char" -> Just StringAtomType
121 "int" -> Just IntAtomType
124atomTypeToTutDType :: AtomType -> Maybe String
125atomTypeToTutDType atomType = case atomType of
126 StringAtomType -> Just "char"
127 IntAtomType -> Just "int"
128 --RelationAtomType rel ->
131relVarP :: Parser RelationalExpr
132relVarP = liftA RelationVariable identifier
134relTerm = parens relExpr
139 attrs <- braces attributeList
140 return $ Project (S.fromList attrs)
142assignP :: Parser DatabaseExpr
144 relVarName <- try $ do
145 relVarName <- identifier
149 return $ Assign relVarName expr
152 oldAttr <- identifier
154 newAttr <- identifier
155 return $ (oldAttr, newAttr)
157renameP :: Parser (RelationalExpr -> RelationalExpr)
160 (oldAttr, newAttr) <- braces renameClause
161 return $ Rename oldAttr newAttr
165 boolExpr <- restrictionPredicateP
166 return $ Restrict boolExpr
169 attrs <- braces attributeList
171 newAttrName <- identifier
172 return $ (attrs, newAttrName)
174groupP :: Parser (RelationalExpr -> RelationalExpr)
177 (groupAttrList, groupAttrName) <- parens groupClause
178 return $ Group (S.fromList groupAttrList) groupAttrName
180--in "Time and Relational Theory" (2014), Date's Tutorial D grammar for ungroup takes one attribute, while in previous books, it take multiple arguments. Let us assume that nested ungroups are the same as multiple attributes.
181ungroupP :: Parser (RelationalExpr -> RelationalExpr)
184 rvaAttrName <- identifier
185 return $ Ungroup rvaAttrName
190 [Postfix whereClauseP],
193 [Infix (reservedOp "join" >> return Join) AssocLeft],
194 [Infix (reservedOp "union" >> return Union) AssocLeft],
195 [Infix (reservedOp "=" >> return Equals) AssocNone]
198relExpr :: Parser RelationalExpr
199relExpr = buildExpressionParser relOperators relTerm
201databaseExpr :: Parser DatabaseExpr
202databaseExpr = insertP
210multipleDatabaseExpr :: Parser DatabaseExpr
211multipleDatabaseExpr = do
212 exprs <- sepBy1 databaseExpr semi
213 return $ MultipleExpr exprs
215insertP :: Parser DatabaseExpr
220 return $ Insert relvar expr
222defineP :: Parser DatabaseExpr
224 relVarName <- try $ do
225 relVarName <- identifier
228 attributes <- makeAttributes
229 return $ Define relVarName attributes
231undefineP :: Parser DatabaseExpr
233 reservedOp "undefine"
234 relVarName <- identifier
235 return $ Undefine relVarName
237deleteP :: Parser DatabaseExpr
240 relVarName <- identifier
241 predicate <- option TruePredicate (reservedOp "where" *> restrictionPredicateP)
242 return $ Delete relVarName predicate
244updateP :: Parser DatabaseExpr
247 relVarName <- identifier
248 predicate <- option TruePredicate (reservedOp "where" *> restrictionPredicateP <* spaces)
249 attributeAssignments <- liftM M.fromList $ parens (sepBy attributeAssignment comma)
250 return $ Update relVarName attributeAssignments predicate
252constraintP :: Parser DatabaseExpr
254 reservedOp "constraint"
255 constraintName <- identifier
259 return $ AddInclusionDependency (InclusionDependency constraintName subset superset)
261attributeAssignment :: Parser (String, Atom)
262attributeAssignment = do
263 attrName <- identifier
265 atom <- stringAtomP <|> intAtomP
266 return $ (attrName, atom)
268parseString :: String -> Either RelationalError DatabaseExpr
269parseString str = case parse (multipleDatabaseExpr <* eof) "" str of
270 Left err -> Left $ ParseError (show err)
273--operators which only rely on database context reading
274data ContextOperator where
275 ShowRelation :: RelationalExpr -> ContextOperator
276 ShowRelationType :: RelationalExpr -> ContextOperator
277 ShowConstraint :: String -> ContextOperator
278 ShowPlan :: DatabaseExpr -> ContextOperator
279 Quit :: ContextOperator
282--operators which manipulate a transaction graph
283data GraphOperator where
284 JumpToHead :: String -> GraphOperator
285 JumpToTransaction :: U.UUID -> GraphOperator
286 Branch :: HeadName -> GraphOperator
287 Commit :: GraphOperator
288 Rollback :: GraphOperator
289 ShowGraph :: GraphOperator
291typeP :: Parser ContextOperator
295 return $ ShowRelationType expr
297showRelP :: Parser ContextOperator
299 reservedOp ":showexpr"
301 return $ ShowRelation expr
303showPlanP :: Parser ContextOperator
305 reservedOp ":showplan"
307 return $ ShowPlan expr
309quitP :: Parser ContextOperator
314showConstraintsP :: Parser ContextOperator
316 reservedOp ":constraints"
317 constraintName <- option "" identifier
318 return $ ShowConstraint constraintName
320jumpToHeadP :: Parser GraphOperator
322 reservedOp ":jumphead"
324 return $ JumpToHead head
326jumpToTransactionP :: Parser GraphOperator
327jumpToTransactionP = do
330 return $ JumpToTransaction uuid
332branchTransactionP :: Parser GraphOperator
333branchTransactionP = do
335 branchName <- identifier
336 return $ Branch branchName
338commitTransactionP :: Parser GraphOperator
339commitTransactionP = do
343rollbackTransactionP :: Parser GraphOperator
344rollbackTransactionP = do
345 reservedOp ":rollback"
348showGraphP :: Parser GraphOperator
350 reservedOp ":showgraph"
353transactionGraphOps = do
355 <|> jumpToTransactionP
356 <|> branchTransactionP
357 <|> commitTransactionP
358 <|> rollbackTransactionP
361contextOps :: Parser ContextOperator
368interpreterOps :: Parser (Either ContextOperator GraphOperator)
369interpreterOps = liftM Left contextOps <|> liftM Right transactionGraphOps
371showRelationAttributes :: Relation -> String
372showRelationAttributes rel = "{" ++ concat (L.intersperse ", " $ map showAttribute attrs) ++ "}"
374 showAttribute (Attribute name atomType) = name ++ " " ++ case atomTypeToTutDType atomType of
377 attrs = values (attributes rel)
378 values m = map snd (M.toAscList m)
380data TutorialDOperatorResult = QuitResult |
381 DisplayResult String |
382 DisplayErrorResult String |
384 NoActionResult --refactor to make this dead- there should be only one parser
386evalContextOp :: DatabaseContext -> ContextOperator -> TutorialDOperatorResult
387evalContextOp context (ShowRelationType expr) = case runState (typeForRelationalExpr expr) context of
388 (Right rel, _) -> DisplayResult $ showRelationAttributes rel
389 (Left err, _) -> DisplayErrorResult $ show err
391evalContextOp context (ShowRelation expr) = do
392 case runState (evalRelationalExpr expr) context of
393 (Left err, _) -> DisplayErrorResult $ show err
394 (Right rel, _) -> DisplayResult $ showRelation rel
396evalContextOp context (ShowConstraint name) = do
397 DisplayResult $ show filteredDeps
399 deps = inclusionDependencies context
400 filteredDeps = case name of
402 name -> HS.filter (\(InclusionDependency n _ _) -> n == name) deps
404evalContextOp context (ShowPlan dbExpr) = do
405 DisplayResult $ show plan
407 plan = evalState (applyStaticDatabaseOptimization dbExpr) context
409evalContextOp context (Quit) = QuitResult
411-- returns the new "current" transaction, updated graph, and tutorial d result
412-- the current transaction is not part of the transaction graph until it is committed
413evalGraphOp :: U.UUID -> DisconnectedTransaction -> TransactionGraph -> GraphOperator -> Either RelationalError (DisconnectedTransaction, TransactionGraph, TutorialDOperatorResult)
415--affects only disconncted transaction
416evalGraphOp newUUID trans graph (JumpToTransaction jumpUUID) = case transactionForUUID jumpUUID graph of
418 Right parentTrans -> Right (newTrans, graph, QuietSuccessResult)
420 newTrans = DisconnectedTransaction jumpUUID (transactionContext parentTrans)
422-- switch from one head to another
423-- affects only disconnectedtransaction
424evalGraphOp newUUID currentTransaction graph (JumpToHead headName) =
425 case transactionForHead headName graph of
426 Just newHeadTransaction -> let disconnectedTrans = newDisconnectedTransaction (transactionUUID newHeadTransaction) (transactionContext newHeadTransaction) in
427 Right (disconnectedTrans, graph, QuietSuccessResult)
428 Nothing -> Left $ NoSuchHeadNameError headName
429-- add new head pointing to branchPoint
430-- repoint the disconnected transaction to the new branch commit (with a potentially different disconnected context)
431-- affects transactiongraph and the disconnectedtransaction is recreated based off the branch
433evalGraphOp newUUID discon@(DisconnectedTransaction parentUUID disconContext) graph (Branch newBranchName) = case transactionForUUID parentUUID graph of
434 Nothing -> (discon, graph, DisplayErrorResult "Failed to find parent transaction.")
435 Just parentTrans -> case addBranch newBranchName parentTrans graph of
436 Nothing -> (discon, graph, DisplayErrorResult "Failed to add branch.")
437 Just newGraph -> (newDiscon, newGraph, DisplayResult "Branched.")
439 newDiscon = DisconnectedTransaction (transactionUUID parentTrans) disconContext
442-- create a new commit and add it to the heads
443-- technically, the new head could be added to an existing commit, but by adding a new commit, the new head is unambiguously linked to a new commit (with a context indentical to its parent)
444evalGraphOp newUUID discon@(DisconnectedTransaction parentUUID disconContext) graph (Branch newBranchName) = case transactionForUUID parentUUID graph of
446 Right parentTrans -> case addBranch newUUID newBranchName parentTrans graph of
448 Right (newTrans, newGraph) -> Right (newDiscon, newGraph, QuietSuccessResult)
450 newDiscon = DisconnectedTransaction newUUID disconContext
452-- add the disconnected transaction to the graph
453-- affects graph and disconnectedtransaction- the new disconnectedtransaction's parent is the freshly committed transaction
454evalGraphOp newTransUUID discon@(DisconnectedTransaction parentUUID context) graph Commit = case transactionForUUID parentUUID graph of
456 Right parentTransaction -> case headNameForTransaction parentTransaction graph of
457 Nothing -> Left $ TransactionIsNotAHeadError parentUUID
458 Just headName -> case maybeUpdatedGraph of
460 Right (_, updatedGraph) -> Right (newDisconnectedTrans, updatedGraph, QuietSuccessResult)
462 newDisconnectedTrans = newDisconnectedTransaction newTransUUID context
463 maybeUpdatedGraph = addDisconnectedTransaction newTransUUID headName discon graph
465-- refresh the disconnected transaction, return the same graph
466evalGraphOp _ discon@(DisconnectedTransaction parentUUID _) graph Rollback = case transactionForUUID parentUUID graph of
468 Right parentTransaction -> Right (newDiscon, graph, QuietSuccessResult)
470 newDiscon = newDisconnectedTransaction parentUUID (transactionContext parentTransaction)
472--display transaction graph as relation
473evalGraphOp _ discon graph ShowGraph = do
474 graphStr <- graphAsRelation discon graph
475 return (discon, graph, DisplayResult $ showRelation graphStr)
477--shouldn't this be Either RelationalError DatabaseContext?
478interpret :: DatabaseContext -> String -> (Maybe RelationalError, DatabaseContext)
479interpret context tutdstring = case parseString tutdstring of
480 Left err -> (Just err, context)
481 Right parsed -> runState (evaluator parsed) context
485 optExpr <- applyStaticDatabaseOptimization expr
488 Left err -> return $ Just err
489 Right optExprA -> evalContextExpr optExprA
492interpretNO :: DatabaseContext -> String -> (Maybe RelationalError, DatabaseContext)
493interpretNO context tutdstring = case parseString tutdstring of
494 Left err -> (Just err, context)
495 Right parsed -> runState (evalContextExpr parsed) context
498-- for interpreter-specific operations
499interpretOps :: U.UUID -> DisconnectedTransaction -> TransactionGraph -> String -> (DisconnectedTransaction, TransactionGraph, TutorialDOperatorResult)
500interpretOps newUUID trans@(DisconnectedTransaction parentUUID context) transGraph instring = case parse interpreterOps "" instring of
501 Left err -> (trans, transGraph, NoActionResult)
502 Right ops -> case ops of
503 Left contextOp -> (trans, transGraph, (evalContextOp context contextOp))
504 Right graphOp -> case evalGraphOp newUUID trans transGraph graphOp of
505 Left err -> (trans, transGraph, DisplayErrorResult $ show err)
506 Right (newDiscon, newGraph, result) -> (newDiscon, newGraph, result)
508promptText :: DisconnectedTransaction -> TransactionGraph -> String
509promptText (DisconnectedTransaction parentUUID _) graph = "TutorialD (" ++ transInfo ++ "): "
511 transInfo = case transactionForUUID parentUUID graph of
512 Left err -> "unknown"
513 Right parentTrans -> case headNameForTransaction parentTrans graph of
514 Nothing -> show $ transactionUUID parentTrans
515 Just headName -> headName
517reprLoop :: DisconnectedTransaction -> TransactionGraph -> IO ()
518reprLoop currentTransaction@(DisconnectedTransaction currentParentUUID currentContext) graph = do
519 homeDirectory <- getHomeDirectory
520 let settings = defaultSettings {historyFile = Just (homeDirectory ++ "/.tutd_history")}
522 maybeLine <- runInputT settings $ getInputLine (promptText currentTransaction graph)
524 let roloop = reprLoop currentTransaction graph
528 newUUID <- nextRandom
529 case interpretOps newUUID currentTransaction graph line of
530 (_, _, QuitResult) -> return ()
531 (_, _, DisplayErrorResult err) -> hPutStrLn stderr ("ERR: " ++ err) >> roloop
532 (updatedTrans, updatedGraph, DisplayResult out) -> do
534 reprLoop updatedTrans updatedGraph
535 (updatedTrans, updatedGraph, QuietSuccessResult) -> do
537 reprLoop updatedTrans updatedGraph
538 (_, _, NoActionResult) -> do
539 let (value, contextup) = interpret currentContext line
540 let updatedTransaction = DisconnectedTransaction currentParentUUID contextup
542 Nothing -> reprLoop updatedTransaction graph
544 hPutStrLn stderr ("ERR:" ++ show err)
545 reprLoop currentTransaction graph
547restrictionPredicateP :: Parser RestrictionPredicateExpr
548restrictionPredicateP = buildExpressionParser predicateOperators predicateTerm
550 predicateOperators = [
551 [Prefix (reservedOp "not" >> return NotPredicate)],
552 [Infix (reservedOp "and" >> return AndPredicate) AssocLeft],
553 [Infix (reservedOp "or" >> return OrPredicate) AssocLeft]
555 predicateTerm = parens restrictionPredicateP
556 <|> try restrictionAttributeEqualityP
557 <|> try relationalBooleanExpr
560relationalBooleanExpr :: Parser RestrictionPredicateExpr
561relationalBooleanExpr = do
563 return $ RelationalExprPredicate relexpr
565restrictionAttributeEqualityP :: Parser RestrictionPredicateExpr
566restrictionAttributeEqualityP = do
567 attributeName <- identifier
569 atom <- stringAtomP <|> intAtomP
570 return $ AttributeEqualityPredicate attributeName atom
572quotedChar = noneOf "\""
573 <|> try (string "\"\"" >> return '"')
575stringAtomP = liftA StringAtom (string "\"" *> many quotedChar <* string "\"")
579 return $ IntAtom (read intstr)
582displayTransactionGraph :: TransactionGraph -> String
583displayTransactionGraph (TransactionGraph heads transSet) = L.intercalate "\n" $ S.foldr (\(Transaction tUUID pUUID _ ) acc -> acc ++ [show tUUID ++ " " ++ show pUUID]) [] transSet
keysesc to close
- j k
- move down, up
- enter
- open
- esc
- back, or up a level
- 1 2 3 4
- readme, files, commits, refs
- /
- filter
- y
- copy the clone command
- c
- compare the chosen commit with another (or shift-click both)
- ?
- show or hide this list