1module TutorialD.Interpreter.RelationalExpr where
5import Text.Parsec.String
6import TutorialD.Interpreter.Base
7import qualified Data.Text as T
8import qualified ProjectM36.Attribute as A
9import ProjectM36.TupleSet
10import ProjectM36.Tuple
11import ProjectM36.Relation
13import qualified Data.Vector as V
14import qualified Data.Set as S
15import Data.Functor.Identity (Identity)
16import Data.Time.Format
17import Control.Applicative (liftA)
18import Data.ByteString.Base64 as B64
19import Data.Text.Encoding as TE
20import TutorialD.Interpreter.DataTypes.DateTime
21import TutorialD.Interpreter.DataTypes.Interval
23import Data.Time.Calendar (Day)
25atomTypeP :: Parser AtomType
26atomTypeP = (reserved "char" *> return stringAtomType) <|>
27 (reserved "int" *> return intAtomType) <|>
28 (reserved "datetime" *> return dateTimeAtomType) <|>
29 (reserved "date" *> return dateAtomType) <|>
30 (reserved "double" *> return doubleAtomType) <|>
31 (reserved "bool" *> return boolAtomType) <|>
32 (reserved "bytestring" *> return byteStringAtomType) <|>
33 (RelationAtomType <$> (reserved "relation" *> makeAttributesP))
36attributeListP :: Parser AttributeNames
38 but <- try (string "all but " <* whiteSpace) <|> string ""
39 let constructor = if but == "" then AttributeNames else InvertedAttributeNames
40 attrs <- sepBy identifier comma
41 return $ constructor (S.fromList (map T.pack attrs))
43makeRelationP :: Parser RelationalExpr
44makeRelationP = ExistingRelation <$> relationP
46--used in relation creation
47makeAttributesP :: Parser Attributes
49 attrList <- braces (sepBy attributeAndTypeP comma)
50 return $ A.attributesFromList attrList
52attributeAndTypeP :: Parser Attribute
54 attrName <- identifier
55 --convert type name into type
57 return $ Attribute (T.pack attrName) atomType
59tupleP :: Parser RelationTuple
62 attrAssocs <- braces (sepBy tupleAtomP comma)
63 let attrs = A.attributesFromList $ map (\(attrName, atom) -> Attribute attrName (atomTypeForAtom atom)) attrAssocs
64 let atoms = V.fromList $ map snd attrAssocs
65 return $ mkRelationTuple attrs atoms
67tupleAtomP :: Parser (AttributeName, Atom)
69 attributeName <- identifier
71 return $ (T.pack attributeName, atom)
73projectP :: Parser (RelationalExpr -> RelationalExpr)
75 attrs <- braces attributeListP
76 return $ Project attrs
78renameClauseP :: Parser (String, String)
83 return $ (oldAttr, newAttr)
85renameP :: Parser (RelationalExpr -> RelationalExpr)
88 (oldAttr, newAttr) <- braces renameClauseP
89 return $ Rename (T.pack oldAttr) (T.pack newAttr)
91whereClauseP :: Parser (RelationalExpr -> RelationalExpr)
94 boolExpr <- restrictionPredicateP
95 return $ Restrict boolExpr
97groupClauseP :: Parser (AttributeNames, String)
99 attrs <- braces attributeListP
101 newAttrName <- identifier
102 return $ (attrs, newAttrName)
104groupP :: Parser (RelationalExpr -> RelationalExpr)
107 (groupAttrList, groupAttrName) <- parens groupClauseP
108 return $ Group groupAttrList (T.pack groupAttrName)
110--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.
111ungroupP :: Parser (RelationalExpr -> RelationalExpr)
114 rvaAttrName <- identifier
115 return $ Ungroup (T.pack rvaAttrName)
117extendP :: Parser (RelationalExpr -> RelationalExpr)
120 tupleExpr <- braces tupleExpressionP
121 return $ Extend tupleExpr
124rewrite summarize as extend as evaluate
125it turns out that summarize must be a relational expression primitive because it requires
127summarize SP per (SP{SNO}) add (sum(@QTY) as SUMQ) === ((SP rename {QTY as _}) group ({_} as x)) : {QTY:=sum(@x)}{S#,QTY}
129summarize rX per (relExpr) add (func(@attr) as funcd) === ((rX rename {attr as _}) group ({_} as _)) : {QTY:=sum(@_)}{relExpr, _}
132summarizeP :: Parser (RelationalExpr -> RelationalExpr)
135 -- "summarize in Tutorial D requires the heading of the per relation to be a subset of that of the relation to be summarized"
136 perExpr <- parens relExpr
138 (funcAtomExpr, newAttrName) <- parens summaryP
139 --rewrite summarize as extend
140 --return $ Summarize funcAtomExpr newAttrName perExpr
142summaryP :: Parser (AtomExpr, AttributeName)
146 attrName <- identifier
147 return (expr, T.pack attrName)
150relOperators :: [[Operator String () Identity RelationalExpr]]
154 [Postfix whereClauseP],
157 [Infix (reservedOp "join" >> return Join) AssocLeft],
158 [Infix (reservedOp "union" >> return Union) AssocLeft],
159 [Infix (reservedOp "=" >> return Equals) AssocNone],
163relExprP :: Parser RelationalExpr
164relExprP = buildExpressionParser relOperators relTerm
166relVarP :: Parser RelationalExpr
167relVarP = liftA (RelationVariable . T.pack) identifier
169relTerm :: Parser RelationalExpr
170relTerm = parens relExprP
174restrictionPredicateP :: Parser RestrictionPredicateExpr
175restrictionPredicateP = buildExpressionParser predicateOperators predicateTerm
177 predicateOperators = [
178 [Prefix (reservedOp "not" >> return NotPredicate)],
179 [Infix (reservedOp "and" >> return AndPredicate) AssocLeft],
180 [Infix (reservedOp "or" >> return OrPredicate) AssocLeft]
182 predicateTerm = parens restrictionPredicateP
183 <|> try restrictionAtomExprP
184 <|> try restrictionAttributeEqualityP
185 <|> try relationalBooleanExprP
188relationalBooleanExprP :: Parser RestrictionPredicateExpr
189relationalBooleanExprP = do
191 return $ RelationalExprPredicate relexpr
193restrictionAttributeEqualityP :: Parser RestrictionPredicateExpr
194restrictionAttributeEqualityP = do
195 attributeName <- identifier
197 atomexpr <- atomExprP
198 return $ AttributeEqualityPredicate (T.pack attributeName) atomexpr
200restrictionAtomExprP :: Parser RestrictionPredicateExpr --atoms which are of type "boolean"
201restrictionAtomExprP = do
202 _ <- char '^' -- not ideal, but allows me to continue to use a context-free grammar
203 AtomExprPredicate <$> atomExprP
205multiTupleExpressionP :: Parser [TupleExpr]
206multiTupleExpressionP = sepBy tupleExpressionP comma
208tupleExpressionP :: Parser TupleExpr
209tupleExpressionP = attributeTupleExpressionP
211attributeTupleExpressionP :: Parser TupleExpr
212attributeTupleExpressionP = do
213 newAttr <- identifier
216 return $ AttributeTupleExpr (T.pack newAttr) atom
218atomExprP :: Parser AtomExpr
219atomExprP = try functionAtomExprP <|>
220 attributeAtomExprP <|>
224attributeAtomExprP :: Parser AtomExpr
225attributeAtomExprP = do
227 attrName <- identifier
228 return $ AttributeAtomExpr (T.pack attrName)
230nakedAtomExprP :: Parser AtomExpr
231nakedAtomExprP = NakedAtomExpr <$> atomP
234atomP = dateTimeAtomP <|>
235 intervalDateTimeAtomP <|>
246functionAtomExprP :: Parser AtomExpr
247functionAtomExprP = do
248 funcName <- identifier
249 argList <- parens (sepBy atomExprP comma)
250 return $ FunctionAtomExpr (T.pack funcName) argList
252relationalAtomExprP :: Parser AtomExpr
253relationalAtomExprP = RelationAtomExpr <$> relExprP
255stringAtomP :: Parser Atom
256stringAtomP = liftA (Atom . T.pack) quotedString
258--until polymorphic type constructors and algebraic data types are properly supported, such constructs must be unfortunately hard-coded
259maybeTextAtomP :: Parser Atom
261 maybeText <- try $ ((Just . T.pack <$> (reserved "Just" *> quotedString)) <|>
262 (reserved "Nothing" *> return Nothing)) <* reserved "::maybe char"
263 return $ Atom maybeText
265maybeIntAtomP :: Parser Atom
267 maybeInt <- try $ ((Just . fromIntegral <$> (reserved "Just" *> integer)) <|>
268 (reserved "Nothing" *> return Nothing)) <* reserved "::maybe int"
269 return $ Atom (maybeInt :: Maybe Int)
271dateAtomP :: Parser Atom
273 dateString' <- try $ do
274 dateString <- quotedString
277 case parseTimeM False defaultTimeLocale "%Y-%m-%d" dateString' of
278 Just todaytime -> return $ Atom (todaytime :: Day)
279 Nothing -> fail "Failed to parse date"
281doubleAtomP :: Parser Atom
282doubleAtomP = Atom <$> (try float)
284intAtomP :: Parser Atom
287 return $ Atom ((fromIntegral i) :: Int)
289boolAtomP :: Parser Atom
291 val <- char 't' <|> char 'f'
292 return $ Atom (val == 't')
294relationAtomP :: Parser Atom
295relationAtomP = Atom <$> relationP
297byteStringAtomP :: Parser Atom
299 byteString' <- try $ do
300 byteString <- quotedString
301 reserved "::bytestring"
303 case B64.decode $ TE.encodeUtf8 (T.pack byteString') of
305 Right bsVal -> return $ Atom bsVal
307--relation constructor -- no choice but to propagate relation-construction errors as parse errors. Perhaps this could be improved in the future
308relationP :: Parser Relation
311 attrs <- (try makeAttributesP <|> return A.emptyAttributes)
312 if not (A.null attrs) then do
313 case mkRelation attrs emptyTupleSet of
314 Left err -> fail (show err)
315 Right rel -> return rel
317 tuples <- braces (sepBy tupleP comma)
318 case mkRelationFromTuples (tupleAttributes (head tuples)) tuples of
319 Left err -> fail (show err)
320 Right rel -> return rel
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