2module TutorialDInterpreter where
10import RelationalTransaction
11import RelationalTransactionShow
13import Text.Parsec.String
14import Text.Parsec.Expr
15import Text.Parsec.Language
16import qualified Text.Parsec.Token as Token
17import qualified Data.Set as S
18import qualified Data.HashSet as HS
19import qualified Data.Map as M
20import qualified Data.List as L
21import Control.Applicative (liftA, (<*), (*>))
22import Control.Monad.State
23import System.Console.Haskeline
25import System.Directory (getHomeDirectory)
26import qualified Data.UUID as U
27import Data.UUID.V4 (nextRandom)
30List of Tutorial D operators:
31show relation expression type :: relexpr -> context -> opresult
32show relvar :: relvar name -> context -> opresult
34show graph :: graph -> DisconnectedTransaction -> opresult
35show constraints :: context -> opresult
36jump to head :: head name -> graph -> DisconnectedTransaction (graph unchanged)
37jump to UUID :: uuid -> graph -> DisconnectedTransaction (graph unchanged)
38branch :: uuid -> branch name -> graph -> (DisconnectedTransation, graph)
39commit :: uuid -> DisconnectedTransaction -> graph -> (DisconnectedTransaction, graph)
40rollback :: DisconnectedTransaction -> graph -> (DisconnectedTransaction, graph)
44lexer :: Token.TokenParser ()
45lexer = Token.makeTokenParser tutD
46 where tutD = emptyDef {
47 Token.reservedOpNames = ["join", "where", "union", "group", "ungroup"],
48 Token.reservedNames = [],
49 Token.identStart = letter <|> char '_' <|> char '#',
50 Token.identLetter = alphaNum <|> char '_' <|> char '#'}
52parens = Token.parens lexer
53reservedOp = Token.reservedOp lexer
54reserved = Token.reserved lexer
55braces = Token.braces lexer
56identifier = Token.identifier lexer
57comma = Token.comma lexer
58semi = Token.semi lexer
62 uuidStr <- many (alphaNum <|> char '-')
63 case U.fromString uuidStr of
64 Nothing -> fail "Invalid uuid string"
65 Just uuid -> return uuid
68attributeList :: Parser [AttributeName]
69attributeList = sepBy identifier comma
71makeRelation :: Parser RelationalExpr
74 attrs <- (try makeAttributes <|> return M.empty)
75 if not (M.null attrs) then do
76 return $ MakeStaticRelation attrs emptyTupleSet
78 tuples <- braces (sepBy tupleP comma)
79 return $ MakeStaticRelation (tupleAttributes (head tuples)) (HS.fromList tuples)
81--used in relation creation
82makeAttributes :: Parser Attributes
84 attrList <- braces (sepBy attributeAndType comma)
85 return $ M.fromList $ map toAttributeAssocList attrList
87 toAttributeAssocList attr@(Attribute attrName _) = (attrName, attr)
89attributeAndType :: Parser Attribute
91 attrName <- identifier
92 attrTypeName <- identifier
93 --convert type name into type
94 case tutDTypeToAtomType attrTypeName of
95 Just t -> return $ Attribute attrName t
96 Nothing -> fail (attrTypeName ++ " is not a valid type name.")
98tupleP :: Parser RelationTuple
101 attrValues <- braces (sepBy tupleAtomP comma)
102 return $ RelationTuple (M.fromList attrValues)
104tupleAtomP :: Parser (AttributeName, Atom)
106 attributeName <- identifier
107 attrType <- identifier
108 atom <- parens (stringAtomP <|> intAtomP)
109 case tutDTypeToAtomType attrType of
110 Nothing -> fail (attrType ++ " is not a valid type name.")
111 Just typeA -> if typeA == atomTypeForAtom atom then
112 return $ (attributeName, atom)
114 fail "type mismatch in tuple generation"
116--convert Tutorial D type to AtomType
117tutDTypeToAtomType :: String -> Maybe AtomType
118tutDTypeToAtomType tutDType = case tutDType of
119 "char" -> Just StringAtomType
120 "int" -> Just IntAtomType
123atomTypeToTutDType :: AtomType -> Maybe String
124atomTypeToTutDType atomType = case atomType of
125 StringAtomType -> Just "char"
126 IntAtomType -> Just "int"
127 --RelationAtomType rel ->
130relVarP :: Parser RelationalExpr
131relVarP = liftA RelationVariable identifier
133relTerm = parens relExpr
138 attrs <- braces attributeList
139 return $ Project (S.fromList attrs)
141assignP :: Parser DatabaseExpr
143 relVarName <- try $ do
144 relVarName <- identifier
148 return $ Assign relVarName expr
151 oldAttr <- identifier
153 newAttr <- identifier
154 return $ (oldAttr, newAttr)
156renameP :: Parser (RelationalExpr -> RelationalExpr)
159 (oldAttr, newAttr) <- braces renameClause
160 return $ Rename oldAttr newAttr
164 boolExpr <- restrictionPredicateP
165 return $ Restrict boolExpr
168 attrs <- braces attributeList
170 newAttrName <- identifier
171 return $ (attrs, newAttrName)
173groupP :: Parser (RelationalExpr -> RelationalExpr)
176 (groupAttrList, groupAttrName) <- parens groupClause
177 return $ Group (S.fromList groupAttrList) groupAttrName
179--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.
180ungroupP :: Parser (RelationalExpr -> RelationalExpr)
183 rvaAttrName <- identifier
184 return $ Ungroup rvaAttrName
189 [Postfix whereClauseP],
192 [Infix (reservedOp "join" >> return Join) AssocLeft],
193 [Infix (reservedOp "union" >> return Union) AssocLeft],
194 [Infix (reservedOp "=" >> return Equals) AssocNone]
197relExpr :: Parser RelationalExpr
198relExpr = buildExpressionParser relOperators relTerm
200databaseExpr :: Parser DatabaseExpr
201databaseExpr = insertP
208multipleDatabaseExpr :: Parser DatabaseExpr
209multipleDatabaseExpr = do
210 exprs <- sepBy1 databaseExpr semi
211 return $ MultipleExpr exprs
213insertP :: Parser DatabaseExpr
218 return $ Insert relvar expr
220defineP :: Parser DatabaseExpr
222 relVarName <- try $ do
223 relVarName <- identifier
226 attributes <- makeAttributes
227 return $ Define relVarName attributes
229deleteP :: Parser DatabaseExpr
232 relVarName <- identifier
233 return $ Delete relVarName
235updateP :: Parser DatabaseExpr
238 relVarName <- identifier
239 predicate <- option TruePredicate (reservedOp "where" *> restrictionPredicateP <* spaces)
240 attributeAssignments <- liftM M.fromList $ parens (sepBy attributeAssignment comma)
241 return $ Update relVarName attributeAssignments predicate
243constraintP :: Parser DatabaseExpr
245 reservedOp "constraint"
246 constraintName <- identifier
250 return $ AddInclusionDependency (InclusionDependency constraintName subset superset)
252attributeAssignment :: Parser (String, Atom)
253attributeAssignment = do
254 attrName <- identifier
256 atom <- stringAtomP <|> intAtomP
257 return $ (attrName, atom)
259parseString :: String -> Either RelationalError DatabaseExpr
260parseString str = case parse multipleDatabaseExpr "" str of
261 Left err -> Left $ ParseError (show err)
264--operators which only rely on database context reading
265data ContextOperator where
266 ShowRelation :: RelationalExpr -> ContextOperator
267 ShowRelationType :: RelationalExpr -> ContextOperator
268 ShowConstraint :: String -> ContextOperator
269 Quit :: ContextOperator
272--operators which manipulate a transaction graph
273data GraphOperator where
274 JumpToHead :: String -> GraphOperator
275 JumpToTransaction :: U.UUID -> GraphOperator
276 Branch :: HeadName -> GraphOperator
277 Commit :: GraphOperator
278 Rollback :: GraphOperator
279 ShowGraph :: GraphOperator
282typeP :: Parser ContextOperator
286 return $ ShowRelationType expr
288showRelP :: Parser ContextOperator
290 reservedOp ":showExpr"
292 return $ ShowRelation expr
294quitP :: Parser ContextOperator
299showConstraintsP :: Parser ContextOperator
301 reservedOp ":constraints"
302 constraintName <- option "" identifier
303 return $ ShowConstraint constraintName
305jumpToHeadP :: Parser GraphOperator
307 reservedOp ":jumphead"
309 return $ JumpToHead head
311jumpToTransactionP :: Parser GraphOperator
312jumpToTransactionP = do
315 return $ JumpToTransaction uuid
317branchTransactionP :: Parser GraphOperator
318branchTransactionP = do
320 branchName <- identifier
321 return $ Branch branchName
323commitTransactionP :: Parser GraphOperator
324commitTransactionP = do
328rollbackTransactionP :: Parser GraphOperator
329rollbackTransactionP = do
330 reservedOp ":rollback"
333showGraphP :: Parser GraphOperator
335 reservedOp ":showGraph"
338transactionGraphOps = do
340 <|> jumpToTransactionP
341 <|> branchTransactionP
342 <|> commitTransactionP
343 <|> rollbackTransactionP
346contextOps :: Parser ContextOperator
352interpreterOps :: Parser (Either ContextOperator GraphOperator)
353interpreterOps = liftM Left contextOps <|> liftM Right transactionGraphOps
355showRelationAttributes :: Relation -> String
356showRelationAttributes rel = "{" ++ concat (L.intersperse ", " $ map showAttribute attrs) ++ "}"
358 showAttribute (Attribute name atomType) = name ++ " " ++ case atomTypeToTutDType atomType of
361 attrs = values (attributes rel)
362 values m = map snd (M.toAscList m)
364data TutorialDOperatorResult = QuitResult |
365 DisplayResult String |
366 DisplayErrorResult String |
368 NoActionResult --refactor to make this dead- there should be only one parser
370evalContextOp :: DatabaseContext -> ContextOperator -> TutorialDOperatorResult
371evalContextOp context (ShowRelationType expr) = case runState (typeForRelationalExpr expr) context of
372 (Right rel, _) -> DisplayResult $ showRelationAttributes rel
373 (Left err, _) -> DisplayErrorResult $ show err
375evalContextOp context (ShowRelation expr) = do
376 case runState (evalRelationalExpr expr) context of
377 (Left err, _) -> DisplayErrorResult $ show err
378 (Right rel, _) -> DisplayResult $ showRelation rel
380evalContextOp context (ShowConstraint name) = do
381 DisplayResult $ show filteredDeps
383 deps = inclusionDependencies context
384 filteredDeps = case name of
386 name -> HS.filter (\(InclusionDependency n _ _) -> n == name) deps
388--I need to pass the transaction graph around instead of the singular context :/
389evalContextOp context (Quit) = QuitResult
391-- returns the new "current" transaction, updated graph, and tutorial d result
392-- the current transaction is not part of the transaction graph until it is committed
393evalGraphOp :: U.UUID -> DisconnectedTransaction -> TransactionGraph -> GraphOperator -> Either RelationalError (DisconnectedTransaction, TransactionGraph, TutorialDOperatorResult)
395--affects only disconncted transaction
396evalGraphOp newUUID trans graph (JumpToTransaction jumpUUID) = case transactionForUUID jumpUUID graph of
398 Right parentTrans -> Right (newTrans, graph, QuietSuccessResult)
400 newTrans = DisconnectedTransaction jumpUUID (transactionContext parentTrans)
402-- switch from one head to another
403-- affects only disconnectedtransaction
404evalGraphOp newUUID currentTransaction graph (JumpToHead headName) =
405 case transactionForHead headName graph of
406 Just newHeadTransaction -> let disconnectedTrans = newDisconnectedTransaction (transactionUUID newHeadTransaction) (transactionContext newHeadTransaction) in
407 Right (disconnectedTrans, graph, QuietSuccessResult)
408 Nothing -> Left $ NoSuchHeadNameError headName
409-- add new head pointing to branchPoint
410-- repoint the disconnected transaction to the new branch commit (with a potentially different disconnected context)
411-- affects transactiongraph and the disconnectedtransaction is recreated based off the branch
413evalGraphOp newUUID discon@(DisconnectedTransaction parentUUID disconContext) graph (Branch newBranchName) = case transactionForUUID parentUUID graph of
414 Nothing -> (discon, graph, DisplayErrorResult "Failed to find parent transaction.")
415 Just parentTrans -> case addBranch newBranchName parentTrans graph of
416 Nothing -> (discon, graph, DisplayErrorResult "Failed to add branch.")
417 Just newGraph -> (newDiscon, newGraph, DisplayResult "Branched.")
419 newDiscon = DisconnectedTransaction (transactionUUID parentTrans) disconContext
422-- create a new commit and add it to the heads
423-- 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)
424evalGraphOp newUUID discon@(DisconnectedTransaction parentUUID disconContext) graph (Branch newBranchName) = case transactionForUUID parentUUID graph of
426 Right parentTrans -> case addBranch newUUID newBranchName parentTrans graph of
428 Right (newTrans, newGraph) -> Right (newDiscon, newGraph, QuietSuccessResult)
430 newDiscon = DisconnectedTransaction newUUID disconContext
432-- add the disconnected transaction to the graph
433-- affects graph and disconnectedtransaction- the new disconnectedtransaction's parent is the freshly committed transaction
434evalGraphOp newTransUUID discon@(DisconnectedTransaction parentUUID context) graph Commit = case transactionForUUID parentUUID graph of
436 Right parentTransaction -> case headNameForTransaction parentTransaction graph of
437 Nothing -> Left $ TransactionIsNotAHeadError parentUUID
438 Just headName -> case maybeUpdatedGraph of
440 Right (_, updatedGraph) -> Right (newDisconnectedTrans, updatedGraph, QuietSuccessResult)
442 newDisconnectedTrans = newDisconnectedTransaction newTransUUID context
443 maybeUpdatedGraph = addDisconnectedTransaction newTransUUID headName discon graph
445-- refresh the disconnected transaction, return the same graph
446evalGraphOp _ discon@(DisconnectedTransaction parentUUID _) graph Rollback = case transactionForUUID parentUUID graph of
448 Right parentTransaction -> Right (newDiscon, graph, QuietSuccessResult)
450 newDiscon = newDisconnectedTransaction parentUUID (transactionContext parentTransaction)
452--display transaction graph as relation
453evalGraphOp _ discon graph ShowGraph = do
454 graphStr <- graphAsRelation discon graph
455 return (discon, graph, DisplayResult $ showRelation graphStr)
457--shouldn't this be Either RelationalError DatabaseContext?
458interpret :: DatabaseContext -> String -> (Maybe RelationalError, DatabaseContext)
459interpret context tutdstring = case parseString tutdstring of
460 Left err -> (Just err, context)
461 Right parsed -> runState (evalContextExpr parsed) context
463-- for interpreter-specific operations
464interpretOps :: U.UUID -> DisconnectedTransaction -> TransactionGraph -> String -> (DisconnectedTransaction, TransactionGraph, TutorialDOperatorResult)
465interpretOps newUUID trans@(DisconnectedTransaction parentUUID context) transGraph instring = case parse interpreterOps "" instring of
466 Left err -> (trans, transGraph, NoActionResult)
467 Right ops -> case ops of
468 Left contextOp -> (trans, transGraph, (evalContextOp context contextOp))
469 Right graphOp -> case evalGraphOp newUUID trans transGraph graphOp of
470 Left err -> (trans, transGraph, DisplayErrorResult $ show err)
471 Right (newDiscon, newGraph, result) -> (newDiscon, newGraph, result)
473promptText :: DisconnectedTransaction -> TransactionGraph -> String
474promptText (DisconnectedTransaction parentUUID _) graph = "TutorialD (" ++ transInfo ++ "): "
476 transInfo = case transactionForUUID parentUUID graph of
477 Left err -> "unknown"
478 Right parentTrans -> case headNameForTransaction parentTrans graph of
479 Nothing -> show $ transactionUUID parentTrans
480 Just headName -> headName
482reprLoop :: DisconnectedTransaction -> TransactionGraph -> IO ()
483reprLoop currentTransaction@(DisconnectedTransaction currentParentUUID currentContext) graph = do
484 homeDirectory <- getHomeDirectory
485 let settings = defaultSettings {historyFile = Just (homeDirectory ++ "/.tutd_history")}
487 maybeLine <- runInputT settings $ getInputLine (promptText currentTransaction graph)
489 let roloop = reprLoop currentTransaction graph
493 newUUID <- nextRandom
494 case interpretOps newUUID currentTransaction graph line of
495 (_, _, QuitResult) -> return ()
496 (_, _, DisplayErrorResult err) -> hPutStrLn stderr ("ERR: " ++ err) >> roloop
497 (updatedTrans, updatedGraph, DisplayResult out) -> do
499 reprLoop updatedTrans updatedGraph
500 (updatedTrans, updatedGraph, QuietSuccessResult) -> do
502 reprLoop updatedTrans updatedGraph
503 (_, _, NoActionResult) -> do
504 let (value, contextup) = interpret currentContext line
505 let updatedTransaction = DisconnectedTransaction currentParentUUID contextup
507 Nothing -> reprLoop updatedTransaction graph
509 hPutStrLn stderr ("ERR:" ++ show err)
510 reprLoop currentTransaction graph
512restrictionPredicateP :: Parser RestrictionPredicateExpr
513restrictionPredicateP = buildExpressionParser predicateOperators predicateTerm
515 predicateOperators = [
516 [Prefix (reservedOp "not" >> return NotPredicate)],
517 [Infix (reservedOp "and" >> return AndPredicate) AssocLeft],
518 [Infix (reservedOp "or" >> return OrPredicate) AssocLeft]
520 predicateTerm = parens restrictionPredicateP
521 <|> try restrictionAttributeEqualityP
522 <|> try relationalBooleanExpr
525relationalBooleanExpr :: Parser RestrictionPredicateExpr
526relationalBooleanExpr = do
528 return $ RelationalExprPredicate relexpr
530restrictionAttributeEqualityP :: Parser RestrictionPredicateExpr
531restrictionAttributeEqualityP = do
532 attributeName <- identifier
534 atom <- stringAtomP <|> intAtomP
535 return $ AttributeEqualityPredicate attributeName atom
537quotedChar = noneOf "\""
538 <|> try (string "\"\"" >> return '"')
540stringAtomP = liftA StringAtom (string "\"" *> many quotedChar <* string "\"")
544 return $ IntAtom (read intstr)
547displayTransactionGraph :: TransactionGraph -> String
548displayTransactionGraph (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