packages feed

project-m36-1.2.0: src/bin/TutorialD/Printer.hs

{-# OPTIONS_GHC -fno-warn-orphans #-}
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE TypeApplications #-}
module TutorialD.Printer where
import ProjectM36.Base
import ProjectM36.Attribute as A hiding (null)
import ProjectM36.DataFrame
import ProjectM36.AccessControlList
import Prettyprinter
import Prettyprinter.Render.Text
import qualified Data.Set as S hiding (fromList)
import qualified Data.Vector as V
import qualified Data.Map.Strict as M
import Data.Time.Calendar
import Data.Time.Clock.POSIX
import qualified Data.ByteString.Base64 as B64
import qualified Data.Text.Encoding as TE
import Data.UUID hiding (null)
import Data.Text (Text)
import TutorialD.Interpreter.Base (uncapitalizedIdentifier)
import Text.Megaparsec
import Data.Either (isLeft)

renderPretty :: Pretty a => a -> Text
renderPretty = renderStrict . layoutPretty defaultLayoutOptions . pretty

instance Pretty Atom where
  pretty (IntegerAtom x) = pretty x
  pretty (IntAtom x) = "int" <> parensList [pretty x]
  pretty (ScientificAtom s) = "scientific" <> parensList [dquotes (pretty (show s))]
  pretty (DoubleAtom x) = pretty x
  pretty (TextAtom x) = dquotes (pretty x)
  pretty (DayAtom x) = "fromGregorian" <> parensList [pretty a, pretty b, pretty c]
    where
      (a,b,c) = toGregorian x
  pretty (DateTimeAtom time) = "dateTimeFromEpochSeconds" <> parensList [pretty @Integer (round (utcTimeToPOSIXSeconds time))]
  pretty (ByteStringAtom bs) = "bytestring" <> parensList [dquotes (pretty (TE.decodeUtf8 (B64.encode bs)))]
  pretty (BoolAtom x) = if x then "True" else "False"
  pretty (UUIDAtom u) = pretty u
  pretty (RelationAtom x) = pretty x
  pretty (RelationalExprAtom re) = pretty re
  pretty (SubrelationFoldAtom _rel _subAttr) = "SubrelationFoldAtom" -- this is only used as an argument to aggregate functions, so users should never be able to construct it directly
  pretty (ConstructedAtom n _ as) = pretty n <+> prettyList as

instance Pretty AtomExpr where
  pretty (AttributeAtomExpr attrName) = "@" <> prettyAttributeName attrName
  pretty (SubrelationAttributeAtomExpr relAttr subAttr) = "@" <> prettyAttributeName relAttr <> "." <> prettyAttributeName subAttr
  pretty (NakedAtomExpr atom)         = pretty atom
  pretty (FunctionAtomExpr atomFuncName' atomExprs _) = pretty atomFuncName' <> prettyAtomExprsAsArguments atomExprs
  pretty (RelationAtomExpr relExpr) = pretty relExpr
  pretty (IfThenAtomExpr ifE thenE elseE) = "if" <+> pretty ifE <+> "then" <+> pretty thenE <+> "else" <+> pretty elseE
  pretty (ConstructedAtomExpr dName [] _) = pretty dName
  pretty (ConstructedAtomExpr dName atomExprs _) = pretty dName <+> hsep (map prettyAtomExpr atomExprs)
                                           
prettyAtomExpr :: AtomExpr -> Doc ann
prettyAtomExpr atomExpr =
  case atomExpr of
    AttributeAtomExpr attrName -> "@" <> prettyAttributeName attrName
    ConstructedAtomExpr dConsName [] () -> pretty dConsName
    ConstructedAtomExpr dConsName atomExprs () -> parens (pretty dConsName <+> hsep (map prettyAtomExpr atomExprs))
    _ -> pretty atomExpr
    
prettyAtomExprsAsArguments :: [AtomExpr] -> Doc ann
prettyAtomExprsAsArguments = {-align .-} parensList . map addAt
  where addAt (atomExpr :: AtomExpr) =
          case atomExpr of
            AttributeAtomExpr attrName -> "@" <> prettyAttributeName attrName
            _ -> pretty atomExpr

nameNeedsQuoting :: StringType -> Bool
nameNeedsQuoting s =
  isLeft (parse uncapitalizedIdentifier "" s)

instance Pretty UUID where
  pretty = pretty . show

instance Pretty TupleExpr where
  pretty (TupleExpr map') = "tuple" <> bracesList (Prelude.map (\(attrName,atom)-> pretty attrName <+> pretty atom) (M.toList map'))

instance Pretty RelationTuple where
  pretty (RelationTuple attrs atoms) = "tuple" <> bracesList (zipWith (\x y-> pretty x <+> pretty y) (V.toList (attributeNames attrs)) (V.toList atoms))

instance Pretty Relation where
  pretty (Relation attrs tupSet) | attrs == mempty && null (asList tupSet) = "false"
  pretty (Relation attrs tupSet) | attrs == mempty && asList tupSet == [RelationTuple mempty mempty] = "true"
  pretty (Relation attrs tupSet) = "relation" <> prettyBracesList (A.toList attrs) <> prettyBracesList (asList tupSet)

instance Pretty Attribute where
  pretty (Attribute n aTy) = pretty n <+> pretty aTy

instance Pretty RelationalExpr where
  pretty (RelationVariable n _) = pretty n
  pretty (ExistingRelation r) = pretty r
  pretty (RelationValuedAttribute attrName) = "@" <> prettyAttributeName attrName
  pretty (NotEquals a b) = pretty' a <+> "!=" <+> pretty' b
  pretty (Equals a b) = pretty' a <+> "==" <+> pretty' b
  pretty (Project ns r) = pretty' (ignoreProjects r) <> pretty ns
  pretty (Extend ext r) = collectExtends r <> pretty ext <> "}"
  pretty (MakeRelationFromExprs Nothing (TupleExprs () tupExprs)) = "relation" <> prettyBracesList tupExprs
  pretty (MakeRelationFromExprs (Just attrExprs) (TupleExprs () tupExprs)) = "relation" <> prettyBracesList attrExprs <> prettyBracesList tupExprs
  pretty (MakeStaticRelation attrs tupSet) = "relation" <> prettyBracesList (A.toList attrs) <> prettyBracesList (asList tupSet)
  pretty (Union a b) = parens $ pretty' a <+> "union" <+> pretty' b
  pretty (Join a b) = parens $ pretty' a <+> "join" <+> pretty' b
  pretty (Rename attrs relExpr) = parens $ pretty relExpr <+> "rename" <+> prettyBracesList (map RenameTuple (S.toList attrs))
  pretty (Difference a b) = parens $ pretty' a <+> "minus" <+> pretty' b
  pretty (Group attrNames attrName relExpr) = parens $ pretty relExpr <+> "group" <+> parens (pretty attrNames <+> "as" <+> pretty attrName)
  pretty (Ungroup attrName relExpr) = parens $ pretty' relExpr <+> "ungroup" <+> pretty attrName
  pretty (Restrict resPreExpr relExpr) = parens $ pretty' relExpr <+> "where" <+> pretty resPreExpr 
  pretty (With pairs a) = "with" <+> parensList (map (\(name,expr)-> pretty name <+> "as" <+> pretty expr) pairs) <+> pretty' a

-- relvar:{a:=?}:{b:=?}:... in ADTs => relvar:{a:=?, b:=?, ...} in Doc ann
collectExtends :: RelationalExpr -> Doc ann
collectExtends (Extend ext r) = collectExtends r <> pretty ext <> ", "
collectExtends r = pretty r <> ":{" 

--relvar{a,b,c,...}{a,b,...}..{a} => relvar{a}
ignoreProjects :: RelationalExpr -> RelationalExpr
ignoreProjects (Project _ r) = ignoreProjects r
ignoreProjects r = r

prettyRelationalExpr :: RelationalExpr -> Doc n
prettyRelationalExpr (RelationVariable n _) = pretty n
prettyRelationalExpr r = parens (pretty r)

pretty' :: RelationalExpr -> Doc n
pretty' = prettyRelationalExpr

instance Pretty AttributeNames where
  pretty (AttributeNames attrNames) = bracesList (map prettyAttributeName (S.toList attrNames))
  pretty (InvertedAttributeNames attrNames) = braces $ "all but" <+> concatWith (surround ", ") (map prettyAttributeName (S.toList attrNames))
  pretty (RelationalExprAttributeNames relExpr) = braces $ "all from" <+> pretty relExpr
  pretty (UnionAttributeNames aAttrNames bAttrNames) = braces ("union of" <+> pretty aAttrNames <+> pretty bAttrNames)
  pretty (IntersectAttributeNames aAttrNames bAttrNames) = braces ("intersection of" <+> pretty aAttrNames <+> pretty bAttrNames)

instance Pretty AttributeExpr where
  pretty (NakedAttributeExpr attr) = pretty attr
  pretty (AttributeAndTypeNameExpr name typeCons _) = prettyAttributeName name <+> pretty typeCons

instance Pretty TypeConstructor where
  pretty (ADTypeConstructor tcName []) = pretty tcName
  pretty (ADTypeConstructor tcName tConsArgs) = pretty tcName <+> hsep (map pretty tConsArgs)
  pretty (PrimitiveTypeConstructor tcName atomType') = pretty tcName <+> pretty atomType'
  pretty (RelationAtomTypeConstructor attrExprs) = "relation" <> prettyBracesList attrExprs
  pretty (TypeVariable x) = pretty x

instance Pretty AtomType where
  pretty IntAtomType = "Int"
  pretty IntegerAtomType = "Integer"
  pretty ScientificAtomType = "Scientific"
  pretty DoubleAtomType = "Double"
  pretty TextAtomType = "Text"
  pretty DayAtomType = "Day"
  pretty DateTimeAtomType = "DateTime"
  pretty ByteStringAtomType = "ByteString"
  pretty BoolAtomType = "Bool"
  pretty UUIDAtomType = "UUID"
  pretty (RelationAtomType attrs) = "relation " <+> prettyBracesList (A.toList attrs)
  pretty (SubrelationFoldAtomType typ) = "SubRelationFoldAtomType" <+> pretty typ
  pretty (ConstructedAtomType tcName tvMap) = pretty tcName <+> hsep (map pretty (M.toList tvMap)) --order matters
  pretty RelationalExprAtomType = "RelationalExpr"
  pretty (TypeVariableType x) = pretty x
    
instance Pretty ExtendTupleExpr where
  pretty (AttributeExtendTupleExpr attrName atomExpr) = pretty attrName <> ":=" <> pretty atomExpr

newtype RenameTuple = RenameTuple { _unRenameTuple :: (AttributeName, AttributeName) }
  
instance Pretty RenameTuple where
  pretty (RenameTuple (n1, n2)) = pretty n1 <+> "as" <+> prettyAttributeName n2
  

instance Pretty RestrictionPredicateExpr where
  pretty TruePredicate = "true"
  pretty (AndPredicate a b) = pretty a <+> "and" <+> pretty b 
  pretty (OrPredicate a b) = pretty a <+> "or" <+> pretty b 
  pretty (NotPredicate a) = "not" <+> pretty a 
  pretty (RelationalExprPredicate relExpr) = pretty relExpr
  pretty (AtomExprPredicate atomExpr) = pretty atomExpr
  pretty (AttributeEqualityPredicate attrName atomExpr) = prettyAttributeName attrName <> "=" <> pretty atomExpr

prettyAttributeName :: AttributeName -> Doc a
prettyAttributeName attrName | nameNeedsQuoting attrName = pretty $ "`" <> attrName <> "`"
prettyAttributeName attrName = pretty attrName 

instance Pretty WithNameExpr where
  pretty (WithNameExpr name _) = pretty name

instance Pretty DataFrameExpr where
  pretty df =
    ":showdataframe" <+>
    pretty (convertExpr df) <+>
    if null (orderExprs df) then
      mempty
      else
      "orderby" <+>
      prettyBracesList (orderExprs df)
    <+> prettyOffset (offset df)
    <+> prettyLimit (limit df)
    where
      prettyOffset Nothing = mempty
      prettyOffset (Just offset') = "offset" <+> pretty (show offset')
      prettyLimit Nothing = mempty
      prettyLimit (Just limit') = "limit" <+> pretty (show limit')

instance Pretty AttributeOrderExpr where
  pretty (AttributeOrderExpr attrName order) =
    pretty attrName <+> pretty order

instance Pretty Order where
  pretty AscendingOrder = "ascending"
  pretty DescendingOrder = "descending"

instance Pretty DatabaseContextExpr where
  pretty expr =
    case expr of
      NoOperation -> mempty
      Define rvname attrExprs -> pretty rvname <+> "::" <+> bracesList (map pretty attrExprs)
      Undefine rvname -> "undefine" <+> pretty rvname
      Assign rvname relExpr -> pretty rvname <+> ":=" <+> pretty relExpr
      Insert rvname relExpr -> "insert" <+> pretty rvname <+> pretty relExpr
      Delete rvname restExpr -> "delete" <+> pretty rvname <+> "where" <+> pretty restExpr
      Update rvname attrAtomMap restExpr -> "update" <+> pretty rvname <+> "where" <+> pretty restExpr <+> pretty attrAtomMap
      AddInclusionDependency idName (InclusionDependency idA idB) ->
        "constraint" <+> pretty idName <+> pretty idA <+> "in" <+> pretty idB
      RemoveInclusionDependency idName -> "deleteconstraint" <+> pretty idName
      AddNotification notName trigger old new ->
        "notify" <+> pretty notName <+> pretty trigger <+> pretty old <+> pretty new
      RemoveNotification notName ->
        "unnotify" <+> pretty notName
      AddTypeConstructor tConsDef dConss ->
        "data" <+> pretty tConsDef <+> "=" <+> group (encloseSep "" "" "| " (pretty <$> dConss))
      RemoveTypeConstructor tConsName ->
        "undata" <+> pretty tConsName
      RemoveAtomFunction fname ->
        "removeatomfunction" <+> pretty fname
      RemoveDatabaseContextFunction fname ->
        "removedatabasecontextfunction" <+> pretty fname
      ExecuteDatabaseContextFunction fname atomExprs ->
        "execute" <+> pretty fname <> prettyParensList atomExprs
      AddRegisteredQuery rQName relExpr ->
        "registerquery" <+> pretty rQName <+> pretty relExpr
      RemoveRegisteredQuery rQName ->
        "unregisterquery" <+> pretty rQName
      AlterACL expr' -> pretty expr'
      MultipleExpr dbcExprs ->
        group (encloseSep "" "" "; " (pretty <$> dbcExprs))

prettyMayGrant :: MayGrant -> Doc ann
prettyMayGrant True = "maygrant"
prettyMayGrant False = "nogrant"

instance Pretty SomePermission where
  pretty perm =
    case perm of
      SomeRelVarPermission perm' -> pretty perm'
      SomeFunctionPermission perm' -> pretty perm'
      SomeAlterSchemaPermission perm' -> pretty perm'
      SomeAlterTransGraphPermission perm' -> pretty perm'
      SomeACLPermission perm' -> pretty perm'
      SomeDBCFunctionPermission perm' -> pretty perm'

instance Pretty RelVarPermission where
  pretty AccessRelVarsPermission = "accessrelvars"

instance Pretty FunctionPermission where
  pretty ExecuteFunctionPermission = "executefunctions"
  pretty ViewFunctionPermission = "viewfunctions"
  pretty AlterFunctionPermission = "alterfunctions"

instance Pretty AlterTransGraphPermission where
  pretty CommitTransactionPermission = "committransaction"

instance Pretty AlterSchemaPermission where
  pretty AlterSchemaPermission = "alterschema"

instance Pretty ACLPermission where
  pretty ViewACLPermission = "viewacls"
  pretty AlterACLPermission = "alteracls"

instance Pretty DBCFunctionPermission where
  pretty ViewDBCFunctionPermission = "viewfunction"
  pretty ExecuteDBCFunctionPermission = "executefunction"
  pretty AlterDBCFunctionPermission = "alterfunction"

instance Pretty AlterDBCACLExpr where
  pretty (GrantAccessExpr roleName somePerm mayGrant) =
    "grant" <+> pretty roleName <+> pretty somePerm <+> prettyMayGrant mayGrant
  pretty (RevokeAccessExpr roleName somePerm) =
    "revoke" <+> pretty roleName <+> pretty somePerm
  pretty (GrantDBCFunctionAccessExpr roleName funcName' perm mayGrant) =
    "grant dbcfunction" <+> pretty roleName <+> pretty funcName' <+> pretty perm <+> prettyMayGrant mayGrant
  pretty (RevokeDBCFunctionAccessExpr roleName funcName' perm) =
    "revoke dbcfunction" <+> pretty roleName <+> pretty funcName' <+> pretty perm

instance Pretty AttributeNameAtomExprMap where
  pretty m =
    group (encloseSep "(" ")" "," (map (\(attrName, atomExpr) -> prettyAttributeName attrName <+> ":=" <+> pretty atomExpr) (M.toList m)))
  
instance Pretty TypeConstructorDef where
  pretty (ADTypeConstructorDef tConsName tVarNames) = pretty tConsName <+> hsep (pretty <$> tVarNames)
  pretty (PrimitiveTypeConstructorDef tConsName _atomType') = pretty tConsName

instance Pretty DataConstructorDef where
  pretty (DataConstructorDef dConsName []) = pretty dConsName
  pretty (DataConstructorDef dConsName args) = "(" <+> pretty dConsName <+> hsep (pretty <$> args) <+> ")"

instance Pretty DataConstructorDefArg where
  pretty (DataConstructorDefTypeConstructorArg tCons) = pretty tCons
  pretty (DataConstructorDefTypeVarNameArg tVar) = pretty tVar
    
bracesList :: [Doc ann] -> Doc ann
bracesList = group . encloseSep (flatAlt "{ " "{") (flatAlt " }" "}") ", "

prettyBracesList :: Pretty a => [a] -> Doc ann
prettyBracesList = align . bracesList . map pretty

parensList :: [Doc ann] -> Doc ann
parensList = group . encloseSep (flatAlt "( " "(") (flatAlt " )" ")") ", "

prettyParensList :: Pretty a => [a] -> Doc ann
prettyParensList = align . parensList . map pretty