From a890f50ce5ce0810c50bd6632dfd155379efd312 Mon Sep 17 00:00:00 2001 From: AgentM Date: Sun, 5 Dec 2021 22:35:52 -0500 Subject: [PATCH] add template haskell parsers for RelationalExpr and DatabaseContextExpr resolves #288 --- project-m36.cabal | 10 ++-- src/lib/ProjectM36/Base.hs | 78 +++++++++++++++++++------------ test/TutorialD/InterpreterTest.hs | 17 ++++++- 3 files changed, 71 insertions(+), 34 deletions(-) diff --git a/project-m36.cabal b/project-m36.cabal index 83d8c2d9..5de75842 100644 --- a/project-m36.cabal +++ b/project-m36.cabal @@ -35,7 +35,7 @@ Flag haskell-scripting Default: True Library - Build-Depends: base>=4.8 && < 4.15, ghc-paths, mtl, containers, unordered-containers, hashable, haskeline, directory, MonadRandom, random-shuffle, uuid >= 1.3.12, cassava >= 0.4.5.1 && < 0.6, text, bytestring, deepseq, deepseq-generics, vector, parallel, monad-parallel, exceptions, transformers, gnuplot, filepath, zlib, directory, temporary, stm, time, hashable-time, old-locale, rset, attoparsec, either, base64-bytestring, data-interval, extended-reals, aeson >= 1.1, path-pieces, conduit, resourcet, http-api-data, semigroups, QuickCheck, quickcheck-instances, list-t, stm-containers >= 0.2.15, foldl, optparse-applicative, Glob, cryptohash-sha256, text-manipulate >= 0.2.0.1 && < 0.4, winery, curryer-rpc, network, async, vector-instances, recursion-schemes, streamly >= 0.7.2, convertible + Build-Depends: base>=4.8 && < 4.15, ghc-paths, mtl, containers, unordered-containers, hashable, haskeline, directory, MonadRandom, random-shuffle, uuid >= 1.3.12, cassava >= 0.4.5.1 && < 0.6, text, bytestring, deepseq, deepseq-generics, vector, parallel, monad-parallel, exceptions, transformers, gnuplot, filepath, zlib, directory, temporary, stm, time, hashable-time, old-locale, rset, attoparsec, either, base64-bytestring, data-interval, extended-reals, aeson >= 1.1, path-pieces, conduit, resourcet, http-api-data, semigroups, QuickCheck, quickcheck-instances, list-t, stm-containers >= 0.2.15, foldl, optparse-applicative, Glob, cryptohash-sha256, text-manipulate >= 0.2.0.1 && < 0.4, winery, curryer-rpc, network, async, vector-instances, recursion-schemes, streamly >= 0.7.2, convertible, template-haskell, th-lift-instances if flag(haskell-scripting) Build-Depends: ghc >= 8.2 && < 8.11 CPP-Options: -DPM36_HASKELL_SCRIPTING @@ -192,7 +192,8 @@ Executable tutd base16-bytestring >= 1.0.0.0, http-conduit, modern-uri, - http-types + http-types, + template-haskell Other-Modules: TutorialD.Interpreter, TutorialD.Interpreter.Base, TutorialD.Interpreter.DatabaseContextExpr, @@ -212,6 +213,7 @@ Executable tutd TutorialD.Interpreter.TransGraphRelationalOperator, TutorialD.Interpreter.SchemaOperator TutorialD.Printer + TutorialD.Interpreter.TH main-is: TutorialD/tutd.hs CC-Options: -fPIC if os(windows) @@ -310,8 +312,8 @@ Test-Suite test-tutoriald import: commontest type: exitcode-stdio-1.0 main-is: TutorialD/InterpreterTest.hs - Other-Modules: TutorialD.Interpreter, TutorialD.Interpreter.Base, TutorialD.Interpreter.Export.Base, TutorialD.Interpreter.Export.CSV, TutorialD.Interpreter.Import.Base, TutorialD.Interpreter.Import.CSV, TutorialD.Interpreter.Import.TutorialD, TutorialD.Interpreter.RODatabaseContextOperator, TutorialD.Interpreter.RelationalExpr, TutorialD.Interpreter.TransactionGraphOperator, TutorialD.Interpreter.Types, TutorialD.Interpreter.DatabaseContextExpr, TutorialD.Interpreter.InformationOperator, TutorialD.Interpreter.Import.BasicExamples, TutorialD.Interpreter.DatabaseContextIOOperator, TutorialD.Interpreter.TestBase, TutorialD.Interpreter.TransGraphRelationalOperator, TutorialD.Interpreter.SchemaOperator, TutorialD.Printer - Build-Depends: base, HUnit, Cabal, containers, hashable, unordered-containers, mtl, vector, time, hashable-time, bytestring, uuid, stm, deepseq, deepseq-generics, parallel, cassava, attoparsec, gnuplot, directory, temporary, haskeline, megaparsec, text, base64-bytestring, data-interval, filepath, stm-containers, list-t, project-m36, random, MonadRandom, semigroups, parser-combinators, prettyprinter + Other-Modules: TutorialD.Interpreter, TutorialD.Interpreter.Base, TutorialD.Interpreter.Export.Base, TutorialD.Interpreter.Export.CSV, TutorialD.Interpreter.Import.Base, TutorialD.Interpreter.Import.CSV, TutorialD.Interpreter.Import.TutorialD, TutorialD.Interpreter.RODatabaseContextOperator, TutorialD.Interpreter.RelationalExpr, TutorialD.Interpreter.TransactionGraphOperator, TutorialD.Interpreter.Types, TutorialD.Interpreter.DatabaseContextExpr, TutorialD.Interpreter.InformationOperator, TutorialD.Interpreter.Import.BasicExamples, TutorialD.Interpreter.DatabaseContextIOOperator, TutorialD.Interpreter.TestBase, TutorialD.Interpreter.TransGraphRelationalOperator, TutorialD.Interpreter.SchemaOperator, TutorialD.Printer, TutorialD.Interpreter.TH + Build-Depends: base, HUnit, Cabal, containers, hashable, unordered-containers, mtl, vector, time, hashable-time, bytestring, uuid, stm, deepseq, deepseq-generics, parallel, cassava, attoparsec, gnuplot, directory, temporary, haskeline, megaparsec, text, base64-bytestring, data-interval, filepath, stm-containers, list-t, project-m36, random, MonadRandom, semigroups, parser-combinators, prettyprinter, template-haskell Test-Suite test-tutoriald-atomfunctionscript import: commontest diff --git a/src/lib/ProjectM36/Base.hs b/src/lib/ProjectM36/Base.hs index 0f17d9d3..1886cb00 100644 --- a/src/lib/ProjectM36/Base.hs +++ b/src/lib/ProjectM36/Base.hs @@ -1,4 +1,4 @@ -{-# LANGUAGE ExistentialQuantification,DeriveGeneric,DeriveAnyClass,FlexibleInstances,OverloadedStrings, DeriveTraversable, DerivingVia, TemplateHaskell, TypeFamilies #-} +{-# LANGUAGE ExistentialQuantification,DeriveGeneric,DeriveAnyClass,FlexibleInstances,OverloadedStrings, DeriveTraversable, DerivingVia, TemplateHaskell, TypeFamilies, DeriveLift #-} {-# OPTIONS_GHC -fno-warn-orphans #-} module ProjectM36.Base where @@ -26,6 +26,8 @@ import Data.Typeable import Data.ByteString (ByteString) import qualified Data.List.NonEmpty as NE import Data.Vector.Instances () +import Language.Haskell.TH.Syntax (Lift(..), Exp(..), mkName, unsafeTExpCoerce) +import Instances.TH.Lift () type StringType = Text @@ -56,7 +58,24 @@ data Atom = IntegerAtom Integer | RelationAtom Relation | RelationalExprAtom RelationalExpr | --used for returning inc deps ConstructedAtom DataConstructorName AtomType [Atom] - deriving (Eq, Show, Typeable, NFData, Generic, Read) + deriving (Eq, Show, Typeable, NFData, Generic, Read, Lift) + +-- lift instances missing from th-lift-instances +instance Lift UTCTime where + lift (UTCTime x y) = do + x' <- lift x + y' <- lift y + return $ AppE (AppE (ConE (mkName "UTCTime")) x') y' + liftTyped = unsafeTExpCoerce . lift + + +instance Lift Day where + lift x = [|toEnum $(lift (fromEnum x))|] + liftTyped = unsafeTExpCoerce . lift + +instance Lift DiffTime where + lift x = [|toEnum $(lift (fromEnum x))|] + liftTyped = unsafeTExpCoerce . lift instance Hashable Atom where hashWithSalt salt (ConstructedAtom dConsName _ atoms) = salt `hashWithSalt` atoms @@ -89,7 +108,7 @@ data AtomType = IntAtomType | RelationalExprAtomType | TypeVariableType TypeVarName --wildcard used in Atom Functions and tuples for data constructors which don't provide all arguments to the type constructor - deriving (Eq, NFData, Generic, Show, Read, Hashable) + deriving (Eq, NFData, Generic, Show, Read, Hashable, Lift) instance Ord AtomType where compare = undefined @@ -106,7 +125,7 @@ isRelationAtomType _ = False type AttributeName = StringType -- | A relation's type is composed of attribute names and types. -data Attribute = Attribute AttributeName AtomType deriving (Eq, Show, Read, Generic, NFData) +data Attribute = Attribute AttributeName AtomType deriving (Eq, Show, Read, Generic, NFData, Lift) instance Hashable Attribute where hashWithSalt salt (Attribute attrName _) = hashWithSalt salt attrName @@ -118,7 +137,7 @@ newtype Attributes = Attributes { attributesVec :: V.Vector Attribute --,attributesSet :: HS.HashSet Attribute --compare with this generated in heap profile and benchmarks } - deriving (NFData, Read, Hashable, Generic) + deriving (NFData, Read, Hashable, Generic, Lift) attributesSet :: Attributes -> HS.HashSet Attribute attributesSet = HS.fromList . V.toList . attributesVec @@ -136,7 +155,7 @@ sortedAttributesIndices :: Attributes -> [(Int, Attribute)] sortedAttributesIndices attrs = L.sortBy (\(_, Attribute name1 _) (_,Attribute name2 _) -> compare name1 name2) $ V.toList (V.indexed (attributesVec attrs)) -- | The relation's tuple set is the body of the relation. -newtype RelationTupleSet = RelationTupleSet { asList :: [RelationTuple] } deriving (Hashable, Show, Generic, Read) +newtype RelationTupleSet = RelationTupleSet { asList :: [RelationTuple] } deriving (Hashable, Show, Generic, Read, Lift) instance Read Relation where readsPrec = error "relation read not supported" @@ -164,7 +183,7 @@ instance Hashable RelationTuple where sortedTupVec = V.map (\(index, _) -> tupVec V.! index) $ V.fromList sortedAttrsIndices -- | A tuple is a set of attributes mapped to their 'Atom' values. -data RelationTuple = RelationTuple Attributes (V.Vector Atom) deriving (Show, Read, Generic) +data RelationTuple = RelationTuple Attributes (V.Vector Atom) deriving (Show, Read, Generic, Lift) instance Eq RelationTuple where tuple1@(RelationTuple attrs1 _) == tuple2@(RelationTuple attrs2 _) = @@ -177,7 +196,7 @@ instance Eq RelationTuple where instance NFData RelationTuple where rnf = genericRnf -data Relation = Relation Attributes RelationTupleSet deriving (Show, Generic,Typeable) +data Relation = Relation Attributes RelationTupleSet deriving (Show, Generic, Typeable, Lift) instance Eq Relation where Relation attrs1 tupSet1 == Relation attrs2 tupSet2 = attrs1 == attrs2 && tupSet1 == tupSet2 @@ -235,12 +254,12 @@ data RelationalExprBase a = --Summarize :: AtomExpr -> AttributeName -> RelationalExpr -> RelationalExpr -> RelationalExpr -- a special case of Extend --Evaluate relationalExpr with scoped views With [(WithNameExprBase a, RelationalExprBase a)] (RelationalExprBase a) - deriving (Show, Read, Eq, Generic, NFData, Foldable, Functor, Traversable) + deriving (Show, Read, Eq, Generic, NFData, Foldable, Functor, Traversable, Lift) instance Hashable RelationalExpr data WithNameExprBase a = WithNameExpr RelVarName a - deriving (Show, Read, Eq, Generic, NFData, Foldable, Functor, Traversable, Hashable) + deriving (Show, Read, Eq, Generic, NFData, Foldable, Functor, Traversable, Hashable, Lift) type WithNameExpr = WithNameExprBase () @@ -262,7 +281,7 @@ type TypeVarName = StringType -- | Metadata definition for type constructors such as @data Either a b@. data TypeConstructorDef = ADTypeConstructorDef TypeConstructorName [TypeVarName] | PrimitiveTypeConstructorDef TypeConstructorName AtomType - deriving (Show, Generic, Eq, NFData, Hashable, Read) + deriving (Show, Generic, Eq, NFData, Hashable, Read, Lift) -- | Found in data constructors and type declarations: Left (Either Int Text) | Right Int type TypeConstructor = TypeConstructorBase () @@ -270,7 +289,7 @@ data TypeConstructorBase a = ADTypeConstructor TypeConstructorName [TypeConstruc PrimitiveTypeConstructor TypeConstructorName AtomType | RelationAtomTypeConstructor [AttributeExprBase a] | TypeVariable TypeVarName - deriving (Show, Generic, Eq, NFData, Hashable, Read) + deriving (Show, Generic, Eq, NFData, Hashable, Read, Lift) type TypeConstructorMapping = [(TypeConstructorDef, DataConstructorDefs)] @@ -280,13 +299,13 @@ type DataConstructorName = StringType type AtomTypeName = StringType -- | Used to define a data constructor in a type constructor context such as @Left a | Right b@ -data DataConstructorDef = DataConstructorDef DataConstructorName [DataConstructorDefArg] deriving (Eq, Show, Generic, NFData, Hashable, Read) +data DataConstructorDef = DataConstructorDef DataConstructorName [DataConstructorDefArg] deriving (Eq, Show, Generic, NFData, Hashable, Read, Lift) type DataConstructorDefs = [DataConstructorDef] data DataConstructorDefArg = DataConstructorDefTypeConstructorArg TypeConstructor | DataConstructorDefTypeVarNameArg TypeVarName - deriving (Show, Generic, Eq, NFData, Hashable, Read) + deriving (Show, Generic, Eq, NFData, Hashable, Read, Lift) type InclusionDependencies = M.Map IncDepName InclusionDependency type RelationVariables = M.Map RelVarName GraphRefRelationalExpr @@ -331,7 +350,7 @@ data DatabaseContext = DatabaseContext { type IncDepName = StringType -- | Inclusion dependencies represent every possible database constraint. Constraints enforce specific, arbitrarily-complex rules to which the database context's relation variables must adhere unconditionally. -data InclusionDependency = InclusionDependency RelationalExpr RelationalExpr deriving (Show, Eq, Generic, NFData, Hashable, Read) +data InclusionDependency = InclusionDependency RelationalExpr RelationalExpr deriving (Show, Eq, Generic, NFData, Hashable, Read, Lift) type AttributeNameAtomExprMap = M.Map AttributeName AtomExpr @@ -371,7 +390,7 @@ data DatabaseContextExprBase a = ExecuteDatabaseContextFunction FunctionName [AtomExprBase a] | MultipleExpr [DatabaseContextExprBase a] - deriving (Show, Read, Eq, Generic, NFData) + deriving (Show, Read, Eq, Generic, NFData, Lift) type ObjModuleName = StringType type ObjFunctionName = StringType @@ -404,7 +423,7 @@ data RestrictionPredicateExprBase a = RelationalExprPredicate (RelationalExprBase a) | --type must be same as true and false relations (no attributes) AtomExprPredicate (AtomExprBase a) | --atom must evaluate to boolean AttributeEqualityPredicate AttributeName (AtomExprBase a) -- relationalexpr must result in relation with single tuple - deriving (Show, Read, Eq, Generic, NFData, Foldable, Functor, Traversable) + deriving (Show, Read, Eq, Generic, NFData, Foldable, Functor, Traversable, Lift) -- child + parent links -- | A transaction graph's head name references the leaves of the transaction graph and can be used during session creation to indicate at which point in the graph commits should persist. @@ -475,11 +494,11 @@ data AtomExprBase a = AttributeAtomExpr AttributeName | FunctionAtomExpr FunctionName [AtomExprBase a] a | RelationAtomExpr (RelationalExprBase a) | ConstructedAtomExpr DataConstructorName [AtomExprBase a] a - deriving (Eq, Show, Read, Generic, NFData, Foldable, Functor, Traversable) + deriving (Eq, Show, Read, Generic, NFData, Foldable, Functor, Traversable, Lift) -- | Used in tuple creation when creating a relation. data ExtendTupleExprBase a = AttributeExtendTupleExpr AttributeName (AtomExprBase a) - deriving (Show, Read, Eq, Generic, NFData, Foldable, Functor, Traversable) + deriving (Show, Read, Eq, Generic, NFData, Foldable, Functor, Traversable, Lift) type ExtendTupleExpr = ExtendTupleExprBase () @@ -504,7 +523,7 @@ data AttributeNamesBase a = AttributeNames (S.Set AttributeName) | UnionAttributeNames (AttributeNamesBase a) (AttributeNamesBase a) | IntersectAttributeNames (AttributeNamesBase a) (AttributeNamesBase a) | RelationalExprAttributeNames (RelationalExprBase a) -- use attribute names from the relational expression's type - deriving (Eq, Show, Read, Generic, NFData, Foldable, Functor, Traversable) + deriving (Eq, Show, Read, Generic, NFData, Foldable, Functor, Traversable, Lift) type AttributeNames = AttributeNamesBase () @@ -529,11 +548,11 @@ type GraphRefAttributeExpr = AttributeExprBase GraphRefTransactionMarker -- | Create attributes dynamically. data AttributeExprBase a = AttributeAndTypeNameExpr AttributeName TypeConstructor a | NakedAttributeExpr Attribute - deriving (Eq, Show, Read, Generic, NFData, Foldable, Functor, Traversable, Hashable) + deriving (Eq, Show, Read, Generic, NFData, Foldable, Functor, Traversable, Hashable, Lift) -- | Dynamically create a tuple from attribute names and 'AtomExpr's. newtype TupleExprBase a = TupleExpr (M.Map AttributeName (AtomExprBase a)) - deriving (Eq, Show, Read, Generic, NFData, Foldable, Functor, Traversable) + deriving (Eq, Show, Read, Generic, NFData, Foldable, Functor, Traversable, Lift) instance Hashable TupleExpr @@ -542,7 +561,7 @@ type TupleExpr = TupleExprBase () type GraphRefTupleExpr = TupleExprBase GraphRefTransactionMarker data TupleExprsBase a = TupleExprs a [TupleExprBase a] - deriving (Eq, Show, Read, Generic, NFData, Foldable, Functor, Traversable) + deriving (Eq, Show, Read, Generic, NFData, Foldable, Functor, Traversable, Lift) instance Hashable TupleExprs @@ -579,7 +598,13 @@ instance Eq (Function a) where f1 == f2 = funcName f1 == funcName f2 instance Hashable (Function a) where - hashWithSalt salt func = salt `hashWithSalt` funcName func `hashWithSalt` funcType func `hashWithSalt` funcBody func + hashWithSalt salt func = salt `hashWithSalt` funcName func `hashWithSalt` funcType func `hashWithSalt` hashfuncbody + where + hashfuncbody = + case funcBody func of + (FunctionScriptBody script _) -> salt `hashWithSalt` script + (FunctionBuiltInBody _) -> salt + (FunctionObjectLoadedBody fp modName entryFunc _) -> salt `hashWithSalt` (fp, modName, entryFunc) data FunctionBody a = FunctionScriptBody FunctionBodyScript a | @@ -587,11 +612,6 @@ data FunctionBody a = FunctionObjectLoadedBody FilePath ObjectModuleName ObjectFileEntryFunctionName a deriving Generic -instance Hashable (FunctionBody a) where - salt `hashWithSalt` (FunctionScriptBody script _) = salt `hashWithSalt` script - salt `hashWithSalt` (FunctionBuiltInBody _) = salt - salt `hashWithSalt` (FunctionObjectLoadedBody fp modName entryFunc _) = salt `hashWithSalt` (fp, modName, entryFunc) - instance NFData a => NFData (FunctionBody a) where rnf (FunctionScriptBody script _) = rnf script rnf (FunctionBuiltInBody _) = rnf () diff --git a/test/TutorialD/InterpreterTest.hs b/test/TutorialD/InterpreterTest.hs index 6cbfe8de..0251d73e 100644 --- a/test/TutorialD/InterpreterTest.hs +++ b/test/TutorialD/InterpreterTest.hs @@ -1,6 +1,8 @@ +{-# LANGUAGE QuasiQuotes #-} import TutorialD.Interpreter.DatabaseContextExpr import TutorialD.Interpreter.TestBase import TutorialD.Interpreter +import TutorialD.Interpreter.TH import TutorialD.Interpreter.Base import Test.HUnit import ProjectM36.Relation as R @@ -84,7 +86,8 @@ main = do testSelfReferencingUncommittedContext, testUnionAndIntersectionAttributeExprs, testUndefineConstraints, - testQuotedRelVarNames + testQuotedRelVarNames, + testTemplateHaskellExprs ] simpleRelTests :: Test @@ -751,3 +754,15 @@ testQuotedRelVarNames = TestCase $ do let qAttrExpected = Right (Assign "test" (MakeRelationFromExprs Nothing (TupleExprs () [TupleExpr (M.singleton "\28450\23383" (NakedAtomExpr (TextAtom "test")))]))) assertEqual "quoted attribute" qAttrExpected (parseDBExpr "test:=relation{tuple{`漢字` \"test\"}}") assertBool "invalid quoted rv" (isLeft (parseDBExpr "`TEST:=true")) + +testTemplateHaskellExprs :: Test +testTemplateHaskellExprs = TestCase $ do + let actualre = [relationalExpr|relation{tuple{a "nice"},tuple{a "test"}}|] +-- expected = mkRelationFromList (attributesFromList [Attribute "a" TextAtomType]) [[TextAtom "nice"],[TextAtom "test"]] + expectedre = MakeRelationFromExprs Nothing (TupleExprs () [TupleExpr (M.fromList [("a",NakedAtomExpr (TextAtom "nice"))]),TupleExpr (M.fromList [("a",NakedAtomExpr (TextAtom "test"))])]) + assertEqual "relationalExpr" expectedre actualre + + let actualdbce = [dbContextExpr|update s where status=20 (sname := "New")|] :: DatabaseContextExpr + expecteddbce = Update "s" (M.singleton "sname" (NakedAtomExpr (TextAtom "New"))) (AttributeEqualityPredicate "status" (NakedAtomExpr (IntegerAtom 20))) + assertEqual "dbContextExpr" expecteddbce actualdbce +