mirror of
https://github.com/fluencelabs/aqua.git
synced 2024-12-12 17:55:33 +00:00
Fixes missing par (#177)
* Fixes missing par * test for par * Par topology bug fixed * test `on` on every par branch * Topology refactoring * Tests compilation wip * Tests compilation wip * Tests compile * Test fix * Non-par tests fixed * The last test remains * Topology tests fixed * SemanticsSpec compiles * transformspec wip * fix diff * TransformSpec with diff * test for error handling * topology resolver spec wip * delete test, rename test * fixed * par with export variable test * test for try without catch * Handle try without catch * XorParTag fix * Wake up target peer after par * Increment version * Fix xor par during func model resolution * test with import and fold * Linker bug fixed Co-authored-by: DieMyst <dmitry.shakhtarin@fluence.ai>
This commit is contained in:
parent
f71de81cb4
commit
985309d4eb
@ -1,22 +1,5 @@
|
||||
import "builtin.aqua"
|
||||
|
||||
service OpH("op"):
|
||||
puk(s: string) -> string
|
||||
pek(s: string, -- trgtr
|
||||
c: string) -> string
|
||||
|
||||
func a( -- ferkjn
|
||||
b: string, -- fr
|
||||
c: string, -- asdf
|
||||
g: string
|
||||
) -> string: -- rgtr
|
||||
|
||||
try:
|
||||
f = "world"
|
||||
OpH "planet"
|
||||
OpH.pek("TRY THIS", -- gtrg
|
||||
c)
|
||||
catch err:
|
||||
OpH.puk(err.msg)
|
||||
<- f
|
||||
import "print.aqua"
|
||||
|
||||
func iterateAndPrint(strings: []string):
|
||||
for s <- strings:
|
||||
print(s)
|
5
aqua-src/print.aqua
Normal file
5
aqua-src/print.aqua
Normal file
@ -0,0 +1,5 @@
|
||||
service Println("println-service-id"):
|
||||
print: string -> ()
|
||||
|
||||
func print(str: string):
|
||||
Println.print(str)
|
@ -1,150 +0,0 @@
|
||||
alias Field : []string
|
||||
alias Argument : []string
|
||||
alias Bytes : []u8
|
||||
alias PeerId : string
|
||||
|
||||
data Service:
|
||||
id: string
|
||||
blueprint_id: string
|
||||
owner_id: string
|
||||
|
||||
data FunctionSignature:
|
||||
arguments: []Argument
|
||||
name: string
|
||||
output_types: []string
|
||||
|
||||
data RecordType:
|
||||
fields: []Field
|
||||
id: u64
|
||||
name: string
|
||||
|
||||
data Interface:
|
||||
function_signatures: []FunctionSignature
|
||||
record_types: []RecordType
|
||||
|
||||
data ServiceInfo:
|
||||
blueprint_id: string
|
||||
service_id: string
|
||||
interface: Interface
|
||||
|
||||
data Info:
|
||||
external_addresses: []string
|
||||
|
||||
data ModuleConfig:
|
||||
name: string
|
||||
|
||||
data Module:
|
||||
name: string
|
||||
hash: string
|
||||
config: ModuleConfig
|
||||
|
||||
data AddBlueprint:
|
||||
name: string
|
||||
dependencies: []string
|
||||
|
||||
data Blueprint:
|
||||
id: string
|
||||
name: string
|
||||
dependencies: []string
|
||||
|
||||
data ScriptInfo:
|
||||
id: string
|
||||
src: string
|
||||
failures: u32
|
||||
interval: string
|
||||
owner: string
|
||||
|
||||
data Contact:
|
||||
peer_id: string
|
||||
addresses: []string
|
||||
|
||||
service Op("op"):
|
||||
identity: -> ()
|
||||
|
||||
service Peer("peer"):
|
||||
-- Checks if there is a direct connection to the peer identified by a given PeerId
|
||||
-- Argument: PeerId – id of the peer to check if there's a connection with
|
||||
-- Returns: bool - true if connected to the peer, false otherwise
|
||||
is_connected: PeerId -> bool
|
||||
|
||||
-- Initiates a connection to the specified peer
|
||||
-- Arguments:
|
||||
-- PeerId – id of the target peer
|
||||
-- [Multiaddr] – an array of target peer's addresses
|
||||
-- Returns: bool - true if connection was successful
|
||||
connect: PeerId, []string -> bool
|
||||
-- Resolves the contact of a peer via Kademlia
|
||||
-- Argument: PeerId – id of the target peer
|
||||
-- Returns: Contact - true if connection was successful
|
||||
get_contact: PeerId -> Contact
|
||||
|
||||
-- Get information about the peer
|
||||
identify: -> Info
|
||||
|
||||
-- Get Unix timestamp in milliseconds
|
||||
timestamp_ms: -> u64
|
||||
|
||||
-- Get Unix timestamp in seconds
|
||||
timestamp_sec: -> u64
|
||||
|
||||
service Kademlia("kad"):
|
||||
-- Instructs node to return the locally-known nodes
|
||||
-- in the Kademlia neighborhood for a given key
|
||||
neighborhood: PeerId -> []PeerId
|
||||
|
||||
service Srv("srv"):
|
||||
-- Used to create a service on a certain node
|
||||
-- Arguments:
|
||||
-- blueprint_id – ID of the blueprint that has been added to the node specified in the service call by the dist add_blueprint service.
|
||||
-- Returns: service_id – the service ID of the created service.
|
||||
create: string -> string
|
||||
|
||||
-- Returns a list of services running on a peer
|
||||
list: -> []Service
|
||||
|
||||
-- Adds an alias on service, so, service could be called
|
||||
-- not only by service_id but by alias as well.
|
||||
-- Argument:
|
||||
-- alias - settable service name
|
||||
-- service_id – ID of the service whose interface you want to name.
|
||||
add_alias: string, string -> ()
|
||||
|
||||
-- Retrieves the functional interface of a service running
|
||||
-- on the node specified in the service call
|
||||
-- Argument: service_id – ID of the service whose interface you want to retrieve.
|
||||
get_interface: string -> ServiceInfo
|
||||
|
||||
service Dist("dist"):
|
||||
-- Used to add modules to the node specified in the service call
|
||||
-- Arguments:
|
||||
-- bytes – a base64 string containing the .wasm module to add.
|
||||
-- config – module info
|
||||
-- Returns: blake3 hash of the module
|
||||
add_module: Bytes, ModuleConfig -> string
|
||||
|
||||
-- Get a list of modules available on the node
|
||||
list_modules: -> []Module
|
||||
|
||||
-- Get the interface of a module
|
||||
get_interface: string -> Interface
|
||||
|
||||
-- Used to add a blueprint to the node specified in the service call
|
||||
add_blueprint: AddBlueprint -> string
|
||||
|
||||
-- Used to get the blueprints available on the node specified in the service call.
|
||||
-- A blueprint is an object of the following structure
|
||||
list_blueprints: -> []Blueprint
|
||||
|
||||
service Script("script"):
|
||||
-- Adds the given script to a node
|
||||
add: string, string -> string
|
||||
|
||||
-- Removes recurring script from a node. Only a creator of the script can delete it
|
||||
remove: string -> bool
|
||||
|
||||
-- Returns a list of existing scripts on the node.
|
||||
-- Each object in the list is of the following structure
|
||||
list: -> ScriptInfo
|
||||
|
||||
func id():
|
||||
Op.identity()
|
@ -2,22 +2,19 @@ package aqua.backend.air
|
||||
|
||||
import aqua.model._
|
||||
import aqua.model.func.Call
|
||||
import aqua.model.func.body._
|
||||
import aqua.model.func.resolved._
|
||||
import aqua.types.StreamType
|
||||
import cats.Eval
|
||||
import cats.data.Chain
|
||||
import cats.free.Cofree
|
||||
import wvlet.log.Logger
|
||||
import wvlet.log.LogSupport
|
||||
|
||||
sealed trait AirGen {
|
||||
def generate: Air
|
||||
|
||||
}
|
||||
|
||||
object AirGen {
|
||||
|
||||
private val logger = Logger.of[AirGen.type]
|
||||
import logger._
|
||||
object AirGen extends LogSupport {
|
||||
|
||||
def lambdaToString(ls: List[LambdaModel]): String = ls match {
|
||||
case Nil => ""
|
||||
@ -46,22 +43,27 @@ object AirGen {
|
||||
case list => list.reduceLeft(SeqGen)
|
||||
}
|
||||
|
||||
private def folder(op: OpTag, ops: Chain[AirGen]): Eval[AirGen] =
|
||||
private def folder(op: ResolvedOp, ops: Chain[AirGen]): Eval[AirGen] =
|
||||
op match {
|
||||
case mt: MetaTag =>
|
||||
folder(mt.op, ops).map(ag => mt.comment.fold(ag)(CommentGen(_, ag)))
|
||||
case SeqTag =>
|
||||
// case mt: MetaTag =>
|
||||
// folder(mt.op, ops).map(ag => mt.comment.fold(ag)(CommentGen(_, ag)))
|
||||
case SeqRes =>
|
||||
Eval later ops.toList.reduceLeftOption(SeqGen).getOrElse(NullGen)
|
||||
case ParTag =>
|
||||
case ParRes =>
|
||||
Eval later ops.toList.reduceLeftOption(ParGen).getOrElse(NullGen)
|
||||
case XorTag =>
|
||||
Eval later ops.toList.reduceLeftOption(XorGen).getOrElse(NullGen)
|
||||
case XorTag.LeftBiased =>
|
||||
Eval later XorGen(opsToSingle(ops), NullGen)
|
||||
case XorRes =>
|
||||
Eval later (ops.toList match {
|
||||
case o :: Nil => XorGen(o, NullGen)
|
||||
case _ =>
|
||||
ops.toList.reduceLeftOption(XorGen).getOrElse {
|
||||
warn("XorRes with no children converted to Null")
|
||||
NullGen
|
||||
}
|
||||
})
|
||||
|
||||
case NextTag(item) =>
|
||||
case NextRes(item) =>
|
||||
Eval later NextGen(item)
|
||||
case MatchMismatchTag(left, right, shouldMatch) =>
|
||||
case MatchMismatchRes(left, right, shouldMatch) =>
|
||||
Eval later MatchMismatchGen(
|
||||
valueToData(left),
|
||||
valueToData(right),
|
||||
@ -69,12 +71,12 @@ object AirGen {
|
||||
opsToSingle(ops)
|
||||
)
|
||||
|
||||
case ForTag(item, iterable) =>
|
||||
case FoldRes(item, iterable) =>
|
||||
Eval later ForGen(valueToData(iterable), item, opsToSingle(ops))
|
||||
case CallServiceTag(serviceId, funcName, Call(args, exportTo), peerId) =>
|
||||
case CallServiceRes(serviceId, funcName, Call(args, exportTo), peerId) =>
|
||||
Eval.later(
|
||||
ServiceCallGen(
|
||||
peerId.map(valueToData).getOrElse(DataView.InitPeerId),
|
||||
valueToData(peerId),
|
||||
valueToData(serviceId),
|
||||
funcName,
|
||||
args.map(valueToData),
|
||||
@ -85,35 +87,14 @@ object AirGen {
|
||||
)
|
||||
)
|
||||
|
||||
case CallArrowTag(funcName, _) =>
|
||||
// TODO: should be already resolved & removed from tree
|
||||
error(
|
||||
s"Unresolved arrow in AirGen: $funcName"
|
||||
)
|
||||
case _: NoAir =>
|
||||
Eval later NullGen
|
||||
|
||||
case OnTag(_, _) =>
|
||||
// TODO should be resolved
|
||||
Eval later opsToSingle(
|
||||
ops
|
||||
)
|
||||
case _: NoAirTag =>
|
||||
// TODO: should be already resolved & removed from tree
|
||||
Eval later NullGen
|
||||
case XorParTag(opsx, opsy) =>
|
||||
// TODO should be resolved
|
||||
error(
|
||||
"XorParTag reached AirGen, most likely it's an error"
|
||||
)
|
||||
Eval later opsToSingle(
|
||||
Chain(apply(opsx.tree), apply(opsy.tree))
|
||||
)
|
||||
|
||||
}
|
||||
|
||||
def apply(op: Cofree[Chain, OpTag]): AirGen =
|
||||
def apply(op: Cofree[Chain, ResolvedOp]): AirGen =
|
||||
Cofree
|
||||
.cata[Chain, OpTag, AirGen](op)(folder)
|
||||
.cata[Chain, ResolvedOp, AirGen](op)(folder)
|
||||
.value
|
||||
}
|
||||
|
||||
|
@ -24,7 +24,7 @@ val airframeLog = "org.wvlet.airframe" %% "airframe-log" % airframeLogV
|
||||
name := "aqua-hll"
|
||||
|
||||
val commons = Seq(
|
||||
baseAquaVersion := "0.1.6",
|
||||
baseAquaVersion := "0.1.7",
|
||||
version := baseAquaVersion.value + "-" + sys.env.getOrElse("BUILD_NUMBER", "SNAPSHOT"),
|
||||
scalaVersion := dottyVersion,
|
||||
libraryDependencies ++= Seq(
|
||||
|
@ -1,6 +1,6 @@
|
||||
package aqua.model
|
||||
|
||||
import aqua.model.func.body.{CallServiceTag, FuncOp}
|
||||
import aqua.model.func.raw.{CallServiceTag, FuncOp}
|
||||
import aqua.model.func.{ArgsCall, FuncCallable, FuncModel}
|
||||
import aqua.types.{ProductType, Type}
|
||||
import cats.Monoid
|
||||
@ -8,6 +8,7 @@ import cats.data.NonEmptyMap
|
||||
import cats.syntax.apply._
|
||||
import cats.syntax.functor._
|
||||
import cats.syntax.monoid._
|
||||
import wvlet.log.LogSupport
|
||||
|
||||
import scala.collection.immutable.SortedMap
|
||||
|
||||
@ -64,7 +65,7 @@ case class AquaContext(
|
||||
.map(ProductType(name, _))
|
||||
}
|
||||
|
||||
object AquaContext {
|
||||
object AquaContext extends LogSupport {
|
||||
|
||||
trait Implicits {
|
||||
implicit val aquaContextMonoid: Monoid[AquaContext]
|
||||
@ -99,7 +100,7 @@ object AquaContext {
|
||||
FuncCallable(
|
||||
fnName,
|
||||
// TODO: capture ability resolution, get ID from the call context
|
||||
FuncOp.leaf(CallServiceTag(serviceId, fnName, call, None)),
|
||||
FuncOp.leaf(CallServiceTag(serviceId, fnName, call)),
|
||||
args,
|
||||
(ret.map(_.model), arrowType.res).mapN(_ -> _),
|
||||
Map.empty,
|
||||
|
@ -1,6 +1,6 @@
|
||||
package aqua.model
|
||||
|
||||
import aqua.model.func.body.FuncOp
|
||||
import aqua.model.func.raw.FuncOp
|
||||
import cats.kernel.Semigroup
|
||||
|
||||
trait Model
|
||||
|
@ -18,10 +18,18 @@ object ValueModel {
|
||||
implicit object ValueModelEq extends Eq[ValueModel] {
|
||||
override def eqv(x: ValueModel, y: ValueModel): Boolean = x == y
|
||||
}
|
||||
|
||||
def varName(vm: ValueModel): Option[String] =
|
||||
vm match {
|
||||
case VarModel(name, _, _) => Some(name)
|
||||
case _ => None
|
||||
}
|
||||
}
|
||||
|
||||
case class LiteralModel(value: String, `type`: Type) extends ValueModel {
|
||||
override def lastType: Type = `type`
|
||||
|
||||
override def toString: String = s"{$value: ${`type`}}"
|
||||
}
|
||||
|
||||
object LiteralModel {
|
||||
|
51
model/src/main/scala/aqua/model/cursor/ChainCursor.scala
Normal file
51
model/src/main/scala/aqua/model/cursor/ChainCursor.scala
Normal file
@ -0,0 +1,51 @@
|
||||
package aqua.model.cursor
|
||||
|
||||
import cats.data.{Chain, NonEmptyList}
|
||||
|
||||
abstract class ChainCursor[C <: ChainCursor[C, T], T](make: NonEmptyList[ChainZipper[T]] => C) {
|
||||
self =>
|
||||
|
||||
val tree: NonEmptyList[ChainZipper[T]]
|
||||
|
||||
def parent: Option[T] = tree.tail.headOption.map(_.current)
|
||||
|
||||
def current: T = tree.head.current
|
||||
|
||||
def leftSiblings: Chain[T] = tree.head.prev
|
||||
def rightSiblings: Chain[T] = tree.head.next
|
||||
|
||||
def mapParent(f: T => T): C =
|
||||
make(
|
||||
NonEmptyList(
|
||||
tree.head,
|
||||
tree.tail match {
|
||||
case h :: t => h.copy(current = f(h.current)) :: t
|
||||
case t => t
|
||||
}
|
||||
)
|
||||
)
|
||||
|
||||
def path: NonEmptyList[T] = tree.map(_.current)
|
||||
|
||||
def moveUp: Option[C] = NonEmptyList.fromList(tree.tail).map(make)
|
||||
|
||||
def pathToRoot: LazyList[C] = LazyList.unfold(this)(_.moveUp.map(c => c -> c))
|
||||
|
||||
def moveDown(focusOn: ChainZipper[T]): C = make(focusOn :: tree)
|
||||
|
||||
def moveLeft: Option[C] =
|
||||
toPrevSibling orElse moveUp.flatMap(_.moveLeft)
|
||||
|
||||
def moveRight: Option[C] =
|
||||
toNextSibling orElse moveUp.flatMap(_.moveRight)
|
||||
|
||||
def toNextSibling: Option[C] = tree.head.moveRight.map(p => make(tree.copy(p)))
|
||||
|
||||
def toPrevSibling: Option[C] = tree.head.moveLeft.map(p => make(tree.copy(p)))
|
||||
|
||||
def allToLeft: LazyList[C] =
|
||||
LazyList.unfold(this)(_.moveLeft.map(c => c -> c))
|
||||
|
||||
def allToRight: LazyList[C] =
|
||||
LazyList.unfold(this)(_.moveRight.map(c => c -> c))
|
||||
}
|
@ -1,4 +1,4 @@
|
||||
package aqua.model.topology
|
||||
package aqua.model.cursor
|
||||
|
||||
import cats.data.Chain
|
||||
import cats.free.Cofree
|
||||
@ -24,17 +24,36 @@ case class ChainZipper[T](prev: Chain[T], current: T, next: Chain[T]) {
|
||||
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 first[T](chain: Chain[T]): Option[ChainZipper[T]] =
|
||||
chain.uncons.map { case (current, next) =>
|
||||
ChainZipper(Chain.empty, current, next)
|
||||
}
|
||||
|
||||
def fromChainMap[T](chain: Chain[T], prev: Chain[T] = Chain.empty)(
|
||||
def last[T](chain: Chain[T]): Option[ChainZipper[T]] =
|
||||
chain.initLast.map { case (prev, current) =>
|
||||
ChainZipper(prev, current, Chain.empty)
|
||||
}
|
||||
|
||||
def traverseChain[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) +: traverseChain(next, prev :+ t)
|
||||
}
|
||||
|
||||
/**
|
||||
* Walks through the given chain as a zipper, applying the given function to it
|
||||
*
|
||||
* @param chain Chain to traverse and map
|
||||
* @param prev Accumulator: previous steps
|
||||
* @param f Function that converts a zipper (meaning an element with its horizontal context) to element
|
||||
* @tparam T Type
|
||||
* @return Resulting chain
|
||||
*/
|
||||
def traverseChainMap[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)
|
||||
f(ChainZipper(prev, t, next)).fold(traverseChainMap(next, prev)(f))(r =>
|
||||
r +: traverseChainMap(next, prev :+ r)(f)
|
||||
)
|
||||
}
|
||||
|
@ -16,6 +16,9 @@ case class Call(args: List[ValueModel], exportTo: Option[Call.Export]) {
|
||||
def argVarNames: Set[String] = args.collect { case VarModel(name, _, _) =>
|
||||
name
|
||||
}.toSet
|
||||
|
||||
override def toString: String =
|
||||
s"[${args.mkString(" ")}]${exportTo.map(_.model).map(" " + _).getOrElse("")}"
|
||||
}
|
||||
|
||||
object Call {
|
||||
|
@ -1,6 +1,6 @@
|
||||
package aqua.model.func
|
||||
|
||||
import aqua.model.func.body.{AssignmentTag, CallArrowTag, CallServiceTag, FuncOp, OpTag}
|
||||
import aqua.model.func.raw.{AssignmentTag, CallArrowTag, CallServiceTag, FuncOp, RawTag}
|
||||
import aqua.model.{Model, ValueModel, VarModel}
|
||||
import aqua.types.{ArrowType, Type}
|
||||
import cats.Eval
|
||||
@ -86,7 +86,7 @@ case class FuncCallable(
|
||||
(
|
||||
noNames,
|
||||
resolvedExports + (assignTo -> value.resolveWith(resolvedExports))
|
||||
) -> Cofree[Chain, OpTag](
|
||||
) -> Cofree[Chain, RawTag](
|
||||
tag.mapValues(_.resolveWith(resolvedExports)),
|
||||
Eval.now(Chain.empty)
|
||||
)
|
||||
@ -119,7 +119,7 @@ case class FuncCallable(
|
||||
}
|
||||
|
||||
// All the other tags are already resolved and need no substitution
|
||||
acc -> Cofree[Chain, OpTag](
|
||||
acc -> Cofree[Chain, RawTag](
|
||||
tag.mapValues(_.resolveWith(resolvedExports)),
|
||||
Eval.now(Chain.empty)
|
||||
)
|
||||
|
@ -1,6 +1,6 @@
|
||||
package aqua.model.func
|
||||
|
||||
import aqua.model.func.body.FuncOp
|
||||
import aqua.model.func.raw.FuncOp
|
||||
import aqua.model.{Model, ValueModel}
|
||||
import aqua.types.Type
|
||||
|
||||
@ -15,6 +15,6 @@ case class FuncModel(
|
||||
arrows: Map[String, FuncCallable],
|
||||
constants: Map[String, ValueModel]
|
||||
): FuncCallable =
|
||||
FuncCallable(name, body, args, ret, arrows, constants)
|
||||
FuncCallable(name, body.fixXorPar, args, ret, arrows, constants)
|
||||
|
||||
}
|
||||
|
@ -1,4 +1,4 @@
|
||||
package aqua.model.func.body
|
||||
package aqua.model.func.raw
|
||||
|
||||
import aqua.model.func.Call
|
||||
import aqua.model.{Model, ValueModel, VarModel}
|
||||
@ -9,8 +9,8 @@ import cats.kernel.Semigroup
|
||||
import cats.syntax.apply._
|
||||
import cats.syntax.functor._
|
||||
|
||||
case class FuncOp(tree: Cofree[Chain, OpTag]) extends Model {
|
||||
def head: OpTag = tree.head
|
||||
case class FuncOp(tree: Cofree[Chain, RawTag]) extends Model {
|
||||
def head: RawTag = tree.head
|
||||
|
||||
lazy val isRightAssoc: Boolean = head match {
|
||||
case XorTag | ParTag => true
|
||||
@ -18,13 +18,13 @@ case class FuncOp(tree: Cofree[Chain, OpTag]) extends Model {
|
||||
case _ => false
|
||||
}
|
||||
|
||||
def cata[T](folder: (OpTag, Chain[T]) => Eval[T]): Eval[T] =
|
||||
def cata[T](folder: (RawTag, Chain[T]) => Eval[T]): Eval[T] =
|
||||
Cofree.cata(tree)(folder)
|
||||
|
||||
def definesVarNames: Eval[Set[String]] = cata[Set[String]] {
|
||||
case (CallArrowTag(_, Call(_, Some(export))), acc) =>
|
||||
Eval.later(acc.foldLeft(Set(export.name))(_ ++ _))
|
||||
case (CallServiceTag(_, _, Call(_, Some(export)), _), acc) =>
|
||||
case (CallServiceTag(_, _, Call(_, Some(export))), acc) =>
|
||||
Eval.later(acc.foldLeft(Set(export.name))(_ ++ _))
|
||||
case (NextTag(export), acc) => Eval.later(acc.foldLeft(Set(export))(_ ++ _))
|
||||
case (_, acc) => Eval.later(acc.foldLeft(Set.empty[String])(_ ++ _))
|
||||
@ -33,25 +33,30 @@ case class FuncOp(tree: Cofree[Chain, OpTag]) extends Model {
|
||||
def exportsVarNames: Eval[Set[String]] = cata[Set[String]] {
|
||||
case (CallArrowTag(_, Call(_, Some(export))), acc) =>
|
||||
Eval.later(acc.foldLeft(Set(export.name))(_ ++ _))
|
||||
case (CallServiceTag(_, _, Call(_, Some(export)), _), acc) =>
|
||||
case (CallServiceTag(_, _, Call(_, Some(export))), acc) =>
|
||||
Eval.later(acc.foldLeft(Set(export.name))(_ ++ _))
|
||||
case (_, acc) => Eval.later(acc.foldLeft(Set.empty[String])(_ ++ _))
|
||||
}
|
||||
|
||||
// TODO: as it is used for checking of intersection, make it a lazy traverse with fail-fast
|
||||
def usesVarNames: Eval[Set[String]] = cata[Set[String]] {
|
||||
case (CallArrowTag(_, call), acc) =>
|
||||
Eval.later(acc.foldLeft(call.argVarNames)(_ ++ _))
|
||||
case (CallServiceTag(_, _, call, _), acc) =>
|
||||
case (CallServiceTag(_, _, call), acc) =>
|
||||
Eval.later(acc.foldLeft(call.argVarNames)(_ ++ _))
|
||||
case (MatchMismatchTag(a, b, _), acc) =>
|
||||
Eval.later(acc.foldLeft(ValueModel.varName(a).toSet ++ ValueModel.varName(b))(_ ++ _))
|
||||
case (ForTag(_, VarModel(name, _, _)), acc) =>
|
||||
Eval.later(acc.foldLeft(Set(name))(_ ++ _))
|
||||
case (_, acc) => Eval.later(acc.foldLeft(Set.empty[String])(_ ++ _))
|
||||
}
|
||||
|
||||
def resolveValues(vals: Map[String, ValueModel]): FuncOp =
|
||||
FuncOp(tree.map[OpTag](_.mapValues(_.resolveWith(vals))))
|
||||
FuncOp(tree.map[RawTag](_.mapValues(_.resolveWith(vals))))
|
||||
|
||||
def rename(vals: Map[String, String]): FuncOp =
|
||||
FuncOp(
|
||||
tree.map[OpTag](op =>
|
||||
tree.map[RawTag](op =>
|
||||
op.mapValues {
|
||||
case v: VarModel if vals.contains(v.name) => v.copy(name = vals(v.name))
|
||||
case v => v
|
||||
@ -68,13 +73,29 @@ case class FuncOp(tree: Cofree[Chain, OpTag]) extends Model {
|
||||
|
||||
def :+:(prev: FuncOp): FuncOp =
|
||||
FuncOp.RightAssocSemi.combine(prev, this)
|
||||
|
||||
// Function body must be fixed before function gets resolved
|
||||
def fixXorPar: FuncOp =
|
||||
FuncOp(cata[Cofree[Chain, RawTag]] {
|
||||
case (XorParTag(left, right), _) =>
|
||||
Eval.now(
|
||||
FuncOps
|
||||
.par(
|
||||
FuncOp.wrap(XorTag, left),
|
||||
right
|
||||
)
|
||||
.tree
|
||||
)
|
||||
|
||||
case (head, tail) => Eval.now(Cofree(head, Eval.now(tail)))
|
||||
}.value)
|
||||
}
|
||||
|
||||
object FuncOp {
|
||||
type Tree = Cofree[Chain, OpTag]
|
||||
type Tree = Cofree[Chain, RawTag]
|
||||
|
||||
def traverseA[A](cf: Tree, init: A)(
|
||||
f: (A, OpTag) => (A, Tree)
|
||||
f: (A, RawTag) => (A, Tree)
|
||||
): Eval[(A, Tree)] = {
|
||||
val (headA, head) = f(init, cf.head)
|
||||
// TODO: it should be in standard library, with some other types
|
||||
@ -95,7 +116,7 @@ object FuncOp {
|
||||
FuncOp(y.tree.copy(tail = (x.tree.tail, y.tree.tail).mapN(_ ++ _)))
|
||||
case (XorTag.LeftBiased, XorTag) =>
|
||||
wrap(SeqTag, FuncOp(y.tree.copy(tail = (x.tree.tail, y.tree.tail).mapN(_ ++ _))))
|
||||
case (XorTag, ParTag) => FuncOp(Cofree[Chain, OpTag](XorParTag(x, y), Eval.now(Chain.empty)))
|
||||
case (XorTag, ParTag) => FuncOp(Cofree[Chain, RawTag](XorParTag(x, y), Eval.now(Chain.empty)))
|
||||
case (_, ParTag | XorTag) =>
|
||||
wrap(SeqTag, FuncOp(y.tree.copy(tail = y.tree.tail.map(_.prepend(x.tree)))))
|
||||
case (_, XorParTag(xor, par)) =>
|
||||
@ -116,10 +137,10 @@ object FuncOp {
|
||||
}
|
||||
}
|
||||
|
||||
def leaf(tag: OpTag): FuncOp = FuncOp(Cofree[Chain, OpTag](tag, Eval.now(Chain.empty)))
|
||||
def leaf(tag: RawTag): FuncOp = FuncOp(Cofree[Chain, RawTag](tag, Eval.now(Chain.empty)))
|
||||
|
||||
def wrap(tag: OpTag, child: FuncOp): FuncOp = node(tag, Chain.one(child))
|
||||
def wrap(tag: RawTag, child: FuncOp): FuncOp = node(tag, Chain.one(child))
|
||||
|
||||
def node(tag: OpTag, children: Chain[FuncOp]): FuncOp =
|
||||
FuncOp(Cofree[Chain, OpTag](tag, Eval.later(children.map(_.tree))))
|
||||
def node(tag: RawTag, children: Chain[FuncOp]): FuncOp =
|
||||
FuncOp(Cofree[Chain, RawTag](tag, Eval.later(children.map(_.tree))))
|
||||
}
|
@ -1,4 +1,4 @@
|
||||
package aqua.model.func.body
|
||||
package aqua.model.func.raw
|
||||
|
||||
import aqua.model.func.Call
|
||||
import aqua.model.{LiteralModel, ValueModel}
|
||||
@ -7,12 +7,12 @@ import cats.free.Cofree
|
||||
|
||||
object FuncOps {
|
||||
|
||||
def noop(peerId: ValueModel): FuncOp =
|
||||
FuncOp.leaf(CallServiceTag(LiteralModel.quote("op"), "identity", Call(Nil, None), Some(peerId)))
|
||||
def noop: FuncOp =
|
||||
FuncOp.leaf(CallServiceTag(LiteralModel.quote("op"), "identity", Call(Nil, None)))
|
||||
|
||||
def identity(what: ValueModel, to: Call.Export): FuncOp =
|
||||
FuncOp.leaf(
|
||||
CallServiceTag(LiteralModel.quote("op"), "identity", Call(what :: Nil, Some(to)), None)
|
||||
CallServiceTag(LiteralModel.quote("op"), "identity", Call(what :: Nil, Some(to)))
|
||||
)
|
||||
|
||||
def callService(srvId: ValueModel, funcName: String, call: Call): FuncOp =
|
||||
@ -51,6 +51,19 @@ object FuncOps {
|
||||
.map(FuncOp(_))
|
||||
)
|
||||
|
||||
def par(ops: FuncOp*): FuncOp =
|
||||
if (ops.length == 1) ops.head
|
||||
else
|
||||
FuncOp.node(
|
||||
ParTag,
|
||||
Chain
|
||||
.fromSeq(ops.flatMap {
|
||||
case FuncOp(Cofree(ParTag, subOps)) => subOps.value.toList
|
||||
case FuncOp(cof) => cof :: Nil
|
||||
})
|
||||
.map(FuncOp(_))
|
||||
)
|
||||
|
||||
def xor(left: FuncOp, right: FuncOp): FuncOp =
|
||||
FuncOp.node(XorTag, Chain(left, right))
|
||||
|
||||
@ -62,16 +75,4 @@ object FuncOps {
|
||||
|
||||
def next(item: String): FuncOp =
|
||||
FuncOp.leaf(NextTag(item))
|
||||
|
||||
def meta(op: FuncOp, skipTopology: Boolean = false, comment: String = null): FuncOp =
|
||||
FuncOp(
|
||||
op.tree.copy(
|
||||
MetaTag(
|
||||
skipTopology = skipTopology,
|
||||
comment = Option(comment),
|
||||
op.head
|
||||
),
|
||||
op.tree.tail
|
||||
)
|
||||
)
|
||||
}
|
98
model/src/main/scala/aqua/model/func/raw/PathFinder.scala
Normal file
98
model/src/main/scala/aqua/model/func/raw/PathFinder.scala
Normal file
@ -0,0 +1,98 @@
|
||||
package aqua.model.func.raw
|
||||
|
||||
import aqua.model.ValueModel
|
||||
import cats.data.Chain
|
||||
import cats.data.Chain.{:==, ==:, nil}
|
||||
import wvlet.log.LogSupport
|
||||
|
||||
import scala.annotation.tailrec
|
||||
|
||||
object PathFinder extends LogSupport {
|
||||
|
||||
def find(from: RawCursor, to: RawCursor, isExit: Boolean = false): Chain[ValueModel] = {
|
||||
|
||||
val fromOn = Chain.fromSeq(from.pathOn).reverse
|
||||
val toOn = Chain.fromSeq(to.pathOn).reverse
|
||||
|
||||
val wasHandled =
|
||||
!isExit &&
|
||||
to.leftSiblings.isEmpty &&
|
||||
to.moveUp.exists(_.pathOn == to.pathOn) &&
|
||||
!to.parentTag.contains(ParTag)
|
||||
|
||||
if (wasHandled) {
|
||||
debug("Was handled")
|
||||
debug(" :: " + from)
|
||||
debug(" -> " + to)
|
||||
Chain.empty
|
||||
} else {
|
||||
debug("Find path")
|
||||
debug(" :: " + from)
|
||||
debug(" -> " + to)
|
||||
findPath(
|
||||
fromOn,
|
||||
toOn,
|
||||
Chain.fromOption(from.currentPeerId),
|
||||
Chain.fromOption(to.currentPeerId)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
def optimizePath(
|
||||
peerIds: Chain[ValueModel],
|
||||
prefix: Chain[ValueModel],
|
||||
suffix: Chain[ValueModel]
|
||||
): Chain[ValueModel] = {
|
||||
val optimized = 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
|
||||
}
|
||||
val noPrefix = skipPrefix(optimized, prefix, optimized)
|
||||
skipSuffix(noPrefix, suffix, noPrefix)
|
||||
}
|
||||
|
||||
def findPath(
|
||||
fromOn: Chain[OnTag],
|
||||
toOn: Chain[OnTag],
|
||||
fromPeer: Chain[ValueModel],
|
||||
toPeer: Chain[ValueModel]
|
||||
): Chain[ValueModel] = {
|
||||
val (from, to) = skipCommonPrefix(fromOn, toOn)
|
||||
val fromFix =
|
||||
if (from.isEmpty && fromPeer != toPeer) Chain.fromOption(fromOn.lastOption) else from
|
||||
val toFix = if (to.isEmpty && fromPeer != toPeer) Chain.fromOption(toOn.lastOption) else to
|
||||
val fromTo = fromFix.reverse.flatMap(_.via.reverse) ++ toFix.flatMap(_.via)
|
||||
val optimized = optimizePath(fromPeer ++ fromTo ++ toPeer, fromPeer, toPeer)
|
||||
|
||||
trace("FIND PATH " + fromFix)
|
||||
trace(" -> " + toFix)
|
||||
trace(s"$fromPeer $toPeer")
|
||||
trace(" Optimized: " + optimized)
|
||||
optimized
|
||||
}
|
||||
|
||||
@tailrec
|
||||
def skipPrefix[T](chain: Chain[T], prefix: Chain[T], init: Chain[T]): Chain[T] =
|
||||
(chain, prefix) match {
|
||||
case (c ==: ctail, p ==: ptail) if c == p => skipPrefix(ctail, ptail, init)
|
||||
case (_, `nil`) => chain
|
||||
case (_, _) => init
|
||||
}
|
||||
|
||||
@tailrec
|
||||
def skipCommonPrefix[T](chain1: Chain[T], chain2: Chain[T]): (Chain[T], Chain[T]) =
|
||||
(chain1, chain2) match {
|
||||
case (c ==: ctail, p ==: ptail) if c == p => skipCommonPrefix(ctail, ptail)
|
||||
case _ => chain1 -> chain2
|
||||
}
|
||||
|
||||
@tailrec
|
||||
def skipSuffix[T](chain: Chain[T], suffix: Chain[T], init: Chain[T]): Chain[T] =
|
||||
(chain, suffix) match {
|
||||
case (cinit :== c, pinit :== p) if c == p => skipSuffix(cinit, pinit, init)
|
||||
case (_, `nil`) => chain
|
||||
case (_, _) => init
|
||||
}
|
||||
}
|
155
model/src/main/scala/aqua/model/func/raw/RawCursor.scala
Normal file
155
model/src/main/scala/aqua/model/func/raw/RawCursor.scala
Normal file
@ -0,0 +1,155 @@
|
||||
package aqua.model.func.raw
|
||||
|
||||
import aqua.model.ValueModel
|
||||
import aqua.model.cursor.{ChainCursor, ChainZipper}
|
||||
import cats.Eval
|
||||
import cats.data.{Chain, NonEmptyList, OptionT}
|
||||
import cats.free.Cofree
|
||||
import cats.syntax.traverse._
|
||||
import wvlet.log.LogSupport
|
||||
|
||||
// Can be heavily optimized by caching parent cursors, not just list of zippers
|
||||
case class RawCursor(tree: NonEmptyList[ChainZipper[FuncOp.Tree]])
|
||||
extends ChainCursor[RawCursor, FuncOp.Tree](RawCursor) with LogSupport {
|
||||
def tag: RawTag = current.head
|
||||
def parentTag: Option[RawTag] = parent.map(_.head)
|
||||
|
||||
def hasChildren: Boolean =
|
||||
current.tailForced.nonEmpty
|
||||
|
||||
lazy val toFirstChild: Option[RawCursor] =
|
||||
ChainZipper.first(current.tail.value).map(moveDown)
|
||||
|
||||
lazy val toLastChild: Option[RawCursor] =
|
||||
ChainZipper.last(current.tail.value).map(moveDown)
|
||||
|
||||
lazy val tagsPath: NonEmptyList[RawTag] = path.map(_.head)
|
||||
|
||||
lazy val pathOn: List[OnTag] = tagsPath.collect { case o: OnTag =>
|
||||
o
|
||||
}
|
||||
|
||||
lazy val rootOn: Option[RawCursor] = moveUp
|
||||
.flatMap(_.rootOn)
|
||||
.orElse(tag match {
|
||||
case _: OnTag =>
|
||||
Some(this)
|
||||
case _ => None
|
||||
})
|
||||
|
||||
lazy val currentPeerId: Option[ValueModel] =
|
||||
pathOn.headOption.map(_.peerId)
|
||||
|
||||
// Cursor to the last sequentially executed operation, if any
|
||||
lazy val lastExecuted: Option[RawCursor] = tag match {
|
||||
case XorTag => toFirstChild.flatMap(_.lastExecuted)
|
||||
case _: SeqGroupTag => toLastChild.flatMap(_.lastExecuted)
|
||||
case ParTag => None
|
||||
case _: NoExecTag => None
|
||||
case _ => Some(this)
|
||||
}
|
||||
|
||||
lazy val firstExecuted: Option[RawCursor] = tag match {
|
||||
case _: SeqGroupTag => toLastChild.flatMap(_.lastExecuted)
|
||||
case ParTag => None
|
||||
case _: NoExecTag => None
|
||||
case _ => Some(this)
|
||||
}
|
||||
|
||||
/**
|
||||
* Sequentially previous cursor
|
||||
* @return
|
||||
*/
|
||||
lazy val seqPrev: Option[RawCursor] =
|
||||
parentTag.flatMap {
|
||||
case p: SeqGroupTag if leftSiblings.nonEmpty =>
|
||||
toPrevSibling.flatMap(c => c.lastExecuted orElse c.seqPrev)
|
||||
case _ =>
|
||||
moveUp.flatMap(_.seqPrev)
|
||||
}
|
||||
|
||||
lazy val seqNext: Option[RawCursor] =
|
||||
parentTag.flatMap {
|
||||
case _: SeqGroupTag if rightSiblings.nonEmpty =>
|
||||
toNextSibling.flatMap(c => c.firstExecuted orElse c.seqNext)
|
||||
case _ => moveUp.flatMap(_.seqNext)
|
||||
}
|
||||
|
||||
// TODO write a test
|
||||
def checkNamesUsedLater(names: Set[String]): Boolean =
|
||||
allToRight
|
||||
.map(_.current)
|
||||
.map(FuncOp(_))
|
||||
.exists(_.usesVarNames.value.intersect(names).nonEmpty)
|
||||
|
||||
lazy val pathFromPrev: Chain[ValueModel] = pathFromPrevD()
|
||||
|
||||
def pathFromPrevD(forExit: Boolean = false): Chain[ValueModel] =
|
||||
parentTag.fold(Chain.empty[ValueModel]) {
|
||||
case _: GroupTag =>
|
||||
seqPrev
|
||||
.orElse(rootOn)
|
||||
.fold(Chain.empty[ValueModel])(PathFinder.find(_, this, isExit = forExit))
|
||||
case _ =>
|
||||
Chain.empty
|
||||
}
|
||||
|
||||
lazy val pathToNext: Chain[ValueModel] = parentTag.fold(Chain.empty[ValueModel]) {
|
||||
case ParTag =>
|
||||
val exports = FuncOp(current).exportsVarNames.value
|
||||
if (exports.nonEmpty && checkNamesUsedLater(exports))
|
||||
seqNext.fold(Chain.empty[ValueModel])(nxt =>
|
||||
PathFinder.find(this, nxt) ++
|
||||
// we need to "wake" the target peer to enable join behaviour
|
||||
Chain.fromOption(nxt.currentPeerId)
|
||||
)
|
||||
else Chain.empty
|
||||
case XorTag if leftSiblings.nonEmpty =>
|
||||
lastExecuted
|
||||
.flatMap(le =>
|
||||
seqNext
|
||||
.map(nxt => PathFinder.find(le, nxt, isExit = true) -> nxt)
|
||||
.flatMap {
|
||||
case (path, nxt) if path.isEmpty && currentPeerId == nxt.currentPeerId =>
|
||||
nxt.pathFromPrevD(true).reverse.initLast.map(_._1)
|
||||
case (path, nxt) =>
|
||||
path.initLast.map {
|
||||
case (init, last)
|
||||
if nxt.pathFromPrevD(forExit = true).headOption.contains(last) =>
|
||||
init
|
||||
case (init, last) => init :+ last
|
||||
}
|
||||
}
|
||||
)
|
||||
.getOrElse(Chain.empty)
|
||||
case _ =>
|
||||
Chain.empty
|
||||
}
|
||||
|
||||
def cata[A](wrap: ChainZipper[Cofree[Chain, A]] => Chain[Cofree[Chain, A]])(
|
||||
f: RawCursor => OptionT[Eval, ChainZipper[Cofree[Chain, A]]]
|
||||
): Eval[Chain[Cofree[Chain, A]]] =
|
||||
f(this).map { case ChainZipper(prev, curr, next) =>
|
||||
val inner = curr.copy(tail =
|
||||
Eval
|
||||
.later(
|
||||
Chain.fromSeq(
|
||||
toFirstChild
|
||||
.map(fc =>
|
||||
LazyList
|
||||
.unfold(fc) { _.toNextSibling.map(c => c -> c) }
|
||||
.prepended(fc)
|
||||
)
|
||||
.getOrElse(LazyList.empty)
|
||||
)
|
||||
)
|
||||
// TODO: this can be parallelized
|
||||
.flatMap(_.traverse(_.cata(wrap)(f)))
|
||||
.map(_.flatMap(identity))
|
||||
.flatMap(addition => curr.tail.map(_ ++ addition))
|
||||
)
|
||||
wrap(ChainZipper(prev, inner, next))
|
||||
}.getOrElse(Chain.empty).memoize
|
||||
|
||||
override def toString: String = s"$tag /: ${moveUp.getOrElse("(|)")}"
|
||||
}
|
@ -1,12 +1,12 @@
|
||||
package aqua.model.func.body
|
||||
package aqua.model.func.raw
|
||||
|
||||
import aqua.model.ValueModel
|
||||
import aqua.model.func.Call
|
||||
import cats.data.Chain
|
||||
|
||||
sealed trait OpTag {
|
||||
sealed trait RawTag {
|
||||
|
||||
def mapValues(f: ValueModel => ValueModel): OpTag = this match {
|
||||
def mapValues(f: ValueModel => ValueModel): RawTag = this match {
|
||||
case OnTag(peerId, via) => OnTag(f(peerId), via.map(f))
|
||||
case MatchMismatchTag(left, right, shouldMatch) =>
|
||||
MatchMismatchTag(f(left), f(right), shouldMatch)
|
||||
@ -16,12 +16,11 @@ sealed trait OpTag {
|
||||
funcName,
|
||||
call.mapValues(f)
|
||||
)
|
||||
case CallServiceTag(serviceId, funcName, call, pid) =>
|
||||
case CallServiceTag(serviceId, funcName, call) =>
|
||||
CallServiceTag(
|
||||
f(serviceId),
|
||||
funcName,
|
||||
call.mapValues(f),
|
||||
pid.map(f)
|
||||
call.mapValues(f)
|
||||
)
|
||||
case AssignmentTag(value, assignTo) =>
|
||||
AssignmentTag(f(value), assignTo)
|
||||
@ -32,49 +31,49 @@ sealed trait OpTag {
|
||||
|
||||
}
|
||||
|
||||
sealed trait NoAirTag extends OpTag
|
||||
sealed trait NoExecTag extends RawTag
|
||||
|
||||
sealed trait GroupTag extends OpTag
|
||||
sealed trait GroupTag extends RawTag
|
||||
sealed trait SeqGroupTag extends GroupTag
|
||||
|
||||
case object SeqTag extends SeqGroupTag
|
||||
case object ParTag extends GroupTag
|
||||
|
||||
case object XorTag extends GroupTag {
|
||||
case object XorTag extends SeqGroupTag {
|
||||
case object LeftBiased extends GroupTag
|
||||
}
|
||||
case class XorParTag(xor: FuncOp, par: FuncOp) extends OpTag
|
||||
case class OnTag(peerId: ValueModel, via: Chain[ValueModel]) extends SeqGroupTag
|
||||
case class NextTag(item: String) extends OpTag
|
||||
case class XorParTag(xor: FuncOp, par: FuncOp) extends RawTag
|
||||
|
||||
case class OnTag(peerId: ValueModel, via: Chain[ValueModel]) extends SeqGroupTag {
|
||||
|
||||
override def toString: String =
|
||||
s"(on $peerId${if (via.nonEmpty) " via " + via.toList.mkString(" via ") else ""})"
|
||||
}
|
||||
case class NextTag(item: String) extends RawTag
|
||||
|
||||
case class MatchMismatchTag(left: ValueModel, right: ValueModel, shouldMatch: Boolean)
|
||||
extends SeqGroupTag
|
||||
case class ForTag(item: String, iterable: ValueModel) extends SeqGroupTag
|
||||
|
||||
case class MetaTag(
|
||||
skipTopology: Boolean = false,
|
||||
comment: Option[String] = None,
|
||||
op: OpTag
|
||||
) extends OpTag
|
||||
|
||||
case class CallArrowTag(
|
||||
funcName: String,
|
||||
call: Call
|
||||
) extends OpTag
|
||||
) extends RawTag
|
||||
|
||||
case class AssignmentTag(
|
||||
value: ValueModel,
|
||||
assignTo: String
|
||||
) extends NoAirTag
|
||||
) extends NoExecTag
|
||||
|
||||
case class AbilityIdTag(
|
||||
value: ValueModel,
|
||||
service: String
|
||||
) extends NoAirTag
|
||||
) extends NoExecTag
|
||||
|
||||
case class CallServiceTag(
|
||||
serviceId: ValueModel,
|
||||
funcName: String,
|
||||
call: Call,
|
||||
peerId: Option[ValueModel] = None
|
||||
) extends OpTag
|
||||
call: Call
|
||||
) extends RawTag {
|
||||
override def toString: String = s"(call _ ($serviceId $funcName) $call)"
|
||||
}
|
@ -1,4 +1,4 @@
|
||||
package aqua.model.func.body
|
||||
package aqua.model.func.raw
|
||||
|
||||
import cats.Show
|
||||
import cats.free.Cofree
|
31
model/src/main/scala/aqua/model/func/resolved/MakeRes.scala
Normal file
31
model/src/main/scala/aqua/model/func/resolved/MakeRes.scala
Normal file
@ -0,0 +1,31 @@
|
||||
package aqua.model.func.resolved
|
||||
|
||||
import aqua.model.func.Call
|
||||
import aqua.model.{LiteralModel, ValueModel}
|
||||
import cats.Eval
|
||||
import cats.data.Chain
|
||||
import cats.free.Cofree
|
||||
|
||||
object MakeRes {
|
||||
val nilTail: Eval[Chain[Cofree[Chain, ResolvedOp]]] = Eval.now(Chain.empty)
|
||||
type Cof = Cofree[Chain, ResolvedOp]
|
||||
def leaf(op: ResolvedOp): Cof = Cofree[Chain, ResolvedOp](op, nilTail)
|
||||
|
||||
def next(item: String): Cof =
|
||||
leaf(NextRes(item))
|
||||
|
||||
def seq(first: Cof, second: Cof, more: Cof*): Cof =
|
||||
Cofree[Chain, ResolvedOp](SeqRes, Eval.later(first +: second +: Chain.fromSeq(more)))
|
||||
|
||||
def par(first: Cof, second: Cof, more: Cof*): Cof =
|
||||
Cofree[Chain, ResolvedOp](ParRes, Eval.later(first +: second +: Chain.fromSeq(more)))
|
||||
|
||||
def xor(first: Cof, second: Cof): Cof =
|
||||
Cofree[Chain, ResolvedOp](XorRes, Eval.later(Chain(first, second)))
|
||||
|
||||
def fold(item: String, iter: ValueModel, body: Cof): Cof =
|
||||
Cofree[Chain, ResolvedOp](FoldRes(item, iter), Eval.now(Chain.one(body)))
|
||||
|
||||
def noop(onPeer: ValueModel): Cof =
|
||||
leaf(CallServiceRes(LiteralModel.quote("op"), "noop", Call(Nil, None), onPeer))
|
||||
}
|
@ -0,0 +1,34 @@
|
||||
package aqua.model.func.resolved
|
||||
|
||||
import aqua.model.ValueModel
|
||||
import aqua.model.func.Call
|
||||
|
||||
sealed trait ResolvedOp
|
||||
|
||||
sealed trait NoAir extends ResolvedOp
|
||||
|
||||
case object SeqRes extends ResolvedOp
|
||||
case object ParRes extends ResolvedOp
|
||||
case object XorRes extends ResolvedOp
|
||||
|
||||
case class NextRes(item: String) extends ResolvedOp
|
||||
|
||||
case class MatchMismatchRes(left: ValueModel, right: ValueModel, shouldMatch: Boolean)
|
||||
extends ResolvedOp {
|
||||
override def toString: String = s"(${if (shouldMatch) "match" else "mismatch"} $left $right)"
|
||||
}
|
||||
case class FoldRes(item: String, iterable: ValueModel) extends ResolvedOp
|
||||
|
||||
case class AbilityIdRes(
|
||||
value: ValueModel,
|
||||
service: String
|
||||
) extends NoAir
|
||||
|
||||
case class CallServiceRes(
|
||||
serviceId: ValueModel,
|
||||
funcName: String,
|
||||
call: Call,
|
||||
peerId: ValueModel
|
||||
) extends ResolvedOp {
|
||||
override def toString: String = s"(call $peerId ($serviceId $funcName) $call)"
|
||||
}
|
@ -1,128 +0,0 @@
|
||||
package aqua.model.topology
|
||||
|
||||
import Topology.Tree
|
||||
import aqua.model.func.body.{MetaTag, OnTag, OpTag, ParTag, SeqTag, XorTag}
|
||||
import cats.Eval
|
||||
import cats.data.Chain
|
||||
import cats.free.Cofree
|
||||
import cats.syntax.functor._
|
||||
import wvlet.log.LogSupport
|
||||
|
||||
case class Cursor(point: ChainZipper[Tree], loc: Location) extends LogSupport {
|
||||
|
||||
def downLoc(tree: Tree): Location =
|
||||
loc.down(point.copy(current = tree))
|
||||
|
||||
def moveUp: Option[Cursor] =
|
||||
loc.path match {
|
||||
case cz :: tail =>
|
||||
Some(Cursor(cz.replaceInjecting(point), Location(tail)))
|
||||
case _ =>
|
||||
None
|
||||
}
|
||||
|
||||
def pathToRoot: LazyList[Cursor] =
|
||||
moveUp.to(LazyList).flatMap(c => c #:: c.pathToRoot)
|
||||
|
||||
def mapParent(f: Tree => Tree): Cursor =
|
||||
copy(loc =
|
||||
Location(
|
||||
loc.path match {
|
||||
case parent :: tail => parent.copy(current = f(parent.current)) :: tail
|
||||
case path => path
|
||||
}
|
||||
)
|
||||
)
|
||||
|
||||
def prevOnTags: Chain[OnTag] =
|
||||
Chain
|
||||
.fromOption(
|
||||
point.prev.lastOption
|
||||
.map(t => loc.pathOn -> t)
|
||||
.orElse(loc.lastLeftSeq.map(_.map(_.pathOn).swap.map(_.current)))
|
||||
)
|
||||
.flatMap(pt => pt._1.widen[OpTag] ++ Cursor.rightBoundary(pt._2))
|
||||
.takeWhile {
|
||||
case ParTag => false
|
||||
case MetaTag(_, _, ParTag) => false
|
||||
case _ => true
|
||||
}
|
||||
.collect {
|
||||
case o: OnTag =>
|
||||
o
|
||||
case MetaTag(false, _, o: OnTag) =>
|
||||
o
|
||||
}
|
||||
|
||||
def nextOnTags: Chain[OnTag] =
|
||||
Chain
|
||||
.fromOption(
|
||||
loc.lastRightSeq
|
||||
.map(_.map(_.pathOn).swap.map(_.current))
|
||||
)
|
||||
.flatMap(pt => pt._1 ++ Cursor.leftBoundary(pt._2))
|
||||
.takeWhile {
|
||||
case ParTag => false
|
||||
case MetaTag(_, _, ParTag) => false
|
||||
case _ => true
|
||||
}
|
||||
.collect {
|
||||
case o: OnTag =>
|
||||
o
|
||||
case MetaTag(false, _, o: OnTag) =>
|
||||
o
|
||||
}
|
||||
}
|
||||
|
||||
object Cursor {
|
||||
|
||||
def rightBoundary(root: Tree): Chain[OpTag] =
|
||||
Chain.fromSeq(rightBoundaryLazy(root))
|
||||
|
||||
def rightBoundaryLazy(root: Tree): LazyList[OpTag] =
|
||||
root.head #:: (root.head match {
|
||||
case XorTag =>
|
||||
root.tailForced.reverse.toList match {
|
||||
case _ :: v :: _ =>
|
||||
// Go through the main branch of xor
|
||||
rightBoundaryLazy(v)
|
||||
case v :: Nil => rightBoundaryLazy(v)
|
||||
case _ => LazyList.empty
|
||||
}
|
||||
case _ =>
|
||||
root.tailForced.lastOption.to(LazyList).flatMap(rightBoundaryLazy)
|
||||
})
|
||||
|
||||
def leftBoundary(root: Tree): Chain[OpTag] =
|
||||
Chain
|
||||
.fromSeq(
|
||||
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()))
|
||||
}
|
||||
|
||||
}
|
@ -1,62 +0,0 @@
|
||||
package aqua.model.topology
|
||||
|
||||
import aqua.model.func.body.{MetaTag, OnTag, SeqGroupTag}
|
||||
import cats.data.Chain
|
||||
import cats.free.Cofree
|
||||
import wvlet.log.LogSupport
|
||||
|
||||
case class Location(path: List[ChainZipper[Topology.Tree]] = Nil) extends LogSupport {
|
||||
def down(h: ChainZipper[Topology.Tree]): Location = copy(h :: path)
|
||||
|
||||
def lastOn: Option[OnTag] = pathOn.lastOption
|
||||
|
||||
def firstOn: Option[OnTag] = pathOn.headOption
|
||||
|
||||
lazy val pathOn: Chain[OnTag] = Chain
|
||||
.fromSeq(path.map(_.current.head).collect {
|
||||
case o: OnTag =>
|
||||
o
|
||||
case MetaTag(false, _, o: OnTag) => o
|
||||
})
|
||||
.reverse
|
||||
|
||||
def lastLeftSeq: Option[(ChainZipper[Topology.Tree], Location)] =
|
||||
path match {
|
||||
case (cz @ ChainZipper(
|
||||
prev,
|
||||
Cofree(_: SeqGroupTag | MetaTag(false, _, _: 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 | MetaTag(false, _, _: SeqGroupTag), _),
|
||||
next
|
||||
)) :: tail if next.nonEmpty =>
|
||||
cz.moveRight.map(_ -> Location(tail))
|
||||
case _ :: tail => Location(tail).lastRightSeq
|
||||
case Nil => None
|
||||
}
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -1,225 +1,129 @@
|
||||
package aqua.model.topology
|
||||
|
||||
import aqua.model.{ValueModel, VarModel}
|
||||
import aqua.model.func.body._
|
||||
import aqua.model.{LiteralModel, ValueModel, VarModel}
|
||||
import aqua.model.func.raw._
|
||||
import cats.Eval
|
||||
import cats.data.Chain
|
||||
import cats.data.Chain.{:==, ==:, nil}
|
||||
import cats.data.{Chain, NonEmptyChain, NonEmptyList, OptionT}
|
||||
import cats.data.Chain.nil
|
||||
import cats.free.Cofree
|
||||
import ChainZipper.Matchers._
|
||||
import Location.Matchers._
|
||||
import aqua.model.cursor.ChainZipper
|
||||
import aqua.model.func.resolved._
|
||||
import aqua.types.{BoxType, ScalarType}
|
||||
import wvlet.log.LogSupport
|
||||
|
||||
import scala.annotation.tailrec
|
||||
import cats.syntax.traverse._
|
||||
|
||||
object Topology extends LogSupport {
|
||||
type Tree = Cofree[Chain, OpTag]
|
||||
type Tree = Cofree[Chain, RawTag]
|
||||
type Res = Cofree[Chain, ResolvedOp]
|
||||
|
||||
def resolve(op: Tree): Tree =
|
||||
def resolve(op: Tree): Res =
|
||||
Cofree
|
||||
.cata[Chain, OpTag, Tree](resolveOnMoves(op)) {
|
||||
case (SeqTag | _: OnTag | MetaTag(false, _, SeqTag | _: OnTag), children) =>
|
||||
.cata[Chain, ResolvedOp, Res](resolveOnMoves(op).value) {
|
||||
case (SeqRes, children) =>
|
||||
Eval.later(
|
||||
Cofree(
|
||||
SeqTag,
|
||||
Eval.now(children.flatMap {
|
||||
case Cofree(SeqTag, ch) => ch.value
|
||||
case cf => Chain.one(cf)
|
||||
})
|
||||
)
|
||||
children.uncons
|
||||
.filter(_._2.isEmpty)
|
||||
.map(_._1)
|
||||
.getOrElse(
|
||||
Cofree(
|
||||
SeqRes,
|
||||
Eval.now(children.flatMap {
|
||||
case Cofree(SeqRes, 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)(transformWalker)
|
||||
.getOrElse(op)
|
||||
|
||||
@tailrec
|
||||
private def transformWalker(c: Cursor): List[Tree] =
|
||||
c match {
|
||||
case Cursor(_, `head`(parent: MetaTag) /: _) if !parent.skipTopology =>
|
||||
transformWalker(c.mapParent(p => p.copy(parent.op, p.tail)))
|
||||
|
||||
case Cursor(
|
||||
`current`(cf),
|
||||
loc @ `head`(parent: GroupTag) /: _
|
||||
) =>
|
||||
// Set the service call IDs
|
||||
val cfu = cf.copy(setServiceCallPeerId(cf.head, loc))
|
||||
|
||||
// We need to get there, finally
|
||||
val currentPeerId = Chain.fromOption(loc.lastOn.map(_.peerId))
|
||||
|
||||
debug("Going to handle: " + cf.head)
|
||||
|
||||
val fromPrevToCurrentPath = fromPrevToCurrent(c, currentPeerId)
|
||||
if (fromPrevToCurrentPath.nonEmpty) debug("BEFORE = " + fromPrevToCurrentPath)
|
||||
|
||||
val fromCurrentToNextPath = fromCurrentToNext(parent, c, currentPeerId)
|
||||
if (fromCurrentToNextPath.nonEmpty) debug("NEXT = " + fromCurrentToNextPath)
|
||||
|
||||
(through(fromPrevToCurrentPath)
|
||||
.append(cfu) ++ through(fromCurrentToNextPath, reversed = true)).toList
|
||||
|
||||
case Cursor(ChainZipper(_, cf, _), loc) =>
|
||||
cf.copy(setServiceCallPeerId(cf.head, loc)) :: Nil
|
||||
}
|
||||
|
||||
def fromPrevToCurrent(c: Cursor, currentPeerId: Chain[ValueModel]): Chain[ValueModel] = {
|
||||
val prevOn = c.prevOnTags
|
||||
val currentOn = c.loc.pathOn
|
||||
|
||||
val wasHandled = c.pathToRoot.collectFirst { case cc @ Cursor(_, `head`(_: GroupTag) /: _) =>
|
||||
cc.loc.pathOn
|
||||
}.exists(cclp =>
|
||||
cclp == currentOn && {
|
||||
val (c1, _) = skipCommonPrefix(prevOn, cclp)
|
||||
c1.isEmpty
|
||||
}
|
||||
def wrap(cz: ChainZipper[Res]): Chain[Res] =
|
||||
Chain.one(
|
||||
if (cz.prev.nonEmpty || cz.next.nonEmpty) Cofree(SeqRes, Eval.now(cz.chain))
|
||||
else cz.current
|
||||
)
|
||||
|
||||
// Need to get from there
|
||||
val prevPeerId =
|
||||
Chain.fromOption(prevOn.lastOption.map(_.peerId) orElse c.loc.firstOn.map(_.peerId))
|
||||
def resolveOnMoves(op: Tree): Eval[Res] =
|
||||
RawCursor(NonEmptyList.one(ChainZipper.one(op)))
|
||||
.cata(wrap) { rc =>
|
||||
debug(s"<:> $rc")
|
||||
OptionT[Eval, ChainZipper[Res]](
|
||||
({
|
||||
case SeqTag => SeqRes
|
||||
case _: OnTag => SeqRes
|
||||
case MatchMismatchTag(a, b, s) => MatchMismatchRes(a, b, s)
|
||||
case ForTag(item, iter) => FoldRes(item, iter)
|
||||
case ParTag => ParRes
|
||||
case XorTag | XorTag.LeftBiased => XorRes
|
||||
case NextTag(item) => NextRes(item)
|
||||
case CallServiceTag(serviceId, funcName, call) =>
|
||||
CallServiceRes(
|
||||
serviceId,
|
||||
funcName,
|
||||
call,
|
||||
rc.currentPeerId
|
||||
.getOrElse(LiteralModel.initPeerId)
|
||||
)
|
||||
}: PartialFunction[RawTag, ResolvedOp]).lift
|
||||
.apply(rc.tag)
|
||||
.map(MakeRes.leaf)
|
||||
.traverse(c =>
|
||||
Eval.later {
|
||||
val cz = ChainZipper(
|
||||
through(rc.pathFromPrev),
|
||||
c,
|
||||
through(rc.pathToNext)
|
||||
)
|
||||
if (cz.next.nonEmpty || cz.prev.nonEmpty) {
|
||||
debug(s"Resolved $rc -> $c")
|
||||
if (cz.prev.nonEmpty)
|
||||
trace("From prev: " + cz.prev.map(_.head).toList.mkString(" -> "))
|
||||
if (cz.next.nonEmpty)
|
||||
trace("To next: " + cz.next.map(_.head).toList.mkString(" -> "))
|
||||
} else debug(s"EMPTY $rc -> $c")
|
||||
cz
|
||||
}
|
||||
)
|
||||
)
|
||||
|
||||
if (wasHandled) Chain.empty[ValueModel]
|
||||
else
|
||||
findPath(prevOn, currentOn, prevPeerId, currentPeerId)
|
||||
}
|
||||
|
||||
def fromCurrentToNext(
|
||||
parent: OpTag,
|
||||
c: Cursor,
|
||||
currentPeerId: Chain[ValueModel]
|
||||
): Chain[ValueModel] = {
|
||||
// Usually we don't need to go next
|
||||
val nextOn = parent match {
|
||||
case ParTag =>
|
||||
val exports = FuncOp(c.point.current).exportsVarNames.value
|
||||
if (exports.isEmpty) Chain.empty[OnTag]
|
||||
else {
|
||||
val isUsed = c.pathToRoot.tail.collect {
|
||||
case Cursor(cz, `head`(gt: GroupTag) /: _) if gt != ParTag =>
|
||||
cz.next.map(FuncOp(_)).map(_.usesVarNames)
|
||||
}.exists(_.exists(_.value.intersect(exports).nonEmpty))
|
||||
if (isUsed) c.nextOnTags else Chain.empty[OnTag]
|
||||
}
|
||||
case XorTag if c.point.prev.nonEmpty => c.nextOnTags
|
||||
case _ => Chain.empty[OnTag]
|
||||
}
|
||||
val nextPeerId =
|
||||
if (nextOn.nonEmpty) Chain.fromOption(nextOn.lastOption.map(_.peerId)) else currentPeerId
|
||||
|
||||
val targetOn: Option[OnTag] = c.point.current.head match {
|
||||
case o: OnTag => Option(o)
|
||||
case _ => None
|
||||
}
|
||||
val currentOn = c.loc.pathOn
|
||||
val currentOnInside = targetOn.fold(currentOn)(currentOn :+ _)
|
||||
findPath(
|
||||
currentOnInside,
|
||||
nextOn,
|
||||
currentPeerId,
|
||||
nextPeerId
|
||||
)
|
||||
}
|
||||
|
||||
def optimizePath(
|
||||
peerIds: Chain[ValueModel],
|
||||
prefix: Chain[ValueModel],
|
||||
suffix: Chain[ValueModel]
|
||||
): Chain[ValueModel] = {
|
||||
val optimized = 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
|
||||
}
|
||||
val noPrefix = skipPrefix(optimized, prefix, optimized)
|
||||
skipSuffix(noPrefix, suffix, noPrefix)
|
||||
}
|
||||
|
||||
def findPath(
|
||||
fromOn: Chain[OnTag],
|
||||
toOn: Chain[OnTag],
|
||||
fromPeer: Chain[ValueModel],
|
||||
toPeer: Chain[ValueModel]
|
||||
): Chain[ValueModel] = {
|
||||
val (from, to) = skipCommonPrefix(fromOn, toOn)
|
||||
val fromFix =
|
||||
if (from.isEmpty && fromPeer != toPeer) Chain.fromOption(fromOn.lastOption) else from
|
||||
val toFix = if (to.isEmpty && fromPeer != toPeer) Chain.fromOption(toOn.lastOption) else to
|
||||
val fromTo = fromFix.reverse.flatMap(_.via.reverse) ++ toFix.flatMap(_.via)
|
||||
val optimized = optimizePath(fromPeer ++ fromTo ++ toPeer, fromPeer, toPeer)
|
||||
|
||||
debug("FIND PATH " + fromFix)
|
||||
debug(" -> " + toFix)
|
||||
debug(" Optimized: " + optimized)
|
||||
optimized
|
||||
}
|
||||
|
||||
@tailrec
|
||||
def skipPrefix[T](chain: Chain[T], prefix: Chain[T], init: Chain[T]): Chain[T] =
|
||||
(chain, prefix) match {
|
||||
case (c ==: ctail, p ==: ptail) if c == p => skipPrefix(ctail, ptail, init)
|
||||
case (_, `nil`) => chain
|
||||
case (_, _) => init
|
||||
}
|
||||
|
||||
@tailrec
|
||||
def skipCommonPrefix[T](chain1: Chain[T], chain2: Chain[T]): (Chain[T], Chain[T]) =
|
||||
(chain1, chain2) match {
|
||||
case (c ==: ctail, p ==: ptail) if c == p => skipCommonPrefix(ctail, ptail)
|
||||
case _ => chain1 -> chain2
|
||||
}
|
||||
|
||||
@tailrec
|
||||
def skipSuffix[T](chain: Chain[T], suffix: Chain[T], init: Chain[T]): Chain[T] =
|
||||
(chain, suffix) match {
|
||||
case (cinit :== c, pinit :== p) if c == p => skipSuffix(cinit, pinit, init)
|
||||
case (_, `nil`) => chain
|
||||
case (_, _) => init
|
||||
}
|
||||
.map(NonEmptyChain.fromChain(_).map(_.uncons))
|
||||
.map {
|
||||
case None =>
|
||||
error("Topology emitted nothing")
|
||||
Cofree(SeqRes, MakeRes.nilTail)
|
||||
case Some((el, `nil`)) => el
|
||||
case Some((el, tail)) =>
|
||||
warn("Topology emitted many nodes, that's unusual")
|
||||
Cofree(SeqRes, Eval.now(el +: tail))
|
||||
}
|
||||
|
||||
// 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], reversed: Boolean = false): Chain[Tree] =
|
||||
def through(peerIds: Chain[ValueModel], reversed: Boolean = false): Chain[Res] =
|
||||
peerIds.map { v =>
|
||||
v.lastType match {
|
||||
case _: BoxType =>
|
||||
val itemName = "-via-peer-"
|
||||
|
||||
FuncOps.meta(
|
||||
FuncOps.fold(
|
||||
itemName,
|
||||
v,
|
||||
if (reversed)
|
||||
FuncOps.seq(
|
||||
FuncOps.next(itemName),
|
||||
FuncOps.noop(VarModel(itemName, ScalarType.string))
|
||||
)
|
||||
else
|
||||
FuncOps.seq(
|
||||
FuncOps.noop(VarModel(itemName, ScalarType.string)),
|
||||
FuncOps.next(itemName)
|
||||
)
|
||||
),
|
||||
skipTopology = true
|
||||
MakeRes.fold(
|
||||
itemName,
|
||||
v,
|
||||
if (reversed)
|
||||
MakeRes.seq(
|
||||
MakeRes.next(itemName),
|
||||
MakeRes.noop(VarModel(itemName, ScalarType.string))
|
||||
)
|
||||
else
|
||||
MakeRes.seq(
|
||||
MakeRes.noop(VarModel(itemName, ScalarType.string)),
|
||||
MakeRes.next(itemName)
|
||||
)
|
||||
)
|
||||
case _ =>
|
||||
FuncOps.noop(v)
|
||||
MakeRes.noop(v)
|
||||
}
|
||||
}
|
||||
.map(_.tree)
|
||||
|
||||
def setServiceCallPeerId(tag: OpTag, loc: Location): OpTag = tag match {
|
||||
case c: CallServiceTag if c.peerId.isEmpty =>
|
||||
c.copy(peerId = loc.lastOn.map(_.peerId))
|
||||
case t => t
|
||||
}
|
||||
}
|
||||
|
@ -2,8 +2,8 @@ package aqua.model.transform
|
||||
|
||||
import aqua.model.{ValueModel, VarModel}
|
||||
import aqua.model.func.Call
|
||||
import aqua.model.func.body.{FuncOp, FuncOps}
|
||||
import aqua.types.{ArrayType, DataType, OptionType, StreamType, Type}
|
||||
import aqua.model.func.raw.{FuncOp, FuncOps}
|
||||
import aqua.types.{ArrayType, DataType, StreamType}
|
||||
import cats.data.Chain
|
||||
|
||||
trait ArgsProvider {
|
||||
@ -26,7 +26,6 @@ case class ArgsFromService(dataServiceId: ValueModel, names: List[(String, DataT
|
||||
item,
|
||||
VarModel(iter, ArrayType(t.element), Chain.empty),
|
||||
FuncOps.seq(
|
||||
// TODO: currently this does not work, as identity wraps everything with an array
|
||||
FuncOps.identity(VarModel(item, t.element), Call.Export(name, t)),
|
||||
FuncOps.next(item)
|
||||
)
|
||||
|
@ -2,7 +2,7 @@ package aqua.model.transform
|
||||
|
||||
import aqua.model.{LiteralModel, ValueModel, VarModel}
|
||||
import aqua.model.func.Call
|
||||
import aqua.model.func.body.{FuncOp, FuncOps, MatchMismatchTag, OnTag, OpTag, XorTag}
|
||||
import aqua.model.func.raw.{FuncOp, FuncOps, MatchMismatchTag, OnTag, RawTag, XorTag}
|
||||
import aqua.types.LiteralType
|
||||
import cats.Eval
|
||||
import cats.data.Chain
|
||||
@ -19,7 +19,7 @@ case class ErrorsCatcher(
|
||||
if (enabled) {
|
||||
var i = 0
|
||||
op
|
||||
.cata[Cofree[Chain, OpTag]] {
|
||||
.cata[Cofree[Chain, RawTag]] {
|
||||
case (tag, children)
|
||||
if children.length == 1 && children.headOption.exists(
|
||||
_.head == XorTag.LeftBiased
|
||||
|
@ -2,7 +2,7 @@ package aqua.model.transform
|
||||
|
||||
import aqua.model.{LiteralModel, ValueModel}
|
||||
import aqua.model.func.Call
|
||||
import aqua.model.func.body.{FuncOp, FuncOps}
|
||||
import aqua.model.func.raw.{FuncOp, FuncOps}
|
||||
import cats.data.Chain
|
||||
|
||||
sealed trait InitPeerCallable {
|
||||
|
@ -2,7 +2,7 @@ package aqua.model.transform
|
||||
|
||||
import aqua.model.{ValueModel, VarModel}
|
||||
import aqua.model.func.{ArgDef, ArgsCall, ArgsDef, Call, FuncCallable}
|
||||
import aqua.model.func.body.{FuncOp, FuncOps}
|
||||
import aqua.model.func.raw.{FuncOp, FuncOps}
|
||||
import aqua.types.ArrowType
|
||||
import cats.Eval
|
||||
import cats.syntax.apply._
|
||||
|
@ -1,27 +1,28 @@
|
||||
package aqua.model.transform
|
||||
|
||||
import aqua.model.func.body._
|
||||
import aqua.model.func.FuncCallable
|
||||
import aqua.model.VarModel
|
||||
import aqua.model.func.resolved.{NoAir, ResolvedOp}
|
||||
import aqua.model.topology.Topology
|
||||
import aqua.types.ScalarType
|
||||
import cats.data.Chain
|
||||
import cats.free.Cofree
|
||||
import wvlet.log.LogSupport
|
||||
|
||||
object Transform {
|
||||
object Transform extends LogSupport {
|
||||
|
||||
def defaultFilter(t: OpTag): Boolean = t match {
|
||||
case _: NoAirTag => false
|
||||
def defaultFilter(t: ResolvedOp): Boolean = t match {
|
||||
case _: NoAir => false
|
||||
case _ => true
|
||||
}
|
||||
|
||||
def clear(
|
||||
tree: Cofree[Chain, OpTag],
|
||||
filter: OpTag => Boolean = defaultFilter
|
||||
): Cofree[Chain, OpTag] =
|
||||
tree: Cofree[Chain, ResolvedOp],
|
||||
filter: ResolvedOp => Boolean = defaultFilter
|
||||
): Cofree[Chain, ResolvedOp] =
|
||||
tree.copy(tail = tree.tail.map(_.filter(t => filter(t.head)).map(clear(_, filter))))
|
||||
|
||||
def forClient(func: FuncCallable, conf: BodyConfig): Cofree[Chain, OpTag] = {
|
||||
def forClient(func: FuncCallable, conf: BodyConfig): Cofree[Chain, ResolvedOp] = {
|
||||
val initCallable: InitPeerCallable = InitViaRelayCallable(
|
||||
Chain.fromOption(conf.relayVarName).map(VarModel(_, ScalarType.string))
|
||||
)
|
||||
|
@ -130,8 +130,8 @@ object Expr {
|
||||
case block if block.isBlock =>
|
||||
acc.copy(block = Some(indent -> currentExpr))
|
||||
// create leaf if token is on current level
|
||||
case e =>
|
||||
acc.copy(currentChildren = acc.currentChildren.append(leaf(e)))
|
||||
case _ =>
|
||||
acc.copy(currentChildren = acc.currentChildren.append(currentExpr))
|
||||
}
|
||||
// if we have root companion, gather all expressions that have indent > than current
|
||||
case r @ Some((_, block)) =>
|
||||
@ -163,10 +163,10 @@ object Expr {
|
||||
window = Chain.empty
|
||||
)
|
||||
// create leaf if token is on current level
|
||||
case e =>
|
||||
case _ =>
|
||||
acc.copy(
|
||||
block = None,
|
||||
currentChildren = withTree.append(leaf(e)),
|
||||
currentChildren = withTree.append(currentExpr),
|
||||
window = Chain.empty
|
||||
)
|
||||
}
|
||||
|
@ -267,4 +267,14 @@ class FuncExprSpec extends AnyFlatSpec with Matchers with AquaSpec {
|
||||
qGenComplex.d() shouldBe elseCall
|
||||
qGenComplex.d() shouldBe CallServiceTag(local, "gt", Call(List(), Some("three")), None)*/
|
||||
}
|
||||
|
||||
"function with par" should "be parsed correctly" in {
|
||||
val script =
|
||||
"""func topologyTest():
|
||||
| on "friend":
|
||||
| str2 <- LocalPrint.print("in on")
|
||||
| par LocalPrint.print("in par")""".stripMargin
|
||||
|
||||
val tree = parser[Id]().parseAll(script).value.toEither.value
|
||||
}
|
||||
}
|
||||
|
@ -1,7 +1,7 @@
|
||||
package aqua.semantics
|
||||
|
||||
import aqua.model.{AquaContext, Model, ScriptModel}
|
||||
import aqua.model.func.body.FuncOp
|
||||
import aqua.model.func.raw.FuncOp
|
||||
import aqua.parser.lexer.Token
|
||||
import aqua.parser.{Ast, Expr}
|
||||
import aqua.semantics.rules.ReportError
|
||||
@ -23,8 +23,9 @@ import monocle.Lens
|
||||
import monocle.macros.GenLens
|
||||
import cats.syntax.apply._
|
||||
import cats.syntax.semigroup._
|
||||
import wvlet.log.LogSupport
|
||||
|
||||
object Semantics {
|
||||
object Semantics extends LogSupport {
|
||||
|
||||
def folder[F[_], G[_]](implicit
|
||||
A: AbilitiesAlgebra[F, G],
|
||||
@ -91,7 +92,7 @@ object Semantics {
|
||||
.run(CompilerState.init[F](init))
|
||||
.map {
|
||||
case (state, gen: ScriptModel) =>
|
||||
val ctx = AquaContext.fromScriptModel(gen, aqum.empty)
|
||||
val ctx = AquaContext.fromScriptModel(gen, init)
|
||||
NonEmptyChain
|
||||
.fromChain(state.errors)
|
||||
.fold[ValidatedNec[SemanticError[F], AquaContext]](Valid(ctx))(Invalid(_))
|
||||
|
@ -1,7 +1,7 @@
|
||||
package aqua.semantics.expr
|
||||
|
||||
import aqua.model.Model
|
||||
import aqua.model.func.body.{AbilityIdTag, FuncOp}
|
||||
import aqua.model.func.raw.{AbilityIdTag, FuncOp}
|
||||
import aqua.parser.expr.AbilityIdExpr
|
||||
import aqua.semantics.Prog
|
||||
import aqua.semantics.rules.ValuesAlgebra
|
||||
|
@ -1,7 +1,7 @@
|
||||
package aqua.semantics.expr
|
||||
|
||||
import aqua.model.Model
|
||||
import aqua.model.func.body.{AssignmentTag, FuncOp, FuncOps}
|
||||
import aqua.model.func.raw.{AssignmentTag, FuncOp, FuncOps}
|
||||
import aqua.parser.expr.{AbilityIdExpr, AssignmentExpr}
|
||||
import aqua.semantics.Prog
|
||||
import aqua.semantics.rules.ValuesAlgebra
|
||||
|
@ -1,7 +1,7 @@
|
||||
package aqua.semantics.expr
|
||||
|
||||
import aqua.model.func.Call
|
||||
import aqua.model.func.body.{CallArrowTag, CallServiceTag, FuncOp}
|
||||
import aqua.model.func.raw.{CallArrowTag, CallServiceTag, FuncOp}
|
||||
import aqua.model.{Model, ValueModel}
|
||||
import aqua.parser.expr.CallArrowExpr
|
||||
import aqua.semantics.Prog
|
||||
|
@ -1,6 +1,6 @@
|
||||
package aqua.semantics.expr
|
||||
|
||||
import aqua.model.func.body.{AssignmentTag, FuncOp, FuncOps, XorTag}
|
||||
import aqua.model.func.raw.{AssignmentTag, FuncOp, FuncOps, XorTag}
|
||||
import aqua.model.{Model, VarModel}
|
||||
import aqua.parser.expr.CatchExpr
|
||||
import aqua.semantics.Prog
|
||||
|
@ -1,7 +1,7 @@
|
||||
package aqua.semantics.expr
|
||||
|
||||
import aqua.model.Model
|
||||
import aqua.model.func.body.{FuncOp, XorTag}
|
||||
import aqua.model.func.raw.{FuncOp, XorTag}
|
||||
import aqua.parser.expr.ElseOtherwiseExpr
|
||||
import aqua.semantics.Prog
|
||||
import aqua.semantics.rules.abilities.AbilitiesAlgebra
|
||||
|
@ -1,7 +1,7 @@
|
||||
package aqua.semantics.expr
|
||||
|
||||
import aqua.model.{Model, ValueModel}
|
||||
import aqua.model.func.body._
|
||||
import aqua.model.func.raw._
|
||||
import aqua.parser.expr.ForExpr
|
||||
import aqua.semantics.Prog
|
||||
import aqua.semantics.rules.ValuesAlgebra
|
||||
@ -41,7 +41,7 @@ class ForSem[F[_]](val expr: ForExpr[F]) extends AnyVal {
|
||||
FuncOp.wrap(
|
||||
ForTag(expr.item.value, vm),
|
||||
FuncOp.node(
|
||||
expr.mode.map(_._2).fold[OpTag](SeqTag) {
|
||||
expr.mode.map(_._2).fold[RawTag](SeqTag) {
|
||||
case ForExpr.ParMode => ParTag
|
||||
case ForExpr.TryMode => XorTag
|
||||
},
|
||||
|
@ -1,6 +1,6 @@
|
||||
package aqua.semantics.expr
|
||||
|
||||
import aqua.model.func.body.FuncOp
|
||||
import aqua.model.func.raw.FuncOp
|
||||
import aqua.model.{Model, ValueModel}
|
||||
import aqua.model.func.{ArgDef, ArgsDef, FuncModel}
|
||||
import aqua.parser.expr.FuncExpr
|
||||
|
@ -1,7 +1,7 @@
|
||||
package aqua.semantics.expr
|
||||
|
||||
import aqua.model.{Model, ValueModel}
|
||||
import aqua.model.func.body.{FuncOp, MatchMismatchTag, XorTag}
|
||||
import aqua.model.func.raw.{FuncOp, MatchMismatchTag, XorTag}
|
||||
import aqua.parser.expr.IfExpr
|
||||
import aqua.semantics.rules.ValuesAlgebra
|
||||
import aqua.semantics.rules.types.TypesAlgebra
|
||||
|
@ -1,7 +1,7 @@
|
||||
package aqua.semantics.expr
|
||||
|
||||
import aqua.model.{Model, ValueModel}
|
||||
import aqua.model.func.body.{FuncOp, OnTag}
|
||||
import aqua.model.func.raw.{FuncOp, OnTag}
|
||||
import aqua.parser.expr.OnExpr
|
||||
import aqua.semantics.Prog
|
||||
import aqua.semantics.rules.ValuesAlgebra
|
||||
|
@ -1,7 +1,7 @@
|
||||
package aqua.semantics.expr
|
||||
|
||||
import aqua.model.Model
|
||||
import aqua.model.func.body.{FuncOp, ParTag}
|
||||
import aqua.model.func.raw.{FuncOp, ParTag}
|
||||
import aqua.parser.expr.ParExpr
|
||||
import aqua.semantics.Prog
|
||||
import cats.free.Free
|
||||
|
@ -1,7 +1,7 @@
|
||||
package aqua.semantics.expr
|
||||
|
||||
import aqua.model.Model
|
||||
import aqua.model.func.body.{FuncOp, XorTag}
|
||||
import aqua.model.func.raw.{FuncOp, XorTag}
|
||||
import aqua.parser.expr.TryExpr
|
||||
import aqua.semantics.Prog
|
||||
import aqua.semantics.rules.ValuesAlgebra
|
||||
|
@ -2,6 +2,7 @@ package aqua.semantics
|
||||
|
||||
import aqua.Node
|
||||
import aqua.Node._
|
||||
import aqua.model.func.raw.{FuncOp, FuncOps, SeqTag}
|
||||
import aqua.model.transform._
|
||||
import aqua.model.{AquaContext, LiteralModel}
|
||||
import aqua.parser.Ast
|
||||
@ -13,7 +14,7 @@ import org.scalatest.matchers.should.Matchers
|
||||
class SemanticsSpec extends AnyFlatSpec with Matchers {
|
||||
|
||||
// use it to fix https://github.com/fluencelabs/aqua/issues/90
|
||||
ignore should "create right model" in {
|
||||
"sem" should "create right model" in {
|
||||
implicit val fileLift: LiftParser[Span.F] = Span.spanLiftParser
|
||||
|
||||
val script =
|
||||
@ -37,15 +38,16 @@ class SemanticsSpec extends AnyFlatSpec with Matchers {
|
||||
|
||||
val proc = Node.cofToNode(func.body.tree)
|
||||
|
||||
val expected =
|
||||
seq(
|
||||
par(
|
||||
on(LiteralModel("\"other-peer\"", LiteralType.string), Nil, callLiteral(1)),
|
||||
callLiteral(1)
|
||||
val expected: Node.Raw =
|
||||
FuncOp.wrap(
|
||||
SeqTag,
|
||||
FuncOps.par(
|
||||
on(LiteralModel("\"other-peer\"", LiteralType.string), Nil, callLiteralRaw(1)),
|
||||
callLiteralRaw(1)
|
||||
)
|
||||
)
|
||||
|
||||
// proc.equalsOrPrintDiff(expected) should be(true)
|
||||
proc.equalsOrPrintDiff(expected) should be(true)
|
||||
|
||||
}
|
||||
}
|
||||
|
@ -1,7 +1,8 @@
|
||||
package aqua
|
||||
|
||||
import aqua.model.func.Call
|
||||
import aqua.model.func.body._
|
||||
import aqua.model.func.raw._
|
||||
import aqua.model.func.resolved.{CallServiceRes, MakeRes, MatchMismatchRes, ResolvedOp}
|
||||
import aqua.model.transform.{BodyConfig, ErrorsCatcher}
|
||||
import aqua.model.{LiteralModel, ValueModel, VarModel}
|
||||
import aqua.types.{ArrayType, LiteralType, ScalarType}
|
||||
@ -12,15 +13,143 @@ import cats.free.Cofree
|
||||
import scala.language.implicitConversions
|
||||
|
||||
// Helper to simplify building and visualizing Cofree structures
|
||||
case class Node(tag: OpTag, ops: List[Node] = Nil) {
|
||||
case class Node[+T](label: T, children: List[Node[T]] = Nil) {
|
||||
|
||||
def cof[TT >: T]: Cofree[Chain, TT] = Node.nodeToCof(this)
|
||||
|
||||
override def toString: String =
|
||||
tag.toString + (if (ops.isEmpty) "\n" else s"{\n${ops.mkString}\n}\n")
|
||||
label.toString + (if (children.isEmpty) "\n" else s"{\n${children.mkString}\n}\n")
|
||||
|
||||
private def equalOrNot[T](left: T, right: T): String = (if (left == right)
|
||||
Console.GREEN + left + Console.RESET
|
||||
else
|
||||
Console.BLUE + left + Console.RED + " != " + Console.YELLOW + right)
|
||||
def equalsOrPrintDiff[TT](other: Node[TT]): Boolean =
|
||||
if (this == other) true
|
||||
else {
|
||||
println(Console.CYAN + "Given: " + this)
|
||||
println(Console.YELLOW + "Other: " + other + Console.RESET)
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
object Node {
|
||||
type Res = Node[ResolvedOp]
|
||||
type Raw = Node[RawTag]
|
||||
|
||||
implicit def cofToNode[T](cof: Cofree[Chain, T]): Node[T] =
|
||||
Node[T](cof.head, cof.tailForced.toList.map(cofToNode[T]))
|
||||
|
||||
implicit def nodeToCof[T](tree: Node[T]): Cofree[Chain, T] =
|
||||
Cofree(tree.label, Eval.later(Chain.fromSeq(tree.children.map(nodeToCof))))
|
||||
|
||||
implicit def rawToFuncOp(tree: Raw): FuncOp =
|
||||
FuncOp(tree.cof)
|
||||
|
||||
implicit def funcOpToRaw(op: FuncOp): Raw =
|
||||
op.tree
|
||||
|
||||
val relay = LiteralModel("-relay-", ScalarType.string)
|
||||
val relayV = VarModel("-relay-", ScalarType.string)
|
||||
val initPeer = LiteralModel.initPeerId
|
||||
val emptyCall = Call(Nil, None)
|
||||
val otherPeer = LiteralModel("other-peer", ScalarType.string)
|
||||
val otherPeerL = LiteralModel("\"other-peer\"", LiteralType.string)
|
||||
val otherRelay = LiteralModel("other-relay", ScalarType.string)
|
||||
val otherPeer2 = LiteralModel("other-peer-2", ScalarType.string)
|
||||
val otherRelay2 = LiteralModel("other-relay-2", ScalarType.string)
|
||||
val varNode = VarModel("node-id", ScalarType.string)
|
||||
val viaList = VarModel("other-relay-2", ArrayType(ScalarType.string))
|
||||
|
||||
def callRes(
|
||||
i: Int,
|
||||
on: ValueModel,
|
||||
exportTo: Option[Call.Export] = None,
|
||||
args: List[ValueModel] = Nil
|
||||
): Res = Node(
|
||||
CallServiceRes(LiteralModel(s"srv$i", ScalarType.string), s"fn$i", Call(args, exportTo), on)
|
||||
)
|
||||
|
||||
def callTag(i: Int, exportTo: Option[Call.Export] = None, args: List[ValueModel] = Nil): Raw =
|
||||
Node(
|
||||
CallServiceTag(LiteralModel(s"srv$i", ScalarType.string), s"fn$i", Call(args, exportTo))
|
||||
)
|
||||
|
||||
def callLiteralRes(i: Int, on: ValueModel, exportTo: Option[Call.Export] = None): Res = Node(
|
||||
CallServiceRes(
|
||||
LiteralModel("\"srv" + i + "\"", LiteralType.string),
|
||||
s"fn$i",
|
||||
Call(Nil, exportTo),
|
||||
on
|
||||
)
|
||||
)
|
||||
|
||||
def callLiteralRaw(i: Int, exportTo: Option[Call.Export] = None): Raw = Node(
|
||||
CallServiceTag(
|
||||
LiteralModel("\"srv" + i + "\"", LiteralType.string),
|
||||
s"fn$i",
|
||||
Call(Nil, exportTo)
|
||||
)
|
||||
)
|
||||
|
||||
def errorCall(bc: BodyConfig, i: Int, on: ValueModel = initPeer): Res = Node[ResolvedOp](
|
||||
CallServiceRes(
|
||||
bc.errorHandlingCallback,
|
||||
bc.errorFuncName,
|
||||
Call(
|
||||
ErrorsCatcher.lastErrorArg :: LiteralModel(
|
||||
i.toString,
|
||||
LiteralType.number
|
||||
) :: Nil,
|
||||
None
|
||||
),
|
||||
on
|
||||
)
|
||||
)
|
||||
|
||||
def respCall(bc: BodyConfig, value: ValueModel, on: ValueModel = initPeer): Res =
|
||||
Node[ResolvedOp](
|
||||
CallServiceRes(
|
||||
bc.callbackSrvId,
|
||||
bc.respFuncName,
|
||||
Call(value :: Nil, None),
|
||||
on
|
||||
)
|
||||
)
|
||||
|
||||
def dataCall(bc: BodyConfig, name: String, on: ValueModel = initPeer): Res = Node[ResolvedOp](
|
||||
CallServiceRes(
|
||||
bc.dataSrvId,
|
||||
name,
|
||||
Call(Nil, Some(Call.Export(name, ScalarType.string))),
|
||||
on
|
||||
)
|
||||
)
|
||||
|
||||
def on(peer: ValueModel, via: List[ValueModel], body: Raw*) =
|
||||
Node(
|
||||
OnTag(peer, Chain.fromSeq(via)),
|
||||
body.toList
|
||||
)
|
||||
|
||||
def `try`(body: Raw*) =
|
||||
Node(XorTag.LeftBiased, body.toList)
|
||||
|
||||
def matchRes(l: ValueModel, r: ValueModel, body: Res): Res =
|
||||
Node(
|
||||
MatchMismatchRes(l, r, shouldMatch = true),
|
||||
body :: Nil
|
||||
)
|
||||
|
||||
def matchRaw(l: ValueModel, r: ValueModel, body: Raw): Raw =
|
||||
Node(
|
||||
MatchMismatchTag(l, r, shouldMatch = true),
|
||||
body :: Nil
|
||||
)
|
||||
|
||||
def through(peer: ValueModel): Node[ResolvedOp] =
|
||||
cofToNode(MakeRes.noop(peer))
|
||||
|
||||
private def equalOrNot[TT](left: TT, right: TT): String = (if (left == right)
|
||||
Console.GREEN + left + Console.RESET
|
||||
else
|
||||
Console.BLUE + left + Console.RED + " != " + Console.YELLOW + right)
|
||||
|
||||
private def diffArg(left: ValueModel, right: ValueModel): String =
|
||||
Console.GREEN + "(" +
|
||||
@ -36,129 +165,53 @@ case class Node(tag: OpTag, ops: List[Node] = Nil) {
|
||||
.mkString("::") + Console.GREEN + ", " +
|
||||
equalOrNot(left.exportTo, right.exportTo) + Console.GREEN + ")"
|
||||
|
||||
private def diffServiceCall(left: CallServiceTag, right: CallServiceTag): String =
|
||||
Console.GREEN + "CallServiceTag(" +
|
||||
equalOrNot(left.serviceId, right.serviceId) + Console.GREEN + ", " +
|
||||
equalOrNot(left.funcName, right.funcName) + Console.GREEN + ", " +
|
||||
diffCall(left.call, right.call) + Console.GREEN + ", " +
|
||||
equalOrNot(left.peerId, right.peerId) +
|
||||
private def diffServiceCall(left: CallServiceRes, right: CallServiceRes): String =
|
||||
Console.GREEN + "(call" +
|
||||
equalOrNot(left.peerId, right.peerId) + Console.GREEN + " (" +
|
||||
equalOrNot(left.serviceId, right.serviceId) + Console.GREEN + " " +
|
||||
equalOrNot(left.funcName, right.funcName) + Console.GREEN + ") " +
|
||||
diffCall(left.call, right.call) + Console.GREEN +
|
||||
Console.GREEN + ")" + Console.RESET
|
||||
|
||||
private def diffTags(left: OpTag, right: OpTag): String = (left, right) match {
|
||||
case (l: CallServiceTag, r: CallServiceTag) => diffServiceCall(l, r)
|
||||
private def diffRes(left: ResolvedOp, right: ResolvedOp): String = (left, right) match {
|
||||
case (l: CallServiceRes, r: CallServiceRes) => diffServiceCall(l, r)
|
||||
case _ =>
|
||||
Console.BLUE + s" $left ${Console.RED}\n != ${Console.YELLOW}${right}${Console.RED}"
|
||||
}
|
||||
|
||||
def diffToString(other: Node): String =
|
||||
(if (tag == other.tag) Console.GREEN + tag
|
||||
else diffTags(tag, other.tag)) + (if (ops.isEmpty && other.ops.isEmpty) "\n"
|
||||
else
|
||||
"{\n") + Console.RESET +
|
||||
(if (ops.length != other.ops.length)
|
||||
Console.RED + s"number of ops: ${ops.length} != ${other.ops.length}\n" + Console.RESET
|
||||
def diffToString(current: Node.Res, other: Node.Res): String =
|
||||
(if (current.label == other.label) Console.GREEN + current.label
|
||||
else
|
||||
diffRes(current.label, other.label)) + (if (
|
||||
current.children.isEmpty && other.children.isEmpty
|
||||
) "\n"
|
||||
else
|
||||
"{\n") + Console.RESET +
|
||||
(if (current.children.length != other.children.length)
|
||||
Console.RED + s"number of ops: ${current.children.length} != ${other.children.length}\n" + Console.RESET
|
||||
else "") +
|
||||
ops
|
||||
.zip(other.ops)
|
||||
.map { case (a, b) =>
|
||||
a.diffToString(b)
|
||||
current.children
|
||||
.map(Option(_))
|
||||
.zipAll(other.children.map(Option(_)), None, None)
|
||||
.map {
|
||||
case (Some(a), Some(b)) =>
|
||||
diffToString(a, b)
|
||||
case (Some(a), _) =>
|
||||
Console.BLUE + a + Console.RESET
|
||||
case (_, Some(b)) =>
|
||||
Console.YELLOW + b + Console.RESET
|
||||
case _ =>
|
||||
Console.RED + "???" + Console.RESET
|
||||
}
|
||||
.mkString + (if (ops.isEmpty && other.ops.isEmpty) ""
|
||||
.mkString + (if (current.children.isEmpty && other.children.isEmpty) ""
|
||||
else
|
||||
((if (tag == other.tag) Console.GREEN
|
||||
((if (current.label == other.label) Console.GREEN
|
||||
else Console.RED) + "}\n" + Console.RESET))
|
||||
|
||||
def equalsOrPrintDiff(other: Node): Boolean =
|
||||
if (this == other) true
|
||||
def equalsOrPrintDiff(current: Node.Res, other: Node.Res): Boolean =
|
||||
if (current == other) true
|
||||
else {
|
||||
println(diffToString(other))
|
||||
println(diffToString(current, other))
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
object Node {
|
||||
type Cof = Cofree[Chain, OpTag]
|
||||
|
||||
implicit def cofToNode(cof: Cof): Node =
|
||||
Node(cof.head, cof.tailForced.toList.map(cofToNode))
|
||||
|
||||
implicit def nodeToCof(tree: Node): Cof =
|
||||
Cofree(tree.tag, Eval.later(Chain.fromSeq(tree.ops.map(nodeToCof))))
|
||||
|
||||
val relay = LiteralModel("-relay-", ScalarType.string)
|
||||
val relayV = VarModel("-relay-", ScalarType.string)
|
||||
val initPeer = LiteralModel.initPeerId
|
||||
val emptyCall = Call(Nil, None)
|
||||
val otherPeer = LiteralModel("other-peer", ScalarType.string)
|
||||
val otherRelay = LiteralModel("other-relay", ScalarType.string)
|
||||
val otherPeer2 = LiteralModel("other-peer-2", ScalarType.string)
|
||||
val otherRelay2 = LiteralModel("other-relay-2", ScalarType.string)
|
||||
val varNode = VarModel("node-id", ScalarType.string)
|
||||
val viaList = VarModel("other-relay-2", ArrayType(ScalarType.string))
|
||||
|
||||
def call(i: Int, on: ValueModel = null) = Node(
|
||||
CallServiceTag(LiteralModel(s"srv$i", ScalarType.string), s"fn$i", Call(Nil, None), Option(on))
|
||||
)
|
||||
|
||||
def callLiteral(i: Int, on: ValueModel = null) = Node(
|
||||
CallServiceTag(
|
||||
LiteralModel("\"srv" + i + "\"", LiteralType.string),
|
||||
s"fn$i",
|
||||
Call(Nil, None),
|
||||
Option(on)
|
||||
)
|
||||
)
|
||||
|
||||
def errorCall(bc: BodyConfig, i: Int, on: ValueModel = null) = Node(
|
||||
CallServiceTag(
|
||||
bc.errorHandlingCallback,
|
||||
bc.errorFuncName,
|
||||
Call(
|
||||
ErrorsCatcher.lastErrorArg :: LiteralModel(
|
||||
i.toString,
|
||||
LiteralType.number
|
||||
) :: Nil,
|
||||
None
|
||||
),
|
||||
Option(on)
|
||||
)
|
||||
)
|
||||
|
||||
def respCall(bc: BodyConfig, value: ValueModel, on: ValueModel = null) = Node(
|
||||
CallServiceTag(
|
||||
bc.callbackSrvId,
|
||||
bc.respFuncName,
|
||||
Call(value :: Nil, None),
|
||||
Option(on)
|
||||
)
|
||||
)
|
||||
|
||||
def dataCall(bc: BodyConfig, name: String, on: ValueModel = null) = Node(
|
||||
CallServiceTag(
|
||||
bc.dataSrvId,
|
||||
name,
|
||||
Call(Nil, Some(Call.Export(name, ScalarType.string))),
|
||||
Option(on)
|
||||
)
|
||||
)
|
||||
|
||||
def seq(nodes: Node*) = Node(SeqTag, nodes.toList)
|
||||
def xor(left: Node, right: Node) = Node(XorTag, left :: right :: Nil)
|
||||
|
||||
def par(left: Node, right: Node) = Node(ParTag, left :: right :: Nil)
|
||||
|
||||
def on(peer: ValueModel, via: List[ValueModel], body: Node*) =
|
||||
Node(
|
||||
OnTag(peer, Chain.fromSeq(via)),
|
||||
body.toList
|
||||
)
|
||||
|
||||
def _match(l: ValueModel, r: ValueModel, body: Node) =
|
||||
Node(
|
||||
MatchMismatchTag(l, r, shouldMatch = true),
|
||||
body :: Nil
|
||||
)
|
||||
|
||||
def through(peer: ValueModel): Node =
|
||||
FuncOps.noop(peer).tree
|
||||
}
|
||||
|
@ -1,23 +0,0 @@
|
||||
package aqua.model.topology
|
||||
|
||||
import aqua.model.func.body.SeqTag
|
||||
import aqua.model.topology.ChainZipper.Matchers.`head`
|
||||
import aqua.model.topology.Location.Matchers._
|
||||
import cats.Eval
|
||||
import cats.data.Chain
|
||||
import cats.free.Cofree
|
||||
import org.scalatest.flatspec.AnyFlatSpec
|
||||
import org.scalatest.matchers.should.Matchers
|
||||
|
||||
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,6 +1,14 @@
|
||||
package aqua.model.topology
|
||||
|
||||
import aqua.Node
|
||||
import aqua.model.VarModel
|
||||
import aqua.model.func.Call
|
||||
import aqua.model.func.raw.FuncOps
|
||||
import aqua.model.func.resolved.{MakeRes, ResolvedOp, SeqRes, XorRes}
|
||||
import aqua.types.ScalarType
|
||||
import cats.Eval
|
||||
import cats.data.Chain
|
||||
import cats.free.Cofree
|
||||
import org.scalatest.flatspec.AnyFlatSpec
|
||||
import org.scalatest.matchers.should.Matchers
|
||||
|
||||
@ -9,21 +17,21 @@ class TopologySpec extends AnyFlatSpec with Matchers {
|
||||
|
||||
"topology resolver" should "do nothing on init peer" in {
|
||||
|
||||
val init = on(
|
||||
val init: Node.Raw = on(
|
||||
initPeer,
|
||||
relay :: Nil,
|
||||
seq(
|
||||
call(1),
|
||||
call(2)
|
||||
FuncOps.seq(
|
||||
callTag(1),
|
||||
callTag(2)
|
||||
)
|
||||
)
|
||||
|
||||
val proc: Node = Topology.resolve(init)
|
||||
val proc: Node.Res = Topology.resolve(init)
|
||||
|
||||
val expected =
|
||||
seq(
|
||||
call(1, initPeer),
|
||||
call(2, initPeer)
|
||||
val expected: Node.Res =
|
||||
MakeRes.seq(
|
||||
callRes(1, initPeer),
|
||||
callRes(2, initPeer)
|
||||
)
|
||||
|
||||
proc should be(expected)
|
||||
@ -32,23 +40,23 @@ class TopologySpec extends AnyFlatSpec with Matchers {
|
||||
|
||||
"topology resolver" should "go through relay to any other node, directly" in {
|
||||
|
||||
val init = on(
|
||||
val init: Node.Raw = on(
|
||||
initPeer,
|
||||
relay :: Nil,
|
||||
on(
|
||||
otherPeer,
|
||||
Nil,
|
||||
seq(
|
||||
call(1),
|
||||
call(2)
|
||||
FuncOps.seq(
|
||||
callTag(1),
|
||||
callTag(2)
|
||||
)
|
||||
)
|
||||
)
|
||||
|
||||
val proc: Node = Topology.resolve(init)
|
||||
val proc: Node.Res = Topology.resolve(init)
|
||||
|
||||
val expected =
|
||||
seq(through(relay), call(1, otherPeer), call(2, otherPeer))
|
||||
val expected: Node.Res =
|
||||
MakeRes.seq(through(relay), callRes(1, otherPeer), callRes(2, otherPeer))
|
||||
|
||||
proc should be(expected)
|
||||
}
|
||||
@ -61,21 +69,139 @@ class TopologySpec extends AnyFlatSpec with Matchers {
|
||||
on(
|
||||
otherPeer,
|
||||
otherRelay :: Nil,
|
||||
seq(
|
||||
call(1),
|
||||
call(2)
|
||||
FuncOps.seq(
|
||||
callTag(1),
|
||||
callTag(2)
|
||||
)
|
||||
)
|
||||
)
|
||||
|
||||
val proc: Node = Topology.resolve(init)
|
||||
val proc: Node.Res = Topology.resolve(init)
|
||||
|
||||
val expected =
|
||||
seq(
|
||||
val expected: Node.Res =
|
||||
MakeRes.seq(
|
||||
through(relay),
|
||||
through(otherRelay),
|
||||
call(1, otherPeer),
|
||||
call(2, otherPeer)
|
||||
callRes(1, otherPeer),
|
||||
callRes(2, otherPeer)
|
||||
)
|
||||
|
||||
proc.equalsOrPrintDiff(expected) should be(true)
|
||||
}
|
||||
|
||||
"topology resolver" should "build return path in par if there are exported variables" in {
|
||||
val export = Some(Call.Export("result", ScalarType.string))
|
||||
val result = VarModel("result", ScalarType.string)
|
||||
|
||||
val init = on(
|
||||
initPeer,
|
||||
relay :: Nil,
|
||||
FuncOps.seq(
|
||||
FuncOps.par(
|
||||
on(
|
||||
otherPeer,
|
||||
otherRelay :: Nil,
|
||||
callTag(1, export)
|
||||
),
|
||||
callTag(2)
|
||||
),
|
||||
callTag(3, None, result :: Nil)
|
||||
)
|
||||
)
|
||||
|
||||
val proc: Node.Res = Topology.resolve(init)
|
||||
|
||||
val expected: Node.Res =
|
||||
MakeRes.seq(
|
||||
MakeRes.par(
|
||||
MakeRes.seq(
|
||||
through(relay),
|
||||
through(otherRelay),
|
||||
callRes(1, otherPeer, export),
|
||||
through(otherRelay),
|
||||
through(relay),
|
||||
// we should return to a caller to continue execution
|
||||
through(initPeer)
|
||||
),
|
||||
callRes(2, initPeer)
|
||||
),
|
||||
callRes(3, initPeer, None, result :: Nil)
|
||||
)
|
||||
|
||||
Node.equalsOrPrintDiff(proc, expected) should be(true)
|
||||
}
|
||||
|
||||
"topology resolver" should "work fine with par" in {
|
||||
val init = on(
|
||||
initPeer,
|
||||
relay :: Nil,
|
||||
FuncOps.par(
|
||||
on(
|
||||
otherPeer,
|
||||
otherRelay :: Nil,
|
||||
callTag(1)
|
||||
),
|
||||
callTag(2)
|
||||
)
|
||||
)
|
||||
|
||||
val proc = Topology.resolve(init)
|
||||
|
||||
val expected: Node.Res =
|
||||
MakeRes.par(
|
||||
MakeRes.seq(
|
||||
through(relay),
|
||||
through(otherRelay),
|
||||
callRes(1, otherPeer)
|
||||
),
|
||||
callRes(2, initPeer)
|
||||
)
|
||||
|
||||
proc.equalsOrPrintDiff(expected) should be(true)
|
||||
}
|
||||
|
||||
"topology resolver" should "create correct calls in try" in {
|
||||
val init = Node.`try`(callTag(1))
|
||||
|
||||
val proc = Topology.resolve(init)
|
||||
|
||||
proc.equalsOrPrintDiff(
|
||||
Cofree[Chain, ResolvedOp](XorRes, Eval.now(Chain.one(callRes(1, initPeer))))
|
||||
) should be(true)
|
||||
}
|
||||
|
||||
"topology resolver" should "work fine with par with on" in {
|
||||
val init: Node.Raw = on(
|
||||
initPeer,
|
||||
relay :: Nil,
|
||||
FuncOps.par(
|
||||
on(
|
||||
otherPeer,
|
||||
otherRelay :: Nil,
|
||||
callTag(1)
|
||||
),
|
||||
on(
|
||||
otherPeer2,
|
||||
otherRelay2 :: Nil,
|
||||
callTag(2)
|
||||
)
|
||||
)
|
||||
)
|
||||
|
||||
val proc: Node.Res = Topology.resolve(init)
|
||||
|
||||
val expected: Node.Res =
|
||||
MakeRes.par(
|
||||
MakeRes.seq(
|
||||
through(relay),
|
||||
through(otherRelay),
|
||||
callRes(1, otherPeer)
|
||||
),
|
||||
MakeRes.seq(
|
||||
through(relay),
|
||||
through(otherRelay2),
|
||||
callRes(2, otherPeer2)
|
||||
)
|
||||
)
|
||||
|
||||
proc.equalsOrPrintDiff(expected) should be(true)
|
||||
@ -89,28 +215,28 @@ class TopologySpec extends AnyFlatSpec with Matchers {
|
||||
on(
|
||||
otherPeer,
|
||||
otherRelay :: Nil,
|
||||
xor(
|
||||
seq(
|
||||
call(1),
|
||||
call(2)
|
||||
FuncOps.xor(
|
||||
FuncOps.seq(
|
||||
callTag(1),
|
||||
callTag(2)
|
||||
),
|
||||
call(3)
|
||||
callTag(3)
|
||||
)
|
||||
)
|
||||
)
|
||||
|
||||
val proc: Node = Topology.resolve(init)
|
||||
val proc: Node.Res = Topology.resolve(init)
|
||||
|
||||
val expected =
|
||||
seq(
|
||||
val expected: Node.Res =
|
||||
MakeRes.seq(
|
||||
through(relay),
|
||||
through(otherRelay),
|
||||
xor(
|
||||
seq(
|
||||
call(1, otherPeer),
|
||||
call(2, otherPeer)
|
||||
MakeRes.xor(
|
||||
MakeRes.seq(
|
||||
callRes(1, otherPeer),
|
||||
callRes(2, otherPeer)
|
||||
),
|
||||
call(3, otherPeer)
|
||||
callRes(3, otherPeer)
|
||||
)
|
||||
)
|
||||
|
||||
@ -118,25 +244,25 @@ class TopologySpec extends AnyFlatSpec with Matchers {
|
||||
}
|
||||
|
||||
"topology resolver" should "simplify a route with init_peer_id" in {
|
||||
val init = on(
|
||||
val init: Node.Raw = on(
|
||||
initPeer,
|
||||
relay :: Nil,
|
||||
seq(
|
||||
FuncOps.seq(
|
||||
on(
|
||||
initPeer,
|
||||
relay :: Nil,
|
||||
call(1)
|
||||
callTag(1)
|
||||
),
|
||||
call(2)
|
||||
callTag(2)
|
||||
)
|
||||
)
|
||||
|
||||
val proc: Node = Topology.resolve(init)
|
||||
val proc: Node.Res = Topology.resolve(init)
|
||||
|
||||
val expected =
|
||||
seq(
|
||||
call(1, initPeer),
|
||||
call(2, initPeer)
|
||||
MakeRes.seq(
|
||||
callRes(1, initPeer),
|
||||
callRes(2, initPeer)
|
||||
)
|
||||
|
||||
proc.equalsOrPrintDiff(expected) should be(true)
|
||||
@ -144,29 +270,29 @@ class TopologySpec extends AnyFlatSpec with Matchers {
|
||||
|
||||
"topology resolver" should "get back to init peer" in {
|
||||
|
||||
val init = on(
|
||||
val init: Node.Raw = on(
|
||||
initPeer,
|
||||
relay :: Nil,
|
||||
seq(
|
||||
FuncOps.seq(
|
||||
on(
|
||||
otherPeer,
|
||||
otherRelay :: Nil,
|
||||
call(1)
|
||||
callTag(1)
|
||||
),
|
||||
call(2)
|
||||
callTag(2)
|
||||
)
|
||||
)
|
||||
|
||||
val proc: Node = Topology.resolve(init)
|
||||
val proc: Node.Res = Topology.resolve(init)
|
||||
|
||||
val expected =
|
||||
seq(
|
||||
val expected: Node.Res =
|
||||
MakeRes.seq(
|
||||
through(relay),
|
||||
through(otherRelay),
|
||||
call(1, otherPeer),
|
||||
callRes(1, otherPeer),
|
||||
through(otherRelay),
|
||||
through(relay),
|
||||
call(2, initPeer)
|
||||
callRes(2, initPeer)
|
||||
)
|
||||
|
||||
// println(Console.BLUE + init)
|
||||
@ -197,19 +323,19 @@ class TopologySpec extends AnyFlatSpec with Matchers {
|
||||
val init = on(
|
||||
initPeer,
|
||||
relay :: Nil,
|
||||
seq(
|
||||
call(1),
|
||||
call(2),
|
||||
call(3),
|
||||
FuncOps.seq(
|
||||
callTag(1),
|
||||
callTag(2),
|
||||
callTag(3),
|
||||
on(
|
||||
varNode,
|
||||
viaList :: Nil,
|
||||
call(4)
|
||||
callTag(4)
|
||||
),
|
||||
on(
|
||||
initPeer,
|
||||
relay :: Nil,
|
||||
call(5)
|
||||
callTag(5)
|
||||
)
|
||||
)
|
||||
)
|
||||
@ -222,50 +348,50 @@ class TopologySpec extends AnyFlatSpec with Matchers {
|
||||
val init = on(
|
||||
initPeer,
|
||||
relay :: Nil,
|
||||
seq(
|
||||
FuncOps.seq(
|
||||
on(
|
||||
otherPeer,
|
||||
otherRelay :: Nil,
|
||||
call(0),
|
||||
callTag(0),
|
||||
on(
|
||||
otherPeer2,
|
||||
otherRelay :: Nil,
|
||||
call(1),
|
||||
_match(
|
||||
callTag(1),
|
||||
matchRaw(
|
||||
otherPeer,
|
||||
otherRelay,
|
||||
on(
|
||||
otherPeer,
|
||||
otherRelay :: Nil,
|
||||
call(2)
|
||||
callTag(2)
|
||||
)
|
||||
)
|
||||
)
|
||||
),
|
||||
call(3)
|
||||
callTag(3)
|
||||
)
|
||||
)
|
||||
|
||||
val proc: Node = Topology.resolve(init)
|
||||
val proc: Node.Res = Topology.resolve(init)
|
||||
|
||||
val expected =
|
||||
seq(
|
||||
val expected: Node.Res =
|
||||
MakeRes.seq(
|
||||
through(relay),
|
||||
through(otherRelay),
|
||||
call(0, otherPeer),
|
||||
callRes(0, otherPeer),
|
||||
through(otherRelay),
|
||||
call(1, otherPeer2),
|
||||
_match(
|
||||
callRes(1, otherPeer2),
|
||||
matchRes(
|
||||
otherPeer,
|
||||
otherRelay,
|
||||
seq(
|
||||
MakeRes.seq(
|
||||
through(otherRelay),
|
||||
call(2, otherPeer)
|
||||
callRes(2, otherPeer)
|
||||
)
|
||||
),
|
||||
through(otherRelay),
|
||||
through(relay),
|
||||
call(3, initPeer)
|
||||
callRes(3, initPeer)
|
||||
)
|
||||
|
||||
// println(Console.BLUE + init)
|
||||
@ -276,4 +402,63 @@ class TopologySpec extends AnyFlatSpec with Matchers {
|
||||
proc.equalsOrPrintDiff(expected) should be(true)
|
||||
}
|
||||
|
||||
"topology resolver" should "resolve xor path" in {
|
||||
|
||||
val init = on(
|
||||
initPeer,
|
||||
relay :: Nil,
|
||||
FuncOps.seq(
|
||||
FuncOps.xor(
|
||||
on(
|
||||
otherPeer,
|
||||
otherRelay :: Nil,
|
||||
callTag(0)
|
||||
),
|
||||
on(
|
||||
initPeer,
|
||||
relay :: Nil,
|
||||
callTag(1)
|
||||
)
|
||||
),
|
||||
on(
|
||||
otherPeer,
|
||||
otherRelay :: Nil,
|
||||
callTag(3)
|
||||
),
|
||||
callTag(4)
|
||||
)
|
||||
)
|
||||
|
||||
val proc: Node.Res = Topology.resolve(init)
|
||||
|
||||
val expected: Node.Res =
|
||||
MakeRes.seq(
|
||||
MakeRes.xor(
|
||||
MakeRes.seq(
|
||||
through(relay),
|
||||
through(otherRelay),
|
||||
callRes(0, otherPeer)
|
||||
),
|
||||
MakeRes.seq(
|
||||
through(otherRelay),
|
||||
through(relay),
|
||||
callRes(1, initPeer),
|
||||
through(relay),
|
||||
through(otherRelay)
|
||||
)
|
||||
),
|
||||
callRes(3, otherPeer),
|
||||
through(otherRelay),
|
||||
through(relay),
|
||||
callRes(4, initPeer)
|
||||
)
|
||||
|
||||
// println(Console.BLUE + init)
|
||||
println(Console.YELLOW + proc)
|
||||
println(Console.MAGENTA + expected)
|
||||
println(Console.RESET)
|
||||
|
||||
Node.equalsOrPrintDiff(proc, expected) should be(true)
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -1,7 +1,8 @@
|
||||
package aqua.model.transform
|
||||
|
||||
import aqua.Node
|
||||
import aqua.model.func.body.{CallArrowTag, CallServiceTag, FuncOp}
|
||||
import aqua.model.func.raw.{CallArrowTag, CallServiceTag, FuncOp, FuncOps}
|
||||
import aqua.model.func.resolved.{CallServiceRes, MakeRes}
|
||||
import aqua.model.func.{ArgsDef, Call, FuncCallable}
|
||||
import aqua.model.{LiteralModel, VarModel}
|
||||
import aqua.types.ScalarType
|
||||
@ -11,14 +12,14 @@ import org.scalatest.matchers.should.Matchers
|
||||
class TransformSpec extends AnyFlatSpec with Matchers {
|
||||
import Node._
|
||||
|
||||
"transform.forClient" should "work well with function 1 (no calls before on)" in {
|
||||
"transform.forClient" should "work well with function 1 (no calls before on), generate correct error handling" in {
|
||||
|
||||
val ret = LiteralModel.quote("return this")
|
||||
|
||||
val func: FuncCallable =
|
||||
FuncCallable(
|
||||
"ret",
|
||||
FuncOp(on(otherPeer, otherRelay :: Nil, call(1))),
|
||||
on(otherPeer, otherRelay :: Nil, callTag(1)),
|
||||
ArgsDef.empty,
|
||||
Some((ret, ScalarType.string)),
|
||||
Map.empty,
|
||||
@ -29,17 +30,17 @@ class TransformSpec extends AnyFlatSpec with Matchers {
|
||||
|
||||
val fc = Transform.forClient(func, bc)
|
||||
|
||||
val procFC: Node = fc
|
||||
val procFC: Node.Res = fc
|
||||
|
||||
val expectedFC = seq(
|
||||
xor(
|
||||
seq(
|
||||
val expectedFC: Node.Res =
|
||||
MakeRes.xor(
|
||||
MakeRes.seq(
|
||||
dataCall(bc, "-relay-", initPeer),
|
||||
through(relayV),
|
||||
through(otherRelay),
|
||||
xor(
|
||||
call(1, otherPeer),
|
||||
seq(
|
||||
MakeRes.xor(
|
||||
callRes(1, otherPeer),
|
||||
MakeRes.seq(
|
||||
through(otherRelay),
|
||||
through(relayV),
|
||||
errorCall(bc, 1, initPeer),
|
||||
@ -48,22 +49,17 @@ class TransformSpec extends AnyFlatSpec with Matchers {
|
||||
),
|
||||
through(otherRelay),
|
||||
through(relayV),
|
||||
xor(
|
||||
MakeRes.xor(
|
||||
respCall(bc, ret, initPeer),
|
||||
seq(
|
||||
errorCall(bc, 2, initPeer)
|
||||
)
|
||||
errorCall(bc, 2, initPeer)
|
||||
)
|
||||
),
|
||||
seq(
|
||||
errorCall(bc, 3, initPeer)
|
||||
)
|
||||
errorCall(bc, 3, initPeer)
|
||||
)
|
||||
)
|
||||
|
||||
println(procFC)
|
||||
//println(procFC)
|
||||
|
||||
procFC.equalsOrPrintDiff(expectedFC) should be(true)
|
||||
Node.equalsOrPrintDiff(procFC, expectedFC) should be(true)
|
||||
|
||||
}
|
||||
|
||||
@ -73,7 +69,7 @@ class TransformSpec extends AnyFlatSpec with Matchers {
|
||||
|
||||
val func: FuncCallable = FuncCallable(
|
||||
"ret",
|
||||
FuncOp(seq(call(0), on(otherPeer, Nil, call(1)))),
|
||||
FuncOps.seq(callTag(0), on(otherPeer, Nil, callTag(1))),
|
||||
ArgsDef.empty,
|
||||
Some((ret, ScalarType.string)),
|
||||
Map.empty,
|
||||
@ -84,14 +80,14 @@ class TransformSpec extends AnyFlatSpec with Matchers {
|
||||
|
||||
val fc = Transform.forClient(func, bc)
|
||||
|
||||
val procFC: Node = fc
|
||||
val procFC: Res = fc
|
||||
|
||||
val expectedFC =
|
||||
seq(
|
||||
val expectedFC: Res =
|
||||
MakeRes.seq(
|
||||
dataCall(bc, "-relay-", initPeer),
|
||||
call(0, initPeer),
|
||||
callRes(0, initPeer),
|
||||
through(relayV),
|
||||
call(1, otherPeer),
|
||||
callRes(1, otherPeer),
|
||||
through(relayV),
|
||||
respCall(bc, ret, initPeer)
|
||||
)
|
||||
@ -119,10 +115,9 @@ class TransformSpec extends AnyFlatSpec with Matchers {
|
||||
CallServiceTag(
|
||||
LiteralModel.quote("srv1"),
|
||||
"foo",
|
||||
Call(Nil, Some(Call.Export("v", ScalarType.string))),
|
||||
None
|
||||
Call(Nil, Some(Call.Export("v", ScalarType.string)))
|
||||
)
|
||||
)
|
||||
).cof
|
||||
),
|
||||
ArgsDef.empty,
|
||||
Some((VarModel("v", ScalarType.string), ScalarType.string)),
|
||||
@ -134,7 +129,7 @@ class TransformSpec extends AnyFlatSpec with Matchers {
|
||||
FuncCallable(
|
||||
"f2",
|
||||
FuncOp(
|
||||
Node(CallArrowTag("callable", Call(Nil, Some(Call.Export("v", ScalarType.string)))))
|
||||
Node(CallArrowTag("callable", Call(Nil, Some(Call.Export("v", ScalarType.string))))).cof
|
||||
),
|
||||
ArgsDef.empty,
|
||||
Some((VarModel("v", ScalarType.string), ScalarType.string)),
|
||||
@ -144,17 +139,17 @@ class TransformSpec extends AnyFlatSpec with Matchers {
|
||||
|
||||
val bc = BodyConfig(wrapWithXor = false)
|
||||
|
||||
val res = Transform.forClient(f2, bc): Node
|
||||
val res = Transform.forClient(f2, bc): Node.Res
|
||||
|
||||
res.equalsOrPrintDiff(
|
||||
seq(
|
||||
MakeRes.seq(
|
||||
dataCall(bc, "-relay-", initPeer),
|
||||
Node(
|
||||
CallServiceTag(
|
||||
CallServiceRes(
|
||||
LiteralModel.quote("srv1"),
|
||||
"foo",
|
||||
Call(Nil, Some(Call.Export("v", ScalarType.string))),
|
||||
Some(initPeer)
|
||||
initPeer
|
||||
)
|
||||
),
|
||||
respCall(bc, VarModel("v", ScalarType.string), initPeer)
|
||||
|
@ -16,7 +16,7 @@ sealed trait Type {
|
||||
sealed trait DataType extends Type
|
||||
|
||||
case class ScalarType private (name: String) extends DataType {
|
||||
override def toString: String = s"ScalarType($name)"
|
||||
override def toString: String = name
|
||||
}
|
||||
|
||||
object ScalarType {
|
||||
@ -72,7 +72,7 @@ object ScalarType {
|
||||
}
|
||||
|
||||
case class LiteralType private (oneOf: Set[ScalarType], name: String) extends DataType {
|
||||
override def toString: String = s"Literal($name)"
|
||||
override def toString: String = s"literal($name)"
|
||||
}
|
||||
|
||||
object LiteralType {
|
||||
|
Loading…
Reference in New Issue
Block a user