project-m36-1.2.6: src/bin/TutorialD/Interpreter/Template.hs
-- | Implements client-side TutorialD templating for use in Haskell code including parameterized replacement.
-- if I want users to use tutoriald from client libraries then it needs to be bundled in the project-m36 library OR I need to make a separate client library (less appealing)
{-# LANGUAGE FlexibleInstances, TypeApplications, StandaloneDeriving, DeriveLift, CPP, TemplateHaskell #-}
module TutorialD.Interpreter.Template where
import TutorialD.Interpreter.RelationalExpr
import TutorialD.Interpreter.DatabaseContextExpr
import TutorialD.Interpreter.TransGraphRelationalOperator
import Text.Megaparsec
import qualified Data.Text as T
import Language.Haskell.TH
import Language.Haskell.TH.Syntax
import Language.Haskell.TH.Quote
import ProjectM36.Base
import ProjectM36.Relation
import ProjectM36.Tuple
import ProjectM36.TransactionGraph
import ProjectM36.DataTypes.Primitive
import ProjectM36.AccessControlList
import Instances.TH.Lift ()
import qualified Data.Vector as V
import qualified Data.Map as M
-- older time modules do not include Lift instance
#if !MIN_VERSION_time(1,14,0)
import Data.Time.Calendar (Day, fromGregorian, toGregorian, toModifiedJulianDay)
import Data.Time.Clock (UTCTime(..), diffTimeToPicoseconds, addUTCTime, secondsToDiffTime, DiffTime, NominalDiffTime, secondsToNominalDiffTime)
import Data.Fixed (Pico)
#endif
relationalExpr :: QuasiQuoter
relationalExpr =
QuasiQuoter
{ quoteExp = parseRelationalExprQ
, quotePat = notHandled "pattern"
, quoteType = notHandled "type"
, quoteDec = notHandled "declaration"
}
where
parseRelationalExprQ tutdString =
case parse (relExprP @()) "" (T.pack tutdString) of
Left err -> fail (show err)
Right parsed -> lift parsed
databaseContextExpr :: QuasiQuoter
databaseContextExpr =
QuasiQuoter
{ quoteExp = \strIn ->
case parse databaseContextExprP "" (T.pack strIn) of
Left err -> fail (show err)
Right parsed -> lift parsed
, quotePat = notHandled "pattern"
, quoteType = notHandled "type"
, quoteDec = notHandled "declaration"
}
transGraphRelationalExpr :: QuasiQuoter
transGraphRelationalExpr =
QuasiQuoter
{ quoteExp = \strIn ->
case parse (relExprP @TransactionIdLookup) "" (T.pack strIn) of
Left err -> fail (show err)
Right parsed -> lift parsed
, quotePat = notHandled "pattern"
, quoteType = notHandled "type"
, quoteDec = notHandled "declaration"
}
deriving instance Lift a => Lift (RelationalExprBase a)
deriving instance Lift a => Lift (TupleExprsBase a)
deriving instance Lift a => Lift (TupleExprBase a)
deriving instance Lift a => Lift (AttributeExprBase a)
deriving instance Lift a => Lift (AttributeNamesBase a)
deriving instance Lift a => Lift (RestrictionPredicateExprBase a)
deriving instance Lift a => Lift (ExtendTupleExprBase a)
deriving instance Lift a => Lift (WithNameExprBase a)
deriving instance Lift a => Lift (AtomExprBase a)
deriving instance Lift RelationTupleSet
deriving instance Lift Attributes
deriving instance Lift Relation
deriving instance Lift TypeConstructor
deriving instance Lift Attribute
deriving instance Lift Atom
deriving instance Lift RelationTuple
deriving instance Lift AtomType
deriving instance (Lift a, Lift r) => Lift (DatabaseContextExprBase a r)
deriving instance Lift r => Lift (AlterDBCACLExprBase r)
deriving instance Lift TypeConstructorDef
deriving instance Lift DataConstructorDef
deriving instance Lift DataConstructorDefArg
deriving instance Lift SomePermission
deriving instance Lift RelVarPermission
deriving instance Lift DBCFunctionPermission
deriving instance Lift FunctionPermission
deriving instance Lift AlterSchemaPermission
deriving instance Lift AlterTransGraphPermission
deriving instance Lift ACLPermission
deriving instance Lift InclusionDependency
deriving instance Lift TransactionIdLookup
deriving instance Lift TransactionIdHeadBacktrack
notHandled :: String -> a
notHandled e = error $ e <> "not supported by TutorialD Quasiquoter"
class AtomReplacer a where
replace :: Atom -> Atom -> a -> a
instance AtomReplacer Atom where
replace needle replacement haystack =
case haystack of
h | h == needle -> replacement
IntegerAtom{} -> haystack
IntAtom{} -> haystack
ScientificAtom{} -> haystack
DoubleAtom{} -> haystack
TextAtom{} -> haystack
DayAtom{} -> haystack
DateTimeAtom{} -> haystack
ByteStringAtom{} -> haystack
BoolAtom{} -> haystack
UUIDAtom{} -> haystack
RelationAtom rel -> RelationAtom (replace needle replacement rel)
SubrelationFoldAtom rel attr -> SubrelationFoldAtom (replace needle replacement rel) attr
ConstructedAtom dConsName aType args ->
ConstructedAtom dConsName aType (fmap (replace needle replacement) args)
instance AtomReplacer (AtomExprBase a) where
replace needle replacement haystack =
case haystack of
AttributeAtomExpr{} -> haystack
SubrelationAttributeAtomExpr{} -> haystack
NakedAtomExpr atom -> NakedAtomExpr (replace needle replacement atom)
FunctionAtomExpr fname args marker ->
FunctionAtomExpr fname (fmap (replace needle replacement) args) marker
RelationAtomExpr relExpr ->
RelationAtomExpr (replace needle replacement relExpr)
IfThenAtomExpr if' then' else' ->
IfThenAtomExpr (replace needle replacement if') (replace needle replacement then') (replace needle replacement else')
ConstructedAtomExpr dConsName args marker ->
ConstructedAtomExpr dConsName (fmap (replace needle replacement) args) marker
-- only allow a replacement if the type does *not* change
instance AtomReplacer Relation where
replace needle replacement haystack =
if atomTypeForAtom needle /= atomTypeForAtom replacement then
error "atoms in Relation can only replaced with one of same type"
else
case relMap tupMapper haystack of
Left err -> error ("replacement rejected: " <> show err)
Right rel -> rel
where
tupMapper tup =
pure $ replace needle replacement tup
instance AtomReplacer (RelationalExprBase a) where
replace needle replacement haystack =
case haystack of
MakeRelationFromExprs attrExprs tupExprs ->
MakeRelationFromExprs attrExprs (replace needle replacement tupExprs)
MakeStaticRelation attrs tupSet ->
MakeStaticRelation attrs (replace needle replacement tupSet)
ExistingRelation rel ->
ExistingRelation (replace needle replacement rel)
RelationVariable{} -> haystack
RelationValuedAttribute{} -> haystack
Project attrNames relExpr ->
Project attrNames (replace needle replacement relExpr)
Union attrNames relExpr ->
Union attrNames (replace needle replacement relExpr)
Join relExprA relExprB ->
Join (replace needle replacement relExprA) (replace needle replacement relExprB)
Rename renames relExpr ->
Rename renames (replace needle replacement relExpr)
Difference relExprA relExprB ->
Difference (replace needle replacement relExprA) (replace needle replacement relExprB)
Group attrNames groupAttr relExpr ->
Group attrNames groupAttr (replace needle replacement relExpr)
Ungroup attrName relExpr ->
Ungroup attrName (replace needle replacement relExpr)
Restrict predExpr relExpr ->
Restrict (replace needle replacement predExpr) (replace needle replacement relExpr)
Equals relExprA relExprB ->
Equals (replace needle replacement relExprA) (replace needle replacement relExprB)
NotEquals relExprA relExprB ->
NotEquals (replace needle replacement relExprA) (replace needle replacement relExprB)
Extend extender relExpr ->
Extend (replace needle replacement extender) (replace needle replacement relExpr)
With withs relExpr ->
With (replace needle replacement withs) (replace needle replacement relExpr)
instance AtomReplacer RelationTuple where
replace needle replacement haystack =
if atomTypeForAtom needle /= atomTypeForAtom replacement then
error "atoms in Relation can only replaced with one of same type"
else
RelationTuple (tupleAttributes haystack) (V.map (replace needle replacement) (tupleAtoms haystack))
instance AtomReplacer (TupleExprsBase a) where
replace needle replacement (TupleExprs marker tupExprs) =
TupleExprs marker (fmap (replace needle replacement) tupExprs)
instance AtomReplacer RelationTupleSet where
replace needle replacement (RelationTupleSet tups) =
RelationTupleSet (fmap (replace needle replacement) tups)
instance AtomReplacer (RestrictionPredicateExprBase a) where
replace needle replacement expr =
case expr of
TruePredicate -> expr
AndPredicate a b ->
AndPredicate (replace needle replacement a) (replace needle replacement b)
OrPredicate a b ->
OrPredicate (replace needle replacement a) (replace needle replacement b)
NotPredicate a ->
NotPredicate (replace needle replacement a)
AtomExprPredicate expr ->
AtomExprPredicate (replace needle replacement expr)
AttributeEqualityPredicate attrName atomExpr ->
AttributeEqualityPredicate attrName (replace needle replacement atomExpr)
instance AtomReplacer (ExtendTupleExprBase a) where
replace needle replacement (AttributeExtendTupleExpr attrName expr) =
AttributeExtendTupleExpr attrName (replace needle replacement expr)
instance AtomReplacer (WithNamesAssocsBase a) where
replace needle replacement assocs =
fmap (\(name, relExpr) -> (name, replace needle replacement relExpr)) assocs
instance AtomReplacer (TupleExprBase a) where
replace needle replacement (TupleExpr tupMap) =
TupleExpr (M.map (\atomExpr -> replace needle replacement atomExpr) tupMap)
instance AtomReplacer (DatabaseContextExprBase a r) where
replace needle replacement expr =
case expr of
NoOperation -> expr
Define{} -> expr
Undefine{} -> expr
Assign rv relExpr ->
Assign rv (replace needle replacement relExpr)
Insert rv relExpr ->
Insert rv (replace needle replacement relExpr)
Delete rv predExpr ->
Delete rv (replace needle replacement predExpr)
Update rv attrAtomMap predExpr ->
Update rv (M.map (replace needle replacement) attrAtomMap) (replace needle replacement predExpr)
AddInclusionDependency nam incDep ->
AddInclusionDependency nam (replace needle replacement incDep)
RemoveInclusionDependency{} -> expr
AddNotification nam expr1 expr2 expr3 ->
AddNotification nam (replace needle replacement expr1) (replace needle replacement expr2) (replace needle replacement expr3)
RemoveNotification{} -> expr
AddTypeConstructor{} -> expr
RemoveTypeConstructor{} -> expr
RemoveAtomFunction{} -> expr
RemoveDatabaseContextFunction{} -> expr
ExecuteDatabaseContextFunction fname args ->
ExecuteDatabaseContextFunction fname (map (replace needle replacement) args)
AddRegisteredQuery qName relExpr ->
AddRegisteredQuery qName (replace needle replacement relExpr)
RemoveRegisteredQuery{} -> expr
AlterACL{} -> expr
MultipleExpr exprs ->
MultipleExpr (map (replace needle replacement) exprs)
instance AtomReplacer InclusionDependency where
replace needle replacement (InclusionDependency exprA exprB) =
InclusionDependency (replace needle replacement exprA) (replace needle replacement exprB)
replaceTextAtom :: AtomReplacer a => T.Text -> T.Text -> a -> a
replaceTextAtom needle replacement haystack =
replace (TextAtom needle) (TextAtom replacement) haystack
replaceIntegerAtom :: AtomReplacer a => Integer -> Integer -> a -> a
replaceIntegerAtom needle replacement haystack =
replace (IntegerAtom needle) (IntegerAtom replacement) haystack
replaceAtoms :: AtomReplacer a => [(Atom, Atom)] -> a -> a
replaceAtoms assocs haystack =
foldr folder haystack assocs
where
folder (needle, replacement) acc =
replace needle replacement acc