Savepoint

This commit is contained in:
InversionSpaces 2024-01-04 10:40:28 +00:00
parent 9aec470d38
commit a73996d8de
4 changed files with 86 additions and 382 deletions

View File

@ -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)

View File

@ -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)
}

View File

@ -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)
}
}

View File

@ -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
}
}
object Simple extends Exports[Map[String, ValueModel]] {
// 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 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 resolved(name: String, exp: Export): ST[Unit] =
State.modify(_.resolved(name, exp))
override def resolved(exports: Map[String, Export]): ST[Unit] =
State.modify(_.resolved(exports))
override def renameExports(renames: Map[String, String]): ST[Unit] =
State.modify(_.renameExports(renames))
override def get(name: String): ST[Option[Export]] =
State.inspect(_.values.get(name))
}
}