diff --git a/aqua-src/antithesis.aqua b/aqua-src/antithesis.aqua index bb2de9c00..75bf448b1 100644 --- a/aqua-src/antithesis.aqua +++ b/aqua-src/antithesis.aqua @@ -1,15 +1,17 @@ aqua Job declares * -export testContainsFunc +export testForFunc -func testContainsFunc() -> bool, bool, bool, bool, bool: +func testForFunc() -> []string, [][]string: streamMap: %string - resFirst = streamMap.contains("key one") - streamMap <<- ("key one", "") - resSecond = streamMap.contains("key one") - resThird = streamMap.contains("key one") - streamMap <<- ("key one", "new") - streamMap <<- ("key two", "") - resFourth = streamMap.contains("key one") - resFifth = streamMap.contains("key two") - <- resFirst, resSecond, resThird, resFourth, resFifth + streamMap <<- ("key one", "1") + streamMap <<- ("key one", "2") + streamMap <<- ("key two", "3") + + streamKeys: *string + streamValues: *[]string + + for kv <- streamMap: + streamKeys <<- kv.key + streamValues <<- kv.value + <- streamKeys, streamValues diff --git a/integration-tests/aqua/examples/streamMap.aqua b/integration-tests/aqua/examples/streamMap.aqua index b9485d8d1..b578288d2 100644 --- a/integration-tests/aqua/examples/streamMap.aqua +++ b/integration-tests/aqua/examples/streamMap.aqua @@ -1,6 +1,6 @@ aqua StreamMapTest declares * -export testGetFunc, testGetStreamFunc, testKeysFunc, testContainsFunc +export testGetFunc, testGetStreamFunc, testKeysFunc, testContainsFunc, testForFunc func testGetFunc() -> []string, []string, []string: streamMap: %string @@ -45,4 +45,25 @@ func testContainsFunc() -> bool, bool, bool, bool, bool: streamMap <<- (keyTwo, "") resFourth = streamMap.contains(keyOne) resFifth = streamMap.contains(keyTwo) - <- resFirst, resSecond, resThird, resFourth, resFifth \ No newline at end of file + <- resFirst, resSecond, resThird, resFourth, resFifth + +func testForFunc() -> []string, [][]string: + streamMap: %string + streamMap <<- ("key one", "1") + streamMap <<- ("key one", "2") + + streamMap <<- ("key two", "3") + streamMap <<- ("key two", "4") + streamMap <<- ("key two", "5") + + streamMap <<- ("key three", "6") + + streamMap <<- ("key four", "7") + + streamKeys: *string + streamValues: *[]string + + for kv <- streamMap: + streamKeys <<- kv.key + streamValues <<- kv.value + <- streamKeys, streamValues \ No newline at end of file diff --git a/integration-tests/src/__test__/examples.spec.ts b/integration-tests/src/__test__/examples.spec.ts index 1c691ccea..2512cd1d7 100644 --- a/integration-tests/src/__test__/examples.spec.ts +++ b/integration-tests/src/__test__/examples.spec.ts @@ -59,7 +59,8 @@ import { testGetFuncCall, testGetStreamFuncCall, testKeysFuncCall, - testContainsFuncCall + testContainsFuncCall, + testForFuncCall } from "../examples/streamMapCall.js"; import { topologyBug205Call, @@ -853,30 +854,36 @@ describe("Testing examples", () => { }); it("streamMap.aqua get function call", async () => { - let [resEmpty, resFirst, resSecond] = await testGetFuncCall(); - expect(resEmpty).toEqual([]); - expect(resFirst).toEqual(["first value"]); - expect(resSecond).toEqual(["first value", "second value"]); - }); + let [resEmpty, resFirst, resSecond] = await testGetFuncCall(); + expect(resEmpty).toEqual([]); + expect(resFirst).toEqual(["first value"]); + expect(resSecond).toEqual(["first value", "second value"]); + }); it("streamMap.aqua get stream function call", async () => { - let [resEmpty, resFirst, resSecond] = await testGetStreamFuncCall(); - expect(resEmpty).toEqual([]); - expect(resFirst).toEqual("first value"); - expect(resSecond).toEqual("second value"); + let [resEmpty, resFirst, resSecond] = await testGetStreamFuncCall(); + expect(resEmpty).toEqual([]); + expect(resFirst).toEqual("first value"); + expect(resSecond).toEqual("second value"); }); it("streamMap.aqua keys function call", async () => { - let [resEmpty, resFirst, resSecond] = await testKeysFuncCall(); - expect(resEmpty).toEqual([]); - expect(resFirst).toEqual(["key one"]); - expect(resSecond).toEqual(["key one", "key two"]); + let [resEmpty, resFirst, resSecond] = await testKeysFuncCall(); + expect(resEmpty).toEqual([]); + expect(resFirst).toEqual(["key one"]); + expect(resSecond).toEqual(["key one", "key two"]); }); it("streamMap.aqua contains function call", async () => { - let res = await testContainsFuncCall(); - expect(res).toEqual([false, true, false, true, true]); - }); + let res = await testContainsFuncCall(); + expect(res).toEqual([false, true, false, true, true]); + }); + + it("streamMap.aqua call with for over map", async () => { + let [keys, values] = await testForFuncCall(); + expect(keys).toEqual(["key one", "key two", "key three", "key four"]); + expect(values).toEqual([["1", "2"], ["3", "4", "5"], ["6"], ["7"]); + }); it("stream.aqua", async () => { let streamResult = await streamCall(); diff --git a/integration-tests/src/examples/streamMapCall.ts b/integration-tests/src/examples/streamMapCall.ts index 567b74ff7..5eb0504c5 100644 --- a/integration-tests/src/examples/streamMapCall.ts +++ b/integration-tests/src/examples/streamMapCall.ts @@ -1,5 +1,5 @@ import { - testGetFunc, testGetStreamFunc, testKeysFunc, testContainsFunc + testGetFunc, testGetStreamFunc, testKeysFunc, testContainsFunc, testForFunc } from "../compiled/examples/streamMap.js"; export async function testGetFuncCall() { @@ -18,3 +18,7 @@ export async function testContainsFuncCall() { return testContainsFunc(); } +export async function testForFuncCall() { + return testForFunc(); +} + diff --git a/model/inline/src/main/scala/aqua/model/inline/raw/ApplyStreamMapRawInliner.scala b/model/inline/src/main/scala/aqua/model/inline/raw/ApplyStreamMapRawInliner.scala index 03d4f2e33..ae87a7e03 100644 --- a/model/inline/src/main/scala/aqua/model/inline/raw/ApplyStreamMapRawInliner.scala +++ b/model/inline/src/main/scala/aqua/model/inline/raw/ApplyStreamMapRawInliner.scala @@ -15,9 +15,6 @@ import cats.syntax.option.* object ApplyStreamMapRawInliner { - private def getIterType(name: String, el: DataType) = - StructType(name, NonEmptyMap.of("key" -> ScalarType.string, "value" -> ArrayType(el))) - private def getStreamFromMapModel( mapName: String, mapType: StreamMapType, @@ -28,7 +25,7 @@ object ApplyStreamMapRawInliner { val mapVar = VarModel(mapName, mapType) val arrayResultType = ArrayType(mapType.element) val streamVar = VarModel(streamName, StreamType(arrayResultType)) - val iter = VarModel(iterName, getIterType("iterName_type", mapType.element)) + val iter = VarModel(iterName, mapType.iterType("iterName_type")) ParModel.wrap( ForModel(iter.name, mapVar, ForModel.Mode.Never).wrap( XorModel.wrap( @@ -67,7 +64,7 @@ object ApplyStreamMapRawInliner { val arrayResultType = ArrayType(mapType.element) val streamVar = VarModel(streamName, StreamType(ScalarType.string)) val canonMap = VarModel(canonName, CanonStreamMapType(arrayResultType)) - val iter = VarModel(iterName, getIterType("iterName_type", mapType.element)) + val iter = VarModel(iterName, mapType.iterType("iterName_type")) val result = VarModel(resultName, CanonStreamType(ScalarType.string)) result -> RestrictionModel(streamVar.name, streamVar.`type`).wrap( CanonicalizeModel(mapVar, CallModel.Export(canonMap)).leaf, diff --git a/semantics/src/main/scala/aqua/semantics/expr/func/ForSem.scala b/semantics/src/main/scala/aqua/semantics/expr/func/ForSem.scala index 8ee6551b0..66c1b75cf 100644 --- a/semantics/src/main/scala/aqua/semantics/expr/func/ForSem.scala +++ b/semantics/src/main/scala/aqua/semantics/expr/func/ForSem.scala @@ -9,6 +9,7 @@ import aqua.raw.value.ValueRaw import aqua.semantics.Prog import aqua.semantics.rules.ValuesAlgebra import aqua.semantics.rules.abilities.AbilitiesAlgebra +import aqua.semantics.rules.mangler.ManglerAlgebra import aqua.semantics.rules.names.NamesAlgebra import aqua.semantics.rules.types.TypesAlgebra import aqua.types.* @@ -26,8 +27,8 @@ class ForSem[S[_]](val expr: ForExpr[S]) extends AnyVal { def program[F[_]: Monad](using V: ValuesAlgebra[S, F], N: NamesAlgebra[S, F], - T: TypesAlgebra[S, F], - A: AbilitiesAlgebra[S, F] + A: AbilitiesAlgebra[S, F], + M: ManglerAlgebra[F] ): Prog[F, Raw] = Prog .around( @@ -81,12 +82,18 @@ object ForSem { )(using V: ValuesAlgebra[S, F], N: NamesAlgebra[S, F], - T: TypesAlgebra[S, F] + M: ManglerAlgebra[F] ): F[Option[ValueRaw]] = (for { value <- V.valueToIterable(iterable) (raw, typ) = value + itemType <- typ match { + case smt: StreamMapType => + val typeName = "-streamMapIter-" + OptionT.liftF(M.rename(typeName).map(s => smt.iterType(s))) + case _ => OptionT.some(typ.element) + } _ <- OptionT.liftF( - N.define(item, typ.element) + N.define(item, itemType) ) } yield raw).value } diff --git a/semantics/src/main/scala/aqua/semantics/expr/func/ParSeqSem.scala b/semantics/src/main/scala/aqua/semantics/expr/func/ParSeqSem.scala index ac9829f08..173d5cb56 100644 --- a/semantics/src/main/scala/aqua/semantics/expr/func/ParSeqSem.scala +++ b/semantics/src/main/scala/aqua/semantics/expr/func/ParSeqSem.scala @@ -8,6 +8,7 @@ import aqua.raw.value.ValueRaw import aqua.semantics.Prog import aqua.semantics.rules.ValuesAlgebra import aqua.semantics.rules.abilities.AbilitiesAlgebra +import aqua.semantics.rules.mangler.ManglerAlgebra import aqua.semantics.rules.names.NamesAlgebra import aqua.semantics.rules.types.TypesAlgebra import aqua.types.{ArrayType, CollectionType, StreamType} @@ -26,7 +27,8 @@ class ParSeqSem[S[_]](val expr: ParSeqExpr[S]) extends AnyVal { V: ValuesAlgebra[S, F], N: NamesAlgebra[S, F], T: TypesAlgebra[S, F], - A: AbilitiesAlgebra[S, F] + A: AbilitiesAlgebra[S, F], + M: ManglerAlgebra[F] ): Prog[F, Raw] = Prog .around( @@ -49,10 +51,7 @@ class ParSeqSem[S[_]](val expr: ParSeqExpr[S]) extends AnyVal { viaVM: List[ValueRaw], ops: Raw )(using - V: ValuesAlgebra[S, F], - N: NamesAlgebra[S, F], - T: TypesAlgebra[S, F], - A: AbilitiesAlgebra[S, F] + V: ValuesAlgebra[S, F] ): F[Raw] = V.valueToRaw(expr.peerId).map((_, iterableVM, ops)).flatMap { case (Some(peerId), Some(vm), FuncOp(op)) => diff --git a/types/src/main/scala/aqua/types/Type.scala b/types/src/main/scala/aqua/types/Type.scala index b669414da..4ca1450ab 100644 --- a/types/src/main/scala/aqua/types/Type.scala +++ b/types/src/main/scala/aqua/types/Type.scala @@ -374,6 +374,9 @@ case class StreamMapType(override val element: DataType) extends MutableStreamTy "contains" -> ArrowType(ProductType(ScalarType.string :: Nil), ProductType(ScalarType.bool :: Nil)) ) + def iterType(name: String) = + StructType(name, NonEmptyMap.of("key" -> ScalarType.string, "value" -> ArrayType(element))) + } object StreamMapType {