From fedd743721c33ccee51b2b6b8efff2b032586329 Mon Sep 17 00:00:00 2001 From: Dima Date: Fri, 27 Jan 2023 17:24:52 +0700 Subject: [PATCH] fix: Canonicalize variable in object creation or copy if variable is a stream (#649) --- .../model/inline/MakeStructRawInliner.scala | 36 +++++++++++-------- .../scala/aqua/model/inline/TagInliner.scala | 12 +++---- .../inline/raw/ApplyIntoCopyRawInliner.scala | 36 ++++++++++--------- model/src/main/scala/aqua/model/OpModel.scala | 13 +++++++ 4 files changed, 59 insertions(+), 38 deletions(-) diff --git a/model/inline/src/main/scala/aqua/model/inline/MakeStructRawInliner.scala b/model/inline/src/main/scala/aqua/model/inline/MakeStructRawInliner.scala index e0763b88f..5c3653a27 100644 --- a/model/inline/src/main/scala/aqua/model/inline/MakeStructRawInliner.scala +++ b/model/inline/src/main/scala/aqua/model/inline/MakeStructRawInliner.scala @@ -12,7 +12,7 @@ import aqua.model.{ import aqua.model.inline.raw.RawInliner import cats.data.Chain import aqua.model.inline.state.{Arrows, Exports, Mangler} -import aqua.raw.value.{MakeStructRaw, LiteralRaw} +import aqua.raw.value.{LiteralRaw, MakeStructRaw} import cats.data.{NonEmptyMap, State} import aqua.model.inline.Inline import aqua.model.inline.RawValueInliner.{unfold, valueToModel} @@ -25,18 +25,24 @@ import cats.syntax.apply.* object MakeStructRawInliner extends RawInliner[MakeStructRaw] { - private def createObj(fields: NonEmptyMap[String, ValueModel], result: VarModel): OpModel.Tree = { - val args = fields.toSortedMap.toList.flatMap { case (name, value) => + private def createObj[S: Mangler]( + fields: NonEmptyMap[String, ValueModel], + result: VarModel + ): State[S, OpModel.Tree] = { + fields.toSortedMap.toList.flatMap { case (name, value) => LiteralModel.fromRaw(LiteralRaw.quote(name)) :: value :: Nil + }.map(TagInliner.canonicalizeIfStream(_, None)).sequence.map { argsWithOps => + val (args, ops) = argsWithOps.unzip + val createOp = + CallServiceModel( + "json", + "obj", + args, + result + ).leaf + SeqModel.wrap((ops.flatten :+ createOp): _*) + } - CallServiceModel( - LiteralModel("\"json\"", ScalarType.string), - "obj", - CallModel( - args, - CallModel.Export(result.name, result.`type`) :: Nil - ) - ).leaf } override def apply[S: Mangler: Exports: Arrows]( @@ -46,11 +52,11 @@ object MakeStructRawInliner extends RawInliner[MakeStructRaw] { for { name <- Mangler[S].findAndForbidName(raw.structType.name + "_obj") foldedFields <- raw.fields.nonEmptyTraverse(unfold(_)) + varModel = VarModel(name, raw.baseType) + valsInline = foldedFields.toSortedMap.values.map(_._2).fold(Inline.empty)(_ |+| _) + fields = foldedFields.map(_._1) + objCreation <- createObj(fields, varModel) } yield { - val varModel = VarModel(name, raw.baseType) - val valsInline = foldedFields.toSortedMap.values.map(_._2).fold(Inline.empty)(_ |+| _) - val fields = foldedFields.map(_._1) - val objCreation = createObj(fields, varModel) ( varModel, Inline( diff --git a/model/inline/src/main/scala/aqua/model/inline/TagInliner.scala b/model/inline/src/main/scala/aqua/model/inline/TagInliner.scala index 4965357a4..99712d087 100644 --- a/model/inline/src/main/scala/aqua/model/inline/TagInliner.scala +++ b/model/inline/src/main/scala/aqua/model/inline/TagInliner.scala @@ -34,7 +34,7 @@ object TagInliner extends Logging { private def none[S]: State[S, (Option[OpModel], Option[OpModel.Tree])] = State.pure(None -> None) - private def fixModel[S: Mangler: Arrows: Exports]( + def canonicalizeIfStream[S: Mangler]( vm: ValueModel, ops: Option[OpModel.Tree] ): State[S, (ValueModel, Option[OpModel.Tree])] = { @@ -125,13 +125,13 @@ object TagInliner extends Logging { for { ld <- valueToModel(left) rd <- valueToModel(right) - ldfixed <- fixModel(ld._1, ld._2) - rdfixed <- fixModel(rd._1, rd._2) + ldCanon <- canonicalizeIfStream(ld._1, ld._2) + rdCanon <- canonicalizeIfStream(rd._1, rd._2) } yield Some( - MatchMismatchModel(ldfixed._1, rdfixed._1, shouldMatch) + MatchMismatchModel(ldCanon._1, rdCanon._1, shouldMatch) ) -> parDesugarPrefixOpt( - ldfixed._2, - rdfixed._2 + ldCanon._2, + rdCanon._2 ) case ForTag(item, iterable, mode) => diff --git a/model/inline/src/main/scala/aqua/model/inline/raw/ApplyIntoCopyRawInliner.scala b/model/inline/src/main/scala/aqua/model/inline/raw/ApplyIntoCopyRawInliner.scala index 9cffb22b8..05a98fc76 100644 --- a/model/inline/src/main/scala/aqua/model/inline/raw/ApplyIntoCopyRawInliner.scala +++ b/model/inline/src/main/scala/aqua/model/inline/raw/ApplyIntoCopyRawInliner.scala @@ -9,7 +9,7 @@ import aqua.model.{ ValueModel, VarModel } -import aqua.model.inline.{Inline, SeqMode} +import aqua.model.inline.{Inline, SeqMode, TagInliner} import aqua.model.inline.MakeStructRawInliner.createObj import aqua.model.inline.RawValueInliner.unfold import aqua.model.inline.state.{Arrows, Exports, Mangler} @@ -25,22 +25,24 @@ import cats.syntax.apply.* object ApplyIntoCopyRawInliner extends Logging { - private def copyObj( + private def copyObj[S: Mangler]( value: VarModel, fields: NonEmptyMap[String, ValueModel], result: VarModel - ): OpModel.Tree = { - val args = fields.toSortedMap.toList.flatMap { case (name, value) => + ): State[S, OpModel.Tree] = { + fields.toSortedMap.toList.flatMap { case (name, value) => LiteralModel.fromRaw(LiteralRaw.quote(name)) :: value :: Nil - } - CallServiceModel( - LiteralModel("\"json\"", ScalarType.string), - "puts", - CallModel( + }.map(TagInliner.canonicalizeIfStream(_, None)).sequence.map { argsWithOps => + val (args, ops) = argsWithOps.unzip + val copyOp = CallServiceModel( + "json", + "puts", value +: args, - CallModel.Export(result.name, result.`type`) :: Nil - ) - ).leaf + result + ).leaf + SeqModel.wrap((ops.flatten :+ copyOp): _*) + } + } def apply[S: Mangler: Exports: Arrows]( @@ -50,16 +52,16 @@ object ApplyIntoCopyRawInliner extends Logging { for { name <- Mangler[S].findAndForbidName(value.name + "_obj_copy") foldedFields <- intoCopy.fields.nonEmptyTraverse(unfold(_)) + varModel = VarModel(name, value.baseType) + valsInline = foldedFields.toSortedMap.values.map(_._2).fold(Inline.empty)(_ |+| _) + fields = foldedFields.map(_._1) + objCopy <- copyObj(value, fields, varModel) } yield { - val varModel = VarModel(name, value.baseType) - val valsInline = foldedFields.toSortedMap.values.map(_._2).fold(Inline.empty)(_ |+| _) - val fields = foldedFields.map(_._1) - val objCreation = copyObj(value, fields, varModel) ( varModel, Inline( valsInline.flattenValues, - Chain.one(SeqModel.wrap((valsInline.predo :+ objCreation).toList: _*)), + Chain.one(SeqModel.wrap((valsInline.predo :+ objCopy).toList: _*)), SeqMode ) ) diff --git a/model/src/main/scala/aqua/model/OpModel.scala b/model/src/main/scala/aqua/model/OpModel.scala index d8ce8ebc3..f2a5ffcde 100644 --- a/model/src/main/scala/aqua/model/OpModel.scala +++ b/model/src/main/scala/aqua/model/OpModel.scala @@ -7,6 +7,7 @@ import cats.Show import cats.Eval import cats.data.NonEmptyList import aqua.tree.{TreeNode, TreeNodeCompanion} +import aqua.types.ScalarType import scala.annotation.tailrec @@ -140,6 +141,18 @@ case class CallServiceModel(serviceId: ValueModel, funcName: String, call: CallM override def exportsVarNames: Set[String] = call.exportTo.map(_.name).toSet } +object CallServiceModel { + def apply(serviceId: String, funcName: String, args: List[ValueModel], result: VarModel): CallServiceModel = + CallServiceModel( + LiteralModel(s"\"$serviceId\"", ScalarType.string), + funcName, + CallModel( + args, + CallModel.Export(result.name, result.`type`) :: Nil + ) + ) +} + case class CanonicalizeModel(operand: ValueModel, exportTo: CallModel.Export) extends ForceExecModel {