diff --git a/model/inline/src/main/scala/aqua/model/inline/RawValueInliner.scala b/model/inline/src/main/scala/aqua/model/inline/RawValueInliner.scala index 604733fd..d0aecc61 100644 --- a/model/inline/src/main/scala/aqua/model/inline/RawValueInliner.scala +++ b/model/inline/src/main/scala/aqua/model/inline/RawValueInliner.scala @@ -3,7 +3,7 @@ package aqua.model.inline import aqua.model.* import aqua.model.inline.Inline.MergeMode.* import aqua.model.inline.raw.* -import aqua.model.inline.state.{Arrows, Counter, Exports, Mangler} +import aqua.model.inline.state.{Counter, Exports, Mangler} import aqua.raw.ops.* import aqua.raw.value.* import aqua.types.{ArrayType, LiteralType, OptionType, StreamType} @@ -23,21 +23,26 @@ object RawValueInliner extends Logging { import aqua.model.inline.Inline.* - private[inline] def unfold[S: Mangler: Exports: Arrows]( + private[inline] def unfold[S: Mangler: Exports]( raw: ValueRaw, propertiesAllowed: Boolean = true - ): State[S, (ValueModel, Inline)] = for { + ): State[S, (Exports.Export, Inline)] = for { optimized <- StateT.liftF(Optimization.optimize(raw)) _ <- StateT.liftF(Eval.later(logger.trace("OPTIMIZIED " + optimized))) result <- optimized match { case VarRaw(name, t) => for { - exports <- Exports[S].exports - model = VarModel(name, t, Chain.empty).resolveWith(exports) - } yield model -> Inline.empty + maybeExport <- Exports[S].get(name) + model = VarModel(name, t) + exp = maybeExport.getOrElse( + Exports.Export.Value(model) + ) + } yield exp -> Inline.empty case LiteralRaw(value, t) => - State.pure(LiteralModel(value, t) -> Inline.empty) + val model = LiteralModel(value, t) + val exp = Exports.Export.Value(model) + State.pure(exp -> Inline.empty) case alr: ApplyPropertyRaw => ApplyPropertiesRawInliner(alr, propertiesAllowed) @@ -115,7 +120,7 @@ object RawValueInliner extends Logging { ): State[S, (CallModel, Option[OpModel.Tree])] = { valueListToModel(call.args).flatMap { args => if (flatStreamArguments) - args.map{ arg => + args.map { arg => TagInliner.flat(arg._1, arg._2, true) }.sequence else diff --git a/model/inline/src/main/scala/aqua/model/inline/raw/ApplyPropertiesRawInliner.scala b/model/inline/src/main/scala/aqua/model/inline/raw/ApplyPropertiesRawInliner.scala index 7327efc8..36eec7b3 100644 --- a/model/inline/src/main/scala/aqua/model/inline/raw/ApplyPropertiesRawInliner.scala +++ b/model/inline/src/main/scala/aqua/model/inline/raw/ApplyPropertiesRawInliner.scala @@ -6,7 +6,8 @@ import aqua.model.ValueModel.Ability import aqua.model.inline.Inline import aqua.model.inline.Inline.MergeMode.* import aqua.model.inline.RawValueInliner.unfold -import aqua.model.inline.state.{Arrows, Exports, Mangler} +import aqua.model.inline.state.Exports.Export +import aqua.model.inline.state.{Exports, Mangler} import aqua.raw.value.* import aqua.types.* @@ -24,7 +25,7 @@ import scribe.Logging object ApplyPropertiesRawInliner extends RawInliner[ApplyPropertyRaw] with Logging { // in perspective literals can have properties and functors (like `nil` with length) - def flatLiteralWithProperties[S: Mangler: Exports: Arrows]( + def flatLiteralWithProperties[S: Mangler: Exports]( literal: LiteralModel, inl: Inline, properties: Chain[PropertyModel] @@ -45,7 +46,7 @@ object ApplyPropertiesRawInliner extends RawInliner[ApplyPropertyRaw] with Loggi } } - private def unfoldAbilityProperty[S: Mangler: Exports: Arrows]( + private def unfoldAbilityProperty[S: Mangler: Exports]( varModel: VarModel, abilityType: NamedType, p: PropertyRaw @@ -112,7 +113,7 @@ object ApplyPropertiesRawInliner extends RawInliner[ApplyPropertyRaw] with Loggi ) } - private[inline] def unfoldProperty[S: Mangler: Exports: Arrows]( + private[inline] def unfoldProperty[S: Mangler: Exports]( varModel: VarModel, p: PropertyRaw ): State[S, (VarModel, Inline)] = @@ -172,7 +173,7 @@ object ApplyPropertiesRawInliner extends RawInliner[ApplyPropertyRaw] with Loggi private case class PropertyRawWithModel(raw: PropertyRaw, model: Option[PropertyModel]) // Unfold properties that we can process in parallel - private def optimizeProperties[S: Mangler: Exports: Arrows]( + private def optimizeProperties[S: Mangler: Exports]( properties: Chain[PropertyRaw] ): State[S, (Chain[PropertyRawWithModel], Inline)] = { properties.map { @@ -211,7 +212,7 @@ object ApplyPropertiesRawInliner extends RawInliner[ApplyPropertyRaw] with Loggi }.sequence.map(_.toList.unzip.bimap(Chain.fromSeq, _.combineAll)) } - private def unfoldProperties[S: Mangler: Exports: Arrows]( + private def unfoldProperties[S: Mangler: Exports]( prevInline: Inline, vm: VarModel, properties: Chain[PropertyRaw], @@ -263,7 +264,7 @@ object ApplyPropertiesRawInliner extends RawInliner[ApplyPropertyRaw] with Loggi /** * Unfold `stream[idx]` */ - private def unfoldStreamGate[S: Mangler: Exports: Arrows]( + private def unfoldStreamGate[S: Mangler: Exports]( streamName: String, streamType: StreamType, idx: ValueRaw @@ -325,21 +326,18 @@ object ApplyPropertiesRawInliner extends RawInliner[ApplyPropertyRaw] with Loggi mergeMode = SeqMode ) - private def unfoldRawWithProperties[S: Mangler: Exports: Arrows]( + private def unfoldRawWithProperties[S: Mangler: Exports]( raw: ValueRaw, properties: Chain[PropertyRaw], propertiesAllowed: Boolean - ): State[S, (ValueModel, Inline)] = + ): State[S, (Export, Inline)] = (raw, properties.uncons) match { - /** - * To inline - */ case ( vr @ VarRaw(_, st @ StreamType(_)), Some(IntoIndexRaw(idx, _), otherProperties) ) => unfold(vr).flatMap { - case (VarModel(nameVM, _, _), inl) => + case (Export.Value(VarModel(nameVM, _, _)), inl) => for { gateInlined <- unfoldStreamGate(nameVM, st, idx) (gateVM, gateInline) = gateInlined @@ -362,12 +360,8 @@ object ApplyPropertiesRawInliner extends RawInliner[ApplyPropertyRaw] with Loggi unfoldProperties(prevInline, vm, properties, propertiesAllowed) // To coerce types .map(identity) - case (l: LiteralModel, inline) => - flatLiteralWithProperties( - l, - inline, - Chain.empty - ).flatMap { (varModel, prevInline) => + case (l: LiteralModel, inl) => + flatLiteralWithProperties(l, inl, Chain.empty).flatMap { (varModel, prevInline) => unfoldProperties(prevInline, varModel, properties, propertiesAllowed).map { case (v, i) => v -> i @@ -389,10 +383,10 @@ object ApplyPropertiesRawInliner extends RawInliner[ApplyPropertyRaw] with Loggi flatten = VarModel(nn, varModel.`type`) } yield flatten -> Inline.tree(FlattenModel(varModel, flatten.name).leaf) - override def apply[S: Mangler: Exports: Arrows]( + override def apply[S: Mangler: Exports]( apr: ApplyPropertyRaw, propertiesAllowed: Boolean - ): State[S, (ValueModel, Inline)] = { + ): State[S, (Export, Inline)] = { val (raw, properties) = apr.unwind unfoldRawWithProperties(raw, properties, propertiesAllowed) } diff --git a/model/inline/src/main/scala/aqua/model/inline/state/Arrows.scala b/model/inline/src/main/scala/aqua/model/inline/state/Arrows.scala deleted file mode 100644 index e6f53efc..00000000 --- a/model/inline/src/main/scala/aqua/model/inline/state/Arrows.scala +++ /dev/null @@ -1,142 +0,0 @@ -package aqua.model.inline.state - -import aqua.model.ValueModel -import aqua.model.{ArgsCall, FuncArrow} -import aqua.raw.arrow.FuncRaw -import aqua.types.* - -import cats.data.State -import cats.instances.list.* -import cats.syntax.functor.* -import cats.syntax.option.* -import cats.syntax.show.* -import cats.syntax.traverse.* - -/** - * State algebra for resolved arrows - * - * @tparam S - * State - */ -trait Arrows[S] extends Scoped[S] { - self => - def save(name: String, arrow: FuncArrow): State[S, Unit] - - /** - * Arrow is resolved – save it to the state [[S]] - * - * @param arrow resolved arrow - * @param topology captured topology - */ - final def resolved( - arrow: FuncRaw, - topology: Option[String] - )(using Exports[S]): State[S, Unit] = - for { - arrs <- arrows - capturedVars <- Exports[S].gather(arrow.capturedVars.toSeq) - capturedArrows = arrs.filterKeys(arrow.capturedVars).toMap ++ - Arrows.arrowsByValues(arrs, capturedVars) - funcArrow = FuncArrow.fromRaw(arrow, capturedArrows, capturedVars, topology) - _ <- save(arrow.name, funcArrow) - } yield () - - /** - * Save arrows to the state [[S]] - * - * @param arrows - * Resolved arrows, accessible by key name which could differ from arrow's name - */ - final def resolved(arrows: Map[String, FuncArrow]): State[S, Unit] = - arrows.toList.traverse(save).void - - /** - * All arrows available for use in scope - */ - val arrows: State[S, Map[String, FuncArrow]] - - /** - * Pick a subset of arrows by names - * - * @param names - * What arrows should be taken - */ - def pickArrows(names: Set[String]): State[S, Map[String, FuncArrow]] = - arrows.map(_.view.filterKeys(names).toMap) - - /** - * Take arrows selected by the function call arguments - * - * @param args - * @return - */ - def argsArrows(args: ArgsCall): State[S, Map[String, FuncArrow]] = - arrows.map(args.arrowArgsMap) - - /** - * Changes the [[S]] type to [[R]] - * - * @param f - * Lens getter - * @param g - * Lens setter - * @tparam R - * New state type - */ - def transformS[R](f: R => S, g: (R, S) => R): Arrows[R] = new Arrows[R] { - - override def save(name: String, arrow: FuncArrow): State[R, Unit] = - self.save(name, arrow).transformS(f, g) - - override val arrows: State[R, Map[String, FuncArrow]] = self.arrows.transformS(f, g) - - override val purge: State[R, R] = - self.purgeR(f, g) - - override protected def fill(s: R): State[R, Unit] = - self.fillR(s, f, g) - - } -} - -object Arrows { - - /** - * Retrieve all arrows that correspond to values - */ - def arrowsByValues( - arrows: Map[String, FuncArrow], - values: Map[String, ValueModel] - ): Map[String, FuncArrow] = { - val arrowKeys = arrows.keySet ++ arrows.values.map(_.funcName) - val varsKeys = values.keySet ++ values.values.collect { case ValueModel.Arrow(vm, _) => - vm.name - } - val keys = arrowKeys.intersect(varsKeys) - - arrows.filter { case (arrowName, arrow) => - keys.contains(arrowName) || keys.contains(arrow.funcName) - } - } - - def apply[S](implicit arrows: Arrows[S]): Arrows[S] = arrows - - // Default implementation with the most straightforward state – just a Map - object Simple extends Arrows[Map[String, FuncArrow]] { - - override def save(name: String, arrow: FuncArrow): State[Map[String, FuncArrow], Unit] = - State.modify(_ + (name -> arrow)) - - override val arrows: State[Map[String, FuncArrow], Map[String, FuncArrow]] = - State.get - - override val purge: State[Map[String, FuncArrow], Map[String, FuncArrow]] = - for { - s <- State.get - _ <- State.set(Map.empty) - } yield s - - override protected def fill(s: Map[String, FuncArrow]): State[Map[String, FuncArrow], Unit] = - State.set(s) - } -} diff --git a/model/inline/src/main/scala/aqua/model/inline/state/Exports.scala b/model/inline/src/main/scala/aqua/model/inline/state/Exports.scala index b5d98d70..e8c39867 100644 --- a/model/inline/src/main/scala/aqua/model/inline/state/Exports.scala +++ b/model/inline/src/main/scala/aqua/model/inline/state/Exports.scala @@ -1,260 +1,107 @@ package aqua.model.inline.state +import aqua.model.FuncArrow import aqua.model.ValueModel.Ability import aqua.model.{LiteralModel, ValueModel, VarModel} +import aqua.types.ServiceType import aqua.types.{AbilityType, GeneralAbilityType, NamedType} import cats.data.{NonEmptyList, State} /** * Exports – trace values available in the scope - * @tparam S - * State + * @tparam S - State */ trait Exports[S] extends Scoped[S] { self => /** - * [[value]] is accessible as [[exportName]] - * @param exportName - * Name - * @param value - * Value + * [[exp]] is accessible as [[name]] + * @param name - Name + * @param exp - Export */ - def resolved(exportName: String, value: ValueModel): State[S, Unit] - - /** - * [[value]] is accessible as [[abilityExportName]].[[fieldName]] - * - * @param abilityExportName - * Ability Name - * @param fieldName - * Field Name - * @param value - * Value - */ - def resolveAbilityField( - abilityExportName: String, - fieldName: String, - value: ValueModel - ): State[S, Unit] - - /** - * Rename ability prefix to new one - */ - def copyWithAbilityPrefix(prefix: String, newPrefix: String): State[S, Unit] - - /** - * Get name of last linked VarModel. If the last element is not VarModel, return None - */ - def getLastVarName(name: String): State[S, Option[String]] - - /** - * Rename names in variables - */ - def renameVariables(renames: Map[String, String]): State[S, Unit] + def resolved(name: String, exp: Exports.Export): State[S, Unit] /** * Resolve the whole map of exports - * @param exports - * name -> value + * @param exports - name -> export */ - def resolved(exports: Map[String, ValueModel]): State[S, Unit] + def resolved(exports: Map[String, Exports.Export]): State[S, Unit] /** - * Get all export keys + * Rename names in variables + * @param renames - oldName -> newName */ - def getKeys: State[S, Set[String]] + def renameExports(renames: Map[String, String]): State[S, Unit] /** - * Get ability field from export - * @param name variable ability name - * @param field ability field + * Get export by name + * @param name - Name */ - def getAbilityField(name: String, field: String): State[S, Option[ValueModel]] - - /** - * Get all the values available in the scope - */ - val exports: State[S, Map[String, ValueModel]] - - final def gather(names: Seq[String]): State[S, Map[String, ValueModel]] = - exports.map(Exports.gatherFrom(names, _)) + def get(name: String): State[S, Option[Exports.Export]] /** * Change [[S]] to [[R]] */ def transformS[R](f: R => S, g: (R, S) => R): Exports[R] = new Exports[R] { - override def resolved(exportName: String, value: ValueModel): State[R, Unit] = - self.resolved(exportName, value).transformS(f, g) + override def resolved(name: String, exp: Exports.Export): State[R, Unit] = + self.resolved(name, exp).transformS(f, g) - override def resolved(exports: Map[String, ValueModel]): State[R, Unit] = + override def resolved(exports: Map[String, Exports.Export]): State[R, Unit] = self.resolved(exports).transformS(f, g) - override def resolveAbilityField( - abilityExportName: String, - fieldName: String, - value: ValueModel - ): State[R, Unit] = - self.resolveAbilityField(abilityExportName, fieldName, value).transformS(f, g) + override def renameExports(renames: Map[String, String]): State[R, Unit] = + self.renameExports(renames).transformS(f, g) - override def copyWithAbilityPrefix(prefix: String, newPrefix: String): State[R, Unit] = - self.copyWithAbilityPrefix(prefix, newPrefix).transformS(f, g) - - override def getLastVarName(name: String): State[R, Option[String]] = - self.getLastVarName(name).transformS(f, g) - - override def renameVariables(renames: Map[String, String]): State[R, Unit] = - self.renameVariables(renames).transformS(f, g) - - override def getKeys: State[R, Set[String]] = - self.getKeys.transformS(f, g) - - override def getAbilityField(name: String, field: String): State[R, Option[ValueModel]] = - self.getAbilityField(name, field).transformS(f, g) - - override val exports: State[R, Map[String, ValueModel]] = - self.exports.transformS(f, g) - - override val purge: State[R, R] = - self.purgeR(f, g) - - override protected def fill(s: R): State[R, Unit] = - self.fillR(s, f, g) + override def get(name: String): State[R, Option[Exports.Export]] = + self.get(name).transformS(f, g) } } object Exports { + + enum Export { + case Value(value: ValueModel) + case Arrow(arrow: FuncArrow) + case Ability(abilityType: AbilityType, values: Map[String, Export]) + case Service(serviceType: ServiceType, arrows: Map[String, Arrow]) + case Context(values: Map[String, Export]) + } + + object Export { + + extension (c: Export.Context) { + + def resolved(name: String, exp: Export): Export.Context = + c.copy(values = c.values + (name -> exp)) + + def resolved(exports: Map[String, Export]): Export.Context = + c.copy(values = c.values ++ exports) + + def renameExports(renames: Map[String, String]): Export.Context = + c.copy(values = c.values.map { case (name, exp) => + renames.getOrElse(name, name) -> exp + }) + } + } + def apply[S](using exports: Exports[S]): Exports[S] = exports - /** - * Gather all the values that are related to the given names - * (ability fields) - * - * @param names names of variables - * @param state exports state - */ - def gatherFrom( - names: Seq[String], - state: Map[String, ValueModel] - ): Map[String, ValueModel] = { - val related = for { - variable <- names - exp <- state.get(variable).toList - at <- exp.`type` match { - case at: GeneralAbilityType => at :: Nil - case _ => Nil - } - field <- at.allFields.toNel.toList - (fieldName, _) = field - } yield AbilityType.fullName(variable, fieldName) + object Simple extends Exports[Export.Context] { - state.filterKeys(names.toSet ++ related).toMap - } + type ST[A] = State[Export.Context, A] - // Get last linked VarModel - def getLastValue(name: String, state: Map[String, ValueModel]): Option[ValueModel] = { - state.get(name) match { - case Some(vm @ VarModel(n, _, _)) => - if (name == n) Option(vm) - else getLastValue(n, state).orElse(Option(vm)) - case lm @ Some(LiteralModel(_, _)) => - lm - case _ => - None - } - } + override def resolved(name: String, exp: Export): ST[Unit] = + State.modify(_.resolved(name, exp)) - object Simple extends Exports[Map[String, ValueModel]] { + override def resolved(exports: Map[String, Export]): ST[Unit] = + State.modify(_.resolved(exports)) - // Make links from one set of abilities to another (for ability assignment) - private def getAbilityPairs( - oldName: String, - newName: String, - at: NamedType, - state: Map[String, ValueModel] - ): NonEmptyList[(String, ValueModel)] = { - at.fields.toNel.flatMap { - case (n, at @ AbilityType(_, _)) => - val newFullName = AbilityType.fullName(newName, n) - val oldFullName = AbilityType.fullName(oldName, n) - getAbilityPairs(oldFullName, newFullName, at, state) - case (n, t) => - val newFullName = AbilityType.fullName(newName, n) - val oldFullName = AbilityType.fullName(oldName, n) - // put link on last variable in chain - val lastVar = Exports.getLastValue(oldFullName, state) - NonEmptyList.of((newFullName, lastVar.getOrElse(VarModel(oldFullName, t)))) - } - } + override def renameExports(renames: Map[String, String]): ST[Unit] = + State.modify(_.renameExports(renames)) - override def resolved( - exportName: String, - value: ValueModel - ): State[Map[String, ValueModel], Unit] = State.modify { state => - value match { - case Ability(vm, at) if vm.properties.isEmpty => - val pairs = getAbilityPairs(vm.name, exportName, at, state) - state ++ pairs.toList.toMap + (exportName -> value) - case _ => state + (exportName -> value) - } - } - - override def getLastVarName(name: String): State[Map[String, ValueModel], Option[String]] = - State.get.map(st => getLastValue(name, st).collect { case VarModel(name, _, _) => name }) - - override def resolved(exports: Map[String, ValueModel]): State[Map[String, ValueModel], Unit] = - State.modify(_ ++ exports) - - override def resolveAbilityField( - abilityExportName: String, - fieldName: String, - value: ValueModel - ): State[Map[String, ValueModel], Unit] = - State.modify(_ + (AbilityType.fullName(abilityExportName, fieldName) -> value)) - - override def copyWithAbilityPrefix( - prefix: String, - newPrefix: String - ): State[Map[String, ValueModel], Unit] = - State.modify { state => - state.flatMap { - case (k, v) if k.startsWith(prefix) => - List(k.replaceFirst(prefix, newPrefix) -> v, k -> v) - case (k, v) => List(k -> v) - } - } - - override def renameVariables( - renames: Map[String, String] - ): State[Map[String, ValueModel], Unit] = - State.modify { - _.map { - case (k, vm @ VarModel(name, _, _)) if renames.contains(name) => - k -> vm.copy(name = renames.getOrElse(name, name)) - case (k, v) => k -> v - } - } - - override def getKeys: State[Map[String, ValueModel], Set[String]] = State.get.map(_.keySet) - - override def getAbilityField( - name: String, - field: String - ): State[Map[String, ValueModel], Option[ValueModel]] = - State.get.map(_.get(AbilityType.fullName(name, field))) - - override val exports: State[Map[String, ValueModel], Map[String, ValueModel]] = - State.get - - override val purge: State[Map[String, ValueModel], Map[String, ValueModel]] = - for { - s <- State.get - _ <- State.set(Map.empty) - } yield s - - override protected def fill(s: Map[String, ValueModel]): State[Map[String, ValueModel], Unit] = - State.set(s) + override def get(name: String): ST[Option[Export]] = + State.inspect(_.values.get(name)) } }