mirror of
https://github.com/fluencelabs/aqua.git
synced 2025-03-15 11:40:50 +00:00
Topology refactoring (#100)
* Topology refactoring * TransformSpec fixed & improved * Fixes #98 * Better Par handling * Introduced Cursor class * Better exit process for par branch * Force move to target peer when exiting from a par branch
This commit is contained in:
parent
27f2912c5f
commit
1fc5557ba6
@ -13,6 +13,8 @@ service Test("test"):
|
|||||||
getUserList: -> []User
|
getUserList: -> []User
|
||||||
doSomething: -> bool
|
doSomething: -> bool
|
||||||
check: bool -> ()
|
check: bool -> ()
|
||||||
|
log: string -> ()
|
||||||
|
getString: string -> string
|
||||||
|
|
||||||
func initAfterJoin(me: User) -> []User:
|
func initAfterJoin(me: User) -> []User:
|
||||||
allUsers <- Test.getUserList()
|
allUsers <- Test.getUserList()
|
||||||
@ -41,4 +43,19 @@ func checkStreams(ch: []string) -> []bool:
|
|||||||
on b:
|
on b:
|
||||||
stream <- Peer.is_connected(b)
|
stream <- Peer.is_connected(b)
|
||||||
handleArr(stream)
|
handleArr(stream)
|
||||||
<- stream
|
<- stream
|
||||||
|
|
||||||
|
func test(user: string, relay_id: string) -> string:
|
||||||
|
on relay_id:
|
||||||
|
Peer.is_connected("on relay_id")
|
||||||
|
on user:
|
||||||
|
Peer.is_connected("on user")
|
||||||
|
par Peer.is_connected("par on init peer")
|
||||||
|
<- ""
|
||||||
|
|
||||||
|
-- should fail, as using str2 in parallel
|
||||||
|
func topologyTest(friend: string, friendRelay: string) -> string:
|
||||||
|
on friend via friendRelay:
|
||||||
|
str2 <- Test.getString("friends string via")
|
||||||
|
par Test.log(str2)
|
||||||
|
<- "finish"
|
@ -14,15 +14,18 @@ case class TypescriptFile(script: ScriptModel) {
|
|||||||
object TypescriptFile {
|
object TypescriptFile {
|
||||||
|
|
||||||
val Header: String =
|
val Header: String =
|
||||||
"""/**
|
s"""/**
|
||||||
| *
|
| *
|
||||||
| * This file is auto-generated. Do not edit manually: changes may be erased.
|
| * This file is auto-generated. Do not edit manually: changes may be erased.
|
||||||
| * Generated by Aqua compiler: https://github.com/fluencelabs/aqua/.
|
| * Generated by Aqua compiler: https://github.com/fluencelabs/aqua/.
|
||||||
| * If you find any bugs, please write an issue on GitHub: https://github.com/fluencelabs/aqua/issues
|
| * If you find any bugs, please write an issue on GitHub: https://github.com/fluencelabs/aqua/issues
|
||||||
| *
|
| * Aqua version: ${Option(getClass.getPackage.getImplementationVersion)
|
||||||
| */
|
.filter(_.nonEmpty)
|
||||||
|import { FluenceClient, PeerIdB58 } from '@fluencelabs/fluence';
|
.getOrElse("Unknown")}
|
||||||
|import { RequestFlowBuilder } from '@fluencelabs/fluence/dist/api.unstable';
|
| *
|
||||||
|""".stripMargin
|
| */
|
||||||
|
|import { FluenceClient, PeerIdB58 } from '@fluencelabs/fluence';
|
||||||
|
|import { RequestFlowBuilder } from '@fluencelabs/fluence/dist/api.unstable';
|
||||||
|
|""".stripMargin
|
||||||
|
|
||||||
}
|
}
|
||||||
|
16
build.sbt
16
build.sbt
@ -1,18 +1,18 @@
|
|||||||
val dottyVersion = "2.13.5"
|
val dottyVersion = "2.13.5"
|
||||||
|
|
||||||
|
//val dottyVersion = "3.0.0-RC3"
|
||||||
|
|
||||||
scalaVersion := dottyVersion
|
scalaVersion := dottyVersion
|
||||||
|
|
||||||
//val dottyVersion = "3.0.0-RC2"
|
|
||||||
|
|
||||||
val baseAquaVersion = settingKey[String]("base aqua version")
|
val baseAquaVersion = settingKey[String]("base aqua version")
|
||||||
|
|
||||||
val catsV = "2.5.0"
|
val catsV = "2.6.0"
|
||||||
val catsParseV = "0.3.2"
|
val catsParseV = "0.3.2"
|
||||||
val monocleV = "3.0.0-M4"
|
val monocleV = "3.0.0-M5"
|
||||||
val scalaTestV = "3.2.7"
|
val scalaTestV = "3.2.7" // TODO update version for scala 3-RC3
|
||||||
val fs2V = "3.0.0"
|
val fs2V = "3.0.2"
|
||||||
val catsEffectV = "3.0.2"
|
val catsEffectV = "3.1.0"
|
||||||
val declineV = "2.0.0-RC1"
|
val declineV = "2.0.0-RC1" // Scala3 issue: https://github.com/bkirwi/decline/issues/260
|
||||||
|
|
||||||
name := "aqua-hll"
|
name := "aqua-hll"
|
||||||
|
|
||||||
|
@ -17,6 +17,7 @@ object Model {
|
|||||||
case (l: ScriptModel, r: ScriptModel) =>
|
case (l: ScriptModel, r: ScriptModel) =>
|
||||||
ScriptModel.SMMonoid.combine(l, r)
|
ScriptModel.SMMonoid.combine(l, r)
|
||||||
|
|
||||||
|
case (l: EmptyModel, r: EmptyModel) => EmptyModel(l.log + " |+| " + r.log)
|
||||||
case (_: EmptyModel, r) => r
|
case (_: EmptyModel, r) => r
|
||||||
case (l, _: EmptyModel) => l
|
case (l, _: EmptyModel) => l
|
||||||
|
|
||||||
@ -24,6 +25,7 @@ object Model {
|
|||||||
ScriptModel.toScriptPart(l).fold(r)(ScriptModel.SMMonoid.combine(_, r))
|
ScriptModel.toScriptPart(l).fold(r)(ScriptModel.SMMonoid.combine(_, r))
|
||||||
case (l: ScriptModel, r) =>
|
case (l: ScriptModel, r) =>
|
||||||
ScriptModel.toScriptPart(r).fold(l)(ScriptModel.SMMonoid.combine(l, _))
|
ScriptModel.toScriptPart(r).fold(l)(ScriptModel.SMMonoid.combine(l, _))
|
||||||
|
|
||||||
case (l, r) =>
|
case (l, r) =>
|
||||||
ScriptModel
|
ScriptModel
|
||||||
.toScriptPart(l)
|
.toScriptPart(l)
|
||||||
@ -31,8 +33,6 @@ object Model {
|
|||||||
ScriptModel.toScriptPart(r).fold(l)(rs => ScriptModel.SMMonoid.combine(ls, rs))
|
ScriptModel.toScriptPart(r).fold(l)(rs => ScriptModel.SMMonoid.combine(ls, rs))
|
||||||
)
|
)
|
||||||
|
|
||||||
case (l: EmptyModel, r: EmptyModel) => EmptyModel(l.log + " |+| " + r.log)
|
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,12 +1,20 @@
|
|||||||
package aqua.model
|
package aqua.model
|
||||||
|
|
||||||
import aqua.types.Type
|
import aqua.types.Type
|
||||||
|
import cats.Eq
|
||||||
import cats.data.Chain
|
import cats.data.Chain
|
||||||
|
|
||||||
sealed trait ValueModel {
|
sealed trait ValueModel {
|
||||||
def resolveWith(map: Map[String, ValueModel]): ValueModel = this
|
def resolveWith(map: Map[String, ValueModel]): ValueModel = this
|
||||||
}
|
}
|
||||||
|
|
||||||
|
object ValueModel {
|
||||||
|
|
||||||
|
implicit object ValueModelEq extends Eq[ValueModel] {
|
||||||
|
override def eqv(x: ValueModel, y: ValueModel): Boolean = x == y
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
case class LiteralModel(value: String) extends ValueModel
|
case class LiteralModel(value: String) extends ValueModel
|
||||||
|
|
||||||
object LiteralModel {
|
object LiteralModel {
|
||||||
|
@ -27,15 +27,19 @@ sealed trait OpTag {
|
|||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
sealed trait GroupTag extends OpTag
|
||||||
|
sealed trait SeqGroupTag extends GroupTag
|
||||||
|
|
||||||
case object SeqTag extends OpTag
|
case object SeqTag extends SeqGroupTag
|
||||||
case object ParTag extends OpTag
|
case object ParTag extends GroupTag
|
||||||
case object XorTag extends OpTag
|
case object XorTag extends GroupTag
|
||||||
case class XorParTag(xor: FuncOp, par: FuncOp) extends OpTag
|
case class XorParTag(xor: FuncOp, par: FuncOp) extends OpTag
|
||||||
case class OnTag(peerId: ValueModel, via: Chain[ValueModel]) extends OpTag
|
case class OnTag(peerId: ValueModel, via: Chain[ValueModel]) extends SeqGroupTag
|
||||||
case class NextTag(item: String) extends OpTag
|
case class NextTag(item: String) extends OpTag
|
||||||
case class MatchMismatchTag(left: ValueModel, right: ValueModel, shouldMatch: Boolean) extends OpTag
|
|
||||||
case class ForTag(item: String, iterable: ValueModel) extends OpTag
|
case class MatchMismatchTag(left: ValueModel, right: ValueModel, shouldMatch: Boolean)
|
||||||
|
extends SeqGroupTag
|
||||||
|
case class ForTag(item: String, iterable: ValueModel) extends SeqGroupTag
|
||||||
|
|
||||||
case class CallArrowTag(
|
case class CallArrowTag(
|
||||||
funcName: String,
|
funcName: String,
|
||||||
|
51
model/src/main/scala/aqua/model/topology/ChainZipper.scala
Normal file
51
model/src/main/scala/aqua/model/topology/ChainZipper.scala
Normal file
@ -0,0 +1,51 @@
|
|||||||
|
package aqua.model.topology
|
||||||
|
|
||||||
|
import cats.data.Chain
|
||||||
|
import cats.free.Cofree
|
||||||
|
|
||||||
|
case class ChainZipper[T](prev: Chain[T], current: T, next: Chain[T]) {
|
||||||
|
|
||||||
|
def chain: Chain[T] = (prev :+ current) ++ next
|
||||||
|
|
||||||
|
def moveLeft: Option[ChainZipper[T]] =
|
||||||
|
prev.initLast.map { case (init, last) =>
|
||||||
|
ChainZipper(init, last, current +: next)
|
||||||
|
}
|
||||||
|
|
||||||
|
def moveRight: Option[ChainZipper[T]] =
|
||||||
|
next.uncons.map { case (head, tail) =>
|
||||||
|
ChainZipper(prev :+ current, head, tail)
|
||||||
|
}
|
||||||
|
|
||||||
|
def replaceInjecting(cz: ChainZipper[T]): ChainZipper[T] =
|
||||||
|
copy(prev ++ cz.prev, cz.current, cz.next ++ next)
|
||||||
|
}
|
||||||
|
|
||||||
|
object ChainZipper {
|
||||||
|
def one[T](el: T): ChainZipper[T] = ChainZipper(Chain.empty, el, Chain.empty)
|
||||||
|
|
||||||
|
def fromChain[T](chain: Chain[T], prev: Chain[T] = Chain.empty): Chain[ChainZipper[T]] =
|
||||||
|
chain.uncons.fold(Chain.empty[ChainZipper[T]]) { case (t, next) =>
|
||||||
|
ChainZipper(prev, t, next) +: fromChain(next, prev :+ t)
|
||||||
|
}
|
||||||
|
|
||||||
|
def fromChainMap[T](chain: Chain[T], prev: Chain[T] = Chain.empty)(
|
||||||
|
f: ChainZipper[T] => Option[T]
|
||||||
|
): Chain[T] =
|
||||||
|
chain.uncons.fold(Chain.empty[T]) { case (t, next) =>
|
||||||
|
f(ChainZipper(prev, t, next)).fold(fromChainMap(next, prev)(f))(r =>
|
||||||
|
r +: fromChainMap(next, prev :+ r)(f)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
object Matchers {
|
||||||
|
|
||||||
|
object `current` {
|
||||||
|
def unapply[T](cz: ChainZipper[T]): Option[T] = Some(cz.current)
|
||||||
|
}
|
||||||
|
|
||||||
|
object `head` {
|
||||||
|
def unapply[F[_], T](cz: ChainZipper[Cofree[F, T]]): Option[T] = Some(cz.current.head)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
81
model/src/main/scala/aqua/model/topology/Cursor.scala
Normal file
81
model/src/main/scala/aqua/model/topology/Cursor.scala
Normal file
@ -0,0 +1,81 @@
|
|||||||
|
package aqua.model.topology
|
||||||
|
|
||||||
|
import Topology.Tree
|
||||||
|
import aqua.model.func.body.{OnTag, OpTag, ParTag, SeqTag}
|
||||||
|
import cats.Eval
|
||||||
|
import cats.data.Chain
|
||||||
|
import cats.free.Cofree
|
||||||
|
|
||||||
|
case class Cursor(point: ChainZipper[Tree], loc: Location) {
|
||||||
|
|
||||||
|
def downLoc(tree: Tree): Location =
|
||||||
|
loc.down(point.copy(current = tree))
|
||||||
|
|
||||||
|
def prevOnTags: Chain[OnTag] =
|
||||||
|
Chain
|
||||||
|
.fromSeq(
|
||||||
|
point.prev.lastOption
|
||||||
|
.orElse(loc.lastLeftSeq.map(_._1.current))
|
||||||
|
.toList
|
||||||
|
.flatMap(Cursor.rightBoundary)
|
||||||
|
.takeWhile {
|
||||||
|
case ParTag => false
|
||||||
|
case _ => true
|
||||||
|
}
|
||||||
|
)
|
||||||
|
.collect { case o: OnTag =>
|
||||||
|
o
|
||||||
|
}
|
||||||
|
|
||||||
|
def nextOnTags: Chain[OnTag] =
|
||||||
|
Chain
|
||||||
|
.fromSeq(
|
||||||
|
loc.lastRightSeq
|
||||||
|
.map(_._1.current)
|
||||||
|
.toList
|
||||||
|
.flatMap(Cursor.leftBoundary)
|
||||||
|
.takeWhile {
|
||||||
|
case ParTag => false
|
||||||
|
case _ => true
|
||||||
|
}
|
||||||
|
)
|
||||||
|
.collect { case o: OnTag =>
|
||||||
|
o
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
object Cursor {
|
||||||
|
|
||||||
|
def rightBoundary(root: Tree): LazyList[OpTag] =
|
||||||
|
root.head #:: LazyList.unfold(root.tail)(_.value.lastOption.map(lo => lo.head -> lo.tail))
|
||||||
|
|
||||||
|
def leftBoundary(root: Tree): LazyList[OpTag] =
|
||||||
|
root.head #:: LazyList.unfold(root.tail)(_.value.headOption.map(lo => lo.head -> lo.tail))
|
||||||
|
|
||||||
|
def transform(root: Tree)(f: Cursor => List[Tree]): Option[Tree] = {
|
||||||
|
def step(cursor: Cursor): Option[Tree] =
|
||||||
|
f(cursor) match {
|
||||||
|
case Nil => None
|
||||||
|
case h :: Nil =>
|
||||||
|
val np = cursor.downLoc(h)
|
||||||
|
Some(h.copy(tail = h.tail.map(ChainZipper.fromChainMap(_)(cz => step(Cursor(cz, np))))))
|
||||||
|
case hs =>
|
||||||
|
ChainZipper
|
||||||
|
.fromChain(Chain.fromSeq(hs))
|
||||||
|
.map(cursor.point.replaceInjecting)
|
||||||
|
.map { cfh =>
|
||||||
|
val np = cursor.loc.down(cfh)
|
||||||
|
cfh.current.copy(tail =
|
||||||
|
cfh.current.tail.map(ChainZipper.fromChainMap(_)(cz => step(Cursor(cz, np))))
|
||||||
|
)
|
||||||
|
}
|
||||||
|
.uncons
|
||||||
|
.map {
|
||||||
|
case (h, t) if t.isEmpty => h
|
||||||
|
case (h, t) => Cofree(SeqTag, Eval.later(h +: t))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
step(Cursor(ChainZipper.one(root), Location()))
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
63
model/src/main/scala/aqua/model/topology/Location.scala
Normal file
63
model/src/main/scala/aqua/model/topology/Location.scala
Normal file
@ -0,0 +1,63 @@
|
|||||||
|
package aqua.model.topology
|
||||||
|
|
||||||
|
import aqua.model.ValueModel
|
||||||
|
import aqua.model.func.body.{OnTag, SeqGroupTag}
|
||||||
|
import cats.data.Chain
|
||||||
|
import cats.free.Cofree
|
||||||
|
|
||||||
|
case class Location(path: List[ChainZipper[Topology.Tree]] = Nil) {
|
||||||
|
def down(h: ChainZipper[Topology.Tree]): Location = copy(h :: path)
|
||||||
|
|
||||||
|
def lastOn: Option[OnTag] = path.map(_.current.head).collectFirst { case o: OnTag =>
|
||||||
|
o
|
||||||
|
}
|
||||||
|
|
||||||
|
def pathOn: List[OnTag] = path.map(_.current.head).collect { case o: OnTag =>
|
||||||
|
o
|
||||||
|
}
|
||||||
|
|
||||||
|
def pathViaChain: Chain[ValueModel] = Chain.fromSeq(
|
||||||
|
path
|
||||||
|
.map(_.current.head)
|
||||||
|
.collectFirst { case t: OnTag =>
|
||||||
|
t.via.toList
|
||||||
|
}
|
||||||
|
.toList
|
||||||
|
.flatten
|
||||||
|
)
|
||||||
|
|
||||||
|
def lastLeftSeq: Option[(ChainZipper[Topology.Tree], Location)] =
|
||||||
|
path match {
|
||||||
|
case (cz @ ChainZipper(prev, Cofree(_: SeqGroupTag, _), _)) :: tail if prev.nonEmpty =>
|
||||||
|
cz.moveLeft.map(_ -> Location(tail))
|
||||||
|
case _ :: tail => Location(tail).lastLeftSeq
|
||||||
|
case Nil => None
|
||||||
|
}
|
||||||
|
|
||||||
|
def lastRightSeq: Option[(ChainZipper[Topology.Tree], Location)] =
|
||||||
|
path match {
|
||||||
|
case (cz @ ChainZipper(_, Cofree(_: SeqGroupTag, _), next)) :: tail if next.nonEmpty =>
|
||||||
|
cz.moveRight.map(_ -> Location(tail))
|
||||||
|
case _ :: tail => Location(tail).lastRightSeq
|
||||||
|
case Nil => None
|
||||||
|
}
|
||||||
|
|
||||||
|
path.collectFirst {
|
||||||
|
case ChainZipper(prev, Cofree(_: SeqGroupTag, _), _) if prev.nonEmpty => prev.lastOption
|
||||||
|
}.flatten
|
||||||
|
}
|
||||||
|
|
||||||
|
object Location {
|
||||||
|
|
||||||
|
object Matchers {
|
||||||
|
|
||||||
|
object /: {
|
||||||
|
|
||||||
|
def unapply(loc: Location): Option[(ChainZipper[Topology.Tree], Location)] =
|
||||||
|
loc.path match {
|
||||||
|
case h :: tail => Some(h -> Location(tail))
|
||||||
|
case _ => None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
96
model/src/main/scala/aqua/model/topology/Topology.scala
Normal file
96
model/src/main/scala/aqua/model/topology/Topology.scala
Normal file
@ -0,0 +1,96 @@
|
|||||||
|
package aqua.model.topology
|
||||||
|
|
||||||
|
import aqua.model.ValueModel
|
||||||
|
import aqua.model.func.body._
|
||||||
|
import cats.Eval
|
||||||
|
import cats.data.Chain
|
||||||
|
import cats.free.Cofree
|
||||||
|
import ChainZipper.Matchers._
|
||||||
|
import Location.Matchers._
|
||||||
|
|
||||||
|
object Topology {
|
||||||
|
type Tree = Cofree[Chain, OpTag]
|
||||||
|
|
||||||
|
// Walks through peer IDs, doing a noop function on each
|
||||||
|
// If same IDs are found in a row, does noop only once
|
||||||
|
// if there's a chain like a -> b -> c -> ... -> b -> g, remove everything between b and b
|
||||||
|
def through(peerIds: Chain[ValueModel]): Chain[Tree] =
|
||||||
|
peerIds
|
||||||
|
.foldLeft(Chain.empty[ValueModel]) {
|
||||||
|
case (acc, p) if acc.lastOption.contains(p) => acc
|
||||||
|
case (acc, p) if acc.contains(p) => acc.takeWhile(_ != p) :+ p
|
||||||
|
case (acc, p) => acc :+ p
|
||||||
|
}
|
||||||
|
.map(FuncOps.noop)
|
||||||
|
.map(_.tree)
|
||||||
|
|
||||||
|
def mapTag(tag: OpTag, loc: Location): OpTag = tag match {
|
||||||
|
case c: CallServiceTag if c.peerId.isEmpty =>
|
||||||
|
c.copy(peerId = loc.lastOn.map(_.peerId))
|
||||||
|
case t => t
|
||||||
|
}
|
||||||
|
|
||||||
|
def resolve(op: Tree): Tree =
|
||||||
|
Cofree
|
||||||
|
.cata[Chain, OpTag, Tree](resolveOnMoves(op)) {
|
||||||
|
case (SeqTag | _: OnTag, children) =>
|
||||||
|
Eval.later(
|
||||||
|
Cofree(
|
||||||
|
SeqTag,
|
||||||
|
Eval.now(children.flatMap {
|
||||||
|
case Cofree(SeqTag, ch) => ch.value
|
||||||
|
case cf => Chain.one(cf)
|
||||||
|
})
|
||||||
|
)
|
||||||
|
)
|
||||||
|
case (head, children) => Eval.later(Cofree(head, Eval.now(children)))
|
||||||
|
}
|
||||||
|
.value
|
||||||
|
|
||||||
|
def resolveOnMoves(op: Tree): Tree =
|
||||||
|
Cursor
|
||||||
|
.transform(op) {
|
||||||
|
case c @ Cursor(
|
||||||
|
cz @ `current`(cf),
|
||||||
|
loc @ `head`(parent: GroupTag) /: _
|
||||||
|
) =>
|
||||||
|
val cfu = cf.copy(mapTag(cf.head, loc))
|
||||||
|
|
||||||
|
val getThere = (cfu.head, loc.pathOn) match {
|
||||||
|
case (OnTag(pid, _), h :: _) if h.peerId == pid => Chain.empty[ValueModel]
|
||||||
|
case (OnTag(_, via), h :: _) =>
|
||||||
|
h.via.reverse ++ via
|
||||||
|
case (_, _) => Chain.empty[ValueModel]
|
||||||
|
}
|
||||||
|
|
||||||
|
val prevOn = c.prevOnTags
|
||||||
|
|
||||||
|
val prevPath = prevOn.map { case OnTag(_, v) =>
|
||||||
|
v.reverse
|
||||||
|
}
|
||||||
|
.flatMap(identity)
|
||||||
|
|
||||||
|
val nextOn = parent match {
|
||||||
|
case ParTag | XorTag => c.nextOnTags
|
||||||
|
case _ => Chain.empty[OnTag]
|
||||||
|
}
|
||||||
|
val nextPath = (if (nextOn.nonEmpty) getThere.reverse else Chain.empty) ++ nextOn.map {
|
||||||
|
case OnTag(_, v) =>
|
||||||
|
v.reverse
|
||||||
|
}
|
||||||
|
.flatMap(identity) ++ Chain.fromOption(
|
||||||
|
// Dirty fix for join behaviour
|
||||||
|
nextOn.lastOption.filter(_ => parent == ParTag).map(_.peerId)
|
||||||
|
)
|
||||||
|
|
||||||
|
if (prevOn.isEmpty && getThere.isEmpty) cfu :: Nil
|
||||||
|
else
|
||||||
|
(through(prevPath ++ loc.pathViaChain ++ getThere)
|
||||||
|
.append(cfu) ++ through(nextPath)).toList
|
||||||
|
|
||||||
|
case Cursor(ChainZipper(_, cf, _), loc) =>
|
||||||
|
cf.copy(mapTag(cf.head, loc)) :: Nil
|
||||||
|
}
|
||||||
|
.getOrElse(op)
|
||||||
|
|
||||||
|
}
|
@ -1,106 +0,0 @@
|
|||||||
package aqua.model.transform
|
|
||||||
|
|
||||||
import aqua.model.ValueModel
|
|
||||||
import aqua.model.func.body.{CallServiceTag, FuncOps, OnTag, OpTag, ParTag, SeqTag}
|
|
||||||
import cats.Eval
|
|
||||||
import cats.data.Chain
|
|
||||||
import cats.free.Cofree
|
|
||||||
|
|
||||||
object Topology {
|
|
||||||
type Tree = Cofree[Chain, OpTag]
|
|
||||||
|
|
||||||
def rightBoundary(root: Tree): List[OpTag] =
|
|
||||||
root.head :: root.tailForced.lastOption.fold(List.empty[OpTag])(rightBoundary)
|
|
||||||
|
|
||||||
// Walks through peer IDs, doing a noop function on each
|
|
||||||
// If same IDs are found in a row, does noop only once
|
|
||||||
// TODO: if there's a chain like a -> b -> c -> ... -> b -> g, remove everything between b and b
|
|
||||||
def through(peerIds: Chain[ValueModel]): Chain[Tree] =
|
|
||||||
peerIds
|
|
||||||
.foldLeft(Chain.empty[ValueModel]) {
|
|
||||||
case (acc, p) if acc.lastOption.contains(p) => acc
|
|
||||||
case (acc, p) => acc :+ p
|
|
||||||
}
|
|
||||||
.map(FuncOps.noop)
|
|
||||||
.map(_.tree)
|
|
||||||
|
|
||||||
// TODO: after topology is resolved, OnTag should be eliminated
|
|
||||||
def resolve(op: Tree): Tree =
|
|
||||||
transformWithPath(op) {
|
|
||||||
case (path, c: CallServiceTag, children) if c.peerId.isEmpty =>
|
|
||||||
Cofree[Chain, OpTag](
|
|
||||||
c.copy(peerId = path.collectFirst { case OnTag(peerId, _) =>
|
|
||||||
peerId
|
|
||||||
}),
|
|
||||||
children
|
|
||||||
)
|
|
||||||
case (path, tag @ OnTag(pid, via), children) =>
|
|
||||||
// Drop seq/par/xor from path
|
|
||||||
val pathOn = path.collect { case ot: OnTag =>
|
|
||||||
ot
|
|
||||||
}
|
|
||||||
|
|
||||||
pathOn match {
|
|
||||||
// If we are on the right node, do nothing
|
|
||||||
case Nil =>
|
|
||||||
Cofree[Chain, OpTag](tag, children)
|
|
||||||
case h :: _ if h.peerId == pid =>
|
|
||||||
Cofree[Chain, OpTag](tag, children)
|
|
||||||
case h :: _ =>
|
|
||||||
Cofree[Chain, OpTag](
|
|
||||||
tag,
|
|
||||||
// TODO: merge children, if possible
|
|
||||||
children.map(through(h.via.reverse ++ via) ++ _)
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
case (path, SeqTag, children) =>
|
|
||||||
// TODO if we have OnTag, and then something else, need to get back
|
|
||||||
// AND keep in mind that we will handle all the children with OnTag processor!
|
|
||||||
|
|
||||||
val pathViaChain = Chain.fromSeq(path.collectFirst { case t: OnTag =>
|
|
||||||
t.via.toList
|
|
||||||
}.toList.flatten)
|
|
||||||
|
|
||||||
def modifyChildrenList(list: List[Tree], prev: Option[Tree]): Chain[Tree] = list match {
|
|
||||||
case Nil => Chain.empty
|
|
||||||
case op :: tail =>
|
|
||||||
// TODO further improve
|
|
||||||
val prevPath = Chain
|
|
||||||
.fromSeq(prev.toList.flatMap(rightBoundary).takeWhile {
|
|
||||||
case ParTag => false
|
|
||||||
case _ => true
|
|
||||||
})
|
|
||||||
.collect { case OnTag(_, v) =>
|
|
||||||
v.reverse
|
|
||||||
}
|
|
||||||
|
|
||||||
if (prevPath.isEmpty) op +: modifyChildrenList(tail, Some(op))
|
|
||||||
else
|
|
||||||
through(prevPath.flatMap(identity) ++ pathViaChain).append(op) ++ modifyChildrenList(
|
|
||||||
tail,
|
|
||||||
Some(op)
|
|
||||||
)
|
|
||||||
|
|
||||||
case o :: ops => o +: modifyChildrenList(ops, Some(o))
|
|
||||||
}
|
|
||||||
|
|
||||||
Cofree[Chain, OpTag](
|
|
||||||
SeqTag,
|
|
||||||
children.map(_.toList).map(modifyChildrenList(_, None))
|
|
||||||
)
|
|
||||||
|
|
||||||
case (_, t, children) =>
|
|
||||||
Cofree[Chain, OpTag](t, children)
|
|
||||||
}
|
|
||||||
|
|
||||||
def transformWithPath(cf: Tree, path: List[OpTag] = Nil)(
|
|
||||||
f: (List[OpTag], OpTag, Eval[Chain[Tree]]) => Tree
|
|
||||||
): Tree = {
|
|
||||||
val newCf = f(path, cf.head, cf.tail)
|
|
||||||
Cofree[Chain, OpTag](
|
|
||||||
newCf.head,
|
|
||||||
newCf.tail.map(_.map(transformWithPath(_, newCf.head :: path)(f)))
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
@ -3,6 +3,7 @@ package aqua.model.transform
|
|||||||
import aqua.model.func.body._
|
import aqua.model.func.body._
|
||||||
import aqua.model.func.FuncCallable
|
import aqua.model.func.FuncCallable
|
||||||
import aqua.model.VarModel
|
import aqua.model.VarModel
|
||||||
|
import aqua.model.topology.Topology
|
||||||
import aqua.types.ScalarType
|
import aqua.types.ScalarType
|
||||||
import cats.data.Chain
|
import cats.data.Chain
|
||||||
import cats.free.Cofree
|
import cats.free.Cofree
|
||||||
|
23
model/src/test/scala/aqua/model/topology/LocationSpec.scala
Normal file
23
model/src/test/scala/aqua/model/topology/LocationSpec.scala
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
package aqua.model.topology
|
||||||
|
|
||||||
|
import org.scalatest.flatspec.AnyFlatSpec
|
||||||
|
import org.scalatest.matchers.should.Matchers
|
||||||
|
import Location.Matchers._
|
||||||
|
import ChainZipper.Matchers._
|
||||||
|
import aqua.model.func.body.SeqTag
|
||||||
|
import cats.Eval
|
||||||
|
import cats.data.Chain
|
||||||
|
import cats.free.Cofree
|
||||||
|
|
||||||
|
class LocationSpec extends AnyFlatSpec with Matchers {
|
||||||
|
|
||||||
|
"matchers" should "unapply correctly" in {
|
||||||
|
val loc =
|
||||||
|
Location(ChainZipper.one(Cofree(SeqTag, Eval.later(Chain.empty[Topology.Tree]))) :: Nil)
|
||||||
|
|
||||||
|
Option(loc).collect { case `head`(SeqTag) /: _ =>
|
||||||
|
true
|
||||||
|
} should be('defined)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -1,4 +1,4 @@
|
|||||||
package aqua.model.transform
|
package aqua.model.topology
|
||||||
|
|
||||||
import aqua.model.Node
|
import aqua.model.Node
|
||||||
import org.scalatest.flatspec.AnyFlatSpec
|
import org.scalatest.flatspec.AnyFlatSpec
|
||||||
@ -20,14 +20,11 @@ class TopologySpec extends AnyFlatSpec with Matchers {
|
|||||||
|
|
||||||
val proc: Node = Topology.resolve(init)
|
val proc: Node = Topology.resolve(init)
|
||||||
|
|
||||||
val expected = on(
|
val expected =
|
||||||
initPeer,
|
|
||||||
relay :: Nil,
|
|
||||||
seq(
|
seq(
|
||||||
call(1, initPeer),
|
call(1, initPeer),
|
||||||
call(2, initPeer)
|
call(2, initPeer)
|
||||||
)
|
)
|
||||||
)
|
|
||||||
|
|
||||||
proc should be(expected)
|
proc should be(expected)
|
||||||
|
|
||||||
@ -50,19 +47,8 @@ class TopologySpec extends AnyFlatSpec with Matchers {
|
|||||||
|
|
||||||
val proc: Node = Topology.resolve(init)
|
val proc: Node = Topology.resolve(init)
|
||||||
|
|
||||||
val expected = on(
|
val expected =
|
||||||
initPeer,
|
seq(through(relay), call(1, otherPeer), call(2, otherPeer))
|
||||||
relay :: Nil,
|
|
||||||
on(
|
|
||||||
otherPeer,
|
|
||||||
Nil,
|
|
||||||
through(relay),
|
|
||||||
seq(
|
|
||||||
call(1, otherPeer),
|
|
||||||
call(2, otherPeer)
|
|
||||||
)
|
|
||||||
)
|
|
||||||
)
|
|
||||||
|
|
||||||
proc should be(expected)
|
proc should be(expected)
|
||||||
}
|
}
|
||||||
@ -84,20 +70,13 @@ class TopologySpec extends AnyFlatSpec with Matchers {
|
|||||||
|
|
||||||
val proc: Node = Topology.resolve(init)
|
val proc: Node = Topology.resolve(init)
|
||||||
|
|
||||||
val expected = on(
|
val expected =
|
||||||
initPeer,
|
seq(
|
||||||
relay :: Nil,
|
|
||||||
on(
|
|
||||||
otherPeer,
|
|
||||||
otherRelay :: Nil,
|
|
||||||
through(relay),
|
through(relay),
|
||||||
through(otherRelay),
|
through(otherRelay),
|
||||||
seq(
|
call(1, otherPeer),
|
||||||
call(1, otherPeer),
|
call(2, otherPeer)
|
||||||
call(2, otherPeer)
|
|
||||||
)
|
|
||||||
)
|
)
|
||||||
)
|
|
||||||
|
|
||||||
proc should be(expected)
|
proc should be(expected)
|
||||||
}
|
}
|
||||||
@ -119,22 +98,15 @@ class TopologySpec extends AnyFlatSpec with Matchers {
|
|||||||
|
|
||||||
val proc: Node = Topology.resolve(init)
|
val proc: Node = Topology.resolve(init)
|
||||||
|
|
||||||
val expected = on(
|
val expected =
|
||||||
initPeer,
|
|
||||||
relay :: Nil,
|
|
||||||
seq(
|
seq(
|
||||||
on(
|
through(relay),
|
||||||
otherPeer,
|
through(otherRelay),
|
||||||
otherRelay :: Nil,
|
call(1, otherPeer),
|
||||||
through(relay),
|
|
||||||
through(otherRelay),
|
|
||||||
call(1, otherPeer)
|
|
||||||
),
|
|
||||||
through(otherRelay),
|
through(otherRelay),
|
||||||
through(relay),
|
through(relay),
|
||||||
call(2, initPeer)
|
call(2, initPeer)
|
||||||
)
|
)
|
||||||
)
|
|
||||||
|
|
||||||
// println(Console.BLUE + init)
|
// println(Console.BLUE + init)
|
||||||
// println(Console.YELLOW + proc)
|
// println(Console.YELLOW + proc)
|
||||||
@ -175,38 +147,25 @@ class TopologySpec extends AnyFlatSpec with Matchers {
|
|||||||
|
|
||||||
val proc: Node = Topology.resolve(init)
|
val proc: Node = Topology.resolve(init)
|
||||||
|
|
||||||
val expected = on(
|
val expected =
|
||||||
initPeer,
|
|
||||||
relay :: Nil,
|
|
||||||
seq(
|
seq(
|
||||||
on(
|
through(relay),
|
||||||
|
through(otherRelay),
|
||||||
|
call(0, otherPeer),
|
||||||
|
through(otherRelay),
|
||||||
|
call(1, otherPeer2),
|
||||||
|
_match(
|
||||||
otherPeer,
|
otherPeer,
|
||||||
otherRelay :: Nil,
|
otherRelay,
|
||||||
through(relay),
|
seq(
|
||||||
through(otherRelay),
|
|
||||||
call(0, otherPeer),
|
|
||||||
on(
|
|
||||||
otherPeer2,
|
|
||||||
otherRelay :: Nil,
|
|
||||||
through(otherRelay),
|
through(otherRelay),
|
||||||
call(1, otherPeer2),
|
call(2, otherPeer)
|
||||||
_match(
|
|
||||||
otherPeer,
|
|
||||||
otherRelay,
|
|
||||||
on(
|
|
||||||
otherPeer,
|
|
||||||
otherRelay :: Nil,
|
|
||||||
through(otherRelay),
|
|
||||||
call(2, otherPeer)
|
|
||||||
)
|
|
||||||
)
|
|
||||||
)
|
)
|
||||||
),
|
),
|
||||||
through(otherRelay),
|
through(otherRelay),
|
||||||
through(relay),
|
through(relay),
|
||||||
call(3, initPeer)
|
call(3, initPeer)
|
||||||
)
|
)
|
||||||
)
|
|
||||||
|
|
||||||
// println(Console.BLUE + init)
|
// println(Console.BLUE + init)
|
||||||
// println(Console.YELLOW + proc)
|
// println(Console.YELLOW + proc)
|
@ -32,17 +32,17 @@ class TransformSpec extends AnyFlatSpec with Matchers {
|
|||||||
|
|
||||||
val expectedFC =
|
val expectedFC =
|
||||||
xor(
|
xor(
|
||||||
on(
|
seq(
|
||||||
initPeer,
|
dataCall(bc, "relay", initPeer),
|
||||||
relayV :: Nil,
|
through(relayV),
|
||||||
seq(
|
call(1, otherPeer),
|
||||||
dataCall(bc, "relay", initPeer),
|
through(relayV),
|
||||||
on(otherPeer, Nil, through(relayV), call(1, otherPeer)),
|
respCall(bc, ret, initPeer)
|
||||||
through(relayV),
|
|
||||||
on(initPeer, relayV :: Nil, respCall(bc, ret, initPeer))
|
|
||||||
)
|
|
||||||
),
|
),
|
||||||
on(initPeer, relayV :: Nil, xorErrorCall(bc, initPeer))
|
seq(
|
||||||
|
through(relayV),
|
||||||
|
xorErrorCall(bc, initPeer)
|
||||||
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
procFC.equalsOrPrintDiff(expectedFC) should be(true)
|
procFC.equalsOrPrintDiff(expectedFC) should be(true)
|
||||||
@ -70,20 +70,18 @@ class TransformSpec extends AnyFlatSpec with Matchers {
|
|||||||
|
|
||||||
val expectedFC =
|
val expectedFC =
|
||||||
xor(
|
xor(
|
||||||
on(
|
seq(
|
||||||
initPeer,
|
dataCall(bc, "relay", initPeer),
|
||||||
relayV :: Nil,
|
call(0, initPeer),
|
||||||
seq(
|
through(relayV),
|
||||||
dataCall(bc, "relay", initPeer),
|
call(1, otherPeer),
|
||||||
seq(
|
through(relayV),
|
||||||
call(0, initPeer),
|
respCall(bc, ret, initPeer)
|
||||||
on(otherPeer, Nil, through(relayV), call(1, otherPeer))
|
|
||||||
),
|
|
||||||
through(relayV),
|
|
||||||
on(initPeer, relayV :: Nil, respCall(bc, ret, initPeer))
|
|
||||||
)
|
|
||||||
),
|
),
|
||||||
on(initPeer, relayV :: Nil, xorErrorCall(bc, initPeer))
|
seq(
|
||||||
|
through(relayV),
|
||||||
|
xorErrorCall(bc, initPeer)
|
||||||
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
procFC.equalsOrPrintDiff(expectedFC) should be(true)
|
procFC.equalsOrPrintDiff(expectedFC) should be(true)
|
||||||
@ -137,25 +135,17 @@ class TransformSpec extends AnyFlatSpec with Matchers {
|
|||||||
val res = Transform.forClient(f2, bc): Node
|
val res = Transform.forClient(f2, bc): Node
|
||||||
|
|
||||||
res.equalsOrPrintDiff(
|
res.equalsOrPrintDiff(
|
||||||
on(
|
seq(
|
||||||
initPeer,
|
dataCall(bc, "relay", initPeer),
|
||||||
relayV :: Nil,
|
Node(
|
||||||
seq(
|
CallServiceTag(
|
||||||
dataCall(bc, "relay", initPeer),
|
LiteralModel("\"srv1\""),
|
||||||
Node(
|
"foo",
|
||||||
CallServiceTag(
|
Call(Nil, Some(Call.Export("v", ScalarType.string))),
|
||||||
LiteralModel("\"srv1\""),
|
Some(initPeer)
|
||||||
"foo",
|
|
||||||
Call(Nil, Some(Call.Export("v", ScalarType.string))),
|
|
||||||
Some(initPeer)
|
|
||||||
)
|
|
||||||
),
|
|
||||||
on(
|
|
||||||
initPeer,
|
|
||||||
relayV :: Nil,
|
|
||||||
respCall(bc, VarModel("v", ScalarType.string), initPeer)
|
|
||||||
)
|
)
|
||||||
)
|
),
|
||||||
|
respCall(bc, VarModel("v", ScalarType.string), initPeer)
|
||||||
)
|
)
|
||||||
) should be(true)
|
) should be(true)
|
||||||
}
|
}
|
||||||
|
@ -1 +1 @@
|
|||||||
sbt.version=1.5.0
|
sbt.version=1.5.1
|
||||||
|
Loading…
x
Reference in New Issue
Block a user