feat: copy structures implementation [fixes LNG-102] (#646)

This commit is contained in:
Dima 2023-01-20 12:02:05 +07:00 committed by GitHub
parent d0a9db5164
commit 50f0723a32
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
21 changed files with 504 additions and 358 deletions

View File

@ -1,6 +1,6 @@
aqua FooBars declares wait
aqua FooBars declares getObjAssign
export wait
export getObjAssign
data Record:
relay_id: []string
@ -83,11 +83,12 @@ data SomeObj:
num: u64
inner: InnerObj
func wait(i: []u32) -> SomeObj:
<- SomeObj(str = "some str",
num = 4,
inner = InnerObj(arr = ["a", "b", "c"], num = i[2])
)
-- func wait(i: []u32) -> SomeObj:
-- obj = SomeObj(str = "some str",
-- num = 4,
-- inner = InnerObj(arr = ["a", "b", "c"], num = i[2])
-- )
-- <- obj.copy(str = "ululu")
-- func a(nums: []u32) -> []u32:
-- <- nums
@ -95,4 +96,25 @@ func wait(i: []u32) -> SomeObj:
-- func some():
-- a([1,2,3,4])
func getObjAssign(arr: []string) -> string:
streamJ: *[]string
streamJ <<- ["111", "222"]
streamJ <<- ["333", "444"]
<- streamJ[arr.length][1]
-- func getObjAssign(arr: []string) -> string:
-- stream: *[]u32
-- stream <<- [0]
-- a = stream[arr.length - 1][0]
-- b = arr[a]
-- <- b
-- func getObjAssign() -> SomeObj, SomeObj, u32:
-- obj = SomeObj(str = "first str",
-- num = 5,
-- inner = InnerObj(arr = ["d", "e", "f"], num = 5)
-- )
-- copiedObj = obj.copy(str = "some str", inner = obj.inner.copy(arr = ["a", "b", "c"])).copy(num = 6)
-- <- obj, copiedObj, copiedObj.inner.copy(arr = ["g"]).arr.length

View File

@ -231,10 +231,6 @@ class AquaCompilerSpec extends AnyFlatSpec with Matchers {
)
),
join(results, LiteralModel.fromRaw(LiteralRaw.number(2))),
ApRes(
VarModel("results_gate", ArrayType(ScalarType.string), Chain(IntoIndexModel("2", ScalarType.string))),
CallModel.Export("results_gate-0", ScalarType.string)
).leaf,
CanonRes(results, init, CallModel.Export(canonResult.name, canonResult.`type`)).leaf,
ApRes(
canonResult,

View File

@ -6,18 +6,21 @@ import aqua.raw.value.ValueRaw
import cats.Monoid
import cats.data.Chain
import scala.collection.immutable.ListMap
sealed trait MergeMode
object SeqMode extends MergeMode
object ParMode extends MergeMode
/**
*
* @param flattenValues values that need to be resolved before `predo`
* @param flattenValues values that need to be resolved before `predo`.
* ListMap for keeping order of values (mostly for debugging purposes)
* @param predo operations tree
* @param mergeMode how `flattenValues` and `predo` must be merged
*/
private[inline] case class Inline(
flattenValues: Map[String, ValueRaw] = Map.empty,
flattenValues: ListMap[String, ValueRaw] = ListMap.empty,
predo: Chain[OpModel.Tree] = Chain.empty,
mergeMode: MergeMode = ParMode
)
@ -26,7 +29,7 @@ private[inline] case class Inline(
private[inline] object Inline {
val empty: Inline = Inline()
def preload(pairs: (String, ValueRaw)*): Inline = Inline(pairs.toMap)
def preload(pairs: (String, ValueRaw)*): Inline = Inline(ListMap.from(pairs))
def tree(tr: OpModel.Tree): Inline = Inline(predo = Chain.one(tr))

View File

@ -33,9 +33,6 @@ object RawValueInliner extends Logging {
case alr: ApplyPropertyRaw =>
ApplyPropertiesRawInliner(alr, propertiesAllowed)
case alr: ApplyFunctorRaw =>
ApplyFunctorRawInliner(alr, propertiesAllowed)
case agr: ApplyGateRaw =>
ApplyGateRawInliner(agr, propertiesAllowed)
@ -91,7 +88,8 @@ object RawValueInliner extends Logging {
}
}.map{ predo =>
inline.mergeMode match
case SeqMode => SeqModel.wrap((inline.predo.toList ++ predo):_*) :: Nil
case SeqMode =>
SeqModel.wrap((inline.predo.toList ++ predo):_*) :: Nil
case ParMode => inline.predo.toList ::: predo
}
}

View File

@ -10,29 +10,29 @@ import aqua.model.{
ValueModel,
VarModel
}
import aqua.model.inline.Inline
import aqua.model.inline.{Inline, SeqMode}
import aqua.model.inline.state.{Arrows, Exports, Mangler}
import aqua.raw.value.ApplyFunctorRaw
import aqua.raw.value.{FunctorRaw, ValueRaw}
import cats.data.State
import cats.data.Chain
import aqua.model.inline.RawValueInliner.unfold
import aqua.types.{BoxType, CanonStreamType, StreamType, ArrayType}
import aqua.types.{ArrayType, BoxType, CanonStreamType, StreamType}
import cats.syntax.monoid.*
import scribe.Logging
object ApplyFunctorRawInliner extends RawInliner[ApplyFunctorRaw] with Logging {
object ApplyFunctorRawInliner extends Logging {
override def apply[S: Mangler: Exports: Arrows](
afr: ApplyFunctorRaw,
propertyAllowed: Boolean
): State[S, (ValueModel, Inline)] = {
val functorModel = FunctorModel(afr.functor.name, afr.functor.`type`)
def apply[S: Mangler: Exports: Arrows](
value: ValueModel,
functor: FunctorRaw
): State[S, (VarModel, Inline)] = {
val functorModel = FunctorModel(functor.name, functor.`type`)
unfold(afr.value).flatMap {
case (v @ VarModel(name, bt, _), inl) =>
value match {
case v @ VarModel(name, bt, _) =>
for {
apName <- Mangler[S].findAndForbidName(name + "_to_functor")
resultName <- Mangler[S].findAndForbidName(s"${name}_${afr.functor.name}")
resultName <- Mangler[S].findAndForbidName(s"${name}_${functor.name}")
(apVar, flat) = {
bt match {
case StreamType(el) =>
@ -46,21 +46,22 @@ object ApplyFunctorRawInliner extends RawInliner[ApplyFunctorRaw] with Logging {
}
}
} yield {
val tree = inl |+| Inline.tree(
SeqModel.wrap(
val tree = Inline(
predo = Chain.one(SeqModel.wrap(
flat,
FlattenModel(apVar, resultName).leaf
)
)),
mergeMode = SeqMode
)
VarModel(resultName, afr.functor.`type`) -> tree
VarModel(resultName, functor.`type`) -> tree
}
case (l @ LiteralModel(_, _), inl) =>
ApplyPropertiesRawInliner.flatLiteralWithProperties(l, inl, Chain.one(functorModel), afr.functor.`type`)
case v =>
// unexpected, properties are prohibited for literals
logger.error(s"Unexpected. Properties are prohibited for literals. Literal: '$v'")
State.pure(v)
case l @ LiteralModel(_, _) =>
ApplyPropertiesRawInliner.flatLiteralWithProperties(
l,
Inline.empty,
Chain.one(functorModel)
)
}
}
}

View File

@ -36,6 +36,31 @@ object ApplyGateRawInliner extends RawInliner[ApplyGateRaw] with Logging {
val incrVar = VarModel(uniqueIdxIncr, ScalarType.u32)
// To wait for the element of a stream by the given index, the following model is generated:
// (seq
// (seq
// (seq
// (call %init_peer_id% ("math" "add") [0 1] stream_incr)
// (fold $stream s
// (seq
// (seq
// (ap s $stream_test)
// (canon %init_peer_id% $stream_test #stream_iter_canon)
// )
// (xor
// (match #stream_iter_canon.length stream_incr
// (null)
// )
// (next s)
// )
// )
// (never)
// )
// )
// (canon %init_peer_id% $stream_test #stream_result_canon)
// )
// (ap #stream_result_canon stream_gate)
// )
val gate = RestrictionModel(varSTest.name, true).wrap(
increment(idxModel, incrVar),
ForModel(iter.name, VarModel(afr.name, afr.streamType), Some(ForModel.NeverMode)).wrap(

View File

@ -0,0 +1,69 @@
package aqua.model.inline.raw
import aqua.model.{
CallModel,
CallServiceModel,
LiteralModel,
OpModel,
SeqModel,
ValueModel,
VarModel
}
import aqua.model.inline.{Inline, SeqMode}
import aqua.model.inline.MakeStructRawInliner.createObj
import aqua.model.inline.RawValueInliner.unfold
import aqua.model.inline.state.{Arrows, Exports, Mangler}
import aqua.raw.value.{IntoCopyRaw, LiteralRaw}
import aqua.types.ScalarType
import cats.data.{Chain, NonEmptyMap, State}
import scribe.Logging
import cats.syntax.traverse.*
import cats.syntax.monoid.*
import cats.syntax.functor.*
import cats.syntax.flatMap.*
import cats.syntax.apply.*
object ApplyIntoCopyRawInliner extends Logging {
private def copyObj(
value: VarModel,
fields: NonEmptyMap[String, ValueModel],
result: VarModel
): OpModel.Tree = {
val args = fields.toSortedMap.toList.flatMap { case (name, value) =>
LiteralModel.fromRaw(LiteralRaw.quote(name)) :: value :: Nil
}
CallServiceModel(
LiteralModel("\"json\"", ScalarType.string),
"puts",
CallModel(
value +: args,
CallModel.Export(result.name, result.`type`) :: Nil
)
).leaf
}
def apply[S: Mangler: Exports: Arrows](
value: VarModel,
intoCopy: IntoCopyRaw
): State[S, (VarModel, Inline)] = {
for {
name <- Mangler[S].findAndForbidName(value.name + "_obj_copy")
foldedFields <- intoCopy.fields.nonEmptyTraverse(unfold(_))
} 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: _*)),
SeqMode
)
)
}
}
}

View File

@ -1,28 +1,60 @@
package aqua.model.inline.raw
import aqua.model.{CallModel, CallServiceModel, CanonicalizeModel, FlattenModel, ForModel, FunctorModel, IntoFieldModel, IntoIndexModel, LiteralModel, MatchMismatchModel, NextModel, PropertyModel, PushToStreamModel, RestrictionModel, SeqModel, ValueModel, VarModel, XorModel}
import aqua.model.{
CallModel,
CallServiceModel,
CanonicalizeModel,
FlattenModel,
ForModel,
FunctorModel,
IntoFieldModel,
IntoIndexModel,
LiteralModel,
MatchMismatchModel,
NextModel,
OpModel,
PropertyModel,
PushToStreamModel,
RestrictionModel,
SeqModel,
ValueModel,
VarModel,
XorModel
}
import aqua.model.inline.Inline
import aqua.model.inline.SeqMode
import aqua.model.inline.RawValueInliner.unfold
import aqua.model.inline.state.{Arrows, Exports, Mangler}
import aqua.raw.value.{ApplyFunctorRaw, ApplyGateRaw, ApplyPropertyRaw, CallArrowRaw, FunctorRaw, IntoFieldRaw, IntoIndexRaw, LiteralRaw, PropertyRaw, ValueRaw, VarRaw}
import aqua.raw.value.{
ApplyGateRaw,
ApplyPropertyRaw,
CallArrowRaw,
FunctorRaw,
IntoCopyRaw,
IntoFieldRaw,
IntoIndexRaw,
LiteralRaw,
PropertyRaw,
ValueRaw,
VarRaw
}
import aqua.types.{ArrayType, CanonStreamType, ScalarType, StreamType, Type}
import cats.Eval
import cats.data.{Chain, IndexedStateT, State}
import cats.syntax.monoid.*
import cats.instances.list.*
import scribe.Logging
object ApplyPropertiesRawInliner extends RawInliner[ApplyPropertyRaw] {
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](
literal: LiteralModel,
inl: Inline,
properties: Chain[PropertyModel],
resultType: Type
): State[S, (ValueModel, Inline)] = {
properties: Chain[PropertyModel]
): State[S, (VarModel, Inline)] = {
for {
apName <- Mangler[S].findAndForbidName("literal_to_functor")
apName <- Mangler[S].findAndForbidName("literal_ap")
resultName <- Mangler[S].findAndForbidName(s"literal_props")
} yield {
val cleanedType = literal.`type` match {
@ -37,98 +69,108 @@ object ApplyPropertiesRawInliner extends RawInliner[ApplyPropertyRaw] {
FlattenModel(apVar, resultName).leaf
)
)
VarModel(resultName, resultType) -> tree
VarModel(resultName, properties.lastOption.map(_.`type`).getOrElse(cleanedType)) -> tree
}
}
private[inline] def removeProperty[S: Mangler: Exports: Arrows](
vm: ValueModel
): State[S, (ValueModel, Inline)] =
vm match {
case VarModel(nameM, btm, propertyM) if propertyM.nonEmpty =>
for {
nameMM <- Mangler[S].findAndForbidName(nameM)
} yield VarModel(nameMM, vm.`type`, Chain.empty) -> Inline.preload(
// TODO use smth more resilient to make VarRaw from a flattened VarModel
nameMM -> ApplyPropertyRaw.fromChain(VarRaw(nameM, btm), propertyM.map(_.toRaw))
)
case _ =>
State.pure(vm -> Inline.empty)
private def removeProperties[S: Mangler](
varModel: VarModel
): State[S, (VarModel, Inline)] = {
for {
nn <- Mangler[S].findAndForbidName(varModel.name + "_flat")
} yield {
val flatten = VarModel(nn, varModel.`type`)
flatten -> Inline.tree(FlattenModel(varModel, flatten.name).leaf)
}
}
private[inline] def unfoldProperty[S: Mangler: Exports: Arrows](
varModel: VarModel,
p: PropertyRaw
): State[S, (PropertyModel, Inline)] = // TODO property for collection
): State[S, (VarModel, Inline)] =
p match {
case IntoFieldRaw(field, t) =>
State.pure(IntoFieldModel(field, t) -> Inline.empty)
case IntoIndexRaw(vm: ApplyPropertyRaw, t) =>
for {
nn <- Mangler[S].findAndForbidName("ap-prop")
} yield IntoIndexModel(nn, t) -> Inline.preload(nn -> vm)
case IntoIndexRaw(vr: (VarRaw | CallArrowRaw), t) =>
unfold(vr, propertiesAllowed = false).map {
case (VarModel(name, _, _), inline) =>
IntoIndexModel(name, t) -> inline
case (LiteralModel(v, _), inline) => IntoIndexModel(v, t) -> inline
}
State.pure(
varModel.copy(properties =
varModel.properties :+ IntoFieldModel(field, t)
) -> Inline.empty
)
case IntoIndexRaw(LiteralRaw(value, _), t) =>
State.pure(IntoIndexModel(value, t) -> Inline.empty)
State.pure(
varModel.copy(properties =
varModel.properties :+ IntoIndexModel(value, t)
) -> Inline.empty
)
case IntoIndexRaw(vr, t) =>
unfold(vr, propertiesAllowed = false).map {
case (VarModel(name, _, _), inline) =>
varModel.copy(properties = varModel.properties :+ IntoIndexModel(name, t)) -> inline
case (LiteralModel(literal, _), inline) =>
varModel.copy(properties = varModel.properties :+ IntoIndexModel(literal, t)) -> inline
}
case f @ FunctorRaw(_, _) =>
for {
flattenVI <-
if (varModel.properties.nonEmpty) removeProperties(varModel)
else State.pure(varModel, Inline.empty)
(flatten, inline) = flattenVI
newVI <- ApplyFunctorRawInliner(flatten, f)
} yield {
newVI._1 -> Inline(
inline.flattenValues ++ newVI._2.flattenValues,
inline.predo ++ newVI._2.predo,
mergeMode = SeqMode
)
}
case ic @ IntoCopyRaw(_, _) =>
for {
flattenVI <-
if (varModel.properties.nonEmpty) removeProperties(varModel)
else State.pure(varModel, Inline.empty)
(flatten, inline) = flattenVI
newVI <- ApplyIntoCopyRawInliner(varModel, ic)
} yield {
newVI._1 -> Inline(
inline.flattenValues ++ newVI._2.flattenValues,
inline.predo ++ newVI._2.predo,
mergeMode = SeqMode
)
}
}
def reachModelWithPropertyModels[S: Mangler: Exports: Arrows](
model: ValueModel,
propertyModels: Chain[PropertyModel],
propertyPrefix: Inline,
propertiesAllowed: Boolean,
streamGateInline: Option[Inline] = None
): State[S, (ValueModel, Inline)] = {
Exports[S].exports.flatMap { exports =>
model match {
case v: VarModel =>
{
val vm = v.copy(properties = v.properties ++ propertyModels).resolveWith(exports)
State.pure((vm, Inline.empty))
}.flatMap { case (genV, genInline) =>
val prefInline = propertyPrefix |+| genInline
if (propertiesAllowed) State.pure(genV -> prefInline)
else
removeProperty(genV).map { case (vmm, mpp) =>
val resultInline = streamGateInline.map { gInline =>
Inline(
prefInline.flattenValues ++ mpp.flattenValues ++ gInline.flattenValues,
Chain.one(
SeqModel.wrap((prefInline.predo ++ mpp.predo ++ gInline.predo).toList: _*)
),
SeqMode
)
}.getOrElse(prefInline |+| mpp)
vmm -> resultInline
}
}
case l: LiteralModel if propertyModels.nonEmpty =>
flatLiteralWithProperties(l, propertyPrefix, propertyModels, propertyModels.lastOption.map(_.`type`).getOrElse(l.`type`))
case v =>
// What does it mean actually? I've no ides
State.pure((v, propertyPrefix))
}
}
}
private def unfoldProperties[S: Mangler: Exports: Arrows](
properties: Chain[PropertyRaw]
): State[S, (Chain[PropertyModel], Inline)] = {
prevInline: Inline,
vm: VarModel,
properties: Chain[PropertyRaw],
propertiesAllowed: Boolean
): State[S, (VarModel, Inline)] = {
properties
.foldLeft[State[S, (Chain[PropertyModel], Inline)]](
State.pure((Chain.empty[PropertyModel], Inline.empty))
) { case (pcm, p) =>
pcm.flatMap { case (pc, m) =>
unfoldProperty(p).map { case (pm, mm) =>
(pc :+ pm, m |+| mm)
.foldLeft[State[S, (VarModel, Inline)]](
State.pure((vm, prevInline))
) { case (state, property) =>
state.flatMap { case (vm, leftInline) =>
unfoldProperty(vm, property).flatMap {
case (v, i) if !propertiesAllowed && v.properties.nonEmpty =>
removeProperties(v).map { case (vf, inlf) =>
vf -> Inline(
leftInline.flattenValues ++ i.flattenValues ++ inlf.flattenValues,
leftInline.predo ++ i.predo ++ inlf.predo,
mergeMode = SeqMode
)
}
case (v, i) =>
State.pure(
v -> Inline(
leftInline.flattenValues ++ i.flattenValues,
leftInline.predo ++ i.predo,
mergeMode = SeqMode
)
)
}
}
}
@ -140,56 +182,47 @@ object ApplyPropertiesRawInliner extends RawInliner[ApplyPropertyRaw] {
propertiesAllowed: Boolean
): State[S, (ValueModel, Inline)] = {
((raw, properties.headOption) match {
// To wait for the element of a stream by the given index, the following model is generated:
// (seq
// (seq
// (seq
// (call %init_peer_id% ("math" "add") [0 1] stream_incr)
// (fold $stream s
// (seq
// (seq
// (ap s $stream_test)
// (canon %init_peer_id% $stream_test #stream_iter_canon)
// )
// (xor
// (match #stream_iter_canon.length stream_incr
// (null)
// )
// (next s)
// )
// )
// (never)
// )
// )
// (canon %init_peer_id% $stream_test #stream_result_canon)
// )
// (ap #stream_result_canon stream_gate)
// )
case (vr @ VarRaw(_, st @ StreamType(_)), Some(IntoIndexRaw(idx, _))) =>
unfold(vr).flatMap {
case (VarModel(nameVM, _, _), inl) =>
case (vm @ VarModel(nameVM, _, _), inl) =>
val gateRaw = ApplyGateRaw(nameVM, st, idx)
unfold(gateRaw).flatMap { case (gateResVal, gateResInline) =>
unfoldProperties(properties).flatMap { case (propertyModels, map) =>
reachModelWithPropertyModels(
gateResVal,
propertyModels,
inl |+| map,
false,
Some(gateResInline)
)
}
unfold(gateRaw).flatMap {
case (gateResVal: VarModel, gateResInline) =>
unfoldProperties(gateResInline, gateResVal, properties, propertiesAllowed).map {
case (v, i) =>
(v: ValueModel) -> Inline(
inl.flattenValues ++ i.flattenValues,
inl.predo ++ i.predo,
mergeMode = SeqMode
)
}
case (v, i) =>
// what if pass nil as stream argument?
logger.error("Unreachable. Unfolded stream cannot be a literal")
State.pure(v -> i)
}
case l =>
// unreachable. Stream cannot be literal
logger.error("Unreachable. Unfolded stream cannot be a literal")
State.pure(l)
}
case (_, _) =>
unfold(raw).flatMap { case (vm, prevInline) =>
unfoldProperties(properties).flatMap { case (propertyModels, map) =>
reachModelWithPropertyModels(vm, propertyModels, prevInline |+| map, propertiesAllowed)
}
unfold(raw).flatMap {
case (vm: VarModel, prevInline) =>
unfoldProperties(prevInline, vm, properties, propertiesAllowed).map { case (v, i) =>
(v: ValueModel) -> i
}
case (l: LiteralModel, inline) =>
flatLiteralWithProperties(
l,
inline,
Chain.empty
).flatMap { (varModel, prevInline) =>
unfoldProperties(prevInline, varModel, properties, propertiesAllowed).map {
case (v, i) =>
(v: ValueModel) -> i
}
}
}
})
@ -198,44 +231,8 @@ object ApplyPropertiesRawInliner extends RawInliner[ApplyPropertyRaw] {
override def apply[S: Mangler: Exports: Arrows](
apr: ApplyPropertyRaw,
propertiesAllowed: Boolean
): State[S, (ValueModel, Inline)] =
): State[S, (ValueModel, Inline)] = {
val (raw, properties) = apr.unwind
val leftToFunctor = properties.takeWhile {
case FunctorRaw(_, _) => false
case _ => true
}
if (leftToFunctor.length == properties.length) {
unfoldRawWithProperties(raw, properties, propertiesAllowed)
} else {
// split properties like this:
// properties -- functor -- properties with functors
// process properties, process functor in ApplyFunctorRawInliner
// then process tail recursively
(for {
ur <- properties.dropWhile {
case FunctorRaw(_, _) => false
case _ => true
}.uncons
(functor: FunctorRaw, right) = ur
} yield {
(leftToFunctor, functor, right)
}).map { case (left, functor, right) =>
for {
vmLeftInline <- unfoldRawWithProperties(raw, left, propertiesAllowed)
(leftVM, leftInline) = vmLeftInline
// TODO: rewrite without `toRaw`
fRaw = ApplyFunctorRaw(leftVM.toRaw, functor)
vmFunctorInline <- ApplyFunctorRawInliner(fRaw, false)
(fVM, fInline) = vmFunctorInline
// TODO: rewrite without `toRaw`
vmRightInline <- unfold(ApplyPropertyRaw.fromChain(fVM.toRaw, right), propertiesAllowed)
(vm, rightInline) = vmRightInline
} yield {
vm -> (leftInline |+| fInline |+| rightInline)
}
}.getOrElse(unfoldRawWithProperties(raw, properties, propertiesAllowed))
}
unfoldRawWithProperties(raw, properties, propertiesAllowed)
}
}

View File

@ -10,6 +10,8 @@ import aqua.raw.value.CallArrowRaw
import cats.data.{Chain, State}
import scribe.Logging
import scala.collection.immutable.ListMap
object CallArrowRawInliner extends RawInliner[CallArrowRaw] with Logging {
private[inline] def unfoldArrow[S: Mangler: Exports: Arrows](
@ -26,7 +28,7 @@ object CallArrowRawInliner extends RawInliner[CallArrowRaw] with Logging {
cd <- callToModel(call, true)
sd <- valueToModel(serviceId)
} yield cd._1.exportTo.map(_.asVar.resolveWith(exports)) -> Inline(
Map.empty,
ListMap.empty,
Chain(
SeqModel.wrap(
sd._2.toList ++
@ -49,7 +51,7 @@ object CallArrowRawInliner extends RawInliner[CallArrowRaw] with Logging {
.callArrowRet(fn, cm)
.map { case (body, vars) =>
vars -> Inline(
Map.empty,
ListMap.empty,
Chain.one(SeqModel.wrap(p.toList :+ body: _*))
)
}

View File

@ -330,7 +330,7 @@ class ArrowInlinerSpec extends AnyFlatSpec with Matchers {
IntoFieldRaw("field", ScalarType.string)
)
val flattenObject = VarRaw("object-1", ScalarType.string)
val flattenObject = VarRaw("object_flat", ScalarType.string)
// raw object
val objectVar = VarRaw(
@ -404,7 +404,7 @@ class ArrowInlinerSpec extends AnyFlatSpec with Matchers {
CallModel(Nil, CallModel.Export(objectVar.name, objectVar.`type`) :: Nil)
).leaf,
SeqModel.wrap(
FlattenModel(ValueModel.fromRaw(objectVarLambda), "object-1").leaf,
FlattenModel(ValueModel.fromRaw(objectVarLambda), flattenObject.name).leaf,
CallServiceModel(
LiteralModel("\"callbackSrv\"", LiteralType.string),
"response",

View File

@ -2,6 +2,7 @@ package aqua.model.inline
import aqua.model.inline.raw.ApplyPropertiesRawInliner
import aqua.model.{
EmptyModel,
FlattenModel,
FunctorModel,
IntoFieldModel,
@ -132,7 +133,7 @@ class RawValueInlinerSpec extends AnyFlatSpec with Matchers {
"x",
ArrayType(ScalarType.string),
Chain.one(IntoIndexModel("y", ScalarType.string))
) -> None
) -> Some(EmptyModel.leaf)
)
}
@ -156,24 +157,6 @@ class RawValueInlinerSpec extends AnyFlatSpec with Matchers {
)
}
"raw value inliner" should "unfold a PropertyModel" in {
import aqua.model.inline.state.Mangler.Simple
// [ys!]
ApplyPropertiesRawInliner
.unfoldProperty[InliningState](`raw ys[0]`)
.run(InliningState(noNames = Set("ys")))
.value
._2 should be(
IntoIndexModel("ap-prop", ScalarType.string) -> Inline(
Map(
"ap-prop" -> VarRaw("ys", ArrayType(ScalarType.i8)).withProperty(
IntoIndexRaw(LiteralRaw.number(0), ScalarType.i8)
)
)
)
)
}
"raw value inliner" should "desugarize a single recursive raw value" in {
// x[ys!]
val (resVal, resTree) = valueToModel[InliningState](
@ -187,7 +170,7 @@ class RawValueInlinerSpec extends AnyFlatSpec with Matchers {
VarModel(
"x",
ArrayType(ScalarType.string),
Chain.one(IntoIndexModel("ap-prop", ScalarType.string))
Chain.one(IntoIndexModel("ys_flat", ScalarType.string))
)
)
@ -200,7 +183,7 @@ class RawValueInlinerSpec extends AnyFlatSpec with Matchers {
ArrayType(ScalarType.i8),
Chain.one(IntoIndexModel("0", ScalarType.i8))
),
"ap-prop"
"ys_flat"
).leaf
) should be(true)
}
@ -215,8 +198,8 @@ class RawValueInlinerSpec extends AnyFlatSpec with Matchers {
"x",
ArrayType(ArrayType(ScalarType.string)),
Chain(
IntoIndexModel("ap-prop", ArrayType(ScalarType.string)),
IntoIndexModel("ap-prop-0", ScalarType.string)
IntoIndexModel("xs_flat", ArrayType(ScalarType.string)),
IntoIndexModel("xss_flat", ScalarType.string)
)
)
)
@ -224,79 +207,57 @@ class RawValueInlinerSpec extends AnyFlatSpec with Matchers {
resTree.isEmpty should be(false)
resTree.get.equalsOrShowDiff(
ParModel.wrap(
SeqModel.wrap(
SeqModel.wrap(
SeqModel.wrap(
SeqModel.wrap(
FlattenModel(
VarModel(
"ys",
ArrayType(ScalarType.u32)
),
"ys_to_functor"
).leaf,
FlattenModel(
VarModel(
"ys_to_functor",
ArrayType(ScalarType.u32),
Chain.one(FunctorModel("length", ScalarType.u32))
),
"ys_length"
).leaf
),
FlattenModel(
VarModel(
"ys_length",
ScalarType.u32
),
"ap-prop-1"
).leaf
),
FlattenModel(
VarModel(
"xs",
ArrayType(ScalarType.u32),
Chain.one(IntoIndexModel("ap-prop-1", ScalarType.u32))
"ys",
ArrayType(ScalarType.u32)
),
"ap-prop"
"ys_to_functor"
).leaf,
FlattenModel(
VarModel(
"ys_to_functor",
ArrayType(ScalarType.u32),
Chain.one(FunctorModel("length", ScalarType.u32))
),
"ys_length"
).leaf
),
SeqModel.wrap(
SeqModel.wrap(
SeqModel.wrap(
FlattenModel(
VarModel(
"yss",
ArrayType(ScalarType.u32)
),
"yss_to_functor"
).leaf,
FlattenModel(
VarModel(
"yss_to_functor",
ArrayType(ScalarType.u32),
Chain.one(FunctorModel("length", ScalarType.u32))
),
"yss_length"
).leaf
),
FlattenModel(
VarModel(
"yss_length",
ScalarType.u32
),
"ap-prop-2"
).leaf
FlattenModel(
VarModel(
"xs",
ArrayType(ScalarType.u32),
Chain.one(IntoIndexModel("ys_length", ScalarType.u32))
),
"xs_flat"
).leaf,
SeqModel.wrap(
FlattenModel(
VarModel(
"xss",
ArrayType(ScalarType.u32),
Chain.one(IntoIndexModel("ap-prop-2", ScalarType.u32))
"yss",
ArrayType(ScalarType.u32)
),
"ap-prop-0"
"yss_to_functor"
).leaf,
FlattenModel(
VarModel(
"yss_to_functor",
ArrayType(ScalarType.u32),
Chain.one(FunctorModel("length", ScalarType.u32))
),
"yss_length"
).leaf
)
),
FlattenModel(
VarModel(
"xss",
ArrayType(ScalarType.u32),
Chain.one(IntoIndexModel("yss_length", ScalarType.u32))
),
"xss_flat"
).leaf
)
) should be(true)
}
@ -314,8 +275,8 @@ class RawValueInlinerSpec extends AnyFlatSpec with Matchers {
"x",
ArrayType(ArrayType(ScalarType.string)),
Chain(
IntoIndexModel("ap-prop", ArrayType(ScalarType.string)),
IntoIndexModel("ap-prop-0", ScalarType.string)
IntoIndexModel("ys_flat", ArrayType(ScalarType.string)),
IntoIndexModel("ys_flat-0", ScalarType.string)
)
)
)
@ -323,14 +284,14 @@ class RawValueInlinerSpec extends AnyFlatSpec with Matchers {
resTree.isEmpty should be(false)
resTree.get.equalsOrShowDiff(
ParModel.wrap(
SeqModel.wrap(
FlattenModel(
VarModel(
"ys",
ArrayType(ScalarType.i8),
Chain.one(IntoIndexModel("0", ScalarType.i8))
),
"ap-prop"
"ys_flat"
).leaf,
FlattenModel(
VarModel(
@ -338,7 +299,7 @@ class RawValueInlinerSpec extends AnyFlatSpec with Matchers {
ArrayType(ScalarType.i8),
Chain.one(IntoIndexModel("1", ScalarType.i8))
),
"ap-prop-0"
"ys_flat-0"
).leaf
)
) should be(true)
@ -357,8 +318,11 @@ class RawValueInlinerSpec extends AnyFlatSpec with Matchers {
resVal should be(
VarModel(
"x_gate-0",
ScalarType.string
"x_gate",
ArrayType(ScalarType.string),
Chain(
IntoIndexModel("ys_flat", ScalarType.string)
)
)
)
println(resTree)
@ -394,8 +358,8 @@ class RawValueInlinerSpec extends AnyFlatSpec with Matchers {
"x",
ArrayType(ArrayType(ScalarType.string)),
Chain(
IntoIndexModel("ap-prop", ArrayType(ScalarType.string)),
IntoIndexModel("ap-prop-0", ScalarType.string)
IntoIndexModel("zs_flat", ArrayType(ScalarType.string)),
IntoIndexModel("ys_flat-0", ScalarType.string)
)
)
)
@ -403,28 +367,23 @@ class RawValueInlinerSpec extends AnyFlatSpec with Matchers {
resTree.isEmpty should be(false)
resTree.get.equalsOrShowDiff(
ParModel.wrap(
// Prepare the zs-0 index
SeqModel.wrap(
// First get ys[0], save as ys-1
FlattenModel(
VarModel(
"ys",
ArrayType(ScalarType.i8),
Chain.one(IntoIndexModel("0", ScalarType.i8))
),
"ap-prop-1"
).leaf,
// Then use that ys-1 as an index of zs
FlattenModel(
VarModel(
"zs",
ArrayType(ScalarType.i8),
Chain.one(IntoIndexModel("ap-prop-1", ScalarType.i8))
),
"ap-prop"
).leaf
),
SeqModel.wrap(
FlattenModel(
VarModel(
"ys",
ArrayType(ScalarType.i8),
Chain.one(IntoIndexModel("0", ScalarType.i8))
),
"ys_flat"
).leaf,
FlattenModel(
VarModel(
"zs",
ArrayType(ScalarType.i8),
Chain.one(IntoIndexModel("ys_flat", ScalarType.i8))
),
"zs_flat"
).leaf,
// Now prepare ys-0
FlattenModel(
VarModel(
@ -432,7 +391,7 @@ class RawValueInlinerSpec extends AnyFlatSpec with Matchers {
ArrayType(ScalarType.i8),
Chain.one(IntoIndexModel("1", ScalarType.i8))
),
"ap-prop-0"
"ys_flat-0"
).leaf
)
) should be(true)

View File

@ -1,6 +1,7 @@
package aqua.raw.value
import aqua.types.Type
import aqua.types.{StructType, Type}
import cats.data.NonEmptyMap
sealed trait PropertyRaw {
def `type`: Type
@ -18,6 +19,14 @@ case class IntoFieldRaw(name: String, `type`: Type) extends PropertyRaw {
override def varNames: Set[String] = Set.empty
}
case class IntoCopyRaw(`type`: StructType, fields: NonEmptyMap[String, ValueRaw]) extends PropertyRaw {
override def map(f: ValueRaw => ValueRaw): IntoCopyRaw = copy(fields = fields.map(f))
override def varNames: Set[String] = Set.empty
override def renameVars(vals: Map[String, String]): IntoCopyRaw = this
}
case class FunctorRaw(name: String, `type`: Type) extends PropertyRaw {
override def map(f: ValueRaw => ValueRaw): FunctorRaw = this

View File

@ -72,21 +72,6 @@ case class ApplyPropertyRaw(value: ValueRaw, property: PropertyRaw) extends Valu
override def varNames: Set[String] = value.varNames ++ property.varNames
}
case class ApplyFunctorRaw(value: ValueRaw, functor: FunctorRaw) extends ValueRaw {
override def baseType: Type = value.baseType
override def `type`: Type = functor.`type`
override def renameVars(map: Map[String, String]): ValueRaw =
ApplyFunctorRaw(value.renameVars(map), functor.renameVars(map))
override def map(f: ValueRaw => ValueRaw): ValueRaw = f(ApplyFunctorRaw(f(value), functor.map(f)))
override def toString: String = s"$value.$functor"
override def varNames: Set[String] = value.varNames ++ functor.varNames
}
object ApplyPropertyRaw {
def fromChain(value: ValueRaw, properties: Chain[PropertyRaw]): ValueRaw =

View File

@ -3,7 +3,7 @@ package aqua.parser.lexer
import aqua.parser.lexer.Token.*
import aqua.parser.lift.LiftParser
import aqua.parser.lift.LiftParser.*
import cats.data.NonEmptyList
import cats.data.{NonEmptyList, NonEmptyMap}
import cats.parse.{Numbers, Parser as P, Parser0 as P0}
import cats.syntax.comonad.*
import cats.syntax.functor.*
@ -36,11 +36,24 @@ case class IntoIndex[F[_]: Comonad](point: F[Unit], idx: Option[ValueToken[F]])
override def mapK[K[_]: Comonad](fk: F ~> K): IntoIndex[K] = copy(fk(point), idx.map(_.mapK(fk)))
}
case class IntoCopy[F[_]: Comonad](point: F[Unit], fields: NonEmptyMap[String, ValueToken[F]])
extends PropertyOp[F] {
override def as[T](v: T): F[T] = point.as(v)
override def mapK[K[_]: Comonad](fk: F ~> K): IntoCopy[K] =
copy(fk(point), fields.map(_.mapK(fk)))
}
object PropertyOp {
private val parseField: P[PropertyOp[Span.S]] =
(`.` *> `name`).lift.map(IntoField(_))
val parseCopy: P[PropertyOp[Span.S]] =
(`.` *> (`copy`.lift ~ namedArgs)).map { case (point, fields) =>
IntoCopy(point, NonEmptyMap.of(fields.head, fields.tail: _*))
}
private val parseIdx: P[PropertyOp[Span.S]] =
(P.defer(
(ValueToken.`value`.between(`[`, `]`).lift | (exclamation *> ValueToken.num).lift)
@ -55,7 +68,7 @@ object PropertyOp {
}
private val parseOp: P[PropertyOp[Span.S]] =
P.oneOf(parseField.backtrack :: parseIdx :: Nil)
P.oneOf(parseCopy.backtrack :: parseField.backtrack :: parseIdx :: Nil)
val ops: P[NonEmptyList[PropertyOp[Span.S]]] =
parseOp.rep

View File

@ -25,9 +25,9 @@ object Token {
private val upperAnum_ = upperAnum ++ f_
private val nl = Set('\n', '\r')
val inAZ = P.charIn(AZ)
val inaz = P.charIn(az)
val whileAnum = P.charsWhile(anum_)
private val inAZ = P.charIn(AZ)
private val inaz = P.charIn(az)
private val whileAnum = P.charsWhile(anum_)
val ` *` : P0[String] = P.charsWhile0(fSpaces)
val ` ` : P[String] = P.charsWhile(fSpaces)
@ -63,6 +63,7 @@ object Token {
val `par`: P[Unit] = P.string("par")
val `co`: P[Unit] = P.string("co")
val `join`: P[Unit] = P.string("join")
val `copy`: P[Unit] = P.string("copy")
val `:` : P[Unit] = P.char(':')
val ` : ` : P[Unit] = P.char(':').surroundedBy(` `.?)
val `anum_*` : P[Unit] = whileAnum.void
@ -116,6 +117,12 @@ object Token {
val `<-` : P[Unit] = P.string("<-")
val `/s*` : P0[Any] = ` \n+` | ` *`
val namedArgs = P.defer(
comma(
((`name` <* (` `.?.with1 *> `=` *> ` `.?)).with1 ~ ValueToken.`value`).surroundedBy(`/s*`)
).between(` `.?.with1 *> `(` <* `/s*`, `/s*` *> `)`)
)
case class LiftToken[F[_]: Functor, A](point: F[A]) extends Token[F] {
override def as[T](v: T): F[T] = Functor[F].as(point, v)

View File

@ -108,11 +108,7 @@ case class StructValueToken[F[_]: Comonad](
object StructValueToken {
val dataValue: P[StructValueToken[Span.S]] =
(`Class`.lift
~ comma(
((`name` <* (` `.?.with1 *> `=` *> ` `.?)).with1 ~ ValueToken.`value`).surroundedBy(`/s*`)
)
.between(` `.?.with1 *> `(` <* `/s*`, `/s*` *> `)`))
(`Class`.lift ~ namedArgs)
.withContext(
"Missing braces '()' after the struct type"
)

View File

@ -1,8 +1,9 @@
package aqua.parser.lexer
import aqua.parser.lift.LiftParser.Implicits.idLiftParser
import aqua.types.LiteralType
import cats.Id
import cats.data.NonEmptyList
import cats.data.{NonEmptyList, NonEmptyMap}
import org.scalatest.EitherValues
import org.scalatest.flatspec.AnyFlatSpec
import org.scalatest.matchers.should.Matchers
@ -17,9 +18,44 @@ class PropertyOpSpec extends AnyFlatSpec with Matchers with EitherValues {
opsP(".field") should be(NonEmptyList.of(IntoField[Id]("field")))
opsP(".field.sub") should be(NonEmptyList.of(IntoField[Id]("field"), IntoField[Id]("sub")))
PropertyOp.ops.parseAll("[-1]").isLeft shouldBe true
PropertyOp.ops.parseAll("!-1").isLeft shouldBe true
PropertyOp.ops.parseAll("[-1]").isLeft shouldBe true
PropertyOp.ops.parseAll("!-1").isLeft shouldBe true
}
"copy ops" should "parse" in {
val opsP = (s: String) => PropertyOp.ops.parseAll(s).value.map(_.mapK(spanToId))
opsP(".copy(a = \"str\", b = 12)") should be(
NonEmptyList.of(
IntoCopy[Id](
(),
NonEmptyMap.of(
"a" -> LiteralToken("\"str\"", LiteralType.string),
"b" -> LiteralToken("12", LiteralType.number)
)
)
)
)
opsP(".copy(a = \"str\", b = 12).copy(c = 54, d = someVar)") should be(
NonEmptyList.of(
IntoCopy[Id](
(),
NonEmptyMap.of(
"a" -> LiteralToken("\"str\"", LiteralType.string),
"b" -> LiteralToken("12", LiteralType.number)
)
),
IntoCopy[Id](
(),
NonEmptyMap.of(
"c" -> LiteralToken("54", LiteralType.number),
"d" -> VarToken("someVar")
)
)
)
)
}
}

View File

@ -7,7 +7,6 @@ import aqua.raw.Raw
import aqua.raw.arrow.ArrowRaw
import aqua.raw.ops.{SeqTag, *}
import aqua.raw.value.{
ApplyFunctorRaw,
ApplyGateRaw,
ApplyPropertyRaw,
CallArrowRaw,
@ -143,7 +142,6 @@ class ArrowSem[S[_]](val expr: ArrowExpr[S]) extends AnyVal {
idx + 1
)
// assign and change return value for all `Apply*Raw`
case (v: ApplyFunctorRaw, _) => assignRaw(v, idx, bodyAcc, returnAcc)
case (v: ApplyGateRaw, _) => assignRaw(v, idx, bodyAcc, returnAcc)
case (v: ApplyPropertyRaw, _) => assignRaw(v, idx, bodyAcc, returnAcc)
case (v: CallArrowRaw, _) => assignRaw(v, idx, bodyAcc, returnAcc)

View File

@ -46,6 +46,15 @@ class ValuesAlgebra[S[_], Alg[_]: Monad](implicit
op match {
case op: IntoField[S] =>
T.resolveField(rootType, op)
case op: IntoCopy[S] =>
op.fields
.map(valueToRaw)
.sequence
.map(_.sequence)
.flatMap {
case None => None.pure[Alg]
case Some(values) => T.resolveCopy(rootType, op, values)
}
case op: IntoIndex[S] =>
op.idx
.fold[Alg[Option[ValueRaw]]](Option(LiteralRaw.Zero).pure[Alg])(
@ -53,7 +62,7 @@ class ValuesAlgebra[S[_], Alg[_]: Monad](implicit
)
.flatMap {
case None => None.pure[Alg]
case Some(vv) => T.resolveIndex(rootType, op, vv)
case Some(values) => T.resolveIndex(rootType, op, values)
}
}

View File

@ -24,6 +24,7 @@ trait TypesAlgebra[S[_], Alg[_]] {
def defineAlias(name: CustomTypeToken[S], target: Type): Alg[Boolean]
def resolveIndex(rootT: Type, op: IntoIndex[S], idx: ValueRaw): Alg[Option[PropertyRaw]]
def resolveCopy(rootT: Type, op: IntoCopy[S], fields: NonEmptyMap[String, ValueRaw]): Alg[Option[PropertyRaw]]
def resolveField(rootT: Type, op: IntoField[S]): Alg[Option[PropertyRaw]]
def ensureValuesComparable(token: Token[S], left: Type, right: Type): Alg[Boolean]

View File

@ -1,7 +1,7 @@
package aqua.semantics.rules.types
import aqua.parser.lexer.*
import aqua.raw.value.{FunctorRaw, IntoFieldRaw, IntoIndexRaw, PropertyRaw, ValueRaw}
import aqua.raw.value.{FunctorRaw, IntoCopyRaw, IntoFieldRaw, IntoIndexRaw, PropertyRaw, ValueRaw}
import aqua.semantics.lsp.{TokenDef, TokenTypeInfo}
import aqua.semantics.rules.{ReportError, StackInterpreter}
import aqua.types.{
@ -158,6 +158,26 @@ class TypesInterpreter[S[_], X](implicit lens: Lens[X, TypesState[S]], error: Re
}
}
// TODO actually it's stateless, exists there just for reporting needs
override def resolveCopy(
rootT: Type,
op: IntoCopy[S],
fields: NonEmptyMap[String, ValueRaw]
): State[X, Option[PropertyRaw]] =
rootT match {
case st: StructType =>
fields.toSortedMap.toList.traverse { case (fieldName, value) =>
st.fields.lookup(fieldName) match {
case Some(t) =>
ensureTypeMatches(op.fields.lookup(fieldName).getOrElse(op), t, value.`type`)
case None => report(op, s"No field with name '$fieldName' in $rootT").as(false)
}
}.map(res => if (res.toList.fold(true)(_ && _)) Some(IntoCopyRaw(st, fields)) else None)
case _ =>
report(op, s"Expected $rootT to be a data type").as(None)
}
// TODO actually it's stateless, exists there just for reporting needs
override def resolveIndex(
rootT: Type,