on master help sign in
finalize TupleExpr default type constructor mapping evaluation

all but one test pass

commitaebcccd7145cefb722fab72d0b930e3133f98706 authorAgentM <agentm@themactionfaction.com> dateTue 12 May 2020, 23:27 (6 years ago) parenta1f21a4
16 files changed+66−31
examples/hair.hs+5−3 src/bin/ProjectM36/Server/RemoteCallTypes/Json.hs+3 src/bin/TutorialD/Interpreter/RelationalExpr.hs+2−1 src/bin/benchmark/Handles.hs+2 src/lib/ProjectM36/Base.hs+12−1 src/lib/ProjectM36/Client.hs+1 src/lib/ProjectM36/NormalizeExpr.hs+7−2 src/lib/ProjectM36/RelationalExpression.hs+7−6 src/lib/ProjectM36/StaticOptimizer.hs+1−1 src/lib/ProjectM36/TransGraphRelationalExpression.hs+10−2 test/Relation/Atomable.hs+1−1 test/Relation/Basic.hs+1−1 test/Server/WebSocket.hs+3−2 test/TransactionGraph/Persist.hs+1−1 test/TutorialD/Interpreter/AtomFunctionScript.hs+8−8 test/TutorialD/Interpreter/Import/TutorialD.hs+2−2
examples/hair.hs+5−3
@@ -31,11 +31,13 @@ main = do
3131
3232 --create a relation with the new Hair AtomType
3333 let blond = NakedAtomExpr (toAtom Blond)
34− eCheck $ executeDatabaseContextExpr sessionId conn (Assign "people" (MakeRelationFromExprs Nothing [
35− TupleExpr (M.fromList [("hair", blond), ("name", NakedAtomExpr (TextAtom "Colin"))])]))
34+ eCheck $ executeDatabaseContextExpr sessionId conn
35+ (Assign "people"
36+ (MakeRelationFromExprs Nothing $ TupleExprs () [
37+ TupleExpr (M.fromList [("hair", blond), ("name", NakedAtomExpr (TextAtom "Colin"))])]))
3638
3739 let restrictionPredicate = AttributeEqualityPredicate "hair" blond
3840 peopleRel <- eCheck $ executeRelationalExpr sessionId conn (Restrict restrictionPredicate (RelationVariable "people" ()))
3941
4042 TIO.putStrLn (showRelation peopleRel)
41−
43+
src/bin/ProjectM36/Server/RemoteCallTypes/Json.hs+3
@@ -22,6 +22,9 @@ instance FromJSON RelationalExpr
2222instance ToJSON TupleExpr
2323instance FromJSON TupleExpr
2424
25+instance ToJSON TupleExprs
26+instance FromJSON TupleExprs
27+
2528instance ToJSON RestrictionPredicateExpr
2629instance FromJSON RestrictionPredicateExpr
2730
src/bin/TutorialD/Interpreter/RelationalExpr.hs+2−1
@@ -29,7 +29,8 @@ makeRelationP = do
2929 reserved "relation"
3030 attrExprs <- try (fmap Just makeAttributeExprsP) <|> pure Nothing
3131 tupleExprs <- braces (sepBy tupleExprP comma) <|> pure []
32− pure $ MakeRelationFromExprs attrExprs tupleExprs
32+ marker <- parseMarkerP
33+ pure $ MakeRelationFromExprs attrExprs (TupleExprs marker tupleExprs)
3334
3435
3536--abstract data type parser- in this context, the type constructor must not include any type arguments
src/bin/benchmark/Handles.hs+2
@@ -6,7 +6,9 @@ import Options.Applicative
66import TutorialD.Interpreter
77import TutorialD.Interpreter.Base hiding (Parser, option)
88import qualified Data.Text as T
9+#if __GLASGOW_HASKELL__ <= 802
910import Data.Monoid
11+#endif
1012import Control.Monad
1113
1214data HandlesArgs = HandlesArgs {
src/lib/ProjectM36/Base.hs+12−1
@@ -185,7 +185,7 @@ type RelationalExpr = RelationalExprBase ()
185185-- | A relational expression represents query (read) operations on a database.
186186data RelationalExprBase a =
187187 --- | Create a relation from tuple expressions.
188− MakeRelationFromExprs (Maybe [AttributeExprBase a]) [TupleExprBase a] |
188+ MakeRelationFromExprs (Maybe [AttributeExprBase a]) (TupleExprsBase a) |
189189 --- | Create and reference a relation from attributes and a tuple set.
190190 MakeStaticRelation Attributes RelationTupleSet |
191191 --- | Reference an existing relation in Haskell-space.
@@ -549,6 +549,15 @@ type TupleExpr = TupleExprBase ()
549549
550550type GraphRefTupleExpr = TupleExprBase GraphRefTransactionMarker
551551
552+data TupleExprsBase a = TupleExprs a [TupleExprBase a]
553+ deriving (Eq, Show, Generic, NFData, Foldable, Functor, Traversable)
554+
555+type GraphRefTupleExprs = TupleExprsBase GraphRefTransactionMarker
556+
557+type TupleExprs = TupleExprsBase ()
558+
559+instance Binary TupleExprs
560+
552561data MergeStrategy =
553562 -- | After a union merge, the merge transaction is a result of union'ing relvars of the same name, introducing all uniquely-named relvars, union of constraints, union of atom functions, notifications, and types (unless the names and definitions collide, e.g. two types of the same name with different definitions)
554563 UnionMergeStrategy |
@@ -624,6 +633,8 @@ unimplemented :: HasCallStack => a
624633unimplemented = undefined
625634
626635--for serializing GraphRefRelationalExpr as part of transaction serialization
636+instance Binary (TupleExprsBase GraphRefTransactionMarker)
637+
627638instance Binary (TupleExprBase GraphRefTransactionMarker)
628639
629640instance Binary GraphRefRelationalExpr
src/lib/ProjectM36/Client.hs+1
@@ -99,6 +99,7 @@ module ProjectM36.Client
9999 AtomType(..),
100100 Atomable(..),
101101 TupleExprBase(..),
102+ TupleExprsBase(..),
102103 AtomExprBase(..),
103104 RestrictionPredicateExprBase(..),
104105 withTransaction
src/lib/ProjectM36/NormalizeExpr.hs+7−2
@@ -21,7 +21,7 @@ processRelationalExpr (MakeRelationFromExprs mAttrs tupleExprs) = do
2121 mAttrs' <- case mAttrs of
2222 Nothing -> pure Nothing
2323 Just mAttrs'' -> Just <$> mapM processAttributeExpr mAttrs''
24− MakeRelationFromExprs mAttrs' <$> mapM processTupleExpr tupleExprs
24+ MakeRelationFromExprs mAttrs' <$> processTupleExprs tupleExprs
2525processRelationalExpr (MakeStaticRelation attrs tupSet) = pure (MakeStaticRelation attrs tupSet)
2626processRelationalExpr (ExistingRelation rel) = pure (ExistingRelation rel)
2727--requires current trans id and graph
@@ -115,8 +115,13 @@ processAtomExpr (FunctionAtomExpr fName atomExprs ()) =
115115processAtomExpr (RelationAtomExpr expr) = RelationAtomExpr <$> processRelationalExpr expr
116116processAtomExpr (ConstructedAtomExpr dConsName atomExprs ()) = ConstructedAtomExpr dConsName <$> mapM processAtomExpr atomExprs <*> askMarker
117117
118+processTupleExprs :: TupleExprs -> ProcessExprM GraphRefTupleExprs
119+processTupleExprs (TupleExprs () tupleExprs) = do
120+ marker <- askMarker
121+ TupleExprs marker <$> mapM processTupleExpr tupleExprs
122+
118123processTupleExpr :: TupleExpr -> ProcessExprM GraphRefTupleExpr
119−processTupleExpr (TupleExpr tMap) =
124+processTupleExpr (TupleExpr tMap) = do
120125 TupleExpr . M.fromList <$> mapM (\(k,v) -> (,) k <$> processAtomExpr v) (M.toList tMap)
121126
122127--convert AttributeExpr to GraphRefAttributeExpr
src/lib/ProjectM36/RelationalExpression.hs+7−6
@@ -1129,10 +1129,10 @@ evalGraphRefAttrExpr (AttributeAndTypeNameExpr attrName tCons transId) = do
11291129evalGraphRefAttrExpr (NakedAttributeExpr attr) = pure attr
11301130
11311131-- for tuple type concrete resolution (Nothing ==> Maybe Int) when the attributes hint is Nothing, we need to first process all the tuples, then extract the concrete types on a per-attribute basis, then reprocess the tuples to include the concrete types
1132−evalGraphRefTupleExprs :: Maybe Attributes -> [GraphRefTupleExpr] -> GraphRefRelationalExprM [RelationTuple]
1133−evalGraphRefTupleExprs _ [] = pure []
1134−evalGraphRefTupleExprs mAttrs tupleExprs = do
1135− tuples <- mapM (evalGraphRefTupleExpr mAttrs) tupleExprs
1132+evalGraphRefTupleExprs :: Maybe Attributes -> GraphRefTupleExprs -> GraphRefRelationalExprM [RelationTuple]
1133+evalGraphRefTupleExprs _ (TupleExprs _ []) = pure []
1134+evalGraphRefTupleExprs mAttrs (TupleExprs fixedMarker tupleExprL) = do
1135+ tuples <- mapM (evalGraphRefTupleExpr mAttrs) tupleExprL
11361136 finalAttrs <- case mAttrs of
11371137 Just attrs -> pure attrs
11381138 Nothing ->
@@ -1151,10 +1151,11 @@ evalGraphRefTupleExprs mAttrs tupleExprs = do
11511151 Right val -> val) (zip (V.toList $ tupleAttributes tup) acc)) (V.toList $ tupleAttributes headTuple) tailTuples
11521152 pure (A.attributesFromList mostResolvedTypes)
11531153 --strategy: if all the tuple expr transaction markers refer to one location, then we can pass the type constructor mapping from that location, otherwise, we cannot assume that the types are the same
1154− tConsMap <- case singularTransactions tupleExprs of
1154+ tConsMap <- case singularTransactions tupleExprL of
11551155 SingularTransactionRef commonTransId -> do
11561156 typeConstructorMapping <$> gfDatabaseContextForMarker commonTransId
1157− NoTransactionsRef -> pure [] --no custom type constructors are referenced, so no tConsMap is needed
1157+ NoTransactionsRef -> do
1158+ typeConstructorMapping <$> gfDatabaseContextForMarker fixedMarker
11581159 -- if there are multiple transaction markers in the TupleExprs, then we can't assume a single type constructor mapping- this could be improved in the future, but if all the tuples are fully resolved, then we don't need further resolution
11591160 _ -> throwError TupleExprsReferenceMultipleMarkersError
11601161 lift $ except $ validateAttributes tConsMap finalAttrs
src/lib/ProjectM36/StaticOptimizer.hs+1−1
@@ -393,7 +393,7 @@ isFalseExpr _ = False
393393
394394-- determine if the created relation can statically be determined to be empty
395395isEmptyRelationExpr :: RelationalExprBase a -> Bool
396−isEmptyRelationExpr (MakeRelationFromExprs _ []) = True
396+isEmptyRelationExpr (MakeRelationFromExprs _ (TupleExprs _ [])) = True
397397isEmptyRelationExpr (MakeStaticRelation _ tupSet) = null (asList tupSet)
398398isEmptyRelationExpr (ExistingRelation rel) = rel == emptyRelationWithAttrs (attributes rel)
399399isEmptyRelationExpr _ = False
src/lib/ProjectM36/TransGraphRelationalExpression.hs+10−2
@@ -29,6 +29,10 @@ type TransGraphTupleExpr = TupleExprBase TransactionIdLookup
2929
3030instance Binary TransGraphTupleExpr
3131
32+type TransGraphTupleExprs = TupleExprsBase TransactionIdLookup
33+
34+instance Binary TransGraphTupleExprs
35+
3236type TransGraphRestrictionPredicateExpr = RestrictionPredicateExprBase TransactionIdLookup
3337
3438instance Binary TransGraphRestrictionPredicateExpr
@@ -69,7 +73,7 @@ findTrans tlook = do
6973-- OUTDATED a previous attempt at this function attempted to convert TransGraphRelationalExpr to RelationalExpr by resolving the transaction lookups. However, there is no way to resolve a FunctionAtomExpr to an Atom without fully evaluating the higher levels (TupleExpr, etc.). An anonymous function expression cannot be serialized, so that workaround is out. Still, I suspect we can reuse the existing static optimizer logic to work on both structures. The current conversion reduces the chance of whole-query optimization due to full-evaluation on top of full-evaluation, so this function would benefit from some re-design.
7074processTransGraphRelationalExpr :: TransGraphRelationalExpr -> TransGraphEvalMonad GraphRefRelationalExpr
7175processTransGraphRelationalExpr (MakeRelationFromExprs mAttrExprs tupleExprs) = do
72− tupleExprs' <- mapM processTransGraphTupleExpr tupleExprs
76+ tupleExprs' <- processTransGraphTupleExprs tupleExprs
7377 case mAttrExprs of
7478 Nothing -> pure (MakeRelationFromExprs Nothing tupleExprs')
7579 Just attrExprs -> do
@@ -119,7 +123,11 @@ processTransGraphRelationalExpr (With views expr) = do
119123 ) views
120124 expr' <- processTransGraphRelationalExpr expr
121125 pure (With evaldViews expr')
122−
126+
127+processTransGraphTupleExprs :: TransGraphTupleExprs -> TransGraphEvalMonad GraphRefTupleExprs
128+processTransGraphTupleExprs (TupleExprs marker texprs) =
129+ TupleExprs <$> findTransId marker <*> mapM processTransGraphTupleExpr texprs
130+
123131processTransGraphTupleExpr :: TransGraphTupleExpr -> TransGraphEvalMonad GraphRefTupleExpr
124132processTransGraphTupleExpr (TupleExpr attrMap) = do
125133 let attrAssoc = mapM (\(attrName, atomExpr) -> do
test/Relation/Atomable.hs+1−1
@@ -101,7 +101,7 @@ testHaskell2DB = TestCase $ do
101101 let createRelExpr = Assign "x" rel
102102
103103 rel = MakeRelationFromExprs Nothing
104− [TupleExpr (M.singleton "a1" (NakedAtomExpr atomVal))]
104+ (TupleExprs () [TupleExpr (M.singleton "a1" (NakedAtomExpr atomVal))])
105105 atomVal = toAtom exampleVal
106106 exampleVal = Test1C 10
107107 checkExecuteDatabaseContextExpr sessionId dbconn createRelExpr
test/Relation/Basic.hs+1−1
@@ -97,7 +97,7 @@ testMkRelationFromExprsBadAttrs = TestCase $ do
9797 let context = DBC.empty
9898 (graph,_) <- freshTransactionGraph context
9999 let reenv = mkRelationalExprEnv context graph
100− reExpr = MakeRelationFromExprs (Just [AttributeAndTypeNameExpr "badAttr1" (PrimitiveTypeConstructor "Int" IntAtomType) ()]) [TupleExpr (M.singleton "badAttr2" (NakedAtomExpr (IntAtom 1)))]
100+ reExpr = MakeRelationFromExprs (Just [AttributeAndTypeNameExpr "badAttr1" (PrimitiveTypeConstructor "Int" IntAtomType) ()]) (TupleExprs () [TupleExpr (M.singleton "badAttr2" (NakedAtomExpr (IntAtom 1)))])
101101 evald = runRelationalExprM reenv (evalRelationalExpr reExpr)
102102 case evald of
103103 Left err -> assertEqual "tuple type mismatch" (TupleAttributeTypeMismatchError (A.attributesFromList [Attribute "badAttr2" IntAtomType])) err
test/Server/WebSocket.hs+3−2
@@ -56,13 +56,14 @@ waitForListenSocket secondsToTry port =
5656 if secondsToTry <= 0 then
5757 throw WaitForSocketListenException
5858 else do
59− hostaddr <- inet_addr "127.0.0.1"
59+ --hostaddr <- inet_addr "127.0.0.1"
60+ hostaddr:_ <- getAddrInfo Nothing (Just "127.0.0.1") (Just (show port))
6061 sock <- socket AF_INET Stream defaultProtocol
6162 let handler :: IOException -> IO ()
6263 handler _ = do
6364 threadDelay 1000000
6465 waitForListenSocket (secondsToTry - 1) port
65− catch (connect sock (SockAddrInet port hostaddr)) handler
66+ catch (connect sock (addrAddress hostaddr)) handler
6667
6768main :: IO ()
6869main = do
test/TransactionGraph/Persist.hs+1−1
@@ -93,7 +93,7 @@ testFunctionPersistence = TestCase $
9393 conn2 <- assertIOEither $ connectProjectM36 connInfo
9494 sess2 <- assertIOEither $ createSessionAtHead conn2 "master"
9595
96− res <- executeRelationalExpr sess2 conn2 (MakeRelationFromExprs Nothing [TupleExpr (M.singleton "a" (FunctionAtomExpr "testdisk" [NakedAtomExpr (IntAtom 3)] ()))])
96+ res <- executeRelationalExpr sess2 conn2 (MakeRelationFromExprs Nothing (TupleExprs () [TupleExpr (M.singleton "a" (FunctionAtomExpr "testdisk" [NakedAtomExpr (IntAtom 3)] ()))]))
9797 let expectedRel = mkRelationFromList (attributesFromList [Attribute "a" IntAtomType]) [[IntAtom 3]]
9898 assertEqual "testdisk dbc function run" expectedRel res
9999
test/TutorialD/Interpreter/AtomFunctionScript.hs+8−8
@@ -29,7 +29,7 @@ testBasicAtomFunction = TestCase $ do
2929 funcAtomExpr = FunctionAtomExpr "mkTest" [NakedAtomExpr (IntegerAtom 3)] ()
3030 tupleExprs = [TupleExpr (M.singleton "x" funcAtomExpr)]
3131 expectedResult = mkRelationFromList (V.fromList attrs) [[IntegerAtom 3]]
32− result <- executeRelationalExpr sess conn (MakeRelationFromExprs (Just (map NakedAttributeExpr attrs)) tupleExprs)
32+ result <- executeRelationalExpr sess conn (MakeRelationFromExprs (Just (map NakedAttributeExpr attrs)) (TupleExprs () tupleExprs))
3333
3434 assertEqual "simple atom function equality" expectedResult result
3535
@@ -40,7 +40,7 @@ testExceptionAtomFunction = TestCase $ do
4040 executeTutorialD sess conn "addatomfunction \"mkTest\" Integer -> Either AtomFunctionError Integer \"\"\"(\\(IntegerAtom x:xs) -> (error (show 1))) :: [Atom] -> Either AtomFunctionError Atom\"\"\""
4141 let attrs = [Attribute "x" IntegerAtomType]
4242 funcAtomExpr = FunctionAtomExpr "mkTest" [NakedAtomExpr (IntegerAtom 3)] ()
43− tupleExprs = [TupleExpr (M.singleton "x" funcAtomExpr)]
43+ tupleExprs = TupleExprs () [TupleExpr (M.singleton "x" funcAtomExpr)]
4444 result <- executeRelationalExpr sess conn (MakeRelationFromExprs (Just (map NakedAttributeExpr attrs)) tupleExprs)
4545 assertBool "catch error exception from script" (case result of
4646 Left (UnhandledExceptionError _) -> True
@@ -57,7 +57,7 @@ testNoArgumentAtomFunction = TestCase $ do
5757 executeTutorialD sess conn "addatomfunction \"mkTest\" Either AtomFunctionError Integer \"\"\"(\\x -> pure (IntegerAtom 5)) :: [Atom] -> Either AtomFunctionError Atom\"\"\""
5858 let attrs = [Attribute "x" IntegerAtomType]
5959 funcAtomExpr = FunctionAtomExpr "mkTest" [] ()
60− tupleExprs = [TupleExpr (M.singleton "x" funcAtomExpr)]
60+ tupleExprs = TupleExprs () [TupleExpr (M.singleton "x" funcAtomExpr)]
6161 expectedResult = mkRelationFromList (V.fromList attrs) [[IntegerAtom 5]]
6262 result <- executeRelationalExpr sess conn (MakeRelationFromExprs (Just (map NakedAttributeExpr attrs)) tupleExprs)
6363 assertEqual "no argument scripted function" expectedResult result
@@ -66,7 +66,7 @@ testArgumentTypeMismatch :: Test
6666testArgumentTypeMismatch = TestCase $ do
6767 (sess, conn) <- dateExamplesConnection emptyNotificationCallback
6868 executeTutorialD sess conn "addatomfunction \"mkTest\" Integer -> Either AtomFunctionError Integer \"\"\"(\\(IntegerAtom x:_) -> pure $ TextAtom \"wrong type\") :: [Atom] -> Either AtomFunctionError Atom\"\"\""
69− let tupleExprs = [TupleExpr (M.singleton "x" funcAtomExpr)]
69+ let tupleExprs = TupleExprs () [TupleExpr (M.singleton "x" funcAtomExpr)]
7070 funcAtomExpr = FunctionAtomExpr "mkTest" [NakedAtomExpr (IntegerAtom 3)] ()
7171 attrs = [Attribute "x" IntegerAtomType]
7272 expectedResult = Left (AtomTypeMismatchError IntegerAtomType TextAtomType)
@@ -80,7 +80,7 @@ testPolymorphicReturnType = TestCase $ do
8080 let funcAtomExpr = FunctionAtomExpr "fromMaybe" [NakedAtomExpr (IntegerAtom 5),
8181 NakedAtomExpr maybeAtom] ()
8282 maybeAtom = ConstructedAtom "Just" (maybeAtomType IntegerAtomType) [IntegerAtom 3]
83− relExpr = MakeRelationFromExprs Nothing [TupleExpr (M.singleton "x" funcAtomExpr)]
83+ relExpr = MakeRelationFromExprs Nothing (TupleExprs () [TupleExpr (M.singleton "x" funcAtomExpr)])
8484 mRelType <- typeForRelationalExpr sess conn relExpr
8585 case mRelType of
8686 Left err -> assertFailure (show err)
@@ -94,7 +94,7 @@ testPolymorphicReturnType = TestCase $ do
9494 let failFuncAtomExpr = FunctionAtomExpr "fromMaybe" [NakedAtomExpr (IntegerAtom 5),
9595 NakedAtomExpr mismatchAtom] ()
9696 mismatchAtom = ConstructedAtom "Just" (maybeAtomType TextAtomType) [TextAtom "fail"]
97− failRelExpr = MakeRelationFromExprs Nothing [TupleExpr (M.singleton "x" failFuncAtomExpr)]
97+ failRelExpr = MakeRelationFromExprs Nothing (TupleExprs () [TupleExpr (M.singleton "x" failFuncAtomExpr)])
9898 mFailRes <- executeRelationalExpr sess conn failRelExpr
9999 let expectedErr = Left (AtomFunctionTypeVariableMismatch "a" IntegerAtomType TextAtomType)
100100 assertEqual "expected type variable mismatch" expectedErr mFailRes
@@ -106,9 +106,9 @@ testScriptedTypeVariable = TestCase $ do
106106 executeTutorialD sess conn "addatomfunction \"idTest\" a -> Either AtomFunctionError a \"(\\\\(x:_) -> pure x) :: [Atom] -> Either AtomFunctionError Atom\""
107107 let attrs = [Attribute "x" IntegerAtomType]
108108 funcAtomExpr = FunctionAtomExpr "idTest" [NakedAtomExpr (IntegerAtom 3)] ()
109− tupleExprs = [TupleExpr (M.singleton "x" funcAtomExpr)]
109+ tupleExprs = TupleExprs () [TupleExpr (M.singleton "x" funcAtomExpr)]
110110 expectedResult = mkRelationFromList (V.fromList attrs) [[IntegerAtom 3]]
111111 result <- executeRelationalExpr sess conn (MakeRelationFromExprs (Just (map NakedAttributeExpr attrs)) tupleExprs)
112112
113113 assertEqual "id function equality" expectedResult result
114−
114+
test/TutorialD/Interpreter/Import/TutorialD.hs+2−2
@@ -20,10 +20,10 @@ testTutdImport = TestCase $
2020 hClose handle
2121 let expectedExpr = MultipleExpr [
2222 Assign "x" (MakeRelationFromExprs Nothing
23− [TupleExpr (M.fromList [("a", NakedAtomExpr $ IntegerAtom 5),
23+ $ TupleExprs () [TupleExpr (M.fromList [("a", NakedAtomExpr $ IntegerAtom 5),
2424 ("b", NakedAtomExpr $ TextAtom "spam")])]),
2525 Assign "y" (MakeRelationFromExprs Nothing
26− [TupleExpr (M.fromList [("b", NakedAtomExpr (TextAtom "漢字"))])])]
26+ $ TupleExprs () [TupleExpr (M.fromList [("b", NakedAtomExpr (TextAtom "漢字"))])])]
2727 imported <- importTutorialD tempPath
2828 assertEqual "import tutd" (Right expectedExpr) imported
2929