@@ -0,0 +1,277 @@
1+{-# LANGUAGE TypeSynonymInstances, FlexibleInstances #-}
2+module TutorialD.Interpreter.TransGraphRelationalOperator where
3+import ProjectM36.Base
4+import ProjectM36.TransGraphRelationalExpression
5+import ProjectM36.TransactionGraph
6+import ProjectM36.MiscUtils
7+import qualified ProjectM36.Client as C
8+
9+import TutorialD.Interpreter.Base
10+import TutorialD.Interpreter.Types
11+
12+import Text.Megaparsec
13+import Text.Megaparsec.Text
14+import Text.Megaparsec.Expr
15+
16+import Control.Applicative
17+import qualified Data.Set as S
18+import qualified Data.Map as M
19+import qualified Data.Text as T
20+import Data.List (sort)
21+
22+class RelationalMarkerExpr a where
23+ parseMarkerP :: Parser a
24+
25+instance RelationalMarkerExpr () where
26+ parseMarkerP = pure ()
27+
28+instance RelationalMarkerExpr TransactionIdLookup where
29+ parseMarkerP = string "@" *> transactionIdLookupP
30+
31+data TransGraphRelationalOperator = ShowTransGraphRelation TransGraphRelationalExpr
32+
33+transactionIdLookupP :: Parser TransactionIdLookup
34+transactionIdLookupP = (TransactionIdLookup <$> uuidP) <|>
35+ (TransactionIdHeadNameLookup <$> identifier)
36+
37+transGraphRelationalOpP :: Parser TransGraphRelationalOperator
38+transGraphRelationalOpP = showTransGraphRelationalOpP
39+
40+showTransGraphRelationalOpP :: Parser TransGraphRelationalOperator
41+showTransGraphRelationalOpP = do
42+ reservedOp ":showtransgraphexpr"
43+ ShowTransGraphRelation <$> relExprP
44+
45+--used in projection
46+attributeListP :: Parser AttributeNames
47+attributeListP = do
48+ but <- try (string "all but " <* spaceConsumer) <|> string ""
49+ let constructor = if but == "" then AttributeNames else InvertedAttributeNames
50+ attrs <- sepBy identifier comma
51+ pure $ constructor (S.fromList attrs)
52+
53+makeRelationP :: RelationalMarkerExpr a => Parser (RelationalExprBase a)
54+makeRelationP = do
55+ reserved "relation"
56+ attrExprs <- try (liftA Just makeAttributeExprsP) <|> pure Nothing
57+ tupleExprs <- braces (sepBy tupleExprP comma) <|> pure []
58+ pure $ MakeRelationFromExprs attrExprs tupleExprs
59+
60+--used in relation creation
61+makeAttributeExprsP :: RelationalMarkerExpr a => Parser [AttributeExprBase a]
62+makeAttributeExprsP = braces (sepBy attributeAndTypeNameP comma)
63+
64+attributeAndTypeNameP :: RelationalMarkerExpr a => Parser (AttributeExprBase a)
65+attributeAndTypeNameP = AttributeAndTypeNameExpr <$> identifier <*> typeConstructorP <*> parseMarkerP
66+
67+--abstract data type parser- in this context, the type constructor must not include any type arguments
68+--Either Text Int
69+adTypeConstructorP :: Parser TypeConstructor
70+adTypeConstructorP = do
71+ tConsName <- capitalizedIdentifier
72+ tConsArgs <- many typeConstructorArgP
73+ pure $ ADTypeConstructor tConsName tConsArgs
74+
75+tupleExprP :: RelationalMarkerExpr a => Parser (TupleExprBase a)
76+tupleExprP = do
77+ reservedOp "tuple"
78+ attrAssocs <- braces (sepBy tupleAtomExprP comma)
79+ --detect duplicate attribute names
80+ let dupAttrNames = dupes (sort (map fst attrAssocs))
81+ if length dupAttrNames /= 0 then
82+ fail ("Attribute names duplicated: " ++ show dupAttrNames)
83+ else
84+ pure (TupleExpr (M.fromList attrAssocs))
85+
86+tupleAtomExprP :: RelationalMarkerExpr a => Parser (AttributeName, AtomExprBase a)
87+tupleAtomExprP = do
88+ attributeName <- identifier
89+ atomExpr <- atomExprP
90+ pure $ (attributeName, atomExpr)
91+
92+projectP :: RelationalMarkerExpr a => Parser (RelationalExprBase a -> RelationalExprBase a)
93+projectP = do
94+ attrs <- braces attributeListP
95+ pure $ Project attrs
96+
97+renameClauseP :: Parser (T.Text, T.Text)
98+renameClauseP = do
99+ oldAttr <- identifier
100+ reservedOp "as"
101+ newAttr <- identifier
102+ pure $ (oldAttr, newAttr)
103+
104+renameP :: RelationalMarkerExpr a => Parser (RelationalExprBase a -> RelationalExprBase a)
105+renameP = do
106+ reservedOp "rename"
107+ (oldAttr, newAttr) <- braces renameClauseP
108+ return $ Rename oldAttr newAttr
109+
110+whereClauseP :: RelationalMarkerExpr a => Parser (RelationalExprBase a -> RelationalExprBase a)
111+whereClauseP = reservedOp "where" *> (Restrict <$> restrictionPredicateP)
112+
113+groupClauseP :: Parser (AttributeNames, T.Text)
114+groupClauseP = do
115+ attrs <- braces attributeListP
116+ reservedOp "as"
117+ newAttrName <- identifier
118+ return $ (attrs, newAttrName)
119+
120+groupP :: RelationalMarkerExpr a => Parser (RelationalExprBase a -> RelationalExprBase a)
121+groupP = do
122+ reservedOp "group"
123+ (groupAttrList, groupAttrName) <- parens groupClauseP
124+ pure $ Group groupAttrList groupAttrName
125+
126+--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.
127+ungroupP :: RelationalMarkerExpr a => Parser (RelationalExprBase a -> RelationalExprBase a)
128+ungroupP = do
129+ reservedOp "ungroup"
130+ rvaAttrName <- identifier
131+ pure $ Ungroup rvaAttrName
132+
133+extendP :: RelationalMarkerExpr a => Parser (RelationalExprBase a -> RelationalExprBase a)
134+extendP = do
135+ reservedOp ":"
136+ tupleExpr <- braces extendTupleExpressionP
137+ return $ Extend tupleExpr
138+
139+relOperators :: RelationalMarkerExpr a => [[Operator Parser (RelationalExprBase a)]]
140+relOperators = [
141+ [Postfix projectP],
142+ [Postfix renameP],
143+ [Postfix whereClauseP],
144+ [Postfix groupP],
145+ [Postfix ungroupP],
146+ [InfixL (reservedOp "join" >> return Join)],
147+ [InfixL (reservedOp "union" >> return Union)],
148+ [InfixL (reservedOp "minus" >> return Difference)],
149+ [InfixN (reservedOp "=" >> return Equals)],
150+ [Postfix extendP]
151+ ]
152+
153+relExprP :: RelationalMarkerExpr a => Parser (RelationalExprBase a)
154+relExprP = makeExprParser relTerm relOperators
155+
156+relVarP :: RelationalMarkerExpr a => Parser (RelationalExprBase a)
157+relVarP = RelationVariable <$> identifier <*> parseMarkerP
158+
159+relTerm :: RelationalMarkerExpr a => Parser (RelationalExprBase a)
160+relTerm = parens relExprP
161+ <|> makeRelationP
162+ <|> relVarP
163+
164+restrictionPredicateP :: RelationalMarkerExpr a => Parser (RestrictionPredicateExprBase a)
165+restrictionPredicateP = makeExprParser predicateTerm predicateOperators
166+ where
167+ predicateOperators = [
168+ [Prefix (reservedOp "not" >> return NotPredicate)],
169+ [InfixL (reservedOp "and" >> return AndPredicate)],
170+ [InfixL (reservedOp "or" >> return OrPredicate)]
171+ ]
172+ predicateTerm = parens restrictionPredicateP
173+ <|> try restrictionAtomExprP
174+ <|> try restrictionAttributeEqualityP
175+ <|> try relationalBooleanExprP
176+
177+
178+relationalBooleanExprP :: RelationalMarkerExpr a => Parser (RestrictionPredicateExprBase a)
179+relationalBooleanExprP = do
180+ relexpr <- relExprP
181+ return $ RelationalExprPredicate relexpr
182+
183+restrictionAttributeEqualityP :: RelationalMarkerExpr a => Parser (RestrictionPredicateExprBase a)
184+restrictionAttributeEqualityP = do
185+ attributeName <- identifier
186+ reservedOp "="
187+ atomexpr <- atomExprP
188+ return $ AttributeEqualityPredicate attributeName atomexpr
189+
190+restrictionAtomExprP :: RelationalMarkerExpr a=> Parser (RestrictionPredicateExprBase a) --atoms which are of type "boolean"
191+restrictionAtomExprP = do
192+ _ <- char '^' -- not ideal, but allows me to continue to use a context-free grammar
193+ AtomExprPredicate <$> atomExprP
194+
195+multiTupleExpressionP :: RelationalMarkerExpr a => Parser [ExtendTupleExprBase a]
196+multiTupleExpressionP = sepBy extendTupleExpressionP comma
197+
198+extendTupleExpressionP :: RelationalMarkerExpr a => Parser (ExtendTupleExprBase a)
199+extendTupleExpressionP = attributeExtendTupleExpressionP
200+
201+attributeExtendTupleExpressionP :: RelationalMarkerExpr a => Parser (ExtendTupleExprBase a)
202+attributeExtendTupleExpressionP = do
203+ newAttr <- identifier
204+ reservedOp ":="
205+ atom <- atomExprP
206+ return $ AttributeExtendTupleExpr newAttr atom
207+
208+atomExprP :: RelationalMarkerExpr a => Parser (AtomExprBase a)
209+atomExprP = consumeAtomExprP True
210+
211+consumeAtomExprP :: RelationalMarkerExpr a => Bool -> Parser (AtomExprBase a)
212+consumeAtomExprP consume = try functionAtomExprP <|>
213+ try (parens (constructedAtomExprP True)) <|>
214+ constructedAtomExprP consume <|>
215+ attributeAtomExprP <|>
216+ nakedAtomExprP <|>
217+ relationalAtomExprP
218+
219+attributeAtomExprP :: RelationalMarkerExpr a => Parser (AtomExprBase a)
220+attributeAtomExprP = do
221+ _ <- string "@"
222+ attrName <- identifier
223+ return $ AttributeAtomExpr attrName
224+
225+nakedAtomExprP :: RelationalMarkerExpr a => Parser (AtomExprBase a)
226+nakedAtomExprP = NakedAtomExpr <$> atomP
227+
228+constructedAtomExprP :: RelationalMarkerExpr a => Bool -> Parser (AtomExprBase a)
229+constructedAtomExprP consume = do
230+ dConsName <- capitalizedIdentifier
231+ dConsArgs <- if consume then sepBy (consumeAtomExprP False) spaceConsumer else pure []
232+ marker <- parseMarkerP
233+ pure $ ConstructedAtomExpr dConsName dConsArgs marker
234+
235+-- used only for primitive type parsing ?
236+atomP :: Parser Atom
237+atomP = stringAtomP <|>
238+ doubleAtomP <|>
239+ intAtomP <|>
240+ boolAtomP
241+
242+functionAtomExprP :: RelationalMarkerExpr a => Parser (AtomExprBase a)
243+functionAtomExprP = do
244+ funcName <- identifier
245+ argList <- parens (sepBy atomExprP comma)
246+ marker <- parseMarkerP
247+ return $ FunctionAtomExpr funcName argList marker
248+
249+relationalAtomExprP :: RelationalMarkerExpr a => Parser (AtomExprBase a)
250+relationalAtomExprP = RelationAtomExpr <$> relExprP
251+
252+stringAtomP :: Parser Atom
253+stringAtomP = liftA TextAtom quotedString
254+
255+doubleAtomP :: Parser Atom
256+doubleAtomP = DoubleAtom <$> (try float)
257+
258+intAtomP :: Parser Atom
259+intAtomP = do
260+ i <- integer
261+ return $ IntAtom (fromIntegral i)
262+
263+boolAtomP :: Parser Atom
264+boolAtomP = do
265+ val <- char 't' <|> char 'f'
266+ return $ BoolAtom (val == 't')
267+
268+relationAtomExprP :: RelationalMarkerExpr a => Parser (AtomExprBase a)
269+relationAtomExprP = RelationAtomExpr <$> makeRelationP
270+
271+evalTransGraphRelationalOp :: C.SessionId -> C.Connection -> TransGraphRelationalOperator -> IO TutorialDOperatorResult
272+evalTransGraphRelationalOp sessionId conn (ShowTransGraphRelation expr) = do
273+ res <- C.executeTransGraphRelationalExpr sessionId conn expr
274+ case res of
275+ Left err -> pure $ DisplayErrorResult $ T.pack (show err)
276+ Right rel -> pure $ DisplayRelationResult rel
277+